diff --git a/.github/workflows/csv-coverage-pr-artifacts.yml b/.github/workflows/csv-coverage-pr-artifacts.yml
index 201eea5c073..8b89b9b22c1 100644
--- a/.github/workflows/csv-coverage-pr-artifacts.yml
+++ b/.github/workflows/csv-coverage-pr-artifacts.yml
@@ -6,6 +6,8 @@ on:
- '.github/workflows/csv-coverage-pr-comment.yml'
- '*/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'
diff --git a/.github/workflows/csv-coverage-update.yml b/.github/workflows/csv-coverage-update.yml
index 10834bdd36a..eb227ec844f 100644
--- a/.github/workflows/csv-coverage-update.yml
+++ b/.github/workflows/csv-coverage-update.yml
@@ -8,7 +8,7 @@ on:
jobs:
update:
name: Update framework coverage report
- if: github.event.repository.fork == false
+ if: github.repository == 'github/codeql'
runs-on: ubuntu-latest
steps:
diff --git a/.gitignore b/.gitignore
index 0951496d45c..bf37ce08333 100644
--- a/.gitignore
+++ b/.gitignore
@@ -24,3 +24,6 @@
/codeql/
csharp/extractor/Semmle.Extraction.CSharp.Driver/Properties/launchSettings.json
+
+# Avoid committing cached package components
+.codeql
diff --git a/README.md b/README.md
index 9012e83f10d..b1d5b82cb31 100644
--- a/README.md
+++ b/README.md
@@ -4,8 +4,8 @@ This open source repository contains the standard CodeQL libraries and queries t
## How do I learn CodeQL and run queries?
-There is [extensive documentation](https://help.semmle.com/QL/learn-ql/) on getting started with writing CodeQL.
-You can use the [interactive query console](https://lgtm.com/help/lgtm/using-query-console) on LGTM.com or the [CodeQL for Visual Studio Code](https://help.semmle.com/codeql/codeql-for-vscode.html) extension to try out your queries on any open source project that's currently being analyzed.
+There is [extensive documentation](https://codeql.github.com/docs/) on getting started with writing CodeQL.
+You can use the [interactive query console](https://lgtm.com/help/lgtm/using-query-console) on LGTM.com or the [CodeQL for Visual Studio Code](https://codeql.github.com/docs/codeql-for-visual-studio-code/) extension to try out your queries on any open source project that's currently being analyzed.
## Contributing
diff --git a/cpp/change-notes/2021-06-10-cleartext-transmission.md b/cpp/change-notes/2021-06-10-cleartext-transmission.md
new file mode 100644
index 00000000000..ce6debf1407
--- /dev/null
+++ b/cpp/change-notes/2021-06-10-cleartext-transmission.md
@@ -0,0 +1,2 @@
+lgtm,codescanning
+* A new query (`cpp/cleartext-transmission`) has been added. This is similar to the `cpp/cleartext-storage-file`, `cpp/cleartext-storage-buffer` and `cpp/cleartext-storage-database` queries but looks for cases where sensitive information is most likely transmitted over a network.
diff --git a/cpp/change-notes/2021-06-22-sql-tainted.md b/cpp/change-notes/2021-06-22-sql-tainted.md
new file mode 100644
index 00000000000..004f96ce25b
--- /dev/null
+++ b/cpp/change-notes/2021-06-22-sql-tainted.md
@@ -0,0 +1,2 @@
+lgtm,codescanning
+* The 'Uncontrolled data in SQL query' (cpp/sql-injection) query now supports the `libpqxx` library.
\ No newline at end of file
diff --git a/cpp/change-notes/2021-08-31-range-analysis-upper-bound.md b/cpp/change-notes/2021-08-31-range-analysis-upper-bound.md
new file mode 100644
index 00000000000..f7ea800f719
--- /dev/null
+++ b/cpp/change-notes/2021-08-31-range-analysis-upper-bound.md
@@ -0,0 +1,4 @@
+lgtm,codescanning
+* The `SimpleRangeAnalysis` library includes information from the
+ immediate guard for determining the upper bound of a stack
+ variable for improved accuracy.
diff --git a/cpp/change-notes/2021-09-13-overflow-static.md b/cpp/change-notes/2021-09-13-overflow-static.md
new file mode 100644
index 00000000000..5a9ef395815
--- /dev/null
+++ b/cpp/change-notes/2021-09-13-overflow-static.md
@@ -0,0 +1,4 @@
+lgtm,codescanning
+* The `memberMayBeVarSize` predicate considers more fields to be variable size.
+ As a result, the "Static buffer overflow" query (cpp/static-buffer-overflow)
+ produces fewer false positives.
diff --git a/cpp/change-notes/2021-09-27-command-line-injection.md b/cpp/change-notes/2021-09-27-command-line-injection.md
new file mode 100644
index 00000000000..53ce1fd1dbe
--- /dev/null
+++ b/cpp/change-notes/2021-09-27-command-line-injection.md
@@ -0,0 +1,2 @@
+lgtm,codescanning
+* The "Uncontrolled data used in OS command" (`cpp/command-line-injection`) query has been enhanced to reduce false positive results and its `@precision` increased to `high`
\ No newline at end of file
diff --git a/cpp/change-notes/2021-09-27-overflow-static.md b/cpp/change-notes/2021-09-27-overflow-static.md
new file mode 100644
index 00000000000..e28ba4970ce
--- /dev/null
+++ b/cpp/change-notes/2021-09-27-overflow-static.md
@@ -0,0 +1,3 @@
+lgtm,codescanning
+* Increase precision to high for the "Static buffer overflow" query
+ (`cpp/static-buffer-overflow`). This means the query is run and displayed by default on Code Scanning and LGTM.
diff --git a/cpp/ql/lib/semmle/code/cpp/File.qll b/cpp/ql/lib/semmle/code/cpp/File.qll
index b2d933686d7..f486dd8d3c5 100644
--- a/cpp/ql/lib/semmle/code/cpp/File.qll
+++ b/cpp/ql/lib/semmle/code/cpp/File.qll
@@ -38,7 +38,7 @@ class Container extends Locatable, @container {
* DEPRECATED: Use `getLocation` instead.
* Gets a URL representing the location of this container.
*
- * For more information see [Providing URLs](https://help.semmle.com/QL/learn-ql/ql/locations.html#providing-urls).
+ * For more information see [Providing URLs](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/#providing-urls).
*/
deprecated string getURL() { none() } // overridden by subclasses
@@ -171,7 +171,7 @@ class Container extends Locatable, @container {
* To get the full path, use `getAbsolutePath`.
*/
class Folder extends Container, @folder {
- override string getAbsolutePath() { folders(underlyingElement(this), result, _) }
+ override string getAbsolutePath() { folders(underlyingElement(this), result) }
override Location getLocation() {
result.getContainer() = this and
@@ -190,7 +190,7 @@ class Folder extends Container, @folder {
* DEPRECATED: use `getAbsolutePath` instead.
* Gets the name of this folder.
*/
- deprecated string getName() { folders(underlyingElement(this), result, _) }
+ deprecated string getName() { folders(underlyingElement(this), result) }
/**
* DEPRECATED: use `getAbsolutePath` instead.
@@ -208,17 +208,7 @@ class Folder extends Container, @folder {
* DEPRECATED: use `getBaseName` instead.
* Gets the last part of the folder name.
*/
- deprecated string getShortName() {
- exists(string longnameRaw, string longname |
- folders(underlyingElement(this), _, longnameRaw) and
- longname = longnameRaw.replaceAll("\\", "/")
- |
- exists(int index |
- result = longname.splitAt("/", index) and
- not exists(longname.splitAt("/", index + 1))
- )
- )
- }
+ deprecated string getShortName() { result = this.getBaseName() }
/**
* DEPRECATED: use `getParentContainer` instead.
@@ -242,7 +232,7 @@ class Folder extends Container, @folder {
* `getStem` and `getExtension`. To get the full path, use `getAbsolutePath`.
*/
class File extends Container, @file {
- override string getAbsolutePath() { files(underlyingElement(this), result, _, _, _) }
+ override string getAbsolutePath() { files(underlyingElement(this), result) }
override string toString() { result = Container.super.toString() }
@@ -336,7 +326,13 @@ class File extends Container, @file {
* for example, for "file.tar.gz", this predicate will have the result
* "tar.gz", while `getExtension` will have the result "gz".
*/
- string getExtensions() { files(underlyingElement(this), _, _, result, _) }
+ string getExtensions() {
+ exists(string name, int firstDotPos |
+ name = this.getBaseName() and
+ firstDotPos = min([name.indexOf("."), name.length() - 1]) and
+ result = name.suffix(firstDotPos + 1)
+ )
+ }
/**
* Gets the short name of this file, that is, the prefix of its base name up
@@ -351,7 +347,16 @@ class File extends Container, @file {
* for example, for "file.tar.gz", this predicate will have the result
* "file", while `getStem` will have the result "file.tar".
*/
- string getShortName() { files(underlyingElement(this), _, result, _, _) }
+ string getShortName() {
+ exists(string name, int firstDotPos |
+ name = this.getBaseName() and
+ firstDotPos = min([name.indexOf("."), name.length()]) and
+ result = name.prefix(firstDotPos)
+ )
+ or
+ this.getAbsolutePath() = "" and
+ result = ""
+ }
}
/**
diff --git a/cpp/ql/lib/semmle/code/cpp/Location.qll b/cpp/ql/lib/semmle/code/cpp/Location.qll
index 2a730ea5768..15ae2121255 100644
--- a/cpp/ql/lib/semmle/code/cpp/Location.qll
+++ b/cpp/ql/lib/semmle/code/cpp/Location.qll
@@ -61,7 +61,7 @@ class Location extends @location {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
- * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
diff --git a/cpp/ql/lib/semmle/code/cpp/XML.qll b/cpp/ql/lib/semmle/code/cpp/XML.qll
index 5871fed0ddd..4c762f4bf65 100755
--- a/cpp/ql/lib/semmle/code/cpp/XML.qll
+++ b/cpp/ql/lib/semmle/code/cpp/XML.qll
@@ -24,7 +24,7 @@ class XMLLocatable extends @xmllocatable, TXMLLocatable {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
- * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
diff --git a/cpp/ql/lib/semmle/code/cpp/commons/Buffer.qll b/cpp/ql/lib/semmle/code/cpp/commons/Buffer.qll
index 88e6a48ff55..2328476d525 100644
--- a/cpp/ql/lib/semmle/code/cpp/commons/Buffer.qll
+++ b/cpp/ql/lib/semmle/code/cpp/commons/Buffer.qll
@@ -2,53 +2,19 @@ import cpp
import semmle.code.cpp.dataflow.DataFlow
/**
- * Holds if `v` is a member variable of `c` that looks like it might be variable sized in practice. For
- * example:
+ * Holds if `v` is a member variable of `c` that looks like it might be variable sized
+ * in practice. For example:
* ```
* struct myStruct { // c
* int amount;
* char data[1]; // v
* };
* ```
- * This requires that `v` is an array of size 0 or 1, and `v` is the last member of `c`. In addition,
- * there must be at least one instance where a `c` pointer is allocated with additional space. For
- * example, holds for `c` if it occurs as
- * ```
- * malloc(sizeof(c) + 100 * sizeof(char))
- * ```
- * but not if it only ever occurs as
- * ```
- * malloc(sizeof(c))
- * ```
+ * This requires that `v` is an array of size 0 or 1.
*/
predicate memberMayBeVarSize(Class c, MemberVariable v) {
- exists(int i |
- // `v` is the last field in `c`
- i = max(int j | c.getCanonicalMember(j) instanceof Field | j) and
- v = c.getCanonicalMember(i) and
- // v is an array of size at most 1
- v.getUnspecifiedType().(ArrayType).getArraySize() <= 1
- ) and
- (
- exists(SizeofOperator so |
- // `sizeof(c)` is taken
- so.(SizeofTypeOperator).getTypeOperand().getUnspecifiedType() = c or
- so.(SizeofExprOperator).getExprOperand().getUnspecifiedType() = c
- |
- // arithmetic is performed on the result
- so.getParent*() instanceof AddExpr
- )
- or
- exists(AddressOfExpr aoe |
- // `&(c.v)` is taken
- aoe.getAddressable() = v
- )
- or
- exists(BuiltInOperationBuiltInOffsetOf oo |
- // `offsetof(c, v)` using a builtin
- oo.getAChild().(VariableAccess).getTarget() = v
- )
- )
+ c = v.getDeclaringType() and
+ v.getUnspecifiedType().(ArrayType).getArraySize() <= 1
}
/**
diff --git a/cpp/ql/lib/semmle/code/cpp/commons/NullTermination.qll b/cpp/ql/lib/semmle/code/cpp/commons/NullTermination.qll
index 6cb366d46ee..ee072fb0ed3 100644
--- a/cpp/ql/lib/semmle/code/cpp/commons/NullTermination.qll
+++ b/cpp/ql/lib/semmle/code/cpp/commons/NullTermination.qll
@@ -93,6 +93,15 @@ predicate variableMustBeNullTerminated(VariableAccess va) {
fc.getArgument(i) = va
)
or
+ // String argument to a formatting function (such as `printf`)
+ exists(int n, FormatLiteral fl |
+ fc.(FormattingFunctionCall).getConversionArgument(n) = va and
+ fl = fc.(FormattingFunctionCall).getFormat() and
+ fl.getConversionType(n) instanceof PointerType and // `%s`, `%ws` etc
+ not fl.getConversionType(n) instanceof VoidPointerType and // exclude: `%p`
+ not fl.hasPrecision(n) // exclude: `%.*s`
+ )
+ or
// Call to a wrapper function that requires null termination
// (not itself adding a null terminator)
exists(Function wrapper, int i, Parameter p, VariableAccess use |
diff --git a/cpp/ql/lib/semmle/code/cpp/commons/Printf.qll b/cpp/ql/lib/semmle/code/cpp/commons/Printf.qll
index ef5f5511f6d..46ca9ccf009 100644
--- a/cpp/ql/lib/semmle/code/cpp/commons/Printf.qll
+++ b/cpp/ql/lib/semmle/code/cpp/commons/Printf.qll
@@ -253,6 +253,21 @@ class FormattingFunctionCall extends Expr {
// format arguments must be known
exists(getTarget().(FormattingFunction).getFirstFormatArgumentIndex())
}
+
+ /**
+ * Gets the argument, if any, to which the output is written. If `isStream` is
+ * `true`, the output argument is a stream (that is, this call behaves like
+ * `fprintf`). If `isStream` is `false`, the output argument is a buffer (that
+ * is, this call behaves like `sprintf`)
+ */
+ Expr getOutputArgument(boolean isStream) {
+ result =
+ this.(Call)
+ .getArgument(this.(Call)
+ .getTarget()
+ .(FormattingFunction)
+ .getOutputParameterIndex(isStream))
+ }
}
/**
diff --git a/cpp/ql/lib/semmle/code/cpp/controlflow/BasicBlocks.qll b/cpp/ql/lib/semmle/code/cpp/controlflow/BasicBlocks.qll
index 16947019f54..e235eba355b 100644
--- a/cpp/ql/lib/semmle/code/cpp/controlflow/BasicBlocks.qll
+++ b/cpp/ql/lib/semmle/code/cpp/controlflow/BasicBlocks.qll
@@ -194,7 +194,7 @@ class BasicBlock extends ControlFlowNodeBase {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
- * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*
* Yields no result if this basic block spans multiple source files.
*/
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 5c2dbb30084..4ca06c93362 100644
--- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll
+++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll
@@ -923,28 +923,29 @@ private module Stage2 {
ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
- class Cc = boolean;
+ class Cc = CallContext;
- class CcCall extends Cc {
- CcCall() { this = true }
+ class CcCall = CallContextCall;
- /** Holds if this call context may be `call`. */
- predicate matchesCall(DataFlowCall call) { any() }
- }
+ class CcNoCall = CallContextNoCall;
- class CcNoCall extends Cc {
- CcNoCall() { this = false }
- }
-
- Cc ccNone() { result = false }
+ Cc ccNone() { result instanceof CallContextAny }
private class LocalCc = Unit;
bindingset[call, c, outercc]
- private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
+ 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[call, c, innercc]
- private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
+ 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, config]
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
@@ -1172,7 +1173,8 @@ private module Stage2 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
- fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ 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))
)
}
@@ -1860,7 +1862,8 @@ private module Stage3 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
- fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ 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))
)
}
@@ -2117,7 +2120,7 @@ private module Stage3 {
private predicate flowCandSummaryCtx(NodeEx node, AccessPathFront argApf, Configuration config) {
exists(AccessPathFront apf |
Stage3::revFlow(node, true, _, apf, config) and
- Stage3::fwdFlow(node, true, TAccessPathFrontSome(argApf), apf, config)
+ Stage3::fwdFlow(node, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
)
}
@@ -2136,7 +2139,8 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
) and
accessPathApproxCostLimits(apLimit, tupleLimit) and
apLimit < tails and
- tupleLimit < (tails - 1) * nodes
+ tupleLimit < (tails - 1) * nodes and
+ not tc.forceHighPrecision()
)
}
@@ -2618,7 +2622,8 @@ private module Stage4 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
- fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ 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))
)
}
@@ -2969,12 +2974,15 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) {
* expected to be expensive. Holds with `unfold = true` otherwise.
*/
private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) {
- exists(int aps, int nodes, int apLimit, int tupleLimit |
- aps = countPotentialAps(apa, config) and
- nodes = countNodesUsingAccessPath(apa, config) and
- accessPathCostLimits(apLimit, tupleLimit) and
- if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true
- )
+ if apa.getHead().forceHighPrecision()
+ then unfold = true
+ else
+ exists(int aps, int nodes, int apLimit, int tupleLimit |
+ aps = countPotentialAps(apa, config) and
+ nodes = countNodesUsingAccessPath(apa, config) and
+ accessPathCostLimits(apLimit, tupleLimit) and
+ if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true
+ )
}
/**
@@ -3244,7 +3252,7 @@ class PathNode extends TPathNode {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
- * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
@@ -3258,24 +3266,16 @@ class PathNode extends TPathNode {
/** Gets the associated configuration. */
Configuration getConfiguration() { none() }
- private predicate isHidden() {
- hiddenNode(this.(PathNodeImpl).getNodeEx().asNode()) and
- not this.isSource() and
- not this instanceof PathNodeSink
- or
- this.(PathNodeImpl).getNodeEx() instanceof TNodeImplicitRead
- }
-
private PathNode getASuccessorIfHidden() {
- this.isHidden() and
+ this.(PathNodeImpl).isHidden() and
result = this.(PathNodeImpl).getASuccessorImpl()
}
/** Gets a successor of this node, if any. */
final PathNode getASuccessor() {
result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and
- not this.isHidden() and
- not result.isHidden()
+ not this.(PathNodeImpl).isHidden() and
+ not result.(PathNodeImpl).isHidden()
}
/** Holds if this node is a source. */
@@ -3287,6 +3287,14 @@ abstract private class PathNodeImpl extends PathNode {
abstract NodeEx getNodeEx();
+ predicate isHidden() {
+ hiddenNode(this.getNodeEx().asNode()) and
+ not this.isSource() and
+ not this instanceof PathNodeSink
+ or
+ this.getNodeEx() instanceof TNodeImplicitRead
+ }
+
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
@@ -3313,10 +3321,15 @@ abstract private class PathNodeImpl extends PathNode {
}
/** Holds if `n` can reach a sink. */
-private predicate reach(PathNode n) { n instanceof PathNodeSink or reach(n.getASuccessor()) }
+private predicate directReach(PathNode n) {
+ n instanceof PathNodeSink or directReach(n.getASuccessor())
+}
-/** Holds if `n1.getSucc() = n2` and `n2` can reach a sink. */
-private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and reach(n2) }
+/** Holds if `n` can reach a sink or is used in a subpath. */
+private predicate reach(PathNode n) { directReach(n) or Subpaths::retReach(n) }
+
+/** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */
+private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and directReach(n2) }
private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2)
@@ -3325,12 +3338,14 @@ private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1
*/
module PathGraph {
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
- query predicate edges(PathNode a, PathNode b) { pathSucc(a, b) }
+ query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b and reach(b) }
/** Holds if `n` is a node in the graph of data flow path explanations. */
query predicate nodes(PathNode n, string key, string val) {
reach(n) and key = "semmle.label" and val = n.toString()
}
+
+ query predicate subpaths = Subpaths::subpaths/4;
}
/**
@@ -3622,6 +3637,87 @@ private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext c
)
}
+private module Subpaths {
+ /**
+ * Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by
+ * `kind`, `sc`, `apout`, and `innercc`.
+ */
+ pragma[nomagic]
+ private predicate subpaths01(
+ PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
+ NodeEx out, AccessPath apout
+ ) {
+ pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and
+ pathIntoCallable(arg, par, _, innercc, sc, _) and
+ paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _,
+ unbindConf(arg.getConfiguration()))
+ }
+
+ /**
+ * Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by
+ * `kind`, `sc`, `apout`, and `innercc`.
+ */
+ pragma[nomagic]
+ private predicate subpaths02(
+ PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
+ NodeEx out, AccessPath apout
+ ) {
+ subpaths01(arg, par, sc, innercc, kind, out, apout) and
+ out.asNode() = kind.getAnOutNode(_)
+ }
+
+ pragma[nomagic]
+ private Configuration getPathNodeConf(PathNode n) { result = n.getConfiguration() }
+
+ /**
+ * Holds if `(arg, par, ret, out)` forms a subpath-tuple.
+ */
+ pragma[nomagic]
+ private predicate subpaths03(
+ PathNode arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, AccessPath apout
+ ) {
+ exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode |
+ subpaths02(arg, par, sc, innercc, kind, out, apout) and
+ ret.getNodeEx() = retnode and
+ kind = retnode.getKind() and
+ innercc = ret.getCallContext() and
+ sc = ret.getSummaryCtx() and
+ ret.getConfiguration() = unbindConf(getPathNodeConf(arg)) and
+ apout = ret.getAp() and
+ not ret.isHidden()
+ )
+ }
+
+ /**
+ * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through
+ * a subpath between `par` and `ret` with the connecting edges `arg -> par` and
+ * `ret -> out` is summarized as the edge `arg -> out`.
+ */
+ predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeMid ret, PathNodeMid out) {
+ exists(ParamNodeEx p, NodeEx o, AccessPath apout |
+ pragma[only_bind_into](arg).getASuccessor() = par and
+ pragma[only_bind_into](arg).getASuccessor() = out and
+ subpaths03(arg, p, ret, o, apout) and
+ par.getNodeEx() = p and
+ out.getNodeEx() = o and
+ out.getAp() = apout
+ )
+ }
+
+ /**
+ * Holds if `n` can reach a return node in a summarized subpath.
+ */
+ predicate retReach(PathNode n) {
+ subpaths(_, _, n, _)
+ or
+ exists(PathNode mid |
+ retReach(mid) and
+ n.getASuccessor() = mid and
+ not subpaths(_, mid, _, _)
+ )
+ }
+}
+
/**
* Holds if data can flow (inter-procedurally) from `source` to `sink`.
*
@@ -3941,7 +4037,7 @@ private module FlowExploration {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
- * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
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 5c2dbb30084..4ca06c93362 100644
--- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll
+++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll
@@ -923,28 +923,29 @@ private module Stage2 {
ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
- class Cc = boolean;
+ class Cc = CallContext;
- class CcCall extends Cc {
- CcCall() { this = true }
+ class CcCall = CallContextCall;
- /** Holds if this call context may be `call`. */
- predicate matchesCall(DataFlowCall call) { any() }
- }
+ class CcNoCall = CallContextNoCall;
- class CcNoCall extends Cc {
- CcNoCall() { this = false }
- }
-
- Cc ccNone() { result = false }
+ Cc ccNone() { result instanceof CallContextAny }
private class LocalCc = Unit;
bindingset[call, c, outercc]
- private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
+ 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[call, c, innercc]
- private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
+ 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, config]
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
@@ -1172,7 +1173,8 @@ private module Stage2 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
- fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ 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))
)
}
@@ -1860,7 +1862,8 @@ private module Stage3 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
- fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ 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))
)
}
@@ -2117,7 +2120,7 @@ private module Stage3 {
private predicate flowCandSummaryCtx(NodeEx node, AccessPathFront argApf, Configuration config) {
exists(AccessPathFront apf |
Stage3::revFlow(node, true, _, apf, config) and
- Stage3::fwdFlow(node, true, TAccessPathFrontSome(argApf), apf, config)
+ Stage3::fwdFlow(node, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
)
}
@@ -2136,7 +2139,8 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
) and
accessPathApproxCostLimits(apLimit, tupleLimit) and
apLimit < tails and
- tupleLimit < (tails - 1) * nodes
+ tupleLimit < (tails - 1) * nodes and
+ not tc.forceHighPrecision()
)
}
@@ -2618,7 +2622,8 @@ private module Stage4 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
- fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ 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))
)
}
@@ -2969,12 +2974,15 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) {
* expected to be expensive. Holds with `unfold = true` otherwise.
*/
private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) {
- exists(int aps, int nodes, int apLimit, int tupleLimit |
- aps = countPotentialAps(apa, config) and
- nodes = countNodesUsingAccessPath(apa, config) and
- accessPathCostLimits(apLimit, tupleLimit) and
- if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true
- )
+ if apa.getHead().forceHighPrecision()
+ then unfold = true
+ else
+ exists(int aps, int nodes, int apLimit, int tupleLimit |
+ aps = countPotentialAps(apa, config) and
+ nodes = countNodesUsingAccessPath(apa, config) and
+ accessPathCostLimits(apLimit, tupleLimit) and
+ if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true
+ )
}
/**
@@ -3244,7 +3252,7 @@ class PathNode extends TPathNode {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
- * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
@@ -3258,24 +3266,16 @@ class PathNode extends TPathNode {
/** Gets the associated configuration. */
Configuration getConfiguration() { none() }
- private predicate isHidden() {
- hiddenNode(this.(PathNodeImpl).getNodeEx().asNode()) and
- not this.isSource() and
- not this instanceof PathNodeSink
- or
- this.(PathNodeImpl).getNodeEx() instanceof TNodeImplicitRead
- }
-
private PathNode getASuccessorIfHidden() {
- this.isHidden() and
+ this.(PathNodeImpl).isHidden() and
result = this.(PathNodeImpl).getASuccessorImpl()
}
/** Gets a successor of this node, if any. */
final PathNode getASuccessor() {
result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and
- not this.isHidden() and
- not result.isHidden()
+ not this.(PathNodeImpl).isHidden() and
+ not result.(PathNodeImpl).isHidden()
}
/** Holds if this node is a source. */
@@ -3287,6 +3287,14 @@ abstract private class PathNodeImpl extends PathNode {
abstract NodeEx getNodeEx();
+ predicate isHidden() {
+ hiddenNode(this.getNodeEx().asNode()) and
+ not this.isSource() and
+ not this instanceof PathNodeSink
+ or
+ this.getNodeEx() instanceof TNodeImplicitRead
+ }
+
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
@@ -3313,10 +3321,15 @@ abstract private class PathNodeImpl extends PathNode {
}
/** Holds if `n` can reach a sink. */
-private predicate reach(PathNode n) { n instanceof PathNodeSink or reach(n.getASuccessor()) }
+private predicate directReach(PathNode n) {
+ n instanceof PathNodeSink or directReach(n.getASuccessor())
+}
-/** Holds if `n1.getSucc() = n2` and `n2` can reach a sink. */
-private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and reach(n2) }
+/** Holds if `n` can reach a sink or is used in a subpath. */
+private predicate reach(PathNode n) { directReach(n) or Subpaths::retReach(n) }
+
+/** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */
+private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and directReach(n2) }
private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2)
@@ -3325,12 +3338,14 @@ private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1
*/
module PathGraph {
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
- query predicate edges(PathNode a, PathNode b) { pathSucc(a, b) }
+ query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b and reach(b) }
/** Holds if `n` is a node in the graph of data flow path explanations. */
query predicate nodes(PathNode n, string key, string val) {
reach(n) and key = "semmle.label" and val = n.toString()
}
+
+ query predicate subpaths = Subpaths::subpaths/4;
}
/**
@@ -3622,6 +3637,87 @@ private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext c
)
}
+private module Subpaths {
+ /**
+ * Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by
+ * `kind`, `sc`, `apout`, and `innercc`.
+ */
+ pragma[nomagic]
+ private predicate subpaths01(
+ PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
+ NodeEx out, AccessPath apout
+ ) {
+ pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and
+ pathIntoCallable(arg, par, _, innercc, sc, _) and
+ paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _,
+ unbindConf(arg.getConfiguration()))
+ }
+
+ /**
+ * Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by
+ * `kind`, `sc`, `apout`, and `innercc`.
+ */
+ pragma[nomagic]
+ private predicate subpaths02(
+ PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
+ NodeEx out, AccessPath apout
+ ) {
+ subpaths01(arg, par, sc, innercc, kind, out, apout) and
+ out.asNode() = kind.getAnOutNode(_)
+ }
+
+ pragma[nomagic]
+ private Configuration getPathNodeConf(PathNode n) { result = n.getConfiguration() }
+
+ /**
+ * Holds if `(arg, par, ret, out)` forms a subpath-tuple.
+ */
+ pragma[nomagic]
+ private predicate subpaths03(
+ PathNode arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, AccessPath apout
+ ) {
+ exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode |
+ subpaths02(arg, par, sc, innercc, kind, out, apout) and
+ ret.getNodeEx() = retnode and
+ kind = retnode.getKind() and
+ innercc = ret.getCallContext() and
+ sc = ret.getSummaryCtx() and
+ ret.getConfiguration() = unbindConf(getPathNodeConf(arg)) and
+ apout = ret.getAp() and
+ not ret.isHidden()
+ )
+ }
+
+ /**
+ * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through
+ * a subpath between `par` and `ret` with the connecting edges `arg -> par` and
+ * `ret -> out` is summarized as the edge `arg -> out`.
+ */
+ predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeMid ret, PathNodeMid out) {
+ exists(ParamNodeEx p, NodeEx o, AccessPath apout |
+ pragma[only_bind_into](arg).getASuccessor() = par and
+ pragma[only_bind_into](arg).getASuccessor() = out and
+ subpaths03(arg, p, ret, o, apout) and
+ par.getNodeEx() = p and
+ out.getNodeEx() = o and
+ out.getAp() = apout
+ )
+ }
+
+ /**
+ * Holds if `n` can reach a return node in a summarized subpath.
+ */
+ predicate retReach(PathNode n) {
+ subpaths(_, _, n, _)
+ or
+ exists(PathNode mid |
+ retReach(mid) and
+ n.getASuccessor() = mid and
+ not subpaths(_, mid, _, _)
+ )
+ }
+}
+
/**
* Holds if data can flow (inter-procedurally) from `source` to `sink`.
*
@@ -3941,7 +4037,7 @@ private module FlowExploration {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
- * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
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 5c2dbb30084..4ca06c93362 100644
--- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll
+++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll
@@ -923,28 +923,29 @@ private module Stage2 {
ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
- class Cc = boolean;
+ class Cc = CallContext;
- class CcCall extends Cc {
- CcCall() { this = true }
+ class CcCall = CallContextCall;
- /** Holds if this call context may be `call`. */
- predicate matchesCall(DataFlowCall call) { any() }
- }
+ class CcNoCall = CallContextNoCall;
- class CcNoCall extends Cc {
- CcNoCall() { this = false }
- }
-
- Cc ccNone() { result = false }
+ Cc ccNone() { result instanceof CallContextAny }
private class LocalCc = Unit;
bindingset[call, c, outercc]
- private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
+ 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[call, c, innercc]
- private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
+ 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, config]
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
@@ -1172,7 +1173,8 @@ private module Stage2 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
- fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ 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))
)
}
@@ -1860,7 +1862,8 @@ private module Stage3 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
- fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ 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))
)
}
@@ -2117,7 +2120,7 @@ private module Stage3 {
private predicate flowCandSummaryCtx(NodeEx node, AccessPathFront argApf, Configuration config) {
exists(AccessPathFront apf |
Stage3::revFlow(node, true, _, apf, config) and
- Stage3::fwdFlow(node, true, TAccessPathFrontSome(argApf), apf, config)
+ Stage3::fwdFlow(node, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
)
}
@@ -2136,7 +2139,8 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
) and
accessPathApproxCostLimits(apLimit, tupleLimit) and
apLimit < tails and
- tupleLimit < (tails - 1) * nodes
+ tupleLimit < (tails - 1) * nodes and
+ not tc.forceHighPrecision()
)
}
@@ -2618,7 +2622,8 @@ private module Stage4 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
- fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ 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))
)
}
@@ -2969,12 +2974,15 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) {
* expected to be expensive. Holds with `unfold = true` otherwise.
*/
private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) {
- exists(int aps, int nodes, int apLimit, int tupleLimit |
- aps = countPotentialAps(apa, config) and
- nodes = countNodesUsingAccessPath(apa, config) and
- accessPathCostLimits(apLimit, tupleLimit) and
- if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true
- )
+ if apa.getHead().forceHighPrecision()
+ then unfold = true
+ else
+ exists(int aps, int nodes, int apLimit, int tupleLimit |
+ aps = countPotentialAps(apa, config) and
+ nodes = countNodesUsingAccessPath(apa, config) and
+ accessPathCostLimits(apLimit, tupleLimit) and
+ if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true
+ )
}
/**
@@ -3244,7 +3252,7 @@ class PathNode extends TPathNode {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
- * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
@@ -3258,24 +3266,16 @@ class PathNode extends TPathNode {
/** Gets the associated configuration. */
Configuration getConfiguration() { none() }
- private predicate isHidden() {
- hiddenNode(this.(PathNodeImpl).getNodeEx().asNode()) and
- not this.isSource() and
- not this instanceof PathNodeSink
- or
- this.(PathNodeImpl).getNodeEx() instanceof TNodeImplicitRead
- }
-
private PathNode getASuccessorIfHidden() {
- this.isHidden() and
+ this.(PathNodeImpl).isHidden() and
result = this.(PathNodeImpl).getASuccessorImpl()
}
/** Gets a successor of this node, if any. */
final PathNode getASuccessor() {
result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and
- not this.isHidden() and
- not result.isHidden()
+ not this.(PathNodeImpl).isHidden() and
+ not result.(PathNodeImpl).isHidden()
}
/** Holds if this node is a source. */
@@ -3287,6 +3287,14 @@ abstract private class PathNodeImpl extends PathNode {
abstract NodeEx getNodeEx();
+ predicate isHidden() {
+ hiddenNode(this.getNodeEx().asNode()) and
+ not this.isSource() and
+ not this instanceof PathNodeSink
+ or
+ this.getNodeEx() instanceof TNodeImplicitRead
+ }
+
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
@@ -3313,10 +3321,15 @@ abstract private class PathNodeImpl extends PathNode {
}
/** Holds if `n` can reach a sink. */
-private predicate reach(PathNode n) { n instanceof PathNodeSink or reach(n.getASuccessor()) }
+private predicate directReach(PathNode n) {
+ n instanceof PathNodeSink or directReach(n.getASuccessor())
+}
-/** Holds if `n1.getSucc() = n2` and `n2` can reach a sink. */
-private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and reach(n2) }
+/** Holds if `n` can reach a sink or is used in a subpath. */
+private predicate reach(PathNode n) { directReach(n) or Subpaths::retReach(n) }
+
+/** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */
+private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and directReach(n2) }
private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2)
@@ -3325,12 +3338,14 @@ private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1
*/
module PathGraph {
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
- query predicate edges(PathNode a, PathNode b) { pathSucc(a, b) }
+ query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b and reach(b) }
/** Holds if `n` is a node in the graph of data flow path explanations. */
query predicate nodes(PathNode n, string key, string val) {
reach(n) and key = "semmle.label" and val = n.toString()
}
+
+ query predicate subpaths = Subpaths::subpaths/4;
}
/**
@@ -3622,6 +3637,87 @@ private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext c
)
}
+private module Subpaths {
+ /**
+ * Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by
+ * `kind`, `sc`, `apout`, and `innercc`.
+ */
+ pragma[nomagic]
+ private predicate subpaths01(
+ PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
+ NodeEx out, AccessPath apout
+ ) {
+ pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and
+ pathIntoCallable(arg, par, _, innercc, sc, _) and
+ paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _,
+ unbindConf(arg.getConfiguration()))
+ }
+
+ /**
+ * Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by
+ * `kind`, `sc`, `apout`, and `innercc`.
+ */
+ pragma[nomagic]
+ private predicate subpaths02(
+ PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
+ NodeEx out, AccessPath apout
+ ) {
+ subpaths01(arg, par, sc, innercc, kind, out, apout) and
+ out.asNode() = kind.getAnOutNode(_)
+ }
+
+ pragma[nomagic]
+ private Configuration getPathNodeConf(PathNode n) { result = n.getConfiguration() }
+
+ /**
+ * Holds if `(arg, par, ret, out)` forms a subpath-tuple.
+ */
+ pragma[nomagic]
+ private predicate subpaths03(
+ PathNode arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, AccessPath apout
+ ) {
+ exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode |
+ subpaths02(arg, par, sc, innercc, kind, out, apout) and
+ ret.getNodeEx() = retnode and
+ kind = retnode.getKind() and
+ innercc = ret.getCallContext() and
+ sc = ret.getSummaryCtx() and
+ ret.getConfiguration() = unbindConf(getPathNodeConf(arg)) and
+ apout = ret.getAp() and
+ not ret.isHidden()
+ )
+ }
+
+ /**
+ * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through
+ * a subpath between `par` and `ret` with the connecting edges `arg -> par` and
+ * `ret -> out` is summarized as the edge `arg -> out`.
+ */
+ predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeMid ret, PathNodeMid out) {
+ exists(ParamNodeEx p, NodeEx o, AccessPath apout |
+ pragma[only_bind_into](arg).getASuccessor() = par and
+ pragma[only_bind_into](arg).getASuccessor() = out and
+ subpaths03(arg, p, ret, o, apout) and
+ par.getNodeEx() = p and
+ out.getNodeEx() = o and
+ out.getAp() = apout
+ )
+ }
+
+ /**
+ * Holds if `n` can reach a return node in a summarized subpath.
+ */
+ predicate retReach(PathNode n) {
+ subpaths(_, _, n, _)
+ or
+ exists(PathNode mid |
+ retReach(mid) and
+ n.getASuccessor() = mid and
+ not subpaths(_, mid, _, _)
+ )
+ }
+}
+
/**
* Holds if data can flow (inter-procedurally) from `source` to `sink`.
*
@@ -3941,7 +4037,7 @@ private module FlowExploration {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
- * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
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 5c2dbb30084..4ca06c93362 100644
--- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll
+++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll
@@ -923,28 +923,29 @@ private module Stage2 {
ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
- class Cc = boolean;
+ class Cc = CallContext;
- class CcCall extends Cc {
- CcCall() { this = true }
+ class CcCall = CallContextCall;
- /** Holds if this call context may be `call`. */
- predicate matchesCall(DataFlowCall call) { any() }
- }
+ class CcNoCall = CallContextNoCall;
- class CcNoCall extends Cc {
- CcNoCall() { this = false }
- }
-
- Cc ccNone() { result = false }
+ Cc ccNone() { result instanceof CallContextAny }
private class LocalCc = Unit;
bindingset[call, c, outercc]
- private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
+ 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[call, c, innercc]
- private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
+ 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, config]
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
@@ -1172,7 +1173,8 @@ private module Stage2 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
- fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ 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))
)
}
@@ -1860,7 +1862,8 @@ private module Stage3 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
- fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ 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))
)
}
@@ -2117,7 +2120,7 @@ private module Stage3 {
private predicate flowCandSummaryCtx(NodeEx node, AccessPathFront argApf, Configuration config) {
exists(AccessPathFront apf |
Stage3::revFlow(node, true, _, apf, config) and
- Stage3::fwdFlow(node, true, TAccessPathFrontSome(argApf), apf, config)
+ Stage3::fwdFlow(node, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
)
}
@@ -2136,7 +2139,8 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
) and
accessPathApproxCostLimits(apLimit, tupleLimit) and
apLimit < tails and
- tupleLimit < (tails - 1) * nodes
+ tupleLimit < (tails - 1) * nodes and
+ not tc.forceHighPrecision()
)
}
@@ -2618,7 +2622,8 @@ private module Stage4 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
- fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ 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))
)
}
@@ -2969,12 +2974,15 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) {
* expected to be expensive. Holds with `unfold = true` otherwise.
*/
private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) {
- exists(int aps, int nodes, int apLimit, int tupleLimit |
- aps = countPotentialAps(apa, config) and
- nodes = countNodesUsingAccessPath(apa, config) and
- accessPathCostLimits(apLimit, tupleLimit) and
- if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true
- )
+ if apa.getHead().forceHighPrecision()
+ then unfold = true
+ else
+ exists(int aps, int nodes, int apLimit, int tupleLimit |
+ aps = countPotentialAps(apa, config) and
+ nodes = countNodesUsingAccessPath(apa, config) and
+ accessPathCostLimits(apLimit, tupleLimit) and
+ if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true
+ )
}
/**
@@ -3244,7 +3252,7 @@ class PathNode extends TPathNode {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
- * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
@@ -3258,24 +3266,16 @@ class PathNode extends TPathNode {
/** Gets the associated configuration. */
Configuration getConfiguration() { none() }
- private predicate isHidden() {
- hiddenNode(this.(PathNodeImpl).getNodeEx().asNode()) and
- not this.isSource() and
- not this instanceof PathNodeSink
- or
- this.(PathNodeImpl).getNodeEx() instanceof TNodeImplicitRead
- }
-
private PathNode getASuccessorIfHidden() {
- this.isHidden() and
+ this.(PathNodeImpl).isHidden() and
result = this.(PathNodeImpl).getASuccessorImpl()
}
/** Gets a successor of this node, if any. */
final PathNode getASuccessor() {
result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and
- not this.isHidden() and
- not result.isHidden()
+ not this.(PathNodeImpl).isHidden() and
+ not result.(PathNodeImpl).isHidden()
}
/** Holds if this node is a source. */
@@ -3287,6 +3287,14 @@ abstract private class PathNodeImpl extends PathNode {
abstract NodeEx getNodeEx();
+ predicate isHidden() {
+ hiddenNode(this.getNodeEx().asNode()) and
+ not this.isSource() and
+ not this instanceof PathNodeSink
+ or
+ this.getNodeEx() instanceof TNodeImplicitRead
+ }
+
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
@@ -3313,10 +3321,15 @@ abstract private class PathNodeImpl extends PathNode {
}
/** Holds if `n` can reach a sink. */
-private predicate reach(PathNode n) { n instanceof PathNodeSink or reach(n.getASuccessor()) }
+private predicate directReach(PathNode n) {
+ n instanceof PathNodeSink or directReach(n.getASuccessor())
+}
-/** Holds if `n1.getSucc() = n2` and `n2` can reach a sink. */
-private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and reach(n2) }
+/** Holds if `n` can reach a sink or is used in a subpath. */
+private predicate reach(PathNode n) { directReach(n) or Subpaths::retReach(n) }
+
+/** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */
+private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and directReach(n2) }
private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2)
@@ -3325,12 +3338,14 @@ private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1
*/
module PathGraph {
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
- query predicate edges(PathNode a, PathNode b) { pathSucc(a, b) }
+ query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b and reach(b) }
/** Holds if `n` is a node in the graph of data flow path explanations. */
query predicate nodes(PathNode n, string key, string val) {
reach(n) and key = "semmle.label" and val = n.toString()
}
+
+ query predicate subpaths = Subpaths::subpaths/4;
}
/**
@@ -3622,6 +3637,87 @@ private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext c
)
}
+private module Subpaths {
+ /**
+ * Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by
+ * `kind`, `sc`, `apout`, and `innercc`.
+ */
+ pragma[nomagic]
+ private predicate subpaths01(
+ PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
+ NodeEx out, AccessPath apout
+ ) {
+ pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and
+ pathIntoCallable(arg, par, _, innercc, sc, _) and
+ paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _,
+ unbindConf(arg.getConfiguration()))
+ }
+
+ /**
+ * Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by
+ * `kind`, `sc`, `apout`, and `innercc`.
+ */
+ pragma[nomagic]
+ private predicate subpaths02(
+ PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
+ NodeEx out, AccessPath apout
+ ) {
+ subpaths01(arg, par, sc, innercc, kind, out, apout) and
+ out.asNode() = kind.getAnOutNode(_)
+ }
+
+ pragma[nomagic]
+ private Configuration getPathNodeConf(PathNode n) { result = n.getConfiguration() }
+
+ /**
+ * Holds if `(arg, par, ret, out)` forms a subpath-tuple.
+ */
+ pragma[nomagic]
+ private predicate subpaths03(
+ PathNode arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, AccessPath apout
+ ) {
+ exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode |
+ subpaths02(arg, par, sc, innercc, kind, out, apout) and
+ ret.getNodeEx() = retnode and
+ kind = retnode.getKind() and
+ innercc = ret.getCallContext() and
+ sc = ret.getSummaryCtx() and
+ ret.getConfiguration() = unbindConf(getPathNodeConf(arg)) and
+ apout = ret.getAp() and
+ not ret.isHidden()
+ )
+ }
+
+ /**
+ * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through
+ * a subpath between `par` and `ret` with the connecting edges `arg -> par` and
+ * `ret -> out` is summarized as the edge `arg -> out`.
+ */
+ predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeMid ret, PathNodeMid out) {
+ exists(ParamNodeEx p, NodeEx o, AccessPath apout |
+ pragma[only_bind_into](arg).getASuccessor() = par and
+ pragma[only_bind_into](arg).getASuccessor() = out and
+ subpaths03(arg, p, ret, o, apout) and
+ par.getNodeEx() = p and
+ out.getNodeEx() = o and
+ out.getAp() = apout
+ )
+ }
+
+ /**
+ * Holds if `n` can reach a return node in a summarized subpath.
+ */
+ predicate retReach(PathNode n) {
+ subpaths(_, _, n, _)
+ or
+ exists(PathNode mid |
+ retReach(mid) and
+ n.getASuccessor() = mid and
+ not subpaths(_, mid, _, _)
+ )
+ }
+}
+
/**
* Holds if data can flow (inter-procedurally) from `source` to `sink`.
*
@@ -3941,7 +4037,7 @@ private module FlowExploration {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
- * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll
index 728f7b56c42..f43a550af57 100644
--- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll
+++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll
@@ -786,13 +786,18 @@ private module Cached {
}
/**
- * Holds if the call context `call` either improves virtual dispatch in
- * `callable` or if it allows us to prune unreachable nodes in `callable`.
+ * Holds if the call context `call` improves virtual dispatch in `callable`.
*/
cached
- predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) {
+ predicate recordDataFlowCallSiteDispatch(DataFlowCall call, DataFlowCallable callable) {
reducedViableImplInCallContext(_, callable, call)
- or
+ }
+
+ /**
+ * Holds if the call context `call` allows us to prune unreachable nodes in `callable`.
+ */
+ cached
+ predicate recordDataFlowCallSiteUnreachable(DataFlowCall call, DataFlowCallable callable) {
exists(Node n | getNodeEnclosingCallable(n) = callable | isUnreachableInCallCached(n, call))
}
@@ -846,6 +851,15 @@ private module Cached {
TAccessPathFrontSome(AccessPathFront apf)
}
+/**
+ * Holds if the call context `call` either improves virtual dispatch in
+ * `callable` or if it allows us to prune unreachable nodes in `callable`.
+ */
+predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) {
+ recordDataFlowCallSiteDispatch(call, callable) or
+ recordDataFlowCallSiteUnreachable(call, callable)
+}
+
/**
* A `Node` at which a cast can occur such that the type should be checked.
*/
@@ -1222,6 +1236,13 @@ class TypedContent extends MkTypedContent {
/** Gets a textual representation of this content. */
string toString() { result = c.toString() }
+
+ /**
+ * Holds if access paths with this `TypedContent` at their head always should
+ * be tracked at high precision. This disables adaptive access path precision
+ * for such access paths.
+ */
+ predicate forceHighPrecision() { forceHighPrecision(c) }
}
/**
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 5c2dbb30084..4ca06c93362 100644
--- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll
+++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll
@@ -923,28 +923,29 @@ private module Stage2 {
ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
- class Cc = boolean;
+ class Cc = CallContext;
- class CcCall extends Cc {
- CcCall() { this = true }
+ class CcCall = CallContextCall;
- /** Holds if this call context may be `call`. */
- predicate matchesCall(DataFlowCall call) { any() }
- }
+ class CcNoCall = CallContextNoCall;
- class CcNoCall extends Cc {
- CcNoCall() { this = false }
- }
-
- Cc ccNone() { result = false }
+ Cc ccNone() { result instanceof CallContextAny }
private class LocalCc = Unit;
bindingset[call, c, outercc]
- private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
+ 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[call, c, innercc]
- private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
+ 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, config]
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
@@ -1172,7 +1173,8 @@ private module Stage2 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
- fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ 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))
)
}
@@ -1860,7 +1862,8 @@ private module Stage3 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
- fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ 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))
)
}
@@ -2117,7 +2120,7 @@ private module Stage3 {
private predicate flowCandSummaryCtx(NodeEx node, AccessPathFront argApf, Configuration config) {
exists(AccessPathFront apf |
Stage3::revFlow(node, true, _, apf, config) and
- Stage3::fwdFlow(node, true, TAccessPathFrontSome(argApf), apf, config)
+ Stage3::fwdFlow(node, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
)
}
@@ -2136,7 +2139,8 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
) and
accessPathApproxCostLimits(apLimit, tupleLimit) and
apLimit < tails and
- tupleLimit < (tails - 1) * nodes
+ tupleLimit < (tails - 1) * nodes and
+ not tc.forceHighPrecision()
)
}
@@ -2618,7 +2622,8 @@ private module Stage4 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
- fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ 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))
)
}
@@ -2969,12 +2974,15 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) {
* expected to be expensive. Holds with `unfold = true` otherwise.
*/
private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) {
- exists(int aps, int nodes, int apLimit, int tupleLimit |
- aps = countPotentialAps(apa, config) and
- nodes = countNodesUsingAccessPath(apa, config) and
- accessPathCostLimits(apLimit, tupleLimit) and
- if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true
- )
+ if apa.getHead().forceHighPrecision()
+ then unfold = true
+ else
+ exists(int aps, int nodes, int apLimit, int tupleLimit |
+ aps = countPotentialAps(apa, config) and
+ nodes = countNodesUsingAccessPath(apa, config) and
+ accessPathCostLimits(apLimit, tupleLimit) and
+ if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true
+ )
}
/**
@@ -3244,7 +3252,7 @@ class PathNode extends TPathNode {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
- * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
@@ -3258,24 +3266,16 @@ class PathNode extends TPathNode {
/** Gets the associated configuration. */
Configuration getConfiguration() { none() }
- private predicate isHidden() {
- hiddenNode(this.(PathNodeImpl).getNodeEx().asNode()) and
- not this.isSource() and
- not this instanceof PathNodeSink
- or
- this.(PathNodeImpl).getNodeEx() instanceof TNodeImplicitRead
- }
-
private PathNode getASuccessorIfHidden() {
- this.isHidden() and
+ this.(PathNodeImpl).isHidden() and
result = this.(PathNodeImpl).getASuccessorImpl()
}
/** Gets a successor of this node, if any. */
final PathNode getASuccessor() {
result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and
- not this.isHidden() and
- not result.isHidden()
+ not this.(PathNodeImpl).isHidden() and
+ not result.(PathNodeImpl).isHidden()
}
/** Holds if this node is a source. */
@@ -3287,6 +3287,14 @@ abstract private class PathNodeImpl extends PathNode {
abstract NodeEx getNodeEx();
+ predicate isHidden() {
+ hiddenNode(this.getNodeEx().asNode()) and
+ not this.isSource() and
+ not this instanceof PathNodeSink
+ or
+ this.getNodeEx() instanceof TNodeImplicitRead
+ }
+
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
@@ -3313,10 +3321,15 @@ abstract private class PathNodeImpl extends PathNode {
}
/** Holds if `n` can reach a sink. */
-private predicate reach(PathNode n) { n instanceof PathNodeSink or reach(n.getASuccessor()) }
+private predicate directReach(PathNode n) {
+ n instanceof PathNodeSink or directReach(n.getASuccessor())
+}
-/** Holds if `n1.getSucc() = n2` and `n2` can reach a sink. */
-private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and reach(n2) }
+/** Holds if `n` can reach a sink or is used in a subpath. */
+private predicate reach(PathNode n) { directReach(n) or Subpaths::retReach(n) }
+
+/** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */
+private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and directReach(n2) }
private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2)
@@ -3325,12 +3338,14 @@ private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1
*/
module PathGraph {
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
- query predicate edges(PathNode a, PathNode b) { pathSucc(a, b) }
+ query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b and reach(b) }
/** Holds if `n` is a node in the graph of data flow path explanations. */
query predicate nodes(PathNode n, string key, string val) {
reach(n) and key = "semmle.label" and val = n.toString()
}
+
+ query predicate subpaths = Subpaths::subpaths/4;
}
/**
@@ -3622,6 +3637,87 @@ private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext c
)
}
+private module Subpaths {
+ /**
+ * Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by
+ * `kind`, `sc`, `apout`, and `innercc`.
+ */
+ pragma[nomagic]
+ private predicate subpaths01(
+ PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
+ NodeEx out, AccessPath apout
+ ) {
+ pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and
+ pathIntoCallable(arg, par, _, innercc, sc, _) and
+ paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _,
+ unbindConf(arg.getConfiguration()))
+ }
+
+ /**
+ * Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by
+ * `kind`, `sc`, `apout`, and `innercc`.
+ */
+ pragma[nomagic]
+ private predicate subpaths02(
+ PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
+ NodeEx out, AccessPath apout
+ ) {
+ subpaths01(arg, par, sc, innercc, kind, out, apout) and
+ out.asNode() = kind.getAnOutNode(_)
+ }
+
+ pragma[nomagic]
+ private Configuration getPathNodeConf(PathNode n) { result = n.getConfiguration() }
+
+ /**
+ * Holds if `(arg, par, ret, out)` forms a subpath-tuple.
+ */
+ pragma[nomagic]
+ private predicate subpaths03(
+ PathNode arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, AccessPath apout
+ ) {
+ exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode |
+ subpaths02(arg, par, sc, innercc, kind, out, apout) and
+ ret.getNodeEx() = retnode and
+ kind = retnode.getKind() and
+ innercc = ret.getCallContext() and
+ sc = ret.getSummaryCtx() and
+ ret.getConfiguration() = unbindConf(getPathNodeConf(arg)) and
+ apout = ret.getAp() and
+ not ret.isHidden()
+ )
+ }
+
+ /**
+ * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through
+ * a subpath between `par` and `ret` with the connecting edges `arg -> par` and
+ * `ret -> out` is summarized as the edge `arg -> out`.
+ */
+ predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeMid ret, PathNodeMid out) {
+ exists(ParamNodeEx p, NodeEx o, AccessPath apout |
+ pragma[only_bind_into](arg).getASuccessor() = par and
+ pragma[only_bind_into](arg).getASuccessor() = out and
+ subpaths03(arg, p, ret, o, apout) and
+ par.getNodeEx() = p and
+ out.getNodeEx() = o and
+ out.getAp() = apout
+ )
+ }
+
+ /**
+ * Holds if `n` can reach a return node in a summarized subpath.
+ */
+ predicate retReach(PathNode n) {
+ subpaths(_, _, n, _)
+ or
+ exists(PathNode mid |
+ retReach(mid) and
+ n.getASuccessor() = mid and
+ not subpaths(_, mid, _, _)
+ )
+ }
+}
+
/**
* Holds if data can flow (inter-procedurally) from `source` to `sink`.
*
@@ -3941,7 +4037,7 @@ private module FlowExploration {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
- * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll
index e64f8277528..986197bb0ce 100644
--- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll
+++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll
@@ -240,6 +240,12 @@ predicate isUnreachableInCall(Node n, DataFlowCall call) { none() } // stub impl
int accessPathLimit() { result = 5 }
+/**
+ * Holds if access paths with `c` at their head always should be tracked at high
+ * precision. This disables adaptive access path precision for such access paths.
+ */
+predicate forceHighPrecision(Content c) { none() }
+
/** The unit type. */
private newtype TUnit = TMkUnit()
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 01338eaeff4..2dfd02d14ef 100644
--- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll
+++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll
@@ -101,7 +101,7 @@ class Node extends TNode {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
- * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll
index 49d11a7e3cc..ece55d181bf 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll
@@ -4,7 +4,7 @@ private import semmle.code.cpp.ir.dataflow.DataFlow
private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
private import semmle.code.cpp.ir.dataflow.DataFlow3
private import semmle.code.cpp.ir.IR
-private import semmle.code.cpp.ir.dataflow.internal.DataFlowDispatch as Dispatch
+private import semmle.code.cpp.ir.dataflow.ResolveCall
private import semmle.code.cpp.controlflow.IRGuards
private import semmle.code.cpp.models.interfaces.Taint
private import semmle.code.cpp.models.interfaces.DataFlow
@@ -355,20 +355,6 @@ predicate taintedIncludingGlobalVars(Expr source, Element tainted, string global
*/
GlobalOrNamespaceVariable globalVarFromId(string id) { id = result.getQualifiedName() }
-/**
- * Resolve potential target function(s) for `call`.
- *
- * If `call` is a call through a function pointer (`ExprCall`) or
- * targets a virtual method, simple data flow analysis is performed
- * in order to identify target(s).
- */
-Function resolveCall(Call call) {
- exists(CallInstruction callInstruction |
- callInstruction.getAST() = call and
- result = Dispatch::viableCallable(callInstruction)
- )
-}
-
/**
* Provides definitions for augmenting source/sink pairs with data-flow paths
* between them. From a `@kind path-problem` query, import this module in the
@@ -479,7 +465,7 @@ module TaintedWithPath {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
- * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
@@ -550,6 +536,39 @@ module TaintedWithPath {
)
}
+ /**
+ * Holds if there is flow from `arg` to `out` across a call that can by summarized by the flow
+ * from `par` to `ret` within it, in the graph of data flow path explanations.
+ */
+ query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) {
+ DataFlow3::PathGraph::subpaths(arg.(WrapPathNode).inner(), par.(WrapPathNode).inner(),
+ ret.(WrapPathNode).inner(), out.(WrapPathNode).inner())
+ or
+ // To avoid showing trivial-looking steps, we _replace_ the last node instead
+ // of adding an edge out of it.
+ exists(WrapPathNode sinkNode |
+ DataFlow3::PathGraph::subpaths(arg.(WrapPathNode).inner(), par.(WrapPathNode).inner(),
+ ret.(WrapPathNode).inner(), sinkNode.inner()) and
+ out.(FinalPathNode).inner() = adjustedSink(sinkNode.inner().getNode())
+ )
+ or
+ // Same for the first node
+ exists(WrapPathNode sourceNode |
+ DataFlow3::PathGraph::subpaths(sourceNode.inner(), par.(WrapPathNode).inner(),
+ ret.(WrapPathNode).inner(), out.(WrapPathNode).inner()) and
+ sourceNode.inner().getNode() = getNodeForExpr(arg.(InitialPathNode).inner())
+ )
+ or
+ // Finally, handle the case where the path goes directly from a source to a
+ // sink, meaning that they both need to be translated.
+ exists(WrapPathNode sinkNode, WrapPathNode sourceNode |
+ DataFlow3::PathGraph::subpaths(sourceNode.inner(), par.(WrapPathNode).inner(),
+ ret.(WrapPathNode).inner(), sinkNode.inner()) and
+ sourceNode.inner().getNode() = getNodeForExpr(arg.(InitialPathNode).inner()) and
+ out.(FinalPathNode).inner() = adjustedSink(sinkNode.inner().getNode())
+ )
+ }
+
/** Holds if `n` is a node in the graph of data flow path explanations. */
query predicate nodes(PathNode n, string key, string val) {
key = "semmle.label" and val = n.toString()
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/ResolveCall.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/ResolveCall.qll
new file mode 100644
index 00000000000..f25386d3ba8
--- /dev/null
+++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/ResolveCall.qll
@@ -0,0 +1,23 @@
+/**
+ * Provides a predicate for non-contextual virtual dispatch and function
+ * pointer resolution.
+ */
+
+import cpp
+private import semmle.code.cpp.ir.ValueNumbering
+private import internal.DataFlowDispatch
+private import semmle.code.cpp.ir.IR
+
+/**
+ * Resolve potential target function(s) for `call`.
+ *
+ * If `call` is a call through a function pointer (`ExprCall`) or its target is
+ * a virtual member function, simple data flow analysis is performed in order
+ * to identify the possible target(s).
+ */
+Function resolveCall(Call call) {
+ exists(CallInstruction callInstruction |
+ callInstruction.getAST() = call and
+ result = viableCallable(callInstruction)
+ )
+}
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 5c2dbb30084..4ca06c93362 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
@@ -923,28 +923,29 @@ private module Stage2 {
ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
- class Cc = boolean;
+ class Cc = CallContext;
- class CcCall extends Cc {
- CcCall() { this = true }
+ class CcCall = CallContextCall;
- /** Holds if this call context may be `call`. */
- predicate matchesCall(DataFlowCall call) { any() }
- }
+ class CcNoCall = CallContextNoCall;
- class CcNoCall extends Cc {
- CcNoCall() { this = false }
- }
-
- Cc ccNone() { result = false }
+ Cc ccNone() { result instanceof CallContextAny }
private class LocalCc = Unit;
bindingset[call, c, outercc]
- private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
+ 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[call, c, innercc]
- private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
+ 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, config]
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
@@ -1172,7 +1173,8 @@ private module Stage2 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
- fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ 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))
)
}
@@ -1860,7 +1862,8 @@ private module Stage3 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
- fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ 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))
)
}
@@ -2117,7 +2120,7 @@ private module Stage3 {
private predicate flowCandSummaryCtx(NodeEx node, AccessPathFront argApf, Configuration config) {
exists(AccessPathFront apf |
Stage3::revFlow(node, true, _, apf, config) and
- Stage3::fwdFlow(node, true, TAccessPathFrontSome(argApf), apf, config)
+ Stage3::fwdFlow(node, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
)
}
@@ -2136,7 +2139,8 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
) and
accessPathApproxCostLimits(apLimit, tupleLimit) and
apLimit < tails and
- tupleLimit < (tails - 1) * nodes
+ tupleLimit < (tails - 1) * nodes and
+ not tc.forceHighPrecision()
)
}
@@ -2618,7 +2622,8 @@ private module Stage4 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
- fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ 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))
)
}
@@ -2969,12 +2974,15 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) {
* expected to be expensive. Holds with `unfold = true` otherwise.
*/
private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) {
- exists(int aps, int nodes, int apLimit, int tupleLimit |
- aps = countPotentialAps(apa, config) and
- nodes = countNodesUsingAccessPath(apa, config) and
- accessPathCostLimits(apLimit, tupleLimit) and
- if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true
- )
+ if apa.getHead().forceHighPrecision()
+ then unfold = true
+ else
+ exists(int aps, int nodes, int apLimit, int tupleLimit |
+ aps = countPotentialAps(apa, config) and
+ nodes = countNodesUsingAccessPath(apa, config) and
+ accessPathCostLimits(apLimit, tupleLimit) and
+ if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true
+ )
}
/**
@@ -3244,7 +3252,7 @@ class PathNode extends TPathNode {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
- * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
@@ -3258,24 +3266,16 @@ class PathNode extends TPathNode {
/** Gets the associated configuration. */
Configuration getConfiguration() { none() }
- private predicate isHidden() {
- hiddenNode(this.(PathNodeImpl).getNodeEx().asNode()) and
- not this.isSource() and
- not this instanceof PathNodeSink
- or
- this.(PathNodeImpl).getNodeEx() instanceof TNodeImplicitRead
- }
-
private PathNode getASuccessorIfHidden() {
- this.isHidden() and
+ this.(PathNodeImpl).isHidden() and
result = this.(PathNodeImpl).getASuccessorImpl()
}
/** Gets a successor of this node, if any. */
final PathNode getASuccessor() {
result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and
- not this.isHidden() and
- not result.isHidden()
+ not this.(PathNodeImpl).isHidden() and
+ not result.(PathNodeImpl).isHidden()
}
/** Holds if this node is a source. */
@@ -3287,6 +3287,14 @@ abstract private class PathNodeImpl extends PathNode {
abstract NodeEx getNodeEx();
+ predicate isHidden() {
+ hiddenNode(this.getNodeEx().asNode()) and
+ not this.isSource() and
+ not this instanceof PathNodeSink
+ or
+ this.getNodeEx() instanceof TNodeImplicitRead
+ }
+
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
@@ -3313,10 +3321,15 @@ abstract private class PathNodeImpl extends PathNode {
}
/** Holds if `n` can reach a sink. */
-private predicate reach(PathNode n) { n instanceof PathNodeSink or reach(n.getASuccessor()) }
+private predicate directReach(PathNode n) {
+ n instanceof PathNodeSink or directReach(n.getASuccessor())
+}
-/** Holds if `n1.getSucc() = n2` and `n2` can reach a sink. */
-private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and reach(n2) }
+/** Holds if `n` can reach a sink or is used in a subpath. */
+private predicate reach(PathNode n) { directReach(n) or Subpaths::retReach(n) }
+
+/** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */
+private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and directReach(n2) }
private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2)
@@ -3325,12 +3338,14 @@ private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1
*/
module PathGraph {
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
- query predicate edges(PathNode a, PathNode b) { pathSucc(a, b) }
+ query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b and reach(b) }
/** Holds if `n` is a node in the graph of data flow path explanations. */
query predicate nodes(PathNode n, string key, string val) {
reach(n) and key = "semmle.label" and val = n.toString()
}
+
+ query predicate subpaths = Subpaths::subpaths/4;
}
/**
@@ -3622,6 +3637,87 @@ private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext c
)
}
+private module Subpaths {
+ /**
+ * Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by
+ * `kind`, `sc`, `apout`, and `innercc`.
+ */
+ pragma[nomagic]
+ private predicate subpaths01(
+ PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
+ NodeEx out, AccessPath apout
+ ) {
+ pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and
+ pathIntoCallable(arg, par, _, innercc, sc, _) and
+ paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _,
+ unbindConf(arg.getConfiguration()))
+ }
+
+ /**
+ * Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by
+ * `kind`, `sc`, `apout`, and `innercc`.
+ */
+ pragma[nomagic]
+ private predicate subpaths02(
+ PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
+ NodeEx out, AccessPath apout
+ ) {
+ subpaths01(arg, par, sc, innercc, kind, out, apout) and
+ out.asNode() = kind.getAnOutNode(_)
+ }
+
+ pragma[nomagic]
+ private Configuration getPathNodeConf(PathNode n) { result = n.getConfiguration() }
+
+ /**
+ * Holds if `(arg, par, ret, out)` forms a subpath-tuple.
+ */
+ pragma[nomagic]
+ private predicate subpaths03(
+ PathNode arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, AccessPath apout
+ ) {
+ exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode |
+ subpaths02(arg, par, sc, innercc, kind, out, apout) and
+ ret.getNodeEx() = retnode and
+ kind = retnode.getKind() and
+ innercc = ret.getCallContext() and
+ sc = ret.getSummaryCtx() and
+ ret.getConfiguration() = unbindConf(getPathNodeConf(arg)) and
+ apout = ret.getAp() and
+ not ret.isHidden()
+ )
+ }
+
+ /**
+ * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through
+ * a subpath between `par` and `ret` with the connecting edges `arg -> par` and
+ * `ret -> out` is summarized as the edge `arg -> out`.
+ */
+ predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeMid ret, PathNodeMid out) {
+ exists(ParamNodeEx p, NodeEx o, AccessPath apout |
+ pragma[only_bind_into](arg).getASuccessor() = par and
+ pragma[only_bind_into](arg).getASuccessor() = out and
+ subpaths03(arg, p, ret, o, apout) and
+ par.getNodeEx() = p and
+ out.getNodeEx() = o and
+ out.getAp() = apout
+ )
+ }
+
+ /**
+ * Holds if `n` can reach a return node in a summarized subpath.
+ */
+ predicate retReach(PathNode n) {
+ subpaths(_, _, n, _)
+ or
+ exists(PathNode mid |
+ retReach(mid) and
+ n.getASuccessor() = mid and
+ not subpaths(_, mid, _, _)
+ )
+ }
+}
+
/**
* Holds if data can flow (inter-procedurally) from `source` to `sink`.
*
@@ -3941,7 +4037,7 @@ private module FlowExploration {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
- * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
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 5c2dbb30084..4ca06c93362 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
@@ -923,28 +923,29 @@ private module Stage2 {
ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
- class Cc = boolean;
+ class Cc = CallContext;
- class CcCall extends Cc {
- CcCall() { this = true }
+ class CcCall = CallContextCall;
- /** Holds if this call context may be `call`. */
- predicate matchesCall(DataFlowCall call) { any() }
- }
+ class CcNoCall = CallContextNoCall;
- class CcNoCall extends Cc {
- CcNoCall() { this = false }
- }
-
- Cc ccNone() { result = false }
+ Cc ccNone() { result instanceof CallContextAny }
private class LocalCc = Unit;
bindingset[call, c, outercc]
- private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
+ 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[call, c, innercc]
- private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
+ 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, config]
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
@@ -1172,7 +1173,8 @@ private module Stage2 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
- fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ 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))
)
}
@@ -1860,7 +1862,8 @@ private module Stage3 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
- fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ 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))
)
}
@@ -2117,7 +2120,7 @@ private module Stage3 {
private predicate flowCandSummaryCtx(NodeEx node, AccessPathFront argApf, Configuration config) {
exists(AccessPathFront apf |
Stage3::revFlow(node, true, _, apf, config) and
- Stage3::fwdFlow(node, true, TAccessPathFrontSome(argApf), apf, config)
+ Stage3::fwdFlow(node, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
)
}
@@ -2136,7 +2139,8 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
) and
accessPathApproxCostLimits(apLimit, tupleLimit) and
apLimit < tails and
- tupleLimit < (tails - 1) * nodes
+ tupleLimit < (tails - 1) * nodes and
+ not tc.forceHighPrecision()
)
}
@@ -2618,7 +2622,8 @@ private module Stage4 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
- fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ 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))
)
}
@@ -2969,12 +2974,15 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) {
* expected to be expensive. Holds with `unfold = true` otherwise.
*/
private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) {
- exists(int aps, int nodes, int apLimit, int tupleLimit |
- aps = countPotentialAps(apa, config) and
- nodes = countNodesUsingAccessPath(apa, config) and
- accessPathCostLimits(apLimit, tupleLimit) and
- if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true
- )
+ if apa.getHead().forceHighPrecision()
+ then unfold = true
+ else
+ exists(int aps, int nodes, int apLimit, int tupleLimit |
+ aps = countPotentialAps(apa, config) and
+ nodes = countNodesUsingAccessPath(apa, config) and
+ accessPathCostLimits(apLimit, tupleLimit) and
+ if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true
+ )
}
/**
@@ -3244,7 +3252,7 @@ class PathNode extends TPathNode {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
- * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
@@ -3258,24 +3266,16 @@ class PathNode extends TPathNode {
/** Gets the associated configuration. */
Configuration getConfiguration() { none() }
- private predicate isHidden() {
- hiddenNode(this.(PathNodeImpl).getNodeEx().asNode()) and
- not this.isSource() and
- not this instanceof PathNodeSink
- or
- this.(PathNodeImpl).getNodeEx() instanceof TNodeImplicitRead
- }
-
private PathNode getASuccessorIfHidden() {
- this.isHidden() and
+ this.(PathNodeImpl).isHidden() and
result = this.(PathNodeImpl).getASuccessorImpl()
}
/** Gets a successor of this node, if any. */
final PathNode getASuccessor() {
result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and
- not this.isHidden() and
- not result.isHidden()
+ not this.(PathNodeImpl).isHidden() and
+ not result.(PathNodeImpl).isHidden()
}
/** Holds if this node is a source. */
@@ -3287,6 +3287,14 @@ abstract private class PathNodeImpl extends PathNode {
abstract NodeEx getNodeEx();
+ predicate isHidden() {
+ hiddenNode(this.getNodeEx().asNode()) and
+ not this.isSource() and
+ not this instanceof PathNodeSink
+ or
+ this.getNodeEx() instanceof TNodeImplicitRead
+ }
+
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
@@ -3313,10 +3321,15 @@ abstract private class PathNodeImpl extends PathNode {
}
/** Holds if `n` can reach a sink. */
-private predicate reach(PathNode n) { n instanceof PathNodeSink or reach(n.getASuccessor()) }
+private predicate directReach(PathNode n) {
+ n instanceof PathNodeSink or directReach(n.getASuccessor())
+}
-/** Holds if `n1.getSucc() = n2` and `n2` can reach a sink. */
-private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and reach(n2) }
+/** Holds if `n` can reach a sink or is used in a subpath. */
+private predicate reach(PathNode n) { directReach(n) or Subpaths::retReach(n) }
+
+/** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */
+private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and directReach(n2) }
private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2)
@@ -3325,12 +3338,14 @@ private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1
*/
module PathGraph {
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
- query predicate edges(PathNode a, PathNode b) { pathSucc(a, b) }
+ query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b and reach(b) }
/** Holds if `n` is a node in the graph of data flow path explanations. */
query predicate nodes(PathNode n, string key, string val) {
reach(n) and key = "semmle.label" and val = n.toString()
}
+
+ query predicate subpaths = Subpaths::subpaths/4;
}
/**
@@ -3622,6 +3637,87 @@ private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext c
)
}
+private module Subpaths {
+ /**
+ * Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by
+ * `kind`, `sc`, `apout`, and `innercc`.
+ */
+ pragma[nomagic]
+ private predicate subpaths01(
+ PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
+ NodeEx out, AccessPath apout
+ ) {
+ pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and
+ pathIntoCallable(arg, par, _, innercc, sc, _) and
+ paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _,
+ unbindConf(arg.getConfiguration()))
+ }
+
+ /**
+ * Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by
+ * `kind`, `sc`, `apout`, and `innercc`.
+ */
+ pragma[nomagic]
+ private predicate subpaths02(
+ PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
+ NodeEx out, AccessPath apout
+ ) {
+ subpaths01(arg, par, sc, innercc, kind, out, apout) and
+ out.asNode() = kind.getAnOutNode(_)
+ }
+
+ pragma[nomagic]
+ private Configuration getPathNodeConf(PathNode n) { result = n.getConfiguration() }
+
+ /**
+ * Holds if `(arg, par, ret, out)` forms a subpath-tuple.
+ */
+ pragma[nomagic]
+ private predicate subpaths03(
+ PathNode arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, AccessPath apout
+ ) {
+ exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode |
+ subpaths02(arg, par, sc, innercc, kind, out, apout) and
+ ret.getNodeEx() = retnode and
+ kind = retnode.getKind() and
+ innercc = ret.getCallContext() and
+ sc = ret.getSummaryCtx() and
+ ret.getConfiguration() = unbindConf(getPathNodeConf(arg)) and
+ apout = ret.getAp() and
+ not ret.isHidden()
+ )
+ }
+
+ /**
+ * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through
+ * a subpath between `par` and `ret` with the connecting edges `arg -> par` and
+ * `ret -> out` is summarized as the edge `arg -> out`.
+ */
+ predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeMid ret, PathNodeMid out) {
+ exists(ParamNodeEx p, NodeEx o, AccessPath apout |
+ pragma[only_bind_into](arg).getASuccessor() = par and
+ pragma[only_bind_into](arg).getASuccessor() = out and
+ subpaths03(arg, p, ret, o, apout) and
+ par.getNodeEx() = p and
+ out.getNodeEx() = o and
+ out.getAp() = apout
+ )
+ }
+
+ /**
+ * Holds if `n` can reach a return node in a summarized subpath.
+ */
+ predicate retReach(PathNode n) {
+ subpaths(_, _, n, _)
+ or
+ exists(PathNode mid |
+ retReach(mid) and
+ n.getASuccessor() = mid and
+ not subpaths(_, mid, _, _)
+ )
+ }
+}
+
/**
* Holds if data can flow (inter-procedurally) from `source` to `sink`.
*
@@ -3941,7 +4037,7 @@ private module FlowExploration {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
- * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
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 5c2dbb30084..4ca06c93362 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
@@ -923,28 +923,29 @@ private module Stage2 {
ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
- class Cc = boolean;
+ class Cc = CallContext;
- class CcCall extends Cc {
- CcCall() { this = true }
+ class CcCall = CallContextCall;
- /** Holds if this call context may be `call`. */
- predicate matchesCall(DataFlowCall call) { any() }
- }
+ class CcNoCall = CallContextNoCall;
- class CcNoCall extends Cc {
- CcNoCall() { this = false }
- }
-
- Cc ccNone() { result = false }
+ Cc ccNone() { result instanceof CallContextAny }
private class LocalCc = Unit;
bindingset[call, c, outercc]
- private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
+ 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[call, c, innercc]
- private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
+ 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, config]
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
@@ -1172,7 +1173,8 @@ private module Stage2 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
- fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ 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))
)
}
@@ -1860,7 +1862,8 @@ private module Stage3 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
- fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ 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))
)
}
@@ -2117,7 +2120,7 @@ private module Stage3 {
private predicate flowCandSummaryCtx(NodeEx node, AccessPathFront argApf, Configuration config) {
exists(AccessPathFront apf |
Stage3::revFlow(node, true, _, apf, config) and
- Stage3::fwdFlow(node, true, TAccessPathFrontSome(argApf), apf, config)
+ Stage3::fwdFlow(node, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
)
}
@@ -2136,7 +2139,8 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
) and
accessPathApproxCostLimits(apLimit, tupleLimit) and
apLimit < tails and
- tupleLimit < (tails - 1) * nodes
+ tupleLimit < (tails - 1) * nodes and
+ not tc.forceHighPrecision()
)
}
@@ -2618,7 +2622,8 @@ private module Stage4 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
- fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ 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))
)
}
@@ -2969,12 +2974,15 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) {
* expected to be expensive. Holds with `unfold = true` otherwise.
*/
private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) {
- exists(int aps, int nodes, int apLimit, int tupleLimit |
- aps = countPotentialAps(apa, config) and
- nodes = countNodesUsingAccessPath(apa, config) and
- accessPathCostLimits(apLimit, tupleLimit) and
- if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true
- )
+ if apa.getHead().forceHighPrecision()
+ then unfold = true
+ else
+ exists(int aps, int nodes, int apLimit, int tupleLimit |
+ aps = countPotentialAps(apa, config) and
+ nodes = countNodesUsingAccessPath(apa, config) and
+ accessPathCostLimits(apLimit, tupleLimit) and
+ if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true
+ )
}
/**
@@ -3244,7 +3252,7 @@ class PathNode extends TPathNode {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
- * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
@@ -3258,24 +3266,16 @@ class PathNode extends TPathNode {
/** Gets the associated configuration. */
Configuration getConfiguration() { none() }
- private predicate isHidden() {
- hiddenNode(this.(PathNodeImpl).getNodeEx().asNode()) and
- not this.isSource() and
- not this instanceof PathNodeSink
- or
- this.(PathNodeImpl).getNodeEx() instanceof TNodeImplicitRead
- }
-
private PathNode getASuccessorIfHidden() {
- this.isHidden() and
+ this.(PathNodeImpl).isHidden() and
result = this.(PathNodeImpl).getASuccessorImpl()
}
/** Gets a successor of this node, if any. */
final PathNode getASuccessor() {
result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and
- not this.isHidden() and
- not result.isHidden()
+ not this.(PathNodeImpl).isHidden() and
+ not result.(PathNodeImpl).isHidden()
}
/** Holds if this node is a source. */
@@ -3287,6 +3287,14 @@ abstract private class PathNodeImpl extends PathNode {
abstract NodeEx getNodeEx();
+ predicate isHidden() {
+ hiddenNode(this.getNodeEx().asNode()) and
+ not this.isSource() and
+ not this instanceof PathNodeSink
+ or
+ this.getNodeEx() instanceof TNodeImplicitRead
+ }
+
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
@@ -3313,10 +3321,15 @@ abstract private class PathNodeImpl extends PathNode {
}
/** Holds if `n` can reach a sink. */
-private predicate reach(PathNode n) { n instanceof PathNodeSink or reach(n.getASuccessor()) }
+private predicate directReach(PathNode n) {
+ n instanceof PathNodeSink or directReach(n.getASuccessor())
+}
-/** Holds if `n1.getSucc() = n2` and `n2` can reach a sink. */
-private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and reach(n2) }
+/** Holds if `n` can reach a sink or is used in a subpath. */
+private predicate reach(PathNode n) { directReach(n) or Subpaths::retReach(n) }
+
+/** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */
+private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and directReach(n2) }
private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2)
@@ -3325,12 +3338,14 @@ private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1
*/
module PathGraph {
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
- query predicate edges(PathNode a, PathNode b) { pathSucc(a, b) }
+ query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b and reach(b) }
/** Holds if `n` is a node in the graph of data flow path explanations. */
query predicate nodes(PathNode n, string key, string val) {
reach(n) and key = "semmle.label" and val = n.toString()
}
+
+ query predicate subpaths = Subpaths::subpaths/4;
}
/**
@@ -3622,6 +3637,87 @@ private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext c
)
}
+private module Subpaths {
+ /**
+ * Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by
+ * `kind`, `sc`, `apout`, and `innercc`.
+ */
+ pragma[nomagic]
+ private predicate subpaths01(
+ PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
+ NodeEx out, AccessPath apout
+ ) {
+ pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and
+ pathIntoCallable(arg, par, _, innercc, sc, _) and
+ paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _,
+ unbindConf(arg.getConfiguration()))
+ }
+
+ /**
+ * Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by
+ * `kind`, `sc`, `apout`, and `innercc`.
+ */
+ pragma[nomagic]
+ private predicate subpaths02(
+ PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
+ NodeEx out, AccessPath apout
+ ) {
+ subpaths01(arg, par, sc, innercc, kind, out, apout) and
+ out.asNode() = kind.getAnOutNode(_)
+ }
+
+ pragma[nomagic]
+ private Configuration getPathNodeConf(PathNode n) { result = n.getConfiguration() }
+
+ /**
+ * Holds if `(arg, par, ret, out)` forms a subpath-tuple.
+ */
+ pragma[nomagic]
+ private predicate subpaths03(
+ PathNode arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, AccessPath apout
+ ) {
+ exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode |
+ subpaths02(arg, par, sc, innercc, kind, out, apout) and
+ ret.getNodeEx() = retnode and
+ kind = retnode.getKind() and
+ innercc = ret.getCallContext() and
+ sc = ret.getSummaryCtx() and
+ ret.getConfiguration() = unbindConf(getPathNodeConf(arg)) and
+ apout = ret.getAp() and
+ not ret.isHidden()
+ )
+ }
+
+ /**
+ * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through
+ * a subpath between `par` and `ret` with the connecting edges `arg -> par` and
+ * `ret -> out` is summarized as the edge `arg -> out`.
+ */
+ predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeMid ret, PathNodeMid out) {
+ exists(ParamNodeEx p, NodeEx o, AccessPath apout |
+ pragma[only_bind_into](arg).getASuccessor() = par and
+ pragma[only_bind_into](arg).getASuccessor() = out and
+ subpaths03(arg, p, ret, o, apout) and
+ par.getNodeEx() = p and
+ out.getNodeEx() = o and
+ out.getAp() = apout
+ )
+ }
+
+ /**
+ * Holds if `n` can reach a return node in a summarized subpath.
+ */
+ predicate retReach(PathNode n) {
+ subpaths(_, _, n, _)
+ or
+ exists(PathNode mid |
+ retReach(mid) and
+ n.getASuccessor() = mid and
+ not subpaths(_, mid, _, _)
+ )
+ }
+}
+
/**
* Holds if data can flow (inter-procedurally) from `source` to `sink`.
*
@@ -3941,7 +4037,7 @@ private module FlowExploration {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
- * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
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 5c2dbb30084..4ca06c93362 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
@@ -923,28 +923,29 @@ private module Stage2 {
ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
- class Cc = boolean;
+ class Cc = CallContext;
- class CcCall extends Cc {
- CcCall() { this = true }
+ class CcCall = CallContextCall;
- /** Holds if this call context may be `call`. */
- predicate matchesCall(DataFlowCall call) { any() }
- }
+ class CcNoCall = CallContextNoCall;
- class CcNoCall extends Cc {
- CcNoCall() { this = false }
- }
-
- Cc ccNone() { result = false }
+ Cc ccNone() { result instanceof CallContextAny }
private class LocalCc = Unit;
bindingset[call, c, outercc]
- private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
+ 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[call, c, innercc]
- private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
+ 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, config]
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
@@ -1172,7 +1173,8 @@ private module Stage2 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
- fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ 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))
)
}
@@ -1860,7 +1862,8 @@ private module Stage3 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
- fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ 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))
)
}
@@ -2117,7 +2120,7 @@ private module Stage3 {
private predicate flowCandSummaryCtx(NodeEx node, AccessPathFront argApf, Configuration config) {
exists(AccessPathFront apf |
Stage3::revFlow(node, true, _, apf, config) and
- Stage3::fwdFlow(node, true, TAccessPathFrontSome(argApf), apf, config)
+ Stage3::fwdFlow(node, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
)
}
@@ -2136,7 +2139,8 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
) and
accessPathApproxCostLimits(apLimit, tupleLimit) and
apLimit < tails and
- tupleLimit < (tails - 1) * nodes
+ tupleLimit < (tails - 1) * nodes and
+ not tc.forceHighPrecision()
)
}
@@ -2618,7 +2622,8 @@ private module Stage4 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
- fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ 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))
)
}
@@ -2969,12 +2974,15 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) {
* expected to be expensive. Holds with `unfold = true` otherwise.
*/
private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) {
- exists(int aps, int nodes, int apLimit, int tupleLimit |
- aps = countPotentialAps(apa, config) and
- nodes = countNodesUsingAccessPath(apa, config) and
- accessPathCostLimits(apLimit, tupleLimit) and
- if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true
- )
+ if apa.getHead().forceHighPrecision()
+ then unfold = true
+ else
+ exists(int aps, int nodes, int apLimit, int tupleLimit |
+ aps = countPotentialAps(apa, config) and
+ nodes = countNodesUsingAccessPath(apa, config) and
+ accessPathCostLimits(apLimit, tupleLimit) and
+ if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true
+ )
}
/**
@@ -3244,7 +3252,7 @@ class PathNode extends TPathNode {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
- * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
@@ -3258,24 +3266,16 @@ class PathNode extends TPathNode {
/** Gets the associated configuration. */
Configuration getConfiguration() { none() }
- private predicate isHidden() {
- hiddenNode(this.(PathNodeImpl).getNodeEx().asNode()) and
- not this.isSource() and
- not this instanceof PathNodeSink
- or
- this.(PathNodeImpl).getNodeEx() instanceof TNodeImplicitRead
- }
-
private PathNode getASuccessorIfHidden() {
- this.isHidden() and
+ this.(PathNodeImpl).isHidden() and
result = this.(PathNodeImpl).getASuccessorImpl()
}
/** Gets a successor of this node, if any. */
final PathNode getASuccessor() {
result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and
- not this.isHidden() and
- not result.isHidden()
+ not this.(PathNodeImpl).isHidden() and
+ not result.(PathNodeImpl).isHidden()
}
/** Holds if this node is a source. */
@@ -3287,6 +3287,14 @@ abstract private class PathNodeImpl extends PathNode {
abstract NodeEx getNodeEx();
+ predicate isHidden() {
+ hiddenNode(this.getNodeEx().asNode()) and
+ not this.isSource() and
+ not this instanceof PathNodeSink
+ or
+ this.getNodeEx() instanceof TNodeImplicitRead
+ }
+
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
@@ -3313,10 +3321,15 @@ abstract private class PathNodeImpl extends PathNode {
}
/** Holds if `n` can reach a sink. */
-private predicate reach(PathNode n) { n instanceof PathNodeSink or reach(n.getASuccessor()) }
+private predicate directReach(PathNode n) {
+ n instanceof PathNodeSink or directReach(n.getASuccessor())
+}
-/** Holds if `n1.getSucc() = n2` and `n2` can reach a sink. */
-private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and reach(n2) }
+/** Holds if `n` can reach a sink or is used in a subpath. */
+private predicate reach(PathNode n) { directReach(n) or Subpaths::retReach(n) }
+
+/** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */
+private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and directReach(n2) }
private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2)
@@ -3325,12 +3338,14 @@ private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1
*/
module PathGraph {
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
- query predicate edges(PathNode a, PathNode b) { pathSucc(a, b) }
+ query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b and reach(b) }
/** Holds if `n` is a node in the graph of data flow path explanations. */
query predicate nodes(PathNode n, string key, string val) {
reach(n) and key = "semmle.label" and val = n.toString()
}
+
+ query predicate subpaths = Subpaths::subpaths/4;
}
/**
@@ -3622,6 +3637,87 @@ private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext c
)
}
+private module Subpaths {
+ /**
+ * Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by
+ * `kind`, `sc`, `apout`, and `innercc`.
+ */
+ pragma[nomagic]
+ private predicate subpaths01(
+ PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
+ NodeEx out, AccessPath apout
+ ) {
+ pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and
+ pathIntoCallable(arg, par, _, innercc, sc, _) and
+ paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _,
+ unbindConf(arg.getConfiguration()))
+ }
+
+ /**
+ * Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by
+ * `kind`, `sc`, `apout`, and `innercc`.
+ */
+ pragma[nomagic]
+ private predicate subpaths02(
+ PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
+ NodeEx out, AccessPath apout
+ ) {
+ subpaths01(arg, par, sc, innercc, kind, out, apout) and
+ out.asNode() = kind.getAnOutNode(_)
+ }
+
+ pragma[nomagic]
+ private Configuration getPathNodeConf(PathNode n) { result = n.getConfiguration() }
+
+ /**
+ * Holds if `(arg, par, ret, out)` forms a subpath-tuple.
+ */
+ pragma[nomagic]
+ private predicate subpaths03(
+ PathNode arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, AccessPath apout
+ ) {
+ exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode |
+ subpaths02(arg, par, sc, innercc, kind, out, apout) and
+ ret.getNodeEx() = retnode and
+ kind = retnode.getKind() and
+ innercc = ret.getCallContext() and
+ sc = ret.getSummaryCtx() and
+ ret.getConfiguration() = unbindConf(getPathNodeConf(arg)) and
+ apout = ret.getAp() and
+ not ret.isHidden()
+ )
+ }
+
+ /**
+ * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through
+ * a subpath between `par` and `ret` with the connecting edges `arg -> par` and
+ * `ret -> out` is summarized as the edge `arg -> out`.
+ */
+ predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeMid ret, PathNodeMid out) {
+ exists(ParamNodeEx p, NodeEx o, AccessPath apout |
+ pragma[only_bind_into](arg).getASuccessor() = par and
+ pragma[only_bind_into](arg).getASuccessor() = out and
+ subpaths03(arg, p, ret, o, apout) and
+ par.getNodeEx() = p and
+ out.getNodeEx() = o and
+ out.getAp() = apout
+ )
+ }
+
+ /**
+ * Holds if `n` can reach a return node in a summarized subpath.
+ */
+ predicate retReach(PathNode n) {
+ subpaths(_, _, n, _)
+ or
+ exists(PathNode mid |
+ retReach(mid) and
+ n.getASuccessor() = mid and
+ not subpaths(_, mid, _, _)
+ )
+ }
+}
+
/**
* Holds if data can flow (inter-procedurally) from `source` to `sink`.
*
@@ -3941,7 +4037,7 @@ private module FlowExploration {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
- * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll
index 728f7b56c42..f43a550af57 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll
@@ -786,13 +786,18 @@ private module Cached {
}
/**
- * Holds if the call context `call` either improves virtual dispatch in
- * `callable` or if it allows us to prune unreachable nodes in `callable`.
+ * Holds if the call context `call` improves virtual dispatch in `callable`.
*/
cached
- predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) {
+ predicate recordDataFlowCallSiteDispatch(DataFlowCall call, DataFlowCallable callable) {
reducedViableImplInCallContext(_, callable, call)
- or
+ }
+
+ /**
+ * Holds if the call context `call` allows us to prune unreachable nodes in `callable`.
+ */
+ cached
+ predicate recordDataFlowCallSiteUnreachable(DataFlowCall call, DataFlowCallable callable) {
exists(Node n | getNodeEnclosingCallable(n) = callable | isUnreachableInCallCached(n, call))
}
@@ -846,6 +851,15 @@ private module Cached {
TAccessPathFrontSome(AccessPathFront apf)
}
+/**
+ * Holds if the call context `call` either improves virtual dispatch in
+ * `callable` or if it allows us to prune unreachable nodes in `callable`.
+ */
+predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) {
+ recordDataFlowCallSiteDispatch(call, callable) or
+ recordDataFlowCallSiteUnreachable(call, callable)
+}
+
/**
* A `Node` at which a cast can occur such that the type should be checked.
*/
@@ -1222,6 +1236,13 @@ class TypedContent extends MkTypedContent {
/** Gets a textual representation of this content. */
string toString() { result = c.toString() }
+
+ /**
+ * Holds if access paths with this `TypedContent` at their head always should
+ * be tracked at high precision. This disables adaptive access path precision
+ * for such access paths.
+ */
+ predicate forceHighPrecision() { forceHighPrecision(c) }
}
/**
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 73bf72a3643..00996a6ebfc 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
@@ -466,6 +466,12 @@ predicate isUnreachableInCall(Node n, DataFlowCall call) { none() } // stub impl
int accessPathLimit() { result = 5 }
+/**
+ * Holds if access paths with `c` at their head always should be tracked at high
+ * precision. This disables adaptive access path precision for such access paths.
+ */
+predicate forceHighPrecision(Content c) { none() }
+
/** The unit type. */
private newtype TUnit = TMkUnit()
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 bf21249d4ca..9e7a95e010d 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
@@ -120,7 +120,7 @@ class Node extends TIRDataFlowNode {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
- * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/ModelUtil.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/ModelUtil.qll
index 2f4037d4ec8..c7e61ea2e33 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/ModelUtil.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/ModelUtil.qll
@@ -38,5 +38,8 @@ Instruction callOutput(CallInstruction call, FunctionOutput output) {
effect.getPrimaryInstruction() = call and
output.isParameterDerefOrQualifierObject(effect.getIndex())
)
- // TODO: return value dereference
+ or
+ // TODO: modify this when we get return value dereferences
+ result = call and
+ output.isReturnValueDeref()
}
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 453838215ff..6f471d8a7e8 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
@@ -821,7 +821,7 @@ class ReturnIndirectionInstruction extends VariableInstruction {
*
* There are several different copy instructions, depending on the source and destination of the
* copy operation:
- * - `CopyInstruction` - Copies a register operand to a register result.
+ * - `CopyValueInstruction` - Copies a register operand to a register result.
* - `LoadInstruction` - Copies a memory operand to a register result.
* - `StoreInstruction` - Copies a register operand to a memory result.
*/
@@ -1856,12 +1856,12 @@ class InitializeDynamicAllocationInstruction extends SideEffectInstruction {
}
/**
- * Gets the address of the allocation this instruction is initializing.
+ * Gets the operand that represents the address of the allocation this instruction is initializing.
*/
final AddressOperand getAllocationAddressOperand() { result = getAnOperand() }
/**
- * Gets the operand for the allocation this instruction is initializing.
+ * Gets the address for the allocation this instruction is initializing.
*/
final Instruction getAllocationAddress() { result = getAllocationAddressOperand().getDef() }
}
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 453838215ff..6f471d8a7e8 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
@@ -821,7 +821,7 @@ class ReturnIndirectionInstruction extends VariableInstruction {
*
* There are several different copy instructions, depending on the source and destination of the
* copy operation:
- * - `CopyInstruction` - Copies a register operand to a register result.
+ * - `CopyValueInstruction` - Copies a register operand to a register result.
* - `LoadInstruction` - Copies a memory operand to a register result.
* - `StoreInstruction` - Copies a register operand to a memory result.
*/
@@ -1856,12 +1856,12 @@ class InitializeDynamicAllocationInstruction extends SideEffectInstruction {
}
/**
- * Gets the address of the allocation this instruction is initializing.
+ * Gets the operand that represents the address of the allocation this instruction is initializing.
*/
final AddressOperand getAllocationAddressOperand() { result = getAnOperand() }
/**
- * Gets the operand for the allocation this instruction is initializing.
+ * Gets the address for the allocation this instruction is initializing.
*/
final Instruction getAllocationAddress() { result = getAllocationAddressOperand().getDef() }
}
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 453838215ff..6f471d8a7e8 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
@@ -821,7 +821,7 @@ class ReturnIndirectionInstruction extends VariableInstruction {
*
* There are several different copy instructions, depending on the source and destination of the
* copy operation:
- * - `CopyInstruction` - Copies a register operand to a register result.
+ * - `CopyValueInstruction` - Copies a register operand to a register result.
* - `LoadInstruction` - Copies a memory operand to a register result.
* - `StoreInstruction` - Copies a register operand to a memory result.
*/
@@ -1856,12 +1856,12 @@ class InitializeDynamicAllocationInstruction extends SideEffectInstruction {
}
/**
- * Gets the address of the allocation this instruction is initializing.
+ * Gets the operand that represents the address of the allocation this instruction is initializing.
*/
final AddressOperand getAllocationAddressOperand() { result = getAnOperand() }
/**
- * Gets the operand for the allocation this instruction is initializing.
+ * Gets the address for the allocation this instruction is initializing.
*/
final Instruction getAllocationAddress() { result = getAllocationAddressOperand().getDef() }
}
diff --git a/cpp/ql/lib/semmle/code/cpp/models/Models.qll b/cpp/ql/lib/semmle/code/cpp/models/Models.qll
index d5c7f50dde1..3eed4341cce 100644
--- a/cpp/ql/lib/semmle/code/cpp/models/Models.qll
+++ b/cpp/ql/lib/semmle/code/cpp/models/Models.qll
@@ -33,3 +33,7 @@ private import implementations.Recv
private import implementations.Accept
private import implementations.Poll
private import implementations.Select
+private import implementations.MySql
+private import implementations.SqLite3
+private import implementations.PostgreSql
+private import implementations.System
diff --git a/cpp/ql/lib/semmle/code/cpp/models/implementations/MySql.qll b/cpp/ql/lib/semmle/code/cpp/models/implementations/MySql.qll
new file mode 100644
index 00000000000..ca5d7020158
--- /dev/null
+++ b/cpp/ql/lib/semmle/code/cpp/models/implementations/MySql.qll
@@ -0,0 +1,32 @@
+/**
+ * Provides implementation classes modeling the MySql C API.
+ * See `semmle.code.cpp.models.Models` for usage information.
+ */
+
+private import semmle.code.cpp.models.interfaces.Sql
+private import semmle.code.cpp.models.interfaces.FunctionInputsAndOutputs
+
+/**
+ * The `mysql_query` family of functions from the MySQL C API.
+ */
+private class MySqlExecutionFunction extends SqlExecutionFunction {
+ MySqlExecutionFunction() {
+ this.hasName(["mysql_query", "mysql_real_query", "mysql_real_query_nonblocking"])
+ }
+
+ override predicate hasSqlArgument(FunctionInput input) { input.isParameterDeref(1) }
+}
+
+/**
+ * The `mysql_real_escape_string` family of functions from the MySQL C API.
+ */
+private class MySqlBarrierFunction extends SqlBarrierFunction {
+ MySqlBarrierFunction() {
+ this.hasName(["mysql_real_escape_string", "mysql_real_escape_string_quote"])
+ }
+
+ override predicate barrierSqlArgument(FunctionInput input, FunctionOutput output) {
+ input.isParameterDeref(2) and
+ output.isParameterDeref(1)
+ }
+}
diff --git a/cpp/ql/lib/semmle/code/cpp/models/implementations/PostgreSql.qll b/cpp/ql/lib/semmle/code/cpp/models/implementations/PostgreSql.qll
new file mode 100644
index 00000000000..595805f176f
--- /dev/null
+++ b/cpp/ql/lib/semmle/code/cpp/models/implementations/PostgreSql.qll
@@ -0,0 +1,94 @@
+private import semmle.code.cpp.models.interfaces.Sql
+private import semmle.code.cpp.models.interfaces.FunctionInputsAndOutputs
+
+private predicate pqxxTransactionSqlArgument(string function, int arg) {
+ function = "exec" and arg = 0
+ or
+ function = "exec0" and arg = 0
+ or
+ function = "exec1" and arg = 0
+ or
+ function = "exec_n" and arg = 1
+ or
+ function = "exec_params" and arg = 0
+ or
+ function = "exec_params0" and arg = 0
+ or
+ function = "exec_params1" and arg = 0
+ or
+ function = "exec_params_n" and arg = 1
+ or
+ function = "query_value" and arg = 0
+ or
+ function = "stream" and arg = 0
+}
+
+private predicate pqxxConnectionSqlArgument(string function, int arg) {
+ function = "prepare" and arg = 1
+}
+
+private predicate pqxxTransationClassNames(string className, string namespace) {
+ namespace = "pqxx" and
+ className in [
+ "dbtransaction", "nontransaction", "basic_robusttransaction", "robusttransaction",
+ "subtransaction", "transaction", "basic_transaction", "transaction_base", "work"
+ ]
+}
+
+private predicate pqxxConnectionClassNames(string className, string namespace) {
+ namespace = "pqxx" and
+ className in ["connection_base", "basic_connection", "connection"]
+}
+
+private predicate pqxxEscapeArgument(string function, int arg) {
+ arg = 0 and
+ function in ["esc", "esc_raw", "quote", "quote_raw", "quote_name", "quote_table", "esc_like"]
+}
+
+private class PostgreSqlExecutionFunction extends SqlExecutionFunction {
+ PostgreSqlExecutionFunction() {
+ exists(Class c |
+ this.getDeclaringType() = c and
+ // transaction exec and connection prepare variations
+ (
+ pqxxTransationClassNames(c.getName(), c.getNamespace().getName()) and
+ pqxxTransactionSqlArgument(this.getName(), _)
+ or
+ pqxxConnectionSqlArgument(this.getName(), _) and
+ pqxxConnectionClassNames(c.getName(), c.getNamespace().getName())
+ )
+ )
+ }
+
+ override predicate hasSqlArgument(FunctionInput input) {
+ exists(int argIndex |
+ pqxxTransactionSqlArgument(this.getName(), argIndex)
+ or
+ pqxxConnectionSqlArgument(this.getName(), argIndex)
+ |
+ input.isParameterDeref(argIndex)
+ )
+ }
+}
+
+private class PostgreSqlBarrierFunction extends SqlBarrierFunction {
+ PostgreSqlBarrierFunction() {
+ exists(Class c |
+ this.getDeclaringType() = c and
+ // transaction and connection escape functions
+ (
+ pqxxTransationClassNames(c.getName(), c.getNamespace().getName()) or
+ pqxxConnectionClassNames(c.getName(), c.getNamespace().getName())
+ ) and
+ pqxxEscapeArgument(this.getName(), _)
+ )
+ }
+
+ override predicate barrierSqlArgument(FunctionInput input, FunctionOutput output) {
+ exists(int argIndex |
+ input.isParameterDeref(argIndex) and
+ output.isReturnValueDeref() and
+ pqxxEscapeArgument(this.getName(), argIndex)
+ )
+ }
+}
diff --git a/cpp/ql/lib/semmle/code/cpp/models/implementations/Recv.qll b/cpp/ql/lib/semmle/code/cpp/models/implementations/Recv.qll
index 691ba528f42..0551185ba14 100644
--- a/cpp/ql/lib/semmle/code/cpp/models/implementations/Recv.qll
+++ b/cpp/ql/lib/semmle/code/cpp/models/implementations/Recv.qll
@@ -85,4 +85,6 @@ private class Recv extends AliasFunction, ArrayFunction, SideEffectFunction,
) and
description = "Buffer read by " + this.getName()
}
+
+ override predicate hasSocketInput(FunctionInput input) { input.isParameter(0) }
}
diff --git a/cpp/ql/lib/semmle/code/cpp/models/implementations/Send.qll b/cpp/ql/lib/semmle/code/cpp/models/implementations/Send.qll
index 6086bc7748f..d871bad68af 100644
--- a/cpp/ql/lib/semmle/code/cpp/models/implementations/Send.qll
+++ b/cpp/ql/lib/semmle/code/cpp/models/implementations/Send.qll
@@ -60,4 +60,6 @@ private class Send extends AliasFunction, ArrayFunction, SideEffectFunction, Rem
override predicate hasRemoteFlowSink(FunctionInput input, string description) {
input.isParameterDeref(1) and description = "Buffer sent by " + this.getName()
}
+
+ override predicate hasSocketInput(FunctionInput input) { input.isParameter(0) }
}
diff --git a/cpp/ql/lib/semmle/code/cpp/models/implementations/SqLite3.qll b/cpp/ql/lib/semmle/code/cpp/models/implementations/SqLite3.qll
new file mode 100644
index 00000000000..d65df9a27ed
--- /dev/null
+++ b/cpp/ql/lib/semmle/code/cpp/models/implementations/SqLite3.qll
@@ -0,0 +1,21 @@
+/**
+ * Provides implementation classes modeling the SQLite C API.
+ * See `semmle.code.cpp.models.Models` for usage information.
+ */
+
+private import semmle.code.cpp.models.interfaces.Sql
+private import semmle.code.cpp.models.interfaces.FunctionInputsAndOutputs
+
+/**
+ * The `sqlite3_exec` and `sqlite3_prepare` families of functions from the SQLite C API.
+ */
+private class SqLite3ExecutionFunction extends SqlExecutionFunction {
+ SqLite3ExecutionFunction() {
+ this.hasName([
+ "sqlite3_exec", "sqlite3_prepare", "sqlite3_prepare_v2", "sqlite3_prepare_v3",
+ "sqlite3_prepare16", "sqlite3_prepare16_v2", "sqlite3_prepare16_v3"
+ ])
+ }
+
+ override predicate hasSqlArgument(FunctionInput input) { input.isParameterDeref(1) }
+}
diff --git a/cpp/ql/lib/semmle/code/cpp/models/implementations/System.qll b/cpp/ql/lib/semmle/code/cpp/models/implementations/System.qll
new file mode 100644
index 00000000000..02a9d0d6744
--- /dev/null
+++ b/cpp/ql/lib/semmle/code/cpp/models/implementations/System.qll
@@ -0,0 +1,43 @@
+import cpp
+import semmle.code.cpp.models.interfaces.SideEffect
+import semmle.code.cpp.models.interfaces.Alias
+import semmle.code.cpp.models.interfaces.CommandExecution
+
+/**
+ * A function for running a command using a command interpreter.
+ */
+private class SystemFunction extends CommandExecutionFunction, ArrayFunction, AliasFunction,
+ SideEffectFunction {
+ SystemFunction() {
+ hasGlobalOrStdName("system") or // system(command)
+ hasGlobalName("popen") or // popen(command, mode)
+ // Windows variants
+ hasGlobalName("_popen") or // _popen(command, mode)
+ hasGlobalName("_wpopen") or // _wpopen(command, mode)
+ hasGlobalName("_wsystem") // _wsystem(command)
+ }
+
+ override predicate hasCommandArgument(FunctionInput input) { input.isParameterDeref(0) }
+
+ override predicate hasArrayWithNullTerminator(int bufParam) { bufParam = 0 or bufParam = 1 }
+
+ override predicate hasArrayInput(int bufParam) { bufParam = 0 or bufParam = 1 }
+
+ override predicate parameterNeverEscapes(int index) { index = 0 or index = 1 }
+
+ override predicate parameterEscapesOnlyViaReturn(int index) { none() }
+
+ override predicate parameterIsAlwaysReturned(int index) { none() }
+
+ override predicate hasOnlySpecificReadSideEffects() { any() }
+
+ override predicate hasOnlySpecificWriteSideEffects() {
+ hasGlobalOrStdName("system") or
+ hasGlobalName("_wsystem")
+ }
+
+ override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
+ (i = 0 or i = 1) and
+ buffer = true
+ }
+}
diff --git a/cpp/ql/lib/semmle/code/cpp/models/interfaces/CommandExecution.qll b/cpp/ql/lib/semmle/code/cpp/models/interfaces/CommandExecution.qll
new file mode 100644
index 00000000000..a6e32341140
--- /dev/null
+++ b/cpp/ql/lib/semmle/code/cpp/models/interfaces/CommandExecution.qll
@@ -0,0 +1,23 @@
+/**
+ * Provides classes for modeling functions that execute new programs by
+ * interpreting string data as shell commands. To use this QL library, create
+ * a QL class extending `CommandExecutionFunction` with a characteristic
+ * predicate that selects the function or set of functions you are modeling.
+ * Within that class, override the `hasCommandArgument` predicate to indicate
+ * which parameters are interpreted as shell commands.
+ */
+
+import cpp
+import FunctionInputsAndOutputs
+import semmle.code.cpp.models.Models
+
+/**
+ * A function, such as `exec` or `popen` that starts a new process by
+ * interpreting a string as a shell command.
+ */
+abstract class CommandExecutionFunction extends Function {
+ /**
+ * Holds if `input` is interpreted as a shell command.
+ */
+ abstract predicate hasCommandArgument(FunctionInput input);
+}
diff --git a/cpp/ql/lib/semmle/code/cpp/models/interfaces/FlowSource.qll b/cpp/ql/lib/semmle/code/cpp/models/interfaces/FlowSource.qll
index 8c80377c8ec..d454257ea86 100644
--- a/cpp/ql/lib/semmle/code/cpp/models/interfaces/FlowSource.qll
+++ b/cpp/ql/lib/semmle/code/cpp/models/interfaces/FlowSource.qll
@@ -18,6 +18,12 @@ abstract class RemoteFlowSourceFunction extends Function {
* Holds if remote data described by `description` flows from `output` of a call to this function.
*/
abstract predicate hasRemoteFlowSource(FunctionOutput output, string description);
+
+ /**
+ * Holds if remote data from this source comes from a socket described by
+ * `input`. There is no result if a socket is not specified.
+ */
+ predicate hasSocketInput(FunctionInput input) { none() }
}
/**
@@ -51,4 +57,10 @@ abstract class RemoteFlowSinkFunction extends Function {
* send over a network connection.
*/
abstract predicate hasRemoteFlowSink(FunctionInput input, string description);
+
+ /**
+ * Holds if data put into this sink is transmitted through a socket described
+ * by `input`. There is no result if a socket is not specified.
+ */
+ predicate hasSocketInput(FunctionInput input) { none() }
}
diff --git a/cpp/ql/lib/semmle/code/cpp/models/interfaces/Sql.qll b/cpp/ql/lib/semmle/code/cpp/models/interfaces/Sql.qll
new file mode 100644
index 00000000000..7d5111c2488
--- /dev/null
+++ b/cpp/ql/lib/semmle/code/cpp/models/interfaces/Sql.qll
@@ -0,0 +1,30 @@
+/**
+ * Provides abstract classes for modeling functions that execute and escape SQL query strings.
+ * To extend this QL library, create a QL class extending `SqlExecutionFunction` or `SqlEscapeFunction`
+ * with a characteristic predicate that selects the function or set of functions you are modeling.
+ * Within that class, override the predicates provided by the class to match the way a
+ * parameter flows into the function and, in the case of `SqlEscapeFunction`, out of the function.
+ */
+
+private import cpp
+
+/**
+ * An abstract class that represents a function that executes an SQL query.
+ */
+abstract class SqlExecutionFunction extends Function {
+ /**
+ * Holds if `input` to this function represents SQL code to be executed.
+ */
+ abstract predicate hasSqlArgument(FunctionInput input);
+}
+
+/**
+ * An abstract class that represents a function that is a barrier to an SQL query string.
+ */
+abstract class SqlBarrierFunction extends Function {
+ /**
+ * Holds if the `output` is a barrier to the SQL input `input` such that is it safe to pass to
+ * an `SqlExecutionFunction`.
+ */
+ abstract predicate barrierSqlArgument(FunctionInput input, FunctionOutput output);
+}
diff --git a/cpp/ql/lib/semmle/code/cpp/rangeanalysis/NanAnalysis.qll b/cpp/ql/lib/semmle/code/cpp/rangeanalysis/NanAnalysis.qll
index e402b672cbc..d3042a4edc9 100644
--- a/cpp/ql/lib/semmle/code/cpp/rangeanalysis/NanAnalysis.qll
+++ b/cpp/ql/lib/semmle/code/cpp/rangeanalysis/NanAnalysis.qll
@@ -29,8 +29,7 @@ predicate nanExcludingComparison(ComparisonOperation guard, boolean polarity) {
*/
private predicate excludesNan(RangeSsaDefinition def, VariableAccess v) {
exists(VariableAccess inCond, ComparisonOperation guard, boolean branch, StackVariable lsv |
- def.isGuardPhi(inCond, guard, branch) and
- inCond.getTarget() = lsv and
+ def.isGuardPhi(lsv, inCond, guard, branch) and
v = def.getAUse(lsv) and
guard.getAnOperand() = inCond and
nanExcludingComparison(guard, branch)
diff --git a/cpp/ql/lib/semmle/code/cpp/rangeanalysis/RangeSSA.qll b/cpp/ql/lib/semmle/code/cpp/rangeanalysis/RangeSSA.qll
index 93dcf989590..bc66d9b2dd0 100644
--- a/cpp/ql/lib/semmle/code/cpp/rangeanalysis/RangeSSA.qll
+++ b/cpp/ql/lib/semmle/code/cpp/rangeanalysis/RangeSSA.qll
@@ -94,11 +94,21 @@ class RangeSsaDefinition extends ControlFlowNodeBase {
predicate isPhiNode(StackVariable v) { exists(RangeSSA x | x.phi_node(v, this.(BasicBlock))) }
/**
+ * DEPRECATED: Use isGuardPhi/4 instead
* If this definition is a phi node corresponding to a guard,
- * then return the variable and the guard.
+ * then return the variable access and the guard.
*/
- predicate isGuardPhi(VariableAccess v, Expr guard, boolean branch) {
- guard_defn(v, guard, this, branch)
+ deprecated predicate isGuardPhi(VariableAccess va, Expr guard, boolean branch) {
+ guard_defn(va, guard, this, branch)
+ }
+
+ /**
+ * If this definition is a phi node corresponding to a guard,
+ * then return the variable guarded, the variable access and the guard.
+ */
+ predicate isGuardPhi(StackVariable v, VariableAccess va, Expr guard, boolean branch) {
+ guard_defn(va, guard, this, branch) and
+ va.getTarget() = v
}
/** Gets the primary location of this definition. */
@@ -133,9 +143,8 @@ class RangeSsaDefinition extends ControlFlowNodeBase {
// below excludes definitions which can only reach guard phi
// nodes by going through the corresponding guard.
not exists(VariableAccess access |
- v = access.getTarget() and
pred.contains(access) and
- this.isGuardPhi(access, _, _)
+ this.isGuardPhi(v, access, _, _)
)
)
}
diff --git a/cpp/ql/lib/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll b/cpp/ql/lib/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll
index 187641559f4..289187d4301 100644
--- a/cpp/ql/lib/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll
+++ b/cpp/ql/lib/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll
@@ -433,10 +433,7 @@ private predicate exprDependsOnDef(Expr e, RangeSsaDefinition srcDef, StackVaria
private predicate phiDependsOnDef(
RangeSsaDefinition phi, StackVariable v, RangeSsaDefinition srcDef, StackVariable srcVar
) {
- exists(VariableAccess access, Expr guard |
- access = v.getAnAccess() and
- phi.isGuardPhi(access, guard, _)
- |
+ exists(VariableAccess access, Expr guard | phi.isGuardPhi(v, access, guard, _) |
exprDependsOnDef(guard.(ComparisonOperation).getAnOperand(), srcDef, srcVar) or
exprDependsOnDef(access, srcDef, srcVar)
)
@@ -1204,8 +1201,7 @@ private float boolConversionUpperBound(Expr expr) {
*/
private float getPhiLowerBounds(StackVariable v, RangeSsaDefinition phi) {
exists(VariableAccess access, Expr guard, boolean branch, float defLB, float guardLB |
- access = v.getAnAccess() and
- phi.isGuardPhi(access, guard, branch) and
+ phi.isGuardPhi(v, access, guard, branch) and
lowerBoundFromGuard(guard, access, guardLB, branch) and
defLB = getFullyConvertedLowerBounds(access)
|
@@ -1230,8 +1226,7 @@ private float getPhiLowerBounds(StackVariable v, RangeSsaDefinition phi) {
/** See comment for `getPhiLowerBounds`, above. */
private float getPhiUpperBounds(StackVariable v, RangeSsaDefinition phi) {
exists(VariableAccess access, Expr guard, boolean branch, float defUB, float guardUB |
- access = v.getAnAccess() and
- phi.isGuardPhi(access, guard, branch) and
+ phi.isGuardPhi(v, access, guard, branch) and
upperBoundFromGuard(guard, access, guardUB, branch) and
defUB = getFullyConvertedUpperBounds(access)
|
@@ -1493,8 +1488,7 @@ private predicate isNEPhi(
exists(
ComparisonOperation cmp, boolean branch, Expr linearExpr, Expr rExpr, float p, float q, float r
|
- access.getTarget() = v and
- phi.isGuardPhi(access, cmp, branch) and
+ phi.isGuardPhi(v, access, cmp, branch) and
eqOpWithSwapAndNegate(cmp, linearExpr, rExpr, false, branch) and
v.getUnspecifiedType() instanceof IntegralOrEnumType and // Float `!=` is too imprecise
r = getValue(rExpr).toFloat() and
@@ -1503,8 +1497,7 @@ private predicate isNEPhi(
)
or
exists(Expr op, boolean branch, Expr linearExpr, float p, float q |
- access.getTarget() = v and
- phi.isGuardPhi(access, op, branch) and
+ phi.isGuardPhi(v, access, op, branch) and
eqZeroWithNegate(op, linearExpr, false, branch) and
v.getUnspecifiedType() instanceof IntegralOrEnumType and // Float `!` is too imprecise
linearAccess(linearExpr, access, p, q) and
@@ -1524,12 +1517,35 @@ private predicate isUnsupportedGuardPhi(Variable v, RangeSsaDefinition phi, Vari
or
eqZeroWithNegate(cmp, _, false, branch)
|
- access.getTarget() = v and
- phi.isGuardPhi(access, cmp, branch) and
+ phi.isGuardPhi(v, access, cmp, branch) and
not isNEPhi(v, phi, access, _)
)
}
+/**
+ * Gets the upper bound of the expression, if the expression is guarded.
+ * An upper bound can only be found, if a guard phi node can be found, and the
+ * expression has only one immediate predecessor.
+ */
+private float getGuardedUpperBound(VariableAccess guardedAccess) {
+ exists(
+ RangeSsaDefinition def, StackVariable v, VariableAccess guardVa, Expr guard, boolean branch
+ |
+ def.isGuardPhi(v, guardVa, guard, branch) and
+ // If the basic block for the variable access being examined has
+ // more than one predecessor, the guard phi node could originate
+ // from one of the predecessors. This is because the guard phi
+ // node is attached to the block at the end of the edge and not on
+ // the actual edge. It is therefore not possible to determine which
+ // edge the guard phi node belongs to. The predicate below ensures
+ // that there is one predecessor, albeit somewhat conservative.
+ exists(unique(BasicBlock b | b = def.(BasicBlock).getAPredecessor())) and
+ guardedAccess = def.getAUse(v) and
+ result = max(float ub | upperBoundFromGuard(guard, guardVa, ub, branch)) and
+ not convertedExprMightOverflow(guard.getAChild+())
+ )
+}
+
cached
private module SimpleRangeAnalysisCached {
/**
@@ -1565,9 +1581,18 @@ private module SimpleRangeAnalysisCached {
*/
cached
float upperBound(Expr expr) {
- // Combine the upper bounds returned by getTruncatedUpperBounds into a
- // single maximum value.
- result = max(float ub | ub = getTruncatedUpperBounds(expr) | ub)
+ // Combine the upper bounds returned by getTruncatedUpperBounds and
+ // getGuardedUpperBound into a single maximum value
+ 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. */
+ cached
+ predicate upperBoundMayBeWidened(Expr e) {
+ isRecursiveExpr(e) and
+ // Widening is not a problem if the post-analysis in `getGuardedUpperBound` has overridden the widening.
+ // Note that the RHS of `<` may be multi-valued.
+ not getGuardedUpperBound(e) < getTruncatedUpperBounds(e)
}
/**
diff --git a/cpp/ql/lib/semmle/code/cpp/security/CommandExecution.qll b/cpp/ql/lib/semmle/code/cpp/security/CommandExecution.qll
index d9bb701be58..f8f7c6c476f 100644
--- a/cpp/ql/lib/semmle/code/cpp/security/CommandExecution.qll
+++ b/cpp/ql/lib/semmle/code/cpp/security/CommandExecution.qll
@@ -4,42 +4,20 @@ import cpp
import semmle.code.cpp.security.FunctionWithWrappers
import semmle.code.cpp.models.interfaces.SideEffect
import semmle.code.cpp.models.interfaces.Alias
+import semmle.code.cpp.models.interfaces.CommandExecution
/**
* A function for running a command using a command interpreter.
*/
-class SystemFunction extends FunctionWithWrappers, ArrayFunction, AliasFunction, SideEffectFunction {
- SystemFunction() {
- hasGlobalOrStdName("system") or // system(command)
- hasGlobalName("popen") or // popen(command, mode)
- // Windows variants
- hasGlobalName("_popen") or // _popen(command, mode)
- hasGlobalName("_wpopen") or // _wpopen(command, mode)
- hasGlobalName("_wsystem") // _wsystem(command)
- }
-
- override predicate interestingArg(int arg) { arg = 0 }
-
- override predicate hasArrayWithNullTerminator(int bufParam) { bufParam = 0 or bufParam = 1 }
-
- override predicate hasArrayInput(int bufParam) { bufParam = 0 or bufParam = 1 }
-
- override predicate parameterNeverEscapes(int index) { index = 0 or index = 1 }
-
- override predicate parameterEscapesOnlyViaReturn(int index) { none() }
-
- override predicate parameterIsAlwaysReturned(int index) { none() }
-
- override predicate hasOnlySpecificReadSideEffects() { any() }
-
- override predicate hasOnlySpecificWriteSideEffects() {
- hasGlobalOrStdName("system") or
- hasGlobalName("_wsystem")
- }
-
- override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
- (i = 0 or i = 1) and
- buffer = true
+class SystemFunction extends FunctionWithWrappers instanceof CommandExecutionFunction {
+ override predicate interestingArg(int arg) {
+ exists(FunctionInput input |
+ this.(CommandExecutionFunction).hasCommandArgument(input) and
+ (
+ input.isParameterDerefOrQualifierObject(arg) or
+ input.isParameterOrQualifierAddress(arg)
+ )
+ )
}
}
diff --git a/cpp/ql/lib/semmle/code/cpp/security/FunctionWithWrappers.qll b/cpp/ql/lib/semmle/code/cpp/security/FunctionWithWrappers.qll
index 5451011b351..654e9d92451 100644
--- a/cpp/ql/lib/semmle/code/cpp/security/FunctionWithWrappers.qll
+++ b/cpp/ql/lib/semmle/code/cpp/security/FunctionWithWrappers.qll
@@ -17,7 +17,7 @@
import cpp
import PrintfLike
-private import TaintTracking
+private import semmle.code.cpp.ir.dataflow.ResolveCall
bindingset[index]
private string toCause(Function func, int index) {
diff --git a/cpp/ql/lib/semmle/code/cpp/security/Security.qll b/cpp/ql/lib/semmle/code/cpp/security/Security.qll
index d39c13a25a0..da808592b3e 100644
--- a/cpp/ql/lib/semmle/code/cpp/security/Security.qll
+++ b/cpp/ql/lib/semmle/code/cpp/security/Security.qll
@@ -7,6 +7,7 @@ import semmle.code.cpp.exprs.Expr
import semmle.code.cpp.commons.Environment
import semmle.code.cpp.security.SecurityOptions
import semmle.code.cpp.models.interfaces.FlowSource
+import semmle.code.cpp.models.interfaces.Sql
/**
* Extend this class to customize the security queries for
@@ -34,13 +35,11 @@ class SecurityOptions extends string {
* An argument to a function that is passed to a SQL server.
*/
predicate sqlArgument(string function, int arg) {
- // MySQL C API
- function = "mysql_query" and arg = 1
- or
- function = "mysql_real_query" and arg = 1
- or
- // SQLite3 C API
- function = "sqlite3_exec" and arg = 1
+ exists(FunctionInput input, SqlExecutionFunction sql |
+ sql.hasName(function) and
+ input.isParameterDeref(arg) and
+ sql.hasSqlArgument(input)
+ )
}
/**
diff --git a/cpp/ql/lib/semmlecode.cpp.dbscheme b/cpp/ql/lib/semmlecode.cpp.dbscheme
index ddd31fd02e5..018f430097e 100644
--- a/cpp/ql/lib/semmlecode.cpp.dbscheme
+++ b/cpp/ql/lib/semmlecode.cpp.dbscheme
@@ -245,7 +245,7 @@ svnchurn(
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `file`.
* For more information, see
- * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
+ * [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. */
@@ -262,7 +262,7 @@ locations_default(
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `file`.
* For more information, see
- * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
locations_stmt(
/** The location of a statement. */
@@ -279,7 +279,7 @@ locations_stmt(
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `file`.
* For more information, see
- * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
locations_expr(
/** The location of an expression. */
@@ -310,23 +310,14 @@ diagnostics(
int location: @location_default ref
);
-/*
- fromSource(0) = unknown,
- fromSource(1) = from source,
- fromSource(2) = from library
-*/
files(
unique int id: @file,
- string name: string ref,
- string simple: string ref,
- string ext: string ref,
- int fromSource: int ref
+ string name: string ref
);
folders(
unique int id: @folder,
- string name: string ref,
- string simple: string ref
+ string name: string ref
);
@container = @folder | @file
diff --git a/cpp/ql/lib/semmlecode.cpp.dbscheme.stats b/cpp/ql/lib/semmlecode.cpp.dbscheme.stats
index 61a31212bfa..c9aaf043a7a 100644
--- a/cpp/ql/lib/semmlecode.cpp.dbscheme.stats
+++ b/cpp/ql/lib/semmlecode.cpp.dbscheme.stats
@@ -12875,18 +12875,6 @@
name
59320
-
-simple
-40580
-
-
-ext
-97
-
-
-fromSource
-10
-
@@ -12906,54 +12894,6 @@
-id
-simple
-
-
-12
-
-
-1
-2
-59320
-
-
-
-
-
-
-id
-ext
-
-
-12
-
-
-1
-2
-59320
-
-
-
-
-
-
-id
-fromSource
-
-
-12
-
-
-1
-2
-59320
-
-
-
-
-
-
name
id
@@ -12969,406 +12909,6 @@
-
-name
-simple
-
-
-12
-
-
-1
-2
-59320
-
-
-
-
-
-
-name
-ext
-
-
-12
-
-
-1
-2
-59320
-
-
-
-
-
-
-name
-fromSource
-
-
-12
-
-
-1
-2
-59320
-
-
-
-
-
-
-simple
-id
-
-
-12
-
-
-1
-2
-30814
-
-
-2
-3
-6123
-
-
-3
-7
-3197
-
-
-7
-42
-444
-
-
-
-
-
-
-simple
-name
-
-
-12
-
-
-1
-2
-30814
-
-
-2
-3
-6123
-
-
-3
-7
-3197
-
-
-7
-42
-444
-
-
-
-
-
-
-simple
-ext
-
-
-12
-
-
-1
-2
-36277
-
-
-2
-3
-3739
-
-
-3
-6
-563
-
-
-
-
-
-
-simple
-fromSource
-
-
-12
-
-
-1
-2
-40580
-
-
-
-
-
-
-ext
-id
-
-
-12
-
-
-1
-2
-10
-
-
-3
-4
-10
-
-
-15
-16
-10
-
-
-38
-39
-10
-
-
-80
-81
-10
-
-
-114
-115
-10
-
-
-441
-442
-10
-
-
-768
-769
-10
-
-
-4013
-4014
-10
-
-
-
-
-
-
-ext
-name
-
-
-12
-
-
-1
-2
-10
-
-
-3
-4
-10
-
-
-15
-16
-10
-
-
-38
-39
-10
-
-
-80
-81
-10
-
-
-114
-115
-10
-
-
-441
-442
-10
-
-
-768
-769
-10
-
-
-4013
-4014
-10
-
-
-
-
-
-
-ext
-simple
-
-
-12
-
-
-1
-2
-10
-
-
-3
-4
-10
-
-
-15
-16
-10
-
-
-38
-39
-10
-
-
-75
-76
-10
-
-
-112
-113
-10
-
-
-428
-429
-10
-
-
-688
-689
-10
-
-
-2838
-2839
-10
-
-
-
-
-
-
-ext
-fromSource
-
-
-12
-
-
-1
-2
-97
-
-
-
-
-
-
-fromSource
-id
-
-
-12
-
-
-5473
-5474
-10
-
-
-
-
-
-
-fromSource
-name
-
-
-12
-
-
-5473
-5474
-10
-
-
-
-
-
-
-fromSource
-simple
-
-
-12
-
-
-3744
-3745
-10
-
-
-
-
-
-
-fromSource
-ext
-
-
-12
-
-
-9
-10
-10
-
-
-
-
-
@@ -13383,10 +12923,6 @@
name
10817
-
-simple
-3099
-
@@ -13406,22 +12942,6 @@
-id
-simple
-
-
-12
-
-
-1
-2
-10817
-
-
-
-
-
-
name
id
@@ -13437,94 +12957,6 @@
-
-name
-simple
-
-
-12
-
-
-1
-2
-10817
-
-
-
-
-
-
-simple
-id
-
-
-12
-
-
-1
-2
-1669
-
-
-2
-3
-661
-
-
-3
-4
-433
-
-
-4
-17
-238
-
-
-27
-121
-97
-
-
-
-
-
-
-simple
-name
-
-
-12
-
-
-1
-2
-1669
-
-
-2
-3
-661
-
-
-3
-4
-433
-
-
-4
-17
-238
-
-
-27
-121
-97
-
-
-
-
-
diff --git a/cpp/ql/src/AlertSuppression.ql b/cpp/ql/src/AlertSuppression.ql
index 6258e8f7818..7239398e8c1 100644
--- a/cpp/ql/src/AlertSuppression.ql
+++ b/cpp/ql/src/AlertSuppression.ql
@@ -68,7 +68,7 @@ class SuppressionScope extends ElementBase {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
- * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
diff --git a/cpp/ql/src/Critical/OverflowStatic.ql b/cpp/ql/src/Critical/OverflowStatic.ql
index 7c447c12323..8b09931cd4a 100644
--- a/cpp/ql/src/Critical/OverflowStatic.ql
+++ b/cpp/ql/src/Critical/OverflowStatic.ql
@@ -5,7 +5,7 @@
* @kind problem
* @problem.severity warning
* @security-severity 9.3
- * @precision medium
+ * @precision high
* @id cpp/static-buffer-overflow
* @tags reliability
* security
@@ -55,6 +55,8 @@ predicate overflowOffsetInLoop(BufferAccess bufaccess, string msg) {
loop.counter().getAnAccess() = bufaccess.getArrayOffset() and
// Ensure that we don't have an upper bound on the array index that's less than the buffer size.
not upperBound(bufaccess.getArrayOffset().getFullyConverted()) < bufaccess.bufferSize() and
+ // The upper bounds analysis must not have been widended
+ not upperBoundMayBeWidened(bufaccess.getArrayOffset().getFullyConverted()) and
msg =
"Potential buffer-overflow: counter '" + loop.counter().toString() + "' <= " +
loop.limit().toString() + " but '" + bufaccess.buffer().getName() + "' has " +
@@ -130,11 +132,13 @@ predicate outOfBounds(BufferAccess bufaccess, string msg) {
(
access > size
or
- access = size and not exists(AddressOfExpr addof | bufaccess = addof.getOperand())
+ access = size and
+ not exists(AddressOfExpr addof | bufaccess = addof.getOperand()) and
+ not exists(BuiltInOperationBuiltInOffsetOf offsetof | offsetof.getAChild() = bufaccess)
) and
msg =
"Potential buffer-overflow: '" + buf + "' has size " + size.toString() + " but '" + buf + "[" +
- access.toString() + "]' is accessed here."
+ access.toString() + "]' may be accessed here."
)
}
diff --git a/cpp/ql/src/Documentation/CommentedOutCode.qll b/cpp/ql/src/Documentation/CommentedOutCode.qll
index 172474bbe29..a4e5b948630 100644
--- a/cpp/ql/src/Documentation/CommentedOutCode.qll
+++ b/cpp/ql/src/Documentation/CommentedOutCode.qll
@@ -198,7 +198,7 @@ class CommentBlock extends Comment {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
- * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
diff --git a/cpp/ql/src/Metrics/Internal/CallableExtents.ql b/cpp/ql/src/Metrics/Internal/CallableExtents.ql
index 80c798b19ff..7a376c6da72 100644
--- a/cpp/ql/src/Metrics/Internal/CallableExtents.ql
+++ b/cpp/ql/src/Metrics/Internal/CallableExtents.ql
@@ -18,7 +18,7 @@ class RangeFunction extends Function {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
- * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
super.getLocation().hasLocationInfo(path, sl, sc, _, _) and
diff --git a/cpp/ql/src/Metrics/Internal/DiagnosticsSumElapsedTimes.ql b/cpp/ql/src/Metrics/Internal/DiagnosticsSumElapsedTimes.ql
new file mode 100644
index 00000000000..530811c0801
--- /dev/null
+++ b/cpp/ql/src/Metrics/Internal/DiagnosticsSumElapsedTimes.ql
@@ -0,0 +1,12 @@
+/**
+ * @name Sum of frontend and extractor time
+ * @description The sum of elapsed frontend time, and the sum of elapsed extractor time.
+ * This query is for internal use only and may change without notice.
+ * @kind table
+ * @id cpp/frontend-and-extractor-time
+ */
+
+import cpp
+
+select sum(Compilation c, float seconds | compilation_time(c, _, 2, seconds) | seconds) as sum_frontend_elapsed_seconds,
+ sum(Compilation c, float seconds | compilation_time(c, _, 4, seconds) | seconds) as sum_extractor_elapsed_seconds
diff --git a/cpp/ql/src/Security/CWE/CWE-078/ExecTainted.c b/cpp/ql/src/Security/CWE/CWE-078/ExecTainted.c
index da5950c5fe5..c61adeee651 100644
--- a/cpp/ql/src/Security/CWE/CWE-078/ExecTainted.c
+++ b/cpp/ql/src/Security/CWE/CWE-078/ExecTainted.c
@@ -9,7 +9,7 @@ int main(int argc, char** argv) {
system(command1);
}
- {
+ {
// GOOD: the user string is encoded by a library routine.
char userNameQuoted[1000] = {0};
encodeShellString(userNameQuoted, 1000, userName);
diff --git a/cpp/ql/src/Security/CWE/CWE-078/ExecTainted.ql b/cpp/ql/src/Security/CWE/CWE-078/ExecTainted.ql
index 5f516eec83b..26652d9c1da 100644
--- a/cpp/ql/src/Security/CWE/CWE-078/ExecTainted.ql
+++ b/cpp/ql/src/Security/CWE/CWE-078/ExecTainted.ql
@@ -3,10 +3,10 @@
* @description Using user-supplied data in an OS command, without
* neutralizing special elements, can make code vulnerable
* to command injection.
- * @kind problem
+ * @kind path-problem
* @problem.severity error
* @security-severity 9.8
- * @precision low
+ * @precision high
* @id cpp/command-line-injection
* @tags security
* external/cwe/cwe-078
@@ -16,13 +16,204 @@
import cpp
import semmle.code.cpp.security.CommandExecution
import semmle.code.cpp.security.Security
-import semmle.code.cpp.security.TaintTracking
+import semmle.code.cpp.valuenumbering.GlobalValueNumbering
+import semmle.code.cpp.ir.IR
+import semmle.code.cpp.ir.dataflow.TaintTracking
+import semmle.code.cpp.ir.dataflow.TaintTracking2
+import semmle.code.cpp.security.FlowSources
+import semmle.code.cpp.models.implementations.Strcat
-from Expr taintedArg, Expr taintSource, string taintCause, string callChain
+Expr sinkAsArgumentIndirection(DataFlow::Node sink) {
+ result =
+ sink.asOperand()
+ .(SideEffectOperand)
+ .getAddressOperand()
+ .getAnyDef()
+ .getUnconvertedResultExpression()
+}
+
+/**
+ * Holds if `fst` is a string that is used in a format or concatenation function resulting in `snd`,
+ * and is *not* placed at the start of the resulting string. This indicates that the author did not
+ * expect `fst` to control what program is run if the resulting string is eventually interpreted as
+ * a command line, for example as an argument to `system`.
+ */
+predicate interestingConcatenation(DataFlow::Node fst, DataFlow::Node snd) {
+ exists(FormattingFunctionCall call, int index, FormatLiteral literal |
+ sinkAsArgumentIndirection(fst) = call.getConversionArgument(index) and
+ snd.asDefiningArgument() = call.getOutputArgument(false) and
+ literal = call.getFormat() and
+ not literal.getConvSpecOffset(index) = 0 and
+ literal.getConversionChar(index) = ["s", "S"]
+ )
+ or
+ // strcat and friends
+ exists(StrcatFunction strcatFunc, CallInstruction call, ReadSideEffectInstruction rse |
+ call.getStaticCallTarget() = strcatFunc and
+ rse.getArgumentDef() = call.getArgument(strcatFunc.getParamSrc()) and
+ fst.asOperand() = rse.getSideEffectOperand() and
+ snd.asInstruction().(WriteSideEffectInstruction).getDestinationAddress() =
+ call.getArgument(strcatFunc.getParamDest())
+ )
+ or
+ exists(CallInstruction call, Operator op, ReadSideEffectInstruction rse |
+ call.getStaticCallTarget() = op and
+ op.hasQualifiedName("std", "operator+") and
+ op.getType().(UserType).hasQualifiedName("std", "basic_string") and
+ call.getArgument(1) = rse.getArgumentOperand().getAnyDef() and // left operand
+ fst.asOperand() = rse.getSideEffectOperand() and
+ call = snd.asInstruction()
+ )
+}
+
+class TaintToConcatenationConfiguration extends TaintTracking::Configuration {
+ TaintToConcatenationConfiguration() { this = "TaintToConcatenationConfiguration" }
+
+ override predicate isSource(DataFlow::Node source) { source instanceof FlowSource }
+
+ override predicate isSink(DataFlow::Node sink) { interestingConcatenation(sink, _) }
+
+ override predicate isSanitizer(DataFlow::Node node) {
+ node.asInstruction().getResultType() instanceof IntegralType
+ or
+ node.asInstruction().getResultType() instanceof FloatingPointType
+ }
+}
+
+class ExecTaintConfiguration extends TaintTracking2::Configuration {
+ ExecTaintConfiguration() { this = "ExecTaintConfiguration" }
+
+ override predicate isSource(DataFlow::Node source) {
+ exists(DataFlow::Node prevSink, TaintToConcatenationConfiguration conf |
+ conf.hasFlow(_, prevSink) and
+ interestingConcatenation(prevSink, source)
+ )
+ }
+
+ override predicate isSink(DataFlow::Node sink) {
+ shellCommand(sinkAsArgumentIndirection(sink), _)
+ }
+
+ override predicate isSanitizerOut(DataFlow::Node node) {
+ isSink(node) // Prevent duplicates along a call chain, since `shellCommand` will include wrappers
+ }
+}
+
+module StitchedPathGraph {
+ // There's a different PathNode class for each DataFlowImplN.qll, so we can't simply combine the
+ // PathGraph predicates directly. Instead, we use a newtype so there's a single type that
+ // contains both sets of PathNodes.
+ newtype TMergedPathNode =
+ TPathNode1(DataFlow::PathNode node) or
+ TPathNode2(DataFlow2::PathNode node)
+
+ // this wraps the toString and location predicates so we can use the merged node type in a
+ // selection
+ class MergedPathNode extends TMergedPathNode {
+ string toString() {
+ exists(DataFlow::PathNode n |
+ this = TPathNode1(n) and
+ result = n.toString()
+ )
+ or
+ exists(DataFlow2::PathNode n |
+ this = TPathNode2(n) and
+ result = n.toString()
+ )
+ }
+
+ DataFlow::Node getNode() {
+ exists(DataFlow::PathNode n |
+ this = TPathNode1(n) and
+ result = n.getNode()
+ )
+ or
+ exists(DataFlow2::PathNode n |
+ this = TPathNode2(n) and
+ result = n.getNode()
+ )
+ }
+
+ DataFlow::PathNode getPathNode1() { this = TPathNode1(result) }
+
+ DataFlow2::PathNode getPathNode2() { this = TPathNode2(result) }
+
+ predicate hasLocationInfo(
+ string filepath, int startline, int startcolumn, int endline, int endcolumn
+ ) {
+ exists(DataFlow::PathNode n |
+ this = TPathNode1(n) and
+ n.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
+ )
+ or
+ exists(DataFlow2::PathNode n |
+ this = TPathNode2(n) and
+ n.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
+ )
+ }
+ }
+
+ query predicate edges(MergedPathNode a, MergedPathNode b) {
+ exists(DataFlow::PathNode an, DataFlow::PathNode bn |
+ a = TPathNode1(an) and
+ b = TPathNode1(bn) and
+ DataFlow::PathGraph::edges(an, bn)
+ )
+ or
+ exists(DataFlow2::PathNode an, DataFlow2::PathNode bn |
+ a = TPathNode2(an) and
+ b = TPathNode2(bn) and
+ DataFlow2::PathGraph::edges(an, bn)
+ )
+ or
+ // This is where paths from the two configurations are connected. `interestingConcatenation`
+ // is the only thing in this module that's actually specific to the query - everything else is
+ // just using types and predicates from the DataFlow library.
+ interestingConcatenation(a.getNode(), b.getNode()) and
+ a instanceof TPathNode1 and
+ b instanceof TPathNode2
+ }
+
+ query predicate nodes(MergedPathNode mpn, string key, string val) {
+ // here we just need the union of the underlying `nodes` predicates
+ exists(DataFlow::PathNode n |
+ mpn = TPathNode1(n) and
+ DataFlow::PathGraph::nodes(n, key, val)
+ )
+ or
+ exists(DataFlow2::PathNode n |
+ mpn = TPathNode2(n) and
+ DataFlow2::PathGraph::nodes(n, key, val)
+ )
+ }
+
+ query predicate subpaths(
+ MergedPathNode arg, MergedPathNode par, MergedPathNode ret, MergedPathNode out
+ ) {
+ // just forward subpaths from the underlying libraries. This might be slightly awkward when
+ // the concatenation is deep in a call chain.
+ DataFlow::PathGraph::subpaths(arg.getPathNode1(), par.getPathNode1(), ret.getPathNode1(),
+ out.getPathNode1())
+ or
+ DataFlow2::PathGraph::subpaths(arg.getPathNode2(), par.getPathNode2(), ret.getPathNode2(),
+ out.getPathNode2())
+ }
+}
+
+import StitchedPathGraph
+
+from
+ DataFlow::PathNode sourceNode, DataFlow::PathNode concatSink, DataFlow2::PathNode concatSource,
+ DataFlow2::PathNode sinkNode, string taintCause, string callChain,
+ TaintToConcatenationConfiguration conf1, ExecTaintConfiguration conf2
where
- shellCommand(taintedArg, callChain) and
- tainted(taintSource, taintedArg) and
- isUserInput(taintSource, taintCause)
-select taintedArg,
- "This argument to an OS command is derived from $@ and then passed to " + callChain, taintSource,
- "user input (" + taintCause + ")"
+ taintCause = sourceNode.getNode().(FlowSource).getSourceType() and
+ conf1.hasFlowPath(sourceNode, concatSink) and
+ interestingConcatenation(concatSink.getNode(), concatSource.getNode()) and // this loses call context
+ conf2.hasFlowPath(concatSource, sinkNode) and
+ shellCommand(sinkAsArgumentIndirection(sinkNode.getNode()), callChain)
+select sinkAsArgumentIndirection(sinkNode.getNode()), TPathNode1(sourceNode).(MergedPathNode),
+ TPathNode2(sinkNode).(MergedPathNode),
+ "This argument to an OS command is derived from $@, dangerously concatenated into $@, and then passed to "
+ + callChain, sourceNode, "user input (" + taintCause + ")", concatSource,
+ concatSource.toString()
diff --git a/cpp/ql/src/Security/CWE/CWE-089/SqlTainted.ql b/cpp/ql/src/Security/CWE/CWE-089/SqlTainted.ql
index a3f935170d7..92c8b9a2bd5 100644
--- a/cpp/ql/src/Security/CWE/CWE-089/SqlTainted.ql
+++ b/cpp/ql/src/Security/CWE/CWE-089/SqlTainted.ql
@@ -30,7 +30,15 @@ class Configuration extends TaintTrackingConfiguration {
}
override predicate isBarrier(Expr e) {
- super.isBarrier(e) or e.getUnspecifiedType() instanceof IntegralType
+ super.isBarrier(e)
+ or
+ e.getUnspecifiedType() instanceof IntegralType
+ or
+ exists(SqlBarrierFunction sql, int arg, FunctionInput input |
+ e = sql.getACallToThisFunction().getArgument(arg) and
+ input.isParameterDeref(arg) and
+ sql.barrierSqlArgument(input, _)
+ )
}
}
diff --git a/cpp/ql/src/Security/CWE/CWE-311/CleartextStorage.inc.qhelp b/cpp/ql/src/Security/CWE/CWE-311/CleartextStorage.inc.qhelp
index eb9a6f8adce..f5e978e05cb 100644
--- a/cpp/ql/src/Security/CWE/CWE-311/CleartextStorage.inc.qhelp
+++ b/cpp/ql/src/Security/CWE/CWE-311/CleartextStorage.inc.qhelp
@@ -9,7 +9,7 @@ storage.
-Ensure that sensitive information is always encrypted before being stored, especially before writing to a file.
+
Ensure that sensitive information is always encrypted before being stored to a file or transmitted over the network.
It may be wise to encrypt information before it is put into a buffer that may be readable in memory.
In general, decrypt sensitive information only at the point where it is necessary for it to be used in
diff --git a/cpp/ql/src/Security/CWE/CWE-311/CleartextTransmission.qhelp b/cpp/ql/src/Security/CWE/CWE-311/CleartextTransmission.qhelp
new file mode 100644
index 00000000000..d715abca84c
--- /dev/null
+++ b/cpp/ql/src/Security/CWE/CWE-311/CleartextTransmission.qhelp
@@ -0,0 +1,5 @@
+
+
+
diff --git a/cpp/ql/src/Security/CWE/CWE-311/CleartextTransmission.ql b/cpp/ql/src/Security/CWE/CWE-311/CleartextTransmission.ql
new file mode 100644
index 00000000000..d7e5343d6dc
--- /dev/null
+++ b/cpp/ql/src/Security/CWE/CWE-311/CleartextTransmission.ql
@@ -0,0 +1,124 @@
+/**
+ * @name Cleartext transmission of sensitive information
+ * @description Transmitting sensitive information across a network in
+ * cleartext can expose it to an attacker.
+ * @kind path-problem
+ * @problem.severity warning
+ * @security-severity 7.5
+ * @precision medium
+ * @id cpp/cleartext-transmission
+ * @tags security
+ * external/cwe/cwe-319
+ */
+
+import cpp
+import semmle.code.cpp.security.SensitiveExprs
+import semmle.code.cpp.dataflow.TaintTracking
+import semmle.code.cpp.models.interfaces.FlowSource
+import DataFlow::PathGraph
+
+/**
+ * A function call that sends or receives data over a network.
+ */
+abstract class NetworkSendRecv extends FunctionCall {
+ /**
+ * Gets the expression for the socket or similar object used for sending or
+ * receiving data (if any).
+ */
+ abstract Expr getSocketExpr();
+
+ /**
+ * Gets the expression for the buffer to be sent from / received into.
+ */
+ abstract Expr getDataExpr();
+}
+
+/**
+ * A function call that sends data over a network.
+ *
+ * note: functions such as `write` may be writing to a network source or a file. We could attempt to determine which, and sort results into `cpp/cleartext-transmission` and perhaps `cpp/cleartext-storage-file`. In practice it usually isn't very important which query reports a result as long as its reported exactly once.
+ */
+class NetworkSend extends NetworkSendRecv {
+ RemoteFlowSinkFunction target;
+
+ NetworkSend() { target = this.getTarget() }
+
+ override Expr getSocketExpr() {
+ exists(FunctionInput input, int arg |
+ target.hasSocketInput(input) and
+ input.isParameter(arg) and
+ result = this.getArgument(arg)
+ )
+ }
+
+ override Expr getDataExpr() {
+ exists(FunctionInput input, int arg |
+ target.hasRemoteFlowSink(input, _) and
+ input.isParameterDeref(arg) and
+ result = this.getArgument(arg)
+ )
+ }
+}
+
+/**
+ * A function call that receives data over a network.
+ */
+class NetworkRecv extends NetworkSendRecv {
+ RemoteFlowSourceFunction target;
+
+ NetworkRecv() { target = this.getTarget() }
+
+ override Expr getSocketExpr() {
+ exists(FunctionInput input, int arg |
+ target.hasSocketInput(input) and
+ input.isParameter(arg) and
+ result = this.getArgument(arg)
+ )
+ }
+
+ override Expr getDataExpr() {
+ exists(FunctionOutput output, int arg |
+ target.hasRemoteFlowSource(output, _) and
+ output.isParameterDeref(arg) and
+ result = this.getArgument(arg)
+ )
+ }
+}
+
+/**
+ * Taint flow from a sensitive expression to a network operation with data
+ * tainted by that expression.
+ */
+class SensitiveSendRecvConfiguration extends TaintTracking::Configuration {
+ SensitiveSendRecvConfiguration() { this = "SensitiveSendRecvConfiguration" }
+
+ override predicate isSource(DataFlow::Node source) { source.asExpr() instanceof SensitiveExpr }
+
+ override predicate isSink(DataFlow::Node sink) {
+ exists(NetworkSendRecv transmission |
+ sink.asExpr() = transmission.getDataExpr() and
+ // a zero socket descriptor is standard input, which is not interesting for this query.
+ not exists(Zero zero |
+ DataFlow::localFlow(DataFlow::exprNode(zero),
+ DataFlow::exprNode(transmission.getSocketExpr()))
+ )
+ )
+ }
+}
+
+from
+ SensitiveSendRecvConfiguration config, DataFlow::PathNode source, DataFlow::PathNode sink,
+ NetworkSendRecv transmission, string msg
+where
+ config.hasFlowPath(source, sink) and
+ sink.getNode().asExpr() = transmission.getDataExpr() and
+ if transmission instanceof NetworkSend
+ then
+ msg =
+ "This operation transmits '" + sink.toString() +
+ "', which may contain unencrypted sensitive data from $@"
+ else
+ msg =
+ "This operation receives into '" + sink.toString() +
+ "', which may put unencrypted sensitive data into $@"
+select transmission, source, sink, msg, source, source.getNode().asExpr().toString()
diff --git a/cpp/ql/src/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql b/cpp/ql/src/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql
index 28e7295dcdc..357e6375570 100644
--- a/cpp/ql/src/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql
+++ b/cpp/ql/src/Security/CWE/CWE-570/IncorrectAllocationErrorHandling.ql
@@ -4,10 +4,13 @@
* @kind problem
* @id cpp/incorrect-allocation-error-handling
* @problem.severity warning
+ * @security-severity 7.5
* @precision medium
* @tags correctness
* security
* external/cwe/cwe-570
+ * external/cwe/cwe-252
+ * external/cwe/cwe-755
*/
import cpp
diff --git a/cpp/ql/src/definitions.qll b/cpp/ql/src/definitions.qll
index eac03ce7082..cb229d66ef1 100644
--- a/cpp/ql/src/definitions.qll
+++ b/cpp/ql/src/definitions.qll
@@ -24,7 +24,7 @@ class Top extends Element {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
- * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
pragma[noopt]
final predicate hasLocationInfo(
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-089/SqlPqxxTainted.cpp b/cpp/ql/src/experimental/Security/CWE/CWE-089/SqlPqxxTainted.cpp
deleted file mode 100644
index 3b85835fff9..00000000000
--- a/cpp/ql/src/experimental/Security/CWE/CWE-089/SqlPqxxTainted.cpp
+++ /dev/null
@@ -1,28 +0,0 @@
-#include
-#include
-#include
-
-int main(int argc, char ** argv) {
-
- if (argc != 2) {
- throw std::runtime_error("Give me a string!");
- }
-
- pqxx::connection c;
- pqxx::work w(c);
-
- // BAD
- char *userName = argv[1];
- char query1[1000] = {0};
- sprintf(query1, "SELECT UID FROM USERS where name = \"%s\"", userName);
- pqxx::row r = w.exec1(query1);
- w.commit();
- std::cout << r[0].as() << std::endl;
-
- // GOOD
- pqxx::result r2 = w.exec("SELECT " + w.quote(argv[1]));
- w.commit();
- std::cout << r2[0][0].c_str() << std::endl;
-
- return 0;
-}
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-089/SqlPqxxTainted.qhelp b/cpp/ql/src/experimental/Security/CWE/CWE-089/SqlPqxxTainted.qhelp
deleted file mode 100644
index 1c01b3e4f3a..00000000000
--- a/cpp/ql/src/experimental/Security/CWE/CWE-089/SqlPqxxTainted.qhelp
+++ /dev/null
@@ -1,31 +0,0 @@
-
-
-
-The code passes user input as part of a SQL query without escaping special elements.
-It generates a SQL query to Postgres using sprintf,
-with the user-supplied data directly passed as an argument
-to sprintf. This leaves the code vulnerable to attack by SQL Injection.
-
-
-
-
-Use a library routine to escape characters in the user-supplied
-string before converting it to SQL. Use esc and quote pqxx library functions.
-
-
-
-
-
-
-
-
-MSDN Library: SQL Injection.
-
-
-
-
-
-
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-089/SqlPqxxTainted.ql b/cpp/ql/src/experimental/Security/CWE/CWE-089/SqlPqxxTainted.ql
deleted file mode 100644
index 8de55953b15..00000000000
--- a/cpp/ql/src/experimental/Security/CWE/CWE-089/SqlPqxxTainted.ql
+++ /dev/null
@@ -1,113 +0,0 @@
-/**
- * @name Uncontrolled data in SQL query to Postgres
- * @description Including user-supplied data in a SQL query to Postgres
- * without neutralizing special elements can make code
- * vulnerable to SQL Injection.
- * @kind path-problem
- * @problem.severity error
- * @precision high
- * @id cpp/sql-injection-via-pqxx
- * @tags security
- * external/cwe/cwe-089
- */
-
-import cpp
-import semmle.code.cpp.security.Security
-import semmle.code.cpp.dataflow.TaintTracking
-import DataFlow::PathGraph
-
-predicate pqxxTransationClassNames(string className, string namespace) {
- namespace = "pqxx" and
- className in [
- "dbtransaction", "nontransaction", "basic_robusttransaction", "robusttransaction",
- "subtransaction", "transaction", "basic_transaction", "transaction_base", "work"
- ]
-}
-
-predicate pqxxConnectionClassNames(string className, string namespace) {
- namespace = "pqxx" and
- className in ["connection_base", "basic_connection", "connection"]
-}
-
-predicate pqxxTransactionSqlArgument(string function, int arg) {
- function = "exec" and arg = 0
- or
- function = "exec0" and arg = 0
- or
- function = "exec1" and arg = 0
- or
- function = "exec_n" and arg = 1
- or
- function = "exec_params" and arg = 0
- or
- function = "exec_params0" and arg = 0
- or
- function = "exec_params1" and arg = 0
- or
- function = "exec_params_n" and arg = 1
- or
- function = "query_value" and arg = 0
- or
- function = "stream" and arg = 0
-}
-
-predicate pqxxConnectionSqlArgument(string function, int arg) { function = "prepare" and arg = 1 }
-
-Expr getPqxxSqlArgument() {
- exists(FunctionCall fc, Expr e, int argIndex, UserType t |
- // examples: 'work' for 'work.exec(...)'; '->' for 'tx->exec()'.
- e = fc.getQualifier() and
- // to find ConnectionHandle/TransationHandle and similar classes which override '->' operator behavior
- // and return pointer to a connection/transation object
- e.getType().refersTo(t) and
- // transaction exec and connection prepare variations
- (
- pqxxTransationClassNames(t.getName(), t.getNamespace().getName()) and
- pqxxTransactionSqlArgument(fc.getTarget().getName(), argIndex)
- or
- pqxxConnectionClassNames(t.getName(), t.getNamespace().getName()) and
- pqxxConnectionSqlArgument(fc.getTarget().getName(), argIndex)
- ) and
- result = fc.getArgument(argIndex)
- )
-}
-
-predicate pqxxEscapeArgument(string function, int arg) {
- arg = 0 and
- function in ["esc", "esc_raw", "quote", "quote_raw", "quote_name", "quote_table", "esc_like"]
-}
-
-predicate isEscapedPqxxArgument(Expr argExpr) {
- exists(FunctionCall fc, Expr e, int argIndex, UserType t |
- // examples: 'work' for 'work.exec(...)'; '->' for 'tx->exec()'.
- e = fc.getQualifier() and
- // to find ConnectionHandle/TransationHandle and similar classes which override '->' operator behavior
- // and return pointer to a connection/transation object
- e.getType().refersTo(t) and
- // transaction and connection escape functions
- (
- pqxxTransationClassNames(t.getName(), t.getNamespace().getName()) or
- pqxxConnectionClassNames(t.getName(), t.getNamespace().getName())
- ) and
- pqxxEscapeArgument(fc.getTarget().getName(), argIndex) and
- // is escaped arg == argExpr
- argExpr = fc.getArgument(argIndex)
- )
-}
-
-class Configuration extends TaintTracking::Configuration {
- Configuration() { this = "SqlPqxxTainted" }
-
- override predicate isSource(DataFlow::Node source) { isUserInput(source.asExpr(), _) }
-
- override predicate isSink(DataFlow::Node sink) { sink.asExpr() = getPqxxSqlArgument() }
-
- override predicate isSanitizer(DataFlow::Node node) { isEscapedPqxxArgument(node.asExpr()) }
-}
-
-from DataFlow::PathNode source, DataFlow::PathNode sink, Configuration config, string taintCause
-where
- config.hasFlowPath(source, sink) and
- isUserInput(source.getNode().asExpr(), taintCause)
-select sink, source, sink, "This argument to a SQL query function is derived from $@", source,
- "user input (" + taintCause + ")"
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-675/DoubleRelease.c b/cpp/ql/src/experimental/Security/CWE/CWE-675/DoubleRelease.c
new file mode 100644
index 00000000000..22de8d4dde4
--- /dev/null
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-675/DoubleRelease.c
@@ -0,0 +1,13 @@
+...
+ fs = socket(AF_UNIX, SOCK_STREAM, 0)
+...
+ close(fs);
+ fs = -1; // GOOD
+...
+
+...
+ fs = socket(AF_UNIX, SOCK_STREAM, 0)
+...
+ close(fs);
+ if(fs) close(fs); // BAD
+...
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-675/DoubleRelease.qhelp b/cpp/ql/src/experimental/Security/CWE/CWE-675/DoubleRelease.qhelp
new file mode 100644
index 00000000000..2b590e025b8
--- /dev/null
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-675/DoubleRelease.qhelp
@@ -0,0 +1,26 @@
+
+
+
+Double release of the descriptor can lead to a crash of the program. Requires the attention of developers.
+
+
+
+We recommend that you exclude situations of possible double release.
+
+
+
+The following example demonstrates an erroneous and corrected use of descriptor deallocation.
+
+
+
+
+
+
+ CERT C Coding Standard:
+ FIO46-C. Do not access a closed file.
+
+
+
+
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-675/DoubleRelease.ql b/cpp/ql/src/experimental/Security/CWE/CWE-675/DoubleRelease.ql
new file mode 100644
index 00000000000..474f00acc55
--- /dev/null
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-675/DoubleRelease.ql
@@ -0,0 +1,142 @@
+/**
+ * @name Errors When Double Release
+ * @description Double release of the descriptor can lead to a crash of the program.
+ * @kind problem
+ * @id cpp/double-release
+ * @problem.severity warning
+ * @precision medium
+ * @tags security
+ * external/cwe/cwe-675
+ * external/cwe/cwe-666
+ */
+
+import cpp
+import semmle.code.cpp.commons.File
+import semmle.code.cpp.valuenumbering.GlobalValueNumbering
+import semmle.code.cpp.valuenumbering.HashCons
+
+/**
+ * A function call that potentially does not return (such as `exit`).
+ */
+class CallMayNotReturn extends FunctionCall {
+ CallMayNotReturn() {
+ // call that is known to not return
+ not exists(this.(ControlFlowNode).getASuccessor())
+ or
+ // call to another function that may not return
+ exists(CallMayNotReturn exit | getTarget() = exit.getEnclosingFunction())
+ or
+ exists(ThrowExpr tex | tex = this.(ControlFlowNode).getASuccessor())
+ }
+}
+
+/** Holds if there are no assignment expressions to the function argument. */
+pragma[inline]
+predicate checkChangeVariable(FunctionCall fc0, ControlFlowNode fc1, ControlFlowNode fc2) {
+ not exists(Expr exptmp |
+ (
+ exptmp = fc0.getArgument(0).(VariableAccess).getTarget().getAnAssignedValue() or
+ exptmp.(AddressOfExpr).getOperand() =
+ fc0.getArgument(0).(VariableAccess).getTarget().getAnAccess()
+ ) and
+ exptmp = fc1.getASuccessor*() and
+ exptmp = fc2.getAPredecessor*()
+ ) and
+ (
+ (
+ not fc0.getArgument(0) instanceof PointerFieldAccess and
+ not fc0.getArgument(0) instanceof ValueFieldAccess
+ or
+ fc0.getArgument(0).(VariableAccess).getQualifier() instanceof ThisExpr
+ )
+ or
+ not exists(Expr exptmp |
+ (
+ exptmp =
+ fc0.getArgument(0)
+ .(VariableAccess)
+ .getQualifier()
+ .(VariableAccess)
+ .getTarget()
+ .getAnAssignedValue() or
+ exptmp.(AddressOfExpr).getOperand() =
+ fc0.getArgument(0)
+ .(VariableAccess)
+ .getQualifier()
+ .(VariableAccess)
+ .getTarget()
+ .getAnAccess()
+ ) and
+ exptmp = fc1.getASuccessor*() and
+ exptmp = fc2.getAPredecessor*()
+ )
+ )
+}
+
+/** Holds if the underlying expression is a call to the close function. Provided that the function parameter does not change after the call. */
+predicate closeReturn(FunctionCall fc) {
+ fcloseCall(fc, _) and
+ checkChangeVariable(fc, fc, fc.getEnclosingFunction())
+}
+
+/** Holds if the underlying expression is a call to the close function. Provided that the function parameter does not change before the call. */
+predicate closeWithoutChangeBefore(FunctionCall fc) {
+ fcloseCall(fc, _) and
+ checkChangeVariable(fc, fc.getEnclosingFunction().getEntryPoint(), fc)
+}
+
+/** Holds, if a sequential call of the specified functions is possible, via a higher-level function call. */
+predicate callInOtherFunctions(FunctionCall fc, FunctionCall fc1) {
+ exists(FunctionCall fec1, FunctionCall fec2 |
+ fc.getEnclosingFunction() != fc1.getEnclosingFunction() and
+ fec1 = fc.getEnclosingFunction().getACallToThisFunction() and
+ fec2 = fc1.getEnclosingFunction().getACallToThisFunction() and
+ fec1.getASuccessor*() = fec2 and
+ checkChangeVariable(fc, fec1, fec2)
+ )
+}
+
+/** Holds if successive calls to close functions are possible. */
+predicate interDoubleCloseFunctions(FunctionCall fc, FunctionCall fc1) {
+ fcloseCall(fc, _) and
+ fcloseCall(fc1, _) and
+ fc != fc1 and
+ fc.getASuccessor*() = fc1 and
+ checkChangeVariable(fc, fc, fc1)
+}
+
+/** Holds if the first arguments of the two functions are similar. */
+predicate similarArguments(FunctionCall fc, FunctionCall fc1) {
+ globalValueNumber(fc.getArgument(0)) = globalValueNumber(fc1.getArgument(0))
+ or
+ fc.getArgument(0).(VariableAccess).getTarget() = fc1.getArgument(0).(VariableAccess).getTarget() and
+ (
+ not fc.getArgument(0) instanceof PointerFieldAccess and
+ not fc.getArgument(0) instanceof ValueFieldAccess
+ or
+ fc.getArgument(0).(VariableAccess).getQualifier() instanceof ThisExpr
+ )
+ or
+ fc.getArgument(0).(VariableAccess).getTarget() = fc1.getArgument(0).(VariableAccess).getTarget() and
+ (
+ fc.getArgument(0) instanceof PointerFieldAccess or
+ fc.getArgument(0) instanceof ValueFieldAccess
+ ) and
+ hashCons(fc.getArgument(0)) = hashCons(fc1.getArgument(0))
+}
+
+from FunctionCall fc, FunctionCall fc1
+where
+ not exists(CallMayNotReturn fctmp | fctmp = fc.getASuccessor*()) and
+ not exists(IfStmt ifs | ifs.getCondition().getAChild*() = fc) and
+ (
+ // detecting a repeated call situation within one function
+ closeReturn(fc) and
+ closeWithoutChangeBefore(fc1) and
+ callInOtherFunctions(fc, fc1)
+ or
+ // detection of repeated call in different functions
+ interDoubleCloseFunctions(fc, fc1)
+ ) and
+ similarArguments(fc, fc1)
+select fc, "Second call to the $@ function is possible.", fc1, fc1.getTarget().getName()
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-787/UnsignedToSignedPointerArith.ql b/cpp/ql/src/experimental/Security/CWE/CWE-787/UnsignedToSignedPointerArith.ql
new file mode 100644
index 00000000000..1fe82c9cc51
--- /dev/null
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-787/UnsignedToSignedPointerArith.ql
@@ -0,0 +1,30 @@
+/**
+ * @name unsigned to signed used in pointer arithmetic
+ * @description finds unsigned to signed conversions used in pointer arithmetic, potentially causing an out-of-bound access
+ * @id cpp/sign-conversion-pointer-arithmetic
+ * @kind problem
+ * @problem.severity warning
+ * @tags reliability
+ * security
+ * external/cwe/cwe-787
+ */
+
+import cpp
+import semmle.code.cpp.dataflow.DataFlow
+import semmle.code.cpp.security.Overflow
+
+from FunctionCall call, Function f, Parameter p, DataFlow::Node sink, PointerArithmeticOperation pao
+where
+ f = call.getTarget() and
+ p = f.getAParameter() and
+ p.getUnspecifiedType().(IntegralType).isSigned() and
+ call.getArgument(p.getIndex()).getUnspecifiedType().(IntegralType).isUnsigned() and
+ pao.getAnOperand() = sink.asExpr() and
+ not exists(Operation a | guardedLesser(a, sink.asExpr())) and
+ not exists(Operation b | guardedGreater(b, call.getArgument(p.getIndex()))) and
+ not call.getArgument(p.getIndex()).isConstant() and
+ DataFlow::localFlow(DataFlow::parameterNode(p), sink) and
+ p.getUnspecifiedType().getSize() < 8
+select call,
+ "This call: $@ passes an unsigned int to a function that requires a signed int: $@. And then used in pointer arithmetic: $@",
+ call, call.toString(), f, f.toString(), sink, sink.toString()
diff --git a/cpp/ql/src/external/CodeDuplication.qll b/cpp/ql/src/external/CodeDuplication.qll
index 8cc56d12e19..1550ca697a3 100644
--- a/cpp/ql/src/external/CodeDuplication.qll
+++ b/cpp/ql/src/external/CodeDuplication.qll
@@ -61,7 +61,7 @@ class Copy extends @duplication_or_similarity {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
- * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
diff --git a/cpp/ql/src/external/DefectFilter.qll b/cpp/ql/src/external/DefectFilter.qll
index 675f3b25faa..b932ffd0470 100644
--- a/cpp/ql/src/external/DefectFilter.qll
+++ b/cpp/ql/src/external/DefectFilter.qll
@@ -8,7 +8,7 @@ import cpp
* column `startcolumn` of line `startline` to column `endcolumn` of line `endline`
* in file `filepath`.
*
- * For more information, see [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
+ * For more information, see [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
external predicate defectResults(
int id, string queryPath, string file, int startline, int startcol, int endline, int endcol,
diff --git a/cpp/ql/src/external/MetricFilter.qll b/cpp/ql/src/external/MetricFilter.qll
index dd9cece78ce..58e8bf154e9 100644
--- a/cpp/ql/src/external/MetricFilter.qll
+++ b/cpp/ql/src/external/MetricFilter.qll
@@ -8,7 +8,7 @@ import cpp
* column `startcolumn` of line `startline` to column `endcolumn` of line `endline`
* in file `filepath`.
*
- * For more information, see [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
+ * For more information, see [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
external predicate metricResults(
int id, string queryPath, string file, int startline, int startcol, int endline, int endcol,
diff --git a/cpp/ql/src/jsf/4.13 Functions/AV Rule 114.ql b/cpp/ql/src/jsf/4.13 Functions/AV Rule 114.ql
index 0e021ca7575..ac5db25ea6b 100644
--- a/cpp/ql/src/jsf/4.13 Functions/AV Rule 114.ql
+++ b/cpp/ql/src/jsf/4.13 Functions/AV Rule 114.ql
@@ -63,6 +63,7 @@ where
functionsMissingReturnStmt(f, blame) and
reachable(blame) and
not functionImperfectlyExtracted(f) and
+ not f.isFromUninstantiatedTemplate(_) and
(blame = stmt or blame.(Expr).getEnclosingStmt() = stmt) and
msg =
"Function " + f.getName() + " should return a value of type " + f.getType().getName() +
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-190/AllocMultiplicationOverflow/AllocMultiplicationOverflow.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-190/AllocMultiplicationOverflow/AllocMultiplicationOverflow.expected
index b8428a43d11..03a070f3c91 100644
--- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-190/AllocMultiplicationOverflow/AllocMultiplicationOverflow.expected
+++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-190/AllocMultiplicationOverflow/AllocMultiplicationOverflow.expected
@@ -8,6 +8,7 @@ nodes
| test.cpp:23:33:23:37 | size1 | semmle.label | size1 |
| test.cpp:30:27:30:31 | ... * ... | semmle.label | ... * ... |
| test.cpp:31:27:31:31 | ... * ... | semmle.label | ... * ... |
+subpaths
#select
| test.cpp:13:33:13:37 | ... * ... | test.cpp:13:33:13:37 | ... * ... | test.cpp:13:33:13:37 | ... * ... | Potentially overflowing value from $@ is used in the size of this allocation. | test.cpp:13:33:13:37 | ... * ... | multiplication |
| test.cpp:15:31:15:35 | ... * ... | test.cpp:15:31:15:35 | ... * ... | test.cpp:15:31:15:35 | ... * ... | Potentially overflowing value from $@ is used in the size of this allocation. | test.cpp:15:31:15:35 | ... * ... | multiplication |
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-359/semmle/tests/PrivateCleartextWrite.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-359/semmle/tests/PrivateCleartextWrite.expected
index 887547f2c2e..d4ab9ad5e11 100644
--- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-359/semmle/tests/PrivateCleartextWrite.expected
+++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-359/semmle/tests/PrivateCleartextWrite.expected
@@ -1,8 +1,12 @@
edges
+| test.cpp:45:18:45:23 | buffer | test.cpp:47:10:47:15 | buffer |
| test.cpp:77:16:77:22 | medical | test.cpp:78:24:78:27 | temp |
| test.cpp:81:17:81:20 | call to func | test.cpp:82:24:82:28 | buff5 |
+| test.cpp:81:22:81:28 | medical | test.cpp:45:18:45:23 | buffer |
| test.cpp:81:22:81:28 | medical | test.cpp:81:17:81:20 | call to func |
nodes
+| test.cpp:45:18:45:23 | buffer | semmle.label | buffer |
+| test.cpp:47:10:47:15 | buffer | semmle.label | buffer |
| test.cpp:57:9:57:18 | theZipcode | semmle.label | theZipcode |
| test.cpp:74:24:74:30 | medical | semmle.label | medical |
| test.cpp:77:16:77:22 | medical | semmle.label | medical |
@@ -12,6 +16,8 @@ nodes
| test.cpp:82:24:82:28 | buff5 | semmle.label | buff5 |
| test.cpp:96:37:96:46 | theZipcode | semmle.label | theZipcode |
| test.cpp:99:42:99:51 | theZipcode | semmle.label | theZipcode |
+subpaths
+| test.cpp:81:22:81:28 | medical | test.cpp:45:18:45:23 | buffer | test.cpp:47:10:47:15 | buffer | test.cpp:81:17:81:20 | call to func |
#select
| test.cpp:57:9:57:18 | theZipcode | test.cpp:57:9:57:18 | theZipcode | test.cpp:57:9:57:18 | theZipcode | This write into the external location 'theZipcode' may contain unencrypted data from $@ | test.cpp:57:9:57:18 | theZipcode | this source. |
| test.cpp:74:24:74:30 | medical | test.cpp:74:24:74:30 | medical | test.cpp:74:24:74:30 | medical | This write into the external location 'medical' may contain unencrypted data from $@ | test.cpp:74:24:74:30 | medical | this source. |
diff --git a/cpp/ql/test/include/iterator.h b/cpp/ql/test/include/iterator.h
new file mode 100644
index 00000000000..77758bfa8da
--- /dev/null
+++ b/cpp/ql/test/include/iterator.h
@@ -0,0 +1,97 @@
+#if !defined(CODEQL_ITERATOR_H)
+#define CODEQL_ITERATOR_H
+
+typedef unsigned long size_t;
+
+#include "type_traits.h"
+
+namespace std {
+ struct ptrdiff_t;
+
+ template struct iterator_traits;
+
+ template
+ struct iterator {
+ typedef Category iterator_category;
+
+ iterator();
+ iterator(iterator > const &other); // non-const -> const conversion constructor
+
+ iterator &operator++();
+ iterator operator++(int);
+ iterator &operator--();
+ iterator operator--(int);
+ bool operator==(iterator other) const;
+ bool operator!=(iterator other) const;
+ reference_type operator*() const;
+ pointer_type operator->() const;
+ iterator operator+(int);
+ iterator operator-(int);
+ iterator &operator+=(int);
+ iterator &operator-=(int);
+ int operator-(iterator);
+ reference_type operator[](int);
+ };
+
+ struct input_iterator_tag {};
+ struct forward_iterator_tag : public input_iterator_tag {};
+ struct bidirectional_iterator_tag : public forward_iterator_tag {};
+ struct random_access_iterator_tag : public bidirectional_iterator_tag {};
+
+ struct output_iterator_tag {};
+
+ template
+ class back_insert_iterator {
+ protected:
+ Container* container = nullptr;
+ public:
+ using iterator_category = output_iterator_tag;
+ using value_type = void;
+ using difference_type = ptrdiff_t;
+ using pointer = void;
+ using reference = void;
+ using container_type = Container;
+ constexpr back_insert_iterator() noexcept = default;
+ constexpr explicit back_insert_iterator(Container& x);
+ back_insert_iterator& operator=(const typename Container::value_type& value);
+ back_insert_iterator& operator=(typename Container::value_type&& value);
+ back_insert_iterator& operator*();
+ back_insert_iterator& operator++();
+ back_insert_iterator operator++(int);
+ };
+
+ template
+ constexpr back_insert_iterator back_inserter(Container& x) {
+ return back_insert_iterator(x);
+ }
+
+ template
+ class front_insert_iterator {
+ protected:
+ Container* container = nullptr;
+ public:
+ using iterator_category = output_iterator_tag;
+ using value_type = void;
+ using difference_type = ptrdiff_t;
+ using pointer = void;
+ using reference = void;
+ using container_type = Container;
+ constexpr front_insert_iterator() noexcept = default;
+ constexpr explicit front_insert_iterator(Container& x);
+ constexpr front_insert_iterator& operator=(const typename Container::value_type& value);
+ constexpr front_insert_iterator& operator=(typename Container::value_type&& value);
+ constexpr front_insert_iterator& operator*();
+ constexpr front_insert_iterator& operator++();
+ constexpr front_insert_iterator operator++(int);
+ };
+ template
+ constexpr front_insert_iterator front_inserter(Container& x) {
+ return front_insert_iterator(x);
+ }
+}
+
+#endif
\ No newline at end of file
diff --git a/cpp/ql/test/include/string.h b/cpp/ql/test/include/string.h
new file mode 100644
index 00000000000..8d577c350f9
--- /dev/null
+++ b/cpp/ql/test/include/string.h
@@ -0,0 +1,85 @@
+#if !defined(CODEQL_STRING_H)
+#define CODEQL_STRING_H
+
+#include "iterator.h"
+
+namespace std
+{
+ template struct char_traits;
+
+ typedef size_t streamsize;
+
+ template class allocator {
+ public:
+ allocator() throw();
+ typedef size_t size_type;
+ };
+
+ template, class Allocator = allocator >
+ class basic_string {
+ public:
+ using value_type = charT;
+ using reference = value_type&;
+ using const_reference = const value_type&;
+ typedef typename Allocator::size_type size_type;
+ static const size_type npos = -1;
+
+ explicit basic_string(const Allocator& a = Allocator());
+ basic_string(const charT* s, const Allocator& a = Allocator());
+ template basic_string(InputIterator begin, InputIterator end, const Allocator& a = Allocator());
+
+ const charT* c_str() const;
+ charT* data() noexcept;
+ size_t length() const;
+
+ typedef std::iterator iterator;
+ typedef std::iterator const_iterator;
+
+ iterator begin();
+ iterator end();
+ const_iterator begin() const;
+ const_iterator end() const;
+ const_iterator cbegin() const;
+ const_iterator cend() const;
+
+ void push_back(charT c);
+
+ const charT& front() const;
+ charT& front();
+ const charT& back() const;
+ charT& back();
+
+ const_reference operator[](size_type pos) const;
+ reference operator[](size_type pos);
+ const_reference at(size_type n) const;
+ reference at(size_type n);
+ template basic_string& operator+=(const T& t);
+ basic_string& operator+=(const charT* s);
+ basic_string& append(const basic_string& str);
+ basic_string& append(const charT* s);
+ basic_string& append(size_type n, charT c);
+ template basic_string& append(InputIterator first, InputIterator last);
+ basic_string& assign(const basic_string& str);
+ basic_string& assign(size_type n, charT c);
+ template basic_string& assign(InputIterator first, InputIterator last);
+ basic_string& insert(size_type pos, const basic_string& str);
+ basic_string& insert(size_type pos, size_type n, charT c);
+ basic_string& insert(size_type pos, const charT* s);
+ iterator insert(const_iterator p, size_type n, charT c);
+ template iterator insert(const_iterator p, InputIterator first, InputIterator last);
+ basic_string& replace(size_type pos1, size_type n1, const basic_string& str);
+ basic_string& replace(size_type pos1, size_type n1, size_type n2, charT c);
+ size_type copy(charT* s, size_type n, size_type pos = 0) const;
+ void clear() noexcept;
+ basic_string substr(size_type pos = 0, size_type n = npos) const;
+ void swap(basic_string& s) noexcept/*(allocator_traits::propagate_on_container_swap::value || allocator_traits::is_always_equal::value)*/;
+ };
+
+ template basic_string operator+(const basic_string& lhs, const basic_string& rhs);
+ template basic_string operator+(const basic_string& lhs, const charT* rhs);
+ template basic_string operator+(const charT* lhs, const basic_string& rhs);
+
+ typedef basic_string string;
+}
+
+#endif
\ No newline at end of file
diff --git a/cpp/ql/test/include/type_traits.h b/cpp/ql/test/include/type_traits.h
index 19bdd46906b..dba04f36cad 100644
--- a/cpp/ql/test/include/type_traits.h
+++ b/cpp/ql/test/include/type_traits.h
@@ -1,21 +1,44 @@
#if !defined(CODEQL_TYPE_TRAITS_H)
#define CODEQL_TYPE_TRAITS_H
+typedef unsigned long size_t;
+
namespace std {
- template
- struct remove_reference {
+ template
+ struct remove_const { typedef T type; };
+
+ template
+ struct remove_const { typedef T type; };
+
+ // `remove_const_t` removes any `const` specifier from `T`
+ template
+ using remove_const_t = typename remove_const::type;
+
+ template
+ struct remove_reference { typedef T type; };
+
+ template
+ struct remove_reference { typedef T type; };
+
+ template
+ struct remove_reference { typedef T type; };
+
+ // `remove_reference_t` removes any `&` from `T`
+ template
+ using remove_reference_t = typename remove_reference::type;
+
+ template
+ struct decay_impl {
typedef T type;
};
- template
- struct remove_reference {
- typedef T type;
+ template
+ struct decay_impl {
+ typedef T* type;
};
- template
- struct remove_reference {
- typedef T type;
- };
+ template
+ using decay_t = typename decay_impl>::type;
}
#endif
diff --git a/cpp/ql/test/library-tests/dataflow/fields/ir-path-flow.expected b/cpp/ql/test/library-tests/dataflow/fields/ir-path-flow.expected
index 2736cdcd9e1..897d2e72fe5 100644
--- a/cpp/ql/test/library-tests/dataflow/fields/ir-path-flow.expected
+++ b/cpp/ql/test/library-tests/dataflow/fields/ir-path-flow.expected
@@ -1,11 +1,18 @@
edges
+| A.cpp:23:10:23:10 | c | A.cpp:25:7:25:17 | Chi [c] |
+| A.cpp:27:17:27:17 | c | A.cpp:27:22:27:32 | Chi [c] |
+| A.cpp:28:8:28:10 | *#this [c] | A.cpp:28:29:28:29 | Store |
| A.cpp:55:5:55:5 | set output argument [c] | A.cpp:56:10:56:10 | b indirection [c] |
+| A.cpp:55:8:55:10 | new | A.cpp:27:17:27:17 | c |
| A.cpp:55:8:55:10 | new | A.cpp:55:5:55:5 | set output argument [c] |
| A.cpp:55:12:55:19 | (C *)... | A.cpp:55:8:55:10 | new |
| A.cpp:55:12:55:19 | new | A.cpp:55:8:55:10 | new |
+| A.cpp:56:10:56:10 | b indirection [c] | A.cpp:28:8:28:10 | *#this [c] |
| A.cpp:56:10:56:10 | b indirection [c] | A.cpp:56:13:56:15 | call to get |
+| A.cpp:57:10:57:25 | new indirection [c] | A.cpp:28:8:28:10 | *#this [c] |
| A.cpp:57:10:57:25 | new indirection [c] | A.cpp:57:28:57:30 | call to get |
| A.cpp:57:11:57:24 | B output argument [c] | A.cpp:57:10:57:25 | new indirection [c] |
+| A.cpp:57:11:57:24 | new | A.cpp:23:10:23:10 | c |
| A.cpp:57:11:57:24 | new | A.cpp:57:11:57:24 | B output argument [c] |
| A.cpp:57:17:57:23 | new | A.cpp:57:11:57:24 | new |
| A.cpp:98:12:98:18 | new | A.cpp:100:5:100:13 | Chi [a] |
@@ -14,10 +21,12 @@ edges
| A.cpp:103:14:103:14 | *c [a] | A.cpp:107:16:107:16 | a |
| A.cpp:126:5:126:5 | Chi [c] | A.cpp:131:8:131:8 | f7 output argument [c] |
| A.cpp:126:5:126:5 | set output argument [c] | A.cpp:126:5:126:5 | Chi [c] |
+| A.cpp:126:8:126:10 | new | A.cpp:27:17:27:17 | c |
| A.cpp:126:8:126:10 | new | A.cpp:126:5:126:5 | set output argument [c] |
| A.cpp:126:12:126:18 | new | A.cpp:126:8:126:10 | new |
| A.cpp:131:8:131:8 | Chi [c] | A.cpp:132:13:132:13 | c |
| A.cpp:131:8:131:8 | f7 output argument [c] | A.cpp:131:8:131:8 | Chi [c] |
+| A.cpp:140:13:140:13 | b | A.cpp:143:7:143:31 | Chi [b] |
| A.cpp:142:7:142:20 | Chi [c] | A.cpp:151:18:151:18 | D output argument [c] |
| A.cpp:142:14:142:20 | new | A.cpp:142:7:142:20 | Chi [c] |
| A.cpp:143:7:143:31 | Chi [b] | A.cpp:151:12:151:24 | D output argument [b] |
@@ -25,6 +34,7 @@ edges
| A.cpp:150:12:150:18 | new | A.cpp:151:12:151:24 | b |
| A.cpp:151:12:151:24 | Chi [b] | A.cpp:152:13:152:13 | b |
| A.cpp:151:12:151:24 | D output argument [b] | A.cpp:151:12:151:24 | Chi [b] |
+| A.cpp:151:12:151:24 | b | A.cpp:140:13:140:13 | b |
| A.cpp:151:12:151:24 | b | A.cpp:151:12:151:24 | D output argument [b] |
| A.cpp:151:18:151:18 | Chi [c] | A.cpp:154:13:154:13 | c |
| A.cpp:151:18:151:18 | D output argument [c] | A.cpp:151:18:151:18 | Chi [c] |
@@ -86,21 +96,49 @@ edges
| arrays.cpp:6:12:6:21 | call to user_input | arrays.cpp:10:8:10:15 | * ... |
| arrays.cpp:15:14:15:23 | call to user_input | arrays.cpp:16:8:16:13 | access to array |
| arrays.cpp:36:26:36:35 | call to user_input | arrays.cpp:37:24:37:27 | data |
+| by_reference.cpp:11:48:11:52 | value | by_reference.cpp:12:5:12:16 | Chi [a] |
+| by_reference.cpp:15:26:15:30 | value | by_reference.cpp:16:5:16:19 | Chi [a] |
+| by_reference.cpp:19:28:19:32 | value | by_reference.cpp:20:11:20:21 | value |
+| by_reference.cpp:20:5:20:8 | setDirectly output argument [a] | by_reference.cpp:20:5:20:8 | Chi [a] |
+| by_reference.cpp:20:11:20:21 | value | by_reference.cpp:15:26:15:30 | value |
+| by_reference.cpp:20:11:20:21 | value | by_reference.cpp:20:5:20:8 | setDirectly output argument [a] |
+| by_reference.cpp:23:34:23:38 | value | by_reference.cpp:24:5:24:17 | value |
+| by_reference.cpp:24:5:24:17 | value | by_reference.cpp:11:48:11:52 | value |
+| by_reference.cpp:24:5:24:17 | value | by_reference.cpp:24:19:24:22 | nonMemberSetA output argument [a] |
+| by_reference.cpp:24:19:24:22 | nonMemberSetA output argument [a] | by_reference.cpp:24:19:24:22 | Chi [a] |
+| by_reference.cpp:31:46:31:46 | *s [a] | by_reference.cpp:32:15:32:15 | Store |
+| by_reference.cpp:35:9:35:19 | *#this [a] | by_reference.cpp:36:18:36:18 | Store |
+| by_reference.cpp:39:9:39:21 | *#this [a] | by_reference.cpp:40:12:40:15 | this indirection [a] |
+| by_reference.cpp:40:12:40:15 | this indirection [a] | by_reference.cpp:35:9:35:19 | *#this [a] |
+| by_reference.cpp:40:12:40:15 | this indirection [a] | by_reference.cpp:40:18:40:28 | call to getDirectly |
+| by_reference.cpp:40:18:40:28 | call to getDirectly | by_reference.cpp:40:18:40:28 | Store |
+| by_reference.cpp:43:9:43:27 | *#this [a] | by_reference.cpp:44:26:44:29 | this indirection [a] |
+| by_reference.cpp:44:12:44:24 | call to nonMemberGetA | by_reference.cpp:44:12:44:24 | Store |
+| by_reference.cpp:44:26:44:29 | this indirection [a] | by_reference.cpp:31:46:31:46 | *s [a] |
+| by_reference.cpp:44:26:44:29 | this indirection [a] | by_reference.cpp:44:12:44:24 | call to nonMemberGetA |
| by_reference.cpp:50:3:50:3 | setDirectly output argument [a] | by_reference.cpp:51:8:51:8 | s indirection [a] |
+| by_reference.cpp:50:5:50:15 | call to user_input | by_reference.cpp:15:26:15:30 | value |
| by_reference.cpp:50:5:50:15 | call to user_input | by_reference.cpp:50:3:50:3 | setDirectly output argument [a] |
| by_reference.cpp:50:17:50:26 | call to user_input | by_reference.cpp:50:5:50:15 | call to user_input |
+| by_reference.cpp:51:8:51:8 | s indirection [a] | by_reference.cpp:35:9:35:19 | *#this [a] |
| by_reference.cpp:51:8:51:8 | s indirection [a] | by_reference.cpp:51:10:51:20 | call to getDirectly |
| by_reference.cpp:56:3:56:3 | setIndirectly output argument [a] | by_reference.cpp:57:8:57:8 | s indirection [a] |
+| by_reference.cpp:56:5:56:17 | call to user_input | by_reference.cpp:19:28:19:32 | value |
| by_reference.cpp:56:5:56:17 | call to user_input | by_reference.cpp:56:3:56:3 | setIndirectly output argument [a] |
| by_reference.cpp:56:19:56:28 | call to user_input | by_reference.cpp:56:5:56:17 | call to user_input |
+| by_reference.cpp:57:8:57:8 | s indirection [a] | by_reference.cpp:39:9:39:21 | *#this [a] |
| by_reference.cpp:57:8:57:8 | s indirection [a] | by_reference.cpp:57:10:57:22 | call to getIndirectly |
| by_reference.cpp:62:3:62:3 | setThroughNonMember output argument [a] | by_reference.cpp:63:8:63:8 | s indirection [a] |
+| by_reference.cpp:62:5:62:23 | call to user_input | by_reference.cpp:23:34:23:38 | value |
| by_reference.cpp:62:5:62:23 | call to user_input | by_reference.cpp:62:3:62:3 | setThroughNonMember output argument [a] |
| by_reference.cpp:62:25:62:34 | call to user_input | by_reference.cpp:62:5:62:23 | call to user_input |
+| by_reference.cpp:63:8:63:8 | s indirection [a] | by_reference.cpp:43:9:43:27 | *#this [a] |
| by_reference.cpp:63:8:63:8 | s indirection [a] | by_reference.cpp:63:10:63:28 | call to getThroughNonMember |
+| by_reference.cpp:68:3:68:15 | call to user_input | by_reference.cpp:11:48:11:52 | value |
| by_reference.cpp:68:3:68:15 | call to user_input | by_reference.cpp:68:17:68:18 | nonMemberSetA output argument [a] |
| by_reference.cpp:68:17:68:18 | nonMemberSetA output argument [a] | by_reference.cpp:69:22:69:23 | & ... indirection [a] |
| by_reference.cpp:68:21:68:30 | call to user_input | by_reference.cpp:68:3:68:15 | call to user_input |
+| by_reference.cpp:69:22:69:23 | & ... indirection [a] | by_reference.cpp:31:46:31:46 | *s [a] |
| by_reference.cpp:69:22:69:23 | & ... indirection [a] | by_reference.cpp:69:8:69:20 | call to nonMemberGetA |
| by_reference.cpp:84:3:84:25 | Chi [a] | by_reference.cpp:102:21:102:39 | taint_inner_a_ptr output argument [a] |
| by_reference.cpp:84:3:84:25 | Chi [a] | by_reference.cpp:106:21:106:41 | taint_inner_a_ptr output argument [a] |
@@ -134,48 +172,73 @@ edges
| by_reference.cpp:128:15:128:23 | Chi | by_reference.cpp:128:15:128:23 | Chi [a] |
| by_reference.cpp:128:15:128:23 | Chi [a] | by_reference.cpp:136:16:136:16 | a |
| by_reference.cpp:128:15:128:23 | taint_a_ref output argument [[]] | by_reference.cpp:128:15:128:23 | Chi |
+| complex.cpp:9:7:9:7 | *#this [a_] | complex.cpp:9:20:9:21 | Store |
+| complex.cpp:10:7:10:7 | *#this [b_] | complex.cpp:10:20:10:21 | Store |
+| complex.cpp:11:17:11:17 | a | complex.cpp:11:22:11:27 | Chi [a_] |
+| complex.cpp:12:8:12:11 | *#this [a_] | complex.cpp:12:22:12:27 | Chi [a_] |
+| complex.cpp:12:17:12:17 | b | complex.cpp:12:22:12:27 | Chi [b_] |
| complex.cpp:40:17:40:17 | *b [a_] | complex.cpp:42:16:42:16 | f indirection [a_] |
| complex.cpp:40:17:40:17 | *b [b_] | complex.cpp:42:16:42:16 | f indirection [b_] |
| complex.cpp:40:17:40:17 | *b [b_] | complex.cpp:43:16:43:16 | f indirection [b_] |
| complex.cpp:42:16:42:16 | a output argument [b_] | complex.cpp:43:16:43:16 | f indirection [b_] |
+| complex.cpp:42:16:42:16 | f indirection [a_] | complex.cpp:9:7:9:7 | *#this [a_] |
| complex.cpp:42:16:42:16 | f indirection [a_] | complex.cpp:42:18:42:18 | call to a |
+| complex.cpp:42:16:42:16 | f indirection [b_] | complex.cpp:9:7:9:7 | *#this [b_] |
| complex.cpp:42:16:42:16 | f indirection [b_] | complex.cpp:42:16:42:16 | a output argument [b_] |
+| complex.cpp:43:16:43:16 | f indirection [b_] | complex.cpp:10:7:10:7 | *#this [b_] |
| complex.cpp:43:16:43:16 | f indirection [b_] | complex.cpp:43:18:43:18 | call to b |
| complex.cpp:53:12:53:12 | setA output argument [a_] | complex.cpp:59:7:59:8 | b1 indirection [a_] |
+| complex.cpp:53:14:53:17 | call to user_input | complex.cpp:11:17:11:17 | a |
| complex.cpp:53:14:53:17 | call to user_input | complex.cpp:53:12:53:12 | setA output argument [a_] |
| complex.cpp:53:19:53:28 | call to user_input | complex.cpp:53:14:53:17 | call to user_input |
| complex.cpp:54:12:54:12 | setB output argument [b_] | complex.cpp:62:7:62:8 | b2 indirection [b_] |
+| complex.cpp:54:14:54:17 | call to user_input | complex.cpp:12:17:12:17 | b |
| complex.cpp:54:14:54:17 | call to user_input | complex.cpp:54:12:54:12 | setB output argument [b_] |
| complex.cpp:54:19:54:28 | call to user_input | complex.cpp:54:14:54:17 | call to user_input |
| complex.cpp:55:12:55:12 | setA output argument [a_] | complex.cpp:56:12:56:12 | f indirection [a_] |
| complex.cpp:55:12:55:12 | setA output argument [a_] | complex.cpp:65:7:65:8 | b3 indirection [a_] |
+| complex.cpp:55:14:55:17 | call to user_input | complex.cpp:11:17:11:17 | a |
| complex.cpp:55:14:55:17 | call to user_input | complex.cpp:55:12:55:12 | setA output argument [a_] |
| complex.cpp:55:19:55:28 | call to user_input | complex.cpp:55:14:55:17 | call to user_input |
+| complex.cpp:56:12:56:12 | f indirection [a_] | complex.cpp:12:8:12:11 | *#this [a_] |
| complex.cpp:56:12:56:12 | f indirection [a_] | complex.cpp:56:12:56:12 | setB output argument [a_] |
| complex.cpp:56:12:56:12 | setB output argument [a_] | complex.cpp:65:7:65:8 | b3 indirection [a_] |
| complex.cpp:56:12:56:12 | setB output argument [b_] | complex.cpp:65:7:65:8 | b3 indirection [b_] |
+| complex.cpp:56:14:56:17 | call to user_input | complex.cpp:12:17:12:17 | b |
| complex.cpp:56:14:56:17 | call to user_input | complex.cpp:56:12:56:12 | setB output argument [b_] |
| complex.cpp:56:19:56:28 | call to user_input | complex.cpp:56:14:56:17 | call to user_input |
| complex.cpp:59:7:59:8 | b1 indirection [a_] | complex.cpp:40:17:40:17 | *b [a_] |
| complex.cpp:62:7:62:8 | b2 indirection [b_] | complex.cpp:40:17:40:17 | *b [b_] |
| complex.cpp:65:7:65:8 | b3 indirection [a_] | complex.cpp:40:17:40:17 | *b [a_] |
| complex.cpp:65:7:65:8 | b3 indirection [b_] | complex.cpp:40:17:40:17 | *b [b_] |
+| constructors.cpp:18:9:18:9 | *#this [a_] | constructors.cpp:18:22:18:23 | Store |
+| constructors.cpp:19:9:19:9 | *#this [b_] | constructors.cpp:19:22:19:23 | Store |
+| constructors.cpp:23:13:23:13 | a | constructors.cpp:23:28:23:28 | Chi [a_] |
+| constructors.cpp:23:20:23:20 | b | constructors.cpp:23:35:23:35 | Chi [b_] |
+| constructors.cpp:23:28:23:28 | Chi [a_] | constructors.cpp:23:35:23:35 | Chi [a_] |
| constructors.cpp:26:15:26:15 | *f [a_] | constructors.cpp:28:10:28:10 | f indirection [a_] |
| constructors.cpp:26:15:26:15 | *f [b_] | constructors.cpp:28:10:28:10 | f indirection [b_] |
| constructors.cpp:26:15:26:15 | *f [b_] | constructors.cpp:29:10:29:10 | f indirection [b_] |
| constructors.cpp:28:10:28:10 | a output argument [b_] | constructors.cpp:29:10:29:10 | f indirection [b_] |
+| constructors.cpp:28:10:28:10 | f indirection [a_] | constructors.cpp:18:9:18:9 | *#this [a_] |
| constructors.cpp:28:10:28:10 | f indirection [a_] | constructors.cpp:28:12:28:12 | call to a |
+| constructors.cpp:28:10:28:10 | f indirection [b_] | constructors.cpp:18:9:18:9 | *#this [b_] |
| constructors.cpp:28:10:28:10 | f indirection [b_] | constructors.cpp:28:10:28:10 | a output argument [b_] |
+| constructors.cpp:29:10:29:10 | f indirection [b_] | constructors.cpp:19:9:19:9 | *#this [b_] |
| constructors.cpp:29:10:29:10 | f indirection [b_] | constructors.cpp:29:12:29:12 | call to b |
| constructors.cpp:34:11:34:20 | call to user_input | constructors.cpp:34:11:34:26 | call to user_input |
| constructors.cpp:34:11:34:26 | Foo output argument [a_] | constructors.cpp:40:9:40:9 | f indirection [a_] |
+| constructors.cpp:34:11:34:26 | call to user_input | constructors.cpp:23:13:23:13 | a |
| constructors.cpp:34:11:34:26 | call to user_input | constructors.cpp:34:11:34:26 | Foo output argument [a_] |
| constructors.cpp:35:11:35:26 | Foo output argument [b_] | constructors.cpp:43:9:43:9 | g indirection [b_] |
+| constructors.cpp:35:11:35:26 | call to user_input | constructors.cpp:23:20:23:20 | b |
| constructors.cpp:35:11:35:26 | call to user_input | constructors.cpp:35:11:35:26 | Foo output argument [b_] |
| constructors.cpp:35:14:35:23 | call to user_input | constructors.cpp:35:11:35:26 | call to user_input |
| constructors.cpp:36:11:36:20 | call to user_input | constructors.cpp:36:11:36:37 | call to user_input |
| constructors.cpp:36:11:36:37 | Foo output argument [a_] | constructors.cpp:46:9:46:9 | h indirection [a_] |
| constructors.cpp:36:11:36:37 | Foo output argument [b_] | constructors.cpp:46:9:46:9 | h indirection [b_] |
+| constructors.cpp:36:11:36:37 | call to user_input | constructors.cpp:23:13:23:13 | a |
+| constructors.cpp:36:11:36:37 | call to user_input | constructors.cpp:23:20:23:20 | b |
| constructors.cpp:36:11:36:37 | call to user_input | constructors.cpp:36:11:36:37 | Foo output argument [a_] |
| constructors.cpp:36:11:36:37 | call to user_input | constructors.cpp:36:11:36:37 | Foo output argument [b_] |
| constructors.cpp:36:25:36:34 | call to user_input | constructors.cpp:36:11:36:37 | call to user_input |
@@ -183,26 +246,39 @@ edges
| constructors.cpp:43:9:43:9 | g indirection [b_] | constructors.cpp:26:15:26:15 | *f [b_] |
| constructors.cpp:46:9:46:9 | h indirection [a_] | constructors.cpp:26:15:26:15 | *f [a_] |
| constructors.cpp:46:9:46:9 | h indirection [b_] | constructors.cpp:26:15:26:15 | *f [b_] |
+| simple.cpp:18:9:18:9 | *#this [a_] | simple.cpp:18:22:18:23 | Store |
+| simple.cpp:19:9:19:9 | *#this [b_] | simple.cpp:19:22:19:23 | Store |
+| simple.cpp:20:19:20:19 | a | simple.cpp:20:24:20:29 | Chi [a_] |
+| simple.cpp:21:10:21:13 | *#this [a_] | simple.cpp:21:24:21:29 | Chi [a_] |
+| simple.cpp:21:19:21:19 | b | simple.cpp:21:24:21:29 | Chi [b_] |
| simple.cpp:26:15:26:15 | *f [a_] | simple.cpp:28:10:28:10 | f indirection [a_] |
| simple.cpp:26:15:26:15 | *f [b_] | simple.cpp:28:10:28:10 | f indirection [b_] |
| simple.cpp:26:15:26:15 | *f [b_] | simple.cpp:29:10:29:10 | f indirection [b_] |
| simple.cpp:28:10:28:10 | a output argument [b_] | simple.cpp:29:10:29:10 | f indirection [b_] |
+| simple.cpp:28:10:28:10 | f indirection [a_] | simple.cpp:18:9:18:9 | *#this [a_] |
| simple.cpp:28:10:28:10 | f indirection [a_] | simple.cpp:28:12:28:12 | call to a |
+| simple.cpp:28:10:28:10 | f indirection [b_] | simple.cpp:18:9:18:9 | *#this [b_] |
| simple.cpp:28:10:28:10 | f indirection [b_] | simple.cpp:28:10:28:10 | a output argument [b_] |
+| simple.cpp:29:10:29:10 | f indirection [b_] | simple.cpp:19:9:19:9 | *#this [b_] |
| simple.cpp:29:10:29:10 | f indirection [b_] | simple.cpp:29:12:29:12 | call to b |
| simple.cpp:39:5:39:5 | setA output argument [a_] | simple.cpp:45:9:45:9 | f indirection [a_] |
+| simple.cpp:39:7:39:10 | call to user_input | simple.cpp:20:19:20:19 | a |
| simple.cpp:39:7:39:10 | call to user_input | simple.cpp:39:5:39:5 | setA output argument [a_] |
| simple.cpp:39:12:39:21 | call to user_input | simple.cpp:39:7:39:10 | call to user_input |
| simple.cpp:40:5:40:5 | setB output argument [b_] | simple.cpp:48:9:48:9 | g indirection [b_] |
+| simple.cpp:40:7:40:10 | call to user_input | simple.cpp:21:19:21:19 | b |
| simple.cpp:40:7:40:10 | call to user_input | simple.cpp:40:5:40:5 | setB output argument [b_] |
| simple.cpp:40:12:40:21 | call to user_input | simple.cpp:40:7:40:10 | call to user_input |
| simple.cpp:41:5:41:5 | setA output argument [a_] | simple.cpp:42:5:42:5 | h indirection [a_] |
| simple.cpp:41:5:41:5 | setA output argument [a_] | simple.cpp:51:9:51:9 | h indirection [a_] |
+| simple.cpp:41:7:41:10 | call to user_input | simple.cpp:20:19:20:19 | a |
| simple.cpp:41:7:41:10 | call to user_input | simple.cpp:41:5:41:5 | setA output argument [a_] |
| simple.cpp:41:12:41:21 | call to user_input | simple.cpp:41:7:41:10 | call to user_input |
+| simple.cpp:42:5:42:5 | h indirection [a_] | simple.cpp:21:10:21:13 | *#this [a_] |
| simple.cpp:42:5:42:5 | h indirection [a_] | simple.cpp:42:5:42:5 | setB output argument [a_] |
| simple.cpp:42:5:42:5 | setB output argument [a_] | simple.cpp:51:9:51:9 | h indirection [a_] |
| simple.cpp:42:5:42:5 | setB output argument [b_] | simple.cpp:51:9:51:9 | h indirection [b_] |
+| simple.cpp:42:7:42:10 | call to user_input | simple.cpp:21:19:21:19 | b |
| simple.cpp:42:7:42:10 | call to user_input | simple.cpp:42:5:42:5 | setB output argument [b_] |
| simple.cpp:42:12:42:21 | call to user_input | simple.cpp:42:7:42:10 | call to user_input |
| simple.cpp:45:9:45:9 | f indirection [a_] | simple.cpp:26:15:26:15 | *f [a_] |
@@ -212,8 +288,10 @@ edges
| simple.cpp:65:5:65:22 | Store [i] | simple.cpp:66:12:66:12 | Store [i] |
| simple.cpp:65:11:65:20 | call to user_input | simple.cpp:65:5:65:22 | Store [i] |
| simple.cpp:66:12:66:12 | Store [i] | simple.cpp:67:13:67:13 | i |
+| simple.cpp:78:9:78:15 | *#this [f1] | simple.cpp:79:19:79:20 | Store |
| simple.cpp:83:9:83:28 | Store [f1] | simple.cpp:84:14:84:20 | this indirection [f1] |
| simple.cpp:83:17:83:26 | call to user_input | simple.cpp:83:9:83:28 | Store [f1] |
+| simple.cpp:84:14:84:20 | this indirection [f1] | simple.cpp:78:9:78:15 | *#this [f1] |
| simple.cpp:84:14:84:20 | this indirection [f1] | simple.cpp:84:14:84:20 | call to getf2f1 |
| simple.cpp:92:5:92:22 | Store [i] | simple.cpp:93:20:93:20 | Store [i] |
| simple.cpp:92:11:92:20 | call to user_input | simple.cpp:92:5:92:22 | Store [i] |
@@ -228,6 +306,12 @@ edges
| struct_init.c:27:7:27:16 | call to user_input | struct_init.c:31:23:31:23 | a |
| struct_init.c:36:10:36:24 | & ... indirection [a] | struct_init.c:14:24:14:25 | *ab [a] |
nodes
+| A.cpp:23:10:23:10 | c | semmle.label | c |
+| A.cpp:25:7:25:17 | Chi [c] | semmle.label | Chi [c] |
+| A.cpp:27:17:27:17 | c | semmle.label | c |
+| A.cpp:27:22:27:32 | Chi [c] | semmle.label | Chi [c] |
+| A.cpp:28:8:28:10 | *#this [c] | semmle.label | *#this [c] |
+| A.cpp:28:29:28:29 | Store | semmle.label | Store |
| A.cpp:55:5:55:5 | set output argument [c] | semmle.label | set output argument [c] |
| A.cpp:55:8:55:10 | new | semmle.label | new |
| A.cpp:55:12:55:19 | (C *)... | semmle.label | (C *)... |
@@ -251,9 +335,11 @@ nodes
| A.cpp:131:8:131:8 | Chi [c] | semmle.label | Chi [c] |
| A.cpp:131:8:131:8 | f7 output argument [c] | semmle.label | f7 output argument [c] |
| A.cpp:132:13:132:13 | c | semmle.label | c |
+| A.cpp:140:13:140:13 | b | semmle.label | b |
| A.cpp:142:7:142:20 | Chi [c] | semmle.label | Chi [c] |
| A.cpp:142:14:142:20 | new | semmle.label | new |
| A.cpp:143:7:143:31 | Chi [b] | semmle.label | Chi [b] |
+| A.cpp:143:7:143:31 | Chi [b] | semmle.label | Chi [b] |
| A.cpp:143:25:143:31 | new | semmle.label | new |
| A.cpp:150:12:150:18 | new | semmle.label | new |
| A.cpp:151:12:151:24 | Chi [b] | semmle.label | Chi [b] |
@@ -336,6 +422,30 @@ nodes
| arrays.cpp:16:8:16:13 | access to array | semmle.label | access to array |
| arrays.cpp:36:26:36:35 | call to user_input | semmle.label | call to user_input |
| arrays.cpp:37:24:37:27 | data | semmle.label | data |
+| by_reference.cpp:11:48:11:52 | value | semmle.label | value |
+| by_reference.cpp:12:5:12:16 | Chi [a] | semmle.label | Chi [a] |
+| by_reference.cpp:15:26:15:30 | value | semmle.label | value |
+| by_reference.cpp:16:5:16:19 | Chi [a] | semmle.label | Chi [a] |
+| by_reference.cpp:19:28:19:32 | value | semmle.label | value |
+| by_reference.cpp:20:5:20:8 | Chi [a] | semmle.label | Chi [a] |
+| by_reference.cpp:20:5:20:8 | setDirectly output argument [a] | semmle.label | setDirectly output argument [a] |
+| by_reference.cpp:20:11:20:21 | value | semmle.label | value |
+| by_reference.cpp:23:34:23:38 | value | semmle.label | value |
+| by_reference.cpp:24:5:24:17 | value | semmle.label | value |
+| by_reference.cpp:24:19:24:22 | Chi [a] | semmle.label | Chi [a] |
+| by_reference.cpp:24:19:24:22 | nonMemberSetA output argument [a] | semmle.label | nonMemberSetA output argument [a] |
+| by_reference.cpp:31:46:31:46 | *s [a] | semmle.label | *s [a] |
+| by_reference.cpp:32:15:32:15 | Store | semmle.label | Store |
+| by_reference.cpp:35:9:35:19 | *#this [a] | semmle.label | *#this [a] |
+| by_reference.cpp:36:18:36:18 | Store | semmle.label | Store |
+| by_reference.cpp:39:9:39:21 | *#this [a] | semmle.label | *#this [a] |
+| by_reference.cpp:40:12:40:15 | this indirection [a] | semmle.label | this indirection [a] |
+| by_reference.cpp:40:18:40:28 | Store | semmle.label | Store |
+| by_reference.cpp:40:18:40:28 | call to getDirectly | semmle.label | call to getDirectly |
+| by_reference.cpp:43:9:43:27 | *#this [a] | semmle.label | *#this [a] |
+| by_reference.cpp:44:12:44:24 | Store | semmle.label | Store |
+| by_reference.cpp:44:12:44:24 | call to nonMemberGetA | semmle.label | call to nonMemberGetA |
+| by_reference.cpp:44:26:44:29 | this indirection [a] | semmle.label | this indirection [a] |
| by_reference.cpp:50:3:50:3 | setDirectly output argument [a] | semmle.label | setDirectly output argument [a] |
| by_reference.cpp:50:5:50:15 | call to user_input | semmle.label | call to user_input |
| by_reference.cpp:50:17:50:26 | call to user_input | semmle.label | call to user_input |
@@ -392,6 +502,17 @@ nodes
| by_reference.cpp:132:14:132:14 | a | semmle.label | a |
| by_reference.cpp:134:29:134:29 | a | semmle.label | a |
| by_reference.cpp:136:16:136:16 | a | semmle.label | a |
+| complex.cpp:9:7:9:7 | *#this [a_] | semmle.label | *#this [a_] |
+| complex.cpp:9:7:9:7 | *#this [b_] | semmle.label | *#this [b_] |
+| complex.cpp:9:20:9:21 | Store | semmle.label | Store |
+| complex.cpp:10:7:10:7 | *#this [b_] | semmle.label | *#this [b_] |
+| complex.cpp:10:20:10:21 | Store | semmle.label | Store |
+| complex.cpp:11:17:11:17 | a | semmle.label | a |
+| complex.cpp:11:22:11:27 | Chi [a_] | semmle.label | Chi [a_] |
+| complex.cpp:12:8:12:11 | *#this [a_] | semmle.label | *#this [a_] |
+| complex.cpp:12:17:12:17 | b | semmle.label | b |
+| complex.cpp:12:22:12:27 | Chi [a_] | semmle.label | Chi [a_] |
+| complex.cpp:12:22:12:27 | Chi [b_] | semmle.label | Chi [b_] |
| complex.cpp:40:17:40:17 | *b [a_] | semmle.label | *b [a_] |
| complex.cpp:40:17:40:17 | *b [b_] | semmle.label | *b [b_] |
| complex.cpp:42:16:42:16 | a output argument [b_] | semmle.label | a output argument [b_] |
@@ -418,6 +539,16 @@ nodes
| complex.cpp:62:7:62:8 | b2 indirection [b_] | semmle.label | b2 indirection [b_] |
| complex.cpp:65:7:65:8 | b3 indirection [a_] | semmle.label | b3 indirection [a_] |
| complex.cpp:65:7:65:8 | b3 indirection [b_] | semmle.label | b3 indirection [b_] |
+| constructors.cpp:18:9:18:9 | *#this [a_] | semmle.label | *#this [a_] |
+| constructors.cpp:18:9:18:9 | *#this [b_] | semmle.label | *#this [b_] |
+| constructors.cpp:18:22:18:23 | Store | semmle.label | Store |
+| constructors.cpp:19:9:19:9 | *#this [b_] | semmle.label | *#this [b_] |
+| constructors.cpp:19:22:19:23 | Store | semmle.label | Store |
+| constructors.cpp:23:13:23:13 | a | semmle.label | a |
+| constructors.cpp:23:20:23:20 | b | semmle.label | b |
+| constructors.cpp:23:28:23:28 | Chi [a_] | semmle.label | Chi [a_] |
+| constructors.cpp:23:35:23:35 | Chi [a_] | semmle.label | Chi [a_] |
+| constructors.cpp:23:35:23:35 | Chi [b_] | semmle.label | Chi [b_] |
| constructors.cpp:26:15:26:15 | *f [a_] | semmle.label | *f [a_] |
| constructors.cpp:26:15:26:15 | *f [b_] | semmle.label | *f [b_] |
| constructors.cpp:28:10:28:10 | a output argument [b_] | semmle.label | a output argument [b_] |
@@ -442,6 +573,17 @@ nodes
| constructors.cpp:43:9:43:9 | g indirection [b_] | semmle.label | g indirection [b_] |
| constructors.cpp:46:9:46:9 | h indirection [a_] | semmle.label | h indirection [a_] |
| constructors.cpp:46:9:46:9 | h indirection [b_] | semmle.label | h indirection [b_] |
+| simple.cpp:18:9:18:9 | *#this [a_] | semmle.label | *#this [a_] |
+| simple.cpp:18:9:18:9 | *#this [b_] | semmle.label | *#this [b_] |
+| simple.cpp:18:22:18:23 | Store | semmle.label | Store |
+| simple.cpp:19:9:19:9 | *#this [b_] | semmle.label | *#this [b_] |
+| simple.cpp:19:22:19:23 | Store | semmle.label | Store |
+| simple.cpp:20:19:20:19 | a | semmle.label | a |
+| simple.cpp:20:24:20:29 | Chi [a_] | semmle.label | Chi [a_] |
+| simple.cpp:21:10:21:13 | *#this [a_] | semmle.label | *#this [a_] |
+| simple.cpp:21:19:21:19 | b | semmle.label | b |
+| simple.cpp:21:24:21:29 | Chi [a_] | semmle.label | Chi [a_] |
+| simple.cpp:21:24:21:29 | Chi [b_] | semmle.label | Chi [b_] |
| simple.cpp:26:15:26:15 | *f [a_] | semmle.label | *f [a_] |
| simple.cpp:26:15:26:15 | *f [b_] | semmle.label | *f [b_] |
| simple.cpp:28:10:28:10 | a output argument [b_] | semmle.label | a output argument [b_] |
@@ -472,6 +614,8 @@ nodes
| simple.cpp:65:11:65:20 | call to user_input | semmle.label | call to user_input |
| simple.cpp:66:12:66:12 | Store [i] | semmle.label | Store [i] |
| simple.cpp:67:13:67:13 | i | semmle.label | i |
+| simple.cpp:78:9:78:15 | *#this [f1] | semmle.label | *#this [f1] |
+| simple.cpp:79:19:79:20 | Store | semmle.label | Store |
| simple.cpp:83:9:83:28 | Store [f1] | semmle.label | Store [f1] |
| simple.cpp:83:17:83:26 | call to user_input | semmle.label | call to user_input |
| simple.cpp:84:14:84:20 | call to getf2f1 | semmle.label | call to getf2f1 |
@@ -490,6 +634,49 @@ nodes
| struct_init.c:27:7:27:16 | call to user_input | semmle.label | call to user_input |
| struct_init.c:31:23:31:23 | a | semmle.label | a |
| struct_init.c:36:10:36:24 | & ... indirection [a] | semmle.label | & ... indirection [a] |
+subpaths
+| A.cpp:55:8:55:10 | new | A.cpp:27:17:27:17 | c | A.cpp:27:22:27:32 | Chi [c] | A.cpp:55:5:55:5 | set output argument [c] |
+| A.cpp:56:10:56:10 | b indirection [c] | A.cpp:28:8:28:10 | *#this [c] | A.cpp:28:29:28:29 | Store | A.cpp:56:13:56:15 | call to get |
+| A.cpp:57:10:57:25 | new indirection [c] | A.cpp:28:8:28:10 | *#this [c] | A.cpp:28:29:28:29 | Store | A.cpp:57:28:57:30 | call to get |
+| A.cpp:57:11:57:24 | new | A.cpp:23:10:23:10 | c | A.cpp:25:7:25:17 | Chi [c] | A.cpp:57:11:57:24 | B output argument [c] |
+| A.cpp:126:8:126:10 | new | A.cpp:27:17:27:17 | c | A.cpp:27:22:27:32 | Chi [c] | A.cpp:126:5:126:5 | set output argument [c] |
+| A.cpp:151:12:151:24 | b | A.cpp:140:13:140:13 | b | A.cpp:143:7:143:31 | Chi [b] | A.cpp:151:12:151:24 | D output argument [b] |
+| by_reference.cpp:20:11:20:21 | value | by_reference.cpp:15:26:15:30 | value | by_reference.cpp:16:5:16:19 | Chi [a] | by_reference.cpp:20:5:20:8 | setDirectly output argument [a] |
+| by_reference.cpp:24:5:24:17 | value | by_reference.cpp:11:48:11:52 | value | by_reference.cpp:12:5:12:16 | Chi [a] | by_reference.cpp:24:19:24:22 | nonMemberSetA output argument [a] |
+| by_reference.cpp:40:12:40:15 | this indirection [a] | by_reference.cpp:35:9:35:19 | *#this [a] | by_reference.cpp:36:18:36:18 | Store | by_reference.cpp:40:18:40:28 | call to getDirectly |
+| by_reference.cpp:44:26:44:29 | this indirection [a] | by_reference.cpp:31:46:31:46 | *s [a] | by_reference.cpp:32:15:32:15 | Store | by_reference.cpp:44:12:44:24 | call to nonMemberGetA |
+| by_reference.cpp:50:5:50:15 | call to user_input | by_reference.cpp:15:26:15:30 | value | by_reference.cpp:16:5:16:19 | Chi [a] | by_reference.cpp:50:3:50:3 | setDirectly output argument [a] |
+| by_reference.cpp:51:8:51:8 | s indirection [a] | by_reference.cpp:35:9:35:19 | *#this [a] | by_reference.cpp:36:18:36:18 | Store | by_reference.cpp:51:10:51:20 | call to getDirectly |
+| by_reference.cpp:56:5:56:17 | call to user_input | by_reference.cpp:19:28:19:32 | value | by_reference.cpp:20:5:20:8 | Chi [a] | by_reference.cpp:56:3:56:3 | setIndirectly output argument [a] |
+| by_reference.cpp:57:8:57:8 | s indirection [a] | by_reference.cpp:39:9:39:21 | *#this [a] | by_reference.cpp:40:18:40:28 | Store | by_reference.cpp:57:10:57:22 | call to getIndirectly |
+| by_reference.cpp:62:5:62:23 | call to user_input | by_reference.cpp:23:34:23:38 | value | by_reference.cpp:24:19:24:22 | Chi [a] | by_reference.cpp:62:3:62:3 | setThroughNonMember output argument [a] |
+| by_reference.cpp:63:8:63:8 | s indirection [a] | by_reference.cpp:43:9:43:27 | *#this [a] | by_reference.cpp:44:12:44:24 | Store | by_reference.cpp:63:10:63:28 | call to getThroughNonMember |
+| by_reference.cpp:68:3:68:15 | call to user_input | by_reference.cpp:11:48:11:52 | value | by_reference.cpp:12:5:12:16 | Chi [a] | by_reference.cpp:68:17:68:18 | nonMemberSetA output argument [a] |
+| by_reference.cpp:69:22:69:23 | & ... indirection [a] | by_reference.cpp:31:46:31:46 | *s [a] | by_reference.cpp:32:15:32:15 | Store | by_reference.cpp:69:8:69:20 | call to nonMemberGetA |
+| complex.cpp:42:16:42:16 | f indirection [a_] | complex.cpp:9:7:9:7 | *#this [a_] | complex.cpp:9:20:9:21 | Store | complex.cpp:42:18:42:18 | call to a |
+| complex.cpp:42:16:42:16 | f indirection [b_] | complex.cpp:9:7:9:7 | *#this [b_] | complex.cpp:9:7:9:7 | *#this [b_] | complex.cpp:42:16:42:16 | a output argument [b_] |
+| complex.cpp:43:16:43:16 | f indirection [b_] | complex.cpp:10:7:10:7 | *#this [b_] | complex.cpp:10:20:10:21 | Store | complex.cpp:43:18:43:18 | call to b |
+| complex.cpp:53:14:53:17 | call to user_input | complex.cpp:11:17:11:17 | a | complex.cpp:11:22:11:27 | Chi [a_] | complex.cpp:53:12:53:12 | setA output argument [a_] |
+| complex.cpp:54:14:54:17 | call to user_input | complex.cpp:12:17:12:17 | b | complex.cpp:12:22:12:27 | Chi [b_] | complex.cpp:54:12:54:12 | setB output argument [b_] |
+| complex.cpp:55:14:55:17 | call to user_input | complex.cpp:11:17:11:17 | a | complex.cpp:11:22:11:27 | Chi [a_] | complex.cpp:55:12:55:12 | setA output argument [a_] |
+| complex.cpp:56:12:56:12 | f indirection [a_] | complex.cpp:12:8:12:11 | *#this [a_] | complex.cpp:12:22:12:27 | Chi [a_] | complex.cpp:56:12:56:12 | setB output argument [a_] |
+| complex.cpp:56:14:56:17 | call to user_input | complex.cpp:12:17:12:17 | b | complex.cpp:12:22:12:27 | Chi [b_] | complex.cpp:56:12:56:12 | setB output argument [b_] |
+| constructors.cpp:28:10:28:10 | f indirection [a_] | constructors.cpp:18:9:18:9 | *#this [a_] | constructors.cpp:18:22:18:23 | Store | constructors.cpp:28:12:28:12 | call to a |
+| constructors.cpp:28:10:28:10 | f indirection [b_] | constructors.cpp:18:9:18:9 | *#this [b_] | constructors.cpp:18:9:18:9 | *#this [b_] | constructors.cpp:28:10:28:10 | a output argument [b_] |
+| constructors.cpp:29:10:29:10 | f indirection [b_] | constructors.cpp:19:9:19:9 | *#this [b_] | constructors.cpp:19:22:19:23 | Store | constructors.cpp:29:12:29:12 | call to b |
+| constructors.cpp:34:11:34:26 | call to user_input | constructors.cpp:23:13:23:13 | a | constructors.cpp:23:35:23:35 | Chi [a_] | constructors.cpp:34:11:34:26 | Foo output argument [a_] |
+| constructors.cpp:35:11:35:26 | call to user_input | constructors.cpp:23:20:23:20 | b | constructors.cpp:23:35:23:35 | Chi [b_] | constructors.cpp:35:11:35:26 | Foo output argument [b_] |
+| constructors.cpp:36:11:36:37 | call to user_input | constructors.cpp:23:13:23:13 | a | constructors.cpp:23:35:23:35 | Chi [a_] | constructors.cpp:36:11:36:37 | Foo output argument [a_] |
+| constructors.cpp:36:11:36:37 | call to user_input | constructors.cpp:23:20:23:20 | b | constructors.cpp:23:35:23:35 | Chi [b_] | constructors.cpp:36:11:36:37 | Foo output argument [b_] |
+| simple.cpp:28:10:28:10 | f indirection [a_] | simple.cpp:18:9:18:9 | *#this [a_] | simple.cpp:18:22:18:23 | Store | simple.cpp:28:12:28:12 | call to a |
+| simple.cpp:28:10:28:10 | f indirection [b_] | simple.cpp:18:9:18:9 | *#this [b_] | simple.cpp:18:9:18:9 | *#this [b_] | simple.cpp:28:10:28:10 | a output argument [b_] |
+| simple.cpp:29:10:29:10 | f indirection [b_] | simple.cpp:19:9:19:9 | *#this [b_] | simple.cpp:19:22:19:23 | Store | simple.cpp:29:12:29:12 | call to b |
+| simple.cpp:39:7:39:10 | call to user_input | simple.cpp:20:19:20:19 | a | simple.cpp:20:24:20:29 | Chi [a_] | simple.cpp:39:5:39:5 | setA output argument [a_] |
+| simple.cpp:40:7:40:10 | call to user_input | simple.cpp:21:19:21:19 | b | simple.cpp:21:24:21:29 | Chi [b_] | simple.cpp:40:5:40:5 | setB output argument [b_] |
+| simple.cpp:41:7:41:10 | call to user_input | simple.cpp:20:19:20:19 | a | simple.cpp:20:24:20:29 | Chi [a_] | simple.cpp:41:5:41:5 | setA output argument [a_] |
+| simple.cpp:42:5:42:5 | h indirection [a_] | simple.cpp:21:10:21:13 | *#this [a_] | simple.cpp:21:24:21:29 | Chi [a_] | simple.cpp:42:5:42:5 | setB output argument [a_] |
+| simple.cpp:42:7:42:10 | call to user_input | simple.cpp:21:19:21:19 | b | simple.cpp:21:24:21:29 | Chi [b_] | simple.cpp:42:5:42:5 | setB output argument [b_] |
+| simple.cpp:84:14:84:20 | this indirection [f1] | simple.cpp:78:9:78:15 | *#this [f1] | simple.cpp:79:19:79:20 | Store | simple.cpp:84:14:84:20 | call to getf2f1 |
#select
| A.cpp:56:13:56:15 | call to get | A.cpp:55:12:55:19 | (C *)... | A.cpp:56:13:56:15 | call to get | call to get flows from $@ | A.cpp:55:12:55:19 | (C *)... | (C *)... |
| A.cpp:56:13:56:15 | call to get | A.cpp:55:12:55:19 | new | A.cpp:56:13:56:15 | call to get | call to get flows from $@ | A.cpp:55:12:55:19 | new | new |
diff --git a/cpp/ql/test/library-tests/dataflow/fields/path-flow.expected b/cpp/ql/test/library-tests/dataflow/fields/path-flow.expected
index 6604dde87d4..7af6f7f488c 100644
--- a/cpp/ql/test/library-tests/dataflow/fields/path-flow.expected
+++ b/cpp/ql/test/library-tests/dataflow/fields/path-flow.expected
@@ -1,21 +1,46 @@
edges
+| A.cpp:23:10:23:10 | c | A.cpp:25:7:25:17 | ... = ... |
+| A.cpp:25:7:25:17 | ... = ... | A.cpp:25:7:25:10 | this [post update] [c] |
+| A.cpp:27:17:27:17 | c | A.cpp:27:22:27:32 | ... = ... |
+| A.cpp:27:22:27:32 | ... = ... | A.cpp:27:22:27:25 | this [post update] [c] |
+| A.cpp:28:8:28:10 | this [c] | A.cpp:28:23:28:26 | this [c] |
+| A.cpp:28:23:28:26 | this [c] | A.cpp:28:29:28:29 | c |
+| A.cpp:29:23:29:23 | c | A.cpp:31:20:31:20 | c |
+| A.cpp:31:14:31:21 | call to B [c] | A.cpp:31:14:31:21 | new [c] |
+| A.cpp:31:20:31:20 | c | A.cpp:23:10:23:10 | c |
+| A.cpp:31:20:31:20 | c | A.cpp:31:14:31:21 | call to B [c] |
| A.cpp:41:15:41:21 | new | A.cpp:43:10:43:12 | & ... |
| A.cpp:47:12:47:18 | new | A.cpp:48:20:48:20 | c |
| A.cpp:48:12:48:18 | call to make [c] | A.cpp:49:10:49:10 | b [c] |
+| A.cpp:48:20:48:20 | c | A.cpp:29:23:29:23 | c |
| A.cpp:48:20:48:20 | c | A.cpp:48:12:48:18 | call to make [c] |
| A.cpp:49:10:49:10 | b [c] | A.cpp:49:13:49:13 | c |
| A.cpp:55:5:55:5 | ref arg b [c] | A.cpp:56:10:56:10 | b [c] |
+| A.cpp:55:12:55:19 | new | A.cpp:27:17:27:17 | c |
| A.cpp:55:12:55:19 | new | A.cpp:55:5:55:5 | ref arg b [c] |
+| A.cpp:56:10:56:10 | b [c] | A.cpp:28:8:28:10 | this [c] |
| A.cpp:56:10:56:10 | b [c] | A.cpp:56:13:56:15 | call to get |
| A.cpp:57:11:57:24 | call to B [c] | A.cpp:57:11:57:24 | new [c] |
+| A.cpp:57:11:57:24 | new [c] | A.cpp:28:8:28:10 | this [c] |
| A.cpp:57:11:57:24 | new [c] | A.cpp:57:28:57:30 | call to get |
+| A.cpp:57:17:57:23 | new | A.cpp:23:10:23:10 | c |
| A.cpp:57:17:57:23 | new | A.cpp:57:11:57:24 | call to B [c] |
| A.cpp:64:10:64:15 | call to setOnB [c] | A.cpp:66:10:66:11 | b2 [c] |
| A.cpp:64:21:64:28 | new | A.cpp:64:10:64:15 | call to setOnB [c] |
+| A.cpp:64:21:64:28 | new | A.cpp:85:26:85:26 | c |
| A.cpp:66:10:66:11 | b2 [c] | A.cpp:66:14:66:14 | c |
| A.cpp:73:10:73:19 | call to setOnBWrap [c] | A.cpp:75:10:75:11 | b2 [c] |
| A.cpp:73:25:73:32 | new | A.cpp:73:10:73:19 | call to setOnBWrap [c] |
+| A.cpp:73:25:73:32 | new | A.cpp:78:27:78:27 | c |
| A.cpp:75:10:75:11 | b2 [c] | A.cpp:75:14:75:14 | c |
+| A.cpp:78:27:78:27 | c | A.cpp:81:21:81:21 | c |
+| A.cpp:81:10:81:15 | call to setOnB [c] | A.cpp:82:12:82:24 | ... ? ... : ... [c] |
+| A.cpp:81:21:81:21 | c | A.cpp:81:10:81:15 | call to setOnB [c] |
+| A.cpp:81:21:81:21 | c | A.cpp:85:26:85:26 | c |
+| A.cpp:85:26:85:26 | c | A.cpp:90:15:90:15 | c |
+| A.cpp:90:7:90:8 | ref arg b2 [c] | A.cpp:91:14:91:15 | b2 [c] |
+| A.cpp:90:15:90:15 | c | A.cpp:27:17:27:17 | c |
+| A.cpp:90:15:90:15 | c | A.cpp:90:7:90:8 | ref arg b2 [c] |
| A.cpp:98:12:98:18 | new | A.cpp:100:5:100:13 | ... = ... |
| A.cpp:100:5:100:6 | c1 [post update] [a] | A.cpp:101:8:101:9 | c1 [a] |
| A.cpp:100:5:100:13 | ... = ... | A.cpp:100:5:100:6 | c1 [post update] [a] |
@@ -25,9 +50,11 @@ edges
| A.cpp:107:12:107:13 | c1 [a] | A.cpp:107:16:107:16 | a |
| A.cpp:120:12:120:13 | c1 [a] | A.cpp:120:16:120:16 | a |
| A.cpp:126:5:126:5 | ref arg b [c] | A.cpp:131:8:131:8 | ref arg b [c] |
+| A.cpp:126:12:126:18 | new | A.cpp:27:17:27:17 | c |
| A.cpp:126:12:126:18 | new | A.cpp:126:5:126:5 | ref arg b [c] |
| A.cpp:131:8:131:8 | ref arg b [c] | A.cpp:132:10:132:10 | b [c] |
| A.cpp:132:10:132:10 | b [c] | A.cpp:132:13:132:13 | c |
+| A.cpp:140:13:140:13 | b | A.cpp:143:7:143:31 | ... = ... |
| A.cpp:142:7:142:7 | b [post update] [c] | A.cpp:143:7:143:31 | ... = ... [c] |
| A.cpp:142:7:142:7 | b [post update] [c] | A.cpp:151:18:151:18 | ref arg b [c] |
| A.cpp:142:7:142:20 | ... = ... | A.cpp:142:7:142:7 | b [post update] [c] |
@@ -35,11 +62,13 @@ edges
| A.cpp:143:7:143:10 | this [post update] [b, c] | A.cpp:151:12:151:24 | call to D [b, c] |
| A.cpp:143:7:143:10 | this [post update] [b] | A.cpp:151:12:151:24 | call to D [b] |
| A.cpp:143:7:143:31 | ... = ... | A.cpp:143:7:143:10 | this [post update] [b] |
+| A.cpp:143:7:143:31 | ... = ... | A.cpp:143:7:143:10 | this [post update] [b] |
| A.cpp:143:7:143:31 | ... = ... [c] | A.cpp:143:7:143:10 | this [post update] [b, c] |
| A.cpp:143:25:143:31 | new | A.cpp:143:7:143:31 | ... = ... |
| A.cpp:150:12:150:18 | new | A.cpp:151:18:151:18 | b |
| A.cpp:151:12:151:24 | call to D [b, c] | A.cpp:153:10:153:10 | d [b, c] |
| A.cpp:151:12:151:24 | call to D [b] | A.cpp:152:10:152:10 | d [b] |
+| A.cpp:151:18:151:18 | b | A.cpp:140:13:140:13 | b |
| A.cpp:151:18:151:18 | b | A.cpp:151:12:151:24 | call to D [b] |
| A.cpp:151:18:151:18 | ref arg b [c] | A.cpp:154:10:154:10 | b [c] |
| A.cpp:152:10:152:10 | d [b] | A.cpp:152:13:152:13 | b |
@@ -49,11 +78,14 @@ edges
| A.cpp:159:12:159:18 | new | A.cpp:160:29:160:29 | b |
| A.cpp:160:18:160:60 | call to MyList [head] | A.cpp:161:38:161:39 | l1 [head] |
| A.cpp:160:29:160:29 | b | A.cpp:160:18:160:60 | call to MyList [head] |
+| A.cpp:160:29:160:29 | b | A.cpp:181:15:181:21 | newHead |
| A.cpp:161:18:161:40 | call to MyList [next, head] | A.cpp:162:38:162:39 | l2 [next, head] |
| A.cpp:161:38:161:39 | l1 [head] | A.cpp:161:18:161:40 | call to MyList [next, head] |
+| A.cpp:161:38:161:39 | l1 [head] | A.cpp:181:32:181:35 | next [head] |
| A.cpp:162:18:162:40 | call to MyList [next, next, head] | A.cpp:165:10:165:11 | l3 [next, next, head] |
| A.cpp:162:18:162:40 | call to MyList [next, next, head] | A.cpp:167:44:167:44 | l [next, next, head] |
| A.cpp:162:38:162:39 | l2 [next, head] | A.cpp:162:18:162:40 | call to MyList [next, next, head] |
+| A.cpp:162:38:162:39 | l2 [next, head] | A.cpp:181:32:181:35 | next [next, head] |
| A.cpp:165:10:165:11 | l3 [next, next, head] | A.cpp:165:14:165:17 | next [next, head] |
| A.cpp:165:14:165:17 | next [next, head] | A.cpp:165:20:165:23 | next [head] |
| A.cpp:165:20:165:23 | next [head] | A.cpp:165:26:165:29 | head |
@@ -62,20 +94,38 @@ edges
| A.cpp:167:47:167:50 | next [head] | A.cpp:169:12:169:12 | l [head] |
| A.cpp:167:47:167:50 | next [next, head] | A.cpp:167:44:167:44 | l [next, head] |
| A.cpp:169:12:169:12 | l [head] | A.cpp:169:15:169:18 | head |
+| A.cpp:181:15:181:21 | newHead | A.cpp:183:7:183:20 | ... = ... |
+| A.cpp:181:32:181:35 | next [head] | A.cpp:184:7:184:23 | ... = ... [head] |
+| A.cpp:181:32:181:35 | next [next, head] | A.cpp:184:7:184:23 | ... = ... [next, head] |
+| A.cpp:183:7:183:20 | ... = ... | A.cpp:183:7:183:10 | this [post update] [head] |
+| A.cpp:184:7:184:23 | ... = ... [head] | A.cpp:184:7:184:10 | this [post update] [next, head] |
+| A.cpp:184:7:184:23 | ... = ... [next, head] | A.cpp:184:7:184:10 | this [post update] [next, next, head] |
| B.cpp:6:15:6:24 | new | B.cpp:7:25:7:25 | e |
| B.cpp:7:16:7:35 | call to Box1 [elem1] | B.cpp:8:25:8:26 | b1 [elem1] |
| B.cpp:7:25:7:25 | e | B.cpp:7:16:7:35 | call to Box1 [elem1] |
+| B.cpp:7:25:7:25 | e | B.cpp:33:16:33:17 | e1 |
| B.cpp:8:16:8:27 | call to Box2 [box1, elem1] | B.cpp:9:10:9:11 | b2 [box1, elem1] |
| B.cpp:8:25:8:26 | b1 [elem1] | B.cpp:8:16:8:27 | call to Box2 [box1, elem1] |
+| B.cpp:8:25:8:26 | b1 [elem1] | B.cpp:44:16:44:17 | b1 [elem1] |
| B.cpp:9:10:9:11 | b2 [box1, elem1] | B.cpp:9:14:9:17 | box1 [elem1] |
| B.cpp:9:14:9:17 | box1 [elem1] | B.cpp:9:20:9:24 | elem1 |
| B.cpp:15:15:15:27 | new | B.cpp:16:37:16:37 | e |
| B.cpp:16:16:16:38 | call to Box1 [elem2] | B.cpp:17:25:17:26 | b1 [elem2] |
| B.cpp:16:37:16:37 | e | B.cpp:16:16:16:38 | call to Box1 [elem2] |
+| B.cpp:16:37:16:37 | e | B.cpp:33:26:33:27 | e2 |
| B.cpp:17:16:17:27 | call to Box2 [box1, elem2] | B.cpp:19:10:19:11 | b2 [box1, elem2] |
| B.cpp:17:25:17:26 | b1 [elem2] | B.cpp:17:16:17:27 | call to Box2 [box1, elem2] |
+| B.cpp:17:25:17:26 | b1 [elem2] | B.cpp:44:16:44:17 | b1 [elem2] |
| B.cpp:19:10:19:11 | b2 [box1, elem2] | B.cpp:19:14:19:17 | box1 [elem2] |
| B.cpp:19:14:19:17 | box1 [elem2] | B.cpp:19:20:19:24 | elem2 |
+| B.cpp:33:16:33:17 | e1 | B.cpp:35:7:35:22 | ... = ... |
+| B.cpp:33:26:33:27 | e2 | B.cpp:36:7:36:22 | ... = ... |
+| B.cpp:35:7:35:22 | ... = ... | B.cpp:35:7:35:10 | this [post update] [elem1] |
+| B.cpp:36:7:36:22 | ... = ... | B.cpp:36:7:36:10 | this [post update] [elem2] |
+| B.cpp:44:16:44:17 | b1 [elem1] | B.cpp:46:7:46:21 | ... = ... [elem1] |
+| B.cpp:44:16:44:17 | b1 [elem2] | B.cpp:46:7:46:21 | ... = ... [elem2] |
+| B.cpp:46:7:46:21 | ... = ... [elem1] | B.cpp:46:7:46:10 | this [post update] [box1, elem1] |
+| B.cpp:46:7:46:21 | ... = ... [elem2] | B.cpp:46:7:46:10 | this [post update] [box1, elem2] |
| C.cpp:18:12:18:18 | call to C [s1] | C.cpp:19:5:19:5 | c [s1] |
| C.cpp:18:12:18:18 | call to C [s3] | C.cpp:19:5:19:5 | c [s3] |
| C.cpp:19:5:19:5 | c [s1] | C.cpp:27:8:27:11 | this [s1] |
@@ -89,8 +139,16 @@ edges
| C.cpp:27:8:27:11 | this [s3] | C.cpp:31:10:31:11 | this [s3] |
| C.cpp:29:10:29:11 | this [s1] | C.cpp:29:10:29:11 | s1 |
| C.cpp:31:10:31:11 | this [s3] | C.cpp:31:10:31:11 | s3 |
+| D.cpp:10:11:10:17 | this [elem] | D.cpp:10:30:10:33 | this [elem] |
+| D.cpp:10:30:10:33 | this [elem] | D.cpp:10:30:10:33 | elem |
+| D.cpp:11:24:11:24 | e | D.cpp:11:29:11:36 | ... = ... |
+| D.cpp:11:29:11:36 | ... = ... | D.cpp:11:29:11:32 | this [post update] [elem] |
+| D.cpp:17:11:17:17 | this [box, elem] | D.cpp:17:30:17:32 | this [box, elem] |
+| D.cpp:17:30:17:32 | this [box, elem] | D.cpp:17:30:17:32 | box [elem] |
| D.cpp:21:30:21:31 | b2 [box, elem] | D.cpp:22:10:22:11 | b2 [box, elem] |
+| D.cpp:22:10:22:11 | b2 [box, elem] | D.cpp:17:11:17:17 | this [box, elem] |
| D.cpp:22:10:22:11 | b2 [box, elem] | D.cpp:22:14:22:20 | call to getBox1 [elem] |
+| D.cpp:22:14:22:20 | call to getBox1 [elem] | D.cpp:10:11:10:17 | this [elem] |
| D.cpp:22:14:22:20 | call to getBox1 [elem] | D.cpp:22:25:22:31 | call to getElem |
| D.cpp:28:15:28:24 | new | D.cpp:30:5:30:20 | ... = ... |
| D.cpp:30:5:30:5 | b [post update] [box, elem] | D.cpp:31:14:31:14 | b [box, elem] |
@@ -100,6 +158,7 @@ edges
| D.cpp:35:15:35:24 | new | D.cpp:37:21:37:21 | e |
| D.cpp:37:5:37:5 | b [post update] [box, elem] | D.cpp:38:14:38:14 | b [box, elem] |
| D.cpp:37:8:37:10 | ref arg box [elem] | D.cpp:37:5:37:5 | b [post update] [box, elem] |
+| D.cpp:37:21:37:21 | e | D.cpp:11:24:11:24 | e |
| D.cpp:37:21:37:21 | e | D.cpp:37:8:37:10 | ref arg box [elem] |
| D.cpp:38:14:38:14 | b [box, elem] | D.cpp:21:30:21:31 | b2 [box, elem] |
| D.cpp:42:15:42:24 | new | D.cpp:44:5:44:26 | ... = ... |
@@ -110,6 +169,7 @@ edges
| D.cpp:49:15:49:24 | new | D.cpp:51:27:51:27 | e |
| D.cpp:51:5:51:5 | ref arg b [box, elem] | D.cpp:52:14:52:14 | b [box, elem] |
| D.cpp:51:8:51:14 | ref arg call to getBox1 [elem] | D.cpp:51:5:51:5 | ref arg b [box, elem] |
+| D.cpp:51:27:51:27 | e | D.cpp:11:24:11:24 | e |
| D.cpp:51:27:51:27 | e | D.cpp:51:8:51:14 | ref arg call to getBox1 [elem] |
| D.cpp:52:14:52:14 | b [box, elem] | D.cpp:21:30:21:31 | b2 [box, elem] |
| D.cpp:56:15:56:24 | new | D.cpp:58:5:58:27 | ... = ... |
@@ -223,17 +283,45 @@ edges
| arrays.cpp:44:8:44:25 | access to array [data] | arrays.cpp:44:27:44:30 | data |
| arrays.cpp:44:10:44:17 | indirect [arr, data] | arrays.cpp:44:20:44:22 | arr [data] |
| arrays.cpp:44:20:44:22 | arr [data] | arrays.cpp:44:8:44:25 | access to array [data] |
+| by_reference.cpp:11:48:11:52 | value | by_reference.cpp:12:5:12:16 | ... = ... |
+| by_reference.cpp:12:5:12:16 | ... = ... | by_reference.cpp:12:5:12:5 | s [post update] [a] |
+| by_reference.cpp:15:26:15:30 | value | by_reference.cpp:16:5:16:19 | ... = ... |
+| by_reference.cpp:16:5:16:19 | ... = ... | by_reference.cpp:16:5:16:8 | this [post update] [a] |
+| by_reference.cpp:19:28:19:32 | value | by_reference.cpp:20:23:20:27 | value |
+| by_reference.cpp:20:23:20:27 | value | by_reference.cpp:15:26:15:30 | value |
+| by_reference.cpp:20:23:20:27 | value | by_reference.cpp:20:5:20:8 | ref arg this [a] |
+| by_reference.cpp:23:34:23:38 | value | by_reference.cpp:24:25:24:29 | value |
+| by_reference.cpp:24:25:24:29 | value | by_reference.cpp:11:48:11:52 | value |
+| by_reference.cpp:24:25:24:29 | value | by_reference.cpp:24:19:24:22 | ref arg this [a] |
+| by_reference.cpp:31:46:31:46 | s [a] | by_reference.cpp:32:12:32:12 | s [a] |
+| by_reference.cpp:32:12:32:12 | s [a] | by_reference.cpp:32:15:32:15 | a |
+| by_reference.cpp:35:9:35:19 | this [a] | by_reference.cpp:36:12:36:15 | this [a] |
+| by_reference.cpp:36:12:36:15 | this [a] | by_reference.cpp:36:18:36:18 | a |
+| by_reference.cpp:39:9:39:21 | this [a] | by_reference.cpp:40:12:40:15 | this [a] |
+| by_reference.cpp:40:12:40:15 | this [a] | by_reference.cpp:35:9:35:19 | this [a] |
+| by_reference.cpp:40:12:40:15 | this [a] | by_reference.cpp:40:18:40:28 | call to getDirectly |
+| by_reference.cpp:43:9:43:27 | this [a] | by_reference.cpp:44:26:44:29 | this [a] |
+| by_reference.cpp:44:26:44:29 | this [a] | by_reference.cpp:31:46:31:46 | s [a] |
+| by_reference.cpp:44:26:44:29 | this [a] | by_reference.cpp:44:12:44:24 | call to nonMemberGetA |
| by_reference.cpp:50:3:50:3 | ref arg s [a] | by_reference.cpp:51:8:51:8 | s [a] |
+| by_reference.cpp:50:17:50:26 | call to user_input | by_reference.cpp:15:26:15:30 | value |
| by_reference.cpp:50:17:50:26 | call to user_input | by_reference.cpp:50:3:50:3 | ref arg s [a] |
+| by_reference.cpp:51:8:51:8 | s [a] | by_reference.cpp:35:9:35:19 | this [a] |
| by_reference.cpp:51:8:51:8 | s [a] | by_reference.cpp:51:10:51:20 | call to getDirectly |
| by_reference.cpp:56:3:56:3 | ref arg s [a] | by_reference.cpp:57:8:57:8 | s [a] |
+| by_reference.cpp:56:19:56:28 | call to user_input | by_reference.cpp:19:28:19:32 | value |
| by_reference.cpp:56:19:56:28 | call to user_input | by_reference.cpp:56:3:56:3 | ref arg s [a] |
+| by_reference.cpp:57:8:57:8 | s [a] | by_reference.cpp:39:9:39:21 | this [a] |
| by_reference.cpp:57:8:57:8 | s [a] | by_reference.cpp:57:10:57:22 | call to getIndirectly |
| by_reference.cpp:62:3:62:3 | ref arg s [a] | by_reference.cpp:63:8:63:8 | s [a] |
+| by_reference.cpp:62:25:62:34 | call to user_input | by_reference.cpp:23:34:23:38 | value |
| by_reference.cpp:62:25:62:34 | call to user_input | by_reference.cpp:62:3:62:3 | ref arg s [a] |
+| by_reference.cpp:63:8:63:8 | s [a] | by_reference.cpp:43:9:43:27 | this [a] |
| by_reference.cpp:63:8:63:8 | s [a] | by_reference.cpp:63:10:63:28 | call to getThroughNonMember |
| by_reference.cpp:68:17:68:18 | ref arg & ... [a] | by_reference.cpp:69:22:69:23 | & ... [a] |
+| by_reference.cpp:68:21:68:30 | call to user_input | by_reference.cpp:11:48:11:52 | value |
| by_reference.cpp:68:21:68:30 | call to user_input | by_reference.cpp:68:17:68:18 | ref arg & ... [a] |
+| by_reference.cpp:69:22:69:23 | & ... [a] | by_reference.cpp:31:46:31:46 | s [a] |
| by_reference.cpp:69:22:69:23 | & ... [a] | by_reference.cpp:69:8:69:20 | call to nonMemberGetA |
| by_reference.cpp:84:3:84:7 | inner [post update] [a] | by_reference.cpp:102:21:102:39 | ref arg & ... [a] |
| by_reference.cpp:84:3:84:7 | inner [post update] [a] | by_reference.cpp:103:27:103:35 | ref arg inner_ptr [a] |
@@ -308,29 +396,43 @@ edges
| by_reference.cpp:135:8:135:13 | pouter [inner_ptr, a] | by_reference.cpp:135:16:135:24 | inner_ptr [a] |
| by_reference.cpp:135:16:135:24 | inner_ptr [a] | by_reference.cpp:135:27:135:27 | a |
| by_reference.cpp:136:8:136:13 | pouter [a] | by_reference.cpp:136:16:136:16 | a |
+| complex.cpp:9:7:9:7 | this [a_] | complex.cpp:9:20:9:21 | this [a_] |
+| complex.cpp:9:20:9:21 | this [a_] | complex.cpp:9:20:9:21 | a_ |
+| complex.cpp:10:7:10:7 | this [b_] | complex.cpp:10:20:10:21 | this [b_] |
+| complex.cpp:10:20:10:21 | this [b_] | complex.cpp:10:20:10:21 | b_ |
+| complex.cpp:11:17:11:17 | a | complex.cpp:11:22:11:27 | ... = ... |
+| complex.cpp:11:22:11:27 | ... = ... | complex.cpp:11:22:11:23 | this [post update] [a_] |
+| complex.cpp:12:17:12:17 | b | complex.cpp:12:22:12:27 | ... = ... |
+| complex.cpp:12:22:12:27 | ... = ... | complex.cpp:12:22:12:23 | this [post update] [b_] |
| complex.cpp:40:17:40:17 | b [inner, f, a_] | complex.cpp:42:8:42:8 | b [inner, f, a_] |
| complex.cpp:40:17:40:17 | b [inner, f, b_] | complex.cpp:43:8:43:8 | b [inner, f, b_] |
| complex.cpp:42:8:42:8 | b [inner, f, a_] | complex.cpp:42:10:42:14 | inner [f, a_] |
| complex.cpp:42:10:42:14 | inner [f, a_] | complex.cpp:42:16:42:16 | f [a_] |
+| complex.cpp:42:16:42:16 | f [a_] | complex.cpp:9:7:9:7 | this [a_] |
| complex.cpp:42:16:42:16 | f [a_] | complex.cpp:42:18:42:18 | call to a |
| complex.cpp:43:8:43:8 | b [inner, f, b_] | complex.cpp:43:10:43:14 | inner [f, b_] |
| complex.cpp:43:10:43:14 | inner [f, b_] | complex.cpp:43:16:43:16 | f [b_] |
+| complex.cpp:43:16:43:16 | f [b_] | complex.cpp:10:7:10:7 | this [b_] |
| complex.cpp:43:16:43:16 | f [b_] | complex.cpp:43:18:43:18 | call to b |
| complex.cpp:53:3:53:4 | b1 [post update] [inner, f, a_] | complex.cpp:59:7:59:8 | b1 [inner, f, a_] |
| complex.cpp:53:6:53:10 | inner [post update] [f, a_] | complex.cpp:53:3:53:4 | b1 [post update] [inner, f, a_] |
| complex.cpp:53:12:53:12 | ref arg f [a_] | complex.cpp:53:6:53:10 | inner [post update] [f, a_] |
+| complex.cpp:53:19:53:28 | call to user_input | complex.cpp:11:17:11:17 | a |
| complex.cpp:53:19:53:28 | call to user_input | complex.cpp:53:12:53:12 | ref arg f [a_] |
| complex.cpp:54:3:54:4 | b2 [post update] [inner, f, b_] | complex.cpp:62:7:62:8 | b2 [inner, f, b_] |
| complex.cpp:54:6:54:10 | inner [post update] [f, b_] | complex.cpp:54:3:54:4 | b2 [post update] [inner, f, b_] |
| complex.cpp:54:12:54:12 | ref arg f [b_] | complex.cpp:54:6:54:10 | inner [post update] [f, b_] |
+| complex.cpp:54:19:54:28 | call to user_input | complex.cpp:12:17:12:17 | b |
| complex.cpp:54:19:54:28 | call to user_input | complex.cpp:54:12:54:12 | ref arg f [b_] |
| complex.cpp:55:3:55:4 | b3 [post update] [inner, f, a_] | complex.cpp:65:7:65:8 | b3 [inner, f, a_] |
| complex.cpp:55:6:55:10 | inner [post update] [f, a_] | complex.cpp:55:3:55:4 | b3 [post update] [inner, f, a_] |
| complex.cpp:55:12:55:12 | ref arg f [a_] | complex.cpp:55:6:55:10 | inner [post update] [f, a_] |
+| complex.cpp:55:19:55:28 | call to user_input | complex.cpp:11:17:11:17 | a |
| complex.cpp:55:19:55:28 | call to user_input | complex.cpp:55:12:55:12 | ref arg f [a_] |
| complex.cpp:56:3:56:4 | b3 [post update] [inner, f, b_] | complex.cpp:65:7:65:8 | b3 [inner, f, b_] |
| complex.cpp:56:6:56:10 | inner [post update] [f, b_] | complex.cpp:56:3:56:4 | b3 [post update] [inner, f, b_] |
| complex.cpp:56:12:56:12 | ref arg f [b_] | complex.cpp:56:6:56:10 | inner [post update] [f, b_] |
+| complex.cpp:56:19:56:28 | call to user_input | complex.cpp:12:17:12:17 | b |
| complex.cpp:56:19:56:28 | call to user_input | complex.cpp:56:12:56:12 | ref arg f [b_] |
| complex.cpp:59:7:59:8 | b1 [inner, f, a_] | complex.cpp:40:17:40:17 | b [inner, f, a_] |
| complex.cpp:62:7:62:8 | b2 [inner, f, b_] | complex.cpp:40:17:40:17 | b [inner, f, b_] |
@@ -357,22 +459,43 @@ edges
| conflated.cpp:60:17:60:26 | call to user_input | conflated.cpp:60:3:60:28 | ... = ... |
| conflated.cpp:61:8:61:9 | ll [next, y] | conflated.cpp:61:12:61:15 | next [y] |
| conflated.cpp:61:12:61:15 | next [y] | conflated.cpp:61:18:61:18 | y |
+| constructors.cpp:18:9:18:9 | this [a_] | constructors.cpp:18:22:18:23 | this [a_] |
+| constructors.cpp:18:22:18:23 | this [a_] | constructors.cpp:18:22:18:23 | a_ |
+| constructors.cpp:19:9:19:9 | this [b_] | constructors.cpp:19:22:19:23 | this [b_] |
+| constructors.cpp:19:22:19:23 | this [b_] | constructors.cpp:19:22:19:23 | b_ |
+| constructors.cpp:23:13:23:13 | a | constructors.cpp:23:28:23:28 | a |
+| constructors.cpp:23:20:23:20 | b | constructors.cpp:23:35:23:35 | b |
+| constructors.cpp:23:28:23:28 | a | constructors.cpp:23:25:23:29 | constructor init of field a_ [post-this] [a_] |
+| constructors.cpp:23:35:23:35 | b | constructors.cpp:23:32:23:36 | constructor init of field b_ [post-this] [b_] |
| constructors.cpp:26:15:26:15 | f [a_] | constructors.cpp:28:10:28:10 | f [a_] |
| constructors.cpp:26:15:26:15 | f [b_] | constructors.cpp:29:10:29:10 | f [b_] |
+| constructors.cpp:28:10:28:10 | f [a_] | constructors.cpp:18:9:18:9 | this [a_] |
| constructors.cpp:28:10:28:10 | f [a_] | constructors.cpp:28:12:28:12 | call to a |
+| constructors.cpp:29:10:29:10 | f [b_] | constructors.cpp:19:9:19:9 | this [b_] |
| constructors.cpp:29:10:29:10 | f [b_] | constructors.cpp:29:12:29:12 | call to b |
+| constructors.cpp:34:11:34:20 | call to user_input | constructors.cpp:23:13:23:13 | a |
| constructors.cpp:34:11:34:20 | call to user_input | constructors.cpp:34:11:34:26 | call to Foo [a_] |
| constructors.cpp:34:11:34:26 | call to Foo [a_] | constructors.cpp:40:9:40:9 | f [a_] |
| constructors.cpp:35:11:35:26 | call to Foo [b_] | constructors.cpp:43:9:43:9 | g [b_] |
+| constructors.cpp:35:14:35:23 | call to user_input | constructors.cpp:23:20:23:20 | b |
| constructors.cpp:35:14:35:23 | call to user_input | constructors.cpp:35:11:35:26 | call to Foo [b_] |
+| constructors.cpp:36:11:36:20 | call to user_input | constructors.cpp:23:13:23:13 | a |
| constructors.cpp:36:11:36:20 | call to user_input | constructors.cpp:36:11:36:37 | call to Foo [a_] |
| constructors.cpp:36:11:36:37 | call to Foo [a_] | constructors.cpp:46:9:46:9 | h [a_] |
| constructors.cpp:36:11:36:37 | call to Foo [b_] | constructors.cpp:46:9:46:9 | h [b_] |
+| constructors.cpp:36:25:36:34 | call to user_input | constructors.cpp:23:20:23:20 | b |
| constructors.cpp:36:25:36:34 | call to user_input | constructors.cpp:36:11:36:37 | call to Foo [b_] |
| constructors.cpp:40:9:40:9 | f [a_] | constructors.cpp:26:15:26:15 | f [a_] |
| constructors.cpp:43:9:43:9 | g [b_] | constructors.cpp:26:15:26:15 | f [b_] |
| constructors.cpp:46:9:46:9 | h [a_] | constructors.cpp:26:15:26:15 | f [a_] |
| constructors.cpp:46:9:46:9 | h [b_] | constructors.cpp:26:15:26:15 | f [b_] |
+| qualifiers.cpp:9:21:9:25 | value | qualifiers.cpp:9:30:9:44 | ... = ... |
+| qualifiers.cpp:9:30:9:44 | ... = ... | qualifiers.cpp:9:30:9:33 | this [post update] [a] |
+| qualifiers.cpp:12:40:12:44 | value | qualifiers.cpp:12:49:12:64 | ... = ... |
+| qualifiers.cpp:12:49:12:64 | ... = ... | qualifiers.cpp:12:49:12:53 | inner [post update] [a] |
+| qualifiers.cpp:13:42:13:46 | value | qualifiers.cpp:13:51:13:65 | ... = ... |
+| qualifiers.cpp:13:51:13:55 | inner [post update] [a] | qualifiers.cpp:13:29:13:33 | inner [a] |
+| qualifiers.cpp:13:51:13:65 | ... = ... | qualifiers.cpp:13:51:13:55 | inner [post update] [a] |
| qualifiers.cpp:22:5:22:9 | ref arg outer [inner, a] | qualifiers.cpp:23:10:23:14 | outer [inner, a] |
| qualifiers.cpp:22:5:22:38 | ... = ... | qualifiers.cpp:22:11:22:18 | call to getInner [post update] [a] |
| qualifiers.cpp:22:11:22:18 | call to getInner [post update] [a] | qualifiers.cpp:22:5:22:9 | ref arg outer [inner, a] |
@@ -381,17 +504,20 @@ edges
| qualifiers.cpp:23:16:23:20 | inner [a] | qualifiers.cpp:23:23:23:23 | a |
| qualifiers.cpp:27:5:27:9 | ref arg outer [inner, a] | qualifiers.cpp:28:10:28:14 | outer [inner, a] |
| qualifiers.cpp:27:11:27:18 | ref arg call to getInner [a] | qualifiers.cpp:27:5:27:9 | ref arg outer [inner, a] |
+| qualifiers.cpp:27:28:27:37 | call to user_input | qualifiers.cpp:9:21:9:25 | value |
| qualifiers.cpp:27:28:27:37 | call to user_input | qualifiers.cpp:27:11:27:18 | ref arg call to getInner [a] |
| qualifiers.cpp:28:10:28:14 | outer [inner, a] | qualifiers.cpp:28:16:28:20 | inner [a] |
| qualifiers.cpp:28:16:28:20 | inner [a] | qualifiers.cpp:28:23:28:23 | a |
| qualifiers.cpp:32:17:32:21 | ref arg outer [inner, a] | qualifiers.cpp:33:10:33:14 | outer [inner, a] |
| qualifiers.cpp:32:23:32:30 | ref arg call to getInner [a] | qualifiers.cpp:32:17:32:21 | ref arg outer [inner, a] |
+| qualifiers.cpp:32:35:32:44 | call to user_input | qualifiers.cpp:12:40:12:44 | value |
| qualifiers.cpp:32:35:32:44 | call to user_input | qualifiers.cpp:32:23:32:30 | ref arg call to getInner [a] |
| qualifiers.cpp:33:10:33:14 | outer [inner, a] | qualifiers.cpp:33:16:33:20 | inner [a] |
| qualifiers.cpp:33:16:33:20 | inner [a] | qualifiers.cpp:33:23:33:23 | a |
| qualifiers.cpp:37:19:37:35 | ref arg * ... [a] | qualifiers.cpp:37:26:37:33 | call to getInner [inner post update] [a] |
| qualifiers.cpp:37:20:37:24 | ref arg outer [inner, a] | qualifiers.cpp:38:10:38:14 | outer [inner, a] |
| qualifiers.cpp:37:26:37:33 | call to getInner [inner post update] [a] | qualifiers.cpp:37:20:37:24 | ref arg outer [inner, a] |
+| qualifiers.cpp:37:38:37:47 | call to user_input | qualifiers.cpp:13:42:13:46 | value |
| qualifiers.cpp:37:38:37:47 | call to user_input | qualifiers.cpp:37:19:37:35 | ref arg * ... [a] |
| qualifiers.cpp:38:10:38:14 | outer [inner, a] | qualifiers.cpp:38:16:38:20 | inner [a] |
| qualifiers.cpp:38:16:38:20 | inner [a] | qualifiers.cpp:38:23:38:23 | a |
@@ -420,17 +546,31 @@ edges
| realistic.cpp:61:25:61:27 | bar [baz, userInput, bufferLen] | realistic.cpp:61:21:61:30 | access to array [baz, userInput, bufferLen] |
| realistic.cpp:61:32:61:34 | baz [userInput, bufferLen] | realistic.cpp:61:37:61:45 | userInput [bufferLen] |
| realistic.cpp:61:37:61:45 | userInput [bufferLen] | realistic.cpp:61:47:61:55 | bufferLen |
+| simple.cpp:18:9:18:9 | this [a_] | simple.cpp:18:22:18:23 | this [a_] |
+| simple.cpp:18:22:18:23 | this [a_] | simple.cpp:18:22:18:23 | a_ |
+| simple.cpp:19:9:19:9 | this [b_] | simple.cpp:19:22:19:23 | this [b_] |
+| simple.cpp:19:22:19:23 | this [b_] | simple.cpp:19:22:19:23 | b_ |
+| simple.cpp:20:19:20:19 | a | simple.cpp:20:24:20:29 | ... = ... |
+| simple.cpp:20:24:20:29 | ... = ... | simple.cpp:20:24:20:25 | this [post update] [a_] |
+| simple.cpp:21:19:21:19 | b | simple.cpp:21:24:21:29 | ... = ... |
+| simple.cpp:21:24:21:29 | ... = ... | simple.cpp:21:24:21:25 | this [post update] [b_] |
| simple.cpp:26:15:26:15 | f [a_] | simple.cpp:28:10:28:10 | f [a_] |
| simple.cpp:26:15:26:15 | f [b_] | simple.cpp:29:10:29:10 | f [b_] |
+| simple.cpp:28:10:28:10 | f [a_] | simple.cpp:18:9:18:9 | this [a_] |
| simple.cpp:28:10:28:10 | f [a_] | simple.cpp:28:12:28:12 | call to a |
+| simple.cpp:29:10:29:10 | f [b_] | simple.cpp:19:9:19:9 | this [b_] |
| simple.cpp:29:10:29:10 | f [b_] | simple.cpp:29:12:29:12 | call to b |
| simple.cpp:39:5:39:5 | ref arg f [a_] | simple.cpp:45:9:45:9 | f [a_] |
+| simple.cpp:39:12:39:21 | call to user_input | simple.cpp:20:19:20:19 | a |
| simple.cpp:39:12:39:21 | call to user_input | simple.cpp:39:5:39:5 | ref arg f [a_] |
| simple.cpp:40:5:40:5 | ref arg g [b_] | simple.cpp:48:9:48:9 | g [b_] |
+| simple.cpp:40:12:40:21 | call to user_input | simple.cpp:21:19:21:19 | b |
| simple.cpp:40:12:40:21 | call to user_input | simple.cpp:40:5:40:5 | ref arg g [b_] |
| simple.cpp:41:5:41:5 | ref arg h [a_] | simple.cpp:51:9:51:9 | h [a_] |
+| simple.cpp:41:12:41:21 | call to user_input | simple.cpp:20:19:20:19 | a |
| simple.cpp:41:12:41:21 | call to user_input | simple.cpp:41:5:41:5 | ref arg h [a_] |
| simple.cpp:42:5:42:5 | ref arg h [b_] | simple.cpp:51:9:51:9 | h [b_] |
+| simple.cpp:42:12:42:21 | call to user_input | simple.cpp:21:19:21:19 | b |
| simple.cpp:42:12:42:21 | call to user_input | simple.cpp:42:5:42:5 | ref arg h [b_] |
| simple.cpp:45:9:45:9 | f [a_] | simple.cpp:26:15:26:15 | f [a_] |
| simple.cpp:48:9:48:9 | g [b_] | simple.cpp:26:15:26:15 | f [b_] |
@@ -440,10 +580,14 @@ edges
| simple.cpp:65:5:65:22 | ... = ... | simple.cpp:65:5:65:5 | a [post update] [i] |
| simple.cpp:65:11:65:20 | call to user_input | simple.cpp:65:5:65:22 | ... = ... |
| simple.cpp:67:10:67:11 | a2 [i] | simple.cpp:67:13:67:13 | i |
+| simple.cpp:78:9:78:15 | this [f2, f1] | simple.cpp:79:16:79:17 | this [f2, f1] |
+| simple.cpp:79:16:79:17 | f2 [f1] | simple.cpp:79:19:79:20 | f1 |
+| simple.cpp:79:16:79:17 | this [f2, f1] | simple.cpp:79:16:79:17 | f2 [f1] |
| simple.cpp:83:9:83:10 | f2 [post update] [f1] | simple.cpp:83:9:83:10 | this [post update] [f2, f1] |
| simple.cpp:83:9:83:10 | this [post update] [f2, f1] | simple.cpp:84:14:84:20 | this [f2, f1] |
| simple.cpp:83:9:83:28 | ... = ... | simple.cpp:83:9:83:10 | f2 [post update] [f1] |
| simple.cpp:83:17:83:26 | call to user_input | simple.cpp:83:9:83:28 | ... = ... |
+| simple.cpp:84:14:84:20 | this [f2, f1] | simple.cpp:78:9:78:15 | this [f2, f1] |
| simple.cpp:84:14:84:20 | this [f2, f1] | simple.cpp:84:14:84:20 | call to getf2f1 |
| simple.cpp:92:5:92:5 | a [post update] [i] | simple.cpp:94:10:94:11 | a2 [i] |
| simple.cpp:92:5:92:22 | ... = ... | simple.cpp:92:5:92:5 | a [post update] [i] |
@@ -477,6 +621,19 @@ edges
| struct_init.c:46:10:46:14 | outer [pointerAB, a] | struct_init.c:46:16:46:24 | pointerAB [a] |
| struct_init.c:46:16:46:24 | pointerAB [a] | struct_init.c:14:24:14:25 | ab [a] |
nodes
+| A.cpp:23:10:23:10 | c | semmle.label | c |
+| A.cpp:25:7:25:10 | this [post update] [c] | semmle.label | this [post update] [c] |
+| A.cpp:25:7:25:17 | ... = ... | semmle.label | ... = ... |
+| A.cpp:27:17:27:17 | c | semmle.label | c |
+| A.cpp:27:22:27:25 | this [post update] [c] | semmle.label | this [post update] [c] |
+| A.cpp:27:22:27:32 | ... = ... | semmle.label | ... = ... |
+| A.cpp:28:8:28:10 | this [c] | semmle.label | this [c] |
+| A.cpp:28:23:28:26 | this [c] | semmle.label | this [c] |
+| A.cpp:28:29:28:29 | c | semmle.label | c |
+| A.cpp:29:23:29:23 | c | semmle.label | c |
+| A.cpp:31:14:31:21 | call to B [c] | semmle.label | call to B [c] |
+| A.cpp:31:14:31:21 | new [c] | semmle.label | new [c] |
+| A.cpp:31:20:31:20 | c | semmle.label | c |
| A.cpp:41:15:41:21 | new | semmle.label | new |
| A.cpp:43:10:43:12 | & ... | semmle.label | & ... |
| A.cpp:47:12:47:18 | new | semmle.label | new |
@@ -500,6 +657,14 @@ nodes
| A.cpp:73:25:73:32 | new | semmle.label | new |
| A.cpp:75:10:75:11 | b2 [c] | semmle.label | b2 [c] |
| A.cpp:75:14:75:14 | c | semmle.label | c |
+| A.cpp:78:27:78:27 | c | semmle.label | c |
+| A.cpp:81:10:81:15 | call to setOnB [c] | semmle.label | call to setOnB [c] |
+| A.cpp:81:21:81:21 | c | semmle.label | c |
+| A.cpp:82:12:82:24 | ... ? ... : ... [c] | semmle.label | ... ? ... : ... [c] |
+| A.cpp:85:26:85:26 | c | semmle.label | c |
+| A.cpp:90:7:90:8 | ref arg b2 [c] | semmle.label | ref arg b2 [c] |
+| A.cpp:90:15:90:15 | c | semmle.label | c |
+| A.cpp:91:14:91:15 | b2 [c] | semmle.label | b2 [c] |
| A.cpp:98:12:98:18 | new | semmle.label | new |
| A.cpp:100:5:100:6 | c1 [post update] [a] | semmle.label | c1 [post update] [a] |
| A.cpp:100:5:100:13 | ... = ... | semmle.label | ... = ... |
@@ -514,11 +679,14 @@ nodes
| A.cpp:131:8:131:8 | ref arg b [c] | semmle.label | ref arg b [c] |
| A.cpp:132:10:132:10 | b [c] | semmle.label | b [c] |
| A.cpp:132:13:132:13 | c | semmle.label | c |
+| A.cpp:140:13:140:13 | b | semmle.label | b |
| A.cpp:142:7:142:7 | b [post update] [c] | semmle.label | b [post update] [c] |
| A.cpp:142:7:142:20 | ... = ... | semmle.label | ... = ... |
| A.cpp:142:14:142:20 | new | semmle.label | new |
| A.cpp:143:7:143:10 | this [post update] [b, c] | semmle.label | this [post update] [b, c] |
| A.cpp:143:7:143:10 | this [post update] [b] | semmle.label | this [post update] [b] |
+| A.cpp:143:7:143:10 | this [post update] [b] | semmle.label | this [post update] [b] |
+| A.cpp:143:7:143:31 | ... = ... | semmle.label | ... = ... |
| A.cpp:143:7:143:31 | ... = ... | semmle.label | ... = ... |
| A.cpp:143:7:143:31 | ... = ... [c] | semmle.label | ... = ... [c] |
| A.cpp:143:25:143:31 | new | semmle.label | new |
@@ -551,6 +719,15 @@ nodes
| A.cpp:167:47:167:50 | next [next, head] | semmle.label | next [next, head] |
| A.cpp:169:12:169:12 | l [head] | semmle.label | l [head] |
| A.cpp:169:15:169:18 | head | semmle.label | head |
+| A.cpp:181:15:181:21 | newHead | semmle.label | newHead |
+| A.cpp:181:32:181:35 | next [head] | semmle.label | next [head] |
+| A.cpp:181:32:181:35 | next [next, head] | semmle.label | next [next, head] |
+| A.cpp:183:7:183:10 | this [post update] [head] | semmle.label | this [post update] [head] |
+| A.cpp:183:7:183:20 | ... = ... | semmle.label | ... = ... |
+| A.cpp:184:7:184:10 | this [post update] [next, head] | semmle.label | this [post update] [next, head] |
+| A.cpp:184:7:184:10 | this [post update] [next, next, head] | semmle.label | this [post update] [next, next, head] |
+| A.cpp:184:7:184:23 | ... = ... [head] | semmle.label | ... = ... [head] |
+| A.cpp:184:7:184:23 | ... = ... [next, head] | semmle.label | ... = ... [next, head] |
| B.cpp:6:15:6:24 | new | semmle.label | new |
| B.cpp:7:16:7:35 | call to Box1 [elem1] | semmle.label | call to Box1 [elem1] |
| B.cpp:7:25:7:25 | e | semmle.label | e |
@@ -567,6 +744,18 @@ nodes
| B.cpp:19:10:19:11 | b2 [box1, elem2] | semmle.label | b2 [box1, elem2] |
| B.cpp:19:14:19:17 | box1 [elem2] | semmle.label | box1 [elem2] |
| B.cpp:19:20:19:24 | elem2 | semmle.label | elem2 |
+| B.cpp:33:16:33:17 | e1 | semmle.label | e1 |
+| B.cpp:33:26:33:27 | e2 | semmle.label | e2 |
+| B.cpp:35:7:35:10 | this [post update] [elem1] | semmle.label | this [post update] [elem1] |
+| B.cpp:35:7:35:22 | ... = ... | semmle.label | ... = ... |
+| B.cpp:36:7:36:10 | this [post update] [elem2] | semmle.label | this [post update] [elem2] |
+| B.cpp:36:7:36:22 | ... = ... | semmle.label | ... = ... |
+| B.cpp:44:16:44:17 | b1 [elem1] | semmle.label | b1 [elem1] |
+| B.cpp:44:16:44:17 | b1 [elem2] | semmle.label | b1 [elem2] |
+| B.cpp:46:7:46:10 | this [post update] [box1, elem1] | semmle.label | this [post update] [box1, elem1] |
+| B.cpp:46:7:46:10 | this [post update] [box1, elem2] | semmle.label | this [post update] [box1, elem2] |
+| B.cpp:46:7:46:21 | ... = ... [elem1] | semmle.label | ... = ... [elem1] |
+| B.cpp:46:7:46:21 | ... = ... [elem2] | semmle.label | ... = ... [elem2] |
| C.cpp:18:12:18:18 | call to C [s1] | semmle.label | call to C [s1] |
| C.cpp:18:12:18:18 | call to C [s3] | semmle.label | call to C [s3] |
| C.cpp:19:5:19:5 | c [s1] | semmle.label | c [s1] |
@@ -582,6 +771,15 @@ nodes
| C.cpp:29:10:29:11 | this [s1] | semmle.label | this [s1] |
| C.cpp:31:10:31:11 | s3 | semmle.label | s3 |
| C.cpp:31:10:31:11 | this [s3] | semmle.label | this [s3] |
+| D.cpp:10:11:10:17 | this [elem] | semmle.label | this [elem] |
+| D.cpp:10:30:10:33 | elem | semmle.label | elem |
+| D.cpp:10:30:10:33 | this [elem] | semmle.label | this [elem] |
+| D.cpp:11:24:11:24 | e | semmle.label | e |
+| D.cpp:11:29:11:32 | this [post update] [elem] | semmle.label | this [post update] [elem] |
+| D.cpp:11:29:11:36 | ... = ... | semmle.label | ... = ... |
+| D.cpp:17:11:17:17 | this [box, elem] | semmle.label | this [box, elem] |
+| D.cpp:17:30:17:32 | box [elem] | semmle.label | box [elem] |
+| D.cpp:17:30:17:32 | this [box, elem] | semmle.label | this [box, elem] |
| D.cpp:21:30:21:31 | b2 [box, elem] | semmle.label | b2 [box, elem] |
| D.cpp:22:10:22:11 | b2 [box, elem] | semmle.label | b2 [box, elem] |
| D.cpp:22:14:22:20 | call to getBox1 [elem] | semmle.label | call to getBox1 [elem] |
@@ -729,6 +927,30 @@ nodes
| arrays.cpp:44:10:44:17 | indirect [arr, data] | semmle.label | indirect [arr, data] |
| arrays.cpp:44:20:44:22 | arr [data] | semmle.label | arr [data] |
| arrays.cpp:44:27:44:30 | data | semmle.label | data |
+| by_reference.cpp:11:48:11:52 | value | semmle.label | value |
+| by_reference.cpp:12:5:12:5 | s [post update] [a] | semmle.label | s [post update] [a] |
+| by_reference.cpp:12:5:12:16 | ... = ... | semmle.label | ... = ... |
+| by_reference.cpp:15:26:15:30 | value | semmle.label | value |
+| by_reference.cpp:16:5:16:8 | this [post update] [a] | semmle.label | this [post update] [a] |
+| by_reference.cpp:16:5:16:19 | ... = ... | semmle.label | ... = ... |
+| by_reference.cpp:19:28:19:32 | value | semmle.label | value |
+| by_reference.cpp:20:5:20:8 | ref arg this [a] | semmle.label | ref arg this [a] |
+| by_reference.cpp:20:23:20:27 | value | semmle.label | value |
+| by_reference.cpp:23:34:23:38 | value | semmle.label | value |
+| by_reference.cpp:24:19:24:22 | ref arg this [a] | semmle.label | ref arg this [a] |
+| by_reference.cpp:24:25:24:29 | value | semmle.label | value |
+| by_reference.cpp:31:46:31:46 | s [a] | semmle.label | s [a] |
+| by_reference.cpp:32:12:32:12 | s [a] | semmle.label | s [a] |
+| by_reference.cpp:32:15:32:15 | a | semmle.label | a |
+| by_reference.cpp:35:9:35:19 | this [a] | semmle.label | this [a] |
+| by_reference.cpp:36:12:36:15 | this [a] | semmle.label | this [a] |
+| by_reference.cpp:36:18:36:18 | a | semmle.label | a |
+| by_reference.cpp:39:9:39:21 | this [a] | semmle.label | this [a] |
+| by_reference.cpp:40:12:40:15 | this [a] | semmle.label | this [a] |
+| by_reference.cpp:40:18:40:28 | call to getDirectly | semmle.label | call to getDirectly |
+| by_reference.cpp:43:9:43:27 | this [a] | semmle.label | this [a] |
+| by_reference.cpp:44:12:44:24 | call to nonMemberGetA | semmle.label | call to nonMemberGetA |
+| by_reference.cpp:44:26:44:29 | this [a] | semmle.label | this [a] |
| by_reference.cpp:50:3:50:3 | ref arg s [a] | semmle.label | ref arg s [a] |
| by_reference.cpp:50:17:50:26 | call to user_input | semmle.label | call to user_input |
| by_reference.cpp:51:8:51:8 | s [a] | semmle.label | s [a] |
@@ -818,6 +1040,18 @@ nodes
| by_reference.cpp:135:27:135:27 | a | semmle.label | a |
| by_reference.cpp:136:8:136:13 | pouter [a] | semmle.label | pouter [a] |
| by_reference.cpp:136:16:136:16 | a | semmle.label | a |
+| complex.cpp:9:7:9:7 | this [a_] | semmle.label | this [a_] |
+| complex.cpp:9:20:9:21 | a_ | semmle.label | a_ |
+| complex.cpp:9:20:9:21 | this [a_] | semmle.label | this [a_] |
+| complex.cpp:10:7:10:7 | this [b_] | semmle.label | this [b_] |
+| complex.cpp:10:20:10:21 | b_ | semmle.label | b_ |
+| complex.cpp:10:20:10:21 | this [b_] | semmle.label | this [b_] |
+| complex.cpp:11:17:11:17 | a | semmle.label | a |
+| complex.cpp:11:22:11:23 | this [post update] [a_] | semmle.label | this [post update] [a_] |
+| complex.cpp:11:22:11:27 | ... = ... | semmle.label | ... = ... |
+| complex.cpp:12:17:12:17 | b | semmle.label | b |
+| complex.cpp:12:22:12:23 | this [post update] [b_] | semmle.label | this [post update] [b_] |
+| complex.cpp:12:22:12:27 | ... = ... | semmle.label | ... = ... |
| complex.cpp:40:17:40:17 | b [inner, f, a_] | semmle.label | b [inner, f, a_] |
| complex.cpp:40:17:40:17 | b [inner, f, b_] | semmle.label | b [inner, f, b_] |
| complex.cpp:42:8:42:8 | b [inner, f, a_] | semmle.label | b [inner, f, a_] |
@@ -874,6 +1108,18 @@ nodes
| conflated.cpp:61:8:61:9 | ll [next, y] | semmle.label | ll [next, y] |
| conflated.cpp:61:12:61:15 | next [y] | semmle.label | next [y] |
| conflated.cpp:61:18:61:18 | y | semmle.label | y |
+| constructors.cpp:18:9:18:9 | this [a_] | semmle.label | this [a_] |
+| constructors.cpp:18:22:18:23 | a_ | semmle.label | a_ |
+| constructors.cpp:18:22:18:23 | this [a_] | semmle.label | this [a_] |
+| constructors.cpp:19:9:19:9 | this [b_] | semmle.label | this [b_] |
+| constructors.cpp:19:22:19:23 | b_ | semmle.label | b_ |
+| constructors.cpp:19:22:19:23 | this [b_] | semmle.label | this [b_] |
+| constructors.cpp:23:13:23:13 | a | semmle.label | a |
+| constructors.cpp:23:20:23:20 | b | semmle.label | b |
+| constructors.cpp:23:25:23:29 | constructor init of field a_ [post-this] [a_] | semmle.label | constructor init of field a_ [post-this] [a_] |
+| constructors.cpp:23:28:23:28 | a | semmle.label | a |
+| constructors.cpp:23:32:23:36 | constructor init of field b_ [post-this] [b_] | semmle.label | constructor init of field b_ [post-this] [b_] |
+| constructors.cpp:23:35:23:35 | b | semmle.label | b |
| constructors.cpp:26:15:26:15 | f [a_] | semmle.label | f [a_] |
| constructors.cpp:26:15:26:15 | f [b_] | semmle.label | f [b_] |
| constructors.cpp:28:10:28:10 | f [a_] | semmle.label | f [a_] |
@@ -892,6 +1138,16 @@ nodes
| constructors.cpp:43:9:43:9 | g [b_] | semmle.label | g [b_] |
| constructors.cpp:46:9:46:9 | h [a_] | semmle.label | h [a_] |
| constructors.cpp:46:9:46:9 | h [b_] | semmle.label | h [b_] |
+| qualifiers.cpp:9:21:9:25 | value | semmle.label | value |
+| qualifiers.cpp:9:30:9:33 | this [post update] [a] | semmle.label | this [post update] [a] |
+| qualifiers.cpp:9:30:9:44 | ... = ... | semmle.label | ... = ... |
+| qualifiers.cpp:12:40:12:44 | value | semmle.label | value |
+| qualifiers.cpp:12:49:12:53 | inner [post update] [a] | semmle.label | inner [post update] [a] |
+| qualifiers.cpp:12:49:12:64 | ... = ... | semmle.label | ... = ... |
+| qualifiers.cpp:13:29:13:33 | inner [a] | semmle.label | inner [a] |
+| qualifiers.cpp:13:42:13:46 | value | semmle.label | value |
+| qualifiers.cpp:13:51:13:55 | inner [post update] [a] | semmle.label | inner [post update] [a] |
+| qualifiers.cpp:13:51:13:65 | ... = ... | semmle.label | ... = ... |
| qualifiers.cpp:22:5:22:9 | ref arg outer [inner, a] | semmle.label | ref arg outer [inner, a] |
| qualifiers.cpp:22:5:22:38 | ... = ... | semmle.label | ... = ... |
| qualifiers.cpp:22:11:22:18 | call to getInner [post update] [a] | semmle.label | call to getInner [post update] [a] |
@@ -946,6 +1202,18 @@ nodes
| realistic.cpp:61:32:61:34 | baz [userInput, bufferLen] | semmle.label | baz [userInput, bufferLen] |
| realistic.cpp:61:37:61:45 | userInput [bufferLen] | semmle.label | userInput [bufferLen] |
| realistic.cpp:61:47:61:55 | bufferLen | semmle.label | bufferLen |
+| simple.cpp:18:9:18:9 | this [a_] | semmle.label | this [a_] |
+| simple.cpp:18:22:18:23 | a_ | semmle.label | a_ |
+| simple.cpp:18:22:18:23 | this [a_] | semmle.label | this [a_] |
+| simple.cpp:19:9:19:9 | this [b_] | semmle.label | this [b_] |
+| simple.cpp:19:22:19:23 | b_ | semmle.label | b_ |
+| simple.cpp:19:22:19:23 | this [b_] | semmle.label | this [b_] |
+| simple.cpp:20:19:20:19 | a | semmle.label | a |
+| simple.cpp:20:24:20:25 | this [post update] [a_] | semmle.label | this [post update] [a_] |
+| simple.cpp:20:24:20:29 | ... = ... | semmle.label | ... = ... |
+| simple.cpp:21:19:21:19 | b | semmle.label | b |
+| simple.cpp:21:24:21:25 | this [post update] [b_] | semmle.label | this [post update] [b_] |
+| simple.cpp:21:24:21:29 | ... = ... | semmle.label | ... = ... |
| simple.cpp:26:15:26:15 | f [a_] | semmle.label | f [a_] |
| simple.cpp:26:15:26:15 | f [b_] | semmle.label | f [b_] |
| simple.cpp:28:10:28:10 | f [a_] | semmle.label | f [a_] |
@@ -969,6 +1237,10 @@ nodes
| simple.cpp:65:11:65:20 | call to user_input | semmle.label | call to user_input |
| simple.cpp:67:10:67:11 | a2 [i] | semmle.label | a2 [i] |
| simple.cpp:67:13:67:13 | i | semmle.label | i |
+| simple.cpp:78:9:78:15 | this [f2, f1] | semmle.label | this [f2, f1] |
+| simple.cpp:79:16:79:17 | f2 [f1] | semmle.label | f2 [f1] |
+| simple.cpp:79:16:79:17 | this [f2, f1] | semmle.label | this [f2, f1] |
+| simple.cpp:79:19:79:20 | f1 | semmle.label | f1 |
| simple.cpp:83:9:83:10 | f2 [post update] [f1] | semmle.label | f2 [post update] [f1] |
| simple.cpp:83:9:83:10 | this [post update] [f2, f1] | semmle.label | this [post update] [f2, f1] |
| simple.cpp:83:9:83:28 | ... = ... | semmle.label | ... = ... |
@@ -1008,6 +1280,65 @@ nodes
| struct_init.c:43:5:43:7 | & ... [a] | semmle.label | & ... [a] |
| struct_init.c:46:10:46:14 | outer [pointerAB, a] | semmle.label | outer [pointerAB, a] |
| struct_init.c:46:16:46:24 | pointerAB [a] | semmle.label | pointerAB [a] |
+subpaths
+| A.cpp:31:20:31:20 | c | A.cpp:23:10:23:10 | c | A.cpp:25:7:25:10 | this [post update] [c] | A.cpp:31:14:31:21 | call to B [c] |
+| A.cpp:48:20:48:20 | c | A.cpp:29:23:29:23 | c | A.cpp:31:14:31:21 | new [c] | A.cpp:48:12:48:18 | call to make [c] |
+| A.cpp:55:12:55:19 | new | A.cpp:27:17:27:17 | c | A.cpp:27:22:27:25 | this [post update] [c] | A.cpp:55:5:55:5 | ref arg b [c] |
+| A.cpp:56:10:56:10 | b [c] | A.cpp:28:8:28:10 | this [c] | A.cpp:28:29:28:29 | c | A.cpp:56:13:56:15 | call to get |
+| A.cpp:57:11:57:24 | new [c] | A.cpp:28:8:28:10 | this [c] | A.cpp:28:29:28:29 | c | A.cpp:57:28:57:30 | call to get |
+| A.cpp:57:17:57:23 | new | A.cpp:23:10:23:10 | c | A.cpp:25:7:25:10 | this [post update] [c] | A.cpp:57:11:57:24 | call to B [c] |
+| A.cpp:64:21:64:28 | new | A.cpp:85:26:85:26 | c | A.cpp:91:14:91:15 | b2 [c] | A.cpp:64:10:64:15 | call to setOnB [c] |
+| A.cpp:73:25:73:32 | new | A.cpp:78:27:78:27 | c | A.cpp:82:12:82:24 | ... ? ... : ... [c] | A.cpp:73:10:73:19 | call to setOnBWrap [c] |
+| A.cpp:81:21:81:21 | c | A.cpp:85:26:85:26 | c | A.cpp:91:14:91:15 | b2 [c] | A.cpp:81:10:81:15 | call to setOnB [c] |
+| A.cpp:90:15:90:15 | c | A.cpp:27:17:27:17 | c | A.cpp:27:22:27:25 | this [post update] [c] | A.cpp:90:7:90:8 | ref arg b2 [c] |
+| A.cpp:126:12:126:18 | new | A.cpp:27:17:27:17 | c | A.cpp:27:22:27:25 | this [post update] [c] | A.cpp:126:5:126:5 | ref arg b [c] |
+| A.cpp:151:18:151:18 | b | A.cpp:140:13:140:13 | b | A.cpp:143:7:143:10 | this [post update] [b] | A.cpp:151:12:151:24 | call to D [b] |
+| A.cpp:160:29:160:29 | b | A.cpp:181:15:181:21 | newHead | A.cpp:183:7:183:10 | this [post update] [head] | A.cpp:160:18:160:60 | call to MyList [head] |
+| A.cpp:161:38:161:39 | l1 [head] | A.cpp:181:32:181:35 | next [head] | A.cpp:184:7:184:10 | this [post update] [next, head] | A.cpp:161:18:161:40 | call to MyList [next, head] |
+| A.cpp:162:38:162:39 | l2 [next, head] | A.cpp:181:32:181:35 | next [next, head] | A.cpp:184:7:184:10 | this [post update] [next, next, head] | A.cpp:162:18:162:40 | call to MyList [next, next, head] |
+| B.cpp:7:25:7:25 | e | B.cpp:33:16:33:17 | e1 | B.cpp:35:7:35:10 | this [post update] [elem1] | B.cpp:7:16:7:35 | call to Box1 [elem1] |
+| B.cpp:8:25:8:26 | b1 [elem1] | B.cpp:44:16:44:17 | b1 [elem1] | B.cpp:46:7:46:10 | this [post update] [box1, elem1] | B.cpp:8:16:8:27 | call to Box2 [box1, elem1] |
+| B.cpp:16:37:16:37 | e | B.cpp:33:26:33:27 | e2 | B.cpp:36:7:36:10 | this [post update] [elem2] | B.cpp:16:16:16:38 | call to Box1 [elem2] |
+| B.cpp:17:25:17:26 | b1 [elem2] | B.cpp:44:16:44:17 | b1 [elem2] | B.cpp:46:7:46:10 | this [post update] [box1, elem2] | B.cpp:17:16:17:27 | call to Box2 [box1, elem2] |
+| D.cpp:22:10:22:11 | b2 [box, elem] | D.cpp:17:11:17:17 | this [box, elem] | D.cpp:17:30:17:32 | box [elem] | D.cpp:22:14:22:20 | call to getBox1 [elem] |
+| D.cpp:22:14:22:20 | call to getBox1 [elem] | D.cpp:10:11:10:17 | this [elem] | D.cpp:10:30:10:33 | elem | D.cpp:22:25:22:31 | call to getElem |
+| D.cpp:37:21:37:21 | e | D.cpp:11:24:11:24 | e | D.cpp:11:29:11:32 | this [post update] [elem] | D.cpp:37:8:37:10 | ref arg box [elem] |
+| D.cpp:51:27:51:27 | e | D.cpp:11:24:11:24 | e | D.cpp:11:29:11:32 | this [post update] [elem] | D.cpp:51:8:51:14 | ref arg call to getBox1 [elem] |
+| by_reference.cpp:20:23:20:27 | value | by_reference.cpp:15:26:15:30 | value | by_reference.cpp:16:5:16:8 | this [post update] [a] | by_reference.cpp:20:5:20:8 | ref arg this [a] |
+| by_reference.cpp:24:25:24:29 | value | by_reference.cpp:11:48:11:52 | value | by_reference.cpp:12:5:12:5 | s [post update] [a] | by_reference.cpp:24:19:24:22 | ref arg this [a] |
+| by_reference.cpp:40:12:40:15 | this [a] | by_reference.cpp:35:9:35:19 | this [a] | by_reference.cpp:36:18:36:18 | a | by_reference.cpp:40:18:40:28 | call to getDirectly |
+| by_reference.cpp:44:26:44:29 | this [a] | by_reference.cpp:31:46:31:46 | s [a] | by_reference.cpp:32:15:32:15 | a | by_reference.cpp:44:12:44:24 | call to nonMemberGetA |
+| by_reference.cpp:50:17:50:26 | call to user_input | by_reference.cpp:15:26:15:30 | value | by_reference.cpp:16:5:16:8 | this [post update] [a] | by_reference.cpp:50:3:50:3 | ref arg s [a] |
+| by_reference.cpp:51:8:51:8 | s [a] | by_reference.cpp:35:9:35:19 | this [a] | by_reference.cpp:36:18:36:18 | a | by_reference.cpp:51:10:51:20 | call to getDirectly |
+| by_reference.cpp:56:19:56:28 | call to user_input | by_reference.cpp:19:28:19:32 | value | by_reference.cpp:20:5:20:8 | ref arg this [a] | by_reference.cpp:56:3:56:3 | ref arg s [a] |
+| by_reference.cpp:57:8:57:8 | s [a] | by_reference.cpp:39:9:39:21 | this [a] | by_reference.cpp:40:18:40:28 | call to getDirectly | by_reference.cpp:57:10:57:22 | call to getIndirectly |
+| by_reference.cpp:62:25:62:34 | call to user_input | by_reference.cpp:23:34:23:38 | value | by_reference.cpp:24:19:24:22 | ref arg this [a] | by_reference.cpp:62:3:62:3 | ref arg s [a] |
+| by_reference.cpp:63:8:63:8 | s [a] | by_reference.cpp:43:9:43:27 | this [a] | by_reference.cpp:44:12:44:24 | call to nonMemberGetA | by_reference.cpp:63:10:63:28 | call to getThroughNonMember |
+| by_reference.cpp:68:21:68:30 | call to user_input | by_reference.cpp:11:48:11:52 | value | by_reference.cpp:12:5:12:5 | s [post update] [a] | by_reference.cpp:68:17:68:18 | ref arg & ... [a] |
+| by_reference.cpp:69:22:69:23 | & ... [a] | by_reference.cpp:31:46:31:46 | s [a] | by_reference.cpp:32:15:32:15 | a | by_reference.cpp:69:8:69:20 | call to nonMemberGetA |
+| complex.cpp:42:16:42:16 | f [a_] | complex.cpp:9:7:9:7 | this [a_] | complex.cpp:9:20:9:21 | a_ | complex.cpp:42:18:42:18 | call to a |
+| complex.cpp:43:16:43:16 | f [b_] | complex.cpp:10:7:10:7 | this [b_] | complex.cpp:10:20:10:21 | b_ | complex.cpp:43:18:43:18 | call to b |
+| complex.cpp:53:19:53:28 | call to user_input | complex.cpp:11:17:11:17 | a | complex.cpp:11:22:11:23 | this [post update] [a_] | complex.cpp:53:12:53:12 | ref arg f [a_] |
+| complex.cpp:54:19:54:28 | call to user_input | complex.cpp:12:17:12:17 | b | complex.cpp:12:22:12:23 | this [post update] [b_] | complex.cpp:54:12:54:12 | ref arg f [b_] |
+| complex.cpp:55:19:55:28 | call to user_input | complex.cpp:11:17:11:17 | a | complex.cpp:11:22:11:23 | this [post update] [a_] | complex.cpp:55:12:55:12 | ref arg f [a_] |
+| complex.cpp:56:19:56:28 | call to user_input | complex.cpp:12:17:12:17 | b | complex.cpp:12:22:12:23 | this [post update] [b_] | complex.cpp:56:12:56:12 | ref arg f [b_] |
+| constructors.cpp:28:10:28:10 | f [a_] | constructors.cpp:18:9:18:9 | this [a_] | constructors.cpp:18:22:18:23 | a_ | constructors.cpp:28:12:28:12 | call to a |
+| constructors.cpp:29:10:29:10 | f [b_] | constructors.cpp:19:9:19:9 | this [b_] | constructors.cpp:19:22:19:23 | b_ | constructors.cpp:29:12:29:12 | call to b |
+| constructors.cpp:34:11:34:20 | call to user_input | constructors.cpp:23:13:23:13 | a | constructors.cpp:23:25:23:29 | constructor init of field a_ [post-this] [a_] | constructors.cpp:34:11:34:26 | call to Foo [a_] |
+| constructors.cpp:35:14:35:23 | call to user_input | constructors.cpp:23:20:23:20 | b | constructors.cpp:23:32:23:36 | constructor init of field b_ [post-this] [b_] | constructors.cpp:35:11:35:26 | call to Foo [b_] |
+| constructors.cpp:36:11:36:20 | call to user_input | constructors.cpp:23:13:23:13 | a | constructors.cpp:23:25:23:29 | constructor init of field a_ [post-this] [a_] | constructors.cpp:36:11:36:37 | call to Foo [a_] |
+| constructors.cpp:36:25:36:34 | call to user_input | constructors.cpp:23:20:23:20 | b | constructors.cpp:23:32:23:36 | constructor init of field b_ [post-this] [b_] | constructors.cpp:36:11:36:37 | call to Foo [b_] |
+| qualifiers.cpp:27:28:27:37 | call to user_input | qualifiers.cpp:9:21:9:25 | value | qualifiers.cpp:9:30:9:33 | this [post update] [a] | qualifiers.cpp:27:11:27:18 | ref arg call to getInner [a] |
+| qualifiers.cpp:32:35:32:44 | call to user_input | qualifiers.cpp:12:40:12:44 | value | qualifiers.cpp:12:49:12:53 | inner [post update] [a] | qualifiers.cpp:32:23:32:30 | ref arg call to getInner [a] |
+| qualifiers.cpp:37:38:37:47 | call to user_input | qualifiers.cpp:13:42:13:46 | value | qualifiers.cpp:13:29:13:33 | inner [a] | qualifiers.cpp:37:19:37:35 | ref arg * ... [a] |
+| qualifiers.cpp:37:38:37:47 | call to user_input | qualifiers.cpp:13:42:13:46 | value | qualifiers.cpp:13:51:13:55 | inner [post update] [a] | qualifiers.cpp:37:19:37:35 | ref arg * ... [a] |
+| simple.cpp:28:10:28:10 | f [a_] | simple.cpp:18:9:18:9 | this [a_] | simple.cpp:18:22:18:23 | a_ | simple.cpp:28:12:28:12 | call to a |
+| simple.cpp:29:10:29:10 | f [b_] | simple.cpp:19:9:19:9 | this [b_] | simple.cpp:19:22:19:23 | b_ | simple.cpp:29:12:29:12 | call to b |
+| simple.cpp:39:12:39:21 | call to user_input | simple.cpp:20:19:20:19 | a | simple.cpp:20:24:20:25 | this [post update] [a_] | simple.cpp:39:5:39:5 | ref arg f [a_] |
+| simple.cpp:40:12:40:21 | call to user_input | simple.cpp:21:19:21:19 | b | simple.cpp:21:24:21:25 | this [post update] [b_] | simple.cpp:40:5:40:5 | ref arg g [b_] |
+| simple.cpp:41:12:41:21 | call to user_input | simple.cpp:20:19:20:19 | a | simple.cpp:20:24:20:25 | this [post update] [a_] | simple.cpp:41:5:41:5 | ref arg h [a_] |
+| simple.cpp:42:12:42:21 | call to user_input | simple.cpp:21:19:21:19 | b | simple.cpp:21:24:21:25 | this [post update] [b_] | simple.cpp:42:5:42:5 | ref arg h [b_] |
+| simple.cpp:84:14:84:20 | this [f2, f1] | simple.cpp:78:9:78:15 | this [f2, f1] | simple.cpp:79:19:79:20 | f1 | simple.cpp:84:14:84:20 | call to getf2f1 |
#select
| A.cpp:43:10:43:12 | & ... | A.cpp:41:15:41:21 | new | A.cpp:43:10:43:12 | & ... | & ... flows from $@ | A.cpp:41:15:41:21 | new | new |
| A.cpp:49:13:49:13 | c | A.cpp:47:12:47:18 | new | A.cpp:49:13:49:13 | c | c flows from $@ | A.cpp:47:12:47:18 | new | new |
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/copyableclass_declonly.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/copyableclass_declonly.cpp
index f8169db1128..307e1ecaa76 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/copyableclass_declonly.cpp
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/copyableclass_declonly.cpp
@@ -64,6 +64,6 @@ void test_copyableclass_declonly()
sink(s1); // $ ast,ir
sink(s2); // $ ast,ir
- sink(s3 = source()); // $ ast MISSING: ir
+ sink(s3 = source()); // $ ast,ir
}
}
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/map.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/map.cpp
index aabd898830e..4e85a6b9aa3 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/map.cpp
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/map.cpp
@@ -415,10 +415,10 @@ void test_unordered_map()
sink(m30["abc"]);
sink(m31.try_emplace("abc", source(), 2)); // $ ast,ir
sink(m31); // $ ast,ir
- sink(m31["abc"]); // $ ast MISSING: ir
+ sink(m31["abc"]); // $ ast,ir
sink(m32.try_emplace("abc", 1, source())); // $ ast,ir
sink(m32); // $ ast,ir
- sink(m32["abc"]); // $ ast MISSING: ir
+ sink(m32["abc"]); // $ ast,ir
// additional emplace test cases
std::unordered_map m33;
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/string.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/string.cpp
index 0e29d314887..9ce653c654a 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/string.cpp
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/string.cpp
@@ -30,20 +30,20 @@ void test_string()
sink(b);
sink(c); // $ ast,ir
sink(b.c_str());
- sink(c.c_str()); // $ ast MISSING: ir
+ sink(c.c_str()); // $ ast,ir
}
void test_strings2()
{
string path1 = user_input();
- sink(path1.c_str(), "r"); // $ ast MISSING: ir
+ sink(path1.c_str(), "r"); // $ ast,ir
string path2;
path2 = user_input();
- sink(path2.c_str(), "r"); // $ ast MISSING: ir
+ sink(path2.c_str(), "r"); // $ ast,ir
string path3(user_input());
- sink(path3.c_str(), "r"); // $ ast MISSING: ir
+ sink(path3.c_str(), "r"); // $ ast,ir
}
void test_string3()
@@ -67,7 +67,7 @@ void test_string4()
// convert back std::string -> char *
cs = ss.c_str();
- sink(cs); // $ ast MISSING: ir
+ sink(cs); // $ ast,ir
sink(ss); // $ ast,ir
}
@@ -159,12 +159,12 @@ void test_string_append() {
sink(s5); // $ ast,ir
s6 = s3;
- sink(s6 += s4); // $ ast MISSING: ir
+ sink(s6 += s4); // $ ast,ir
sink(s6); // $ ast,ir
s7 = s3;
- sink(s7 += source()); // $ ast MISSING: ir
- sink(s7 += " "); // $ ast MISSING: ir
+ sink(s7 += source()); // $ ast,ir
+ sink(s7 += " "); // $ ast,ir
sink(s7); // $ ast,ir
s8 = s3;
@@ -196,10 +196,10 @@ void test_string_assign() {
sink(s3.assign(s1));
sink(s3);
- sink(s4.assign(s2)); // $ ast MISSING: ir
+ sink(s4.assign(s2)); // $ ast,ir
sink(s4); // $ ast,ir
- sink(s5.assign(10, c)); // $ ast MISSING: ir
+ sink(s5.assign(10, c)); // $ ast,ir
sink(s5); // $ ast,ir
sink(s6.assign(s1));
@@ -217,15 +217,15 @@ void test_string_insert() {
sink(s3);
s4 = s2;
- sink(s4.insert(0, s1)); // $ ast MISSING: ir
+ sink(s4.insert(0, s1)); // $ ast,ir
sink(s4); // $ ast,ir
s5 = s1;
- sink(s5.insert(0, s2)); // $ ast MISSING: ir
+ sink(s5.insert(0, s2)); // $ ast,ir
sink(s5); // $ ast,ir
s6 = s1;
- sink(s6.insert(0, 10, c)); // $ ast MISSING: ir
+ sink(s6.insert(0, 10, c)); // $ ast,ir
sink(s6); // $ ast,ir
}
@@ -240,15 +240,15 @@ void test_string_replace() {
sink(s3);
s4 = s2;
- sink(s4.replace(1, 2, s1)); // $ ast MISSING: ir
+ sink(s4.replace(1, 2, s1)); // $ ast,ir
sink(s4); // $ ast,ir
s5 = s1;
- sink(s5.replace(1, 2, s2)); // $ ast MISSING: ir
+ sink(s5.replace(1, 2, s2)); // $ ast,ir
sink(s5); // $ ast,ir
s6 = s1;
- sink(s6.replace(1, 2, 10, c)); // $ ast MISSING: ir
+ sink(s6.replace(1, 2, 10, c)); // $ ast,ir
sink(s6); // $ ast,ir
}
@@ -309,7 +309,7 @@ void test_string_data()
std::string b(source());
sink(a.data());
- sink(b.data()); // $ ast MISSING: ir
+ sink(b.data()); // $ ast,ir
sink(a.length());
sink(b.length());
}
@@ -360,7 +360,7 @@ void test_string_iterators() {
std::string s4("world");
sink(s1);
- sink(s1.append(s2.begin(), s2.end())); // $ ast MISSING: ir
+ sink(s1.append(s2.begin(), s2.end())); // $ ast,ir
sink(s1); // $ ast,ir
sink(s3);
@@ -433,7 +433,7 @@ void test_string_insert_more()
sink(s1.insert(0, cs1));
sink(s1);
- sink(s2.insert(0, cs2)); // $ ast MISSING: ir
+ sink(s2.insert(0, cs2)); // $ ast,ir
sink(s2); // $ ast,ir
}
@@ -446,7 +446,7 @@ void test_string_iterator_methods()
sink(a.insert(a.begin(), 10, 'x'));
sink(a);
- sink(b.insert(b.begin(), 10, ns_char::source())); // $ ast MISSING: ir
+ sink(b.insert(b.begin(), 10, ns_char::source())); // $ ast,ir
sink(b); // $ ast,ir
}
@@ -459,10 +459,10 @@ void test_string_iterator_methods()
sink(c.insert(c.end(), s1.begin(), s1.end()));
sink(c);
- sink(d.insert(d.end(), s2.begin(), s2.end())); // $ ast MISSING: ir
+ sink(d.insert(d.end(), s2.begin(), s2.end())); // $ ast,ir
sink(d); // $ ast,ir
- sink(s2.insert(s2.end(), s1.begin(), s1.end())); // $ ast MISSING: ir
+ sink(s2.insert(s2.end(), s1.begin(), s1.end())); // $ ast,ir
sink(s2); // $ ast,ir
}
@@ -475,10 +475,10 @@ void test_string_iterator_methods()
sink(e.append(s3.begin(), s3.end()));
sink(e);
- sink(f.append(s4.begin(), s4.end())); // $ ast MISSING: ir
+ sink(f.append(s4.begin(), s4.end())); // $ ast,ir
sink(f); // $ ast,ir
- sink(s4.append(s3.begin(), s3.end())); // $ ast MISSING: ir
+ sink(s4.append(s3.begin(), s3.end())); // $ ast,ir
sink(s4); // $ ast,ir
}
@@ -491,7 +491,7 @@ void test_string_iterator_methods()
sink(g.assign(s5.cbegin(), s5.cend()));
sink(g);
- sink(h.assign(s6.cbegin(), s6.cend())); // $ ast MISSING: ir
+ sink(h.assign(s6.cbegin(), s6.cend())); // $ ast,ir
sink(h); // $ ast,ir
sink(s6.assign(s5.cbegin(), s5.cend()));
@@ -519,8 +519,8 @@ void test_string_front_back() {
sink(a.front());
sink(a.back());
a.push_back(ns_char::source());
- sink(a.front()); // $ SPURIOUS: ast
- sink(a.back()); // $ ast MISSING: ir
+ sink(a.front()); // $ SPURIOUS: ast,ir
+ sink(a.back()); // $ ast,ir
}
void test_string_return_assign() {
@@ -533,12 +533,12 @@ void test_string_return_assign() {
std::string f("ff");
sink( a += (b += "bb") );
- sink( c += (d += source()) ); // $ ast MISSING: ir
- sink( (e += "ee") += source() ); // $ ast MISSING: ir
- sink( (f += source()) += "ff" ); // $ ast MISSING: ir
+ sink( c += (d += source()) ); // $ ast,ir
+ sink( (e += "ee") += source() ); // $ ast,ir
+ sink( (f += source()) += "ff" ); // $ ast,ir
sink(a);
sink(b);
- sink(c); // $ ast MISSING: ir
+ sink(c); // $ ast,ir
sink(d); // $ ast,ir
sink(e); // $ ast MISSING: ir
sink(f); // $ ast,ir
@@ -553,12 +553,12 @@ void test_string_return_assign() {
std::string f("ff");
sink( a.assign(b.assign("bb")) );
- sink( c.assign(d.assign(source())) ); // $ ast MISSING: ir
- sink( e.assign("ee").assign(source()) ); // $ ast MISSING: ir
+ sink( c.assign(d.assign(source())) ); // $ ast,ir
+ sink( e.assign("ee").assign(source()) ); // $ ast,ir
sink( f.assign(source()).assign("ff") );
sink(a);
sink(b);
- sink(c); // $ ast MISSING: ir
+ sink(c); // $ ast,ir
sink(d); // $ ast,ir
sink(e); // $ ast MISSING: ir
sink(f); // $ SPURIOUS: ast,ir
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/stringstream.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/stringstream.cpp
index fab96fc878d..249c8ac21bf 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/stringstream.cpp
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/stringstream.cpp
@@ -53,15 +53,15 @@ void test_stringstream_string(int amount)
sink(ss7); // $ SPURIOUS: ast,ir
sink(ss8.put('a'));
- sink(ss9.put(ns_char::source())); // $ ast MISSING: ir
- sink(ss10.put('a').put(ns_char::source()).put('z')); // $ ast MISSING: ir
+ sink(ss9.put(ns_char::source())); // $ ast,ir
+ sink(ss10.put('a').put(ns_char::source()).put('z')); // $ ast,ir
sink(ss8);
sink(ss9); // $ ast,ir
sink(ss10); // $ ast MISSING: ir
sink(ss11.write("begin", 5));
- sink(ss12.write(source(), 5)); // $ ast MISSING: ir
- sink(ss13.write("begin", 5).write(source(), amount).write("end", 3)); // $ ast MISSING: ir
+ sink(ss12.write(source(), 5)); // $ ast,ir
+ sink(ss13.write("begin", 5).write(source(), amount).write("end", 3)); // $ ast,ir
sink(ss11);
sink(ss12); // $ ast,ir
sink(ss13); // $ ast MISSING: ir
@@ -73,7 +73,7 @@ void test_stringstream_int(int source)
int v1 = 0, v2 = 0;
sink(ss1 << 1234);
- sink(ss2 << source); // $ ast MISSING: ir
+ sink(ss2 << source); // $ ast,ir
sink(ss1 >> v1);
sink(ss2 >> v2); // $ ast,ir
@@ -97,7 +97,7 @@ void test_stringstream_constructors()
std::stringstream ss6;
sink(ss5 = std::stringstream("abc"));
- sink(ss6 = std::stringstream(source())); // $ ast MISSING: ir
+ sink(ss6 = std::stringstream(source())); // $ ast,ir
sink(ss1);
sink(ss2); // $ ast,ir
@@ -193,7 +193,7 @@ void test_stringstream_putback()
sink(ss.get());
sink(ss.putback('b'));
sink(ss.get());
- sink(ss.putback(ns_char::source())); // $ ast MISSING: ir
+ sink(ss.putback(ns_char::source())); // $ ast,ir
sink(ss.get()); // $ ast,ir
}
@@ -263,6 +263,6 @@ void test_chaining()
sink(b1); // $ ast,ir
sink(b2); // $ ast,ir
- sink(ss2.write("abc", 3).flush().write(source(), 3).flush().write("xyz", 3)); // $ ast MISSING: ir
+ sink(ss2.write("abc", 3).flush().write(source(), 3).flush().write("xyz", 3)); // $ ast,ir
sink(ss2); // $ ast MISSING: ir
}
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 9434dbe52ae..92a44f2950c 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp
@@ -466,7 +466,7 @@ void test_qualifiers()
sink(d.getString());
d.setString(strings::source());
sink(d); // $ ast,ir
- sink(d.getString()); // $ ast MISSING: ir
+ sink(d.getString()); // $ ast,ir
}
// --- non-standard swap ---
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp
index 4f0c8fab414..aacef1f4a5b 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp
@@ -68,8 +68,8 @@ void test_element_taint(int x) {
v5.push_back(source());
sink(v5); // $ ast,ir
- sink(v5.front()); // $ SPURIOUS: ast
- sink(v5.back()); // $ ast MISSING: ir
+ sink(v5.front()); // $ SPURIOUS: ast,ir
+ sink(v5.back()); // $ ast,ir
v6.data()[2] = source();
sink(v6); // $ ast MISSING: ir
@@ -81,8 +81,8 @@ void test_element_taint(int x) {
v7.insert(it, source());
}
sink(v7); // $ ast,ir
- sink(v7.front()); // $ ast MISSING: ir
- sink(v7.back()); // $ SPURIOUS: ast
+ sink(v7.front()); // $ ast,ir
+ sink(v7.back()); // $ SPURIOUS: ast,ir
{
const std::vector &v8c = v8;
@@ -283,8 +283,8 @@ void test_data_more() {
v1.push_back(source());
sink(v1); // $ ast,ir
- sink(v1.data()); // $ ast MISSING: ir
- sink(v1.data()[2]); // $ ast MISSING: ir
+ sink(v1.data()); // $ ast,ir
+ sink(v1.data()[2]); // $ ast,ir
*(v2.data()) = ns_int::source();
sink(v2); // $ ast MISSING: ir
@@ -305,10 +305,10 @@ void test_vector_insert() {
sink(a.insert(a.end(), b.begin(), b.end()));
sink(a);
- sink(c.insert(c.end(), d.begin(), d.end())); // $ ast MISSING: ir
+ sink(c.insert(c.end(), d.begin(), d.end())); // $ ast,ir
sink(c); // $ ast,ir
- sink(d.insert(d.end(), a.begin(), a.end())); // $ ast MISSING: ir
+ sink(d.insert(d.end(), a.begin(), a.end())); // $ ast,ir
sink(d); // $ ast,ir
}
diff --git a/cpp/ql/test/library-tests/rangeanalysis/SimpleRangeAnalysis/lowerBound.expected b/cpp/ql/test/library-tests/rangeanalysis/SimpleRangeAnalysis/lowerBound.expected
index 7478060a95b..112b6cb0201 100644
--- a/cpp/ql/test/library-tests/rangeanalysis/SimpleRangeAnalysis/lowerBound.expected
+++ b/cpp/ql/test/library-tests/rangeanalysis/SimpleRangeAnalysis/lowerBound.expected
@@ -594,6 +594,15 @@
| test.c:659:9:659:9 | u | 0 |
| test.c:664:12:664:12 | s | -2147483648 |
| test.c:665:7:665:8 | s2 | -4 |
+| test.c:670:7:670:7 | x | -2147483648 |
+| test.c:671:9:671:9 | y | -2147483648 |
+| test.c:675:7:675:7 | y | -2147483648 |
+| test.c:684:7:684:7 | x | -2147483648 |
+| test.c:689:7:689:7 | x | -2147483648 |
+| test.c:696:8:696:8 | x | 2147483647 |
+| test.c:696:12:696:12 | y | 256 |
+| test.c:697:9:697:9 | x | 2147483647 |
+| test.c:698:9:698:9 | y | 256 |
| test.cpp:10:7:10:7 | b | -2147483648 |
| test.cpp:11:5:11:5 | x | -2147483648 |
| test.cpp:13:10:13:10 | x | -2147483648 |
@@ -647,16 +656,18 @@
| test.cpp:97:10:97:10 | i | -2147483648 |
| test.cpp:97:22:97:22 | i | -2147483648 |
| test.cpp:98:5:98:5 | i | -2147483648 |
-| test.cpp:105:7:105:7 | n | -32768 |
-| test.cpp:108:7:108:7 | n | 0 |
-| test.cpp:109:5:109:5 | n | 1 |
-| test.cpp:111:5:111:5 | n | 0 |
-| test.cpp:114:8:114:8 | n | 0 |
-| test.cpp:115:5:115:5 | n | 0 |
-| test.cpp:117:5:117:5 | n | 1 |
-| test.cpp:120:3:120:3 | n | 0 |
-| test.cpp:120:8:120:8 | n | 1 |
-| test.cpp:120:12:120:12 | n | 0 |
-| test.cpp:121:4:121:4 | n | 0 |
-| test.cpp:121:8:121:8 | n | 0 |
-| test.cpp:121:12:121:12 | n | 1 |
+| test.cpp:98:9:98:9 | i | -2147483648 |
+| test.cpp:99:5:99:5 | i | -2147483648 |
+| test.cpp:106:7:106:7 | n | -32768 |
+| test.cpp:109:7:109:7 | n | 0 |
+| test.cpp:110:5:110:5 | n | 1 |
+| test.cpp:112:5:112:5 | n | 0 |
+| test.cpp:115:8:115:8 | n | 0 |
+| test.cpp:116:5:116:5 | n | 0 |
+| test.cpp:118:5:118:5 | n | 1 |
+| test.cpp:121:3:121:3 | n | 0 |
+| test.cpp:121:8:121:8 | n | 1 |
+| test.cpp:121:12:121:12 | n | 0 |
+| test.cpp:122:4:122:4 | n | 0 |
+| test.cpp:122:8:122:8 | n | 0 |
+| test.cpp:122:12:122:12 | n | 1 |
diff --git a/cpp/ql/test/library-tests/rangeanalysis/SimpleRangeAnalysis/ternaryLower.expected b/cpp/ql/test/library-tests/rangeanalysis/SimpleRangeAnalysis/ternaryLower.expected
index fedd3853ce2..f012490f115 100644
--- a/cpp/ql/test/library-tests/rangeanalysis/SimpleRangeAnalysis/ternaryLower.expected
+++ b/cpp/ql/test/library-tests/rangeanalysis/SimpleRangeAnalysis/ternaryLower.expected
@@ -15,5 +15,5 @@
| test.c:394:20:394:36 | ... ? ... : ... | 0.0 | 0.0 | 100.0 |
| test.c:606:5:606:14 | ... ? ... : ... | 0.0 | 1.0 | 0.0 |
| test.c:607:5:607:14 | ... ? ... : ... | 0.0 | 0.0 | 1.0 |
-| test.cpp:120:3:120:12 | ... ? ... : ... | 0.0 | 1.0 | 0.0 |
-| test.cpp:121:3:121:12 | ... ? ... : ... | 0.0 | 0.0 | 1.0 |
+| test.cpp:121:3:121:12 | ... ? ... : ... | 0.0 | 1.0 | 0.0 |
+| test.cpp:122:3:122:12 | ... ? ... : ... | 0.0 | 0.0 | 1.0 |
diff --git a/cpp/ql/test/library-tests/rangeanalysis/SimpleRangeAnalysis/ternaryUpper.expected b/cpp/ql/test/library-tests/rangeanalysis/SimpleRangeAnalysis/ternaryUpper.expected
index 0b8fe8c1164..8a387c3ae46 100644
--- a/cpp/ql/test/library-tests/rangeanalysis/SimpleRangeAnalysis/ternaryUpper.expected
+++ b/cpp/ql/test/library-tests/rangeanalysis/SimpleRangeAnalysis/ternaryUpper.expected
@@ -15,5 +15,5 @@
| test.c:394:20:394:36 | ... ? ... : ... | 100.0 | 99.0 | 100.0 |
| test.c:606:5:606:14 | ... ? ... : ... | 32767.0 | 32767.0 | 0.0 |
| test.c:607:5:607:14 | ... ? ... : ... | 32767.0 | 0.0 | 32767.0 |
-| test.cpp:120:3:120:12 | ... ? ... : ... | 32767.0 | 32767.0 | 0.0 |
-| test.cpp:121:3:121:12 | ... ? ... : ... | 32767.0 | 0.0 | 32767.0 |
+| test.cpp:121:3:121:12 | ... ? ... : ... | 32767.0 | 32767.0 | 0.0 |
+| test.cpp:122:3:122:12 | ... ? ... : ... | 32767.0 | 0.0 | 32767.0 |
diff --git a/cpp/ql/test/library-tests/rangeanalysis/SimpleRangeAnalysis/test.c b/cpp/ql/test/library-tests/rangeanalysis/SimpleRangeAnalysis/test.c
index 40168c3e697..8c7978ac4aa 100644
--- a/cpp/ql/test/library-tests/rangeanalysis/SimpleRangeAnalysis/test.c
+++ b/cpp/ql/test/library-tests/rangeanalysis/SimpleRangeAnalysis/test.c
@@ -664,3 +664,37 @@ void test_mod(int s) {
int s2 = s % 5;
out(s2); // -4 .. 4
}
+
+void exit(int);
+void guard_with_exit(int x, int y) {
+ if (x) {
+ if (y != 0) {
+ exit(0);
+ }
+ }
+ out(y); // ..
+
+ // This test ensures that we correctly identify
+ // that the upper bound for y is max_int when calling `out(y)`.
+ // The RangeSsa will place guardPhy on `out(y)`, and consequently there is no
+ // frontier phi node at out(y).
+}
+
+void test(int x) {
+ if (x >= 10) {
+ return;
+ }
+ // The basic below has two predecessors.
+label:
+ out(x);
+ goto label;
+}
+
+void test_overflow() {
+ const int x = 2147483647; // 2^31-1
+ const int y = 256;
+ if ((x + y) <= 512) {
+ out(x);
+ out(y);
+ }
+}
diff --git a/cpp/ql/test/library-tests/rangeanalysis/SimpleRangeAnalysis/test.cpp b/cpp/ql/test/library-tests/rangeanalysis/SimpleRangeAnalysis/test.cpp
index 515633f4f73..e5eb9554966 100644
--- a/cpp/ql/test/library-tests/rangeanalysis/SimpleRangeAnalysis/test.cpp
+++ b/cpp/ql/test/library-tests/rangeanalysis/SimpleRangeAnalysis/test.cpp
@@ -95,6 +95,7 @@ int ref_to_number(int &i, const int &ci, int &aliased) {
return alias;
for (; i <= 12345; i++) { // test that widening works for references
+ i = i;
i;
}
diff --git a/cpp/ql/test/library-tests/rangeanalysis/SimpleRangeAnalysis/upperBound.expected b/cpp/ql/test/library-tests/rangeanalysis/SimpleRangeAnalysis/upperBound.expected
index ce6bed728eb..8772a763a4d 100644
--- a/cpp/ql/test/library-tests/rangeanalysis/SimpleRangeAnalysis/upperBound.expected
+++ b/cpp/ql/test/library-tests/rangeanalysis/SimpleRangeAnalysis/upperBound.expected
@@ -584,9 +584,9 @@
| test.c:639:9:639:10 | ss | 2 |
| test.c:645:8:645:8 | s | 2147483647 |
| test.c:645:15:645:15 | s | 127 |
-| test.c:645:23:645:23 | s | 15 |
-| test.c:646:18:646:18 | s | 15 |
-| test.c:646:22:646:22 | s | 15 |
+| test.c:645:23:645:23 | s | 9 |
+| test.c:646:18:646:18 | s | 9 |
+| test.c:646:22:646:22 | s | 9 |
| test.c:647:9:647:14 | result | 127 |
| test.c:653:7:653:7 | i | 0 |
| test.c:654:9:654:9 | i | 2147483647 |
@@ -594,6 +594,15 @@
| test.c:659:9:659:9 | u | 4294967295 |
| test.c:664:12:664:12 | s | 2147483647 |
| test.c:665:7:665:8 | s2 | 4 |
+| test.c:670:7:670:7 | x | 2147483647 |
+| test.c:671:9:671:9 | y | 2147483647 |
+| test.c:675:7:675:7 | y | 2147483647 |
+| test.c:684:7:684:7 | x | 2147483647 |
+| test.c:689:7:689:7 | x | 15 |
+| test.c:696:8:696:8 | x | 2147483647 |
+| test.c:696:12:696:12 | y | 256 |
+| test.c:697:9:697:9 | x | 2147483647 |
+| test.c:698:9:698:9 | y | 256 |
| test.cpp:10:7:10:7 | b | 2147483647 |
| test.cpp:11:5:11:5 | x | 2147483647 |
| test.cpp:13:10:13:10 | x | 2147483647 |
@@ -646,17 +655,19 @@
| test.cpp:95:12:95:16 | alias | 2147483647 |
| test.cpp:97:10:97:10 | i | 65535 |
| test.cpp:97:22:97:22 | i | 32767 |
-| test.cpp:98:5:98:5 | i | 32767 |
-| test.cpp:105:7:105:7 | n | 32767 |
-| test.cpp:108:7:108:7 | n | 32767 |
-| test.cpp:109:5:109:5 | n | 32767 |
-| test.cpp:111:5:111:5 | n | 0 |
-| test.cpp:114:8:114:8 | n | 32767 |
-| test.cpp:115:5:115:5 | n | 0 |
-| test.cpp:117:5:117:5 | n | 32767 |
-| test.cpp:120:3:120:3 | n | 32767 |
-| test.cpp:120:8:120:8 | n | 32767 |
-| test.cpp:120:12:120:12 | n | 0 |
-| test.cpp:121:4:121:4 | n | 32767 |
-| test.cpp:121:8:121:8 | n | 0 |
-| test.cpp:121:12:121:12 | n | 32767 |
+| test.cpp:98:5:98:5 | i | 2147483647 |
+| test.cpp:98:9:98:9 | i | 12345 |
+| test.cpp:99:5:99:5 | i | 32767 |
+| test.cpp:106:7:106:7 | n | 32767 |
+| test.cpp:109:7:109:7 | n | 32767 |
+| test.cpp:110:5:110:5 | n | 32767 |
+| test.cpp:112:5:112:5 | n | 0 |
+| test.cpp:115:8:115:8 | n | 32767 |
+| test.cpp:116:5:116:5 | n | 0 |
+| test.cpp:118:5:118:5 | n | 32767 |
+| test.cpp:121:3:121:3 | n | 32767 |
+| test.cpp:121:8:121:8 | n | 32767 |
+| test.cpp:121:12:121:12 | n | 0 |
+| test.cpp:122:4:122:4 | n | 32767 |
+| test.cpp:122:8:122:8 | n | 0 |
+| test.cpp:122:12:122:12 | n | 32767 |
diff --git a/cpp/ql/test/query-tests/Critical/OverflowStatic/OverflowStatic.expected b/cpp/ql/test/query-tests/Critical/OverflowStatic/OverflowStatic.expected
index 01a2dfc38b3..0e5bbee7d73 100644
--- a/cpp/ql/test/query-tests/Critical/OverflowStatic/OverflowStatic.expected
+++ b/cpp/ql/test/query-tests/Critical/OverflowStatic/OverflowStatic.expected
@@ -5,10 +5,10 @@
| test2.c:33:26:33:27 | 46 | Potential buffer-overflow: 'buffer' has size 40 not 46. |
| test2.c:34:22:34:23 | 47 | Potential buffer-overflow: 'buffer' has size 40 not 47. |
| test2.c:35:23:35:24 | 48 | Potential buffer-overflow: 'buffer' has size 40 not 48. |
-| test.c:14:9:14:13 | access to array | Potential buffer-overflow: 'xs' has size 5 but 'xs[5]' is accessed here. |
-| test.c:15:9:15:13 | access to array | Potential buffer-overflow: 'xs' has size 5 but 'xs[6]' is accessed here. |
-| test.c:20:9:20:18 | access to array | Potential buffer-overflow: 'ys' has size 5 but 'ys[5]' is accessed here. |
-| test.c:21:9:21:18 | access to array | Potential buffer-overflow: 'ys' has size 5 but 'ys[6]' is accessed here. |
+| test.c:14:9:14:13 | access to array | Potential buffer-overflow: 'xs' has size 5 but 'xs[5]' may be accessed here. |
+| test.c:15:9:15:13 | access to array | Potential buffer-overflow: 'xs' has size 5 but 'xs[6]' may be accessed here. |
+| test.c:20:9:20:18 | access to array | Potential buffer-overflow: 'ys' has size 5 but 'ys[5]' may be accessed here. |
+| test.c:21:9:21:18 | access to array | Potential buffer-overflow: 'ys' has size 5 but 'ys[6]' may be accessed here. |
| test.cpp:19:3:19:12 | access to array | Potential buffer-overflow: counter 'i' <= 3 but 'buffer1' has 3 elements. |
| test.cpp:20:3:20:12 | access to array | Potential buffer-overflow: counter 'i' <= 3 but 'buffer2' has 3 elements. |
| test.cpp:24:27:24:27 | 4 | Potential buffer-overflow: 'buffer1' has size 3 not 4. |
diff --git a/cpp/ql/test/query-tests/Critical/OverflowStatic/test.c b/cpp/ql/test/query-tests/Critical/OverflowStatic/test.c
index baa8999412c..3c726a452b9 100644
--- a/cpp/ql/test/query-tests/Critical/OverflowStatic/test.c
+++ b/cpp/ql/test/query-tests/Critical/OverflowStatic/test.c
@@ -27,3 +27,47 @@ void f(void) {
c = stru.zs[6]; // GOOD (zs is variable size)
}
+void* malloc(long unsigned int);
+void test_buffer_sentinal() {
+ struct { char len; char buf[1]; } *b = malloc(10); // len(buf.buffer) effectively 8
+ b->buf[0] = 0; // GOOD
+ b->buf[7] = 0; // GOOD
+ b->buf[8] = 0; // BAD [NOT DETECTED]
+}
+
+union u {
+ unsigned long value;
+ char ptr[1];
+};
+
+void union_test() {
+ union u u;
+ u.ptr[0] = 0; // GOOD
+ u.ptr[sizeof(u)-1] = 0; // GOOD
+ u.ptr[sizeof(u)] = 0; // BAD [NOT DETECTED]
+}
+
+void test_struct_union() {
+ struct { union u u; } v;
+ v.u.ptr[0] = 0; // GOOD
+ v.u.ptr[sizeof(union u)-1] = 0; // GOOD
+ v.u.ptr[sizeof(union u)] = 0; // BAD [NOT DETECTED]
+}
+
+void union_test2() {
+ union { char ptr[1]; unsigned long value; } u;
+ u.ptr[0] = 0; // GOOD
+ u.ptr[sizeof(u)-1] = 0; // GOOD
+ u.ptr[sizeof(u)] = 0; // BAD [NOT DETECTED]
+}
+
+typedef struct {
+ char len;
+ char buf[1];
+} var_buf;
+
+void test_alloc() {
+ // Special case of taking sizeof without any addition or multiplications
+ var_buf *b = malloc(sizeof(var_buf));
+ b->buf[1] = 0; // BAD [NOT DETECTED]
+}
diff --git a/cpp/ql/test/query-tests/Critical/OverflowStatic/test.cpp b/cpp/ql/test/query-tests/Critical/OverflowStatic/test.cpp
index be9e14bd841..01a4a0adaef 100644
--- a/cpp/ql/test/query-tests/Critical/OverflowStatic/test.cpp
+++ b/cpp/ql/test/query-tests/Critical/OverflowStatic/test.cpp
@@ -46,3 +46,13 @@ void f2(char *src)
ptr = &(buffer[1]);
memcpy(ptr, src, 100); // BAD [NOT DETECTED]
}
+
+void f3() {
+ int i;
+ char buffer[5];
+ for (i=0; i<10; i++) {
+ if (i < 5) {
+ buffer[i] = 0; // GOOD
+ }
+ }
+}
diff --git a/cpp/ql/test/query-tests/Likely Bugs/Arithmetic/PointlessComparison/PointlessComparison.c b/cpp/ql/test/query-tests/Likely Bugs/Arithmetic/PointlessComparison/PointlessComparison.c
index 9fc257e3eeb..fd1bc655051 100644
--- a/cpp/ql/test/query-tests/Likely Bugs/Arithmetic/PointlessComparison/PointlessComparison.c
+++ b/cpp/ql/test/query-tests/Likely Bugs/Arithmetic/PointlessComparison/PointlessComparison.c
@@ -115,7 +115,7 @@ int twoReasons(int a, int b) {
if (a <= 0 && b > 5) {
return a < b;
}
- if (a <= 100 && b > 105) {
+ if (a <= 100 && b > 105) { // BUG [Not detected - this clause is always false]
return a > b;
}
return 0;
diff --git a/cpp/ql/test/query-tests/Likely Bugs/Conversion/CastArrayPointerArithmetic/CastArrayPointerArithmetic.expected b/cpp/ql/test/query-tests/Likely Bugs/Conversion/CastArrayPointerArithmetic/CastArrayPointerArithmetic.expected
index db84b3b0911..794a7fba821 100644
--- a/cpp/ql/test/query-tests/Likely Bugs/Conversion/CastArrayPointerArithmetic/CastArrayPointerArithmetic.expected
+++ b/cpp/ql/test/query-tests/Likely Bugs/Conversion/CastArrayPointerArithmetic/CastArrayPointerArithmetic.expected
@@ -34,6 +34,7 @@ nodes
| test.cpp:88:21:88:22 | d2 | semmle.label | d2 |
| test.cpp:95:21:95:21 | d | semmle.label | d |
| test.cpp:96:21:96:23 | dss | semmle.label | dss |
+subpaths
#select
| test.cpp:27:2:27:2 | b | test.cpp:57:19:57:19 | d | test.cpp:27:2:27:2 | b | Pointer arithmetic here may be done with the wrong type because of the cast $@. | test.cpp:57:19:57:19 | d | here |
| test.cpp:27:2:27:2 | b | test.cpp:74:19:74:21 | dss | test.cpp:27:2:27:2 | b | Pointer arithmetic here may be done with the wrong type because of the cast $@. | test.cpp:74:19:74:21 | dss | here |
diff --git a/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ImproperNullTermination/ImproperNullTermination.expected b/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ImproperNullTermination/ImproperNullTermination.expected
index b8f5c71e8e2..c83205ef49f 100644
--- a/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ImproperNullTermination/ImproperNullTermination.expected
+++ b/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ImproperNullTermination/ImproperNullTermination.expected
@@ -23,3 +23,5 @@
| test.cpp:365:19:365:25 | buffer2 | Variable $@ may not be null terminated. | test.cpp:363:8:363:14 | buffer2 | buffer2 |
| test.cpp:392:17:392:22 | buffer | Variable $@ may not be null terminated. | test.cpp:390:8:390:13 | buffer | buffer |
| test.cpp:398:18:398:23 | buffer | Variable $@ may not be null terminated. | test.cpp:396:8:396:13 | buffer | buffer |
+| test.cpp:444:10:444:15 | buffer | Variable $@ may not be null terminated. | test.cpp:442:8:442:13 | buffer | buffer |
+| test.cpp:450:16:450:21 | buffer | Variable $@ may not be null terminated. | test.cpp:448:8:448:13 | buffer | buffer |
diff --git a/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ImproperNullTermination/test.cpp b/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ImproperNullTermination/test.cpp
index 7fa4ef60093..45410c15ebf 100644
--- a/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ImproperNullTermination/test.cpp
+++ b/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ImproperNullTermination/test.cpp
@@ -433,3 +433,36 @@ void test_read_fread(int read_src, FILE *s)
strlen(buffer); // GOOD
}
}
+
+int printf(const char *format, ...);
+
+void test_printf(char *str)
+{
+ {
+ char buffer[1024];
+
+ printf(buffer, ""); // BAD
+ }
+
+ {
+ char buffer[1024];
+
+ printf("%s", buffer); // BAD
+ }
+
+ {
+ size_t len = strlen(str);
+ char *copied_str = (char *)malloc(len);
+
+ memcpy(copied_str, str, len);
+ printf("%s", copied_str); // BAD [NOT DETECTED]
+ }
+
+ {
+ size_t len = strlen(str);
+ char *copied_str = (char *)malloc(len + 1);
+
+ memcpy(copied_str, str, len + 1);
+ printf("%s", copied_str); // GOOD
+ }
+}
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-022/SAMATE/TaintedPath/CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-022/SAMATE/TaintedPath/CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp
new file mode 100644
index 00000000000..876584c5117
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-022/SAMATE/TaintedPath/CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp
@@ -0,0 +1,134 @@
+/**
+ * This test case is closely based on CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp
+ * from the SAMATE Juliet test suite.
+ */
+
+#define NULL (0)
+
+typedef unsigned long size_t;
+typedef size_t time_t;
+time_t time(time_t *timer);
+
+typedef struct {} FILE;
+extern FILE *stdin;
+FILE *fopen(const char *filename, const char *mode);
+int fclose(FILE *stream);
+#define FILENAME_MAX (4096)
+
+typedef unsigned long size_t;
+size_t strlen(const char *s);
+char *strcat(char *s1, const char *s2);
+char *fgets(char *s, int n, FILE *stream);
+
+int globalReturnsTrue()
+{
+ return 1;
+}
+
+int globalReturnsFalse()
+{
+ return 0;
+}
+
+void printLine(const char *str);
+
+#define BASEPATH "c:\\temp\\"
+#define FOPEN fopen
+
+namespace CWE23_Relative_Path_Traversal__char_console_fopen_11
+{
+
+void bad()
+{
+ char * data;
+ char dataBuffer[FILENAME_MAX] = BASEPATH;
+ data = dataBuffer;
+ if(globalReturnsTrue())
+ {
+ {
+ /* Read input from the console */
+ size_t dataLen = strlen(data);
+ /* if there is room in data, read into it from the console */
+ if (FILENAME_MAX-dataLen > 1)
+ {
+ /* POTENTIAL FLAW: Read data from the console */
+ if (fgets(data+dataLen, (int)(FILENAME_MAX-dataLen), stdin) != NULL)
+ {
+ /* The next few lines remove the carriage return from the string that is
+ * inserted by fgets() */
+ dataLen = strlen(data);
+ if (dataLen > 0 && data[dataLen-1] == '\n')
+ {
+ data[dataLen-1] = '\0';
+ }
+ }
+ else
+ {
+ printLine("fgets() failed");
+ /* Restore NUL terminator if fgets fails */
+ data[dataLen] = '\0';
+ }
+ }
+ }
+ }
+ {
+ FILE *pFile = NULL;
+ /* POTENTIAL FLAW: Possibly opening a file without validating the file name or path */
+ pFile = FOPEN(data, "wb+");
+ if (pFile != NULL)
+ {
+ fclose(pFile);
+ }
+ }
+}
+
+/* goodG2B1() - use goodsource and badsink by changing the globalReturnsTrue() to globalReturnsFalse() */
+static void goodG2B1()
+{
+ char * data;
+ char dataBuffer[FILENAME_MAX] = BASEPATH;
+ data = dataBuffer;
+ if(globalReturnsFalse())
+ {
+ /* INCIDENTAL: CWE 561 Dead Code, the code below will never run */
+ printLine("Benign, fixed string");
+ }
+ else
+ {
+ /* FIX: Use a fixed file name */
+ strcat(data, "file.txt");
+ }
+ {
+ FILE *pFile = NULL;
+ /* POTENTIAL FLAW: Possibly opening a file without validating the file name or path */
+ pFile = FOPEN(data, "wb+");
+ if (pFile != NULL)
+ {
+ fclose(pFile);
+ }
+ }
+}
+
+/* goodG2B2() - use goodsource and badsink by reversing the blocks in the if statement */
+static void goodG2B2()
+{
+ char * data;
+ char dataBuffer[FILENAME_MAX] = BASEPATH;
+ data = dataBuffer;
+ if(globalReturnsTrue())
+ {
+ /* FIX: Use a fixed file name */
+ strcat(data, "file.txt");
+ }
+ {
+ FILE *pFile = NULL;
+ /* POTENTIAL FLAW: Possibly opening a file without validating the file name or path */
+ pFile = FOPEN(data, "wb+");
+ if (pFile != NULL)
+ {
+ fclose(pFile);
+ }
+ }
+}
+
+}
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-022/SAMATE/TaintedPath/TaintedPath.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-022/SAMATE/TaintedPath/TaintedPath.expected
new file mode 100644
index 00000000000..6271123d9d1
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-022/SAMATE/TaintedPath/TaintedPath.expected
@@ -0,0 +1,18 @@
+edges
+| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:55:27:55:38 | ... + ... | CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | (const char *)... |
+| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:55:27:55:38 | ... + ... | CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | data |
+| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:55:27:55:38 | ... + ... | CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | data indirection |
+| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:55:27:55:38 | fgets output argument | CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | (const char *)... |
+| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:55:27:55:38 | fgets output argument | CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | data |
+| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:55:27:55:38 | fgets output argument | CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | data indirection |
+subpaths
+nodes
+| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:55:27:55:38 | ... + ... | semmle.label | ... + ... |
+| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:55:27:55:38 | fgets output argument | semmle.label | fgets output argument |
+| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | (const char *)... | semmle.label | (const char *)... |
+| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | (const char *)... | semmle.label | (const char *)... |
+| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | data | semmle.label | data |
+| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | data indirection | semmle.label | data indirection |
+| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | data indirection | semmle.label | data indirection |
+#select
+| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | data | CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:55:27:55:38 | ... + ... | CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | data | This argument to a file access function is derived from $@ and then passed to fopen(filename) | CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:55:27:55:38 | ... + ... | user input (fgets) |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-022/SAMATE/TaintedPath/TaintedPath.qlref b/cpp/ql/test/query-tests/Security/CWE/CWE-022/SAMATE/TaintedPath/TaintedPath.qlref
new file mode 100644
index 00000000000..1677939387d
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-022/SAMATE/TaintedPath/TaintedPath.qlref
@@ -0,0 +1 @@
+Security/CWE/CWE-022/TaintedPath.ql
\ No newline at end of file
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-022/semmle/tests/TaintedPath.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-022/semmle/tests/TaintedPath.expected
index 7b513c574ad..cb8dfd321b3 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-022/semmle/tests/TaintedPath.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-022/semmle/tests/TaintedPath.expected
@@ -5,6 +5,7 @@ edges
| test.c:9:23:9:26 | argv | test.c:17:11:17:18 | fileName |
| test.c:9:23:9:26 | argv | test.c:17:11:17:18 | fileName indirection |
| test.c:9:23:9:26 | argv | test.c:17:11:17:18 | fileName indirection |
+subpaths
nodes
| test.c:9:23:9:26 | argv | semmle.label | argv |
| test.c:9:23:9:26 | argv | semmle.label | argv |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-078/SAMATE/ExecTainted/ExecTainted.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-078/SAMATE/ExecTainted/ExecTainted.expected
new file mode 100644
index 00000000000..51c8906abb5
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-078/SAMATE/ExecTainted/ExecTainted.expected
@@ -0,0 +1,17 @@
+edges
+| tests.cpp:33:34:33:39 | call to getenv | tests.cpp:38:39:38:49 | environment indirection |
+| tests.cpp:38:25:38:36 | strncat output argument | tests.cpp:42:5:42:16 | Phi |
+| tests.cpp:38:39:38:49 | environment indirection | tests.cpp:38:25:38:36 | strncat output argument |
+| tests.cpp:38:39:38:49 | environment indirection | tests.cpp:38:25:38:36 | strncat output argument |
+| tests.cpp:42:5:42:16 | Phi | tests.cpp:51:22:51:25 | badSource output argument |
+| tests.cpp:51:22:51:25 | badSource output argument | tests.cpp:53:16:53:19 | data indirection |
+nodes
+| tests.cpp:33:34:33:39 | call to getenv | semmle.label | call to getenv |
+| tests.cpp:38:25:38:36 | strncat output argument | semmle.label | strncat output argument |
+| tests.cpp:38:39:38:49 | environment indirection | semmle.label | environment indirection |
+| tests.cpp:42:5:42:16 | Phi | semmle.label | Phi |
+| tests.cpp:51:22:51:25 | badSource output argument | semmle.label | badSource output argument |
+| tests.cpp:53:16:53:19 | data indirection | semmle.label | data indirection |
+subpaths
+#select
+| tests.cpp:53:16:53:19 | data | tests.cpp:33:34:33:39 | call to getenv | tests.cpp:53:16:53:19 | data indirection | This argument to an OS command is derived from $@, dangerously concatenated into $@, and then passed to system(string) | tests.cpp:33:34:33:39 | call to getenv | user input (an environment variable) | tests.cpp:38:25:38:36 | strncat output argument | strncat output argument |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-078/SAMATE/ExecTainted/ExecTainted.qlref b/cpp/ql/test/query-tests/Security/CWE/CWE-078/SAMATE/ExecTainted/ExecTainted.qlref
new file mode 100644
index 00000000000..9fe2347270f
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-078/SAMATE/ExecTainted/ExecTainted.qlref
@@ -0,0 +1 @@
+Security/CWE/CWE-078/ExecTainted.ql
\ No newline at end of file
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-078/SAMATE/ExecTainted/tests.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-078/SAMATE/ExecTainted/tests.cpp
new file mode 100644
index 00000000000..80f8221d903
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-078/SAMATE/ExecTainted/tests.cpp
@@ -0,0 +1,58 @@
+//semmle-extractor-options: --edg --target --edg win64
+
+// A selection of tests from the SAMATE Juliet framework for rule CWE-78.
+
+// library types, functions etc
+#define NULL (0)
+typedef unsigned long size_t;
+size_t strlen(const char *s);
+char *strncat(char *s1, const char *s2, size_t n);
+char *getenv(const char *name);
+int system(const char *string);
+void exit(int status);
+
+#define FULL_COMMAND "dir "
+#define ENV_VARIABLE "ADD"
+#define GETENV getenv
+#define SYSTEM system
+
+void printLine(const char *str);
+
+// ----------
+
+/* The static variable below is used to drive control flow in the source function */
+static int badStatic = 0;
+
+static char * badSource(char * data)
+{
+ if(badStatic)
+ {
+ {
+ /* Append input from an environment variable to data */
+ size_t dataLen = strlen(data);
+ char * environment = GETENV(ENV_VARIABLE);
+ /* If there is data in the environment variable */
+ if (environment != NULL)
+ {
+ /* POTENTIAL FLAW: Read data from an environment variable */
+ strncat(data+dataLen, environment, 100-dataLen-1);
+ }
+ }
+ }
+ return data;
+}
+
+void CWE78_OS_Command_Injection__char_environment_system_21_bad()
+{
+ char * data;
+ char data_buf[100] = FULL_COMMAND;
+ data = data_buf;
+ badStatic = 1; /* true */
+ data = badSource(data);
+ /* POTENTIAL FLAW: Execute command in data possibly leading to command injection [NOT DETECTED] */
+ if (SYSTEM(data) != 0)
+ {
+ printLine("command execution failed!");
+ exit(1);
+ }
+}
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-078/semmle/ExecTainted/ExecTainted.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-078/semmle/ExecTainted/ExecTainted.expected
index a78a7a99d90..4b1b5a61ace 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-078/semmle/ExecTainted/ExecTainted.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-078/semmle/ExecTainted/ExecTainted.expected
@@ -1 +1,85 @@
-| test.c:21:12:21:19 | command1 | This argument to an OS command is derived from $@ and then passed to system(string) | test.c:14:20:14:23 | argv | user input (argv) |
+edges
+| test.cpp:16:20:16:23 | argv | test.cpp:22:45:22:52 | userName indirection |
+| test.cpp:22:13:22:20 | sprintf output argument | test.cpp:23:12:23:19 | command1 indirection |
+| test.cpp:22:45:22:52 | userName indirection | test.cpp:22:13:22:20 | sprintf output argument |
+| test.cpp:22:45:22:52 | userName indirection | test.cpp:22:13:22:20 | sprintf output argument |
+| test.cpp:47:21:47:26 | call to getenv | test.cpp:50:35:50:43 | envCflags indirection |
+| test.cpp:50:11:50:17 | sprintf output argument | test.cpp:51:10:51:16 | command indirection |
+| test.cpp:50:35:50:43 | envCflags indirection | test.cpp:50:11:50:17 | sprintf output argument |
+| test.cpp:50:35:50:43 | envCflags indirection | test.cpp:50:11:50:17 | sprintf output argument |
+| test.cpp:62:9:62:16 | fread output argument | test.cpp:64:20:64:27 | filename indirection |
+| test.cpp:64:11:64:17 | strncat output argument | test.cpp:65:10:65:16 | command indirection |
+| test.cpp:64:20:64:27 | filename indirection | test.cpp:64:11:64:17 | strncat output argument |
+| test.cpp:64:20:64:27 | filename indirection | test.cpp:64:11:64:17 | strncat output argument |
+| test.cpp:82:9:82:16 | fread output argument | test.cpp:84:20:84:27 | filename indirection |
+| test.cpp:84:11:84:17 | strncat output argument | test.cpp:85:32:85:38 | command indirection |
+| test.cpp:84:20:84:27 | filename indirection | test.cpp:84:11:84:17 | strncat output argument |
+| test.cpp:84:20:84:27 | filename indirection | test.cpp:84:11:84:17 | strncat output argument |
+| test.cpp:91:9:91:16 | fread output argument | test.cpp:93:17:93:24 | filename indirection |
+| test.cpp:93:11:93:14 | strncat output argument | test.cpp:94:45:94:48 | path indirection |
+| test.cpp:93:17:93:24 | filename indirection | test.cpp:93:11:93:14 | strncat output argument |
+| test.cpp:93:17:93:24 | filename indirection | test.cpp:93:11:93:14 | strncat output argument |
+| test.cpp:106:20:106:25 | call to getenv | test.cpp:107:33:107:36 | path indirection |
+| test.cpp:107:31:107:31 | call to operator+ | test.cpp:108:18:108:22 | call to c_str indirection |
+| test.cpp:107:33:107:36 | path indirection | test.cpp:107:31:107:31 | call to operator+ |
+| test.cpp:107:33:107:36 | path indirection | test.cpp:107:31:107:31 | call to operator+ |
+| test.cpp:113:20:113:25 | call to getenv | test.cpp:114:19:114:22 | path indirection |
+| test.cpp:114:17:114:17 | Call | test.cpp:114:25:114:29 | call to c_str indirection |
+| test.cpp:114:19:114:22 | path indirection | test.cpp:114:17:114:17 | Call |
+| test.cpp:114:19:114:22 | path indirection | test.cpp:114:17:114:17 | Call |
+| test.cpp:119:20:119:25 | call to getenv | test.cpp:120:19:120:22 | path indirection |
+| test.cpp:120:17:120:17 | Call | test.cpp:120:10:120:30 | call to data indirection |
+| test.cpp:120:19:120:22 | path indirection | test.cpp:120:17:120:17 | Call |
+| test.cpp:120:19:120:22 | path indirection | test.cpp:120:17:120:17 | Call |
+| test.cpp:140:9:140:11 | fread output argument | test.cpp:142:31:142:33 | str indirection |
+| test.cpp:142:11:142:17 | sprintf output argument | test.cpp:143:10:143:16 | command indirection |
+| test.cpp:142:31:142:33 | str indirection | test.cpp:142:11:142:17 | sprintf output argument |
+| test.cpp:142:31:142:33 | str indirection | test.cpp:142:11:142:17 | sprintf output argument |
+nodes
+| test.cpp:16:20:16:23 | argv | semmle.label | argv |
+| test.cpp:22:13:22:20 | sprintf output argument | semmle.label | sprintf output argument |
+| test.cpp:22:45:22:52 | userName indirection | semmle.label | userName indirection |
+| test.cpp:23:12:23:19 | command1 indirection | semmle.label | command1 indirection |
+| test.cpp:47:21:47:26 | call to getenv | semmle.label | call to getenv |
+| test.cpp:50:11:50:17 | sprintf output argument | semmle.label | sprintf output argument |
+| test.cpp:50:35:50:43 | envCflags indirection | semmle.label | envCflags indirection |
+| test.cpp:51:10:51:16 | command indirection | semmle.label | command indirection |
+| test.cpp:62:9:62:16 | fread output argument | semmle.label | fread output argument |
+| test.cpp:64:11:64:17 | strncat output argument | semmle.label | strncat output argument |
+| test.cpp:64:20:64:27 | filename indirection | semmle.label | filename indirection |
+| test.cpp:65:10:65:16 | command indirection | semmle.label | command indirection |
+| test.cpp:82:9:82:16 | fread output argument | semmle.label | fread output argument |
+| test.cpp:84:11:84:17 | strncat output argument | semmle.label | strncat output argument |
+| test.cpp:84:20:84:27 | filename indirection | semmle.label | filename indirection |
+| test.cpp:85:32:85:38 | command indirection | semmle.label | command indirection |
+| test.cpp:91:9:91:16 | fread output argument | semmle.label | fread output argument |
+| test.cpp:93:11:93:14 | strncat output argument | semmle.label | strncat output argument |
+| test.cpp:93:17:93:24 | filename indirection | semmle.label | filename indirection |
+| test.cpp:94:45:94:48 | path indirection | semmle.label | path indirection |
+| test.cpp:106:20:106:25 | call to getenv | semmle.label | call to getenv |
+| test.cpp:107:31:107:31 | call to operator+ | semmle.label | call to operator+ |
+| test.cpp:107:33:107:36 | path indirection | semmle.label | path indirection |
+| test.cpp:108:18:108:22 | call to c_str indirection | semmle.label | call to c_str indirection |
+| test.cpp:113:20:113:25 | call to getenv | semmle.label | call to getenv |
+| test.cpp:114:17:114:17 | Call | semmle.label | Call |
+| test.cpp:114:19:114:22 | path indirection | semmle.label | path indirection |
+| test.cpp:114:25:114:29 | call to c_str indirection | semmle.label | call to c_str indirection |
+| test.cpp:119:20:119:25 | call to getenv | semmle.label | call to getenv |
+| test.cpp:120:10:120:30 | call to data indirection | semmle.label | call to data indirection |
+| test.cpp:120:17:120:17 | Call | semmle.label | Call |
+| test.cpp:120:19:120:22 | path indirection | semmle.label | path indirection |
+| test.cpp:140:9:140:11 | fread output argument | semmle.label | fread output argument |
+| test.cpp:142:11:142:17 | sprintf output argument | semmle.label | sprintf output argument |
+| test.cpp:142:31:142:33 | str indirection | semmle.label | str indirection |
+| test.cpp:143:10:143:16 | command indirection | semmle.label | command indirection |
+subpaths
+#select
+| test.cpp:23:12:23:19 | command1 | test.cpp:16:20:16:23 | argv | test.cpp:23:12:23:19 | command1 indirection | This argument to an OS command is derived from $@, dangerously concatenated into $@, and then passed to system(string) | test.cpp:16:20:16:23 | argv | user input (a command-line argument) | test.cpp:22:13:22:20 | sprintf output argument | sprintf output argument |
+| test.cpp:51:10:51:16 | command | test.cpp:47:21:47:26 | call to getenv | test.cpp:51:10:51:16 | command indirection | This argument to an OS command is derived from $@, dangerously concatenated into $@, and then passed to system(string) | test.cpp:47:21:47:26 | call to getenv | user input (an environment variable) | test.cpp:50:11:50:17 | sprintf output argument | sprintf output argument |
+| test.cpp:65:10:65:16 | command | test.cpp:62:9:62:16 | fread output argument | test.cpp:65:10:65:16 | command indirection | This argument to an OS command is derived from $@, dangerously concatenated into $@, and then passed to system(string) | test.cpp:62:9:62:16 | fread output argument | user input (String read by fread) | test.cpp:64:11:64:17 | strncat output argument | strncat output argument |
+| test.cpp:85:32:85:38 | command | test.cpp:82:9:82:16 | fread output argument | test.cpp:85:32:85:38 | command indirection | This argument to an OS command is derived from $@, dangerously concatenated into $@, and then passed to execl | test.cpp:82:9:82:16 | fread output argument | user input (String read by fread) | test.cpp:84:11:84:17 | strncat output argument | strncat output argument |
+| test.cpp:94:45:94:48 | path | test.cpp:91:9:91:16 | fread output argument | test.cpp:94:45:94:48 | path indirection | This argument to an OS command is derived from $@, dangerously concatenated into $@, and then passed to execl | test.cpp:91:9:91:16 | fread output argument | user input (String read by fread) | test.cpp:93:11:93:14 | strncat output argument | strncat output argument |
+| test.cpp:108:18:108:22 | call to c_str | test.cpp:106:20:106:25 | call to getenv | test.cpp:108:18:108:22 | call to c_str indirection | This argument to an OS command is derived from $@, dangerously concatenated into $@, and then passed to system(string) | test.cpp:106:20:106:25 | call to getenv | user input (an environment variable) | test.cpp:107:31:107:31 | call to operator+ | call to operator+ |
+| test.cpp:114:25:114:29 | call to c_str | test.cpp:113:20:113:25 | call to getenv | test.cpp:114:25:114:29 | call to c_str indirection | This argument to an OS command is derived from $@, dangerously concatenated into $@, and then passed to system(string) | test.cpp:113:20:113:25 | call to getenv | user input (an environment variable) | test.cpp:114:17:114:17 | Call | Call |
+| test.cpp:120:25:120:28 | call to data | test.cpp:119:20:119:25 | call to getenv | test.cpp:120:10:120:30 | call to data indirection | This argument to an OS command is derived from $@, dangerously concatenated into $@, and then passed to system(string) | test.cpp:119:20:119:25 | call to getenv | user input (an environment variable) | test.cpp:120:17:120:17 | Call | Call |
+| test.cpp:143:10:143:16 | command | test.cpp:140:9:140:11 | fread output argument | test.cpp:143:10:143:16 | command indirection | This argument to an OS command is derived from $@, dangerously concatenated into $@, and then passed to system(string) | test.cpp:140:9:140:11 | fread output argument | user input (String read by fread) | test.cpp:142:11:142:17 | sprintf output argument | sprintf output argument |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-078/semmle/ExecTainted/test.c b/cpp/ql/test/query-tests/Security/CWE/CWE-078/semmle/ExecTainted/test.c
deleted file mode 100644
index c3155787368..00000000000
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-078/semmle/ExecTainted/test.c
+++ /dev/null
@@ -1,33 +0,0 @@
-// Semmle test case for rule ExecTainted.ql (Uncontrolled data used in OS command)
-// Associated with CWE-078: OS Command Injection. http://cwe.mitre.org/data/definitions/78.html
-
-///// Library routines /////
-
-int sprintf(char *s, const char *format, ...);
-int system(const char *string);
-
-extern void encodeShellString(char *shellStr, int maxChars, const char* cStr);
-
-///// Test code /////
-
-int main(int argc, char** argv) {
- char *userName = argv[2];
-
- {
- // BAD: a string from the user is injected directly into
- // a command.
- char command1[1000] = {0};
- sprintf(command1, "userinfo -v \"%s\"", userName);
- system(command1);
- }
-
- {
- // GOOD: the user string is encoded by a library routine.
- char userNameQuoted[1000] = {0};
- encodeShellString(userNameQuoted, 1000, userName);
- char command2[1000] = {0};
- sprintf(command2, "userinfo -v %s", userNameQuoted);
- system(command2);
- }
-}
-
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-078/semmle/ExecTainted/test.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-078/semmle/ExecTainted/test.cpp
new file mode 100644
index 00000000000..8407ffb68da
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-078/semmle/ExecTainted/test.cpp
@@ -0,0 +1,174 @@
+// Semmle test case for rule ExecTainted.ql (Uncontrolled data used in OS command)
+// Associated with CWE-078: OS Command Injection. http://cwe.mitre.org/data/definitions/78.html
+
+///// Library routines /////
+
+int sprintf(char *s, const char *format, ...);
+int system(const char *string);
+
+char *getenv(char *var);
+
+extern void encodeShellString(char *shellStr, int maxChars, const char* cStr);
+#include "../../../../../../include/string.h"
+///// Test code /////
+
+int main(int argc, char** argv) {
+ char *userName = argv[2];
+
+ {
+ // BAD: a string from the user is injected directly into
+ // a command.
+ char command1[1000] = {0};
+ sprintf(command1, "userinfo -v \"%s\"", userName);
+ system(command1);
+ }
+
+ {
+ // GOOD: the user string is encoded by a library routine.
+ char userNameQuoted[1000] = {0};
+ encodeShellString(userNameQuoted, 1000, userName);
+ char command2[1000] = {0};
+ sprintf(command2, "userinfo -v %s", userNameQuoted);
+ system(command2);
+ }
+}
+
+void test2(char* arg2) {
+ // GOOD?: the user string is the *first* part of the command, like $CC in many environments
+ char *envCC = getenv("CC");
+
+ char command[1000];
+ sprintf("%s %s", envCC, arg2);
+ system(command);
+}
+
+void test3(char* arg1) {
+ // GOOD?: the user string is a `$CFLAGS` environment variable
+ char *envCflags = getenv("CFLAGS");
+
+ char command[1000];
+ sprintf(command, "%s %s", arg1, envCflags);
+ system(command);
+}
+
+typedef unsigned long size_t;
+typedef void FILE;
+size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
+char *strncat(char *s1, const char *s2, size_t n);
+
+void test4(FILE *f) {
+ // BAD: the user string is injected directly into a command
+ char command[1000] = "mv ", filename[1000];
+ fread(filename, 1, 1000, f);
+
+ strncat(command, filename, 1000);
+ system(command);
+}
+
+void test5(FILE *f) {
+ // GOOD?: the user string is the start of a command
+ char command[1000], filename[1000] = " test.txt";
+ fread(command, 1, 1000, f);
+
+ strncat(command, filename, 1000);
+ system(command);
+}
+
+int execl(char *path, char *arg1, ...);
+
+void test6(FILE *f) {
+ // BAD: the user string is injected directly into a command
+ char command[1000] = "mv ", filename[1000];
+ fread(filename, 1, 1000, f);
+
+ strncat(command, filename, 1000);
+ execl("/bin/sh", "sh", "-c", command);
+}
+
+void test7(FILE *f) {
+ // GOOD [FALSE POSITIVE]: the user string is a positional argument to a shell script
+ char path[1000] = "/home/me/", filename[1000];
+ fread(filename, 1, 1000, f);
+
+ strncat(path, filename, 1000);
+ execl("/bin/sh", "sh", "-c", "script.sh", path);
+}
+
+void test8(char *arg2) {
+ // GOOD?: the user string is the *first* part of the command, like $CC in many environments
+ std::string envCC(getenv("CC"));
+ std::string command = envCC + arg2;
+ system(command.c_str());
+}
+
+void test9(FILE *f) {
+ // BAD: the user string is injected directly into a command
+ std::string path(getenv("something"));
+ std::string command = "mv " + path;
+ system(command.c_str());
+}
+
+void test10(FILE *f) {
+ // BAD: the user string is injected directly into a command
+ std::string path(getenv("something"));
+ system(("mv " + path).c_str());
+}
+
+void test11(FILE *f) {
+ // BAD: the user string is injected directly into a command
+ std::string path(getenv("something"));
+ system(("mv " + path).data());
+}
+
+int atoi(char *);
+
+void test12(FILE *f) {
+ char temp[10];
+ char command[1000];
+
+ fread(temp, 1, 10, f);
+
+ int x = atoi(temp);
+ sprintf(command, "tail -n %d foo.log", x);
+ system(command); // GOOD: the user string was converted to an integer and back
+}
+
+void test13(FILE *f) {
+ char str[1000];
+ char command[1000];
+
+ fread(str, 1, 1000, f);
+
+ sprintf(command, "echo %s", str);
+ system(command); // BAD: the user string was printed into the command with the %s specifier
+}
+
+void test14(FILE *f) {
+ char str[1000];
+ char command[1000];
+
+ fread(str, 1, 1000, f);
+
+ sprintf(command, "echo %p", str);
+ system(command); // GOOD: the user string's address was printed into the command with the %p specifier
+}
+
+void test15(FILE *f) {
+ char temp[10];
+ char command[1000];
+
+ fread(temp, 1, 10, f);
+
+ int x = atoi(temp);
+
+ char temp2[10];
+ sprintf(temp2, "%d", x);
+ sprintf(command, "tail -n %s foo.log", temp2);
+
+ system(command); // GOOD: the user string was converted to an integer and back
+}
+
+
+// TODO: test for call context sensitivity at concatenation site
+
+// open question: do we want to report certain sources even when they're the start of the string?
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-079/semmle/CgiXss/CgiXss.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-079/semmle/CgiXss/CgiXss.expected
index d9fa0d7029f..ebf051ddd0b 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-079/semmle/CgiXss/CgiXss.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-079/semmle/CgiXss/CgiXss.expected
@@ -24,6 +24,7 @@ edges
| search.c:55:17:55:25 | raw_query indirection | search.c:14:24:14:28 | *query |
| search.c:57:5:57:15 | raw_query | search.c:22:24:22:28 | query |
| search.c:57:17:57:25 | raw_query indirection | search.c:22:24:22:28 | *query |
+subpaths
nodes
| search.c:14:24:14:28 | *query | semmle.label | *query |
| search.c:14:24:14:28 | query | semmle.label | query |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-089/SqlTainted/SqlTainted.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-089/SqlTainted/SqlTainted.expected
index e267dd48bba..2b572375ce9 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-089/SqlTainted/SqlTainted.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-089/SqlTainted/SqlTainted.expected
@@ -5,6 +5,15 @@ edges
| test.c:15:20:15:23 | argv | test.c:21:18:21:23 | query1 |
| test.c:15:20:15:23 | argv | test.c:21:18:21:23 | query1 indirection |
| test.c:15:20:15:23 | argv | test.c:21:18:21:23 | query1 indirection |
+| test.cpp:43:27:43:30 | argv | test.cpp:43:27:43:33 | (const char *)... |
+| test.cpp:43:27:43:30 | argv | test.cpp:43:27:43:33 | (const char *)... |
+| test.cpp:43:27:43:30 | argv | test.cpp:43:27:43:33 | access to array |
+| test.cpp:43:27:43:30 | argv | test.cpp:43:27:43:33 | access to array |
+| test.cpp:43:27:43:30 | argv | test.cpp:43:27:43:33 | access to array |
+| test.cpp:43:27:43:30 | argv | test.cpp:43:27:43:33 | access to array |
+| test.cpp:43:27:43:30 | argv | test.cpp:43:27:43:33 | access to array indirection |
+| test.cpp:43:27:43:30 | argv | test.cpp:43:27:43:33 | access to array indirection |
+subpaths
nodes
| test.c:15:20:15:23 | argv | semmle.label | argv |
| test.c:15:20:15:23 | argv | semmle.label | argv |
@@ -13,5 +22,15 @@ nodes
| test.c:21:18:21:23 | query1 | semmle.label | query1 |
| test.c:21:18:21:23 | query1 indirection | semmle.label | query1 indirection |
| test.c:21:18:21:23 | query1 indirection | semmle.label | query1 indirection |
+| test.cpp:43:27:43:30 | argv | semmle.label | argv |
+| test.cpp:43:27:43:30 | argv | semmle.label | argv |
+| test.cpp:43:27:43:33 | (const char *)... | semmle.label | (const char *)... |
+| test.cpp:43:27:43:33 | (const char *)... | semmle.label | (const char *)... |
+| test.cpp:43:27:43:33 | access to array | semmle.label | access to array |
+| test.cpp:43:27:43:33 | access to array | semmle.label | access to array |
+| test.cpp:43:27:43:33 | access to array | semmle.label | access to array |
+| test.cpp:43:27:43:33 | access to array indirection | semmle.label | access to array indirection |
+| test.cpp:43:27:43:33 | access to array indirection | semmle.label | access to array indirection |
#select
| test.c:21:18:21:23 | query1 | test.c:15:20:15:23 | argv | test.c:21:18:21:23 | query1 | This argument to a SQL query function is derived from $@ and then passed to mysql_query(sqlArg) | test.c:15:20:15:23 | argv | user input (argv) |
+| test.cpp:43:27:43:33 | access to array | test.cpp:43:27:43:30 | argv | test.cpp:43:27:43:33 | access to array | This argument to a SQL query function is derived from $@ and then passed to pqxx::work::exec1((unnamed parameter 0)) | test.cpp:43:27:43:30 | argv | user input (argv) |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-089/SqlTainted/test.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-089/SqlTainted/test.cpp
new file mode 100644
index 00000000000..8bdf7dded23
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-089/SqlTainted/test.cpp
@@ -0,0 +1,48 @@
+int sprintf(char* str, const char* format, ...);
+
+namespace std
+{
+ template struct char_traits;
+
+ template class allocator {
+ public:
+ allocator() throw();
+ };
+
+ template, class Allocator = allocator >
+ class basic_string {
+ public:
+ explicit basic_string(const Allocator& a = Allocator());
+ basic_string(const charT* s, const Allocator& a = Allocator());
+
+ const charT* c_str() const;
+ };
+
+ typedef basic_string string;
+}
+
+namespace pqxx {
+ struct connection {};
+
+ struct row {};
+ struct result {};
+
+ struct work {
+ work(connection&);
+
+ row exec1(const char*);
+ result exec(const std::string&);
+ std::string quote(const char*);
+ };
+}
+
+int main(int argc, char** argv) {
+ pqxx::connection c;
+ pqxx::work w(c);
+
+ pqxx::row r = w.exec1(argv[1]); // BAD
+
+ pqxx::result r2 = w.exec(w.quote(argv[1])); // GOOD
+
+ return 0;
+}
\ No newline at end of file
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-114/SAMATE/UncontrolledProcessOperation/UncontrolledProcessOperation.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-114/SAMATE/UncontrolledProcessOperation/UncontrolledProcessOperation.expected
new file mode 100644
index 00000000000..11cca0cf50b
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-114/SAMATE/UncontrolledProcessOperation/UncontrolledProcessOperation.expected
@@ -0,0 +1,31 @@
+edges
+| test.cpp:37:73:37:76 | *data | test.cpp:43:32:43:35 | (LPCSTR)... |
+| test.cpp:37:73:37:76 | *data | test.cpp:43:32:43:35 | data |
+| test.cpp:37:73:37:76 | *data | test.cpp:43:32:43:35 | data indirection |
+| test.cpp:37:73:37:76 | data | test.cpp:43:32:43:35 | (LPCSTR)... |
+| test.cpp:37:73:37:76 | data | test.cpp:43:32:43:35 | data |
+| test.cpp:37:73:37:76 | data | test.cpp:43:32:43:35 | data |
+| test.cpp:37:73:37:76 | data | test.cpp:43:32:43:35 | data indirection |
+| test.cpp:64:30:64:35 | call to getenv | test.cpp:73:17:73:22 | data |
+| test.cpp:64:30:64:35 | call to getenv | test.cpp:73:17:73:22 | data |
+| test.cpp:64:30:64:35 | call to getenv | test.cpp:73:24:73:27 | data indirection |
+| test.cpp:64:30:64:35 | call to getenv | test.cpp:73:24:73:27 | data indirection |
+| test.cpp:73:17:73:22 | data | test.cpp:37:73:37:76 | data |
+| test.cpp:73:24:73:27 | data indirection | test.cpp:37:73:37:76 | *data |
+subpaths
+nodes
+| test.cpp:37:73:37:76 | *data | semmle.label | *data |
+| test.cpp:37:73:37:76 | data | semmle.label | data |
+| test.cpp:43:32:43:35 | (LPCSTR)... | semmle.label | (LPCSTR)... |
+| test.cpp:43:32:43:35 | (LPCSTR)... | semmle.label | (LPCSTR)... |
+| test.cpp:43:32:43:35 | data | semmle.label | data |
+| test.cpp:43:32:43:35 | data | semmle.label | data |
+| test.cpp:43:32:43:35 | data | semmle.label | data |
+| test.cpp:43:32:43:35 | data indirection | semmle.label | data indirection |
+| test.cpp:43:32:43:35 | data indirection | semmle.label | data indirection |
+| test.cpp:64:30:64:35 | call to getenv | semmle.label | call to getenv |
+| test.cpp:64:30:64:35 | call to getenv | semmle.label | call to getenv |
+| test.cpp:73:17:73:22 | data | semmle.label | data |
+| test.cpp:73:24:73:27 | data indirection | semmle.label | data indirection |
+#select
+| test.cpp:43:32:43:35 | data | test.cpp:64:30:64:35 | call to getenv | test.cpp:43:32:43:35 | data | The value of this argument may come from $@ and is being passed to LoadLibraryA | test.cpp:64:30:64:35 | call to getenv | call to getenv |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-114/SAMATE/UncontrolledProcessOperation/UncontrolledProcessOperation.qlref b/cpp/ql/test/query-tests/Security/CWE/CWE-114/SAMATE/UncontrolledProcessOperation/UncontrolledProcessOperation.qlref
new file mode 100644
index 00000000000..a9ca1db5199
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-114/SAMATE/UncontrolledProcessOperation/UncontrolledProcessOperation.qlref
@@ -0,0 +1 @@
+Security/CWE/CWE-114/UncontrolledProcessOperation.ql
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-114/SAMATE/UncontrolledProcessOperation/test.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-114/SAMATE/UncontrolledProcessOperation/test.cpp
new file mode 100644
index 00000000000..299e0372d4a
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-114/SAMATE/UncontrolledProcessOperation/test.cpp
@@ -0,0 +1,132 @@
+// Some SAMATE Juliet test cases for CWE-114.
+
+typedef unsigned long size_t;
+typedef unsigned int BOOL;
+typedef const char *LPCSTR;
+typedef void *HMODULE;
+#define NULL (0)
+
+size_t strlen(const char *s);
+char *strncat(char *s1, const char *s2, size_t n);
+
+HMODULE LoadLibraryA(LPCSTR libname);
+BOOL FreeLibrary(HMODULE hModule);
+
+char *getenv(const char *name);
+
+#define GETENV getenv
+#define ENV_VARIABLE "ADD"
+
+void printLine(const char *msg);
+
+// --- CWE114_Process_Control__w32_char_environment_82 ---
+
+class CWE114_Process_Control__w32_char_environment_82_base
+{
+public:
+ /* pure virtual function */
+ virtual void action(char * data) = 0;
+};
+
+class CWE114_Process_Control__w32_char_environment_82_bad : public CWE114_Process_Control__w32_char_environment_82_base
+{
+public:
+ void action(char * data);
+};
+
+void CWE114_Process_Control__w32_char_environment_82_bad::action(char * data)
+{
+ {
+ HMODULE hModule;
+ /* POTENTIAL FLAW: If the path to the library is not specified, an attacker may be able to
+ * replace his own file with the intended library */
+ hModule = LoadLibraryA(data);
+ if (hModule != NULL)
+ {
+ FreeLibrary(hModule);
+ printLine("Library loaded and freed successfully");
+ }
+ else
+ {
+ printLine("Unable to load library");
+ }
+ }
+}
+
+void bad()
+{
+ char * data;
+ char dataBuffer[100] = "";
+ data = dataBuffer;
+ {
+ /* Append input from an environment variable to data */
+ size_t dataLen = strlen(data);
+ char * environment = GETENV(ENV_VARIABLE);
+ /* If there is data in the environment variable */
+ if (environment != NULL)
+ {
+ /* POTENTIAL FLAW: Read data from an environment variable */
+ strncat(data+dataLen, environment, 100-dataLen-1);
+ }
+ }
+ CWE114_Process_Control__w32_char_environment_82_base* baseObject = new CWE114_Process_Control__w32_char_environment_82_bad;
+ baseObject->action(data);
+ delete baseObject;
+}
+
+// --- CWE114_Process_Control__w32_char_console_33 ---
+
+typedef struct {} FILE;
+char *fgets(char *s, int n, FILE *stream);
+FILE *stdin;
+
+void CWE114_Process_Control__w32_char_console_33_bad()
+{
+ char * data;
+ char * &dataRef = data;
+ char dataBuffer[100] = "";
+ data = dataBuffer;
+ {
+ /* Read input from the console */
+ size_t dataLen = strlen(data);
+ /* if there is room in data, read into it from the console */
+ if (100-dataLen > 1)
+ {
+ /* POTENTIAL FLAW: Read data from the console [NOT DETECTED] */
+ if (fgets(data+dataLen, (int)(100-dataLen), stdin) != NULL)
+ {
+ /* The next few lines remove the carriage return from the string that is
+ * inserted by fgets() */
+ dataLen = strlen(data);
+ if (dataLen > 0 && data[dataLen-1] == '\n')
+ {
+ data[dataLen-1] = '\0';
+ }
+ }
+ else
+ {
+ printLine("fgets() failed");
+ /* Restore NUL terminator if fgets fails */
+ data[dataLen] = '\0';
+ }
+ }
+ }
+ {
+ char * data = dataRef;
+ {
+ HMODULE hModule;
+ /* POTENTIAL FLAW: If the path to the library is not specified, an attacker may be able to
+ * replace his own file with the intended library */
+ hModule = LoadLibraryA(data);
+ if (hModule != NULL)
+ {
+ FreeLibrary(hModule);
+ printLine("Library loaded and freed successfully");
+ }
+ else
+ {
+ printLine("Unable to load library");
+ }
+ }
+ }
+}
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-114/semmle/UncontrolledProcessOperation/UncontrolledProcessOperation.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-114/semmle/UncontrolledProcessOperation/UncontrolledProcessOperation.expected
index c610e346bb1..b9a0f7b3631 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-114/semmle/UncontrolledProcessOperation/UncontrolledProcessOperation.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-114/semmle/UncontrolledProcessOperation/UncontrolledProcessOperation.expected
@@ -47,6 +47,7 @@ edges
| test.cpp:106:17:106:22 | recv output argument | test.cpp:107:15:107:20 | (const char *)... |
| test.cpp:106:17:106:22 | recv output argument | test.cpp:107:15:107:20 | buffer |
| test.cpp:106:17:106:22 | recv output argument | test.cpp:107:15:107:20 | buffer indirection |
+subpaths
nodes
| test.cpp:24:30:24:36 | *command | semmle.label | *command |
| test.cpp:24:30:24:36 | command | semmle.label | command |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/BadlyBoundedWrite.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/BadlyBoundedWrite.expected
new file mode 100644
index 00000000000..bd60a176ca9
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/BadlyBoundedWrite.expected
@@ -0,0 +1,4 @@
+| tests.cpp:350:13:350:19 | call to strncat | This 'call to strncat' operation is limited to 100 bytes but the destination is only 50 bytes. |
+| tests.cpp:452:9:452:15 | call to wcsncpy | This 'call to wcsncpy' operation is limited to 396 bytes but the destination is only 200 bytes. |
+| tests.cpp:481:9:481:16 | call to swprintf | This 'call to swprintf' operation is limited to 400 bytes but the destination is only 200 bytes. |
+| tests.cpp:630:13:630:20 | call to swprintf | This 'call to swprintf' operation is limited to 400 bytes but the destination is only 200 bytes. |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/BadlyBoundedWrite.qlref b/cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/BadlyBoundedWrite.qlref
new file mode 100644
index 00000000000..9636c74d0a8
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/BadlyBoundedWrite.qlref
@@ -0,0 +1 @@
+Security/CWE/CWE-120/BadlyBoundedWrite.ql
\ No newline at end of file
diff --git a/java/ql/test/library-tests/frameworks/guava/flow.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/OffsetUseBeforeRangeCheck.expected
similarity index 100%
rename from java/ql/test/library-tests/frameworks/guava/flow.expected
rename to cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/OffsetUseBeforeRangeCheck.expected
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/OffsetUseBeforeRangeCheck.qlref b/cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/OffsetUseBeforeRangeCheck.qlref
new file mode 100644
index 00000000000..d934901f174
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/OffsetUseBeforeRangeCheck.qlref
@@ -0,0 +1 @@
+Best Practices/Likely Errors/OffsetUseBeforeRangeCheck.ql
\ No newline at end of file
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/OverflowBuffer.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/OverflowBuffer.expected
new file mode 100644
index 00000000000..73f1b74db56
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/OverflowBuffer.expected
@@ -0,0 +1,19 @@
+| tests.cpp:45:9:45:14 | call to memcpy | This 'memcpy' operation accesses 32 bytes but the $@ is only 16 bytes. | tests.cpp:32:10:32:18 | charFirst | destination buffer |
+| tests.cpp:60:9:60:14 | call to memcpy | This 'memcpy' operation accesses 32 bytes but the $@ is only 16 bytes. | tests.cpp:32:10:32:18 | charFirst | destination buffer |
+| tests.cpp:171:9:171:14 | call to memcpy | This 'memcpy' operation accesses 100 bytes but the $@ is only 50 bytes. | tests.cpp:164:20:164:25 | call to malloc | destination buffer |
+| tests.cpp:172:9:172:19 | access to array | This array indexing operation accesses byte offset 99 but the $@ is only 50 bytes. | tests.cpp:164:20:164:25 | call to malloc | array |
+| tests.cpp:192:9:192:14 | call to memcpy | This 'memcpy' operation accesses 100 bytes but the $@ is only 50 bytes. | tests.cpp:181:10:181:22 | dataBadBuffer | destination buffer |
+| tests.cpp:192:9:192:14 | call to memcpy | This 'memcpy' operation accesses 100 bytes but the $@ is only 50 bytes. | tests.cpp:185:12:185:24 | dataBadBuffer | destination buffer |
+| tests.cpp:193:9:193:19 | access to array | This array indexing operation accesses byte offset 99 but the $@ is only 50 bytes. | tests.cpp:181:10:181:22 | dataBadBuffer | array |
+| tests.cpp:193:9:193:19 | access to array | This array indexing operation accesses byte offset 99 but the $@ is only 50 bytes. | tests.cpp:185:12:185:24 | dataBadBuffer | array |
+| tests.cpp:212:9:212:14 | call to memcpy | This 'memcpy' operation accesses 100 bytes but the $@ is only 50 bytes. | tests.cpp:201:36:201:41 | call to alloca | destination buffer |
+| tests.cpp:212:9:212:14 | call to memcpy | This 'memcpy' operation accesses 100 bytes but the $@ is only 50 bytes. | tests.cpp:205:12:205:24 | dataBadBuffer | destination buffer |
+| tests.cpp:213:9:213:19 | access to array | This array indexing operation accesses byte offset 99 but the $@ is only 50 bytes. | tests.cpp:201:36:201:41 | call to alloca | array |
+| tests.cpp:213:9:213:19 | access to array | This array indexing operation accesses byte offset 99 but the $@ is only 50 bytes. | tests.cpp:205:12:205:24 | dataBadBuffer | array |
+| tests.cpp:237:9:237:19 | access to array | This array indexing operation accesses byte offset 99 but the $@ is only 50 bytes. | tests.cpp:221:36:221:41 | call to alloca | array |
+| tests.cpp:237:9:237:19 | access to array | This array indexing operation accesses byte offset 99 but the $@ is only 50 bytes. | tests.cpp:225:12:225:24 | dataBadBuffer | array |
+| tests.cpp:261:9:261:19 | access to array | This array indexing operation accesses byte offset 99 but the $@ is only 50 bytes. | tests.cpp:245:10:245:22 | dataBadBuffer | array |
+| tests.cpp:261:9:261:19 | access to array | This array indexing operation accesses byte offset 99 but the $@ is only 50 bytes. | tests.cpp:249:12:249:24 | dataBadBuffer | array |
+| tests.cpp:384:9:384:14 | call to memcpy | This 'memcpy' operation accesses 40 bytes but the $@ is only 10 bytes. | tests.cpp:380:19:380:24 | call to alloca | destination buffer |
+| tests.cpp:434:9:434:19 | access to array | This array indexing operation accesses byte offset 399 but the $@ is only 200 bytes. | tests.cpp:422:12:422:26 | new[] | array |
+| tests.cpp:453:9:453:19 | access to array | This array indexing operation accesses byte offset 399 but the $@ is only 200 bytes. | tests.cpp:445:12:445:26 | new[] | array |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/OverflowBuffer.qlref b/cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/OverflowBuffer.qlref
new file mode 100644
index 00000000000..5c2bacec579
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/OverflowBuffer.qlref
@@ -0,0 +1 @@
+Security/CWE/CWE-119/OverflowBuffer.ql
\ No newline at end of file
diff --git a/python/ql/test/experimental/library-tests/frameworks/sqlalchemy/ConceptsTest.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/OverflowDestination.expected
similarity index 100%
rename from python/ql/test/experimental/library-tests/frameworks/sqlalchemy/ConceptsTest.expected
rename to cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/OverflowDestination.expected
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/OverflowDestination.qlref b/cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/OverflowDestination.qlref
new file mode 100644
index 00000000000..a4213e22fcd
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/OverflowDestination.qlref
@@ -0,0 +1 @@
+Critical/OverflowDestination.ql
\ No newline at end of file
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/OverflowStatic.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/OverflowStatic.expected
new file mode 100644
index 00000000000..ab9263b8544
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/OverflowStatic.expected
@@ -0,0 +1,2 @@
+| tests.cpp:45:51:45:72 | sizeof() | Potential buffer-overflow: 'charFirst' has size 16 not 32. |
+| tests.cpp:60:52:60:74 | sizeof() | Potential buffer-overflow: 'charFirst' has size 16 not 32. |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/OverflowStatic.qlref b/cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/OverflowStatic.qlref
new file mode 100644
index 00000000000..9ff1c3b33dc
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/OverflowStatic.qlref
@@ -0,0 +1 @@
+Critical/OverflowStatic.ql
\ No newline at end of file
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/OverrunWrite.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/OverrunWrite.expected
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/OverrunWrite.qlref b/cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/OverrunWrite.qlref
new file mode 100644
index 00000000000..f6c962c1a7b
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/OverrunWrite.qlref
@@ -0,0 +1 @@
+Security/CWE/CWE-120/OverrunWrite.ql
\ No newline at end of file
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/OverrunWriteFloat.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/OverrunWriteFloat.expected
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/OverrunWriteFloat.qlref b/cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/OverrunWriteFloat.qlref
new file mode 100644
index 00000000000..757d1592e83
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/OverrunWriteFloat.qlref
@@ -0,0 +1 @@
+Security/CWE/CWE-120/OverrunWriteFloat.ql
\ No newline at end of file
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/StrncpyFlippedArgs.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/StrncpyFlippedArgs.expected
new file mode 100644
index 00000000000..778adb97718
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/StrncpyFlippedArgs.expected
@@ -0,0 +1,3 @@
+| tests.cpp:290:13:290:19 | call to wcsncpy | Potentially unsafe call to wcsncpy; third argument should be size of destination. |
+| tests.cpp:306:4:306:10 | call to wcsncpy | Potentially unsafe call to wcsncpy; third argument should be size of destination. |
+| tests.cpp:452:9:452:15 | call to wcsncpy | Potentially unsafe call to wcsncpy; third argument should be size of destination. |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/StrncpyFlippedArgs.qlref b/cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/StrncpyFlippedArgs.qlref
new file mode 100644
index 00000000000..bf0bf1ea7d0
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/StrncpyFlippedArgs.qlref
@@ -0,0 +1 @@
+Likely Bugs/Memory Management/StrncpyFlippedArgs.ql
\ No newline at end of file
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/UnboundedWrite.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/UnboundedWrite.expected
new file mode 100644
index 00000000000..7cefb7cfafc
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/UnboundedWrite.expected
@@ -0,0 +1,4 @@
+edges
+subpaths
+nodes
+#select
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/UnboundedWrite.qlref b/cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/UnboundedWrite.qlref
new file mode 100644
index 00000000000..767f2ea4db9
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/UnboundedWrite.qlref
@@ -0,0 +1 @@
+Security/CWE/CWE-120/UnboundedWrite.ql
\ No newline at end of file
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/tests.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/tests.cpp
new file mode 100644
index 00000000000..1c7f9ad60f5
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/tests.cpp
@@ -0,0 +1,671 @@
+// A sample of tests from the SAMATE Juliet framework for rule CWE-119.
+
+// library types, functions etc
+typedef unsigned long size_t;
+void *malloc(size_t size);
+void *alloca(size_t size);
+void free(void *ptr);
+#define ALLOCA alloca
+
+void *memcpy(void *s1, const void *s2, size_t n);
+void *memset(void *s, int c, size_t n);
+char *strcpy(char *s1, const char *s2);
+size_t strlen(const char *s);
+
+void exit(int status);
+
+typedef unsigned int DWORD;
+DWORD GetCurrentDirectoryA(DWORD bufferLength, char *buffer);
+bool PathAppendA(char *path, const char *more);
+#define MAX_PATH 4096
+
+void printLine(const char *str);
+void printSizeTLine(size_t val);
+void printIntLine(int val);
+
+// ----------
+
+#define SRC_STR "0123456789abcde0123"
+
+typedef struct _charVoid
+{
+ char charFirst[16];
+ void * voidSecond;
+ void * voidThird;
+} charVoid;
+
+void CWE121_Stack_Based_Buffer_Overflow__char_type_overrun_memcpy_01_bad()
+{
+ {
+ charVoid structCharVoid;
+ structCharVoid.voidSecond = (void *)SRC_STR;
+ /* Print the initial block pointed to by structCharVoid.voidSecond */
+ printLine((char *)structCharVoid.voidSecond);
+ /* FLAW: Use the sizeof(structCharVoid) which will overwrite the pointer voidSecond */
+ memcpy(structCharVoid.charFirst, SRC_STR, sizeof(structCharVoid));
+ structCharVoid.charFirst[(sizeof(structCharVoid.charFirst)/sizeof(char))-1] = '\0'; /* null terminate the string */
+ printLine((char *)structCharVoid.charFirst);
+ printLine((char *)structCharVoid.voidSecond);
+ }
+}
+
+void CWE122_Heap_Based_Buffer_Overflow__char_type_overrun_memcpy_01_bad()
+{
+ {
+ charVoid * structCharVoid = (charVoid *)malloc(sizeof(charVoid));
+ structCharVoid->voidSecond = (void *)SRC_STR;
+ /* Print the initial block pointed to by structCharVoid->voidSecond */
+ printLine((char *)structCharVoid->voidSecond);
+ /* FLAW: Use the sizeof(*structCharVoid) which will overwrite the pointer y */
+ memcpy(structCharVoid->charFirst, SRC_STR, sizeof(*structCharVoid));
+ structCharVoid->charFirst[(sizeof(structCharVoid->charFirst)/sizeof(char))-1] = '\0'; /* null terminate the string */
+ printLine((char *)structCharVoid->charFirst);
+ printLine((char *)structCharVoid->voidSecond);
+ free(structCharVoid);
+ }
+}
+
+void CWE124_Buffer_Underwrite__char_alloca_cpy_01_bad()
+{
+ char * data;
+ char * dataBuffer = (char *)ALLOCA(100*sizeof(char));
+ memset(dataBuffer, 'A', 100-1);
+ dataBuffer[100-1] = '\0';
+ /* FLAW: Set data pointer to before the allocated memory buffer */
+ data = dataBuffer - 8;
+ {
+ char source[100];
+ memset(source, 'C', 100-1); /* fill with 'C's */
+ source[100-1] = '\0'; /* null terminate */
+ /* POTENTIAL FLAW: Possibly copying data to memory before the destination buffer */
+ strcpy(data, source); // [NOT DETECTED]
+ printLine(data);
+ }
+}
+
+void CWE126_Buffer_Overread__char_alloca_loop_01_bad()
+{
+ char * data;
+ char * dataBadBuffer = (char *)ALLOCA(50*sizeof(char));
+ char * dataGoodBuffer = (char *)ALLOCA(100*sizeof(char));
+ memset(dataBadBuffer, 'A', 50-1); /* fill with 'A's */
+ dataBadBuffer[50-1] = '\0'; /* null terminate */
+ memset(dataGoodBuffer, 'A', 100-1); /* fill with 'A's */
+ dataGoodBuffer[100-1] = '\0'; /* null terminate */
+ /* FLAW: Set data pointer to a small buffer */
+ data = dataBadBuffer;
+ {
+ size_t i, destLen;
+ char dest[100];
+ memset(dest, 'C', 100-1);
+ dest[100-1] = '\0'; /* null terminate */
+ destLen = strlen(dest);
+ /* POTENTIAL FLAW: using length of the dest where data
+ * could be smaller than dest causing buffer overread */
+ for (i = 0; i < destLen; i++)
+ {
+ dest[i] = data[i]; // [NOT DETECTED]
+ }
+ dest[100-1] = '\0';
+ printLine(dest);
+ }
+}
+
+void CWE127_Buffer_Underread__char_alloca_cpy_01_bad()
+{
+ char * data;
+ char * dataBuffer = (char *)ALLOCA(100*sizeof(char));
+ memset(dataBuffer, 'A', 100-1);
+ dataBuffer[100-1] = '\0';
+ /* FLAW: Set data pointer to before the allocated memory buffer */
+ data = dataBuffer - 8;
+ {
+ char dest[100*2];
+ memset(dest, 'C', 100*2-1); /* fill with 'C's */
+ dest[100*2-1] = '\0'; /* null terminate */
+ /* POTENTIAL FLAW: Possibly copy from a memory location located before the source buffer */
+ strcpy(dest, data); // [NOT DETECTED]
+ printLine(dest);
+ }
+}
+
+#define BAD_PATH_SIZE (MAX_PATH / 2) /* maintenance note: must be < MAX_PATH in order for 'bad' to be 'bad' */
+
+void CWE785_Path_Manipulation_Function_Without_Max_Sized_Buffer__w32_01_bad()
+{
+ {
+ char path[BAD_PATH_SIZE];
+ DWORD length;
+ length = GetCurrentDirectoryA(BAD_PATH_SIZE, path);
+ if (length == 0 || length >= BAD_PATH_SIZE) /* failure conditions for this API call */
+ {
+ exit(1);
+ }
+ /* FLAW: PathAppend assumes the 'path' parameter is MAX_PATH */
+ /* INCIDENTAL: CWE 121 stack based buffer overflow, which is intrinsic to
+ * this example identified on the CWE webpage */
+ if (!PathAppendA(path, "AAAAAAAAAAAA")) // [NOT DETECTED]
+ {
+ exit(1);
+ }
+ printSizeTLine(strlen(path));
+ printIntLine(BAD_PATH_SIZE);
+ printLine(path);
+ }
+}
+
+#define NULL (0)
+
+void CWE122_Heap_Based_Buffer_Overflow__c_CWE805_char_memcpy_01_bad()
+{
+ char * data;
+ data = NULL;
+ /* FLAW: Allocate and point data to a small buffer that is smaller than the large buffer used in the sinks */
+ data = (char *)malloc(50*sizeof(char));
+ data[0] = '\0'; /* null terminate */
+ {
+ char source[100];
+ memset(source, 'C', 100-1); /* fill with 'C's */
+ source[100-1] = '\0'; /* null terminate */
+ /* POTENTIAL FLAW: Possible buffer overflow if source is larger than data */
+ memcpy(data, source, 100*sizeof(char));
+ data[100-1] = '\0'; /* Ensure the destination buffer is null terminated */
+ printLine(data);
+ free(data);
+ }
+}
+
+void CWE121_Stack_Based_Buffer_Overflow__CWE805_char_declare_memcpy_01_bad()
+{
+ char * data;
+ char dataBadBuffer[50];
+ char dataGoodBuffer[100];
+ /* FLAW: Set a pointer to a "small" buffer. This buffer will be used in the sinks as a destination
+ * buffer in various memory copying functions using a "large" source buffer. */
+ data = dataBadBuffer;
+ data[0] = '\0'; /* null terminate */
+ {
+ char source[100];
+ memset(source, 'C', 100-1); /* fill with 'C's */
+ source[100-1] = '\0'; /* null terminate */
+ /* POTENTIAL FLAW: Possible buffer overflow if the size of data is less than the length of source */
+ memcpy(data, source, 100*sizeof(char));
+ data[100-1] = '\0'; /* Ensure the destination buffer is null terminated */
+ printLine(data);
+ }
+}
+
+void CWE121_Stack_Based_Buffer_Overflow__CWE805_char_alloca_memcpy_01_bad()
+{
+ char * data;
+ char * dataBadBuffer = (char *)ALLOCA(50*sizeof(char));
+ char * dataGoodBuffer = (char *)ALLOCA(100*sizeof(char));
+ /* FLAW: Set a pointer to a "small" buffer. This buffer will be used in the sinks as a destination
+ * buffer in various memory copying functions using a "large" source buffer. */
+ data = dataBadBuffer;
+ data[0] = '\0'; /* null terminate */
+ {
+ char source[100];
+ memset(source, 'C', 100-1); /* fill with 'C's */
+ source[100-1] = '\0'; /* null terminate */
+ /* POTENTIAL FLAW: Possible buffer overflow if the size of data is less than the length of source */
+ memcpy(data, source, 100*sizeof(char));
+ data[100-1] = '\0'; /* Ensure the destination buffer is null terminated */
+ printLine(data);
+ }
+}
+
+void CWE121_Stack_Based_Buffer_Overflow__CWE805_char_alloca_loop_01_bad()
+{
+ char * data;
+ char * dataBadBuffer = (char *)ALLOCA(50*sizeof(char));
+ char * dataGoodBuffer = (char *)ALLOCA(100*sizeof(char));
+ /* FLAW: Set a pointer to a "small" buffer. This buffer will be used in the sinks as a destination
+ * buffer in various memory copying functions using a "large" source buffer. */
+ data = dataBadBuffer;
+ data[0] = '\0'; /* null terminate */
+ {
+ size_t i;
+ char source[100];
+ memset(source, 'C', 100-1); /* fill with 'C's */
+ source[100-1] = '\0'; /* null terminate */
+ /* POTENTIAL FLAW: Possible buffer overflow if the size of data is less than the length of source */
+ for (i = 0; i < 100; i++)
+ {
+ data[i] = source[i];
+ }
+ data[100-1] = '\0'; /* Ensure the destination buffer is null terminated */
+ printLine(data);
+ }
+}
+
+void CWE121_Stack_Based_Buffer_Overflow__CWE805_char_declare_loop_01_bad()
+{
+ char * data;
+ char dataBadBuffer[50];
+ char dataGoodBuffer[100];
+ /* FLAW: Set a pointer to a "small" buffer. This buffer will be used in the sinks as a destination
+ * buffer in various memory copying functions using a "large" source buffer. */
+ data = dataBadBuffer;
+ data[0] = '\0'; /* null terminate */
+ {
+ size_t i;
+ char source[100];
+ memset(source, 'C', 100-1); /* fill with 'C's */
+ source[100-1] = '\0'; /* null terminate */
+ /* POTENTIAL FLAW: Possible buffer overflow if the size of data is less than the length of source */
+ for (i = 0; i < 100; i++)
+ {
+ data[i] = source[i];
+ }
+ data[100-1] = '\0'; /* Ensure the destination buffer is null terminated */
+ printLine(data);
+ }
+}
+
+wchar_t *wcsncpy(wchar_t *destination, const wchar_t *source, size_t num);
+size_t wcslen(const wchar_t *str);
+char *strcat(char *destination, const char *source);
+char *strncat(char *destination, const char *source, size_t num);
+
+void *memmove(void *destination, const void *source, size_t num);
+
+void printWLine(const wchar_t *line);
+
+/* MAINTENANCE NOTE: The length of this string should equal the 10 */
+#define SRC_STRING L"AAAAAAAAAA"
+
+namespace CWE122_Heap_Based_Buffer_Overflow__cpp_CWE193_wchar_t_ncpy_01
+{
+ void bad()
+ {
+ wchar_t * data;
+ data = NULL;
+ /* FLAW: Did not leave space for a null terminator */
+ data = new wchar_t[10];
+ {
+ wchar_t source[10+1] = SRC_STRING;
+ /* Copy length + 1 to include NUL terminator from source */
+ /* POTENTIAL FLAW: data may not have enough space to hold source */
+ wcsncpy(data, source, wcslen(source) + 1);
+ printWLine(data);
+ delete [] data;
+ }
+ }
+
+ static void goodG2B()
+ {
+ wchar_t * data;
+ data = NULL;
+ /* FIX: Allocate space for a null terminator */
+ data = new wchar_t[10+1];
+ {
+ wchar_t source[10+1] = SRC_STRING;
+ /* Copy length + 1 to include NUL terminator from source */
+ /* POTENTIAL FLAW: data may not have enough space to hold source */
+ wcsncpy(data, source, wcslen(source) + 1); // [FALSE POSITIVE RESULT] (debatable)
+ printWLine(data);
+ delete [] data;
+ }
+ }
+} /* close namespace */
+
+namespace CWE122_Heap_Based_Buffer_Overflow__cpp_CWE193_wchar_t_memmove_31
+{
+ void bad()
+ {
+ wchar_t * data;
+ data = NULL;
+ /* FLAW: Did not leave space for a null terminator */
+ data = new wchar_t[10];
+ {
+ wchar_t * dataCopy = data;
+ wchar_t * data = dataCopy;
+ {
+ wchar_t source[10+1] = SRC_STRING;
+ /* Copy length + 1 to include NUL terminator from source */
+ /* POTENTIAL FLAW: data may not have enough space to hold source */
+ memmove(data, source, (wcslen(source) + 1) * sizeof(wchar_t)); // [NOT DETECTED]
+ printWLine(data);
+ delete [] data;
+ }
+ }
+ }
+} /* close namespace */
+
+namespace CWE122_Heap_Based_Buffer_Overflow__cpp_CWE805_char_ncat_01
+{
+ void bad()
+ {
+ char * data;
+ data = NULL;
+ /* FLAW: Allocate using new[] and point data to a small buffer that is smaller than the large buffer used in the sinks */
+ data = new char[50];
+ data[0] = '\0'; /* null terminate */
+ {
+ char source[100];
+ memset(source, 'C', 100-1); /* fill with 'C's */
+ source[100-1] = '\0'; /* null terminate */
+ /* POTENTIAL FLAW: Possible buffer overflow if source is larger than sizeof(data)-strlen(data) */
+ strncat(data, source, 100);
+ printLine(data);
+ delete [] data;
+ }
+ }
+} /* close namespace */
+
+void CWE122_Heap_Based_Buffer_Overflow__c_dest_char_cat_01_bad()
+{
+ char * data;
+ data = NULL;
+ /* FLAW: Allocate and point data to a small buffer that is smaller than the large buffer used in the sinks */
+ data = (char *)malloc(50*sizeof(char));
+ data[0] = '\0'; /* null terminate */
+ {
+ char source[100];
+ memset(source, 'C', 100-1); /* fill with 'C's */
+ source[100-1] = '\0'; /* null terminate */
+ /* POTENTIAL FLAW: Possible buffer overflow if source is larger than sizeof(data)-strlen(data) */
+ strcat(data, source); // [NOT DETECTED]
+ printLine(data);
+ free(data);
+ }
+}
+
+void CWE121_Stack_Based_Buffer_Overflow__CWE131_memcpy_01_bad()
+{
+ int * data;
+ data = NULL;
+ /* FLAW: Allocate memory without using sizeof(int) */
+ data = (int *)ALLOCA(10);
+ {
+ int source[10] = {0};
+ /* POTENTIAL FLAW: Possible buffer overflow if data was not allocated correctly in the source */
+ memcpy(data, source, 10*sizeof(int));
+ printIntLine(data[0]);
+ }
+}
+
+typedef long long int64_t;
+wchar_t *wmemset(wchar_t *dest, wchar_t c, size_t count);
+void* calloc(size_t num, size_t size);
+
+void printLongLongLine(int64_t longLongIntNumber);
+void printDoubleLine(double doubleNumber);
+
+void CWE122_Heap_Based_Buffer_Overflow__cpp_CWE805_int64_t_loop_01_bad()
+{
+ int64_t * data;
+ data = NULL;
+ /* FLAW: Allocate using new[] and point data to a small buffer that is smaller than the large buffer used in the sinks */
+ data = new int64_t[50];
+ {
+ int64_t source[100] = {0}; /* fill with 0's */
+ {
+ size_t i;
+ /* POTENTIAL FLAW: Possible buffer overflow if data < 100 */
+ for (i = 0; i < 100; i++)
+ {
+ data[i] = source[i]; // [NOT DETECTED]
+ }
+ printLongLongLine(data[0]);
+ delete [] data;
+ }
+ }
+}
+
+void CWE122_Heap_Based_Buffer_Overflow__cpp_CWE805_wchar_t_loop_01_bad()
+{
+ wchar_t * data;
+ data = NULL;
+ /* FLAW: Allocate using new[] and point data to a small buffer that is smaller than the large buffer used in the sinks */
+ data = new wchar_t[50];
+ data[0] = L'\0'; /* null terminate */
+ {
+ size_t i;
+ wchar_t source[100];
+ wmemset(source, L'C', 100-1); /* fill with L'C's */
+ source[100-1] = L'\0'; /* null terminate */
+ /* POTENTIAL FLAW: Possible buffer overflow if source is larger than data */
+ for (i = 0; i < 100; i++)
+ {
+ data[i] = source[i];
+ }
+ data[100-1] = L'\0'; /* Ensure the destination buffer is null terminated */
+ printWLine(data);
+ delete [] data;
+ }
+}
+
+void CWE122_Heap_Based_Buffer_Overflow__cpp_CWE805_wchar_t_ncpy_01_bad()
+{
+ wchar_t * data;
+ data = NULL;
+ /* FLAW: Allocate using new[] and point data to a small buffer that is smaller than the large buffer used in the sinks */
+ data = new wchar_t[50];
+ data[0] = L'\0'; /* null terminate */
+ {
+ wchar_t source[100];
+ wmemset(source, L'C', 100-1); /* fill with L'C's */
+ source[100-1] = L'\0'; /* null terminate */
+ /* POTENTIAL FLAW: Possible buffer overflow if source is larger than data */
+ wcsncpy(data, source, 100-1);
+ data[100-1] = L'\0'; /* Ensure the destination buffer is null terminated */
+ printWLine(data);
+ delete [] data;
+ }
+}
+
+#ifdef _WIN32
+int _snwprintf(wchar_t *buffer, size_t count, const wchar_t *format, ...);
+#define SNPRINTF _snwprintf
+#else
+int snprintf(char *s, size_t n, const char *format, ...);
+int swprintf(wchar_t *wcs, size_t maxlen, const wchar_t *format, ...);
+//#define SNPRINTF snprintf --- original code; using snprintf appears to be a mistake in samate?
+#define SNPRINTF swprintf
+#endif
+
+void CWE122_Heap_Based_Buffer_Overflow__cpp_CWE805_wchar_t_snprintf_01_bad()
+{
+ wchar_t * data;
+ data = NULL;
+ /* FLAW: Allocate using new[] and point data to a small buffer that is smaller than the large buffer used in the sinks */
+ data = new wchar_t[50];
+ data[0] = L'\0'; /* null terminate */
+ {
+ wchar_t source[100];
+ wmemset(source, L'C', 100-1); /* fill with L'C's */
+ source[100-1] = L'\0'; /* null terminate */
+ /* POTENTIAL FLAW: Possible buffer overflow if source is larger than data */
+ SNPRINTF(data, 100, L"%s", source);
+ printWLine(data);
+ delete [] data;
+ }
+}
+
+/* classes used in some test cases as a custom type */
+class TwoIntsClass
+{
+ public: // Needed to access variables from label files
+ int intOne;
+ int intTwo;
+};
+
+class OneIntClass
+{
+ public: // Needed to access variables from label files
+ int intOne;
+};
+
+void *operator new(size_t size, void *ptr) throw(); // placement new (from #include )
+
+void CWE122_Heap_Based_Buffer_Overflow__placement_new_01_bad()
+{
+ char * data;
+ char * dataBadBuffer = (char *)malloc(sizeof(OneIntClass));
+ char * dataGoodBuffer = (char *)malloc(sizeof(TwoIntsClass));
+ /* POTENTIAL FLAW: Initialize data to a buffer small than the sizeof(TwoIntsClass) */
+ data = dataBadBuffer;
+ {
+ /* The Visual C++ compiler generates a warning if you initialize the class with ().
+ * This will cause the compile to default-initialize the object.
+ * See http://msdn.microsoft.com/en-us/library/wewb47ee%28v=VS.100%29.aspx
+ */
+ /* POTENTIAL FLAW: data may not be large enough to hold a TwoIntsClass */
+ TwoIntsClass * classTwo = new(data) TwoIntsClass; // [NOT DETECTED]
+ /* Initialize and make use of the class */
+ classTwo->intOne = 5;
+ classTwo->intTwo = 10; /* POTENTIAL FLAW: If sizeof(data) < sizeof(TwoIntsClass) then this line will be a buffer overflow */
+ printIntLine(classTwo->intOne);
+ /* skip printing classTwo->intTwo since that could be a buffer overread */
+ free(data);
+ }
+}
+
+void CWE122_Heap_Based_Buffer_Overflow__sizeof_double_01_bad()
+{
+ double * data;
+ /* Initialize data */
+ data = NULL;
+ /* INCIDENTAL: CWE-467 (Use of sizeof() on a pointer type) */
+ /* FLAW: Using sizeof the pointer and not the data type in malloc() */
+ data = (double *)malloc(sizeof(data)); // [NOT DETECTED]
+ *data = 1.7E300;
+ /* POTENTIAL FLAW: Attempt to use data, which may not have enough memory allocated */
+ printDoubleLine(*data);
+ free(data);
+}
+
+int rand(void);
+#define RAND32() ((rand()<<30) ^ (rand()<<15) ^ rand())
+
+void CWE122_Heap_Based_Buffer_Overflow__c_CWE129_rand_01_bad()
+{
+ int data;
+ /* Initialize data */
+ data = -1;
+ /* POTENTIAL FLAW: Set data to a random value */
+ data = RAND32();
+ {
+ int i;
+ int * buffer = (int *)malloc(10 * sizeof(int));
+ /* initialize buffer */
+ for (i = 0; i < 10; i++)
+ {
+ buffer[i] = 0;
+ }
+ /* POTENTIAL FLAW: Attempt to write to an index of the array that is above the upper bound
+ * This code does check to see if the array index is negative */
+ if (data >= 0)
+ {
+ buffer[data] = 1; // [NOT DETECTED]
+ /* Print the array values */
+ for(i = 0; i < 10; i++)
+ {
+ printIntLine(buffer[i]);
+ }
+ }
+ else
+ {
+ printLine("ERROR: Array index is negative.");
+ }
+ free(buffer);
+ }
+}
+
+typedef struct FILE;
+int fscanf(FILE *stream, const char *format, ...);
+FILE *stdin;
+
+void CWE122_Heap_Based_Buffer_Overflow__c_CWE129_fscanf_01_bad()
+{
+ int data;
+ /* Initialize data */
+ data = -1;
+ /* POTENTIAL FLAW: Read data from the console using fscanf() */
+ fscanf(stdin, "%d", &data);
+ {
+ int i;
+ int * buffer = (int *)malloc(10 * sizeof(int));
+ /* initialize buffer */
+ for (i = 0; i < 10; i++)
+ {
+ buffer[i] = 0;
+ }
+ /* POTENTIAL FLAW: Attempt to write to an index of the array that is above the upper bound
+ * This code does check to see if the array index is negative */
+ if (data >= 0)
+ {
+ buffer[data] = 1; // [NOT DETECTED]
+ /* Print the array values */
+ for(i = 0; i < 10; i++)
+ {
+ printIntLine(buffer[i]);
+ }
+ }
+ else
+ {
+ printLine("ERROR: Array index is negative.");
+ }
+ free(buffer);
+ }
+}
+
+void CWE122_Heap_Based_Buffer_Overflow__cpp_CWE805_wchar_t_snprintf_31_bad()
+{
+ wchar_t * data;
+ data = NULL;
+ /* FLAW: Allocate using new[] and point data to a small buffer that is smaller than the large buffer used in the sinks */
+ data = new wchar_t[50];
+ data[0] = L'\0'; /* null terminate */
+ {
+ wchar_t * dataCopy = data;
+ wchar_t * data = dataCopy;
+ {
+ wchar_t source[100];
+ wmemset(source, L'C', 100-1); /* fill with L'C's */
+ source[100-1] = L'\0'; /* null terminate */
+ /* POTENTIAL FLAW: Possible buffer overflow if source is larger than data */
+ SNPRINTF(data, 100, L"%s", source);
+ printWLine(data);
+ delete [] data;
+ }
+ }
+}
+
+int rand(void);
+
+int globalReturnsTrueOrFalse()
+{
+ return (rand() % 2);
+}
+
+#define SRC_STRING "AAAAAAAAAA"
+
+void CWE121_Stack_Based_Buffer_Overflow__CWE193_char_declare_cpy_12_bad()
+{
+ char * data;
+ char dataBadBuffer[10];
+ char dataGoodBuffer[10+1];
+ if(globalReturnsTrueOrFalse())
+ {
+ /* FLAW: Set a pointer to a buffer that does not leave room for a NULL terminator when performing
+ * string copies in the sinks */
+ data = dataBadBuffer;
+ data[0] = '\0'; /* null terminate */
+ }
+ else
+ {
+ /* FIX: Set a pointer to a buffer that leaves room for a NULL terminator when performing
+ * string copies in the sinks */
+ data = dataGoodBuffer;
+ data[0] = '\0'; /* null terminate */
+ }
+ {
+ char source[10+1] = SRC_STRING;
+ /* POTENTIAL FLAW: data may not have enough space to hold source */ // [NOT DETECTED]
+ strcpy(data, source);
+ printLine(data);
+ }
+}
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-119/semmle/tests/OverflowBuffer.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-119/semmle/tests/OverflowBuffer.expected
index 614716b470d..795d83587c3 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-119/semmle/tests/OverflowBuffer.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-119/semmle/tests/OverflowBuffer.expected
@@ -72,12 +72,9 @@
| unions.cpp:30:2:30:7 | call to memset | This 'memset' operation accesses 200 bytes but the $@ is only 100 bytes. | unions.cpp:15:7:15:11 | small | destination buffer |
| unions.cpp:34:2:34:7 | call to memset | This 'memset' operation accesses 200 bytes but the $@ is only 100 bytes. | unions.cpp:16:7:16:11 | large | destination buffer |
| unions.cpp:34:2:34:7 | call to memset | This 'memset' operation accesses 200 bytes but the $@ is only 100 bytes. | unions.cpp:34:14:34:18 | large | destination buffer |
-| var_size_struct.cpp:54:5:54:14 | access to array | This array indexing operation accesses byte offset 1 but the $@ is only 1 byte. | var_size_struct.cpp:32:8:32:10 | str | array |
-| var_size_struct.cpp:55:5:55:14 | access to array | This array indexing operation accesses byte offset 1 but the $@ is only 1 byte. | var_size_struct.cpp:38:8:38:10 | str | array |
| var_size_struct.cpp:71:3:71:8 | call to memset | This 'memset' operation accesses 1025 bytes but the $@ is only 1024 bytes. | var_size_struct.cpp:63:8:63:11 | data | destination buffer |
| var_size_struct.cpp:73:3:73:9 | call to strncpy | This 'strncpy' operation may access 1025 bytes but the $@ is only 1024 bytes. | var_size_struct.cpp:63:8:63:11 | data | destination buffer |
| var_size_struct.cpp:87:3:87:19 | access to array | This array indexing operation accesses byte offset 67 but the $@ is only 64 bytes. | var_size_struct.cpp:78:7:78:14 | elements | array |
| var_size_struct.cpp:99:3:99:8 | call to memset | This 'memset' operation accesses 129 bytes but the $@ is only 128 bytes. | var_size_struct.cpp:92:8:92:10 | str | destination buffer |
| var_size_struct.cpp:101:3:101:8 | call to memset | This 'memset' operation accesses 129 bytes but the $@ is only 128 bytes. | var_size_struct.cpp:92:8:92:10 | str | destination buffer |
| var_size_struct.cpp:103:3:103:9 | call to strncpy | This 'strncpy' operation may access 129 bytes but the $@ is only 128 bytes. | var_size_struct.cpp:92:8:92:10 | str | destination buffer |
-| var_size_struct.cpp:171:3:171:8 | call to memset | This 'memset' operation accesses 100 bytes but the $@ is only 1 byte. | var_size_struct.cpp:125:17:125:19 | arr | destination buffer |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-119/semmle/tests/OverflowStatic.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-119/semmle/tests/OverflowStatic.expected
index ef334a73a2c..ac44bbf028d 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-119/semmle/tests/OverflowStatic.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-119/semmle/tests/OverflowStatic.expected
@@ -3,8 +3,6 @@
| tests.cpp:163:3:163:11 | access to array | Potential buffer-overflow: counter 'k' <= 100 but 'buffer' has 100 elements. |
| tests.cpp:164:8:164:16 | access to array | Potential buffer-overflow: counter 'k' <= 100 but 'buffer' has 100 elements. |
| tests.cpp:245:42:245:42 | 6 | Potential buffer-overflow: 'global_array_5' has size 5 not 6. |
-| tests.cpp:349:2:349:14 | access to array | Potential buffer-overflow: 'charArray' has size 10 but 'charArray[10]' is accessed here. |
-| tests.cpp:350:17:350:29 | access to array | Potential buffer-overflow: 'charArray' has size 10 but 'charArray[10]' is accessed here. |
-| var_size_struct.cpp:54:5:54:14 | access to array | Potential buffer-overflow: 'str' has size 1 but 'str[1]' is accessed here. |
-| var_size_struct.cpp:55:5:55:14 | access to array | Potential buffer-overflow: 'str' has size 1 but 'str[1]' is accessed here. |
+| tests.cpp:349:2:349:14 | access to array | Potential buffer-overflow: 'charArray' has size 10 but 'charArray[10]' may be accessed here. |
+| tests.cpp:350:17:350:29 | access to array | Potential buffer-overflow: 'charArray' has size 10 but 'charArray[10]' may be accessed here. |
| var_size_struct.cpp:103:39:103:41 | 129 | Potential buffer-overflow: 'str' has size 128 not 129. |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-119/semmle/tests/UnboundedWrite.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-119/semmle/tests/UnboundedWrite.expected
index 58e3dda0964..7cefb7cfafc 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-119/semmle/tests/UnboundedWrite.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-119/semmle/tests/UnboundedWrite.expected
@@ -1,3 +1,4 @@
edges
+subpaths
nodes
#select
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-119/semmle/tests/var_size_struct.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-119/semmle/tests/var_size_struct.cpp
index 00b433c60ef..a514135f348 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-119/semmle/tests/var_size_struct.cpp
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-119/semmle/tests/var_size_struct.cpp
@@ -51,8 +51,8 @@ void testVarString(int n) {
s1->str[1] = '?'; // GOOD
s2->str[1] = '?'; // GOOD
s3->str[1] = '?'; // GOOD
- s4->str[1] = '?'; // BAD
- s5->str[1] = '?'; // BAD
+ s4->str[1] = '?'; // BAD [NOT DETECTED]
+ s5->str[1] = '?'; // BAD [NOT DETECTED]
}
}
@@ -161,14 +161,12 @@ void useVarStruct34(varStruct5 *vs5) {
varStruct5 *vs5b = (varStruct5 *)malloc(sizeof(*vs5));
varStruct6 *vs6 = (varStruct6 *)malloc(offsetof(varStruct6, arr) + 9); // establish varStruct6 as variable size
varStruct7 *vs7 = (varStruct7 *)malloc(sizeForVarStruct7(9)); // establish varStruct7 as variable size
- varStruct8 *vs8a = (varStruct8 *)malloc(sizeof(varStruct8) + 9); // establish varStruct8 as variable size
- varStruct8 *vs8b = (varStruct8 *)malloc(sizeof(varStruct8));
varStruct9 *vs9 = (varStruct9 *)malloc(__builtin_offsetof(varStruct9, arr) + 9); // establish varStruct9 as variable size
}
void testVarStruct34(varStruct3 *vs3, varStruct4 *vs4, varStruct5 *vs5, varStruct6 *vs6, varStruct7 *vs7, varStruct8 *vs8, varStruct9 *vs9) {
memset(vs3->arr, 'x', 100); // GOOD: it's variable size, we don't know how big so shouldn't flag
- memset(vs4->arr, 'x', 100); // BAD: it's not variable size, so this is a buffer overflow
+ memset(vs4->arr, 'x', 100); // BAD: [NOT DETECTED] it's not variable size, so this is a buffer overflow
memset(vs5->arr, 'x', 100); // GOOD: it's variable size, we don't know how big so shouldn't flag
memset(vs6->arr, 'x', 100); // GOOD: it's variable size, we don't know how big so shouldn't flag
memset(vs7->arr, 'x', 100); // GOOD: it's variable size, we don't know how big so shouldn't flag
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-119/semmle/tests/varsize.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-119/semmle/tests/varsize.expected
index d9e9effde62..79406c3eaef 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-119/semmle/tests/varsize.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-119/semmle/tests/varsize.expected
@@ -1,12 +1,17 @@
| var_size_struct.cpp:13:8:13:17 | VarString1 | var_size_struct.cpp:15:8:15:10 | str |
| var_size_struct.cpp:18:8:18:17 | VarString2 | var_size_struct.cpp:20:8:20:10 | str |
| var_size_struct.cpp:24:8:24:17 | VarString3 | var_size_struct.cpp:26:8:26:10 | str |
+| var_size_struct.cpp:30:8:30:17 | VarString4 | var_size_struct.cpp:32:8:32:10 | str |
+| var_size_struct.cpp:36:8:36:17 | VarString5 | var_size_struct.cpp:38:8:38:10 | str |
| var_size_struct.cpp:36:8:36:17 | VarString5 | var_size_struct.cpp:39:8:39:11 | str2 |
| var_size_struct.cpp:61:8:61:17 | varStruct1 | var_size_struct.cpp:63:8:63:11 | data |
| var_size_struct.cpp:76:8:76:17 | varStruct2 | var_size_struct.cpp:78:7:78:14 | elements |
+| var_size_struct.cpp:106:8:106:20 | notVarStruct2 | var_size_struct.cpp:107:8:107:10 | str |
| var_size_struct.cpp:119:8:119:17 | varStruct3 | var_size_struct.cpp:121:17:121:19 | arr |
+| var_size_struct.cpp:123:8:123:17 | varStruct4 | var_size_struct.cpp:125:17:125:19 | arr |
| var_size_struct.cpp:127:8:127:17 | varStruct5 | var_size_struct.cpp:129:17:129:19 | arr |
| var_size_struct.cpp:131:8:131:17 | varStruct6 | var_size_struct.cpp:133:17:133:19 | arr |
| var_size_struct.cpp:135:8:135:17 | varStruct7 | var_size_struct.cpp:137:17:137:19 | arr |
| var_size_struct.cpp:139:8:139:17 | varStruct8 | var_size_struct.cpp:141:9:141:11 | arr |
| var_size_struct.cpp:143:8:143:17 | varStruct9 | var_size_struct.cpp:145:17:145:19 | arr |
+| var_size_struct.cpp:181:8:181:18 | PseudoUnion | var_size_struct.cpp:183:7:183:10 | data |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-120/semmle/tests/UnboundedWrite.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-120/semmle/tests/UnboundedWrite.expected
index 5255753b235..e98353732b9 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-120/semmle/tests/UnboundedWrite.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-120/semmle/tests/UnboundedWrite.expected
@@ -49,6 +49,7 @@ edges
| tests.c:34:10:34:13 | argv | tests.c:34:10:34:16 | access to array |
| tests.c:34:10:34:13 | argv | tests.c:34:10:34:16 | access to array indirection |
| tests.c:34:10:34:13 | argv | tests.c:34:10:34:16 | access to array indirection |
+subpaths
nodes
| tests.c:28:22:28:25 | argv | semmle.label | argv |
| tests.c:28:22:28:25 | argv | semmle.label | argv |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-129/SAMATE/ImproperArrayIndexValidation/CWE122_Heap_Based_Buffer_Overflow__c_CWE129_fgets_01.c b/cpp/ql/test/query-tests/Security/CWE/CWE-129/SAMATE/ImproperArrayIndexValidation/CWE122_Heap_Based_Buffer_Overflow__c_CWE129_fgets_01.c
new file mode 100644
index 00000000000..2092902b665
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-129/SAMATE/ImproperArrayIndexValidation/CWE122_Heap_Based_Buffer_Overflow__c_CWE129_fgets_01.c
@@ -0,0 +1,65 @@
+// Snippet from a SAMATE Juliet test case for rule CWE-122 / CWE-129
+// CWE122_Heap_Based_Buffer_Overflow__c_CWE129_fgets_01.c
+
+typedef unsigned long size_t;
+void *malloc(size_t size);
+void free(void *ptr);
+#define NULL (0)
+
+typedef struct {} FILE;
+FILE *stdin;
+char *fgets(char *s, int n, FILE *stream);
+
+int atoi(const char *nptr);
+
+void printLine(const char *str);
+void printIntLine(int val);
+
+// ---
+
+#define CHAR_ARRAY_SIZE (64)
+
+void CWE122_Heap_Based_Buffer_Overflow__c_CWE129_fgets_01_bad()
+{
+ int data;
+ /* Initialize data */
+ data = -1;
+ {
+ char inputBuffer[CHAR_ARRAY_SIZE] = "";
+ /* POTENTIAL FLAW: Read data from the console using fgets() */
+ if (fgets(inputBuffer, CHAR_ARRAY_SIZE, stdin) != NULL)
+ {
+ /* Convert to int */
+ data = atoi(inputBuffer);
+ }
+ else
+ {
+ printLine("fgets() failed.");
+ }
+ }
+ {
+ int i;
+ int * buffer = (int *)malloc(10 * sizeof(int));
+ /* initialize buffer */
+ for (i = 0; i < 10; i++)
+ {
+ buffer[i] = 0;
+ }
+ /* POTENTIAL FLAW: Attempt to write to an index of the array that is above the upper bound
+ * This code does check to see if the array index is negative */
+ if (data >= 0)
+ {
+ buffer[data] = 1;
+ /* Print the array values */
+ for(i = 0; i < 10; i++)
+ {
+ printIntLine(buffer[i]);
+ }
+ }
+ else
+ {
+ printLine("ERROR: Array index is negative.");
+ }
+ free(buffer);
+ }
+}
\ No newline at end of file
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-129/SAMATE/ImproperArrayIndexValidation/ImproperArrayIndexValidation.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-129/SAMATE/ImproperArrayIndexValidation/ImproperArrayIndexValidation.expected
new file mode 100644
index 00000000000..008ff07b800
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-129/SAMATE/ImproperArrayIndexValidation/ImproperArrayIndexValidation.expected
@@ -0,0 +1 @@
+| CWE122_Heap_Based_Buffer_Overflow__c_CWE129_fgets_01.c:52:20:52:23 | data | $@ flows to here and is used in an array indexing expression, potentially causing an invalid access. | CWE122_Heap_Based_Buffer_Overflow__c_CWE129_fgets_01.c:30:19:30:29 | inputBuffer | User-provided value |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-129/SAMATE/ImproperArrayIndexValidation/ImproperArrayIndexValidation.qlref b/cpp/ql/test/query-tests/Security/CWE/CWE-129/SAMATE/ImproperArrayIndexValidation/ImproperArrayIndexValidation.qlref
new file mode 100644
index 00000000000..f1d46d8f8d6
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-129/SAMATE/ImproperArrayIndexValidation/ImproperArrayIndexValidation.qlref
@@ -0,0 +1 @@
+Security/CWE/CWE-129/ImproperArrayIndexValidation.ql
\ No newline at end of file
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-134/SAMATE/UncontrolledFormatString.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-134/SAMATE/UncontrolledFormatString.expected
new file mode 100644
index 00000000000..5bce7b8aa38
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-134/SAMATE/UncontrolledFormatString.expected
@@ -0,0 +1,46 @@
+edges
+| char_connect_socket_w32_vsnprintf_01_bad.c:94:46:94:69 | recv output argument | char_connect_socket_w32_vsnprintf_01_bad.c:125:15:125:18 | data |
+| char_connect_socket_w32_vsnprintf_01_bad.c:94:46:94:69 | recv output argument | char_connect_socket_w32_vsnprintf_01_bad.c:125:15:125:18 | data |
+| char_connect_socket_w32_vsnprintf_01_bad.c:94:46:94:69 | recv output argument | char_connect_socket_w32_vsnprintf_01_bad.c:125:15:125:18 | data indirection |
+| char_connect_socket_w32_vsnprintf_01_bad.c:94:55:94:68 | ... + ... | char_connect_socket_w32_vsnprintf_01_bad.c:125:15:125:18 | data |
+| char_connect_socket_w32_vsnprintf_01_bad.c:94:55:94:68 | ... + ... | char_connect_socket_w32_vsnprintf_01_bad.c:125:15:125:18 | data |
+| char_connect_socket_w32_vsnprintf_01_bad.c:94:55:94:68 | ... + ... | char_connect_socket_w32_vsnprintf_01_bad.c:125:15:125:18 | data indirection |
+| char_console_fprintf_01_bad.c:30:23:30:35 | ... + ... | char_console_fprintf_01_bad.c:49:21:49:24 | (const char *)... |
+| char_console_fprintf_01_bad.c:30:23:30:35 | ... + ... | char_console_fprintf_01_bad.c:49:21:49:24 | data |
+| char_console_fprintf_01_bad.c:30:23:30:35 | ... + ... | char_console_fprintf_01_bad.c:49:21:49:24 | data indirection |
+| char_console_fprintf_01_bad.c:30:23:30:35 | fgets output argument | char_console_fprintf_01_bad.c:49:21:49:24 | (const char *)... |
+| char_console_fprintf_01_bad.c:30:23:30:35 | fgets output argument | char_console_fprintf_01_bad.c:49:21:49:24 | data |
+| char_console_fprintf_01_bad.c:30:23:30:35 | fgets output argument | char_console_fprintf_01_bad.c:49:21:49:24 | data indirection |
+| char_environment_fprintf_01_bad.c:27:30:27:35 | call to getenv | char_environment_fprintf_01_bad.c:36:21:36:24 | (const char *)... |
+| char_environment_fprintf_01_bad.c:27:30:27:35 | call to getenv | char_environment_fprintf_01_bad.c:36:21:36:24 | (const char *)... |
+| char_environment_fprintf_01_bad.c:27:30:27:35 | call to getenv | char_environment_fprintf_01_bad.c:36:21:36:24 | data |
+| char_environment_fprintf_01_bad.c:27:30:27:35 | call to getenv | char_environment_fprintf_01_bad.c:36:21:36:24 | data |
+| char_environment_fprintf_01_bad.c:27:30:27:35 | call to getenv | char_environment_fprintf_01_bad.c:36:21:36:24 | data indirection |
+| char_environment_fprintf_01_bad.c:27:30:27:35 | call to getenv | char_environment_fprintf_01_bad.c:36:21:36:24 | data indirection |
+subpaths
+nodes
+| char_connect_socket_w32_vsnprintf_01_bad.c:94:46:94:69 | recv output argument | semmle.label | recv output argument |
+| char_connect_socket_w32_vsnprintf_01_bad.c:94:55:94:68 | ... + ... | semmle.label | ... + ... |
+| char_connect_socket_w32_vsnprintf_01_bad.c:125:15:125:18 | data | semmle.label | data |
+| char_connect_socket_w32_vsnprintf_01_bad.c:125:15:125:18 | data | semmle.label | data |
+| char_connect_socket_w32_vsnprintf_01_bad.c:125:15:125:18 | data | semmle.label | data |
+| char_connect_socket_w32_vsnprintf_01_bad.c:125:15:125:18 | data indirection | semmle.label | data indirection |
+| char_connect_socket_w32_vsnprintf_01_bad.c:125:15:125:18 | data indirection | semmle.label | data indirection |
+| char_console_fprintf_01_bad.c:30:23:30:35 | ... + ... | semmle.label | ... + ... |
+| char_console_fprintf_01_bad.c:30:23:30:35 | fgets output argument | semmle.label | fgets output argument |
+| char_console_fprintf_01_bad.c:49:21:49:24 | (const char *)... | semmle.label | (const char *)... |
+| char_console_fprintf_01_bad.c:49:21:49:24 | (const char *)... | semmle.label | (const char *)... |
+| char_console_fprintf_01_bad.c:49:21:49:24 | data | semmle.label | data |
+| char_console_fprintf_01_bad.c:49:21:49:24 | data indirection | semmle.label | data indirection |
+| char_console_fprintf_01_bad.c:49:21:49:24 | data indirection | semmle.label | data indirection |
+| char_environment_fprintf_01_bad.c:27:30:27:35 | call to getenv | semmle.label | call to getenv |
+| char_environment_fprintf_01_bad.c:27:30:27:35 | call to getenv | semmle.label | call to getenv |
+| char_environment_fprintf_01_bad.c:36:21:36:24 | (const char *)... | semmle.label | (const char *)... |
+| char_environment_fprintf_01_bad.c:36:21:36:24 | (const char *)... | semmle.label | (const char *)... |
+| char_environment_fprintf_01_bad.c:36:21:36:24 | data | semmle.label | data |
+| char_environment_fprintf_01_bad.c:36:21:36:24 | data indirection | semmle.label | data indirection |
+| char_environment_fprintf_01_bad.c:36:21:36:24 | data indirection | semmle.label | data indirection |
+#select
+| char_connect_socket_w32_vsnprintf_01_bad.c:125:15:125:18 | data | char_connect_socket_w32_vsnprintf_01_bad.c:94:55:94:68 | ... + ... | char_connect_socket_w32_vsnprintf_01_bad.c:125:15:125:18 | data | The value of this argument may come from $@ and is being used as a formatting argument to badVaSink(data), which calls vsnprintf(format) | char_connect_socket_w32_vsnprintf_01_bad.c:94:55:94:68 | ... + ... | recv |
+| char_console_fprintf_01_bad.c:49:21:49:24 | data | char_console_fprintf_01_bad.c:30:23:30:35 | ... + ... | char_console_fprintf_01_bad.c:49:21:49:24 | data | The value of this argument may come from $@ and is being used as a formatting argument to fprintf(format) | char_console_fprintf_01_bad.c:30:23:30:35 | ... + ... | fgets |
+| char_environment_fprintf_01_bad.c:36:21:36:24 | data | char_environment_fprintf_01_bad.c:27:30:27:35 | call to getenv | char_environment_fprintf_01_bad.c:36:21:36:24 | data | The value of this argument may come from $@ and is being used as a formatting argument to fprintf(format) | char_environment_fprintf_01_bad.c:27:30:27:35 | call to getenv | getenv |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-134/SAMATE/UncontrolledFormatString.qlref b/cpp/ql/test/query-tests/Security/CWE/CWE-134/SAMATE/UncontrolledFormatString.qlref
new file mode 100644
index 00000000000..079e0c8a7c0
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-134/SAMATE/UncontrolledFormatString.qlref
@@ -0,0 +1 @@
+Security/CWE/CWE-134/UncontrolledFormatString.ql
\ No newline at end of file
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-134/SAMATE/char_connect_socket_w32_vsnprintf_01_bad.c b/cpp/ql/test/query-tests/Security/CWE/CWE-134/SAMATE/char_connect_socket_w32_vsnprintf_01_bad.c
new file mode 100644
index 00000000000..ccba3e54840
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-134/SAMATE/char_connect_socket_w32_vsnprintf_01_bad.c
@@ -0,0 +1,126 @@
+// External test case from SAMATE's Juliet Test Suite for C/C++
+// (http://samate.nist.gov/SRD/testsuite.php)
+// Associated with CWE-134: Uncontrolled format string. http://cwe.mitre.org/data/definitions/134.html
+// This is a snippet with added declarations, not the entire test case.
+
+typedef unsigned long size_t;
+
+typedef void *va_list;
+#define va_start(ap, parmN)
+#define va_end(ap)
+#define va_arg(ap, type) ((type)0)
+
+int vsnprintf(char *s, size_t n, const char *format, va_list arg);
+
+size_t strlen(const char *s);
+
+#define SOCKET int
+#define INVALID_SOCKET (0)
+#define SOCKET_ERROR (1)
+#define AF_INET (2)
+#define SOCK_STREAM (3)
+#define IPPROTO_TCP (4)
+#define IP_ADDRESS (5)
+#define TCP_PORT (6)
+typedef int in_addr_t;
+struct in_addr {
+ in_addr_t s_addr;
+};
+struct sockaddr_in {
+ int sin_family;
+ int sin_port;
+ struct in_addr sin_addr;
+};
+in_addr_t inet_addr(const char *cp);
+
+#define IP_ADDRESS "0.0.0.0"
+
+void printLine(char *);
+
+static void badVaSink(char * data, ...)
+{
+ {
+ char dest[100] = "";
+ va_list args;
+ va_start(args, data);
+ /* POTENTIAL FLAW: Do not specify the format allowing a possible format string vulnerability */
+ vsnprintf(dest, 100-1, data, args);
+ va_end(args);
+ printLine(dest);
+ }
+}
+
+void CWE134_Uncontrolled_Format_String__char_connect_socket_w32_vsnprintf_01_bad()
+{
+ char * data;
+ char dataBuffer[100] = "";
+ data = dataBuffer;
+ {
+#ifdef _WIN32
+ WSADATA wsaData;
+ int wsaDataInit = 0;
+#endif
+ int recvResult;
+ struct sockaddr_in service;
+ char *replace;
+ SOCKET connectSocket = INVALID_SOCKET;
+ size_t dataLen = strlen(data);
+ do
+ {
+#ifdef _WIN32
+ if (WSAStartup(MAKEWORD(2,2), &wsaData) != NO_ERROR)
+ {
+ break;
+ }
+ wsaDataInit = 1;
+#endif
+ /* POTENTIAL FLAW: Read data using a connect socket */
+ connectSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (connectSocket == INVALID_SOCKET)
+ {
+ break;
+ }
+ memset(&service, 0, sizeof(service));
+ service.sin_family = AF_INET;
+ service.sin_addr.s_addr = inet_addr(IP_ADDRESS);
+ service.sin_port = htons(TCP_PORT);
+ if (connect(connectSocket, (struct sockaddr*)&service, sizeof(service)) == SOCKET_ERROR)
+ {
+ break;
+ }
+ /* Abort on error or the connection was closed, make sure to recv one
+ * less char than is in the recv_buf in order to append a terminator */
+ /* Abort on error or the connection was closed */
+ recvResult = recv(connectSocket, (char *)(data + dataLen), sizeof(char) * (100 - dataLen - 1), 0);
+ if (recvResult == SOCKET_ERROR || recvResult == 0)
+ {
+ break;
+ }
+ /* Append null terminator */
+ data[dataLen + recvResult / sizeof(char)] = '\0';
+ /* Eliminate CRLF */
+ replace = strchr(data, '\r');
+ if (replace)
+ {
+ *replace = '\0';
+ }
+ replace = strchr(data, '\n');
+ if (replace)
+ {
+ *replace = '\0';
+ }
+ }
+ while (0);
+ if (connectSocket != INVALID_SOCKET)
+ {
+ CLOSE_SOCKET(connectSocket);
+ }
+#ifdef _WIN32
+ if (wsaDataInit)
+ {
+ WSACleanup();
+ }
+#endif
+ }
+ badVaSink(data, data);
+}
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-134/SAMATE/char_console_fprintf_01_bad.c b/cpp/ql/test/query-tests/Security/CWE/CWE-134/SAMATE/char_console_fprintf_01_bad.c
new file mode 100644
index 00000000000..1af281b9f42
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-134/SAMATE/char_console_fprintf_01_bad.c
@@ -0,0 +1,97 @@
+// External test case from SAMATE's Juliet Test Suite for C/C++
+// (http://samate.nist.gov/SRD/testsuite.php)
+// Associated with CWE-134: Uncontrolled format string. http://cwe.mitre.org/data/definitions/134.html
+// Examples amended to have all function declarations inlined.
+
+#define NULL 0
+typedef unsigned long size_t;
+typedef struct {} FILE;
+extern FILE * stdin;
+extern FILE * stdout;
+size_t strlen(const char *s);
+char *fgets(char *s, int n, FILE *stream);
+int fprintf(FILE *stream, const char *format, ...);
+char *strcpy(char *s1, const char *s2);
+
+void printLine(char *);
+
+void CWE134_Uncontrolled_Format_String__char_console_fprintf_01_bad()
+{
+ char * data;
+ char data_buf[100] = "";
+ data = data_buf;
+ {
+ /* Read input from the console */
+ size_t data_len = strlen(data);
+ /* if there is room in data, read into it from the console */
+ /* POTENTIAL FLAW: Read data from the console */
+ if(100-data_len > 1)
+ {
+ if (fgets(data+data_len, (int)(100-data_len), stdin) != NULL)
+ {
+ /* The next 3 lines remove the carriage return from the string that is
+ * inserted by fgets() */
+ data_len = strlen(data);
+ if (data_len > 0 && data[data_len-1] == '\n')
+ {
+ data[data_len-1] = '\0';
+ }
+ }
+ else
+ {
+ printLine("fgets() failed");
+ /* Restore NUL terminator if fgets fails */
+ data[data_len] = '\0';
+ }
+ }
+ }
+ /* POTENTIAL FLAW: Do not specify the format allowing a possible format string vulnerability */
+ fprintf(stdout, data);
+}
+
+/* goodG2B uses the GoodSource with the BadSink */
+static void goodG2B()
+{
+ char * data;
+ char data_buf[100] = "";
+ data = data_buf;
+ /* FIX: Use a fixed string that does not contain a format specifier */
+ strcpy(data, "fixedstringtest");
+ /* POTENTIAL FLAW: Do not specify the format allowing a possible format string vulnerability */
+ fprintf(stdout, data);
+}
+
+/* goodB2G uses the BadSource with the GoodSink */
+static void goodB2G()
+{
+ char * data;
+ char data_buf[100] = "";
+ data = data_buf;
+ {
+ /* Read input from the console */
+ size_t data_len = strlen(data);
+ /* if there is room in data, read into it from the console */
+ /* POTENTIAL FLAW: Read data from the console */
+ if(100-data_len > 1)
+ {
+ if (fgets(data+data_len, (int)(100-data_len), stdin) != NULL)
+ {
+ /* The next 3 lines remove the carriage return from the string that is
+ * inserted by fgets() */
+ data_len = strlen(data);
+ if (data_len > 0 && data[data_len-1] == '\n')
+ {
+ data[data_len-1] = '\0';
+ }
+ }
+ else
+ {
+ printLine("fgets() failed");
+ /* Restore NUL terminator if fgets fails */
+ data[data_len] = '\0';
+ }
+ }
+ }
+ /* FIX: Specify the format disallowing a format string vulnerability */
+ fprintf(stdout, "%s\n", data);
+}
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-134/SAMATE/char_environment_fprintf_01_bad.c b/cpp/ql/test/query-tests/Security/CWE/CWE-134/SAMATE/char_environment_fprintf_01_bad.c
new file mode 100644
index 00000000000..f759a9d8e61
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-134/SAMATE/char_environment_fprintf_01_bad.c
@@ -0,0 +1,70 @@
+// External test case from SAMATE's Juliet Test Suite for C/C++
+// (http://samate.nist.gov/SRD/testsuite.php)
+// Associated with CWE-134: Uncontrolled format string. http://cwe.mitre.org/data/definitions/134.html
+// Examples amended to have all function declarations inlined.
+
+#define NULL 0
+typedef struct {} FILE;
+typedef unsigned long size_t;
+extern FILE * stdout;
+size_t strlen(const char *s);
+char *getenv(const char *name);
+char *strcpy(char *s1, const char *s2);
+char *strncat(char *s1, const char *s2, size_t n);
+int fprintf(FILE *stream, const char *format, ...);
+
+#define ENV_VARIABLE "ADD"
+#define GETENV getenv
+
+void CWE134_Uncontrolled_Format_String__char_environment_fprintf_01_bad()
+{
+ char * data;
+ char data_buf[100] = "";
+ data = data_buf;
+ {
+ /* Append input from an environment variable to data */
+ size_t data_len = strlen(data);
+ char * environment = GETENV(ENV_VARIABLE);
+ /* If there is data in the environment variable */
+ if (environment != NULL)
+ {
+ /* POTENTIAL FLAW: Read data from an environment variable */
+ strncat(data+data_len, environment, 100-data_len-1);
+ }
+ }
+ /* POTENTIAL FLAW: Do not specify the format allowing a possible format string vulnerability */
+ fprintf(stdout, data);
+}
+
+/* goodG2B uses the GoodSource with the BadSink */
+static void goodG2B()
+{
+ char * data;
+ char data_buf[100] = "";
+ data = data_buf;
+ /* FIX: Use a fixed string that does not contain a format specifier */
+ strcpy(data, "fixedstringtest");
+ /* POTENTIAL FLAW: Do not specify the format allowing a possible format string vulnerability */
+ fprintf(stdout, data);
+}
+
+/* goodB2G uses the BadSource with the GoodSink */
+static void goodB2G()
+{
+ char * data;
+ char data_buf[100] = "";
+ data = data_buf;
+ {
+ /* Append input from an environment variable to data */
+ size_t data_len = strlen(data);
+ char * environment = GETENV(ENV_VARIABLE);
+ /* If there is data in the environment variable */
+ if (environment != NULL)
+ {
+ /* POTENTIAL FLAW: Read data from an environment variable */
+ strncat(data+data_len, environment, 100-data_len-1);
+ }
+ }
+ /* FIX: Specify the format disallowing a format string vulnerability */
+ fprintf(stdout, "%s\n", data);
+}
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/argv/argvLocal.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/argv/argvLocal.expected
index b1cf6fbaf8d..a5a17967f69 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/argv/argvLocal.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/argv/argvLocal.expected
@@ -1,4 +1,5 @@
edges
+| argvLocal.c:9:25:9:31 | correct | argvLocal.c:9:25:9:31 | *correct |
| argvLocal.c:95:9:95:12 | argv | argvLocal.c:95:9:95:15 | (const char *)... |
| argvLocal.c:95:9:95:12 | argv | argvLocal.c:95:9:95:15 | (const char *)... |
| argvLocal.c:95:9:95:12 | argv | argvLocal.c:95:9:95:15 | access to array |
@@ -13,6 +14,7 @@ edges
| argvLocal.c:96:15:96:18 | argv | argvLocal.c:96:15:96:21 | access to array |
| argvLocal.c:96:15:96:18 | argv | argvLocal.c:96:15:96:21 | access to array indirection |
| argvLocal.c:96:15:96:18 | argv | argvLocal.c:96:15:96:21 | access to array indirection |
+| argvLocal.c:96:15:96:21 | access to array indirection | argvLocal.c:9:25:9:31 | *correct |
| argvLocal.c:100:7:100:10 | argv | argvLocal.c:101:9:101:10 | (const char *)... |
| argvLocal.c:100:7:100:10 | argv | argvLocal.c:101:9:101:10 | (const char *)... |
| argvLocal.c:100:7:100:10 | argv | argvLocal.c:101:9:101:10 | i1 |
@@ -41,6 +43,7 @@ edges
| argvLocal.c:100:7:100:10 | argv | argvLocal.c:145:15:145:16 | i7 |
| argvLocal.c:100:7:100:10 | argv | argvLocal.c:145:15:145:16 | i7 indirection |
| argvLocal.c:100:7:100:10 | argv | argvLocal.c:145:15:145:16 | i7 indirection |
+| argvLocal.c:102:15:102:16 | i1 indirection | argvLocal.c:9:25:9:31 | *correct |
| argvLocal.c:105:14:105:17 | argv | argvLocal.c:106:9:106:13 | (const char *)... |
| argvLocal.c:105:14:105:17 | argv | argvLocal.c:106:9:106:13 | (const char *)... |
| argvLocal.c:105:14:105:17 | argv | argvLocal.c:106:9:106:13 | access to array |
@@ -69,6 +72,8 @@ edges
| argvLocal.c:105:14:105:17 | argv | argvLocal.c:111:15:111:17 | * ... |
| argvLocal.c:105:14:105:17 | argv | argvLocal.c:111:15:111:17 | * ... indirection |
| argvLocal.c:105:14:105:17 | argv | argvLocal.c:111:15:111:17 | * ... indirection |
+| argvLocal.c:107:15:107:19 | access to array indirection | argvLocal.c:9:25:9:31 | *correct |
+| argvLocal.c:111:15:111:17 | * ... indirection | argvLocal.c:9:25:9:31 | *correct |
| argvLocal.c:115:13:115:16 | argv | argvLocal.c:116:9:116:10 | (const char *)... |
| argvLocal.c:115:13:115:16 | argv | argvLocal.c:116:9:116:10 | (const char *)... |
| argvLocal.c:115:13:115:16 | argv | argvLocal.c:116:9:116:10 | i3 |
@@ -113,7 +118,9 @@ edges
| argvLocal.c:115:13:115:16 | argv | argvLocal.c:136:15:136:18 | -- ... |
| argvLocal.c:115:13:115:16 | argv | argvLocal.c:136:15:136:18 | -- ... indirection |
| argvLocal.c:115:13:115:16 | argv | argvLocal.c:136:15:136:18 | -- ... indirection |
+| argvLocal.c:117:2:117:13 | i3 | argvLocal.c:9:25:9:31 | correct |
| argvLocal.c:117:2:117:13 | i3 | argvLocal.c:117:15:117:16 | printWrapper output argument |
+| argvLocal.c:117:15:117:16 | i3 indirection | argvLocal.c:9:25:9:31 | *correct |
| argvLocal.c:117:15:117:16 | i3 indirection | argvLocal.c:117:15:117:16 | printWrapper output argument |
| argvLocal.c:117:15:117:16 | printWrapper output argument | argvLocal.c:121:9:121:10 | (const char *)... |
| argvLocal.c:117:15:117:16 | printWrapper output argument | argvLocal.c:121:9:121:10 | i4 |
@@ -129,7 +136,9 @@ edges
| argvLocal.c:117:15:117:16 | printWrapper output argument | argvLocal.c:136:15:136:18 | -- ... |
| argvLocal.c:117:15:117:16 | printWrapper output argument | argvLocal.c:136:15:136:18 | -- ... |
| argvLocal.c:117:15:117:16 | printWrapper output argument | argvLocal.c:136:15:136:18 | -- ... indirection |
+| argvLocal.c:122:2:122:13 | i4 | argvLocal.c:9:25:9:31 | correct |
| argvLocal.c:122:2:122:13 | i4 | argvLocal.c:122:15:122:16 | printWrapper output argument |
+| argvLocal.c:122:15:122:16 | i4 indirection | argvLocal.c:9:25:9:31 | *correct |
| argvLocal.c:122:15:122:16 | i4 indirection | argvLocal.c:122:15:122:16 | printWrapper output argument |
| argvLocal.c:122:15:122:16 | printWrapper output argument | argvLocal.c:135:9:135:12 | (const char *)... |
| argvLocal.c:122:15:122:16 | printWrapper output argument | argvLocal.c:135:9:135:12 | ... ++ |
@@ -165,7 +174,9 @@ edges
| argvLocal.c:126:10:126:13 | argv | argvLocal.c:132:15:132:20 | ... + ... |
| argvLocal.c:126:10:126:13 | argv | argvLocal.c:132:15:132:20 | ... + ... indirection |
| argvLocal.c:126:10:126:13 | argv | argvLocal.c:132:15:132:20 | ... + ... indirection |
+| argvLocal.c:128:2:128:13 | i5 | argvLocal.c:9:25:9:31 | correct |
| argvLocal.c:128:2:128:13 | i5 | argvLocal.c:128:15:128:16 | printWrapper output argument |
+| argvLocal.c:128:15:128:16 | i5 indirection | argvLocal.c:9:25:9:31 | *correct |
| argvLocal.c:128:15:128:16 | i5 indirection | argvLocal.c:128:15:128:16 | printWrapper output argument |
| argvLocal.c:128:15:128:16 | printWrapper output argument | argvLocal.c:131:9:131:14 | (const char *)... |
| argvLocal.c:128:15:128:16 | printWrapper output argument | argvLocal.c:131:9:131:14 | ... + ... |
@@ -173,6 +184,9 @@ edges
| argvLocal.c:128:15:128:16 | printWrapper output argument | argvLocal.c:132:15:132:20 | ... + ... |
| argvLocal.c:128:15:128:16 | printWrapper output argument | argvLocal.c:132:15:132:20 | ... + ... |
| argvLocal.c:128:15:128:16 | printWrapper output argument | argvLocal.c:132:15:132:20 | ... + ... indirection |
+| argvLocal.c:132:15:132:20 | ... + ... indirection | argvLocal.c:9:25:9:31 | *correct |
+| argvLocal.c:136:15:136:18 | -- ... indirection | argvLocal.c:9:25:9:31 | *correct |
+| argvLocal.c:145:15:145:16 | i7 indirection | argvLocal.c:9:25:9:31 | *correct |
| argvLocal.c:149:11:149:14 | argv | argvLocal.c:150:9:150:10 | (const char *)... |
| argvLocal.c:149:11:149:14 | argv | argvLocal.c:150:9:150:10 | (const char *)... |
| argvLocal.c:149:11:149:14 | argv | argvLocal.c:150:9:150:10 | i8 |
@@ -187,6 +201,7 @@ edges
| argvLocal.c:149:11:149:14 | argv | argvLocal.c:151:15:151:16 | i8 |
| argvLocal.c:149:11:149:14 | argv | argvLocal.c:151:15:151:16 | i8 indirection |
| argvLocal.c:149:11:149:14 | argv | argvLocal.c:151:15:151:16 | i8 indirection |
+| argvLocal.c:151:15:151:16 | i8 indirection | argvLocal.c:9:25:9:31 | *correct |
| argvLocal.c:156:23:156:26 | argv | argvLocal.c:157:9:157:10 | (const char *)... |
| argvLocal.c:156:23:156:26 | argv | argvLocal.c:157:9:157:10 | (const char *)... |
| argvLocal.c:156:23:156:26 | argv | argvLocal.c:157:9:157:10 | i9 |
@@ -199,6 +214,7 @@ edges
| argvLocal.c:156:23:156:26 | argv | argvLocal.c:158:15:158:16 | i9 |
| argvLocal.c:156:23:156:26 | argv | argvLocal.c:158:15:158:16 | i9 indirection |
| argvLocal.c:156:23:156:26 | argv | argvLocal.c:158:15:158:16 | i9 indirection |
+| argvLocal.c:158:15:158:16 | i9 indirection | argvLocal.c:9:25:9:31 | *correct |
| argvLocal.c:163:22:163:25 | argv | argvLocal.c:164:9:164:11 | (const char *)... |
| argvLocal.c:163:22:163:25 | argv | argvLocal.c:164:9:164:11 | (const char *)... |
| argvLocal.c:163:22:163:25 | argv | argvLocal.c:164:9:164:11 | i91 |
@@ -211,6 +227,7 @@ edges
| argvLocal.c:163:22:163:25 | argv | argvLocal.c:165:15:165:17 | i91 |
| argvLocal.c:163:22:163:25 | argv | argvLocal.c:165:15:165:17 | i91 indirection |
| argvLocal.c:163:22:163:25 | argv | argvLocal.c:165:15:165:17 | i91 indirection |
+| argvLocal.c:165:15:165:17 | i91 indirection | argvLocal.c:9:25:9:31 | *correct |
| argvLocal.c:168:18:168:21 | argv | argvLocal.c:169:9:169:20 | (char *)... |
| argvLocal.c:168:18:168:21 | argv | argvLocal.c:169:9:169:20 | (char *)... |
| argvLocal.c:168:18:168:21 | argv | argvLocal.c:169:9:169:20 | (const char *)... |
@@ -229,6 +246,14 @@ edges
| argvLocal.c:168:18:168:21 | argv | argvLocal.c:170:24:170:26 | i10 |
| argvLocal.c:168:18:168:21 | argv | argvLocal.c:170:24:170:26 | i10 |
| argvLocal.c:168:18:168:21 | argv | argvLocal.c:170:24:170:26 | i10 |
+| argvLocal.c:170:15:170:26 | i10 indirection | argvLocal.c:9:25:9:31 | *correct |
+subpaths
+| argvLocal.c:117:2:117:13 | i3 | argvLocal.c:9:25:9:31 | correct | argvLocal.c:9:25:9:31 | *correct | argvLocal.c:117:15:117:16 | printWrapper output argument |
+| argvLocal.c:117:15:117:16 | i3 indirection | argvLocal.c:9:25:9:31 | *correct | argvLocal.c:9:25:9:31 | *correct | argvLocal.c:117:15:117:16 | printWrapper output argument |
+| argvLocal.c:122:2:122:13 | i4 | argvLocal.c:9:25:9:31 | correct | argvLocal.c:9:25:9:31 | *correct | argvLocal.c:122:15:122:16 | printWrapper output argument |
+| argvLocal.c:122:15:122:16 | i4 indirection | argvLocal.c:9:25:9:31 | *correct | argvLocal.c:9:25:9:31 | *correct | argvLocal.c:122:15:122:16 | printWrapper output argument |
+| argvLocal.c:128:2:128:13 | i5 | argvLocal.c:9:25:9:31 | correct | argvLocal.c:9:25:9:31 | *correct | argvLocal.c:128:15:128:16 | printWrapper output argument |
+| argvLocal.c:128:15:128:16 | i5 indirection | argvLocal.c:9:25:9:31 | *correct | argvLocal.c:9:25:9:31 | *correct | argvLocal.c:128:15:128:16 | printWrapper output argument |
nodes
| argvLocal.c:9:25:9:31 | *correct | semmle.label | *correct |
| argvLocal.c:9:25:9:31 | *correct | semmle.label | *correct |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/funcs/funcsLocal.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/funcs/funcsLocal.expected
index ceaf0489ec6..0f78d29fd36 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/funcs/funcsLocal.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/funcs/funcsLocal.expected
@@ -51,6 +51,7 @@ edges
| funcsLocal.c:41:18:41:20 | i61 | funcsLocal.c:42:9:42:10 | (const char *)... |
| funcsLocal.c:41:18:41:20 | i61 | funcsLocal.c:42:9:42:10 | i6 |
| funcsLocal.c:41:18:41:20 | i61 | funcsLocal.c:42:9:42:10 | i6 indirection |
+subpaths
nodes
| funcsLocal.c:16:8:16:9 | fread output argument | semmle.label | fread output argument |
| funcsLocal.c:16:8:16:9 | i1 | semmle.label | i1 |
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 58e3dda0964..7cefb7cfafc 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,3 +1,4 @@
edges
+subpaths
nodes
#select
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/globalVars/UncontrolledFormatStringThroughGlobalVar.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/globalVars/UncontrolledFormatStringThroughGlobalVar.expected
index f095153f39c..8d957ee499c 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/globalVars/UncontrolledFormatStringThroughGlobalVar.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/globalVars/UncontrolledFormatStringThroughGlobalVar.expected
@@ -46,6 +46,7 @@ edges
| 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
nodes
| globalVars.c:8:7:8:10 | copy | semmle.label | copy |
| globalVars.c:9:7:9:11 | copy2 | semmle.label | copy2 |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/ifs/ifs.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/ifs/ifs.expected
index 8c2c89035e2..2805eed6ad0 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/ifs/ifs.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/ifs/ifs.expected
@@ -71,6 +71,7 @@ edges
| ifs.c:123:8:123:11 | argv | ifs.c:124:9:124:10 | i9 |
| ifs.c:123:8:123:11 | argv | ifs.c:124:9:124:10 | i9 indirection |
| ifs.c:123:8:123:11 | argv | ifs.c:124:9:124:10 | i9 indirection |
+subpaths
nodes
| ifs.c:61:8:61:11 | argv | semmle.label | argv |
| ifs.c:61:8:61:11 | argv | semmle.label | argv |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/SAMATE/ArithmeticTainted.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-190/SAMATE/ArithmeticTainted.expected
new file mode 100644
index 00000000000..690f5ed6550
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/SAMATE/ArithmeticTainted.expected
@@ -0,0 +1,14 @@
+edges
+| examples.cpp:63:26:63:30 | & ... | examples.cpp:66:11:66:14 | data |
+| examples.cpp:63:26:63:30 | & ... | examples.cpp:66:11:66:14 | data |
+| examples.cpp:63:26:63:30 | fscanf output argument | examples.cpp:66:11:66:14 | data |
+| examples.cpp:63:26:63:30 | fscanf output argument | examples.cpp:66:11:66:14 | data |
+subpaths
+nodes
+| examples.cpp:63:26:63:30 | & ... | semmle.label | & ... |
+| examples.cpp:63:26:63:30 | fscanf output argument | semmle.label | fscanf output argument |
+| examples.cpp:66:11:66:14 | data | semmle.label | data |
+| examples.cpp:66:11:66:14 | data | semmle.label | data |
+| examples.cpp:66:11:66:14 | data | semmle.label | data |
+#select
+| examples.cpp:66:11:66:14 | data | examples.cpp:63:26:63:30 | & ... | examples.cpp:66:11:66:14 | data | $@ flows to here and is used in arithmetic, potentially causing an underflow. | examples.cpp:63:26:63:30 | & ... | User-provided value |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/SAMATE/ArithmeticTainted.qlref b/cpp/ql/test/query-tests/Security/CWE/CWE-190/SAMATE/ArithmeticTainted.qlref
new file mode 100644
index 00000000000..3939653db1c
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/SAMATE/ArithmeticTainted.qlref
@@ -0,0 +1 @@
+Security/CWE/CWE-190/ArithmeticTainted.ql
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/SAMATE/ArithmeticUncontrolled.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-190/SAMATE/ArithmeticUncontrolled.expected
new file mode 100644
index 00000000000..c908f8789c4
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/SAMATE/ArithmeticUncontrolled.expected
@@ -0,0 +1,78 @@
+edges
+| examples.cpp:22:26:22:33 | (unsigned int)... | examples.cpp:25:31:25:34 | data |
+| examples.cpp:22:26:22:33 | (unsigned int)... | examples.cpp:25:31:25:34 | data |
+| examples.cpp:22:26:22:33 | (unsigned int)... | examples.cpp:25:31:25:34 | data |
+| examples.cpp:22:26:22:33 | (unsigned int)... | examples.cpp:25:31:25:34 | data |
+| examples.cpp:22:26:22:33 | (unsigned int)... | examples.cpp:25:31:25:34 | data |
+| examples.cpp:22:26:22:33 | (unsigned int)... | examples.cpp:25:31:25:34 | data |
+| examples.cpp:22:26:22:33 | call to rand | examples.cpp:25:31:25:34 | data |
+| examples.cpp:22:26:22:33 | call to rand | examples.cpp:25:31:25:34 | data |
+| examples.cpp:22:26:22:33 | call to rand | examples.cpp:25:31:25:34 | data |
+| examples.cpp:22:26:22:33 | call to rand | examples.cpp:25:31:25:34 | data |
+| examples.cpp:22:26:22:33 | call to rand | examples.cpp:25:31:25:34 | data |
+| examples.cpp:22:26:22:33 | call to rand | examples.cpp:25:31:25:34 | data |
+| examples.cpp:35:26:35:33 | (unsigned int)... | examples.cpp:38:9:38:12 | data |
+| examples.cpp:35:26:35:33 | (unsigned int)... | examples.cpp:38:9:38:12 | data |
+| examples.cpp:35:26:35:33 | (unsigned int)... | examples.cpp:38:9:38:12 | data |
+| examples.cpp:35:26:35:33 | (unsigned int)... | examples.cpp:38:9:38:12 | data |
+| examples.cpp:35:26:35:33 | (unsigned int)... | examples.cpp:38:9:38:12 | data |
+| examples.cpp:35:26:35:33 | (unsigned int)... | examples.cpp:38:9:38:12 | data |
+| examples.cpp:35:26:35:33 | call to rand | examples.cpp:38:9:38:12 | data |
+| examples.cpp:35:26:35:33 | call to rand | examples.cpp:38:9:38:12 | data |
+| examples.cpp:35:26:35:33 | call to rand | examples.cpp:38:9:38:12 | data |
+| examples.cpp:35:26:35:33 | call to rand | examples.cpp:38:9:38:12 | data |
+| examples.cpp:35:26:35:33 | call to rand | examples.cpp:38:9:38:12 | data |
+| examples.cpp:35:26:35:33 | call to rand | examples.cpp:38:9:38:12 | data |
+nodes
+| examples.cpp:22:26:22:33 | (unsigned int)... | semmle.label | (unsigned int)... |
+| examples.cpp:22:26:22:33 | (unsigned int)... | semmle.label | (unsigned int)... |
+| examples.cpp:22:26:22:33 | (unsigned int)... | semmle.label | (unsigned int)... |
+| examples.cpp:22:26:22:33 | (unsigned int)... | semmle.label | (unsigned int)... |
+| examples.cpp:22:26:22:33 | (unsigned int)... | semmle.label | (unsigned int)... |
+| examples.cpp:22:26:22:33 | (unsigned int)... | semmle.label | (unsigned int)... |
+| examples.cpp:22:26:22:33 | call to rand | semmle.label | call to rand |
+| examples.cpp:22:26:22:33 | call to rand | semmle.label | call to rand |
+| examples.cpp:22:26:22:33 | call to rand | semmle.label | call to rand |
+| examples.cpp:22:26:22:33 | call to rand | semmle.label | call to rand |
+| examples.cpp:22:26:22:33 | call to rand | semmle.label | call to rand |
+| examples.cpp:22:26:22:33 | call to rand | semmle.label | call to rand |
+| examples.cpp:25:31:25:34 | data | semmle.label | data |
+| examples.cpp:35:26:35:33 | (unsigned int)... | semmle.label | (unsigned int)... |
+| examples.cpp:35:26:35:33 | (unsigned int)... | semmle.label | (unsigned int)... |
+| examples.cpp:35:26:35:33 | (unsigned int)... | semmle.label | (unsigned int)... |
+| examples.cpp:35:26:35:33 | (unsigned int)... | semmle.label | (unsigned int)... |
+| examples.cpp:35:26:35:33 | (unsigned int)... | semmle.label | (unsigned int)... |
+| examples.cpp:35:26:35:33 | (unsigned int)... | semmle.label | (unsigned int)... |
+| examples.cpp:35:26:35:33 | call to rand | semmle.label | call to rand |
+| examples.cpp:35:26:35:33 | call to rand | semmle.label | call to rand |
+| examples.cpp:35:26:35:33 | call to rand | semmle.label | call to rand |
+| examples.cpp:35:26:35:33 | call to rand | semmle.label | call to rand |
+| examples.cpp:35:26:35:33 | call to rand | semmle.label | call to rand |
+| examples.cpp:35:26:35:33 | call to rand | semmle.label | call to rand |
+| examples.cpp:38:9:38:12 | data | semmle.label | data |
+subpaths
+#select
+| examples.cpp:25:31:25:34 | data | examples.cpp:22:26:22:33 | (unsigned int)... | examples.cpp:25:31:25:34 | data | $@ flows to here and is used in arithmetic, potentially causing an underflow. | examples.cpp:22:26:22:33 | call to rand | Uncontrolled value |
+| examples.cpp:25:31:25:34 | data | examples.cpp:22:26:22:33 | (unsigned int)... | examples.cpp:25:31:25:34 | data | $@ flows to here and is used in arithmetic, potentially causing an underflow. | examples.cpp:22:26:22:33 | call to rand | Uncontrolled value |
+| examples.cpp:25:31:25:34 | data | examples.cpp:22:26:22:33 | (unsigned int)... | examples.cpp:25:31:25:34 | data | $@ flows to here and is used in arithmetic, potentially causing an underflow. | examples.cpp:22:26:22:33 | call to rand | Uncontrolled value |
+| examples.cpp:25:31:25:34 | data | examples.cpp:22:26:22:33 | (unsigned int)... | examples.cpp:25:31:25:34 | data | $@ flows to here and is used in arithmetic, potentially causing an underflow. | examples.cpp:22:26:22:33 | call to rand | Uncontrolled value |
+| examples.cpp:25:31:25:34 | data | examples.cpp:22:26:22:33 | (unsigned int)... | examples.cpp:25:31:25:34 | data | $@ flows to here and is used in arithmetic, potentially causing an underflow. | examples.cpp:22:26:22:33 | call to rand | Uncontrolled value |
+| examples.cpp:25:31:25:34 | data | examples.cpp:22:26:22:33 | (unsigned int)... | examples.cpp:25:31:25:34 | data | $@ flows to here and is used in arithmetic, potentially causing an underflow. | examples.cpp:22:26:22:33 | call to rand | Uncontrolled value |
+| examples.cpp:25:31:25:34 | data | examples.cpp:22:26:22:33 | call to rand | examples.cpp:25:31:25:34 | data | $@ flows to here and is used in arithmetic, potentially causing an underflow. | examples.cpp:22:26:22:33 | call to rand | Uncontrolled value |
+| examples.cpp:25:31:25:34 | data | examples.cpp:22:26:22:33 | call to rand | examples.cpp:25:31:25:34 | data | $@ flows to here and is used in arithmetic, potentially causing an underflow. | examples.cpp:22:26:22:33 | call to rand | Uncontrolled value |
+| examples.cpp:25:31:25:34 | data | examples.cpp:22:26:22:33 | call to rand | examples.cpp:25:31:25:34 | data | $@ flows to here and is used in arithmetic, potentially causing an underflow. | examples.cpp:22:26:22:33 | call to rand | Uncontrolled value |
+| examples.cpp:25:31:25:34 | data | examples.cpp:22:26:22:33 | call to rand | examples.cpp:25:31:25:34 | data | $@ flows to here and is used in arithmetic, potentially causing an underflow. | examples.cpp:22:26:22:33 | call to rand | Uncontrolled value |
+| examples.cpp:25:31:25:34 | data | examples.cpp:22:26:22:33 | call to rand | examples.cpp:25:31:25:34 | data | $@ flows to here and is used in arithmetic, potentially causing an underflow. | examples.cpp:22:26:22:33 | call to rand | Uncontrolled value |
+| examples.cpp:25:31:25:34 | data | examples.cpp:22:26:22:33 | call to rand | examples.cpp:25:31:25:34 | data | $@ flows to here and is used in arithmetic, potentially causing an underflow. | examples.cpp:22:26:22:33 | call to rand | Uncontrolled value |
+| examples.cpp:38:9:38:12 | data | examples.cpp:35:26:35:33 | (unsigned int)... | examples.cpp:38:9:38:12 | data | $@ flows to here and is used in arithmetic, potentially causing an underflow. | examples.cpp:35:26:35:33 | call to rand | Uncontrolled value |
+| examples.cpp:38:9:38:12 | data | examples.cpp:35:26:35:33 | (unsigned int)... | examples.cpp:38:9:38:12 | data | $@ flows to here and is used in arithmetic, potentially causing an underflow. | examples.cpp:35:26:35:33 | call to rand | Uncontrolled value |
+| examples.cpp:38:9:38:12 | data | examples.cpp:35:26:35:33 | (unsigned int)... | examples.cpp:38:9:38:12 | data | $@ flows to here and is used in arithmetic, potentially causing an underflow. | examples.cpp:35:26:35:33 | call to rand | Uncontrolled value |
+| examples.cpp:38:9:38:12 | data | examples.cpp:35:26:35:33 | (unsigned int)... | examples.cpp:38:9:38:12 | data | $@ flows to here and is used in arithmetic, potentially causing an underflow. | examples.cpp:35:26:35:33 | call to rand | Uncontrolled value |
+| examples.cpp:38:9:38:12 | data | examples.cpp:35:26:35:33 | (unsigned int)... | examples.cpp:38:9:38:12 | data | $@ flows to here and is used in arithmetic, potentially causing an underflow. | examples.cpp:35:26:35:33 | call to rand | Uncontrolled value |
+| examples.cpp:38:9:38:12 | data | examples.cpp:35:26:35:33 | (unsigned int)... | examples.cpp:38:9:38:12 | data | $@ flows to here and is used in arithmetic, potentially causing an underflow. | examples.cpp:35:26:35:33 | call to rand | Uncontrolled value |
+| examples.cpp:38:9:38:12 | data | examples.cpp:35:26:35:33 | call to rand | examples.cpp:38:9:38:12 | data | $@ flows to here and is used in arithmetic, potentially causing an underflow. | examples.cpp:35:26:35:33 | call to rand | Uncontrolled value |
+| examples.cpp:38:9:38:12 | data | examples.cpp:35:26:35:33 | call to rand | examples.cpp:38:9:38:12 | data | $@ flows to here and is used in arithmetic, potentially causing an underflow. | examples.cpp:35:26:35:33 | call to rand | Uncontrolled value |
+| examples.cpp:38:9:38:12 | data | examples.cpp:35:26:35:33 | call to rand | examples.cpp:38:9:38:12 | data | $@ flows to here and is used in arithmetic, potentially causing an underflow. | examples.cpp:35:26:35:33 | call to rand | Uncontrolled value |
+| examples.cpp:38:9:38:12 | data | examples.cpp:35:26:35:33 | call to rand | examples.cpp:38:9:38:12 | data | $@ flows to here and is used in arithmetic, potentially causing an underflow. | examples.cpp:35:26:35:33 | call to rand | Uncontrolled value |
+| examples.cpp:38:9:38:12 | data | examples.cpp:35:26:35:33 | call to rand | examples.cpp:38:9:38:12 | data | $@ flows to here and is used in arithmetic, potentially causing an underflow. | examples.cpp:35:26:35:33 | call to rand | Uncontrolled value |
+| examples.cpp:38:9:38:12 | data | examples.cpp:35:26:35:33 | call to rand | examples.cpp:38:9:38:12 | data | $@ flows to here and is used in arithmetic, potentially causing an underflow. | examples.cpp:35:26:35:33 | call to rand | Uncontrolled value |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/SAMATE/ArithmeticUncontrolled.qlref b/cpp/ql/test/query-tests/Security/CWE/CWE-190/SAMATE/ArithmeticUncontrolled.qlref
new file mode 100644
index 00000000000..1fcafc3ca1c
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/SAMATE/ArithmeticUncontrolled.qlref
@@ -0,0 +1 @@
+Security/CWE/CWE-190/ArithmeticUncontrolled.ql
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/SAMATE/ArithmeticWithExtremeValues.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-190/SAMATE/ArithmeticWithExtremeValues.expected
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/SAMATE/ArithmeticWithExtremeValues.qlref b/cpp/ql/test/query-tests/Security/CWE/CWE-190/SAMATE/ArithmeticWithExtremeValues.qlref
new file mode 100644
index 00000000000..ab2c35ce59b
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/SAMATE/ArithmeticWithExtremeValues.qlref
@@ -0,0 +1 @@
+Security/CWE/CWE-190/ArithmeticWithExtremeValues.ql
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/SAMATE/IntegerOverflowTainted.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-190/SAMATE/IntegerOverflowTainted.expected
new file mode 100644
index 00000000000..9d1fa5388ff
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/SAMATE/IntegerOverflowTainted.expected
@@ -0,0 +1 @@
+| examples.cpp:66:9:66:14 | -- ... | $@ flows to here and is used in an expression which might overflow negatively. | examples.cpp:63:26:63:30 | & ... | User-provided value |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/SAMATE/IntegerOverflowTainted.qlref b/cpp/ql/test/query-tests/Security/CWE/CWE-190/SAMATE/IntegerOverflowTainted.qlref
new file mode 100644
index 00000000000..df42008c632
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/SAMATE/IntegerOverflowTainted.qlref
@@ -0,0 +1 @@
+Security/CWE/CWE-190/IntegerOverflowTainted.ql
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/SAMATE/examples.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-190/SAMATE/examples.cpp
new file mode 100644
index 00000000000..b2cdbbe7133
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/SAMATE/examples.cpp
@@ -0,0 +1,70 @@
+// Some SAMATE Juliet test cases for rule CWE-190.
+
+///// Library functions //////
+
+typedef struct {} FILE;
+extern FILE *stdin;
+int fscanf(FILE *stream, const char *format, ...);
+int rand(void);
+
+#define URAND31() (((unsigned)rand()<<30) ^ ((unsigned)rand()<<15) ^ rand())
+#define RAND32() ((int)(rand() & 1 ? URAND31() : -URAND31() - 1))
+
+void printUnsignedLine(unsigned unsignedNumber);
+
+//// Test code /////
+
+void CWE191_Integer_Underflow__unsigned_int_rand_sub_01_bad()
+{
+ unsigned int data;
+ data = 0;
+ /* POTENTIAL FLAW: Use a random value */
+ data = (unsigned int)RAND32();
+ {
+ /* POTENTIAL FLAW: Subtracting 1 from data could cause an underflow */
+ unsigned int result = data - 1;
+ printUnsignedLine(result);
+ }
+}
+
+void CWE191_Integer_Underflow__unsigned_int_rand_postdec_01_bad()
+{
+ unsigned int data;
+ data = 0;
+ /* POTENTIAL FLAW: Use a random value */
+ data = (unsigned int)RAND32();
+ {
+ /* POTENTIAL FLAW: Decrementing data could cause an underflow */
+ data--;
+ unsigned int result = data;
+ printUnsignedLine(result);
+ }
+}
+
+void CWE191_Integer_Underflow__unsigned_int_min_postdec_01_bad()
+{
+ unsigned int data;
+ data = 0;
+ /* POTENTIAL FLAW: Use the minimum size of the data type */
+ data = 0;
+ {
+ /* POTENTIAL FLAW: Decrementing data could cause an underflow [NOT DETECTED] */
+ data--;
+ unsigned int result = data;
+ printUnsignedLine(result);
+ }
+}
+
+void CWE191_Integer_Underflow__unsigned_int_fscanf_predec_01_bad()
+{
+ unsigned int data;
+ data = 0;
+ /* POTENTIAL FLAW: Use a value input from the console */
+ fscanf (stdin, "%u", &data);
+ {
+ /* POTENTIAL FLAW: Decrementing data could cause an underflow */
+ --data;
+ unsigned int result = data;
+ printUnsignedLine(result);
+ }
+}
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/ArithmeticUncontrolled/ArithmeticUncontrolled.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/ArithmeticUncontrolled/ArithmeticUncontrolled.expected
index 11fb721c987..8f7f3aa2efa 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/ArithmeticUncontrolled/ArithmeticUncontrolled.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/ArithmeticUncontrolled/ArithmeticUncontrolled.expected
@@ -105,6 +105,7 @@ nodes
| test.cpp:223:20:223:23 | call to rand | semmle.label | call to rand |
| test.cpp:223:20:223:25 | (unsigned int)... | semmle.label | (unsigned int)... |
| test.cpp:227:8:227:8 | x | semmle.label | x |
+subpaths
#select
| test.c:21:17:21:17 | r | test.c:18:13:18:16 | call to rand | test.c:21:17:21:17 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:18:13:18:16 | call to rand | Uncontrolled value |
| test.c:35:5:35:5 | r | test.c:34:13:34:18 | call to rand | test.c:35:5:35:5 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:34:13:34:18 | call to rand | Uncontrolled value |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/TaintedAllocationSize.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/TaintedAllocationSize.expected
index 694f46e1d26..7fe8651f89a 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/TaintedAllocationSize.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/TaintedAllocationSize.expected
@@ -73,6 +73,7 @@ edges
| test.cpp:305:18:305:21 | Chi | test.cpp:308:10:308:27 | ... * ... |
| test.cpp:305:18:305:21 | Chi | test.cpp:308:10:308:27 | ... * ... |
| test.cpp:305:18:305:21 | get_size output argument [[]] | test.cpp:305:18:305:21 | Chi |
+subpaths
nodes
| test.cpp:40:21:40:24 | argv | semmle.label | argv |
| test.cpp:40:21:40:24 | argv | semmle.label | argv |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/ArithmeticTainted.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/ArithmeticTainted.expected
index e2fefe4a442..e0f72bffd44 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/ArithmeticTainted.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/ArithmeticTainted.expected
@@ -23,6 +23,7 @@ edges
| test.c:51:17:51:20 | argv | test.c:54:7:54:10 | len3 |
| test.c:51:17:51:20 | argv | test.c:54:7:54:10 | len3 |
| test.c:51:17:51:20 | argv | test.c:54:7:54:10 | len3 |
+subpaths
nodes
| test2.cpp:12:21:12:21 | v | semmle.label | v |
| test2.cpp:14:11:14:11 | v | semmle.label | v |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-197/SAMATE/IntegerOverflowTainted/IntegerOverflowTainted.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-197/SAMATE/IntegerOverflowTainted/IntegerOverflowTainted.expected
new file mode 100644
index 00000000000..88b0b206cda
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-197/SAMATE/IntegerOverflowTainted/IntegerOverflowTainted.expected
@@ -0,0 +1 @@
+| tests.cpp:38:31:38:34 | data | $@ flows to here and is used in an expression which might overflow. | tests.cpp:57:27:57:31 | & ... | User-provided value |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-197/SAMATE/IntegerOverflowTainted/IntegerOverflowTainted.qlref b/cpp/ql/test/query-tests/Security/CWE/CWE-197/SAMATE/IntegerOverflowTainted/IntegerOverflowTainted.qlref
new file mode 100644
index 00000000000..72ed7d53685
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-197/SAMATE/IntegerOverflowTainted/IntegerOverflowTainted.qlref
@@ -0,0 +1 @@
+Security/CWE/CWE-190/IntegerOverflowTainted.ql
\ No newline at end of file
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-197/SAMATE/IntegerOverflowTainted/tests.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-197/SAMATE/IntegerOverflowTainted/tests.cpp
new file mode 100644
index 00000000000..79f9a79c97f
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-197/SAMATE/IntegerOverflowTainted/tests.cpp
@@ -0,0 +1,74 @@
+//semmle-extractor-options: --edg --target --edg win64
+
+// A selection of tests from the SAMATE Juliet framework for rule CWE-197.
+
+// library types, functions etc
+typedef struct {} FILE;
+int fscanf(FILE *stream, const char *format, ...);
+#define CHAR_MAX (127)
+FILE *stdin;
+
+void printHexCharLine(char charHex);
+
+// ----------
+
+class CWE197_Numeric_Truncation_Error__short_fscanf_82_base
+{
+public:
+ /* pure virtual function */
+ virtual void action(short data) = 0;
+};
+
+class CWE197_Numeric_Truncation_Error__short_fscanf_82_bad : public CWE197_Numeric_Truncation_Error__short_fscanf_82_base
+{
+public:
+ void action(short data);
+};
+
+class CWE197_Numeric_Truncation_Error__short_fscanf_82_goodG2B : public CWE197_Numeric_Truncation_Error__short_fscanf_82_base
+{
+public:
+ void action(short data);
+};
+
+void CWE197_Numeric_Truncation_Error__short_fscanf_82_bad::action(short data)
+{
+ {
+ /* POTENTIAL FLAW: Convert data to a char, possibly causing a truncation error */
+ char charData = (char)data;
+ printHexCharLine(charData);
+ }
+}
+
+void CWE197_Numeric_Truncation_Error__short_fscanf_82_goodG2B::action(short data)
+{
+ {
+ char charData = (char)data;
+ printHexCharLine(charData);
+ }
+}
+
+void bad()
+{
+ short data;
+ /* Initialize data */
+ data = -1;
+ /* FLAW: Use a number input from the console using fscanf() */
+ fscanf (stdin, "%hd", &data);
+ CWE197_Numeric_Truncation_Error__short_fscanf_82_base* baseObject = new CWE197_Numeric_Truncation_Error__short_fscanf_82_bad;
+ baseObject->action(data);
+ delete baseObject;
+}
+
+/* goodG2B uses the GoodSource with the BadSink */
+static void goodG2B()
+{
+ short data;
+ /* Initialize data */
+ data = -1;
+ /* FIX: Use a positive integer less than CHAR_MAX*/
+ data = CHAR_MAX-5;
+ CWE197_Numeric_Truncation_Error__short_fscanf_82_base* baseObject = new CWE197_Numeric_Truncation_Error__short_fscanf_82_goodG2B;
+ baseObject->action(data);
+ delete baseObject;
+}
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-290/semmle/AuthenticationBypass/AuthenticationBypass.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-290/semmle/AuthenticationBypass/AuthenticationBypass.expected
index 1ad31c3f9f7..360ced04144 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-290/semmle/AuthenticationBypass/AuthenticationBypass.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-290/semmle/AuthenticationBypass/AuthenticationBypass.expected
@@ -17,6 +17,7 @@ edges
| test.cpp:38:25:38:42 | (const char *)... | test.cpp:42:14:42:20 | address |
| test.cpp:38:25:38:42 | (const char *)... | test.cpp:42:14:42:20 | address |
| test.cpp:38:25:38:42 | (const char *)... | test.cpp:42:14:42:20 | address indirection |
+subpaths
nodes
| test.cpp:16:25:16:30 | call to getenv | semmle.label | call to getenv |
| test.cpp:16:25:16:42 | (const char *)... | semmle.label | (const char *)... |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-311/semmle/tests/CleartextBufferWrite.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-311/semmle/tests/CleartextBufferWrite.expected
index e4f9bc918a4..400c49237ca 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-311/semmle/tests/CleartextBufferWrite.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-311/semmle/tests/CleartextBufferWrite.expected
@@ -5,6 +5,7 @@ edges
| test.cpp:54:17:54:20 | argv | test.cpp:58:25:58:29 | input |
| test.cpp:54:17:54:20 | argv | test.cpp:58:25:58:29 | input indirection |
| test.cpp:54:17:54:20 | argv | test.cpp:58:25:58:29 | input indirection |
+subpaths
nodes
| test.cpp:54:17:54:20 | argv | semmle.label | argv |
| test.cpp:54:17:54:20 | argv | semmle.label | argv |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-311/semmle/tests/CleartextTransmission.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-311/semmle/tests/CleartextTransmission.expected
new file mode 100644
index 00000000000..3534c2c7cad
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-311/semmle/tests/CleartextTransmission.expected
@@ -0,0 +1,49 @@
+edges
+| test3.cpp:68:21:68:29 | password1 | test3.cpp:70:15:70:17 | ptr |
+| test3.cpp:75:15:75:22 | password | test3.cpp:77:15:77:17 | ptr |
+| test3.cpp:106:20:106:25 | buffer | test3.cpp:108:14:108:19 | buffer |
+| test3.cpp:111:28:111:33 | buffer | test3.cpp:113:9:113:14 | buffer |
+| test3.cpp:120:9:120:23 | global_password | test3.cpp:138:16:138:29 | call to get_global_str |
+| test3.cpp:128:11:128:18 | password | test3.cpp:106:20:106:25 | buffer |
+| test3.cpp:132:21:132:22 | call to id | test3.cpp:134:15:134:17 | ptr |
+| test3.cpp:132:24:132:32 | password1 | test3.cpp:111:28:111:33 | buffer |
+| test3.cpp:132:24:132:32 | password1 | test3.cpp:132:21:132:22 | call to id |
+| test3.cpp:138:16:138:29 | call to get_global_str | test3.cpp:140:15:140:18 | data |
+| test3.cpp:151:19:151:26 | password | test3.cpp:153:15:153:20 | buffer |
+nodes
+| test3.cpp:20:15:20:23 | password1 | semmle.label | password1 |
+| test3.cpp:24:15:24:23 | password2 | semmle.label | password2 |
+| test3.cpp:41:15:41:22 | password | semmle.label | password |
+| test3.cpp:49:15:49:22 | password | semmle.label | password |
+| test3.cpp:68:21:68:29 | password1 | semmle.label | password1 |
+| test3.cpp:70:15:70:17 | ptr | semmle.label | ptr |
+| test3.cpp:75:15:75:22 | password | semmle.label | password |
+| test3.cpp:77:15:77:17 | ptr | semmle.label | ptr |
+| test3.cpp:95:12:95:19 | password | semmle.label | password |
+| test3.cpp:106:20:106:25 | buffer | semmle.label | buffer |
+| test3.cpp:108:14:108:19 | buffer | semmle.label | buffer |
+| test3.cpp:111:28:111:33 | buffer | semmle.label | buffer |
+| test3.cpp:113:9:113:14 | buffer | semmle.label | buffer |
+| test3.cpp:120:9:120:23 | global_password | semmle.label | global_password |
+| test3.cpp:128:11:128:18 | password | semmle.label | password |
+| test3.cpp:132:21:132:22 | call to id | semmle.label | call to id |
+| test3.cpp:132:24:132:32 | password1 | semmle.label | password1 |
+| test3.cpp:134:15:134:17 | ptr | semmle.label | ptr |
+| test3.cpp:138:16:138:29 | call to get_global_str | semmle.label | call to get_global_str |
+| test3.cpp:140:15:140:18 | data | semmle.label | data |
+| test3.cpp:151:19:151:26 | password | semmle.label | password |
+| test3.cpp:153:15:153:20 | buffer | semmle.label | buffer |
+subpaths
+| test3.cpp:132:24:132:32 | password1 | test3.cpp:111:28:111:33 | buffer | test3.cpp:113:9:113:14 | buffer | test3.cpp:132:21:132:22 | call to id |
+#select
+| test3.cpp:20:3:20:6 | call to send | test3.cpp:20:15:20:23 | password1 | test3.cpp:20:15:20:23 | password1 | This operation transmits 'password1', which may contain unencrypted sensitive data from $@ | test3.cpp:20:15:20:23 | password1 | password1 |
+| test3.cpp:24:3:24:6 | call to send | test3.cpp:24:15:24:23 | password2 | test3.cpp:24:15:24:23 | password2 | This operation transmits 'password2', which may contain unencrypted sensitive data from $@ | test3.cpp:24:15:24:23 | password2 | password2 |
+| test3.cpp:41:3:41:6 | call to recv | test3.cpp:41:15:41:22 | password | test3.cpp:41:15:41:22 | password | This operation receives into 'password', which may put unencrypted sensitive data into $@ | test3.cpp:41:15:41:22 | password | password |
+| test3.cpp:49:3:49:6 | call to recv | test3.cpp:49:15:49:22 | password | test3.cpp:49:15:49:22 | password | This operation receives into 'password', which may put unencrypted sensitive data into $@ | test3.cpp:49:15:49:22 | password | password |
+| test3.cpp:70:3:70:6 | call to send | test3.cpp:68:21:68:29 | password1 | test3.cpp:70:15:70:17 | ptr | This operation transmits 'ptr', which may contain unencrypted sensitive data from $@ | test3.cpp:68:21:68:29 | password1 | password1 |
+| test3.cpp:77:3:77:6 | call to recv | test3.cpp:75:15:75:22 | password | test3.cpp:77:15:77:17 | ptr | This operation receives into 'ptr', which may put unencrypted sensitive data into $@ | test3.cpp:75:15:75:22 | password | password |
+| test3.cpp:95:3:95:6 | call to read | test3.cpp:95:12:95:19 | password | test3.cpp:95:12:95:19 | password | This operation receives into 'password', which may put unencrypted sensitive data into $@ | test3.cpp:95:12:95:19 | password | password |
+| test3.cpp:108:2:108:5 | call to recv | test3.cpp:128:11:128:18 | password | test3.cpp:108:14:108:19 | buffer | This operation receives into 'buffer', which may put unencrypted sensitive data into $@ | test3.cpp:128:11:128:18 | password | password |
+| test3.cpp:134:3:134:6 | call to send | test3.cpp:132:24:132:32 | password1 | test3.cpp:134:15:134:17 | ptr | This operation transmits 'ptr', which may contain unencrypted sensitive data from $@ | test3.cpp:132:24:132:32 | password1 | password1 |
+| test3.cpp:140:3:140:6 | call to send | test3.cpp:120:9:120:23 | global_password | test3.cpp:140:15:140:18 | data | This operation transmits 'data', which may contain unencrypted sensitive data from $@ | test3.cpp:120:9:120:23 | global_password | global_password |
+| test3.cpp:153:3:153:6 | call to send | test3.cpp:151:19:151:26 | password | test3.cpp:153:15:153:20 | buffer | This operation transmits 'buffer', which may contain unencrypted sensitive data from $@ | test3.cpp:151:19:151:26 | password | password |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-311/semmle/tests/CleartextTransmission.qlref b/cpp/ql/test/query-tests/Security/CWE/CWE-311/semmle/tests/CleartextTransmission.qlref
new file mode 100644
index 00000000000..bb3fc66f1f1
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-311/semmle/tests/CleartextTransmission.qlref
@@ -0,0 +1 @@
+Security/CWE/CWE-311/CleartextTransmission.ql
\ No newline at end of file
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-311/semmle/tests/test3.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-311/semmle/tests/test3.cpp
new file mode 100644
index 00000000000..010ed2c8062
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-311/semmle/tests/test3.cpp
@@ -0,0 +1,155 @@
+
+typedef unsigned long size_t;
+#define STDIN_FILENO (0)
+
+size_t strlen(const char *s);
+
+void send(int fd, const void *buf, size_t bufLen, int d);
+void recv(int fd, void *buf, size_t bufLen, int d);
+void read(int fd, void *buf, size_t bufLen);
+
+void LogonUserA(int a, int b, const char *password, int d, int e, int f);
+
+int val();
+
+void test_send(const char *password1, const char *password2, const char *password_hash, const char *message)
+{
+ {
+ LogonUserA(val(), val(), password1, val(), val(), val()); // proof `password1` is plaintext
+
+ send(val(), password1, strlen(password1), val()); // BAD: `password1` is sent plaintext (certainly)
+ }
+
+ {
+ send(val(), password2, strlen(password2), val()); // BAD: `password2` is sent plaintext (probably)
+ }
+
+ {
+ send(val(), password_hash, strlen(password_hash), val()); // GOOD: `password_hash` is sent encrypted
+ }
+
+ {
+ send(val(), message, strlen(message), val()); // GOOD: `message` is not a password
+ }
+}
+
+void test_receive()
+{
+ {
+ char password[256];
+
+ recv(val(), password, 256, val()); // BAD: `password` is received plaintext (certainly)
+
+ LogonUserA(val(), val(), password, val(), val(), val()); // (proof `password` is plaintext)
+ }
+
+ {
+ char password[256];
+
+ recv(val(), password, 256, val()); // BAD: `password` is received plaintext (probably)
+ }
+
+ {
+ char password_hash[256];
+
+ recv(val(), password_hash, 256, val()); // GOOD: `password` is received encrypted
+ }
+
+ {
+ char message[256];
+
+ recv(val(), message, 256, val()); // GOOD: `message` is not a password
+ }
+}
+
+void test_dataflow(const char *password1)
+{
+ {
+ const char *ptr = password1;
+
+ send(val(), ptr, strlen(ptr), val()); // BAD: `password` is sent plaintext
+ }
+
+ {
+ char password[256];
+ char *ptr = password;
+
+ recv(val(), ptr, 256, val()); // BAD: `password` is received plaintext
+ }
+
+ {
+ char buffer[256];
+
+ recv(val(), buffer, 256, val()); // BAD: `password` is received plaintext [NOT DETECTED]
+
+ char *password = buffer;
+ }
+}
+
+void test_read()
+{
+ {
+ char password[256];
+ int fd = val();
+
+ read(fd, password, 256); // BAD: `password` is received plaintext
+ }
+
+ {
+ char password[256];
+ int fd = STDIN_FILENO;
+
+ read(fd, password, 256); // GOOD: `password` is received from stdin, not a network socket
+ }
+}
+
+void my_recv(char *buffer, size_t bufferSize)
+{
+ recv(val(), buffer, bufferSize, val());
+}
+
+const char *id(const char *buffer)
+{
+ return buffer;
+}
+
+char *global_password;
+
+char *get_global_str()
+{
+ return global_password;
+}
+
+void test_interprocedural(const char *password1)
+{
+ {
+ char password[256];
+
+ my_recv(password, 256); // BAD: `password` is received plaintext [detected on line 108]
+ }
+
+ {
+ const char *ptr = id(password1);
+
+ send(val(), ptr, strlen(ptr), val()); // BAD: `password1` is sent plaintext
+ }
+
+ {
+ char *data = get_global_str();
+
+ send(val(), data, strlen(data), val()); // BAD: `global_password` is sent plaintext
+ }
+}
+
+char *strncpy(char *s1, const char *s2, size_t n);
+
+void test_taint(const char *password)
+{
+ {
+ char buffer[16];
+
+ strncpy(buffer, password, 16);
+ buffer[15] = 0;
+ send(val(), buffer, 16, val()); // BAD: `password` is (partially) sent plaintext
+ }
+}
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-497/SAMATE/ExposedSystemData.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-497/SAMATE/ExposedSystemData.expected
new file mode 100644
index 00000000000..ffd6f77205e
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-497/SAMATE/ExposedSystemData.expected
@@ -0,0 +1 @@
+| tests.c:70:9:70:15 | call to fprintf | This operation exposes system data from $@. | tests.c:54:13:54:22 | call to LogonUserA | call to LogonUserA |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-497/SAMATE/ExposedSystemData.qlref b/cpp/ql/test/query-tests/Security/CWE/CWE-497/SAMATE/ExposedSystemData.qlref
new file mode 100644
index 00000000000..0c88835bf1f
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-497/SAMATE/ExposedSystemData.qlref
@@ -0,0 +1 @@
+Security/CWE/CWE-497/ExposedSystemData.ql
\ No newline at end of file
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-497/SAMATE/OutputWrite.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-497/SAMATE/OutputWrite.expected
new file mode 100644
index 00000000000..fe7e5b34c77
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-497/SAMATE/OutputWrite.expected
@@ -0,0 +1,7 @@
+| tests.c:29:9:29:14 | call to printf | tests.c:29:16:29:21 | %s\n |
+| tests.c:29:9:29:14 | call to printf | tests.c:29:24:29:27 | line |
+| tests.c:43:13:43:21 | call to printLine | tests.c:43:23:43:38 | fgets() failed |
+| tests.c:62:13:62:21 | call to printLine | tests.c:62:23:62:52 | User logged in successfully. |
+| tests.c:67:13:67:21 | call to printLine | tests.c:67:23:67:40 | Unable to login. |
+| tests.c:70:9:70:15 | call to fprintf | tests.c:70:25:70:67 | User attempted access with password: %s\n |
+| tests.c:70:9:70:15 | call to fprintf | tests.c:70:70:70:77 | password |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-497/SAMATE/OutputWrite.ql b/cpp/ql/test/query-tests/Security/CWE/CWE-497/SAMATE/OutputWrite.ql
new file mode 100644
index 00000000000..d9e67a944e9
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-497/SAMATE/OutputWrite.ql
@@ -0,0 +1,4 @@
+import semmle.code.cpp.security.OutputWrite
+
+from OutputWrite ow
+select ow, ow.getASource()
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-497/SAMATE/tests.c b/cpp/ql/test/query-tests/Security/CWE/CWE-497/SAMATE/tests.c
new file mode 100644
index 00000000000..4b1df2a96e1
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-497/SAMATE/tests.c
@@ -0,0 +1,72 @@
+// SAMATE Juliet test cases for rule CWE-497.
+
+// library functions etc
+typedef struct {} FILE;
+
+// define stdout, stderr in a similar style to MinGW
+FILE std_files[2];
+#define stdin (&std_files[0])
+#define stderr (&std_files[1])
+
+typedef unsigned long size_t;
+size_t strlen(const char *s);
+int printf(const char *format, ...);
+int fprintf(FILE *stream, const char *format, ...);
+char *fgets(char *s, int n, FILE *stream);
+
+typedef struct {} *HANDLE;
+int LogonUserA(const char *lpszUserName, const char *lpszDomain, const char *lpszPassword, int dwLogonType, int dwLogonProvider, HANDLE *phToken);
+void CloseHandle(HANDLE h);
+
+#define NULL (0)
+#define LOGON32_LOGON_NETWORK (1)
+#define LOGON32_PROVIDER_DEFAULT (2)
+
+void printLine(const char * line)
+{
+ if(line != NULL)
+ {
+ printf("%s\n", line);
+ }
+}
+
+void CWE535_Info_Exposure_Shell_Error__w32_char_01_bad()
+{
+ {
+ char password[100] = "";
+ size_t passwordLen = 0;
+ HANDLE pHandle;
+ char * username = "User";
+ char * domain = "Domain";
+ if (fgets(password, 100, stdin) == NULL)
+ {
+ printLine("fgets() failed");
+ /* Restore NUL terminator if fgets fails */
+ password[0] = '\0';
+ }
+ /* Remove the carriage return from the string that is inserted by fgets() */
+ passwordLen = strlen(password);
+ if (passwordLen > 0)
+ {
+ password[passwordLen-1] = '\0';
+ }
+ /* Use the password in LogonUser() to establish that it is "sensitive" */
+ if (LogonUserA(
+ username,
+ domain,
+ password,
+ LOGON32_LOGON_NETWORK,
+ LOGON32_PROVIDER_DEFAULT,
+ &pHandle) != 0)
+ {
+ printLine("User logged in successfully.");
+ CloseHandle(pHandle);
+ }
+ else
+ {
+ printLine("Unable to login.");
+ }
+ /* FLAW: Write sensitive data to stderr */
+ fprintf(stderr, "User attempted access with password: %s\n", password);
+ }
+}
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-675/semmle/tests/DoubleRelease.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-675/semmle/tests/DoubleRelease.expected
new file mode 100644
index 00000000000..8bed519ab4a
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-675/semmle/tests/DoubleRelease.expected
@@ -0,0 +1,3 @@
+| test.cpp:20:3:20:8 | call to fclose | Second call to the $@ function is possible. | test.cpp:21:3:21:8 | call to fclose | fclose |
+| test.cpp:31:3:31:8 | call to fclose | Second call to the $@ function is possible. | test.cpp:32:3:32:8 | call to fclose | fclose |
+| test.cpp:38:3:38:8 | call to fclose | Second call to the $@ function is possible. | test.cpp:44:3:44:8 | call to fclose | fclose |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-675/semmle/tests/DoubleRelease.qlref b/cpp/ql/test/query-tests/Security/CWE/CWE-675/semmle/tests/DoubleRelease.qlref
new file mode 100644
index 00000000000..3edd226abaa
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-675/semmle/tests/DoubleRelease.qlref
@@ -0,0 +1 @@
+experimental/Security/CWE/CWE-675/DoubleRelease.ql
\ No newline at end of file
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-675/semmle/tests/test.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-675/semmle/tests/test.cpp
new file mode 100644
index 00000000000..986a95b1ce9
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-675/semmle/tests/test.cpp
@@ -0,0 +1,83 @@
+#define NULL (0)
+typedef int FILE;
+FILE *fopen(const char *filename, const char *mode);
+int fclose(FILE *stream);
+extern FILE * fe;
+void test1()
+{
+ FILE *f;
+
+ f = fopen("myFile.txt", "wt");
+ fclose(f); // GOOD
+ f = NULL;
+}
+
+void test2()
+{
+ FILE *f;
+
+ f = fopen("myFile.txt", "wt");
+ fclose(f); // BAD
+ fclose(f);
+}
+
+void test3()
+{
+ FILE *f;
+ FILE *g;
+
+ f = fopen("myFile.txt", "wt");
+ g = f;
+ fclose(f); // BAD
+ fclose(g);
+}
+
+int fGtest4_1()
+{
+ fe = fopen("myFile.txt", "wt");
+ fclose(fe); // BAD
+ return -1;
+}
+
+int fGtest4_2()
+{
+ fclose(fe);
+ return -1;
+}
+
+void Gtest4()
+{
+ fGtest4_1();
+ fGtest4_2();
+}
+
+int fGtest5_1()
+{
+ fe = fopen("myFile.txt", "wt");
+ fclose(fe); // GOOD
+ fe = NULL;
+ return -1;
+}
+
+int fGtest5_2()
+{
+ fclose(fe);
+ return -1;
+}
+
+void Gtest5()
+{
+ fGtest5_1();
+ fGtest5_2();
+}
+
+int main(int argc, char *argv[])
+{
+ test1();
+ test2();
+ test3();
+
+ Gtest4();
+ Gtest5();
+ return 0;
+}
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-676/SAMATE/DangerousUseOfCin/DangerousUseOfCin.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-676/SAMATE/DangerousUseOfCin/DangerousUseOfCin.expected
new file mode 100644
index 00000000000..f5c0b85e28f
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-676/SAMATE/DangerousUseOfCin/DangerousUseOfCin.expected
@@ -0,0 +1 @@
+| test.cpp:59:17:59:17 | call to operator>> | Use of 'cin' without specifying the length of the input may be dangerous. |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-676/SAMATE/DangerousUseOfCin/DangerousUseOfCin.qlref b/cpp/ql/test/query-tests/Security/CWE/CWE-676/SAMATE/DangerousUseOfCin/DangerousUseOfCin.qlref
new file mode 100644
index 00000000000..5a35bf81fd9
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-676/SAMATE/DangerousUseOfCin/DangerousUseOfCin.qlref
@@ -0,0 +1 @@
+Security/CWE/CWE-676/DangerousUseOfCin.ql
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-676/SAMATE/DangerousUseOfCin/test.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-676/SAMATE/DangerousUseOfCin/test.cpp
new file mode 100644
index 00000000000..704c2a87b3f
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-676/SAMATE/DangerousUseOfCin/test.cpp
@@ -0,0 +1,81 @@
+// SAMATE Juliet test case for rule DangerousUseOfCin.ql / CWE-676.
+
+// --- library types, functions etc ---
+
+typedef unsigned long size_t;
+
+namespace std
+{
+ // --- std::istream ---
+
+ // std::char_traits
+ template class char_traits;
+
+ typedef size_t streamsize;
+
+ class ios_base {
+ public:
+ streamsize width(streamsize wide);
+ };
+
+ template >
+ class basic_ios : public ios_base {
+ public:
+ };
+
+ // std::basic_istream
+ template >
+ class basic_istream : virtual public basic_ios {
+ };
+
+ // operator>> std::basic_istream -> char*
+ template basic_istream& operator>>(basic_istream&, charT*);
+
+ // std::istream
+ typedef basic_istream istream;
+
+ // --- std::cin ---
+
+ extern istream cin;
+}
+
+void printLine(const char *str);
+
+// --- test cases ---
+
+using namespace std;
+
+#define CHAR_BUFFER_SIZE 10
+
+void CWE676_Use_of_Potentially_Dangerous_Function__basic_17_bad()
+{
+ int j;
+ for(j = 0; j < 1; j++)
+ {
+ {
+ char charBuffer[CHAR_BUFFER_SIZE];
+ /* FLAW: using cin in an inherently dangerous fashion */
+ /* INCIDENTAL CWE120 Buffer Overflow since cin extraction is unbounded. */
+ cin >> charBuffer; // BAD
+ charBuffer[CHAR_BUFFER_SIZE-1] = '\0';
+ printLine(charBuffer);
+ }
+ }
+}
+
+/* good1() changes the conditions on the for statements */
+static void CWE676_Use_of_Potentially_Dangerous_Function__basic_17_good1()
+{
+ int k;
+ for(k = 0; k < 1; k++)
+ {
+ {
+ char charBuffer[CHAR_BUFFER_SIZE];
+ /* FIX: Use cin after specifying the length of the input */
+ cin.width(CHAR_BUFFER_SIZE);
+ cin >> charBuffer; // GOOD
+ charBuffer[CHAR_BUFFER_SIZE-1] = '\0';
+ printLine(charBuffer);
+ }
+ }
+}
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-772/SAMATE/FileMayNotBeClosed.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-772/SAMATE/FileMayNotBeClosed.expected
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-772/SAMATE/FileMayNotBeClosed.qlref b/cpp/ql/test/query-tests/Security/CWE/CWE-772/SAMATE/FileMayNotBeClosed.qlref
new file mode 100644
index 00000000000..fd711c007f0
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-772/SAMATE/FileMayNotBeClosed.qlref
@@ -0,0 +1 @@
+Critical/FileMayNotBeClosed.ql
\ No newline at end of file
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-772/SAMATE/FileNeverClosed.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-772/SAMATE/FileNeverClosed.expected
new file mode 100644
index 00000000000..c328cee7ec9
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-772/SAMATE/FileNeverClosed.expected
@@ -0,0 +1,3 @@
+| tests.cpp:220:12:220:16 | call to fopen | The file is never closed |
+| tests.cpp:252:12:252:15 | call to open | The file is never closed |
+| tests.cpp:278:12:278:21 | call to CreateFile | The file is never closed |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-772/SAMATE/FileNeverClosed.qlref b/cpp/ql/test/query-tests/Security/CWE/CWE-772/SAMATE/FileNeverClosed.qlref
new file mode 100644
index 00000000000..825ac26f500
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-772/SAMATE/FileNeverClosed.qlref
@@ -0,0 +1 @@
+Critical/FileNeverClosed.ql
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-772/SAMATE/MemoryMayNotBeFreed.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-772/SAMATE/MemoryMayNotBeFreed.expected
new file mode 100644
index 00000000000..11a19a071d0
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-772/SAMATE/MemoryMayNotBeFreed.expected
@@ -0,0 +1,2 @@
+| tests.cpp:198:31:198:36 | call to malloc | The memory allocated here may not be released at $@. | tests.cpp:212:1:212:1 | return ... | this exit point |
+| tests.cpp:325:5:325:68 | ... = ... | The memory allocated here may not be released at $@. | tests.cpp:333:1:333:1 | return ... | this exit point |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-772/SAMATE/MemoryMayNotBeFreed.qlref b/cpp/ql/test/query-tests/Security/CWE/CWE-772/SAMATE/MemoryMayNotBeFreed.qlref
new file mode 100644
index 00000000000..33da8e296e2
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-772/SAMATE/MemoryMayNotBeFreed.qlref
@@ -0,0 +1 @@
+Critical/MemoryMayNotBeFreed.ql
\ No newline at end of file
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-772/SAMATE/MemoryNeverFreed.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-772/SAMATE/MemoryNeverFreed.expected
new file mode 100644
index 00000000000..087186d9dc6
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-772/SAMATE/MemoryNeverFreed.expected
@@ -0,0 +1,2 @@
+| tests.cpp:71:20:71:26 | new | This memory is never freed |
+| tests.cpp:136:24:136:29 | call to malloc | This memory is never freed |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-772/SAMATE/MemoryNeverFreed.qlref b/cpp/ql/test/query-tests/Security/CWE/CWE-772/SAMATE/MemoryNeverFreed.qlref
new file mode 100644
index 00000000000..2d1336a55eb
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-772/SAMATE/MemoryNeverFreed.qlref
@@ -0,0 +1 @@
+Critical/MemoryNeverFreed.ql
\ No newline at end of file
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-772/SAMATE/tests.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-772/SAMATE/tests.cpp
new file mode 100644
index 00000000000..e7b889deb08
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-772/SAMATE/tests.cpp
@@ -0,0 +1,333 @@
+// Sample of SAMATE Juliet tests for CWE-772.
+
+// --- library types, functions etc ---
+
+#define NULL (0)
+typedef unsigned long size_t;
+
+void *malloc(size_t size);
+void *realloc(void *ptr, size_t size);
+void *alloca(size_t size);
+#define ALLOCA alloca
+void free(void *ptr);
+
+typedef struct {} FILE;
+FILE *fopen(const char *filename, const char *mode);
+int fclose(FILE *stream);
+
+char *strcpy(char *s1, const char *s2);
+
+void printLine(const char *str);
+void printIntLine(int val);
+
+// --- open ---
+
+typedef unsigned int mode_t;
+int open(const char *path, int oflags, mode_t mode);
+#define OPEN open
+int close(int fd);
+#define CLOSE close
+
+#define O_RDWR (1)
+#define O_CREAT (2)
+#define S_IREAD (3)
+#define S_IWRITE (4)
+
+// --- Windows ---
+
+typedef unsigned int HANDLE;
+#define INVALID_HANDLE_VALUE (-1)
+typedef const char *LPCTSTR;
+typedef unsigned long DWORD;
+typedef struct _SECURITY_ATTRIBUTES {} *LPSECURITY_ATTRIBUTES;
+typedef bool BOOL;
+HANDLE CreateFile(
+ LPCTSTR lpFileName,
+ DWORD dwDesiredAccess,
+ DWORD dwShareMode,
+ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
+ DWORD dwCreationDisposition,
+ DWORD dwFlagsAndAttributes,
+ HANDLE hTemplateFile);
+BOOL CloseHandle(HANDLE hObject);
+
+#define GENERIC_READ (1)
+#define GENERIC_WRITE (2)
+#define OPEN_ALWAYS (3)
+#define FILE_ATTRIBUTE_NORMAL (4)
+
+// --- test cases ---
+
+namespace CWE401_Memory_Leak__new_int_17
+{
+ void bad()
+ {
+ int i,j;
+ int * data;
+ data = NULL;
+ for(i = 0; i < 1; i++)
+ {
+ /* POTENTIAL FLAW: Allocate memory on the heap */
+ data = new int; // BAD
+ /* Initialize and make use of data */
+ *data = 5;
+ printIntLine(*data);
+ }
+ for(j = 0; j < 1; j++)
+ {
+ /* POTENTIAL FLAW: No deallocation */
+ ; /* empty statement needed for some flow variants */
+ }
+ }
+
+ /* goodB2G() - use badsource and goodsink in the for statements */
+ static void goodB2G()
+ {
+ int i,k;
+ int * data;
+ data = NULL;
+ for(i = 0; i < 1; i++)
+ {
+ /* POTENTIAL FLAW: Allocate memory on the heap */
+ data = new int; // GOOD
+ /* Initialize and make use of data */
+ *data = 5;
+ printIntLine(*data);
+ }
+ for(k = 0; k < 1; k++)
+ {
+ /* FIX: Deallocate memory */
+ delete data;
+ }
+ }
+
+ /* goodG2B() - use goodsource and badsink in the for statements */
+ static void goodG2B()
+ {
+ int h,j;
+ int * data;
+ data = NULL;
+ for(h = 0; h < 1; h++)
+ {
+ /* FIX: Use memory allocated on the stack */
+ int dataGoodBuffer; // GOOD
+ data = &dataGoodBuffer;
+ /* Initialize and make use of data */
+ *data = 5;
+ printIntLine(*data);
+ }
+ for(j = 0; j < 1; j++)
+ {
+ /* POTENTIAL FLAW: No deallocation */
+ ; /* empty statement needed for some flow variants */
+ }
+ }
+} /* close namespace */
+
+void CWE401_Memory_Leak__char_malloc_32_bad()
+{
+ char * data;
+ char * *dataPtr1 = &data;
+ char * *dataPtr2 = &data;
+ data = NULL;
+ {
+ char * data = *dataPtr1;
+ /* POTENTIAL FLAW: Allocate memory on the heap */
+ data = (char *)malloc(100*sizeof(char)); // BAD
+ /* Initialize and make use of data */
+ strcpy(data, "A String");
+ printLine(data);
+ *dataPtr1 = data;
+ }
+ {
+ char * data = *dataPtr2;
+ /* POTENTIAL FLAW: No deallocation */
+ ; /* empty statement needed for some flow variants */
+ }
+}
+
+/* goodG2B() uses the GoodSource with the BadSink */
+static void CWE401_Memory_Leak__char_malloc_32_goodG2B()
+{
+ char * data;
+ char * *dataPtr1 = &data;
+ char * *dataPtr2 = &data;
+ data = NULL;
+ {
+ char * data = *dataPtr1;
+ /* FIX: Use memory allocated on the stack with ALLOCA */
+ data = (char *)ALLOCA(100*sizeof(char)); // GOOD
+ /* Initialize and make use of data */
+ strcpy(data, "A String");
+ printLine(data);
+ *dataPtr1 = data;
+ }
+ {
+ char * data = *dataPtr2;
+ /* POTENTIAL FLAW: No deallocation */
+ ; /* empty statement needed for some flow variants */
+ }
+}
+
+/* goodB2G() uses the BadSource with the GoodSink */
+static void CWE401_Memory_Leak__char_malloc_32_goodB2G()
+{
+ char * data;
+ char * *dataPtr1 = &data;
+ char * *dataPtr2 = &data;
+ data = NULL;
+ {
+ char * data = *dataPtr1;
+ /* POTENTIAL FLAW: Allocate memory on the heap */
+ data = (char *)malloc(100*sizeof(char)); // GOOD
+ /* Initialize and make use of data */
+ strcpy(data, "A String");
+ printLine(data);
+ *dataPtr1 = data;
+ }
+ {
+ char * data = *dataPtr2;
+ /* FIX: Deallocate memory */
+ free(data);
+ }
+}
+
+void CWE401_Memory_Leak__malloc_realloc_char_01_bad()
+{
+ {
+ char * data = (char *)malloc(100*sizeof(char)); // BAD
+ /* Initialize and make use of data */
+ strcpy(data, "A String");
+ printLine(data);
+ /* FLAW: If realloc() fails, the initial memory block will not be freed() */
+ data = (char *)realloc(data, (130000)*sizeof(char));
+ if (data != NULL)
+ {
+ /* Reinitialize and make use of data */
+ strcpy(data, "New String");
+ printLine(data);
+ free(data);
+ }
+ }
+}
+
+void CWE775_Missing_Release_of_File_Descriptor_or_Handle__fopen_no_close_17_bad()
+{
+ int j;
+ FILE * data;
+ data = NULL;
+ /* POTENTIAL FLAW: Open a file without closing it */
+ data = fopen("BadSource_fopen.txt", "w+"); // BAD
+ for(j = 0; j < 1; j++)
+ {
+ /* FLAW: No attempt to close the file */
+ ; /* empty statement needed for some flow variants */
+ }
+}
+
+/* goodB2G() - use the goodsink in the for statement */
+static void CWE775_Missing_Release_of_File_Descriptor_or_Handle__fopen_no_close_17_goodB2G()
+{
+ int k;
+ FILE * data;
+ data = NULL;
+ /* POTENTIAL FLAW: Open a file without closing it */
+ data = fopen("BadSource_fopen.txt", "w+"); // GOOD
+ for(k = 0; k < 1; k++)
+ {
+ /* FIX: If the file is still opened, close it */
+ if (data != NULL)
+ {
+ fclose(data);
+ }
+ }
+}
+
+void CWE775_Missing_Release_of_File_Descriptor_or_Handle__open_no_close_01_bad()
+{
+ int data;
+ /* Initialize data */
+ data = -1;
+ /* POTENTIAL FLAW: Open a file without closing it */
+ data = OPEN("BadSource_open.txt", O_RDWR|O_CREAT, S_IREAD|S_IWRITE); // BAD
+ /* FLAW: No attempt to close the file */
+ ; /* empty statement needed for some flow variants */
+}
+
+/* goodB2G() uses the BadSource with the GoodSink */
+static void CWE775_Missing_Release_of_File_Descriptor_or_Handle__open_no_close_01_goodB2G()
+{
+ int data;
+ /* Initialize data */
+ data = -1;
+ /* POTENTIAL FLAW: Open a file without closing it */
+ data = OPEN("BadSource_open.txt", O_RDWR|O_CREAT, S_IREAD|S_IWRITE); // GOOD
+ /* FIX: If the file is still opened, close it */
+ if (data != -1)
+ {
+ CLOSE(data);
+ }
+}
+
+void CWE775_Missing_Release_of_File_Descriptor_or_Handle__w32CreateFile_no_close_01_bad()
+{
+ HANDLE data;
+ /* Initialize data */
+ data = INVALID_HANDLE_VALUE;
+ /* POTENTIAL FLAW: Open a file without closing it */
+ data = CreateFile("BadSource_w32CreateFile.txt", // BAD
+ (GENERIC_WRITE|GENERIC_READ),
+ 0,
+ NULL,
+ OPEN_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+ /* FLAW: No attempt to close the file */
+ ; /* empty statement needed for some flow variants */
+}
+
+/* goodB2G() uses the BadSource with the GoodSink */
+static void CWE775_Missing_Release_of_File_Descriptor_or_Handle__w32CreateFile_no_close_01_goodB2G()
+{
+ HANDLE data;
+ /* Initialize data */
+ data = INVALID_HANDLE_VALUE;
+ /* POTENTIAL FLAW: Open a file without closing it */
+ data = CreateFile("BadSource_w32CreateFile.txt", // GOOD
+ (GENERIC_WRITE|GENERIC_READ),
+ 0,
+ NULL,
+ OPEN_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+ /* FIX: If the file is still opened, close it */
+ if (data != INVALID_HANDLE_VALUE)
+ {
+ CloseHandle(data);
+ }
+}
+
+void exit(int status);
+
+typedef struct _twoIntsStruct
+{
+ int intOne;
+ int intTwo;
+} twoIntsStruct;
+
+void printStructLine(const twoIntsStruct * structTwoIntsStruct);
+
+void CWE401_Memory_Leak__twoIntsStruct_realloc_01_bad()
+{
+ twoIntsStruct * data;
+ data = NULL;
+ /* POTENTIAL FLAW: Allocate memory on the heap */
+ data = (twoIntsStruct *)realloc(data, 100*sizeof(twoIntsStruct));
+ if (data == NULL) {exit(-1);}
+ /* Initialize and make use of data */
+ data[0].intOne = 0;
+ data[0].intTwo = 0;
+ printStructLine(&data[0]);
+ /* POTENTIAL FLAW: No deallocation */
+ ; /* empty statement needed for some flow variants */
+}
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-807/semmle/TaintedCondition/TaintedCondition.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-807/semmle/TaintedCondition/TaintedCondition.expected
index e04a20830d0..1fdb1497922 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-807/semmle/TaintedCondition/TaintedCondition.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-807/semmle/TaintedCondition/TaintedCondition.expected
@@ -7,6 +7,7 @@ edges
| test.cpp:20:29:20:47 | (const char *)... | test.cpp:24:11:24:16 | call to strcmp |
| test.cpp:20:29:20:47 | (const char *)... | test.cpp:41:10:41:38 | ! ... |
| test.cpp:20:29:20:47 | (const char *)... | test.cpp:41:11:41:16 | call to strcmp |
+subpaths
nodes
| test.cpp:20:29:20:34 | call to getenv | semmle.label | call to getenv |
| test.cpp:20:29:20:47 | (const char *)... | semmle.label | (const char *)... |
diff --git a/cpp/ql/test/query-tests/Security/CWE/README.md b/cpp/ql/test/query-tests/Security/CWE/README.md
new file mode 100644
index 00000000000..465f0cb3b3f
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/README.md
@@ -0,0 +1,5 @@
+# CWE specific security tests
+
+## Source from the Juliet Test Suite
+
+Some of the the files in these tests contain source code copied or derived from the public domain "Juliet Test Suite for C/C++" (provided by NIST / SAMATE Team at https://samate.nist.gov/SARD/testsuite.php). Such tests are typically in subdirectories named "SAMATE".
diff --git a/cpp/upgrades/7806a11dd7ab6611c4245b2e96b8ed13cb5c6056/old.dbscheme b/cpp/upgrades/7806a11dd7ab6611c4245b2e96b8ed13cb5c6056/old.dbscheme
new file mode 100644
index 00000000000..7806a11dd7a
--- /dev/null
+++ b/cpp/upgrades/7806a11dd7ab6611c4245b2e96b8ed13cb5c6056/old.dbscheme
@@ -0,0 +1,2136 @@
+
+/**
+ * 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 date of the snapshot.
+ */
+snapshotDate(unique date snapshotDate : date ref);
+
+/**
+ * The source location of the snapshot.
+ */
+sourceLocationPrefix(string prefix : string ref);
+
+/**
+ * Data used by the 'duplicate code' detection.
+ */
+duplicateCode(
+ unique int id : @duplication,
+ string relativePath : string ref,
+ int equivClass : int ref
+);
+
+/**
+ * Data used by the 'similar code' detection.
+ */
+similarCode(
+ unique int id : @similarity,
+ string relativePath : string ref,
+ int equivClass : int ref
+);
+
+/**
+ * Data used by the 'duplicate code' and 'similar code' detection.
+ */
+@duplication_or_similarity = @duplication | @similarity
+
+/**
+ * Data used by the 'duplicate code' and 'similar code' detection.
+ */
+#keyset[id, offset]
+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
+);
+
+/**
+ * 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://help.semmle.com/QL/learn-ql/ql/locations.html).
+ */
+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://help.semmle.com/QL/learn-ql/ql/locations.html).
+ */
+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://help.semmle.com/QL/learn-ql/ql/locations.html).
+ */
+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
+)
+
+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,
+ unique 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
+);
+
+/**
+ * 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_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_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
+);
+
+#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/upgrades/7806a11dd7ab6611c4245b2e96b8ed13cb5c6056/semmlecode.cpp.dbscheme b/cpp/upgrades/7806a11dd7ab6611c4245b2e96b8ed13cb5c6056/semmlecode.cpp.dbscheme
new file mode 100644
index 00000000000..018f430097e
--- /dev/null
+++ b/cpp/upgrades/7806a11dd7ab6611c4245b2e96b8ed13cb5c6056/semmlecode.cpp.dbscheme
@@ -0,0 +1,2136 @@
+
+/**
+ * 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 date of the snapshot.
+ */
+snapshotDate(unique date snapshotDate : date ref);
+
+/**
+ * The source location of the snapshot.
+ */
+sourceLocationPrefix(string prefix : string ref);
+
+/**
+ * Data used by the 'duplicate code' detection.
+ */
+duplicateCode(
+ unique int id : @duplication,
+ string relativePath : string ref,
+ int equivClass : int ref
+);
+
+/**
+ * Data used by the 'similar code' detection.
+ */
+similarCode(
+ unique int id : @similarity,
+ string relativePath : string ref,
+ int equivClass : int ref
+);
+
+/**
+ * Data used by the 'duplicate code' and 'similar code' detection.
+ */
+@duplication_or_similarity = @duplication | @similarity
+
+/**
+ * Data used by the 'duplicate code' and 'similar code' detection.
+ */
+#keyset[id, offset]
+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
+);
+
+/**
+ * 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
+)
+
+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,
+ unique 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
+);
+
+/**
+ * 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_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_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
+);
+
+#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/upgrades/7806a11dd7ab6611c4245b2e96b8ed13cb5c6056/upgrade.properties b/cpp/upgrades/7806a11dd7ab6611c4245b2e96b8ed13cb5c6056/upgrade.properties
new file mode 100644
index 00000000000..63872bd6f10
--- /dev/null
+++ b/cpp/upgrades/7806a11dd7ab6611c4245b2e96b8ed13cb5c6056/upgrade.properties
@@ -0,0 +1,2 @@
+description: Non-functional change to dbscheme comments
+compatibility: full
diff --git a/cpp/upgrades/ddd31fd02e51ad270bc9e6712708e5a5b6881518/old.dbscheme b/cpp/upgrades/ddd31fd02e51ad270bc9e6712708e5a5b6881518/old.dbscheme
new file mode 100644
index 00000000000..ddd31fd02e5
--- /dev/null
+++ b/cpp/upgrades/ddd31fd02e51ad270bc9e6712708e5a5b6881518/old.dbscheme
@@ -0,0 +1,2145 @@
+
+/**
+ * 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 date of the snapshot.
+ */
+snapshotDate(unique date snapshotDate : date ref);
+
+/**
+ * The source location of the snapshot.
+ */
+sourceLocationPrefix(string prefix : string ref);
+
+/**
+ * Data used by the 'duplicate code' detection.
+ */
+duplicateCode(
+ unique int id : @duplication,
+ string relativePath : string ref,
+ int equivClass : int ref
+);
+
+/**
+ * Data used by the 'similar code' detection.
+ */
+similarCode(
+ unique int id : @similarity,
+ string relativePath : string ref,
+ int equivClass : int ref
+);
+
+/**
+ * Data used by the 'duplicate code' and 'similar code' detection.
+ */
+@duplication_or_similarity = @duplication | @similarity
+
+/**
+ * Data used by the 'duplicate code' and 'similar code' detection.
+ */
+#keyset[id, offset]
+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
+);
+
+/**
+ * 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://help.semmle.com/QL/learn-ql/ql/locations.html).
+ */
+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://help.semmle.com/QL/learn-ql/ql/locations.html).
+ */
+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://help.semmle.com/QL/learn-ql/ql/locations.html).
+ */
+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
+);
+
+/*
+ fromSource(0) = unknown,
+ fromSource(1) = from source,
+ fromSource(2) = from library
+*/
+files(
+ unique int id: @file,
+ string name: string ref,
+ string simple: string ref,
+ string ext: string ref,
+ int fromSource: int ref
+);
+
+folders(
+ unique int id: @folder,
+ string name: string ref,
+ string simple: 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
+)
+
+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,
+ unique 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
+);
+
+/**
+ * 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_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_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
+);
+
+#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/upgrades/ddd31fd02e51ad270bc9e6712708e5a5b6881518/semmlecode.cpp.dbscheme b/cpp/upgrades/ddd31fd02e51ad270bc9e6712708e5a5b6881518/semmlecode.cpp.dbscheme
new file mode 100644
index 00000000000..7806a11dd7a
--- /dev/null
+++ b/cpp/upgrades/ddd31fd02e51ad270bc9e6712708e5a5b6881518/semmlecode.cpp.dbscheme
@@ -0,0 +1,2136 @@
+
+/**
+ * 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 date of the snapshot.
+ */
+snapshotDate(unique date snapshotDate : date ref);
+
+/**
+ * The source location of the snapshot.
+ */
+sourceLocationPrefix(string prefix : string ref);
+
+/**
+ * Data used by the 'duplicate code' detection.
+ */
+duplicateCode(
+ unique int id : @duplication,
+ string relativePath : string ref,
+ int equivClass : int ref
+);
+
+/**
+ * Data used by the 'similar code' detection.
+ */
+similarCode(
+ unique int id : @similarity,
+ string relativePath : string ref,
+ int equivClass : int ref
+);
+
+/**
+ * Data used by the 'duplicate code' and 'similar code' detection.
+ */
+@duplication_or_similarity = @duplication | @similarity
+
+/**
+ * Data used by the 'duplicate code' and 'similar code' detection.
+ */
+#keyset[id, offset]
+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
+);
+
+/**
+ * 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://help.semmle.com/QL/learn-ql/ql/locations.html).
+ */
+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://help.semmle.com/QL/learn-ql/ql/locations.html).
+ */
+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://help.semmle.com/QL/learn-ql/ql/locations.html).
+ */
+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
+)
+
+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,
+ unique 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
+);
+
+/**
+ * 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_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_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
+);
+
+#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/upgrades/ddd31fd02e51ad270bc9e6712708e5a5b6881518/upgrade.properties b/cpp/upgrades/ddd31fd02e51ad270bc9e6712708e5a5b6881518/upgrade.properties
new file mode 100644
index 00000000000..a0c4ba602a1
--- /dev/null
+++ b/cpp/upgrades/ddd31fd02e51ad270bc9e6712708e5a5b6881518/upgrade.properties
@@ -0,0 +1,4 @@
+description: Removed unused column from the `folders` and `files` relations
+compatibility: full
+files.rel: reorder files.rel (int id, string name, string simple, string ext, int fromSource) id name
+folders.rel: reorder folders.rel (int id, string name, string simple) id name
\ No newline at end of file
diff --git a/cpp/upgrades/qlpack.yml b/cpp/upgrades/qlpack.yml
index acc305bb6a2..7f18cb54c8e 100644
--- a/cpp/upgrades/qlpack.yml
+++ b/cpp/upgrades/qlpack.yml
@@ -1,3 +1,4 @@
name: codeql/cpp-upgrades
upgrades: .
version: 0.0.2
+library: true
diff --git a/csharp/autobuilder/Semmle.Autobuild.Shared/BuildActions.cs b/csharp/autobuilder/Semmle.Autobuild.Shared/BuildActions.cs
index 9fd505d5457..1cf1d6253a5 100644
--- a/csharp/autobuilder/Semmle.Autobuild.Shared/BuildActions.cs
+++ b/csharp/autobuilder/Semmle.Autobuild.Shared/BuildActions.cs
@@ -161,8 +161,11 @@ namespace Semmle.Autobuild.Shared
pi.WorkingDirectory = workingDirectory;
// Environment variables can only be used when not redirecting stdout
- if (!redirectStandardOutput && environment is not null)
- environment.ForEach(kvp => pi.Environment[kvp.Key] = kvp.Value);
+ if (!redirectStandardOutput)
+ {
+ if (environment is not null)
+ environment.ForEach(kvp => pi.Environment[kvp.Key] = kvp.Value);
+ }
return pi;
}
diff --git a/csharp/change-notes/2021-09-09-service-stack-support.md b/csharp/change-notes/2021-09-09-service-stack-support.md
new file mode 100644
index 00000000000..1be10e7aafb
--- /dev/null
+++ b/csharp/change-notes/2021-09-09-service-stack-support.md
@@ -0,0 +1,2 @@
+lgtm,codescanning
+* added support for ServiceStack framework with support for SQL injection, XSS and external API calls
diff --git a/csharp/change-notes/2021-10-04-constand-condition.md b/csharp/change-notes/2021-10-04-constand-condition.md
new file mode 100644
index 00000000000..70ad5b5ea75
--- /dev/null
+++ b/csharp/change-notes/2021-10-04-constand-condition.md
@@ -0,0 +1,2 @@
+lgtm,codescanning
+* Discards in tuple patterns, for example `(_, string s)`, are no longer flagged by the query "Constant condition".
\ No newline at end of file
diff --git a/csharp/change-notes/2021-10-04-dead-store-of-local.md b/csharp/change-notes/2021-10-04-dead-store-of-local.md
new file mode 100644
index 00000000000..307f10af654
--- /dev/null
+++ b/csharp/change-notes/2021-10-04-dead-store-of-local.md
@@ -0,0 +1,2 @@
+lgtm,codescanning
+* `using` declarations are no longer flagged by the query "Useless assignment to local variable".
diff --git a/csharp/documentation/library-coverage/coverage.csv b/csharp/documentation/library-coverage/coverage.csv
index dbac4906d86..32137cb6d38 100644
--- a/csharp/documentation/library-coverage/coverage.csv
+++ b/csharp/documentation/library-coverage/coverage.csv
@@ -1,2 +1,6 @@
-package,sink,source,summary,sink:html,sink:xss,source:local,summary:taint
-System,5,3,13,4,1,3,13
+package,sink,source,summary,sink:code,sink:html,sink:remote,sink:sql,sink:xss,source:local,summary:taint
+Dapper,55,,,,,,55,,,
+Microsoft.ApplicationBlocks.Data,28,,,,,,28,,,
+MySql.Data.MySqlClient,48,,,,,,48,,,
+ServiceStack,194,,7,27,,75,92,,,7
+System,28,3,13,,4,,23,1,3,13
diff --git a/csharp/documentation/library-coverage/coverage.rst b/csharp/documentation/library-coverage/coverage.rst
index 568eadb4cc6..367e7a35bd2 100644
--- a/csharp/documentation/library-coverage/coverage.rst
+++ b/csharp/documentation/library-coverage/coverage.rst
@@ -7,6 +7,8 @@ C# framework & library support
:widths: auto
Framework / library,Package,Flow sources,Taint & value steps,Sinks (total),`CWE-079` :sub:`Cross-site scripting`
- System,"``System.*``, ``System``",3,13,5,5
- Totals,,3,13,5,5
+ `ServiceStack `_,"``ServiceStack.*``, ``ServiceStack``",,7,194,
+ System,"``System.*``, ``System``",3,13,28,5
+ Others,"``Dapper``, ``Microsoft.ApplicationBlocks.Data``, ``MySql.Data.MySqlClient``",,,131,
+ Totals,,3,20,353,5
diff --git a/csharp/documentation/library-coverage/frameworks.csv b/csharp/documentation/library-coverage/frameworks.csv
index d0cc09b4e9c..6539f78b265 100644
--- a/csharp/documentation/library-coverage/frameworks.csv
+++ b/csharp/documentation/library-coverage/frameworks.csv
@@ -1,2 +1,3 @@
Framework name,URL,Namespace prefixes
System,,System.* System
+ServiceStack,https://servicestack.net/,ServiceStack.* ServiceStack
diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/File.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/File.cs
index c2fd6837e3e..85ced50fe7e 100644
--- a/csharp/extractor/Semmle.Extraction.CIL/Entities/File.cs
+++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/File.cs
@@ -37,7 +37,7 @@ namespace Semmle.Extraction.CIL.Entities
yield return parent;
yield return Tuples.containerparent(parent, this);
}
- yield return Tuples.files(this, TransformedPath.Value, TransformedPath.NameWithoutExtension, TransformedPath.Extension);
+ yield return Tuples.files(this, TransformedPath.Value);
}
}
}
diff --git a/csharp/extractor/Semmle.Extraction.CIL/Entities/Folder.cs b/csharp/extractor/Semmle.Extraction.CIL/Entities/Folder.cs
index 9c3fbadcf20..e854127137d 100644
--- a/csharp/extractor/Semmle.Extraction.CIL/Entities/Folder.cs
+++ b/csharp/extractor/Semmle.Extraction.CIL/Entities/Folder.cs
@@ -28,7 +28,7 @@ namespace Semmle.Extraction.CIL.Entities
yield return parentFolder;
yield return Tuples.containerparent(parentFolder, this);
}
- yield return Tuples.folders(this, transformedPath.Value, transformedPath.NameWithoutExtension);
+ yield return Tuples.folders(this, transformedPath.Value);
}
}
diff --git a/csharp/extractor/Semmle.Extraction.CIL/Tuples.cs b/csharp/extractor/Semmle.Extraction.CIL/Tuples.cs
index c8ed3445c6f..d58647aba77 100644
--- a/csharp/extractor/Semmle.Extraction.CIL/Tuples.cs
+++ b/csharp/extractor/Semmle.Extraction.CIL/Tuples.cs
@@ -203,14 +203,14 @@ namespace Semmle.Extraction.CIL
internal static Tuple containerparent(Folder parent, IFileOrFolder child) =>
new Tuple("containerparent", parent, child);
- internal static Tuple files(File file, string fullName, string name, string extension) =>
- new Tuple("files", file, fullName, name, extension, 0);
+ internal static Tuple files(File file, string fullName) =>
+ new Tuple("files", file, fullName);
internal static Tuple file_extraction_mode(File file, int mode) =>
new Tuple("file_extraction_mode", file, mode);
- internal static Tuple folders(Folder folder, string path, string name) =>
- new Tuple("folders", folder, path, name);
+ internal static Tuple folders(Folder folder, string path) =>
+ new Tuple("folders", folder, path);
internal static Tuple locations_default(PdbSourceLocation label, File file, int startLine, int startCol, int endLine, int endCol) =>
new Tuple("locations_default", label, file, startLine, startCol, endLine, endCol);
diff --git a/csharp/extractor/Semmle.Extraction.CSharp.Driver/Driver.cs b/csharp/extractor/Semmle.Extraction.CSharp.Driver/Driver.cs
index e8a42b3f5a0..047a8ddb8ae 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp.Driver/Driver.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp.Driver/Driver.cs
@@ -1,3 +1,7 @@
+using System;
+using System.Text.RegularExpressions;
+using System.Collections.Generic;
+
namespace Semmle.Extraction.CSharp
{
///
@@ -9,6 +13,31 @@ namespace Semmle.Extraction.CSharp
{
Extractor.SetInvariantCulture();
+ Console.WriteLine($"Semmle.Extraction.CSharp.Driver: called with {string.Join(", ", args)}");
+
+ if (args.Length > 0 && args[0] == "--dotnetexec")
+ {
+ var compilerRegEx = new Regex(@"csc\.exe|mcs\.exe|csc\.dll", RegexOptions.Compiled);
+ var cil = args.Length > 1 && args[1] == "--cil";
+ for (var i = cil ? 2 : 1; i < args.Length; i++)
+ {
+ if (compilerRegEx.IsMatch(args[i]))
+ {
+ var argsList = new List();
+ if (cil)
+ argsList.Add("--cil");
+ argsList.Add("--compiler");
+ argsList.Add(args[i]);
+ if (i + 1 < args.Length)
+ argsList.AddRange(args[(i + 1)..]);
+ return (int)Extractor.Run(argsList.ToArray());
+ }
+ }
+
+ Console.WriteLine($"Semmle.Extraction.CSharp.Driver: not a compiler invocation");
+ return 0;
+ }
+
return (int)Extractor.Run(args);
}
}
diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/File.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/File.cs
index f22edb158aa..fae77b033ad 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/File.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/File.cs
@@ -17,7 +17,7 @@ namespace Semmle.Extraction.CSharp.Entities
public override void Populate(TextWriter trapFile)
{
- trapFile.files(this, TransformedPath.Value, TransformedPath.NameWithoutExtension, TransformedPath.Extension);
+ trapFile.files(this, TransformedPath.Value);
if (TransformedPath.ParentDirectory is PathTransformer.ITransformedPath dir)
trapFile.containerparent(Extraction.Entities.Folder.Create(Context, dir), this);
diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Populators/CompilationUnitVisitor.cs b/csharp/extractor/Semmle.Extraction.CSharp/Populators/CompilationUnitVisitor.cs
index 6116c3511a1..be53e48f319 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp/Populators/CompilationUnitVisitor.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp/Populators/CompilationUnitVisitor.cs
@@ -59,8 +59,15 @@ namespace Semmle.Extraction.CSharp.Populators
return;
}
- var entryPoint = Cx.Compilation.GetEntryPoint(System.Threading.CancellationToken.None)!;
+ var entryPoint = Cx.Compilation.GetEntryPoint(System.Threading.CancellationToken.None);
var entryMethod = Method.Create(Cx, entryPoint);
+ if (entryMethod is null)
+ {
+ Cx.ExtractionError("No entry method found. Skipping the extraction of global statements.",
+ null, Cx.CreateLocation(globalStatements[0].GetLocation()), null, Severity.Info);
+ return;
+ }
+
var block = GlobalStatementsBlock.Create(Cx, entryMethod);
for (var i = 0; i < globalStatements.Count; i++)
diff --git a/csharp/extractor/Semmle.Extraction/Entities/Folder.cs b/csharp/extractor/Semmle.Extraction/Entities/Folder.cs
index 2826ab49ed1..465d545d983 100644
--- a/csharp/extractor/Semmle.Extraction/Entities/Folder.cs
+++ b/csharp/extractor/Semmle.Extraction/Entities/Folder.cs
@@ -8,7 +8,7 @@ namespace Semmle.Extraction.Entities
public override void Populate(TextWriter trapFile)
{
- trapFile.folders(this, Symbol.Value, Symbol.NameWithoutExtension);
+ trapFile.folders(this, Symbol.Value);
if (Symbol.ParentDirectory is PathTransformer.ITransformedPath parent)
trapFile.containerparent(Create(Context, parent), this);
}
diff --git a/csharp/extractor/Semmle.Extraction/Entities/GeneratedFile.cs b/csharp/extractor/Semmle.Extraction/Entities/GeneratedFile.cs
index 7c0e5df7be9..b4a771f53db 100644
--- a/csharp/extractor/Semmle.Extraction/Entities/GeneratedFile.cs
+++ b/csharp/extractor/Semmle.Extraction/Entities/GeneratedFile.cs
@@ -10,7 +10,7 @@ namespace Semmle.Extraction.Entities
public override void Populate(TextWriter trapFile)
{
- trapFile.files(this, "", "", "");
+ trapFile.files(this, "");
}
public override void WriteId(EscapingTextWriter trapFile)
diff --git a/csharp/extractor/Semmle.Extraction/Tuples.cs b/csharp/extractor/Semmle.Extraction/Tuples.cs
index 49c14df7643..2cff4bfbdf1 100644
--- a/csharp/extractor/Semmle.Extraction/Tuples.cs
+++ b/csharp/extractor/Semmle.Extraction/Tuples.cs
@@ -18,14 +18,14 @@ namespace Semmle.Extraction
trapFile.WriteTuple("extractor_messages", error, (int)severity, origin, errorMessage, entityText, location, stackTrace);
}
- public static void files(this System.IO.TextWriter trapFile, File file, string fullName, string name, string extension)
+ public static void files(this System.IO.TextWriter trapFile, File file, string fullName)
{
- trapFile.WriteTuple("files", file, fullName, name, extension, 0);
+ trapFile.WriteTuple("files", file, fullName);
}
- internal static void folders(this System.IO.TextWriter trapFile, Folder folder, string path, string name)
+ internal static void folders(this System.IO.TextWriter trapFile, Folder folder, string path)
{
- trapFile.WriteTuple("folders", folder, path, name);
+ trapFile.WriteTuple("folders", folder, path);
}
public static void locations_default(this System.IO.TextWriter trapFile, SourceLocation label, Entities.File file, int startLine, int startCol, int endLine, int endCol)
diff --git a/csharp/ql/lib/qlpack.yml b/csharp/ql/lib/qlpack.yml
index b760898bf7c..43ea66c228e 100644
--- a/csharp/ql/lib/qlpack.yml
+++ b/csharp/ql/lib/qlpack.yml
@@ -1,7 +1,6 @@
name: codeql/csharp-all
version: 0.0.2
dbscheme: semmlecode.csharp.dbscheme
-suites: codeql-suites
extractor: csharp
library: true
dependencies:
diff --git a/csharp/ql/lib/semmle/code/csharp/Attribute.qll b/csharp/ql/lib/semmle/code/csharp/Attribute.qll
index 2085c4c650a..06fbda2a150 100644
--- a/csharp/ql/lib/semmle/code/csharp/Attribute.qll
+++ b/csharp/ql/lib/semmle/code/csharp/Attribute.qll
@@ -25,7 +25,7 @@ class Attributable extends @attributable {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
- * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
diff --git a/csharp/ql/lib/semmle/code/csharp/File.qll b/csharp/ql/lib/semmle/code/csharp/File.qll
index d8b23bb61f4..df9ce6f3cf6 100644
--- a/csharp/ql/lib/semmle/code/csharp/File.qll
+++ b/csharp/ql/lib/semmle/code/csharp/File.qll
@@ -33,7 +33,7 @@ class Container extends @container {
/**
* Gets a URL representing the location of this container.
*
- * For more information see [Providing URLs](https://help.semmle.com/QL/learn-ql/ql/locations.html#providing-urls).
+ * For more information see [Providing URLs](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/#providing-urls).
*/
string getURL() { none() }
@@ -171,14 +171,14 @@ class Container extends @container {
/** A folder. */
class Folder extends Container, @folder {
- override string getAbsolutePath() { folders(this, result, _) }
+ override string getAbsolutePath() { folders(this, result) }
override string getURL() { result = "folder://" + getAbsolutePath() }
}
/** A file. */
class File extends Container, @file {
- override string getAbsolutePath() { files(this, result, _, _, _) }
+ override string getAbsolutePath() { files(this, result) }
/** Gets the number of lines in this file. */
int getNumberOfLines() { numlines(this, result, _, _) }
@@ -192,7 +192,7 @@ class File extends Container, @file {
override string getURL() { result = "file://" + this.getAbsolutePath() + ":0:0:0:0" }
/** Holds if this file contains source code. */
- predicate fromSource() { files(this, _, _, "cs", _) }
+ predicate fromSource() { this.getExtension() = "cs" }
/** Holds if this file is a library. */
predicate fromLibrary() {
diff --git a/csharp/ql/lib/semmle/code/csharp/Implements.qll b/csharp/ql/lib/semmle/code/csharp/Implements.qll
index c2d33d102dd..cc821b52823 100644
--- a/csharp/ql/lib/semmle/code/csharp/Implements.qll
+++ b/csharp/ql/lib/semmle/code/csharp/Implements.qll
@@ -398,6 +398,15 @@ private module Gvn {
)
}
+ pragma[noinline]
+ private predicate toStringPart(int i, int j) {
+ exists(Unification::GenericType t, int children |
+ t = this.getConstructedGenericDeclaringTypeAt(i) and
+ children = t.getNumberOfArgumentsSelf() and
+ if children = 0 then j = 0 else j in [0 .. 2 * children]
+ )
+ }
+
language[monotonicAggregates]
string toString() {
this.isFullyConstructed() and
@@ -406,11 +415,7 @@ private module Gvn {
or
result =
strictconcat(int i, int j |
- exists(Unification::GenericType t, int children |
- t = this.getConstructedGenericDeclaringTypeAt(i) and
- children = t.getNumberOfArgumentsSelf() and
- if children = 0 then j = 0 else j in [0 .. 2 * children]
- )
+ this.toStringPart(i, j)
|
this.toStringConstructedPart(i, j) order by i desc, j
)
diff --git a/csharp/ql/lib/semmle/code/csharp/Location.qll b/csharp/ql/lib/semmle/code/csharp/Location.qll
index 97f9302c474..22d87f42424 100644
--- a/csharp/ql/lib/semmle/code/csharp/Location.qll
+++ b/csharp/ql/lib/semmle/code/csharp/Location.qll
@@ -28,7 +28,7 @@ class Location extends @location {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
- * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
diff --git a/csharp/ql/lib/semmle/code/csharp/Unification.qll b/csharp/ql/lib/semmle/code/csharp/Unification.qll
index 73f739a1f7b..3a2c6745f45 100644
--- a/csharp/ql/lib/semmle/code/csharp/Unification.qll
+++ b/csharp/ql/lib/semmle/code/csharp/Unification.qll
@@ -17,7 +17,7 @@ module Gvn {
string getNameNested(Type t) {
if not t instanceof NestedType or t.(NestedType).getDeclaringType() instanceof GenericType
then result = t.getName()
- else result = getNameNested(t.(NestedType).getDeclaringType()) + "." + t.getName()
+ else result = getNameNested(t.(NestedType).getDeclaringType()) + "+" + t.getName()
}
/**
@@ -277,6 +277,18 @@ module Gvn {
)
}
+ pragma[noinline]
+ private predicate toStringPart(int i, int j) {
+ exists(int offset |
+ exists(GenericType t, int children |
+ t = this.getConstructedGenericDeclaringTypeAt(i) and
+ children = t.getNumberOfArgumentsSelf() and
+ (if this.isDeclaringTypeAt(i) then offset = 1 else offset = 0) and
+ if children = 0 then j in [0 .. offset] else j in [0 .. 2 * children + offset]
+ )
+ )
+ }
+
language[monotonicAggregates]
string toString() {
this.isFullyConstructed() and
@@ -284,13 +296,8 @@ module Gvn {
result = k.toStringBuiltin(this.getArg(0).toString())
or
result =
- strictconcat(int i, int j, int offset |
- exists(GenericType t, int children |
- t = this.getConstructedGenericDeclaringTypeAt(i) and
- children = t.getNumberOfArgumentsSelf() and
- (if this.isDeclaringTypeAt(i) then offset = 1 else offset = 0) and
- if children = 0 then j in [0 .. offset] else j in [0 .. 2 * children + offset]
- )
+ strictconcat(int i, int j |
+ toStringPart(i, j)
|
this.toStringConstructedPart(i, j) order by i desc, j
)
@@ -326,79 +333,53 @@ module Gvn {
getTypeArgument(k, t, i) = TTypeParameterGvnType()
}
- /**
- * Hold if (non-type-parameters) `arg1` and `arg2` are unifiable, and both are
- * the `i`th type argument of a compound type of kind `k`.
- */
- pragma[nomagic]
- private predicate unifiableNonTypeParameterTypeArguments(
- CompoundTypeKind k, GvnTypeArgument arg1, GvnTypeArgument arg2, int i
- ) {
- exists(int j |
- arg1 = getNonTypeParameterTypeArgument(k, _, i) and
- arg2 = getNonTypeParameterTypeArgument(k, _, j) and
- i <= j and
- j <= i
- |
- arg1 = arg2
- or
- unifiable(arg1, arg2)
- )
- }
-
/**
* Hold if `arg1` and `arg2` are unifiable, and both are the `i`th type argument
* of a compound type of kind `k`.
+ *
+ * `subsumes` indicates whether `arg1` in fact subsumes `arg2`.
*/
pragma[nomagic]
private predicate unifiableTypeArguments(
- CompoundTypeKind k, GvnTypeArgument arg1, GvnTypeArgument arg2, int i
+ CompoundTypeKind k, GvnTypeArgument arg1, GvnTypeArgument arg2, int i, boolean subsumes
) {
- unifiableNonTypeParameterTypeArguments(k, arg1, arg2, i)
- or
- exists(int j |
- arg1 = TTypeParameterGvnType() and
- typeArgumentIsTypeParameter(k, _, i) and
- arg2 = getTypeArgument(k, _, j) and
- i <= j and
- j <= i
+ arg1 = getNonTypeParameterTypeArgument(k, _, pragma[only_bind_into](i)) and
+ arg2 = getNonTypeParameterTypeArgument(k, _, pragma[only_bind_into](i)) and
+ (
+ arg1 = arg2 and
+ subsumes = true
+ or
+ unifiable(arg1, arg2, subsumes)
)
or
- exists(int j |
- arg1 = getTypeArgument(k, _, i) and
- typeArgumentIsTypeParameter(k, _, j) and
- arg2 = TTypeParameterGvnType() and
- i <= j and
- j <= i
- )
+ arg1 = TTypeParameterGvnType() and
+ typeArgumentIsTypeParameter(k, _, pragma[only_bind_into](i)) and
+ arg2 = getTypeArgument(k, _, pragma[only_bind_into](i)) and
+ subsumes = true
+ or
+ arg1 = getNonTypeParameterTypeArgument(k, _, pragma[only_bind_into](i)) and
+ typeArgumentIsTypeParameter(k, _, pragma[only_bind_into](i)) and
+ arg2 = TTypeParameterGvnType() and
+ subsumes = false
}
pragma[nomagic]
private predicate unifiableSingle0(
- CompoundTypeKind k, ConstructedGvnType t2, GvnTypeArgument arg1, GvnTypeArgument arg2
+ CompoundTypeKind k, ConstructedGvnType t2, GvnTypeArgument arg1, GvnTypeArgument arg2,
+ boolean subsumes
) {
- unifiableTypeArguments(k, arg1, arg2, 0) and
+ unifiableTypeArguments(k, arg1, arg2, 0, subsumes) and
arg2 = getTypeArgument(k, t2, 0) and
k.getNumberOfTypeParameters() = 1
}
- /**
- * Holds if the type arguments of types `t1` and `t2` are unifiable, `t1`
- * and `t2` are of the same kind, and the number of type arguments is 1.
- */
- private predicate unifiableSingle(ConstructedGvnType t1, ConstructedGvnType t2) {
- exists(CompoundTypeKind k, GvnTypeArgument arg1, GvnTypeArgument arg2 |
- unifiableSingle0(k, t2, arg1, arg2) and
- arg1 = getTypeArgument(k, t1, 0)
- )
- }
-
pragma[nomagic]
private predicate unifiableMultiple01Aux0(
- CompoundTypeKind k, ConstructedGvnType t2, GvnTypeArgument arg10, GvnTypeArgument arg21
+ CompoundTypeKind k, ConstructedGvnType t2, GvnTypeArgument arg10, GvnTypeArgument arg21,
+ boolean subsumes
) {
exists(GvnTypeArgument arg20 |
- unifiableTypeArguments(k, arg10, arg20, 0) and
+ unifiableTypeArguments(k, arg10, arg20, 0, subsumes) and
arg20 = getTypeArgument(k, t2, 0) and
arg21 = getTypeArgument(k, t2, 1)
)
@@ -406,43 +387,24 @@ module Gvn {
pragma[nomagic]
private predicate unifiableMultiple01Aux1(
- CompoundTypeKind k, ConstructedGvnType t1, GvnTypeArgument arg10, GvnTypeArgument arg21
+ CompoundTypeKind k, ConstructedGvnType t1, GvnTypeArgument arg10, GvnTypeArgument arg21,
+ boolean subsumes
) {
exists(GvnTypeArgument arg11 |
- unifiableTypeArguments(k, arg11, arg21, 1) and
+ unifiableTypeArguments(k, arg11, arg21, 1, subsumes) and
arg10 = getTypeArgument(k, t1, 0) and
arg11 = getTypeArgument(k, t1, 1)
)
}
- /**
- * Holds if the first two type arguments of types `t1` and `t2` are unifiable,
- * and both `t1` and `t2` are of kind `k`.
- */
- private predicate unifiableMultiple01(
- CompoundTypeKind k, ConstructedGvnType t1, ConstructedGvnType t2
- ) {
- exists(GvnTypeArgument arg10, GvnTypeArgument arg21 |
- unifiableMultiple01Aux0(k, t2, arg10, arg21) and
- unifiableMultiple01Aux1(k, t1, arg10, arg21)
- )
- }
-
pragma[nomagic]
private predicate unifiableMultiple2Aux(
- CompoundTypeKind k, ConstructedGvnType t2, int i, GvnTypeArgument arg1, GvnTypeArgument arg2
+ CompoundTypeKind k, ConstructedGvnType t2, int i, GvnTypeArgument arg1, boolean subsumes
) {
- unifiableTypeArguments(k, arg1, arg2, i) and
- arg2 = getTypeArgument(k, t2, i) and
- i >= 2
- }
-
- private predicate unifiableMultiple2(
- CompoundTypeKind k, ConstructedGvnType t1, ConstructedGvnType t2, int i
- ) {
- exists(GvnTypeArgument arg1, GvnTypeArgument arg2 |
- unifiableMultiple2Aux(k, t2, i, arg1, arg2) and
- arg1 = getTypeArgument(k, t1, i)
+ exists(GvnTypeArgument arg2 |
+ unifiableTypeArguments(k, arg1, arg2, i, subsumes) and
+ arg2 = getTypeArgument(k, t2, i) and
+ i >= 2
)
}
@@ -452,43 +414,33 @@ module Gvn {
*/
pragma[nomagic]
private predicate unifiableMultiple(
- CompoundTypeKind k, ConstructedGvnType t1, ConstructedGvnType t2, int i
+ CompoundTypeKind k, ConstructedGvnType t1, ConstructedGvnType t2, int i, boolean subsumes
) {
- unifiableMultiple01(k, t1, t2) and i = 1
+ exists(GvnTypeArgument arg10, GvnTypeArgument arg21, boolean subsumes1, boolean subsumes2 |
+ unifiableMultiple01Aux0(k, t2, arg10, arg21, subsumes1) and
+ unifiableMultiple01Aux1(k, t1, arg10, arg21, subsumes2) and
+ subsumes = subsumes1.booleanAnd(subsumes2)
+ ) and
+ i = 1
or
- unifiableMultiple(k, t1, t2, i - 1) and
- unifiableMultiple2(k, t1, t2, i)
- }
-
- private newtype TTypePath =
- TTypePathNil() or
- TTypePathCons(int head, TTypePath tail) { exists(getTypeAtCons(_, head, tail)) }
-
- /**
- * Gets the GVN inside GVN `t`, by following the path `path`, if any.
- */
- private GvnType getTypeAt(GvnType t, TTypePath path) {
- path = TTypePathNil() and
- result = t
- or
- exists(ConstructedGvnTypeList l, int head, TTypePath tail |
- t = TConstructedGvnType(l) and
- path = TTypePathCons(head, tail) and
- result = getTypeAtCons(l, head, tail)
+ exists(GvnTypeArgument arg1, boolean subsumes1, boolean subsumes2 |
+ unifiableMultiple(k, t1, t2, i - 1, subsumes1) and
+ unifiableMultiple2Aux(k, t2, i, arg1, subsumes2) and
+ arg1 = getTypeArgument(k, t1, i) and
+ subsumes = subsumes1.booleanAnd(subsumes2)
)
}
- private GvnType getTypeAtCons(ConstructedGvnTypeList l, int head, TTypePath tail) {
- result = getTypeAt(l.getArg(head), tail)
- }
-
- /**
- * Gets the leaf GVN inside GVN `t`, by following the path `path`, if any.
- */
- pragma[noinline]
- private GvnType getLeafTypeAt(GvnType t, TTypePath path) {
- result = getTypeAt(t, path) and
- not result instanceof ConstructedGvnType
+ pragma[nomagic]
+ private predicate unifiable(ConstructedGvnType t1, ConstructedGvnType t2, boolean subsumes) {
+ exists(CompoundTypeKind k, GvnTypeArgument arg1, GvnTypeArgument arg2 |
+ unifiableSingle0(k, t2, arg1, arg2, subsumes) and
+ arg1 = getTypeArgument(k, t1, 0)
+ )
+ or
+ exists(CompoundTypeKind k |
+ unifiableMultiple(k, t1, t2, k.getNumberOfTypeParameters() - 1, subsumes)
+ )
}
cached
@@ -540,33 +492,20 @@ module Gvn {
}
/**
- * Holds if GVNs `t1` and `t2` can be unified. That is, is it possible to
+ * Holds if GVNs `t1` and `t2` can be unified. That is, it is possible to
* replace all type parameters in `t1` and `t2` with some GVNs (possibly
* type parameters themselves) to make the two substituted terms equal.
*/
cached
- predicate unifiable(ConstructedGvnType t1, ConstructedGvnType t2) {
- unifiableSingle(t1, t2)
- or
- exists(CompoundTypeKind k | unifiableMultiple(k, t1, t2, k.getNumberOfTypeParameters() - 1))
- }
+ predicate unifiable(ConstructedGvnType t1, ConstructedGvnType t2) { unifiable(t1, t2, _) }
/**
- * Holds if GVN `t1` subsumes GVN `t2`. That is, is it possible to replace all
+ * Holds if GVN `t1` subsumes GVN `t2`. That is, it is possible to replace all
* type parameters in `t1` with some GVNs (possibly type parameters themselves)
* to make the two substituted terms equal.
*/
cached
- predicate subsumes(ConstructedGvnType t1, ConstructedGvnType t2) {
- unifiable(t1, t2) and // subsumption implies unification
- forall(TTypePath path, GvnType leaf1 | leaf1 = getLeafTypeAt(t1, path) |
- exists(GvnType child2 | child2 = getTypeAt(t2, path) |
- leaf1 = TTypeParameterGvnType()
- or
- leaf1 = child2
- )
- )
- }
+ predicate subsumes(ConstructedGvnType t1, ConstructedGvnType t2) { unifiable(t1, t2, true) }
}
import Cached
diff --git a/csharp/ql/lib/semmle/code/csharp/XML.qll b/csharp/ql/lib/semmle/code/csharp/XML.qll
index 5871fed0ddd..4c762f4bf65 100755
--- a/csharp/ql/lib/semmle/code/csharp/XML.qll
+++ b/csharp/ql/lib/semmle/code/csharp/XML.qll
@@ -24,7 +24,7 @@ class XMLLocatable extends @xmllocatable, TXMLLocatable {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
- * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/Bound.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/Bound.qll
index b129203db70..55a8b1f4c3f 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/Bound.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/Bound.qll
@@ -30,7 +30,7 @@ abstract class Bound extends TBound {
* The location spans column `sc` of line `sl` to
* column `ec` of line `el` in file `path`.
* For more information, see
- * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
path = "" and sl = 0 and sc = 0 and el = 0 and ec = 0
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/ExternalFlow.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/ExternalFlow.qll
index f36e00403c1..c7140238213 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/ExternalFlow.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/ExternalFlow.qll
@@ -88,6 +88,9 @@ private module Frameworks {
private import semmle.code.csharp.security.dataflow.flowsinks.Html
private import semmle.code.csharp.frameworks.System
private import semmle.code.csharp.security.dataflow.XSSSinks
+ private import semmle.code.csharp.frameworks.ServiceStack
+ private import semmle.code.csharp.frameworks.Sql
+ private import semmle.code.csharp.frameworks.EntityFramework
}
/**
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 5c2dbb30084..4ca06c93362 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll
@@ -923,28 +923,29 @@ private module Stage2 {
ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
- class Cc = boolean;
+ class Cc = CallContext;
- class CcCall extends Cc {
- CcCall() { this = true }
+ class CcCall = CallContextCall;
- /** Holds if this call context may be `call`. */
- predicate matchesCall(DataFlowCall call) { any() }
- }
+ class CcNoCall = CallContextNoCall;
- class CcNoCall extends Cc {
- CcNoCall() { this = false }
- }
-
- Cc ccNone() { result = false }
+ Cc ccNone() { result instanceof CallContextAny }
private class LocalCc = Unit;
bindingset[call, c, outercc]
- private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
+ 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[call, c, innercc]
- private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
+ 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, config]
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
@@ -1172,7 +1173,8 @@ private module Stage2 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
- fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ 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))
)
}
@@ -1860,7 +1862,8 @@ private module Stage3 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
- fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ 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))
)
}
@@ -2117,7 +2120,7 @@ private module Stage3 {
private predicate flowCandSummaryCtx(NodeEx node, AccessPathFront argApf, Configuration config) {
exists(AccessPathFront apf |
Stage3::revFlow(node, true, _, apf, config) and
- Stage3::fwdFlow(node, true, TAccessPathFrontSome(argApf), apf, config)
+ Stage3::fwdFlow(node, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
)
}
@@ -2136,7 +2139,8 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
) and
accessPathApproxCostLimits(apLimit, tupleLimit) and
apLimit < tails and
- tupleLimit < (tails - 1) * nodes
+ tupleLimit < (tails - 1) * nodes and
+ not tc.forceHighPrecision()
)
}
@@ -2618,7 +2622,8 @@ private module Stage4 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
- fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ 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))
)
}
@@ -2969,12 +2974,15 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) {
* expected to be expensive. Holds with `unfold = true` otherwise.
*/
private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) {
- exists(int aps, int nodes, int apLimit, int tupleLimit |
- aps = countPotentialAps(apa, config) and
- nodes = countNodesUsingAccessPath(apa, config) and
- accessPathCostLimits(apLimit, tupleLimit) and
- if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true
- )
+ if apa.getHead().forceHighPrecision()
+ then unfold = true
+ else
+ exists(int aps, int nodes, int apLimit, int tupleLimit |
+ aps = countPotentialAps(apa, config) and
+ nodes = countNodesUsingAccessPath(apa, config) and
+ accessPathCostLimits(apLimit, tupleLimit) and
+ if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true
+ )
}
/**
@@ -3244,7 +3252,7 @@ class PathNode extends TPathNode {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
- * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
@@ -3258,24 +3266,16 @@ class PathNode extends TPathNode {
/** Gets the associated configuration. */
Configuration getConfiguration() { none() }
- private predicate isHidden() {
- hiddenNode(this.(PathNodeImpl).getNodeEx().asNode()) and
- not this.isSource() and
- not this instanceof PathNodeSink
- or
- this.(PathNodeImpl).getNodeEx() instanceof TNodeImplicitRead
- }
-
private PathNode getASuccessorIfHidden() {
- this.isHidden() and
+ this.(PathNodeImpl).isHidden() and
result = this.(PathNodeImpl).getASuccessorImpl()
}
/** Gets a successor of this node, if any. */
final PathNode getASuccessor() {
result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and
- not this.isHidden() and
- not result.isHidden()
+ not this.(PathNodeImpl).isHidden() and
+ not result.(PathNodeImpl).isHidden()
}
/** Holds if this node is a source. */
@@ -3287,6 +3287,14 @@ abstract private class PathNodeImpl extends PathNode {
abstract NodeEx getNodeEx();
+ predicate isHidden() {
+ hiddenNode(this.getNodeEx().asNode()) and
+ not this.isSource() and
+ not this instanceof PathNodeSink
+ or
+ this.getNodeEx() instanceof TNodeImplicitRead
+ }
+
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
@@ -3313,10 +3321,15 @@ abstract private class PathNodeImpl extends PathNode {
}
/** Holds if `n` can reach a sink. */
-private predicate reach(PathNode n) { n instanceof PathNodeSink or reach(n.getASuccessor()) }
+private predicate directReach(PathNode n) {
+ n instanceof PathNodeSink or directReach(n.getASuccessor())
+}
-/** Holds if `n1.getSucc() = n2` and `n2` can reach a sink. */
-private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and reach(n2) }
+/** Holds if `n` can reach a sink or is used in a subpath. */
+private predicate reach(PathNode n) { directReach(n) or Subpaths::retReach(n) }
+
+/** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */
+private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and directReach(n2) }
private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2)
@@ -3325,12 +3338,14 @@ private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1
*/
module PathGraph {
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
- query predicate edges(PathNode a, PathNode b) { pathSucc(a, b) }
+ query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b and reach(b) }
/** Holds if `n` is a node in the graph of data flow path explanations. */
query predicate nodes(PathNode n, string key, string val) {
reach(n) and key = "semmle.label" and val = n.toString()
}
+
+ query predicate subpaths = Subpaths::subpaths/4;
}
/**
@@ -3622,6 +3637,87 @@ private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext c
)
}
+private module Subpaths {
+ /**
+ * Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by
+ * `kind`, `sc`, `apout`, and `innercc`.
+ */
+ pragma[nomagic]
+ private predicate subpaths01(
+ PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
+ NodeEx out, AccessPath apout
+ ) {
+ pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and
+ pathIntoCallable(arg, par, _, innercc, sc, _) and
+ paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _,
+ unbindConf(arg.getConfiguration()))
+ }
+
+ /**
+ * Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by
+ * `kind`, `sc`, `apout`, and `innercc`.
+ */
+ pragma[nomagic]
+ private predicate subpaths02(
+ PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
+ NodeEx out, AccessPath apout
+ ) {
+ subpaths01(arg, par, sc, innercc, kind, out, apout) and
+ out.asNode() = kind.getAnOutNode(_)
+ }
+
+ pragma[nomagic]
+ private Configuration getPathNodeConf(PathNode n) { result = n.getConfiguration() }
+
+ /**
+ * Holds if `(arg, par, ret, out)` forms a subpath-tuple.
+ */
+ pragma[nomagic]
+ private predicate subpaths03(
+ PathNode arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, AccessPath apout
+ ) {
+ exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode |
+ subpaths02(arg, par, sc, innercc, kind, out, apout) and
+ ret.getNodeEx() = retnode and
+ kind = retnode.getKind() and
+ innercc = ret.getCallContext() and
+ sc = ret.getSummaryCtx() and
+ ret.getConfiguration() = unbindConf(getPathNodeConf(arg)) and
+ apout = ret.getAp() and
+ not ret.isHidden()
+ )
+ }
+
+ /**
+ * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through
+ * a subpath between `par` and `ret` with the connecting edges `arg -> par` and
+ * `ret -> out` is summarized as the edge `arg -> out`.
+ */
+ predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeMid ret, PathNodeMid out) {
+ exists(ParamNodeEx p, NodeEx o, AccessPath apout |
+ pragma[only_bind_into](arg).getASuccessor() = par and
+ pragma[only_bind_into](arg).getASuccessor() = out and
+ subpaths03(arg, p, ret, o, apout) and
+ par.getNodeEx() = p and
+ out.getNodeEx() = o and
+ out.getAp() = apout
+ )
+ }
+
+ /**
+ * Holds if `n` can reach a return node in a summarized subpath.
+ */
+ predicate retReach(PathNode n) {
+ subpaths(_, _, n, _)
+ or
+ exists(PathNode mid |
+ retReach(mid) and
+ n.getASuccessor() = mid and
+ not subpaths(_, mid, _, _)
+ )
+ }
+}
+
/**
* Holds if data can flow (inter-procedurally) from `source` to `sink`.
*
@@ -3941,7 +4037,7 @@ private module FlowExploration {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
- * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
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 5c2dbb30084..4ca06c93362 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll
@@ -923,28 +923,29 @@ private module Stage2 {
ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
- class Cc = boolean;
+ class Cc = CallContext;
- class CcCall extends Cc {
- CcCall() { this = true }
+ class CcCall = CallContextCall;
- /** Holds if this call context may be `call`. */
- predicate matchesCall(DataFlowCall call) { any() }
- }
+ class CcNoCall = CallContextNoCall;
- class CcNoCall extends Cc {
- CcNoCall() { this = false }
- }
-
- Cc ccNone() { result = false }
+ Cc ccNone() { result instanceof CallContextAny }
private class LocalCc = Unit;
bindingset[call, c, outercc]
- private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
+ 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[call, c, innercc]
- private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
+ 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, config]
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
@@ -1172,7 +1173,8 @@ private module Stage2 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
- fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ 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))
)
}
@@ -1860,7 +1862,8 @@ private module Stage3 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
- fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ 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))
)
}
@@ -2117,7 +2120,7 @@ private module Stage3 {
private predicate flowCandSummaryCtx(NodeEx node, AccessPathFront argApf, Configuration config) {
exists(AccessPathFront apf |
Stage3::revFlow(node, true, _, apf, config) and
- Stage3::fwdFlow(node, true, TAccessPathFrontSome(argApf), apf, config)
+ Stage3::fwdFlow(node, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
)
}
@@ -2136,7 +2139,8 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
) and
accessPathApproxCostLimits(apLimit, tupleLimit) and
apLimit < tails and
- tupleLimit < (tails - 1) * nodes
+ tupleLimit < (tails - 1) * nodes and
+ not tc.forceHighPrecision()
)
}
@@ -2618,7 +2622,8 @@ private module Stage4 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
- fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ 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))
)
}
@@ -2969,12 +2974,15 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) {
* expected to be expensive. Holds with `unfold = true` otherwise.
*/
private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) {
- exists(int aps, int nodes, int apLimit, int tupleLimit |
- aps = countPotentialAps(apa, config) and
- nodes = countNodesUsingAccessPath(apa, config) and
- accessPathCostLimits(apLimit, tupleLimit) and
- if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true
- )
+ if apa.getHead().forceHighPrecision()
+ then unfold = true
+ else
+ exists(int aps, int nodes, int apLimit, int tupleLimit |
+ aps = countPotentialAps(apa, config) and
+ nodes = countNodesUsingAccessPath(apa, config) and
+ accessPathCostLimits(apLimit, tupleLimit) and
+ if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true
+ )
}
/**
@@ -3244,7 +3252,7 @@ class PathNode extends TPathNode {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
- * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
@@ -3258,24 +3266,16 @@ class PathNode extends TPathNode {
/** Gets the associated configuration. */
Configuration getConfiguration() { none() }
- private predicate isHidden() {
- hiddenNode(this.(PathNodeImpl).getNodeEx().asNode()) and
- not this.isSource() and
- not this instanceof PathNodeSink
- or
- this.(PathNodeImpl).getNodeEx() instanceof TNodeImplicitRead
- }
-
private PathNode getASuccessorIfHidden() {
- this.isHidden() and
+ this.(PathNodeImpl).isHidden() and
result = this.(PathNodeImpl).getASuccessorImpl()
}
/** Gets a successor of this node, if any. */
final PathNode getASuccessor() {
result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and
- not this.isHidden() and
- not result.isHidden()
+ not this.(PathNodeImpl).isHidden() and
+ not result.(PathNodeImpl).isHidden()
}
/** Holds if this node is a source. */
@@ -3287,6 +3287,14 @@ abstract private class PathNodeImpl extends PathNode {
abstract NodeEx getNodeEx();
+ predicate isHidden() {
+ hiddenNode(this.getNodeEx().asNode()) and
+ not this.isSource() and
+ not this instanceof PathNodeSink
+ or
+ this.getNodeEx() instanceof TNodeImplicitRead
+ }
+
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
@@ -3313,10 +3321,15 @@ abstract private class PathNodeImpl extends PathNode {
}
/** Holds if `n` can reach a sink. */
-private predicate reach(PathNode n) { n instanceof PathNodeSink or reach(n.getASuccessor()) }
+private predicate directReach(PathNode n) {
+ n instanceof PathNodeSink or directReach(n.getASuccessor())
+}
-/** Holds if `n1.getSucc() = n2` and `n2` can reach a sink. */
-private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and reach(n2) }
+/** Holds if `n` can reach a sink or is used in a subpath. */
+private predicate reach(PathNode n) { directReach(n) or Subpaths::retReach(n) }
+
+/** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */
+private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and directReach(n2) }
private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2)
@@ -3325,12 +3338,14 @@ private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1
*/
module PathGraph {
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
- query predicate edges(PathNode a, PathNode b) { pathSucc(a, b) }
+ query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b and reach(b) }
/** Holds if `n` is a node in the graph of data flow path explanations. */
query predicate nodes(PathNode n, string key, string val) {
reach(n) and key = "semmle.label" and val = n.toString()
}
+
+ query predicate subpaths = Subpaths::subpaths/4;
}
/**
@@ -3622,6 +3637,87 @@ private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext c
)
}
+private module Subpaths {
+ /**
+ * Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by
+ * `kind`, `sc`, `apout`, and `innercc`.
+ */
+ pragma[nomagic]
+ private predicate subpaths01(
+ PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
+ NodeEx out, AccessPath apout
+ ) {
+ pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and
+ pathIntoCallable(arg, par, _, innercc, sc, _) and
+ paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _,
+ unbindConf(arg.getConfiguration()))
+ }
+
+ /**
+ * Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by
+ * `kind`, `sc`, `apout`, and `innercc`.
+ */
+ pragma[nomagic]
+ private predicate subpaths02(
+ PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
+ NodeEx out, AccessPath apout
+ ) {
+ subpaths01(arg, par, sc, innercc, kind, out, apout) and
+ out.asNode() = kind.getAnOutNode(_)
+ }
+
+ pragma[nomagic]
+ private Configuration getPathNodeConf(PathNode n) { result = n.getConfiguration() }
+
+ /**
+ * Holds if `(arg, par, ret, out)` forms a subpath-tuple.
+ */
+ pragma[nomagic]
+ private predicate subpaths03(
+ PathNode arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, AccessPath apout
+ ) {
+ exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode |
+ subpaths02(arg, par, sc, innercc, kind, out, apout) and
+ ret.getNodeEx() = retnode and
+ kind = retnode.getKind() and
+ innercc = ret.getCallContext() and
+ sc = ret.getSummaryCtx() and
+ ret.getConfiguration() = unbindConf(getPathNodeConf(arg)) and
+ apout = ret.getAp() and
+ not ret.isHidden()
+ )
+ }
+
+ /**
+ * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through
+ * a subpath between `par` and `ret` with the connecting edges `arg -> par` and
+ * `ret -> out` is summarized as the edge `arg -> out`.
+ */
+ predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeMid ret, PathNodeMid out) {
+ exists(ParamNodeEx p, NodeEx o, AccessPath apout |
+ pragma[only_bind_into](arg).getASuccessor() = par and
+ pragma[only_bind_into](arg).getASuccessor() = out and
+ subpaths03(arg, p, ret, o, apout) and
+ par.getNodeEx() = p and
+ out.getNodeEx() = o and
+ out.getAp() = apout
+ )
+ }
+
+ /**
+ * Holds if `n` can reach a return node in a summarized subpath.
+ */
+ predicate retReach(PathNode n) {
+ subpaths(_, _, n, _)
+ or
+ exists(PathNode mid |
+ retReach(mid) and
+ n.getASuccessor() = mid and
+ not subpaths(_, mid, _, _)
+ )
+ }
+}
+
/**
* Holds if data can flow (inter-procedurally) from `source` to `sink`.
*
@@ -3941,7 +4037,7 @@ private module FlowExploration {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
- * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
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 5c2dbb30084..4ca06c93362 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll
@@ -923,28 +923,29 @@ private module Stage2 {
ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
- class Cc = boolean;
+ class Cc = CallContext;
- class CcCall extends Cc {
- CcCall() { this = true }
+ class CcCall = CallContextCall;
- /** Holds if this call context may be `call`. */
- predicate matchesCall(DataFlowCall call) { any() }
- }
+ class CcNoCall = CallContextNoCall;
- class CcNoCall extends Cc {
- CcNoCall() { this = false }
- }
-
- Cc ccNone() { result = false }
+ Cc ccNone() { result instanceof CallContextAny }
private class LocalCc = Unit;
bindingset[call, c, outercc]
- private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
+ 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[call, c, innercc]
- private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
+ 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, config]
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
@@ -1172,7 +1173,8 @@ private module Stage2 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
- fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ 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))
)
}
@@ -1860,7 +1862,8 @@ private module Stage3 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
- fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ 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))
)
}
@@ -2117,7 +2120,7 @@ private module Stage3 {
private predicate flowCandSummaryCtx(NodeEx node, AccessPathFront argApf, Configuration config) {
exists(AccessPathFront apf |
Stage3::revFlow(node, true, _, apf, config) and
- Stage3::fwdFlow(node, true, TAccessPathFrontSome(argApf), apf, config)
+ Stage3::fwdFlow(node, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
)
}
@@ -2136,7 +2139,8 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
) and
accessPathApproxCostLimits(apLimit, tupleLimit) and
apLimit < tails and
- tupleLimit < (tails - 1) * nodes
+ tupleLimit < (tails - 1) * nodes and
+ not tc.forceHighPrecision()
)
}
@@ -2618,7 +2622,8 @@ private module Stage4 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
- fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ 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))
)
}
@@ -2969,12 +2974,15 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) {
* expected to be expensive. Holds with `unfold = true` otherwise.
*/
private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) {
- exists(int aps, int nodes, int apLimit, int tupleLimit |
- aps = countPotentialAps(apa, config) and
- nodes = countNodesUsingAccessPath(apa, config) and
- accessPathCostLimits(apLimit, tupleLimit) and
- if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true
- )
+ if apa.getHead().forceHighPrecision()
+ then unfold = true
+ else
+ exists(int aps, int nodes, int apLimit, int tupleLimit |
+ aps = countPotentialAps(apa, config) and
+ nodes = countNodesUsingAccessPath(apa, config) and
+ accessPathCostLimits(apLimit, tupleLimit) and
+ if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true
+ )
}
/**
@@ -3244,7 +3252,7 @@ class PathNode extends TPathNode {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
- * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
@@ -3258,24 +3266,16 @@ class PathNode extends TPathNode {
/** Gets the associated configuration. */
Configuration getConfiguration() { none() }
- private predicate isHidden() {
- hiddenNode(this.(PathNodeImpl).getNodeEx().asNode()) and
- not this.isSource() and
- not this instanceof PathNodeSink
- or
- this.(PathNodeImpl).getNodeEx() instanceof TNodeImplicitRead
- }
-
private PathNode getASuccessorIfHidden() {
- this.isHidden() and
+ this.(PathNodeImpl).isHidden() and
result = this.(PathNodeImpl).getASuccessorImpl()
}
/** Gets a successor of this node, if any. */
final PathNode getASuccessor() {
result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and
- not this.isHidden() and
- not result.isHidden()
+ not this.(PathNodeImpl).isHidden() and
+ not result.(PathNodeImpl).isHidden()
}
/** Holds if this node is a source. */
@@ -3287,6 +3287,14 @@ abstract private class PathNodeImpl extends PathNode {
abstract NodeEx getNodeEx();
+ predicate isHidden() {
+ hiddenNode(this.getNodeEx().asNode()) and
+ not this.isSource() and
+ not this instanceof PathNodeSink
+ or
+ this.getNodeEx() instanceof TNodeImplicitRead
+ }
+
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
@@ -3313,10 +3321,15 @@ abstract private class PathNodeImpl extends PathNode {
}
/** Holds if `n` can reach a sink. */
-private predicate reach(PathNode n) { n instanceof PathNodeSink or reach(n.getASuccessor()) }
+private predicate directReach(PathNode n) {
+ n instanceof PathNodeSink or directReach(n.getASuccessor())
+}
-/** Holds if `n1.getSucc() = n2` and `n2` can reach a sink. */
-private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and reach(n2) }
+/** Holds if `n` can reach a sink or is used in a subpath. */
+private predicate reach(PathNode n) { directReach(n) or Subpaths::retReach(n) }
+
+/** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */
+private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and directReach(n2) }
private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2)
@@ -3325,12 +3338,14 @@ private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1
*/
module PathGraph {
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
- query predicate edges(PathNode a, PathNode b) { pathSucc(a, b) }
+ query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b and reach(b) }
/** Holds if `n` is a node in the graph of data flow path explanations. */
query predicate nodes(PathNode n, string key, string val) {
reach(n) and key = "semmle.label" and val = n.toString()
}
+
+ query predicate subpaths = Subpaths::subpaths/4;
}
/**
@@ -3622,6 +3637,87 @@ private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext c
)
}
+private module Subpaths {
+ /**
+ * Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by
+ * `kind`, `sc`, `apout`, and `innercc`.
+ */
+ pragma[nomagic]
+ private predicate subpaths01(
+ PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
+ NodeEx out, AccessPath apout
+ ) {
+ pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and
+ pathIntoCallable(arg, par, _, innercc, sc, _) and
+ paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _,
+ unbindConf(arg.getConfiguration()))
+ }
+
+ /**
+ * Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by
+ * `kind`, `sc`, `apout`, and `innercc`.
+ */
+ pragma[nomagic]
+ private predicate subpaths02(
+ PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
+ NodeEx out, AccessPath apout
+ ) {
+ subpaths01(arg, par, sc, innercc, kind, out, apout) and
+ out.asNode() = kind.getAnOutNode(_)
+ }
+
+ pragma[nomagic]
+ private Configuration getPathNodeConf(PathNode n) { result = n.getConfiguration() }
+
+ /**
+ * Holds if `(arg, par, ret, out)` forms a subpath-tuple.
+ */
+ pragma[nomagic]
+ private predicate subpaths03(
+ PathNode arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, AccessPath apout
+ ) {
+ exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode |
+ subpaths02(arg, par, sc, innercc, kind, out, apout) and
+ ret.getNodeEx() = retnode and
+ kind = retnode.getKind() and
+ innercc = ret.getCallContext() and
+ sc = ret.getSummaryCtx() and
+ ret.getConfiguration() = unbindConf(getPathNodeConf(arg)) and
+ apout = ret.getAp() and
+ not ret.isHidden()
+ )
+ }
+
+ /**
+ * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through
+ * a subpath between `par` and `ret` with the connecting edges `arg -> par` and
+ * `ret -> out` is summarized as the edge `arg -> out`.
+ */
+ predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeMid ret, PathNodeMid out) {
+ exists(ParamNodeEx p, NodeEx o, AccessPath apout |
+ pragma[only_bind_into](arg).getASuccessor() = par and
+ pragma[only_bind_into](arg).getASuccessor() = out and
+ subpaths03(arg, p, ret, o, apout) and
+ par.getNodeEx() = p and
+ out.getNodeEx() = o and
+ out.getAp() = apout
+ )
+ }
+
+ /**
+ * Holds if `n` can reach a return node in a summarized subpath.
+ */
+ predicate retReach(PathNode n) {
+ subpaths(_, _, n, _)
+ or
+ exists(PathNode mid |
+ retReach(mid) and
+ n.getASuccessor() = mid and
+ not subpaths(_, mid, _, _)
+ )
+ }
+}
+
/**
* Holds if data can flow (inter-procedurally) from `source` to `sink`.
*
@@ -3941,7 +4037,7 @@ private module FlowExploration {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
- * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
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 5c2dbb30084..4ca06c93362 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll
@@ -923,28 +923,29 @@ private module Stage2 {
ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
- class Cc = boolean;
+ class Cc = CallContext;
- class CcCall extends Cc {
- CcCall() { this = true }
+ class CcCall = CallContextCall;
- /** Holds if this call context may be `call`. */
- predicate matchesCall(DataFlowCall call) { any() }
- }
+ class CcNoCall = CallContextNoCall;
- class CcNoCall extends Cc {
- CcNoCall() { this = false }
- }
-
- Cc ccNone() { result = false }
+ Cc ccNone() { result instanceof CallContextAny }
private class LocalCc = Unit;
bindingset[call, c, outercc]
- private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
+ 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[call, c, innercc]
- private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
+ 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, config]
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
@@ -1172,7 +1173,8 @@ private module Stage2 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
- fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ 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))
)
}
@@ -1860,7 +1862,8 @@ private module Stage3 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
- fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ 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))
)
}
@@ -2117,7 +2120,7 @@ private module Stage3 {
private predicate flowCandSummaryCtx(NodeEx node, AccessPathFront argApf, Configuration config) {
exists(AccessPathFront apf |
Stage3::revFlow(node, true, _, apf, config) and
- Stage3::fwdFlow(node, true, TAccessPathFrontSome(argApf), apf, config)
+ Stage3::fwdFlow(node, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
)
}
@@ -2136,7 +2139,8 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
) and
accessPathApproxCostLimits(apLimit, tupleLimit) and
apLimit < tails and
- tupleLimit < (tails - 1) * nodes
+ tupleLimit < (tails - 1) * nodes and
+ not tc.forceHighPrecision()
)
}
@@ -2618,7 +2622,8 @@ private module Stage4 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
- fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ 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))
)
}
@@ -2969,12 +2974,15 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) {
* expected to be expensive. Holds with `unfold = true` otherwise.
*/
private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) {
- exists(int aps, int nodes, int apLimit, int tupleLimit |
- aps = countPotentialAps(apa, config) and
- nodes = countNodesUsingAccessPath(apa, config) and
- accessPathCostLimits(apLimit, tupleLimit) and
- if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true
- )
+ if apa.getHead().forceHighPrecision()
+ then unfold = true
+ else
+ exists(int aps, int nodes, int apLimit, int tupleLimit |
+ aps = countPotentialAps(apa, config) and
+ nodes = countNodesUsingAccessPath(apa, config) and
+ accessPathCostLimits(apLimit, tupleLimit) and
+ if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true
+ )
}
/**
@@ -3244,7 +3252,7 @@ class PathNode extends TPathNode {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
- * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
@@ -3258,24 +3266,16 @@ class PathNode extends TPathNode {
/** Gets the associated configuration. */
Configuration getConfiguration() { none() }
- private predicate isHidden() {
- hiddenNode(this.(PathNodeImpl).getNodeEx().asNode()) and
- not this.isSource() and
- not this instanceof PathNodeSink
- or
- this.(PathNodeImpl).getNodeEx() instanceof TNodeImplicitRead
- }
-
private PathNode getASuccessorIfHidden() {
- this.isHidden() and
+ this.(PathNodeImpl).isHidden() and
result = this.(PathNodeImpl).getASuccessorImpl()
}
/** Gets a successor of this node, if any. */
final PathNode getASuccessor() {
result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and
- not this.isHidden() and
- not result.isHidden()
+ not this.(PathNodeImpl).isHidden() and
+ not result.(PathNodeImpl).isHidden()
}
/** Holds if this node is a source. */
@@ -3287,6 +3287,14 @@ abstract private class PathNodeImpl extends PathNode {
abstract NodeEx getNodeEx();
+ predicate isHidden() {
+ hiddenNode(this.getNodeEx().asNode()) and
+ not this.isSource() and
+ not this instanceof PathNodeSink
+ or
+ this.getNodeEx() instanceof TNodeImplicitRead
+ }
+
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
@@ -3313,10 +3321,15 @@ abstract private class PathNodeImpl extends PathNode {
}
/** Holds if `n` can reach a sink. */
-private predicate reach(PathNode n) { n instanceof PathNodeSink or reach(n.getASuccessor()) }
+private predicate directReach(PathNode n) {
+ n instanceof PathNodeSink or directReach(n.getASuccessor())
+}
-/** Holds if `n1.getSucc() = n2` and `n2` can reach a sink. */
-private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and reach(n2) }
+/** Holds if `n` can reach a sink or is used in a subpath. */
+private predicate reach(PathNode n) { directReach(n) or Subpaths::retReach(n) }
+
+/** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */
+private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and directReach(n2) }
private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2)
@@ -3325,12 +3338,14 @@ private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1
*/
module PathGraph {
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
- query predicate edges(PathNode a, PathNode b) { pathSucc(a, b) }
+ query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b and reach(b) }
/** Holds if `n` is a node in the graph of data flow path explanations. */
query predicate nodes(PathNode n, string key, string val) {
reach(n) and key = "semmle.label" and val = n.toString()
}
+
+ query predicate subpaths = Subpaths::subpaths/4;
}
/**
@@ -3622,6 +3637,87 @@ private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext c
)
}
+private module Subpaths {
+ /**
+ * Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by
+ * `kind`, `sc`, `apout`, and `innercc`.
+ */
+ pragma[nomagic]
+ private predicate subpaths01(
+ PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
+ NodeEx out, AccessPath apout
+ ) {
+ pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and
+ pathIntoCallable(arg, par, _, innercc, sc, _) and
+ paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _,
+ unbindConf(arg.getConfiguration()))
+ }
+
+ /**
+ * Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by
+ * `kind`, `sc`, `apout`, and `innercc`.
+ */
+ pragma[nomagic]
+ private predicate subpaths02(
+ PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
+ NodeEx out, AccessPath apout
+ ) {
+ subpaths01(arg, par, sc, innercc, kind, out, apout) and
+ out.asNode() = kind.getAnOutNode(_)
+ }
+
+ pragma[nomagic]
+ private Configuration getPathNodeConf(PathNode n) { result = n.getConfiguration() }
+
+ /**
+ * Holds if `(arg, par, ret, out)` forms a subpath-tuple.
+ */
+ pragma[nomagic]
+ private predicate subpaths03(
+ PathNode arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, AccessPath apout
+ ) {
+ exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode |
+ subpaths02(arg, par, sc, innercc, kind, out, apout) and
+ ret.getNodeEx() = retnode and
+ kind = retnode.getKind() and
+ innercc = ret.getCallContext() and
+ sc = ret.getSummaryCtx() and
+ ret.getConfiguration() = unbindConf(getPathNodeConf(arg)) and
+ apout = ret.getAp() and
+ not ret.isHidden()
+ )
+ }
+
+ /**
+ * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through
+ * a subpath between `par` and `ret` with the connecting edges `arg -> par` and
+ * `ret -> out` is summarized as the edge `arg -> out`.
+ */
+ predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeMid ret, PathNodeMid out) {
+ exists(ParamNodeEx p, NodeEx o, AccessPath apout |
+ pragma[only_bind_into](arg).getASuccessor() = par and
+ pragma[only_bind_into](arg).getASuccessor() = out and
+ subpaths03(arg, p, ret, o, apout) and
+ par.getNodeEx() = p and
+ out.getNodeEx() = o and
+ out.getAp() = apout
+ )
+ }
+
+ /**
+ * Holds if `n` can reach a return node in a summarized subpath.
+ */
+ predicate retReach(PathNode n) {
+ subpaths(_, _, n, _)
+ or
+ exists(PathNode mid |
+ retReach(mid) and
+ n.getASuccessor() = mid and
+ not subpaths(_, mid, _, _)
+ )
+ }
+}
+
/**
* Holds if data can flow (inter-procedurally) from `source` to `sink`.
*
@@ -3941,7 +4037,7 @@ private module FlowExploration {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
- * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
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 5c2dbb30084..4ca06c93362 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll
@@ -923,28 +923,29 @@ private module Stage2 {
ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
- class Cc = boolean;
+ class Cc = CallContext;
- class CcCall extends Cc {
- CcCall() { this = true }
+ class CcCall = CallContextCall;
- /** Holds if this call context may be `call`. */
- predicate matchesCall(DataFlowCall call) { any() }
- }
+ class CcNoCall = CallContextNoCall;
- class CcNoCall extends Cc {
- CcNoCall() { this = false }
- }
-
- Cc ccNone() { result = false }
+ Cc ccNone() { result instanceof CallContextAny }
private class LocalCc = Unit;
bindingset[call, c, outercc]
- private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
+ 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[call, c, innercc]
- private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
+ 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, config]
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
@@ -1172,7 +1173,8 @@ private module Stage2 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
- fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ 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))
)
}
@@ -1860,7 +1862,8 @@ private module Stage3 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
- fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ 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))
)
}
@@ -2117,7 +2120,7 @@ private module Stage3 {
private predicate flowCandSummaryCtx(NodeEx node, AccessPathFront argApf, Configuration config) {
exists(AccessPathFront apf |
Stage3::revFlow(node, true, _, apf, config) and
- Stage3::fwdFlow(node, true, TAccessPathFrontSome(argApf), apf, config)
+ Stage3::fwdFlow(node, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
)
}
@@ -2136,7 +2139,8 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
) and
accessPathApproxCostLimits(apLimit, tupleLimit) and
apLimit < tails and
- tupleLimit < (tails - 1) * nodes
+ tupleLimit < (tails - 1) * nodes and
+ not tc.forceHighPrecision()
)
}
@@ -2618,7 +2622,8 @@ private module Stage4 {
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
- fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
+ 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))
)
}
@@ -2969,12 +2974,15 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) {
* expected to be expensive. Holds with `unfold = true` otherwise.
*/
private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) {
- exists(int aps, int nodes, int apLimit, int tupleLimit |
- aps = countPotentialAps(apa, config) and
- nodes = countNodesUsingAccessPath(apa, config) and
- accessPathCostLimits(apLimit, tupleLimit) and
- if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true
- )
+ if apa.getHead().forceHighPrecision()
+ then unfold = true
+ else
+ exists(int aps, int nodes, int apLimit, int tupleLimit |
+ aps = countPotentialAps(apa, config) and
+ nodes = countNodesUsingAccessPath(apa, config) and
+ accessPathCostLimits(apLimit, tupleLimit) and
+ if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true
+ )
}
/**
@@ -3244,7 +3252,7 @@ class PathNode extends TPathNode {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
- * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
@@ -3258,24 +3266,16 @@ class PathNode extends TPathNode {
/** Gets the associated configuration. */
Configuration getConfiguration() { none() }
- private predicate isHidden() {
- hiddenNode(this.(PathNodeImpl).getNodeEx().asNode()) and
- not this.isSource() and
- not this instanceof PathNodeSink
- or
- this.(PathNodeImpl).getNodeEx() instanceof TNodeImplicitRead
- }
-
private PathNode getASuccessorIfHidden() {
- this.isHidden() and
+ this.(PathNodeImpl).isHidden() and
result = this.(PathNodeImpl).getASuccessorImpl()
}
/** Gets a successor of this node, if any. */
final PathNode getASuccessor() {
result = this.(PathNodeImpl).getASuccessorImpl().getASuccessorIfHidden*() and
- not this.isHidden() and
- not result.isHidden()
+ not this.(PathNodeImpl).isHidden() and
+ not result.(PathNodeImpl).isHidden()
}
/** Holds if this node is a source. */
@@ -3287,6 +3287,14 @@ abstract private class PathNodeImpl extends PathNode {
abstract NodeEx getNodeEx();
+ predicate isHidden() {
+ hiddenNode(this.getNodeEx().asNode()) and
+ not this.isSource() and
+ not this instanceof PathNodeSink
+ or
+ this.getNodeEx() instanceof TNodeImplicitRead
+ }
+
private string ppAp() {
this instanceof PathNodeSink and result = ""
or
@@ -3313,10 +3321,15 @@ abstract private class PathNodeImpl extends PathNode {
}
/** Holds if `n` can reach a sink. */
-private predicate reach(PathNode n) { n instanceof PathNodeSink or reach(n.getASuccessor()) }
+private predicate directReach(PathNode n) {
+ n instanceof PathNodeSink or directReach(n.getASuccessor())
+}
-/** Holds if `n1.getSucc() = n2` and `n2` can reach a sink. */
-private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and reach(n2) }
+/** Holds if `n` can reach a sink or is used in a subpath. */
+private predicate reach(PathNode n) { directReach(n) or Subpaths::retReach(n) }
+
+/** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */
+private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and directReach(n2) }
private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2)
@@ -3325,12 +3338,14 @@ private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1
*/
module PathGraph {
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
- query predicate edges(PathNode a, PathNode b) { pathSucc(a, b) }
+ query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b and reach(b) }
/** Holds if `n` is a node in the graph of data flow path explanations. */
query predicate nodes(PathNode n, string key, string val) {
reach(n) and key = "semmle.label" and val = n.toString()
}
+
+ query predicate subpaths = Subpaths::subpaths/4;
}
/**
@@ -3622,6 +3637,87 @@ private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext c
)
}
+private module Subpaths {
+ /**
+ * Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by
+ * `kind`, `sc`, `apout`, and `innercc`.
+ */
+ pragma[nomagic]
+ private predicate subpaths01(
+ PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
+ NodeEx out, AccessPath apout
+ ) {
+ pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and
+ pathIntoCallable(arg, par, _, innercc, sc, _) and
+ paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _,
+ unbindConf(arg.getConfiguration()))
+ }
+
+ /**
+ * Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by
+ * `kind`, `sc`, `apout`, and `innercc`.
+ */
+ pragma[nomagic]
+ private predicate subpaths02(
+ PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
+ NodeEx out, AccessPath apout
+ ) {
+ subpaths01(arg, par, sc, innercc, kind, out, apout) and
+ out.asNode() = kind.getAnOutNode(_)
+ }
+
+ pragma[nomagic]
+ private Configuration getPathNodeConf(PathNode n) { result = n.getConfiguration() }
+
+ /**
+ * Holds if `(arg, par, ret, out)` forms a subpath-tuple.
+ */
+ pragma[nomagic]
+ private predicate subpaths03(
+ PathNode arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, AccessPath apout
+ ) {
+ exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode |
+ subpaths02(arg, par, sc, innercc, kind, out, apout) and
+ ret.getNodeEx() = retnode and
+ kind = retnode.getKind() and
+ innercc = ret.getCallContext() and
+ sc = ret.getSummaryCtx() and
+ ret.getConfiguration() = unbindConf(getPathNodeConf(arg)) and
+ apout = ret.getAp() and
+ not ret.isHidden()
+ )
+ }
+
+ /**
+ * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through
+ * a subpath between `par` and `ret` with the connecting edges `arg -> par` and
+ * `ret -> out` is summarized as the edge `arg -> out`.
+ */
+ predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeMid ret, PathNodeMid out) {
+ exists(ParamNodeEx p, NodeEx o, AccessPath apout |
+ pragma[only_bind_into](arg).getASuccessor() = par and
+ pragma[only_bind_into](arg).getASuccessor() = out and
+ subpaths03(arg, p, ret, o, apout) and
+ par.getNodeEx() = p and
+ out.getNodeEx() = o and
+ out.getAp() = apout
+ )
+ }
+
+ /**
+ * Holds if `n` can reach a return node in a summarized subpath.
+ */
+ predicate retReach(PathNode n) {
+ subpaths(_, _, n, _)
+ or
+ exists(PathNode mid |
+ retReach(mid) and
+ n.getASuccessor() = mid and
+ not subpaths(_, mid, _, _)
+ )
+ }
+}
+
/**
* Holds if data can flow (inter-procedurally) from `source` to `sink`.
*
@@ -3941,7 +4037,7 @@ private module FlowExploration {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
- * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll
index 728f7b56c42..f43a550af57 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll
@@ -786,13 +786,18 @@ private module Cached {
}
/**
- * Holds if the call context `call` either improves virtual dispatch in
- * `callable` or if it allows us to prune unreachable nodes in `callable`.
+ * Holds if the call context `call` improves virtual dispatch in `callable`.
*/
cached
- predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) {
+ predicate recordDataFlowCallSiteDispatch(DataFlowCall call, DataFlowCallable callable) {
reducedViableImplInCallContext(_, callable, call)
- or
+ }
+
+ /**
+ * Holds if the call context `call` allows us to prune unreachable nodes in `callable`.
+ */
+ cached
+ predicate recordDataFlowCallSiteUnreachable(DataFlowCall call, DataFlowCallable callable) {
exists(Node n | getNodeEnclosingCallable(n) = callable | isUnreachableInCallCached(n, call))
}
@@ -846,6 +851,15 @@ private module Cached {
TAccessPathFrontSome(AccessPathFront apf)
}
+/**
+ * Holds if the call context `call` either improves virtual dispatch in
+ * `callable` or if it allows us to prune unreachable nodes in `callable`.
+ */
+predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) {
+ recordDataFlowCallSiteDispatch(call, callable) or
+ recordDataFlowCallSiteUnreachable(call, callable)
+}
+
/**
* A `Node` at which a cast can occur such that the type should be checked.
*/
@@ -1222,6 +1236,13 @@ class TypedContent extends MkTypedContent {
/** Gets a textual representation of this content. */
string toString() { result = c.toString() }
+
+ /**
+ * Holds if access paths with this `TypedContent` at their head always should
+ * be tracked at high precision. This disables adaptive access path precision
+ * for such access paths.
+ */
+ predicate forceHighPrecision() { forceHighPrecision(c) }
}
/**
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll
index ca4d0fa98e7..d75afd20b6c 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll
@@ -1918,6 +1918,12 @@ private predicate viableConstantBooleanParamArg(
int accessPathLimit() { result = 5 }
+/**
+ * Holds if access paths with `c` at their head always should be tracked at high
+ * precision. This disables adaptive access path precision for such access paths.
+ */
+predicate forceHighPrecision(Content c) { c instanceof ElementContent }
+
/** The unit type. */
private newtype TUnit = TMkUnit()
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPublic.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPublic.qll
index bfc2f5469d0..56286903951 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPublic.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPublic.qll
@@ -58,7 +58,7 @@ class Node extends TNode {
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
- * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll
index 523516e60f8..8bca0699e04 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll
@@ -186,10 +186,17 @@ module Private {
TArgumentSummaryComponent(int i) { parameterPosition(i) } or
TReturnSummaryComponent(ReturnKind rk)
+ private TSummaryComponent thisParam() {
+ result = TParameterSummaryComponent(instanceParameterPosition())
+ }
+
newtype TSummaryComponentStack =
TSingletonSummaryComponentStack(SummaryComponent c) or
TConsSummaryComponentStack(SummaryComponent head, SummaryComponentStack tail) {
tail.(RequiredSummaryComponentStack).required(head)
+ or
+ tail.(RequiredSummaryComponentStack).required(TParameterSummaryComponent(_)) and
+ head = thisParam()
}
pragma[nomagic]
@@ -198,21 +205,63 @@ module Private {
boolean preservesValue
) {
c.propagatesFlow(input, output, preservesValue)
+ or
+ // observe side effects of callbacks on input arguments
+ c.propagatesFlow(output, input, preservesValue) and
+ preservesValue = true and
+ isCallbackParameter(input) and
+ isContentOfArgument(output)
+ or
+ // flow from the receiver of a callback into the instance-parameter
+ exists(SummaryComponentStack s, SummaryComponentStack callbackRef |
+ c.propagatesFlow(s, _, _) or c.propagatesFlow(_, s, _)
+ |
+ callbackRef = s.drop(_) and
+ (isCallbackParameter(callbackRef) or callbackRef.head() = TReturnSummaryComponent(_)) and
+ input = callbackRef.tail() and
+ output = TConsSummaryComponentStack(thisParam(), input) and
+ preservesValue = true
+ )
+ }
+
+ private predicate isCallbackParameter(SummaryComponentStack s) {
+ s.head() = TParameterSummaryComponent(_) and exists(s.tail())
+ }
+
+ private predicate isContentOfArgument(SummaryComponentStack s) {
+ s.head() = TContentSummaryComponent(_) and isContentOfArgument(s.tail())
+ or
+ s = TSingletonSummaryComponentStack(TArgumentSummaryComponent(_))
+ }
+
+ private predicate outputState(SummarizedCallable c, SummaryComponentStack s) {
+ summary(c, _, s, _)
+ or
+ exists(SummaryComponentStack out |
+ outputState(c, out) and
+ out.head() = TContentSummaryComponent(_) and
+ s = out.tail()
+ )
+ or
+ // Add the argument node corresponding to the requested post-update node
+ inputState(c, s) and isCallbackParameter(s)
+ }
+
+ private predicate inputState(SummarizedCallable c, SummaryComponentStack s) {
+ summary(c, s, _, _)
+ or
+ exists(SummaryComponentStack inp | inputState(c, inp) and s = inp.tail())
+ or
+ exists(SummaryComponentStack out |
+ outputState(c, out) and
+ out.head() = TParameterSummaryComponent(_) and
+ s = out.tail()
+ )
}
private newtype TSummaryNodeState =
- TSummaryNodeInputState(SummaryComponentStack s) {
- exists(SummaryComponentStack input |
- summary(_, input, _, _) and
- s = input.drop(_)
- )
- } or
- TSummaryNodeOutputState(SummaryComponentStack s) {
- exists(SummaryComponentStack output |
- summary(_, _, output, _) and
- s = output.drop(_)
- )
- }
+ TSummaryNodeInputState(SummaryComponentStack s) { inputState(_, s) } or
+ TSummaryNodeOutputState(SummaryComponentStack s) { outputState(_, s) }
/**
* A state used to break up (complex) flow summaries into atomic flow steps.
@@ -238,20 +287,14 @@ module Private {
pragma[nomagic]
predicate isInputState(SummarizedCallable c, SummaryComponentStack s) {
this = TSummaryNodeInputState(s) and
- exists(SummaryComponentStack input |
- summary(c, input, _, _) and
- s = input.drop(_)
- )
+ inputState(c, s)
}
/** Holds if this state is a valid output state for `c`. */
pragma[nomagic]
predicate isOutputState(SummarizedCallable c, SummaryComponentStack s) {
this = TSummaryNodeOutputState(s) and
- exists(SummaryComponentStack output |
- summary(c, _, output, _) and
- s = output.drop(_)
- )
+ outputState(c, s)
}
/** Gets a textual representation of this state. */
@@ -331,19 +374,12 @@ module Private {
receiver = summaryNodeInputState(c, s.drop(1))
}
- private Node pre(Node post) {
- summaryPostUpdateNode(post, result)
- or
- not summaryPostUpdateNode(post, _) and
- result = post
- }
-
private predicate callbackInput(
SummarizedCallable c, SummaryComponentStack s, Node receiver, int i
) {
any(SummaryNodeState state).isOutputState(c, s) and
s.head() = TParameterSummaryComponent(i) and
- receiver = pre(summaryNodeOutputState(c, s.drop(1)))
+ receiver = summaryNodeInputState(c, s.drop(1))
}
/** Holds if a call targeting `receiver` should be synthesized inside `c`. */
@@ -395,7 +431,7 @@ module Private {
or
exists(int i | head = TParameterSummaryComponent(i) |
result =
- getCallbackParameterType(getNodeType(summaryNodeOutputState(pragma[only_bind_out](c),
+ getCallbackParameterType(getNodeType(summaryNodeInputState(pragma[only_bind_out](c),
s.drop(1))), i)
)
)
@@ -421,10 +457,16 @@ module Private {
}
/** Holds if summary node `post` is a post-update node with pre-update node `pre`. */
- predicate summaryPostUpdateNode(Node post, ParamNode pre) {
+ predicate summaryPostUpdateNode(Node post, Node pre) {
exists(SummarizedCallable c, int i |
isParameterPostUpdate(post, c, i) and
- pre.isParameterOf(c, i)
+ pre.(ParamNode).isParameterOf(c, i)
+ )
+ or
+ exists(SummarizedCallable callable, SummaryComponentStack s |
+ callbackInput(callable, s, _, _) and
+ pre = summaryNodeOutputState(callable, s) and
+ post = summaryNodeInputState(callable, s)
)
}
@@ -462,7 +504,11 @@ module Private {
// for `StringBuilder.append(x)` with a specified value flow from qualifier to
// return value and taint flow from argument 0 to the qualifier, then this
// allows us to infer taint flow from argument 0 to the return value.
- summaryPostUpdateNode(pred, succ) and preservesValue = true
+ succ instanceof ParamNode and summaryPostUpdateNode(pred, succ) and preservesValue = true
+ or
+ // Similarly we would like to chain together summaries where values get passed
+ // into callbacks along the way.
+ pred instanceof ArgNode and summaryPostUpdateNode(succ, pred) and preservesValue = true
}
/**
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImplSpecific.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImplSpecific.qll
index b0f67e8692f..822822a24c6 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImplSpecific.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImplSpecific.qll
@@ -16,6 +16,9 @@ private import semmle.code.csharp.dataflow.ExternalFlow
/** Holds is `i` is a valid parameter position. */
predicate parameterPosition(int i) { i in [-1 .. any(Parameter p).getPosition()] }
+/** Gets the parameter position of the instance parameter. */
+int instanceParameterPosition() { none() } // disables implicit summary flow to `this` for callbacks
+
/** Gets the synthesized summary data-flow node for the given values. */
Node summaryNode(SummarizedCallable c, SummaryNodeState state) { result = TSummaryNode(c, state) }
diff --git a/csharp/ql/lib/semmle/code/csharp/dispatch/Dispatch.qll b/csharp/ql/lib/semmle/code/csharp/dispatch/Dispatch.qll
index 80066392c1c..d0b4ef45ce8 100644
--- a/csharp/ql/lib/semmle/code/csharp/dispatch/Dispatch.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dispatch/Dispatch.qll
@@ -508,7 +508,7 @@ private module Internal {
override RuntimeCallable getADynamicTarget() {
result = getAViableInherited()
or
- result = getAViableOverrider() and strictcount(getAViableOverrider()) < 1000
+ result = getAViableOverrider()
or
// Simple case: target method cannot be overridden
result = getAStaticTarget() and
diff --git a/csharp/ql/lib/semmle/code/csharp/dispatch/RuntimeCallable.qll b/csharp/ql/lib/semmle/code/csharp/dispatch/RuntimeCallable.qll
index 22758fce4ab..bb279fcb4fb 100644
--- a/csharp/ql/lib/semmle/code/csharp/dispatch/RuntimeCallable.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dispatch/RuntimeCallable.qll
@@ -15,7 +15,10 @@ private import dotnet
class RuntimeCallable extends DotNet::Callable {
RuntimeCallable() {
not this.(Modifiable).isAbstract() and
- not getDeclaringType() instanceof Interface
+ (
+ not getDeclaringType() instanceof Interface or
+ this.(Virtualizable).isVirtual()
+ )
}
}
diff --git a/csharp/ql/lib/semmle/code/csharp/frameworks/EntityFramework.qll b/csharp/ql/lib/semmle/code/csharp/frameworks/EntityFramework.qll
index 29939efccdb..36882d3b12e 100644
--- a/csharp/ql/lib/semmle/code/csharp/frameworks/EntityFramework.qll
+++ b/csharp/ql/lib/semmle/code/csharp/frameworks/EntityFramework.qll
@@ -9,6 +9,7 @@ private import semmle.code.csharp.frameworks.system.data.Entity
private import semmle.code.csharp.frameworks.system.collections.Generic
private import semmle.code.csharp.frameworks.Sql
private import semmle.code.csharp.dataflow.FlowSummary
+private import semmle.code.csharp.dataflow.ExternalFlow
private import semmle.code.csharp.dataflow.internal.DataFlowPrivate as DataFlowPrivate
/**
@@ -234,26 +235,29 @@ module EntityFramework {
override Expr getSql() { result = this.getArgumentForParameter(sqlParam) }
}
- /** A call to `System.Data.Entity.DbSet.SqlQuery`. */
- class SystemDataEntityDbSetSqlExpr extends SqlExpr, MethodCall {
- SystemDataEntityDbSetSqlExpr() {
- this.getTarget() = any(SystemDataEntity::DbSet dbSet).getSqlQueryMethod()
+ /** The sink method `System.Data.Entity.DbSet.SqlQuery`. */
+ private class SystemDataEntityDbSetSqlQuerySinkModelCsv extends SinkModelCsv {
+ override predicate row(string row) {
+ row =
+ ["System.Data.Entity;DbSet;false;SqlQuery;(System.String,System.Object[]);;Argument[0];sql"]
}
-
- override Expr getSql() { result = this.getArgumentForName("sql") }
}
- /** A call to a method in `System.Data.Entity.Database` that executes SQL. */
- class SystemDataEntityDatabaseSqlExpr extends SqlExpr, MethodCall {
- SystemDataEntityDatabaseSqlExpr() {
- exists(SystemDataEntity::Database db |
- this.getTarget() = db.getSqlQueryMethod() or
- this.getTarget() = db.getExecuteSqlCommandMethod() or
- this.getTarget() = db.getExecuteSqlCommandAsyncMethod()
- )
+ /** A sink method in `System.Data.Entity.Database` that executes SQL. */
+ private class SystemDataEntityDatabaseSinkModelCsv extends SinkModelCsv {
+ override predicate row(string row) {
+ row =
+ [
+ "System.Data.Entity;Database;false;SqlQuery;(System.Type,System.String,System.Object[]);;Argument[1];sql",
+ "System.Data.Entity;Database;false;SqlQuery<>;(System.String,System.Object[]);;Argument[0];sql",
+ "System.Data.Entity;Database;false;ExecuteSqlCommand;(System.String,System.Object[]);;Argument[0];sql",
+ "System.Data.Entity;Database;false;ExecuteSqlCommand;(System.Data.Entity.TransactionalBehavior,System.String,System.Object[]);;Argument[1];sql",
+ "System.Data.Entity;Database;false;ExecuteSqlCommandAsync;(System.Data.Entity.TransactionalBehavior,System.String,System.Threading.CancellationToken,System.Object[]);;Argument[1];sql",
+ "System.Data.Entity;Database;false;ExecuteSqlCommandAsync;(System.String,System.Threading.CancellationToken,System.Object[]);;Argument[0];sql",
+ "System.Data.Entity;Database;false;ExecuteSqlCommandAsync;(System.String,System.Object[]);;Argument[0];sql",
+ "System.Data.Entity;Database;false;ExecuteSqlCommandAsync;(System.Data.Entity.TransactionalBehavior,System.String,System.Object[]);;Argument[1];sql"
+ ]
}
-
- override Expr getSql() { result = this.getArgumentForName("sql") }
}
/** Holds if `t` is compatible with a DB column type. */
diff --git a/csharp/ql/lib/semmle/code/csharp/frameworks/ServiceStack.qll b/csharp/ql/lib/semmle/code/csharp/frameworks/ServiceStack.qll
new file mode 100644
index 00000000000..6604597d9bc
--- /dev/null
+++ b/csharp/ql/lib/semmle/code/csharp/frameworks/ServiceStack.qll
@@ -0,0 +1,321 @@
+/**
+ * General modelling of ServiceStack framework including separate modules for:
+ * - flow sources
+ * - SQLi sinks
+ * - XSS sinks
+ */
+
+import csharp
+private import semmle.code.csharp.dataflow.ExternalFlow
+
+/** A class representing a Service */
+private class ServiceClass extends Class {
+ ServiceClass() {
+ this.getBaseClass+().hasQualifiedName("ServiceStack", "Service") or
+ this.getABaseType*().getABaseInterface().hasQualifiedName("ServiceStack", "IService")
+ }
+
+ /** Get a method that handles incoming requests */
+ Method getARequestMethod() {
+ exists(string name |
+ result = this.getAMethod(name) and
+ name.regexpMatch("(Get|Post|Put|Delete|Any|Option|Head|Patch)(Async|Json|Xml|Jsv|Csv|Html|Protobuf|Msgpack|Wire)?")
+ )
+ }
+}
+
+/** Top-level Request DTO types */
+private class RequestDTO extends Class {
+ RequestDTO() {
+ this.getABaseType*().getABaseInterface().hasQualifiedName("ServiceStack", "IReturn")
+ }
+}
+
+/** Flow sources for the ServiceStack framework */
+module Sources {
+ private import semmle.code.csharp.security.dataflow.flowsources.Remote
+
+ /**
+ * Remote flow sources for ServiceStack. Parameters of well-known `request` methods.
+ */
+ private class ServiceStackSource extends RemoteFlowSource {
+ ServiceStackSource() {
+ exists(ServiceClass service |
+ service.getARequestMethod().getAParameter() = this.asParameter()
+ )
+ }
+
+ override string getSourceType() { result = "ServiceStack request parameter" }
+ }
+}
+
+private class ServiceStackRemoteSinkModelCsv extends SinkModelCsv {
+ override predicate row(string row) {
+ row =
+ [
+ // IRestClient
+ "ServiceStack;IRestClient;true;Send<>;(System.String,System.String,System.Object);;Argument[2];remote",
+ "ServiceStack;IRestClient;true;Patch<>;(System.String,System.Object);;Argument[1];remote",
+ "ServiceStack;IRestClient;true;Post<>;(System.String,System.Object);;Argument[1];remote",
+ "ServiceStack;IRestClient;true;Put<>;(System.String,System.Object);;Argument[1];remote",
+ // IRestClientSync
+ "ServiceStack;IRestClientSync;true;CustomMethod;(System.String,ServiceStack.IReturnVoid);;Argument[1];remote",
+ "ServiceStack;IRestClientSync;true;CustomMethod<>;(System.String,System.Object);;Argument[1];remote",
+ "ServiceStack;IRestClientSync;true;CustomMethod<>;(System.String,ServiceStack.IReturn);;Argument[1];remote",
+ "ServiceStack;IRestClientSync;true;Delete;(ServiceStack.IReturnVoid);;Argument[0];remote",
+ "ServiceStack;IRestClientSync;true;Delete<>;(System.Object);;Argument[0];remote",
+ "ServiceStack;IRestClientSync;true;Delete<>;(ServiceStack.IReturn);;Argument[0];remote",
+ "ServiceStack;IRestClientSync;true;Get;(ServiceStack.IReturnVoid);;Argument[0];remote",
+ "ServiceStack;IRestClientSync;true;Get<>;(System.Object);;Argument[0];remote",
+ "ServiceStack;IRestClientSync;true;Get<>;(ServiceStack.IReturn);;Argument[0];remote",
+ "ServiceStack;IRestClientSync;true;Patch;(ServiceStack.IReturnVoid);;Argument[0];remote",
+ "ServiceStack;IRestClientSync;true;Patch<>;(System.Object);;Argument[0];remote",
+ "ServiceStack;IRestClientSync;true;Patch<>;(ServiceStack.IReturn);;Argument[0];remote",
+ "ServiceStack;IRestClientSync;true;Post;(ServiceStack.IReturnVoid);;Argument[0];remote",
+ "ServiceStack;IRestClientSync;true;Post<>;(System.Object);;Argument[0];remote",
+ "ServiceStack;IRestClientSync;true;Post<>;(ServiceStack.IReturn);;Argument[0];remote",
+ "ServiceStack;IRestClientSync;true;Put;(ServiceStack.IReturnVoid);;Argument[0];remote",
+ "ServiceStack;IRestClientSync;true;Put<>;(System.Object);;Argument[0];remote",
+ "ServiceStack;IRestClientSync;true;Put<>;(ServiceStack.IReturn);;Argument[0];remote",
+ // IRestGateway
+ "ServiceStack;IRestGateway;true;Delete<>;(ServiceStack.IReturn);;Argument[0];remote",
+ "ServiceStack;IRestGateway;true;Get<>;(ServiceStack.IReturn);;Argument[0];remote",
+ "ServiceStack;IRestGateway;true;Post<>;(ServiceStack.IReturn);;Argument[0];remote",
+ "ServiceStack;IRestGateway;true;Put<>;(ServiceStack.IReturn);;Argument[0];remote",
+ "ServiceStack;IRestGateway;true;Send<>;(ServiceStack.IReturn);;Argument[0];remote",
+ // IOneWayClient
+ "ServiceStack;IOneWayClient;true;SendAllOneWay;(System.Collections.Generic.IEnumerable);;Element of Argument[1];remote",
+ "ServiceStack;IOneWayClient;true;SendOneWay;(System.String,System.Object);;Argument[1];remote",
+ "ServiceStack;IOneWayClient;true;SendOneWay;(System.Object);;Argument[0];remote",
+ // IServiceGateway
+ "ServiceStack;IServiceGateway;true;Publish;(System.Object);;Argument[0];remote",
+ "ServiceStack;IServiceGateway;true;PublishAll;(System.Collections.Generic.IEnumerable);;Element of Argument[0];remote",
+ "ServiceStack;IServiceGateway;true;Send<>;(System.Object);;Argument[0];remote",
+ "ServiceStack;IServiceGateway;true;SendAll<>;(System.Collections.Generic.IEnumerable);;Element of Argument[0];remote",
+ // IRestClientAsync
+ "ServiceStack;IRestClientAsync;true;CustomMethodAsync;(System.String,ServiceStack.IReturnVoid,System.Threading.CancellationToken);;Argument[1];remote",
+ "ServiceStack;IRestClientAsync;true;CustomMethodAsync<>;(System.String,System.Object,System.Threading.CancellationToken);;Argument[1];remote",
+ "ServiceStack;IRestClientAsync;true;CustomMethodAsync<>;(System.String,ServiceStack.IReturn,System.Threading.CancellationToken);;Argument[1];remote",
+ "ServiceStack;IRestClientAsync;true;DeleteAsync;(ServiceStack.IReturnVoid,System.Threading.CancellationToken);;Argument[0];remote",
+ "ServiceStack;IRestClientAsync;true;DeleteAsync<>;(System.Object,System.Threading.CancellationToken);;Argument[0];remote",
+ "ServiceStack;IRestClientAsync;true;DeleteAsync<>;(ServiceStack.IReturn,System.Threading.CancellationToken);;Argument[0];remote",
+ "ServiceStack;IRestClientAsync;true;GetAsync;(ServiceStack.IReturnVoid,System.Threading.CancellationToken);;Argument[0];remote",
+ "ServiceStack;IRestClientAsync;true;GetAsync<>;(System.Object,System.Threading.CancellationToken);;Argument[0];remote",
+ "ServiceStack;IRestClientAsync;true;GetAsync<>;(ServiceStack.IReturn,System.Threading.CancellationToken);;Argument[0];remote",
+ "ServiceStack;IRestClientAsync;true;PatchAsync;(ServiceStack.IReturnVoid,System.Threading.CancellationToken);;Argument[0];remote",
+ "ServiceStack;IRestClientAsync;true;PatchAsync<>;(System.Object,System.Threading.CancellationToken);;Argument[0];remote",
+ "ServiceStack;IRestClientAsync;true;PatchAsync<>;(ServiceStack.IReturn,System.Threading.CancellationToken);;Argument[0];remote",
+ "ServiceStack;IRestClientAsync;true;PostAsync;(ServiceStack.IReturnVoid,System.Threading.CancellationToken);;Argument[0];remote",
+ "ServiceStack;IRestClientAsync;true;PostAsync<>;(System.Object,System.Threading.CancellationToken);;Argument[0];remote",
+ "ServiceStack;IRestClientAsync;true;PostAsync<>;(ServiceStack.IReturn,System.Threading.CancellationToken);;Argument[0];remote",
+ "ServiceStack;IRestClientAsync;true;PutAsync;(ServiceStack.IReturnVoid,System.Threading.CancellationToken);;Argument[0];remote",
+ "ServiceStack;IRestClientAsync;true;PutAsync<>;(System.Object,System.Threading.CancellationToken);;Argument[0];remote",
+ "ServiceStack;IRestClientAsync;true;PutAsync<>;(ServiceStack.IReturn,System.Threading.CancellationToken);;Argument[0];remote",
+ // IRestGatewayAsync
+ "ServiceStack;IRestGatewayAsync;true;DeleteAsync<>;(ServiceStack.IReturn,System.Threading.CancellationToken);;Argument[0];remote",
+ "ServiceStack;IRestGatewayAsync;true;GetAsync<>;(ServiceStack.IReturn,System.Threading.CancellationToken);;Argument[0];remote",
+ "ServiceStack;IRestGatewayAsync;true;PostAsync<>;(ServiceStack.IReturn,System.Threading.CancellationToken);;Argument[0];remote",
+ "ServiceStack;IRestGatewayAsync;true;PutAsync<>;(ServiceStack.IReturn,System.Threading.CancellationToken);;Argument[0];remote",
+ "ServiceStack;IRestGatewayAsync;true;SendAsync<>;(ServiceStack.IReturn,System.Threading.CancellationToken);;Argument[0];remote",
+ // IServiceGatewayAsync
+ "ServiceStack;IServiceGatewayAsync;true;PublishAsync;(System.Object,System.Threading.CancellationToken);;Argument[0];remote",
+ "ServiceStack;IServiceGatewayAsync;true;PublishAllAsync;(System.Collections.Generic.IEnumerable,System.Threading.CancellationToken);;Element of Argument[0];remote",
+ "ServiceStack;IServiceGatewayAsync;true;SendAsync<>;(System.Object,System.Threading.CancellationToken);;Argument[0];remote",
+ "ServiceStack;IServiceGatewayAsync;true;SendAllAsync<>;(System.Collections.Generic.IEnumerable,System.Threading.CancellationToken);;Element of Argument[0];remote",
+ // ServiceClientBase
+ "ServiceStack;ServiceClientBase;true;Publish<>;(T);;Argument[0];remote",
+ "ServiceStack;ServiceClientBase;true;Publish<>;(ServiceStack.Messaging.IMessage);;Argument[0];remote",
+ "ServiceStack;ServiceClientBase;true;Delete;(System.Object);;Argument[0];remote",
+ "ServiceStack;ServiceClientBase;true;Get;(System.Object);;Argument[0];remote",
+ "ServiceStack;ServiceClientBase;true;Patch;(System.Object);;Argument[0];remote",
+ "ServiceStack;ServiceClientBase;true;Post;(System.Object);;Argument[0];remote",
+ "ServiceStack;ServiceClientBase;true;Put;(System.Object);;Argument[0];remote",
+ "ServiceStack;ServiceClientBase;true;Head;(System.Object);;Argument[0];remote",
+ "ServiceStack;ServiceClientBase;true;Head;(ServiceStack.IReturn);;Argument[0];remote",
+ "ServiceStack;ServiceClientBase;true;CustomMethod;(System.String,System.String,System.Object);;Argument[2];remote",
+ "ServiceStack;ServiceClientBase;true;CustomMethod<>;(System.String,System.String,System.Object);;Argument[2];remote",
+ "ServiceStack;ServiceClientBase;true;CustomMethodAsync<>;(System.String,System.String,System.Object,System.Threading.CancellationToken);;Argument[2];remote",
+ "ServiceStack;ServiceClientBase;true;DownloadBytes;(System.String,System.String,System.Object);;Argument[2];remote",
+ "ServiceStack;ServiceClientBase;true;DownloadBytesAsync;(System.String,System.String,System.Object);;Argument[2];remote"
+ ]
+ }
+}
+
+private class ServiceStackSqlSinkModelCsv extends SinkModelCsv {
+ override predicate row(string row) {
+ row =
+ [
+ // SqlExpression
+ "ServiceStack.OrmLite;SqlExpression<>;true;UnsafeAnd;(System.String,System.Object[]);;Argument[0];sql",
+ "ServiceStack.OrmLite;SqlExpression<>;true;UnsafeFrom;(System.String);;Argument[0];sql",
+ "ServiceStack.OrmLite;SqlExpression<>;true;UnsafeGroupBy;(System.String);;Argument[0];sql",
+ "ServiceStack.OrmLite;SqlExpression<>;true;UnsafeHaving;(System.String,System.Object[]);;Argument[0];sql",
+ "ServiceStack.OrmLite;SqlExpression<>;true;UnsafeOr;(System.String,System.Object[]);;Argument[0];sql",
+ "ServiceStack.OrmLite;SqlExpression<>;true;UnsafeOrderBy;(System.String);;Argument[0];sql",
+ "ServiceStack.OrmLite;SqlExpression<>;true;UnsafeSelect;(System.String,System.Boolean);;Argument[0];sql",
+ "ServiceStack.OrmLite;SqlExpression<>;true;UnsafeSelect;(System.String);;Argument[0];sql",
+ "ServiceStack.OrmLite;SqlExpression<>;true;UnsafeWhere;(System.String,System.Object[]);;Argument[0];sql",
+ // IUntypedSqlExpression
+ "ServiceStack.OrmLite;IUntypedSqlExpression;true;UnsafeAnd;(System.String,System.Object[]);;Argument[0];sql",
+ "ServiceStack.OrmLite;IUntypedSqlExpression;true;UnsafeFrom;(System.String);;Argument[0];sql",
+ "ServiceStack.OrmLite;IUntypedSqlExpression;true;UnsafeOr;(System.String,System.Object[]);;Argument[0];sql",
+ "ServiceStack.OrmLite;IUntypedSqlExpression;true;UnsafeSelect;(System.String);;Argument[0];sql",
+ "ServiceStack.OrmLite;IUntypedSqlExpression;true;UnsafeWhere;(System.String,System.Object[]);;Argument[0];sql",
+ // OrmLiteReadApi
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;ExecuteNonQuery;(System.Data.IDbConnection,System.String);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;ExecuteNonQuery;(System.Data.IDbConnection,System.String,System.Object);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;ExecuteNonQuery;(System.Data.IDbConnection,System.String,System.Action);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;ExecuteNonQuery;(System.Data.IDbConnection,System.String,System.Collections.Generic.Dictionary);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;Exists<>;(System.Data.IDbConnection,System.String,System.Object);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;Dictionary<,>;(System.Data.IDbConnection,System.String,System.Object);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;Lookup<,>;(System.Data.IDbConnection,System.String,System.Object);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;Lookup<,>;(System.Data.IDbConnection,System.String,System.Collections.Generic.IEnumerable);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;KeyValuePairs;(System.Data.IDbConnection,System.String,System.System.Object);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;Scalar<>;(System.Data.IDbConnection,System.String,System.Object);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;Scalar<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.IEnumerable);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;Select<>;(System.Data.IDbConnection,System.Type,System.String,System.Object);;Argument[2];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;Select<>;(System.Data.IDbConnection,System.String);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;Select<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.Dictionary);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;Select<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.IEnumerable);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;Select<>;(System.Data.IDbConnection,System.String,System.Object);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;SelectLazy<>;(System.Data.IDbConnection,System.String,System.Object);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;SelectNonDefaults<>;(System.Data.IDbConnection,System.String,T);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;Single<>;(System.Data.IDbConnection,System.String,System.Object);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;Single<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.IEnumerable);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;SqlColumn<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.Dictionary);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;SqlColumn<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.IEnumerable);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;SqlColumn<>;(System.Data.IDbConnection,System.String,System.Object);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;SqlList<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.Dictionary);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;SqlList<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.IEnumerable);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;SqlList<>;(System.Data.IDbConnection,System.String,System.Object);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;SqlList<>;(System.Data.IDbConnection,System.String,System.Action);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;SqlScalar<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.Dictionary);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;SqlScalar<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.IEnumerable);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;SqlScalar<>;(System.Data.IDbConnection,System.String,System.Object);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;Column<>;(System.Data.IDbConnection,System.String,System.Object);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;Column<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.IEnumerable);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;ColumnDistinct<>;(System.Data.IDbConnection,System.String,System.Object);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;ColumnDistinct<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.IEnumerable);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;ColumnLazy<>;(System.Data.IDbConnection,System.String,System.Object);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;ColumnLazy<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.IEnumerable);;Argument[1];sql",
+ // OrmLiteReadExpressionsApi
+ "ServiceStack.OrmLite;OrmLiteReadExpressionsApi;false;RowCount;(System.Data.IDbConnection,System.String,System.Object);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadExpressionsApi;false;RowCount;(System.Data.IDbConnection,System.String,System.Collections.Generic.IEnumerable);;Argument[1];sql",
+ // OrmLiteReadExpressionsApiAsync
+ "ServiceStack.OrmLite;OrmLiteReadExpressionsApiAsync;false;RowCountAsync;(System.Data.IDbConnection,System.String,System.Object,System.Threading.CancellationToken);;Argument[1];sql",
+ // OrmLiteReadApiAsync
+ "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;ColumnAsync<>;(System.Data.IDbConnection,System.String,System.Object,System.Threading.CancellationToken);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;ColumnAsync<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.IEnumerable,System.Threading.CancellationToken);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;ColumnDistinctAsync<>;(System.Data.IDbConnection,System.String,System.Object,System.Threading.CancellationToken);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;ColumnDistinctAsync<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.IEnumerable,System.Threading.CancellationToken);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;DictionaryAsync<,>;(System.Data.IDbConnection,System.String,System.Object,System.Threading.CancellationToken);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;ExecuteNonQueryAsync;(System.Data.IDbConnection,System.String,System.Threading.CancellationToken);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;ExecuteNonQueryAsync;(System.Data.IDbConnection,System.String,System.Object,System.Threading.CancellationToken);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;ExecuteNonQueryAsync;(System.Data.IDbConnection,System.String,System.Collections.Generic.Dictionary,System.Threading.CancellationToken);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;ExistsAsync<>;(System.Data.IDbConnection,System.String,System.Object,System.Threading.CancellationToken);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;KeyValuePairsAsync<,>;(System.Data.IDbConnection,System.String,System.Object,System.Threading.CancellationToken);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;KeyValuePairsAsync<,>;(System.Data.IDbConnection,System.String,System.Collections.Generic.IEnumerable,System.Threading.CancellationToken);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;LookupAsync<,>;(System.Data.IDbConnection,System.String,System.Object,System.Threading.CancellationToken);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;LookupAsync<,>;(System.Data.IDbCommand,System.String,System.Collections.Generic.IEnumerable,System.Threading.CancellationToken);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;LookupAsync<,>;(System.Data.IDbConnection,System.String,System.Collections.Generic.IEnumerable,System.Threading.CancellationToken);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;ScalarAsync<>;(System.Data.IDbConnection,System.String,System.Object,System.Threading.CancellationToken);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;ScalarAsync<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.IEnumerable,System.Threading.CancellationToken);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;SelectAsync<>;(System.Data.IDbConnection,System.Type,System.String,System.Object,System.Threading.CancellationToken);;Argument[2];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;SelectAsync<>;(System.Data.IDbConnection,System.String,System.Threading.CancellationToken);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;SelectAsync<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.Dictionary,System.Threading.CancellationToken);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;SelectAsync<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.IEnumerable,System.Threading.CancellationToken);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;SelectAsync<>;(System.Data.IDbConnection,System.String,System.Object,System.Threading.CancellationToken);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;SelectNonDefaultsAsync<>;(System.Data.IDbConnection,System.String,T,System.Threading.CancellationToken);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;SingleAsync<>;(System.Data.IDbConnection,System.String,System.Object,System.Threading.CancellationToken);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;SingleAsync<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.IEnumerable,System.Threading.CancellationToken);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;SqlColumnAsync<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.Dictionary,System.Threading.CancellationToken);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;SqlColumnAsync<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.IEnumerable,System.Threading.CancellationToken);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;SqlColumnAsync<>;(System.Data.IDbConnection,System.String,System.Object,System.Threading.CancellationToken);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;SqlListAsync<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.Dictionary,System.Threading.CancellationToken);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;SqlListAsync<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.IEnumerable,System.Threading.CancellationToken);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;SqlListAsync<>;(System.Data.IDbConnection,System.String,System.Object,System.Threading.CancellationToken);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;SqlListAsync<>;(System.Data.IDbConnection,System.String,System.Action,System.Threading.CancellationToken);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;SqlScalarAsync<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.Dictionary,System.Threading.CancellationToken);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;SqlScalarAsync<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.IEnumerable,System.Threading.CancellationToken);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;SqlScalarAsync<>;(System.Data.IDbConnection,System.String,System.Object,System.Threading.CancellationToken);;Argument[1];sql",
+ // Write API
+ "ServiceStack.OrmLite;OrmLiteWriteApi;false;ExecuteSql;(System.Data.IDbConnection,System.String);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteWriteApi;false;ExecuteSql;(System.Data.IDbConnection,System.String,System.Object);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteWriteApi;false;ExecuteSql;(System.Data.IDbConnection,System.String,System.Collections.Generic.Dictionary);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteWriteApiAsync;false;ExecuteSqlAsync;(System.Data.IDbConnection,System.String,System.Threading.CancellationToken);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteWriteApiAsync;false;ExecuteSqlAsync;(System.Data.IDbConnection,System.String,System.Object,System.Threading.CancellationToken);;Argument[1];sql"
+ ]
+ }
+}
+
+private class ServiceStackCodeInjectionSinkModelCsv extends SinkModelCsv {
+ override predicate row(string row) {
+ row =
+ [
+ // Redis API
+ "ServiceStack.Redis;IRedisClient;true;Custom;(System.Object[]);;Argument[0];code",
+ "ServiceStack.Redis;IRedisClient;true;ExecCachedLua;(System.String,System.Func);;Argument[0];code",
+ "ServiceStack.Redis;IRedisClient;true;ExecLua;(System.String,System.String[],System.String[]);;Argument[0];code",
+ "ServiceStack.Redis;IRedisClient;true;ExecLua;(System.String,System.String[]);;Argument[0];code",
+ "ServiceStack.Redis;IRedisClient;true;ExecLuaAsInt;(System.String,System.String[],System.String[]);;Argument[0];code",
+ "ServiceStack.Redis;IRedisClient;true;ExecLuaAsInt;(System.String,System.String[]);;Argument[0];code",
+ "ServiceStack.Redis;IRedisClient;true;ExecLuaAsList;(System.String,System.String[],System.String[]);;Argument[0];code",
+ "ServiceStack.Redis;IRedisClient;true;ExecLuaAsList;(System.String,System.String[]);;Argument[0];code",
+ "ServiceStack.Redis;IRedisClient;true;ExecLuaAsString;(System.String,System.String[],System.String[]);;Argument[0];code",
+ "ServiceStack.Redis;IRedisClient;true;ExecLuaAsString;(System.String,System.String[]);;Argument[0];code",
+ "ServiceStack.Redis;IRedisClient;true;LoadLuaScript;(System.String);;Argument[0];code",
+ // IRedisClientAsync
+ "ServiceStack.Redis;IRedisClientAsync;true;CustomAsync;(System.Object[]);;Argument[0];code",
+ "ServiceStack.Redis;IRedisClientAsync;true;CustomAsync;(System.Object[],System.Threading.CancellationToken);;Element of Argument[0];code",
+ "ServiceStack.Redis;IRedisClientAsync;true;ExecCachedLuaAsync;(System.String,System.Func>,System.Threading.CancellationToken);;Argument[0];code",
+ "ServiceStack.Redis;IRedisClientAsync;true;ExecLuaAsync;(System.String,System.String[],System.String[],System.Threading.CancellationToken);;Argument[0];code",
+ "ServiceStack.Redis;IRedisClientAsync;true;ExecLuaAsync;(System.String,System.String[],System.Threading.CancellationToken);;Argument[0];code",
+ "ServiceStack.Redis;IRedisClientAsync;true;ExecLuaAsync;(System.String,System.String[]);;Argument[0];code",
+ "ServiceStack.Redis;IRedisClientAsync;true;ExecLuaAsIntAsync;(System.String,System.String[],System.String[],System.Threading.CancellationToken);;Argument[0];code",
+ "ServiceStack.Redis;IRedisClientAsync;true;ExecLuaAsIntAsync;(System.String,System.String[],System.Threading.CancellationToken);;Argument[0];code",
+ "ServiceStack.Redis;IRedisClientAsync;true;ExecLuaAsIntAsync;(System.String,System.String[]);;Argument[0];code",
+ "ServiceStack.Redis;IRedisClientAsync;true;ExecLuaAsStringAsync;(System.String,System.String[],System.String[],System.Threading.CancellationToken);;Argument[0];code",
+ "ServiceStack.Redis;IRedisClientAsync;true;ExecLuaAsStringAsync;(System.String,System.String[],System.Threading.CancellationToken);;Argument[0];code",
+ "ServiceStack.Redis;IRedisClientAsync;true;ExecLuaAsStringAsync;(System.String,System.String[]);;Argument[0];code",
+ "ServiceStack.Redis;IRedisClientAsync;true;ExecLuaAsListAsync;(System.String,System.String[],System.String[],System.Threading.CancellationToken);;Argument[0];code",
+ "ServiceStack.Redis;IRedisClientAsync;true;ExecLuaAsListAsync;(System.String,System.String[],System.Threading.CancellationToken);;Argument[0];code",
+ "ServiceStack.Redis;IRedisClientAsync;true;ExecLuaAsListAsync;(System.String,System.String[]);;Argument[0];code",
+ "ServiceStack.Redis;IRedisClientAsync;true;LoadLuaScriptAsync;(System.String,System.Threading.CancellationToken);;Argument[0];code"
+ ]
+ }
+}
+
+private class ServiceStackXssSummaryModelCsv extends SummaryModelCsv {
+ override predicate row(string row) {
+ row =
+ [
+ "ServiceStack;HttpResult;false;HttpResult;(System.String,System.String);;Argument[0];ReturnValue;taint",
+ "ServiceStack;HttpResult;false;HttpResult;(System.Object,System.String,System.Net.HttpStatusCode);;Argument[0];ReturnValue;taint",
+ "ServiceStack;HttpResult;false;HttpResult;(System.Object,System.String);;Argument[0];ReturnValue;taint",
+ "ServiceStack;HttpResult;false;HttpResult;(System.Object,System.Net.HttpStatusCode);;Argument[0];ReturnValue;taint",
+ "ServiceStack;HttpResult;false;HttpResult;(System.Object);;Argument[0];ReturnValue;taint",
+ "ServiceStack;HttpResult;false;HttpResult;(System.IO.Stream,System.String);;Argument[0];ReturnValue;taint",
+ "ServiceStack;HttpResult;false;HttpResult;(System.Byte[],System.String);;Argument[0];ReturnValue;taint"
+ ]
+ }
+}
+
+/** XSS support for ServiceStack framework */
+module XSS {
+ private import semmle.code.csharp.security.dataflow.XSSSinks
+
+ /** XSS sinks for ServiceStack */
+ class XssSink extends Sink {
+ XssSink() {
+ exists(ServiceClass service, Method m, Expr e |
+ service.getARequestMethod() = m and
+ this.asExpr() = e and
+ m.canReturn(e) and
+ (
+ e.getType() instanceof StringType or
+ e.getType().hasQualifiedName("ServiceStack", "HttpResult")
+ )
+ )
+ }
+ }
+}
diff --git a/csharp/ql/lib/semmle/code/csharp/frameworks/Sql.qll b/csharp/ql/lib/semmle/code/csharp/frameworks/Sql.qll
index 1a727030bea..78975527af6 100644
--- a/csharp/ql/lib/semmle/code/csharp/frameworks/Sql.qll
+++ b/csharp/ql/lib/semmle/code/csharp/frameworks/Sql.qll
@@ -7,6 +7,7 @@ private import semmle.code.csharp.frameworks.EntityFramework
private import semmle.code.csharp.frameworks.NHibernate
private import semmle.code.csharp.frameworks.Dapper
private import semmle.code.csharp.dataflow.DataFlow4
+private import semmle.code.csharp.dataflow.ExternalFlow
/** An expression containing a SQL command. */
abstract class SqlExpr extends Expr {
@@ -28,71 +29,236 @@ class CommandTextAssignmentSqlExpr extends SqlExpr, AssignExpr {
override Expr getSql() { result = this.getRValue() }
}
-/** A construction of an `IDbCommand` object. */
+/** A construction of an unknown `IDbCommand` object. */
class IDbCommandConstructionSqlExpr extends SqlExpr, ObjectCreation {
IDbCommandConstructionSqlExpr() {
exists(InstanceConstructor ic | ic = this.getTarget() |
ic.getDeclaringType().getABaseType*() instanceof SystemDataIDbCommandInterface and
- ic.getParameter(0).getType() instanceof StringType
+ ic.getParameter(0).getType() instanceof StringType and
+ not ic.getDeclaringType()
+ .hasQualifiedName([
+ // Known sealed classes:
+ "System.Data.SqlClient.SqlCommand", "System.Data.Odbc.OdbcCommand",
+ "System.Data.OleDb.OleDbCommand", "System.Data.EntityClient.EntityCommand"
+ ])
)
}
override Expr getSql() { result = this.getArgument(0) }
}
+/** A construction of a known `IDbCommand` object. */
+private class IDbCommandConstructionSinkModelCsv extends SinkModelCsv {
+ override predicate row(string row) {
+ row =
+ [
+ // SqlCommand
+ "System.Data.SqlClient;SqlCommand;false;SqlCommand;(System.String);;Argument[0];sql",
+ "System.Data.SqlClient;SqlCommand;false;SqlCommand;(System.String,System.Data.SqlClient.SqlConnection);;Argument[0];sql",
+ "System.Data.SqlClient;SqlCommand;false;SqlCommand;(System.String,System.Data.SqlClient.SqlConnection,System.Data.SqlClient.SqlTransaction);;Argument[0];sql",
+ // OdbcCommand
+ "System.Data.Odbc;OdbcCommand;false;OdbcCommand;(System.String);;Argument[0];sql",
+ "System.Data.Odbc;OdbcCommand;false;OdbcCommand;(System.String,System.Data.Odbc.OdbcConnection);;Argument[0];sql",
+ "System.Data.Odbc;OdbcCommand;false;OdbcCommand;(System.String,System.Data.Odbc.OdbcConnection,System.Data.Odbc.OdbcTransaction);;Argument[0];sql",
+ // OleDbCommand
+ "System.Data.OleDb;OleDbCommand;false;OleDbCommand;(System.String);;Argument[0];sql",
+ "System.Data.OleDb;OleDbCommand;false;OleDbCommand;(System.String,System.Data.OleDb.OleDbConnection);;Argument[0];sql",
+ "System.Data.OleDb;OleDbCommand;false;OleDbCommand;(System.String,System.Data.OleDb.OleDbConnection,System.Data.OleDb.OleDbTransaction);;Argument[0];sql",
+ // EntityCommand
+ "System.Data.EntityClient;EntityCommand;false;EntityCommand;(System.String);;Argument[0];sql",
+ "System.Data.EntityClient;EntityCommand;false;EntityCommand;(System.String,System.Data.EntityClient.EntityConnection);;Argument[0];sql",
+ "System.Data.EntityClient;EntityCommand;false;EntityCommand;(System.String,System.Data.EntityClient.EntityConnection,System.Data.EntityClient.EntityTransaction);;Argument[0];sql"
+ ]
+ }
+}
+
/** A construction of an `SqlDataAdapter` object. */
-class SqlDataAdapterConstructionSqlExpr extends SqlExpr, ObjectCreation {
- SqlDataAdapterConstructionSqlExpr() {
- exists(InstanceConstructor ic |
- ic = this.getTarget() and
- ic.getDeclaringType() instanceof SystemDataSqlClientSqlDataAdapterClass and
- ic.getParameter(0).getType() instanceof StringType
- )
+private class SqlDataAdapterConstructionSinkModelCsv extends SinkModelCsv {
+ override predicate row(string row) {
+ row =
+ [
+ "System.Data.SqlClient;SqlDataAdapter;false;SqlDataAdapter;(System.String,System.String);;Argument[0];sql",
+ "System.Data.SqlClient;SqlDataAdapter;false;SqlDataAdapter;(System.String,System.Data.SqlClient.SqlConnection);;Argument[0];sql"
+ ]
}
-
- override Expr getSql() { result = this.getArgument(0) }
}
/** A `MySql.Data.MySqlClient.MySqlHelper` method. */
-class MySqlHelperMethodCallSqlExpr extends SqlExpr, MethodCall {
- MySqlHelperMethodCallSqlExpr() {
- this.getQualifier().getType().(Class).hasQualifiedName("MySql.Data.MySqlClient", "MySqlHelper")
- }
-
- override Expr getSql() {
- exists(int i |
- result = getArgument(i) and
- this.getTarget().getParameter(i).hasName("commandText") and
- this.getTarget().getParameter(i).getType() instanceof StringType
- )
+private class MySqlHelperMethodCallSinkModelCsv extends SinkModelCsv {
+ override predicate row(string row) {
+ row =
+ [
+ // ExecuteDataRow/Async
+ "MySql.Data.MySqlClient;MySqlHelper;false;ExecuteDataRow;(System.String,System.String,MySql.Data.MySqlClient.MySqlParameter[]);;Argument[1];sql",
+ "MySql.Data.MySqlClient;MySqlHelper;false;ExecuteDataRowAsync;(System.String,System.String,MySql.Data.MySqlClient.MySqlParameter[]);;Argument[1];sql",
+ "MySql.Data.MySqlClient;MySqlHelper;false;ExecuteDataRowAsync;(System.String,System.String,System.Threading.CancellationToken,MySql.Data.MySqlClient.MySqlParameter[]);;Argument[1];sql",
+ // ExecuteDataset
+ "MySql.Data.MySqlClient;MySqlHelper;false;ExecuteDataset;(System.String,System.String);;Argument[1];sql",
+ "MySql.Data.MySqlClient;MySqlHelper;false;ExecuteDataset;(System.String,System.String,MySql.Data.MySqlClient.MySqlParameter[]);;Argument[1];sql",
+ "MySql.Data.MySqlClient;MySqlHelper;false;ExecuteDataset;(MySql.Data.MySqlClient.MySqlConnection,System.String);;Argument[1];sql",
+ "MySql.Data.MySqlClient;MySqlHelper;false;ExecuteDataset;(MySql.Data.MySqlClient.MySqlConnection,System.String,MySql.Data.MySqlClient.MySqlParameter[]);;Argument[1];sql",
+ // ExecuteDatasetAsync
+ "MySql.Data.MySqlClient;MySqlHelper;false;ExecuteDatasetAsync;(System.String,System.String);;Argument[1];sql",
+ "MySql.Data.MySqlClient;MySqlHelper;false;ExecuteDatasetAsync;(System.String,System.String,MySql.Data.MySqlClient.MySqlParameter[]);;Argument[1];sql",
+ "MySql.Data.MySqlClient;MySqlHelper;false;ExecuteDatasetAsync;(System.String,System.String,System.Threading.CancellationToken);;Argument[1];sql",
+ "MySql.Data.MySqlClient;MySqlHelper;false;ExecuteDatasetAsync;(System.String,System.String,System.Threading.CancellationToken,MySql.Data.MySqlClient.MySqlParameter[]);;Argument[1];sql",
+ "MySql.Data.MySqlClient;MySqlHelper;false;ExecuteDatasetAsync;(MySql.Data.MySqlClient.MySqlConnection,System.String);;Argument[1];sql",
+ "MySql.Data.MySqlClient;MySqlHelper;false;ExecuteDatasetAsync;(MySql.Data.MySqlClient.MySqlConnection,System.String,MySql.Data.MySqlClient.MySqlParameter[]);;Argument[1];sql",
+ "MySql.Data.MySqlClient;MySqlHelper;false;ExecuteDatasetAsync;(MySql.Data.MySqlClient.MySqlConnection,System.String,System.Threading.CancellationToken);;Argument[1];sql",
+ "MySql.Data.MySqlClient;MySqlHelper;false;ExecuteDatasetAsync;(MySql.Data.MySqlClient.MySqlConnection,System.String,System.Threading.CancellationToken,MySql.Data.MySqlClient.MySqlParameter[]);;Argument[1];sql",
+ // ExecuteNonQuery
+ "MySql.Data.MySqlClient;MySqlHelper;false;ExecuteNonQuery;(System.String,System.String,MySql.Data.MySqlClient.MySqlParameter[]);;Argument[1];sql",
+ "MySql.Data.MySqlClient;MySqlHelper;false;ExecuteNonQuery;(MySql.Data.MySqlClient.MySqlConnection,System.String,MySql.Data.MySqlClient.MySqlParameter[]);;Argument[1];sql",
+ // ExecuteNonQueryAsync
+ "MySql.Data.MySqlClient;MySqlHelper;false;ExecuteNonQueryAsync;(System.String,System.String,MySql.Data.MySqlClient.MySqlParameter[]);;Argument[1];sql",
+ "MySql.Data.MySqlClient;MySqlHelper;false;ExecuteNonQueryAsync;(System.String,System.String,System.Threading.CancellationToken,MySql.Data.MySqlClient.MySqlParameter[]);;Argument[1];sql",
+ "MySql.Data.MySqlClient;MySqlHelper;false;ExecuteNonQueryAsync;(MySql.Data.MySqlClient.MySqlConnection,System.String,MySql.Data.MySqlClient.MySqlParameter[]);;Argument[1];sql",
+ "MySql.Data.MySqlClient;MySqlHelper;false;ExecuteNonQueryAsync;(MySql.Data.MySqlClient.MySqlConnection,System.String,System.Threading.CancellationToken,MySql.Data.MySqlClient.MySqlParameter[]);;Argument[1];sql",
+ // ExecuteReader
+ "MySql.Data.MySqlClient;MySqlHelper;false;ExecuteReader;(System.String,System.String);;Argument[1];sql",
+ "MySql.Data.MySqlClient;MySqlHelper;false;ExecuteReader;(System.String,System.String,MySql.Data.MySqlClient.MySqlParameter[]);;Argument[1];sql",
+ "MySql.Data.MySqlClient;MySqlHelper;false;ExecuteReader;(MySql.Data.MySqlClient.MySqlConnection,System.String);;Argument[1];sql",
+ "MySql.Data.MySqlClient;MySqlHelper;false;ExecuteReader;(MySql.Data.MySqlClient.MySqlConnection,System.String,MySql.Data.MySqlClient.MySqlParameter[]);;Argument[1];sql",
+ // ExecuteReaderAsync
+ "MySql.Data.MySqlClient;MySqlHelper;false;ExecuteReaderAsync;(System.String,System.String);;Argument[1];sql",
+ "MySql.Data.MySqlClient;MySqlHelper;false;ExecuteReaderAsync;(System.String,System.String,MySql.Data.MySqlClient.MySqlParameter[]);;Argument[1];sql",
+ "MySql.Data.MySqlClient;MySqlHelper;false;ExecuteReaderAsync;(System.String,System.String,System.Threading.CancellationToken);;Argument[1];sql",
+ "MySql.Data.MySqlClient;MySqlHelper;false;ExecuteReaderAsync;(System.String,System.String,System.Threading.CancellationToken,MySql.Data.MySqlClient.MySqlParameter[]);;Argument[1];sql",
+ "MySql.Data.MySqlClient;MySqlHelper;false;ExecuteReaderAsync;(MySql.Data.MySqlClient.MySqlConnection,System.String);;Argument[1];sql",
+ "MySql.Data.MySqlClient;MySqlHelper;false;ExecuteReaderAsync;(MySql.Data.MySqlClient.MySqlConnection,System.String,MySql.Data.MySqlClient.MySqlParameter[]);;Argument[1];sql",
+ "MySql.Data.MySqlClient;MySqlHelper;false;ExecuteReaderAsync;(MySql.Data.MySqlClient.MySqlConnection,System.String,System.Threading.CancellationToken);;Argument[1];sql",
+ "MySql.Data.MySqlClient;MySqlHelper;false;ExecuteReaderAsync;(MySql.Data.MySqlClient.MySqlConnection,System.String,System.Threading.CancellationToken,MySql.Data.MySqlClient.MySqlParameter[]);;Argument[1];sql",
+ // ExecuteScalar
+ "MySql.Data.MySqlClient;MySqlHelper;false;ExecuteScalar;(System.String,System.String);;Argument[1];sql",
+ "MySql.Data.MySqlClient;MySqlHelper;false;ExecuteScalar;(System.String,System.String,MySql.Data.MySqlClient.MySqlParameter[]);;Argument[1];sql",
+ "MySql.Data.MySqlClient;MySqlHelper;false;ExecuteScalar;(MySql.Data.MySqlClient.MySqlConnection,System.String);;Argument[1];sql",
+ "MySql.Data.MySqlClient;MySqlHelper;false;ExecuteScalar;(MySql.Data.MySqlClient.MySqlConnection,System.String,MySql.Data.MySqlClient.MySqlParameter[]);;Argument[1];sql",
+ // ExecuteScalarAsync
+ "MySql.Data.MySqlClient;MySqlHelper;false;ExecuteScalarAsync;(System.String,System.String);;Argument[1];sql",
+ "MySql.Data.MySqlClient;MySqlHelper;false;ExecuteScalarAsync;(System.String,System.String,MySql.Data.MySqlClient.MySqlParameter[]);;Argument[1];sql",
+ "MySql.Data.MySqlClient;MySqlHelper;false;ExecuteScalarAsync;(System.String,System.String,System.Threading.CancellationToken);;Argument[1];sql",
+ "MySql.Data.MySqlClient;MySqlHelper;false;ExecuteScalarAsync;(System.String,System.String,System.Threading.CancellationToken,MySql.Data.MySqlClient.MySqlParameter[]);;Argument[1];sql",
+ "MySql.Data.MySqlClient;MySqlHelper;false;ExecuteScalarAsync;(MySql.Data.MySqlClient.MySqlConnection,System.String);;Argument[1];sql",
+ "MySql.Data.MySqlClient;MySqlHelper;false;ExecuteScalarAsync;(MySql.Data.MySqlClient.MySqlConnection,System.String,MySql.Data.MySqlClient.MySqlParameter[]);;Argument[1];sql",
+ "MySql.Data.MySqlClient;MySqlHelper;false;ExecuteScalarAsync;(MySql.Data.MySqlClient.MySqlConnection,System.String,System.Threading.CancellationToken);;Argument[1];sql",
+ "MySql.Data.MySqlClient;MySqlHelper;false;ExecuteScalarAsync;(MySql.Data.MySqlClient.MySqlConnection,System.String,System.Threading.CancellationToken,MySql.Data.MySqlClient.MySqlParameter[]);;Argument[1];sql",
+ // UpdateDataset/Async
+ "MySql.Data.MySqlClient;MySqlHelper;false;UpdateDataset;(System.String,System.String,System.Data.DataSet,System.String);;Argument[1];sql",
+ "MySql.Data.MySqlClient;MySqlHelper;false;UpdateDatasetAsync;(System.String,System.String,System.Data.DataSet,System.String);;Argument[1];sql",
+ "MySql.Data.MySqlClient;MySqlHelper;false;UpdateDatasetAsync;(System.String,System.String,System.Data.DataSet,System.String,System.Threading.CancellationToken);;Argument[1];sql"
+ ]
}
}
/** A `Microsoft.ApplicationBlocks.Data.SqlHelper` method. */
-class MicrosoftSqlHelperMethodCallSqlExpr extends SqlExpr, MethodCall {
- MicrosoftSqlHelperMethodCallSqlExpr() {
- this.getQualifier()
- .getType()
- .(Class)
- .hasQualifiedName("Microsoft.ApplicationBlocks.Data", "SqlHelper")
- }
-
- override Expr getSql() {
- exists(int i |
- result = getArgument(i) and
- this.getTarget().getParameter(i).hasName("commandText") and
- this.getTarget().getParameter(i).getType() instanceof StringType
- )
+private class MicrosoftSqlHelperSinkModelCsv extends SinkModelCsv {
+ override predicate row(string row) {
+ row =
+ [
+ // ExecuteNonQuery
+ "Microsoft.ApplicationBlocks.Data;SqlHelper;false;ExecuteNonQuery;(System.String,System.Data.CommandType,System.String);;Argument[2];sql",
+ "Microsoft.ApplicationBlocks.Data;SqlHelper;false;ExecuteNonQuery;(System.String,System.Data.CommandType,System.String,System.Data.SqlClient.SqlParameter[]);;Argument[2];sql",
+ "Microsoft.ApplicationBlocks.Data;SqlHelper;false;ExecuteNonQuery;(System.Data.SqlClient.SqlConnection,System.Data.CommandType,System.String);;Argument[2];sql",
+ "Microsoft.ApplicationBlocks.Data;SqlHelper;false;ExecuteNonQuery;(System.Data.SqlClient.SqlConnection,System.Data.CommandType,System.String,System.Data.SqlClient.SqlParameter[]);;Argument[2];sql",
+ "Microsoft.ApplicationBlocks.Data;SqlHelper;false;ExecuteNonQuery;(System.Data.SqlClient.SqlTransaction,System.Data.CommandType,System.String);;Argument[2];sql",
+ "Microsoft.ApplicationBlocks.Data;SqlHelper;false;ExecuteNonQuery;(System.Data.SqlClient.SqlTransaction,System.Data.CommandType,System.String,System.Data.SqlClient.SqlParameter[]);;Argument[2];sql",
+ // ExecuteDataset
+ "Microsoft.ApplicationBlocks.Data;SqlHelper;false;ExecuteDataset;(System.String,System.Data.CommandType,System.String);;Argument[2];sql",
+ "Microsoft.ApplicationBlocks.Data;SqlHelper;false;ExecuteDataset;(System.String,System.Data.CommandType,System.String,System.Data.SqlClient.SqlParameter[]);;Argument[2];sql",
+ "Microsoft.ApplicationBlocks.Data;SqlHelper;false;ExecuteDataset;(System.Data.SqlClient.SqlConnection,System.Data.CommandType,System.String);;Argument[2];sql",
+ "Microsoft.ApplicationBlocks.Data;SqlHelper;false;ExecuteDataset;(System.Data.SqlClient.SqlConnection,System.Data.CommandType,System.String,System.Data.SqlClient.SqlParameter[]);;Argument[2];sql",
+ "Microsoft.ApplicationBlocks.Data;SqlHelper;false;ExecuteDataset;(System.Data.SqlClient.SqlTransaction,System.Data.CommandType,System.String);;Argument[2];sql",
+ "Microsoft.ApplicationBlocks.Data;SqlHelper;false;ExecuteDataset;(System.Data.SqlClient.SqlTransaction,System.Data.CommandType,System.String,System.Data.SqlClient.SqlParameter[]);;Argument[2];sql",
+ // ExecuteReader
+ "Microsoft.ApplicationBlocks.Data;SqlHelper;false;ExecuteReader;(System.String,System.Data.CommandType,System.String);;Argument[2];sql",
+ "Microsoft.ApplicationBlocks.Data;SqlHelper;false;ExecuteReader;(System.String,System.Data.CommandType,System.String,System.Data.SqlClient.SqlParameter[]);;Argument[2];sql",
+ "Microsoft.ApplicationBlocks.Data;SqlHelper;false;ExecuteReader;(System.Data.SqlClient.SqlConnection,System.Data.CommandType,System.String);;Argument[2];sql",
+ "Microsoft.ApplicationBlocks.Data;SqlHelper;false;ExecuteReader;(System.Data.SqlClient.SqlConnection,System.Data.CommandType,System.String,System.Data.SqlClient.SqlParameter[]);;Argument[2];sql",
+ "Microsoft.ApplicationBlocks.Data;SqlHelper;false;ExecuteReader;(System.Data.SqlClient.SqlTransaction,System.Data.CommandType,System.String);;Argument[2];sql",
+ "Microsoft.ApplicationBlocks.Data;SqlHelper;false;ExecuteReader;(System.Data.SqlClient.SqlTransaction,System.Data.CommandType,System.String,System.Data.SqlClient.SqlParameter[]);;Argument[2];sql",
+ // ExecuteScalar
+ "Microsoft.ApplicationBlocks.Data;SqlHelper;false;ExecuteScalar;(System.String,System.Data.CommandType,System.String);;Argument[2];sql",
+ "Microsoft.ApplicationBlocks.Data;SqlHelper;false;ExecuteScalar;(System.String,System.Data.CommandType,System.String,System.Data.SqlClient.SqlParameter[]);;Argument[2];sql",
+ "Microsoft.ApplicationBlocks.Data;SqlHelper;false;ExecuteScalar;(System.Data.SqlClient.SqlConnection,System.Data.CommandType,System.String);;Argument[2];sql",
+ "Microsoft.ApplicationBlocks.Data;SqlHelper;false;ExecuteScalar;(System.Data.SqlClient.SqlConnection,System.Data.CommandType,System.String,System.Data.SqlClient.SqlParameter[]);;Argument[2];sql",
+ "Microsoft.ApplicationBlocks.Data;SqlHelper;false;ExecuteScalar;(System.Data.SqlClient.SqlTransaction,System.Data.CommandType,System.String);;Argument[2];sql",
+ "Microsoft.ApplicationBlocks.Data;SqlHelper;false;ExecuteScalar;(System.Data.SqlClient.SqlTransaction,System.Data.CommandType,System.String,System.Data.SqlClient.SqlParameter[]);;Argument[2];sql",
+ // ExecuteXmlReader
+ "Microsoft.ApplicationBlocks.Data;SqlHelper;false;ExecuteXmlReader;(System.Data.SqlClient.SqlConnection,System.Data.CommandType,System.String);;Argument[2];sql",
+ "Microsoft.ApplicationBlocks.Data;SqlHelper;false;ExecuteXmlReader;(System.Data.SqlClient.SqlConnection,System.Data.CommandType,System.String,System.Data.SqlClient.SqlParameter[]);;Argument[2];sql",
+ "Microsoft.ApplicationBlocks.Data;SqlHelper;false;ExecuteXmlReader;(System.Data.SqlClient.SqlTransaction,System.Data.CommandType,System.String);;Argument[2];sql",
+ "Microsoft.ApplicationBlocks.Data;SqlHelper;false;ExecuteXmlReader;(System.Data.SqlClient.SqlTransaction,System.Data.CommandType,System.String,System.Data.SqlClient.SqlParameter[]);;Argument[2];sql"
+ ]
}
}
/** A `Dapper.SqlMapper` method that is taking a SQL string argument. */
-class DapperSqlMethodCallSqlExpr extends SqlExpr, MethodCall {
- DapperSqlMethodCallSqlExpr() {
- this.getTarget() = any(Dapper::SqlMapperClass c).getAQueryMethod()
+private class DapperSqlMapperSinkModelCsv extends SinkModelCsv {
+ override predicate row(string row) {
+ row =
+ [
+ // Execute*
+ "Dapper;SqlMapper;false;Execute;(System.Data.IDbConnection,System.String,System.Object,System.Data.IDbTransaction,System.Nullable,System.Nullable);;Argument[1];sql",
+ "Dapper;SqlMapper;false;ExecuteAsync;(System.Data.IDbConnection,System.String,System.Object,System.Data.IDbTransaction,System.Nullable,System.Nullable);;Argument[1];sql",
+ "Dapper;SqlMapper;false;ExecuteScalar;(System.Data.IDbConnection,System.String,System.Object,System.Data.IDbTransaction,System.Nullable,System.Nullable);;Argument[1];sql",
+ "Dapper;SqlMapper;false;ExecuteScalarAsync;(System.Data.IDbConnection,System.String,System.Object,System.Data.IDbTransaction,System.Nullable,System.Nullable);;Argument[1];sql",
+ "Dapper;SqlMapper;false;ExecuteScalar<>;(System.Data.IDbConnection,System.String,System.Object,System.Data.IDbTransaction,System.Nullable,System.Nullable);;Argument[1];sql",
+ "Dapper;SqlMapper;false;ExecuteScalarAsync<>;(System.Data.IDbConnection,System.String,System.Object,System.Data.IDbTransaction,System.Nullable,System.Nullable);;Argument[1];sql",
+ "Dapper;SqlMapper;false;ExecuteReader;(System.Data.IDbConnection,System.String,System.Object,System.Data.IDbTransaction,System.Nullable,System.Nullable);;Argument[1];sql",
+ "Dapper;SqlMapper;false;ExecuteReaderAsync;(System.Data.IDbConnection,System.String,System.Object,System.Data.IDbTransaction,System.Nullable,System.Nullable);;Argument[1];sql",
+ "Dapper;SqlMapper;false;ExecuteReaderAsync;(System.Data.DbConnection,System.String,System.Object,System.Data.IDbTransaction,System.Nullable,System.Nullable);;Argument[1];sql",
+ // Query*
+ "Dapper;SqlMapper;false;Query;(System.Data.IDbConnection,System.String,System.Object,System.Data.IDbTransaction,System.Boolean,System.Nullable,System.Nullable);;Argument[1];sql",
+ "Dapper;SqlMapper;false;QueryAsync;(System.Data.IDbConnection,System.String,System.Object,System.Data.IDbTransaction,System.Nullable,System.Nullable);;Argument[1];sql",
+ "Dapper;SqlMapper;false;Query<>;(System.Data.IDbConnection,System.String,System.Object,System.Data.IDbTransaction,System.Boolean,System.Nullable,System.Nullable);;Argument[1];sql",
+ "Dapper;SqlMapper;false;QueryAsync<>;(System.Data.IDbConnection,System.String,System.Object,System.Data.IDbTransaction,System.Nullable,System.Nullable);;Argument[1];sql",
+ "Dapper;SqlMapper;false;QueryMultiple;(System.Data.IDbConnection,System.String,System.Object,System.Data.IDbTransaction,System.Nullable,System.Nullable);;Argument[1];sql",
+ "Dapper;SqlMapper;false;QueryMultipleAsync;(System.Data.IDbConnection,System.String,System.Object,System.Data.IDbTransaction,System.Nullable,System.Nullable);;Argument[1];sql",
+ "Dapper;SqlMapper;false;QueryFirst;(System.Data.IDbConnection,System.String,System.Object,System.Data.IDbTransaction,System.Nullable,System.Nullable);;Argument[1];sql",
+ "Dapper;SqlMapper;false;QueryFirstAsync;(System.Data.IDbConnection,System.String,System.Object,System.Data.IDbTransaction,System.Nullable,System.Nullable);;Argument[1];sql",
+ "Dapper;SqlMapper;false;QueryFirst<>;(System.Data.IDbConnection,System.String,System.Object,System.Data.IDbTransaction,System.Nullable,System.Nullable);;Argument[1];sql",
+ "Dapper;SqlMapper;false;QueryFirstAsync<>;(System.Data.IDbConnection,System.String,System.Object,System.Data.IDbTransaction,System.Nullable,System.Nullable);;Argument[1];sql",
+ "Dapper;SqlMapper;false;QueryFirstOrDefault;(System.Data.IDbConnection,System.String,System.Object,System.Data.IDbTransaction,System.Nullable,System.Nullable);;Argument[1];sql",
+ "Dapper;SqlMapper;false;QueryFirstOrDefaultAsync;(System.Data.IDbConnection,System.String,System.Object,System.Data.IDbTransaction,System.Nullable,System.Nullable);;Argument[1];sql",
+ "Dapper;SqlMapper;false;QueryFirstOrDefault<>;(System.Data.IDbConnection,System.String,System.Object,System.Data.IDbTransaction,System.Nullable,System.Nullable);;Argument[1];sql",
+ "Dapper;SqlMapper;false;QueryFirstOrDefaultAsync<>;(System.Data.IDbConnection,System.String,System.Object,System.Data.IDbTransaction,System.Nullable,System.Nullable);;Argument[1];sql",
+ "Dapper;SqlMapper;false;QuerySingle;(System.Data.IDbConnection,System.String,System.Object,System.Data.IDbTransaction,System.Nullable,System.Nullable);;Argument[1];sql",
+ "Dapper;SqlMapper;false;QuerySingleAsync;(System.Data.IDbConnection,System.String,System.Object,System.Data.IDbTransaction,System.Nullable,System.Nullable);;Argument[1];sql",
+ "Dapper;SqlMapper;false;QuerySingle<>;(System.Data.IDbConnection,System.String,System.Object,System.Data.IDbTransaction,System.Nullable,System.Nullable);;Argument[1];sql",
+ "Dapper;SqlMapper;false;QuerySingleAsync<>;(System.Data.IDbConnection,System.String,System.Object,System.Data.IDbTransaction,System.Nullable,System.Nullable);;Argument[1];sql",
+ "Dapper;SqlMapper;false;QuerySingleOrDefault;(System.Data.IDbConnection,System.String,System.Object,System.Data.IDbTransaction,System.Nullable,System.Nullable);;Argument[1];sql",
+ "Dapper;SqlMapper;false;QuerySingleOrDefaultAsync;(System.Data.IDbConnection,System.String,System.Object,System.Data.IDbTransaction,System.Nullable,System.Nullable);;Argument[1];sql",
+ "Dapper;SqlMapper;false;QuerySingleOrDefault<>;(System.Data.IDbConnection,System.String,System.Object,System.Data.IDbTransaction,System.Nullable,System.Nullable);;Argument[1];sql",
+ "Dapper;SqlMapper;false;QuerySingleOrDefaultAsync<>;(System.Data.IDbConnection,System.String,System.Object,System.Data.IDbTransaction,System.Nullable,System.Nullable);;Argument[1];sql",
+ // Query* with System.Type parameter
+ "Dapper;SqlMapper;false;Query;(System.Data.IDbConnection,System.Type,System.String,System.Object,System.Data.IDbTransaction,System.Boolean,System.Nullable,System.Nullable);;Argument[2];sql",
+ "Dapper;SqlMapper;false;QueryAsync;(System.Data.IDbConnection,System.Type,System.String,System.Object,System.Data.IDbTransaction,System.Boolean,System.Nullable,System.Nullable);;Argument[2];sql",
+ "Dapper;SqlMapper;false;QueryFirst;(System.Data.IDbConnection,System.Type,System.String,System.Object,System.Data.IDbTransaction,System.Nullable,System.Nullable);;Argument[2];sql",
+ "Dapper;SqlMapper;false;QueryFirstAsync;(System.Data.IDbConnection,System.Type,System.String,System.Object,System.Data.IDbTransaction,System.Nullable,System.Nullable);;Argument[2];sql",
+ "Dapper;SqlMapper;false;QueryFirstOrDefault;(System.Data.IDbConnection,System.Type,System.String,System.Object,System.Data.IDbTransaction,System.Nullable,System.Nullable);;Argument[2];sql",
+ "Dapper;SqlMapper;false;QueryFirstOrDefaultAsync;(System.Data.IDbConnection,System.Type,System.String,System.Object,System.Data.IDbTransaction,System.Nullable,System.Nullable);;Argument[2];sql",
+ "Dapper;SqlMapper;false;QuerySingle;(System.Data.IDbConnection,System.Type,System.String,System.Object,System.Data.IDbTransaction,System.Nullable,System.Nullable);;Argument[2];sql",
+ "Dapper;SqlMapper;false;QuerySingleAsync;(System.Data.IDbConnection,System.Type,System.String,System.Object,System.Data.IDbTransaction,System.Nullable,System.Nullable);;Argument[2];sql",
+ "Dapper;SqlMapper;false;QuerySingleOrDefault;(System.Data.IDbConnection,System.Type,System.String,System.Object,System.Data.IDbTransaction,System.Nullable,System.Nullable);;Argument[2];sql",
+ "Dapper;SqlMapper;false;QuerySingleOrDefaultAsync;(System.Data.IDbConnection,System.Type,System.String,System.Object,System.Data.IDbTransaction,System.Nullable,System.Nullable);;Argument[2];sql",
+ // Query with multiple type parameters
+ "Dapper;SqlMapper;false;Query<,,>;(System.Data.IDbConnection,System.String,System.Func,System.Object,System.Data.IDbTransaction,System.Boolean,System.String,System.Nullable,System.Nullable);;Argument[1];sql",
+ "Dapper;SqlMapper;false;QueryAsync<,,>;(System.Data.IDbConnection,System.String,System.Func,System.Object,System.Data.IDbTransaction,System.Boolean,System.String,System.Nullable,System.Nullable);;Argument[1];sql",
+ "Dapper;SqlMapper;false;Query<,,,>;(System.Data.IDbConnection,System.String,System.Func,System.Object,System.Data.IDbTransaction,System.Boolean,System.String,System.Nullable,System.Nullable);;Argument[1];sql",
+ "Dapper;SqlMapper;false;QueryAsync<,,,>;(System.Data.IDbConnection,System.String,System.Func,System.Object,System.Data.IDbTransaction,System.Boolean,System.String,System.Nullable,System.Nullable);;Argument[1];sql",
+ "Dapper;SqlMapper;false;Query<,,,,>;(System.Data.IDbConnection,System.String,System.Func,System.Object,System.Data.IDbTransaction,System.Boolean,System.String,System.Nullable,System.Nullable);;Argument[1];sql",
+ "Dapper;SqlMapper;false;QueryAsync<,,,,>;(System.Data.IDbConnection,System.String,System.Func,System.Object,System.Data.IDbTransaction,System.Boolean,System.String,System.Nullable,System.Nullable);;Argument[1];sql",
+ "Dapper;SqlMapper;false;Query<,,,,,>;(System.Data.IDbConnection,System.String,System.Func,System.Object,System.Data.IDbTransaction,System.Boolean,System.String,System.Nullable,System.Nullable