Compare commits

..

89 Commits

Author SHA1 Message Date
Óscar San José
72534e882b Merge pull request #21483 from github/release-prep/2.25.0
Release preparation for version 2.25.0
2026-03-16 15:45:36 +01:00
github-actions[bot]
d6055754b6 Release preparation for version 2.25.0 2026-03-16 12:15:34 +00:00
Anders Schack-Mulligen
c24b43d01e Merge pull request #21482 from aschackmull/csharp/rangeanalysis-no-split
C#: Remove splitting-awareness from Range Analysis.
2026-03-16 10:54:49 +01:00
Jeroen Ketema
179a4cd41a Merge pull request #21474 from jketema/jketema/swift-linux-2
Swift: Ignore some DB-CHECK results on Linux
2026-03-16 10:50:05 +01:00
Anders Schack-Mulligen
a929c0bf24 C#: Remove splitting-awareness from Range Analysis. 2026-03-16 09:58:14 +01:00
Anders Schack-Mulligen
427ccee3b9 Merge pull request #21473 from aschackmull/csharp/dataflow-no-split
C#: Remove splitting-awareness from data flow.
2026-03-16 09:33:31 +01:00
Asger F
22f16dda85 Merge pull request #21368 from asgerf/browser-sources
JS: Add 'browser' source kinds
2026-03-16 09:24:54 +01:00
Anders Schack-Mulligen
db0a3e38e2 C#: Accept a few irrelevant taint steps. 2026-03-16 09:09:54 +01:00
Anders Schack-Mulligen
e7edf15031 C#: Clean up. 2026-03-16 08:51:51 +01:00
Anders Schack-Mulligen
4c77e0f315 C#: Remove splitting-awareness for local expression steps. 2026-03-16 08:51:51 +01:00
Anders Schack-Mulligen
7124cd4e6e C#: Remove splitting-awareness for source-to-def steps. 2026-03-16 08:51:50 +01:00
Anders Schack-Mulligen
c076992b83 C#: Remove splitting-awareness in ObjectInitializerNode. 2026-03-16 08:51:49 +01:00
Anders Schack-Mulligen
659d8e7c90 C#: Remove splitting-awareness in argumentOf. 2026-03-16 08:51:49 +01:00
Anders Schack-Mulligen
1e8de0511b C#: Remove splitting-awareness in lambda flow. 2026-03-16 08:51:48 +01:00
Anders Schack-Mulligen
bce0a4d2a7 C#: Remove splitting-awareness for store steps. 2026-03-16 08:51:48 +01:00
Anders Schack-Mulligen
2160910d56 C#: Remove splitting-awareness for read steps. 2026-03-16 08:51:47 +01:00
Anders Schack-Mulligen
a5c8a5b5f8 C#: Remove splitting-awareness for taint steps. 2026-03-16 08:51:47 +01:00
Jeroen Ketema
f9f1d9eecc Swift: Ignore some DB-CHECK results on Linux 2026-03-13 20:06:57 +01:00
Owen Mansel-Chan
d52e9bc18c Merge pull request #21370 from github/owen-mc/go/overlay-annotations
Go: Add overlay annotations from script
2026-03-13 16:46:01 +00:00
Owen Mansel-Chan
b8b841cfba Add overlay[loca] in 4 more tests 2026-03-13 16:19:00 +00:00
Owen Mansel-Chan
df9f8ee386 Merge branch 'main' into owen-mc/go/overlay-annotations 2026-03-13 15:55:17 +00:00
Owen Mansel-Chan
99f4930e24 Explicitly mark DataFlowNodes.qll as overlay[local] 2026-03-13 15:23:39 +00:00
Owen Mansel-Chan
e9df9147ad Add overlay annotations in 4 PrintAst tests 2026-03-13 15:03:05 +00:00
Owen Mansel-Chan
f32f85399a Mark various files as overlay[local] 2026-03-13 15:03:02 +00:00
Jonas Jensen
c56feb7644 Go: annotate the standard library with for overlay
This commit is auto-generated with:

    python3 config/add-overlay-annotations.py go
2026-03-13 15:03:01 +00:00
Jonas Jensen
7ef60a8649 Update the overlay annotation script for go
The Go libraries follow their own naming convention for "query
libraries". These need to be exempted from automatic `overlay[local?]`
annotations since otherwise it appears that too many predicates are
evaluated, possibly because of inadequate use of sentinels.
2026-03-13 15:02:58 +00:00
Asger F
7d6e08ecf1 Merge pull request #21461 from github/asger/js-shebang-bun-tsx
JS: Recognise bun and tsx in shebang lines
2026-03-13 15:07:12 +01:00
Asger F
dfa6d20072 JS: Replace broken link with plain text 2026-03-13 15:05:07 +01:00
Asger F
821cc0e875 JS: Address PR review comments
- Fix misplaced semicolons in test files (was inside comment, moved before it)
- Update QLdoc comments to reference new browser source kind names
- Update docs to list browser source kinds and fix outdated 'only remote' note

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-13 14:58:04 +01:00
Anders Schack-Mulligen
f11815c633 Merge pull request #21471 from aschackmull/csharp/rm-prebasicblock
C#: Delete PreBasicBlocks.
2026-03-13 08:54:06 +01:00
Owen Mansel-Chan
52cfd49087 Merge pull request #21469 from github/dependabot/go_modules/go/extractor/extractor-dependencies-7af763c229
Bump the extractor-dependencies group across 1 directory with 2 updates
2026-03-13 07:06:44 +00:00
Anders Schack-Mulligen
8c1c039edf C#: Delete PreBasicBlocks. 2026-03-13 08:00:08 +01:00
dependabot[bot]
c9e0927992 Bump the extractor-dependencies group across 1 directory with 2 updates
Bumps the extractor-dependencies group with 2 updates in the /go/extractor directory: [golang.org/x/mod](https://github.com/golang/mod) and [golang.org/x/tools](https://github.com/golang/tools).


Updates `golang.org/x/mod` from 0.33.0 to 0.34.0
- [Commits](https://github.com/golang/mod/compare/v0.33.0...v0.34.0)

Updates `golang.org/x/tools` from 0.42.0 to 0.43.0
- [Release notes](https://github.com/golang/tools/releases)
- [Commits](https://github.com/golang/tools/compare/v0.42.0...v0.43.0)

---
updated-dependencies:
- dependency-name: golang.org/x/mod
  dependency-version: 0.34.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: extractor-dependencies
- dependency-name: golang.org/x/tools
  dependency-version: 0.43.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: extractor-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-13 03:04:44 +00:00
Jeroen Ketema
d5f667e585 Merge pull request #21467 from jketema/jketema/swift-linux
Swift: Disable stack protector pass
2026-03-12 22:42:51 +01:00
Jeroen Ketema
b758732a28 Merge pull request #21468 from jketema/jketema/swift-lines
Swift: Limit successfully extracted lines
2026-03-12 17:24:28 +01:00
Jeroen Ketema
ba3fadbf20 Swift: Rename function 2026-03-12 16:37:13 +01:00
Owen Mansel-Chan
d7d1554461 Merge pull request #21465 from owen-mc/go/small-tweaks
Go: improve detection of type expressions when database is missing some type information
2026-03-12 14:58:16 +00:00
Jeroen Ketema
12e0f3f359 Swift: Limit successfully extracted lines 2026-03-12 15:46:23 +01:00
Owen Mansel-Chan
0bb6ff58cc Merge pull request #21466 from owen-mc/go/add-nil-helper-predicate
Go: Add and use `exprRefersToNil` predicate
2026-03-12 14:36:03 +00:00
Jeroen Ketema
b9c0aca11a Swift: Fix formatting 2026-03-12 15:00:18 +01:00
Jeroen Ketema
ee3674cb80 Swift: Disable stack protector pass 2026-03-12 14:43:05 +01:00
Owen Mansel-Chan
c271755985 Add and use exprRefersToNil predicate 2026-03-12 13:28:57 +00:00
Owen Mansel-Chan
a16c43881b Use "database" instead of "snapshot" in QLDocs 2026-03-12 13:28:06 +00:00
Owen Mansel-Chan
39e0382089 Improve QLDoc for isTypeExprTopDown 2026-03-12 13:28:05 +00:00
Owen Mansel-Chan
22e012c6f4 Expand isTypeExprTopDown
We should be using all subtypes of `FieldBase`. This allows us to find
more type expressions, and is also simpler to evaluate.
2026-03-12 13:28:03 +00:00
Asger F
b8c44be599 Add QL test for bun/tsx shebang recognition in TypeScript files
Add test files with #!/usr/bin/env bun, #!/usr/bin/env tsx, and
#!/usr/bin/env node shebangs. The query lists extracted .ts files,
verifying that all three shebangs are recognized and the files are
not skipped by the extractor.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-12 10:09:56 +01:00
Asger F
84d1828a9c JavaScript extractor: recognise bun and tsx in shebang lines
Update the shebang regexp (renamed NODE_INVOCATION -> JS_INVOCATION) to
also match 'bun' and 'tsx' so that scripts using these runtimes are
correctly identified as JavaScript files.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-12 09:35:36 +01:00
Mario Campos
f2e7dca65c Merge pull request #21454 from github/mario-campos-patch-1
Correct comment about AES crypto algorithm strength
2026-03-11 22:43:21 -05:00
Mario Campos
b9b3b3a0b5 Empty commit for missed Green Check 2026-03-11 22:37:20 -05:00
Asger F
5db30c9947 JS: Add change note 2026-03-11 15:40:07 +01:00
Mario Campos
6fb10555ff Correct comment about AES crypto algorithm strength 2026-03-11 09:27:03 -05:00
Ian Lynagh
bbd02b855b Merge pull request #21424 from github/idrissrio/cpp/overlay/discard
C/C++ overlay: update discard mechanism
2026-03-11 13:45:52 +00:00
Idriss Riouak
48a03e2a04 Apply suggestions from code review
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-03-11 14:25:53 +01:00
idrissrio
a92d97744f C/C++ overlay: address review comment 2026-03-11 14:25:52 +01:00
idrissrio
ef6c1a9968 C/C++ overlay: fix failing header_dependency test 2026-03-11 14:25:50 +01:00
idrissrio
72142b51f7 C/C++ overlay: switch to updated discard strategy 2026-03-11 14:25:49 +01:00
Tom Hvitved
c06d4d2647 Merge pull request #21422 from hvitved/rust/type-mention-refactor
Rust: Small refactor in `TypeMention.qll`
2026-03-11 14:01:03 +01:00
Asger F
4a001f960f JS: Add tests in request forgery queries 2026-03-11 13:53:25 +01:00
Asger F
1253553aec JS: Add browser source kinds 2026-03-11 13:50:07 +01:00
Ian Lynagh
68dfa5c83b Merge pull request #21451 from igfoo/igfoo/fix-build
Revert "Bump rules_android from 0.6.4 to 0.7.1"
2026-03-11 12:27:20 +00:00
Ian Lynagh
25a20f74f0 Revert "Bump rules_android from 0.6.4 to 0.7.1"
This reverts commit c7349740f0.

It was making the build fail
2026-03-11 11:54:18 +00:00
Taus
5a65282241 Merge pull request #21429 from github/tausbn/fix-bad-join-in-method-call-order
Python: Fix bad join in method call order computation
2026-03-10 18:17:35 +01:00
Paolo Tranquilli
79499c240a Merge pull request #21444 from github/dependabot/bazel/googletest-1.17.0.bcr.2
Bump googletest from 1.14.0.bcr.1 to 1.17.0.bcr.2
2026-03-10 16:41:38 +01:00
Paolo Tranquilli
267a46d01b Merge pull request #21445 from github/dependabot/bazel/rules_shell-0.6.1
Bump rules_shell from 0.5.0 to 0.6.1
2026-03-10 16:41:24 +01:00
Ian Lynagh
341059d2d0 Merge pull request #21437 from igfoo/igfoo/onemk
C++: Small simplification
2026-03-10 15:36:38 +00:00
Paolo Tranquilli
3c3c58b0a9 Merge pull request #21443 from github/dependabot/bazel/rules_android-0.7.1
Bump rules_android from 0.6.4 to 0.7.1
2026-03-10 16:06:40 +01:00
Paolo Tranquilli
9bf1072a01 Merge pull request #21447 from github/revert-21414-redsun82/rerun-slash-command
Revert "Add `/rerun` slash command for failed internal checks"
2026-03-10 15:55:45 +01:00
Paolo Tranquilli
a5f23ade8c Revert "Add /rerun slash command for failed internal checks" 2026-03-10 14:43:59 +01:00
Paolo Tranquilli
017b6f2e44 Merge pull request #21414 from github/redsun82/rerun-slash-command
Add `/rerun` slash command for failed internal checks
2026-03-10 14:01:03 +01:00
Anders Schack-Mulligen
6a6bb5ebf9 Merge pull request #21441 from aschackmull/cfg/switch-sharing
Cfg: Share more code for switch statements.
2026-03-10 13:50:21 +01:00
dependabot[bot]
b631138b63 Bump rules_shell from 0.5.0 to 0.6.1
Bumps [rules_shell](https://github.com/bazelbuild/rules_shell) from 0.5.0 to 0.6.1.
- [Release notes](https://github.com/bazelbuild/rules_shell/releases)
- [Commits](https://github.com/bazelbuild/rules_shell/compare/v0.5.0...v0.6.1)

---
updated-dependencies:
- dependency-name: rules_shell
  dependency-version: 0.6.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-10 11:21:40 +00:00
dependabot[bot]
093d36ebe6 Bump googletest from 1.14.0.bcr.1 to 1.17.0.bcr.2
Bumps [googletest](https://github.com/google/googletest) from 1.14.0.bcr.1 to 1.17.0.bcr.2.
- [Release notes](https://github.com/google/googletest/releases)
- [Commits](https://github.com/google/googletest/commits)

---
updated-dependencies:
- dependency-name: googletest
  dependency-version: 1.17.0.bcr.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-10 11:21:37 +00:00
dependabot[bot]
c7349740f0 Bump rules_android from 0.6.4 to 0.7.1
Bumps [rules_android](https://github.com/bazelbuild/rules_android) from 0.6.4 to 0.7.1.
- [Release notes](https://github.com/bazelbuild/rules_android/releases)
- [Commits](https://github.com/bazelbuild/rules_android/compare/v0.6.4...v0.7.1)

---
updated-dependencies:
- dependency-name: rules_android
  dependency-version: 0.7.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-10 11:21:34 +00:00
Anders Schack-Mulligen
efa797a21d Update shared/controlflow/codeql/controlflow/ControlFlowGraph.qll
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-03-10 11:22:15 +01:00
Anders Schack-Mulligen
77d4f5a2dc Cfg: Update fallsThrough default. 2026-03-10 11:10:24 +01:00
Anders Schack-Mulligen
edf88b34da Cfg: Move Case.getBodyElement to shared code. 2026-03-10 11:02:58 +01:00
Owen Mansel-Chan
0215ea3ee3 Merge pull request #21426 from owen-mc/cpp/validate-constructor-summary-models
C++: Add model validation for constructor summary models
2026-03-10 09:42:24 +00:00
Anders Schack-Mulligen
35ac66d3aa Cfg: Move getCaseControlFlowOrder to shared code. 2026-03-10 10:39:32 +01:00
Anders Schack-Mulligen
219fe03637 Merge pull request #21430 from aschackmull/csharp/switch-ast-simplify
C#: Disentangle SwitchStmt AST and CFG.
2026-03-10 10:23:37 +01:00
Ian Lynagh
dbb8bb86ba C++: Small simplification 2026-03-09 17:45:38 +00:00
Anders Schack-Mulligen
4013f00b19 C#: Disentangle SwitchStmt AST and CFG. 2026-03-09 15:07:59 +01:00
Owen Mansel-Chan
e0e5319b11 C#: Make corresponding predicate private 2026-03-09 13:44:52 +00:00
Owen Mansel-Chan
d8007a85e6 Java: Make corresponding predicate private 2026-03-09 13:44:50 +00:00
Owen Mansel-Chan
512e27187e Make new predicate private 2026-03-09 13:44:48 +00:00
Taus
c5360ba46c Python: Fix bad join in method call order computation
This join had badness 1127 on the project FiacreT/M-moire, producing ~31
million tuples in order to end up with only ~27k tuples later in the
pipeline. With the fix, we reduce this by roughly the full 31 million
(the new materialised helper predicate accounting for roughly 130k
tuples on its own).

Co-authored-by: Mathias Vorreiter Pedersen <mathiasvp@github.com>
2026-03-09 13:09:29 +00:00
Owen Mansel-Chan
097681e705 Update cpp/ql/lib/semmle/code/cpp/dataflow/ExternalFlow.qll
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-03-07 10:17:58 +00:00
Owen Mansel-Chan
63e8061917 Add model validation for constructor summary models 2026-03-07 09:57:09 +00:00
Tom Hvitved
4dca9aa958 Rust: Small refactor in TypeMention.qll 2026-03-06 15:33:11 +01:00
Paolo Tranquilli
9bf4262dbb Add /rerun slash command for failed internal checks 2026-03-05 11:38:27 +01:00
424 changed files with 1813 additions and 2718 deletions

View File

@@ -21,7 +21,7 @@ bazel_dep(name = "rules_java", version = "9.0.3")
bazel_dep(name = "rules_pkg", version = "1.0.1")
bazel_dep(name = "rules_nodejs", version = "6.7.3")
bazel_dep(name = "rules_python", version = "1.9.0")
bazel_dep(name = "rules_shell", version = "0.5.0")
bazel_dep(name = "rules_shell", version = "0.6.1")
bazel_dep(name = "bazel_skylib", version = "1.8.1")
bazel_dep(name = "abseil-cpp", version = "20260107.1", repo_name = "absl")
bazel_dep(name = "nlohmann_json", version = "3.11.3", repo_name = "json")
@@ -29,7 +29,7 @@ bazel_dep(name = "fmt", version = "12.1.0-codeql.1")
bazel_dep(name = "rules_kotlin", version = "2.2.2-codeql.1")
bazel_dep(name = "gazelle", version = "0.47.0")
bazel_dep(name = "rules_dotnet", version = "0.21.5-codeql.1")
bazel_dep(name = "googletest", version = "1.14.0.bcr.1")
bazel_dep(name = "googletest", version = "1.17.0.bcr.2")
bazel_dep(name = "rules_rust", version = "0.68.1.codeql.1")
bazel_dep(name = "zstd", version = "1.5.7.bcr.1")

View File

@@ -1,3 +1,7 @@
## 0.4.30
No user-facing changes.
## 0.4.29
No user-facing changes.

View File

@@ -0,0 +1,3 @@
## 0.4.30
No user-facing changes.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.4.29
lastReleaseVersion: 0.4.30

View File

@@ -1,5 +1,5 @@
name: codeql/actions-all
version: 0.4.30-dev
version: 0.4.30
library: true
warnOnImplicitThis: true
dependencies:

View File

@@ -1,3 +1,7 @@
## 0.6.22
No user-facing changes.
## 0.6.21
No user-facing changes.

View File

@@ -0,0 +1,3 @@
## 0.6.22
No user-facing changes.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.6.21
lastReleaseVersion: 0.6.22

View File

@@ -1,5 +1,5 @@
name: codeql/actions-queries
version: 0.6.22-dev
version: 0.6.22
library: false
warnOnImplicitThis: true
groups: [actions, queries]

View File

@@ -199,6 +199,7 @@ def annotate_as_appropriate(filename, lines):
# as overlay[local?]. It is not clear that these heuristics are exactly what we want,
# but they seem to work well enough for now (as determined by speed and accuracy numbers).
if (filename.endswith("Test.qll") or
re.search(r"go/ql/lib/semmle/go/security/[^/]+[.]qll$", filename.replace(os.sep, "/")) or
((filename.endswith("Query.qll") or filename.endswith("Config.qll")) and
any("implements DataFlow::ConfigSig" in line for line in lines))):
return None

View File

@@ -172,10 +172,6 @@
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/reachability/PrintDominance.qll",
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/PrintDominance.qll"
],
"C# ControlFlowReachability": [
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/ControlFlowReachability.qll",
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/ControlFlowReachability.qll"
],
"C++ ExternalAPIs": [
"cpp/ql/src/Security/CWE/CWE-020/ExternalAPIs.qll",
"cpp/ql/src/Security/CWE/CWE-020/ir/ExternalAPIs.qll"

View File

@@ -1,3 +1,9 @@
## 8.0.1
### Minor Analysis Improvements
* Inline expectations test comments, which are of the form `// $ tag` or `// $ tag=value`, are now parsed more strictly and will not be recognized if there isn't a space after the `$` symbol.
## 8.0.0
### Breaking Changes

View File

@@ -1,4 +1,5 @@
---
category: minorAnalysis
---
## 8.0.1
### Minor Analysis Improvements
* Inline expectations test comments, which are of the form `// $ tag` or `// $ tag=value`, are now parsed more strictly and will not be recognized if there isn't a space after the `$` symbol.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 8.0.0
lastReleaseVersion: 8.0.1

View File

@@ -1,5 +1,5 @@
name: codeql/cpp-all
version: 8.0.1-dev
version: 8.0.1
groups: cpp
dbscheme: semmlecode.cpp.dbscheme
extractor: cpp

View File

@@ -1663,7 +1663,7 @@ private module Cached {
private predicate compares_ge(
ValueNumber test, Operand left, Operand right, int k, boolean isGe, GuardValue value
) {
exists(int onemk | k = 1 - onemk | compares_lt(test, right, left, onemk, isGe, value))
compares_lt(test, right, left, 1 - k, isGe, value)
}
/** Rearrange various simple comparisons into `left < right + k` form. */

View File

@@ -353,12 +353,26 @@ module CsvValidation {
)
}
private string getIncorrectConstructorSummaryOutput() {
exists(string namespace, string type, string name, string output |
type = name or
type = name + "<" + any(string s)
|
summaryModel(namespace, type, _, name, _, _, _, output, _, _, _) and
output.matches("ReturnValue%") and
result =
"Constructor model for " + namespace + "." + type +
" should use `Argument[this]` in the output, not `ReturnValue`."
)
}
/** Holds if some row in a CSV-based flow model appears to contain typos. */
query predicate invalidModelRow(string msg) {
msg =
[
getInvalidModelSignature(), getInvalidModelInput(), getInvalidModelOutput(),
getInvalidModelSubtype(), getInvalidModelColumnCount(), KindVal::getInvalidModelKind()
getInvalidModelSubtype(), getInvalidModelColumnCount(), KindVal::getInvalidModelKind(),
getIncorrectConstructorSummaryOutput()
]
}
}

View File

@@ -6,117 +6,67 @@ private import OverlayXml
/**
* Holds always for the overlay variant and never for the base variant.
* This local predicate is used to define local predicates that behave
* differently for the base and overlay variant.
*/
overlay[local]
predicate isOverlay() { databaseMetadata("isOverlay", "true") }
overlay[local]
private string getLocationFilePath(@location_default loc) {
exists(@file file | locations_default(loc, file, _, _, _, _) | files(file, result))
}
/**
* Gets the file path for an element with a single location.
* Holds if the TRAP file or tag `t` is reachable from source file `sourceFile`
* in the base (isOverlayVariant=false) or overlay (isOverlayVariant=true) variant.
*/
overlay[local]
private string getSingleLocationFilePath(@element e) {
exists(@location_default loc |
var_decls(e, _, _, _, loc)
or
fun_decls(e, _, _, _, loc)
or
type_decls(e, _, loc)
or
namespace_decls(e, _, loc, _)
or
macroinvocations(e, _, loc, _)
or
preprocdirects(e, _, loc)
or
diagnostics(e, _, _, _, _, loc)
or
usings(e, _, loc, _)
or
static_asserts(e, _, _, loc, _)
or
derivations(e, _, _, _, loc)
or
frienddecls(e, _, _, loc)
or
comments(e, _, loc)
or
exprs(e, _, loc)
or
stmts(e, _, loc)
or
initialisers(e, _, _, loc)
or
attributes(e, _, _, _, loc)
or
attribute_args(e, _, _, _, loc)
or
namequalifiers(e, _, _, loc)
or
enumconstants(e, _, _, _, _, loc)
or
type_mentions(e, _, loc, _)
or
lambda_capture(e, _, _, _, _, _, loc)
or
concept_templates(e, _, loc)
|
result = getLocationFilePath(loc)
private predicate locallyReachableTrapOrTag(
boolean isOverlayVariant, string sourceFile, @trap_or_tag t
) {
exists(@source_file sf, @trap trap |
(if isOverlay() then isOverlayVariant = true else isOverlayVariant = false) and
source_file_uses_trap(sf, trap) and
source_file_name(sf, sourceFile) and
(t = trap or trap_uses_tag(trap, t))
)
}
/**
* Gets the file path for an element with potentially multiple locations.
* Holds if element `e` is in TRAP file or tag `t`
* in the base (isOverlayVariant=false) or overlay (isOverlayVariant=true) variant.
*/
overlay[local]
private string getMultiLocationFilePath(@element e) {
exists(@location_default loc |
var_decls(_, e, _, _, loc)
or
fun_decls(_, e, _, _, loc)
or
type_decls(_, e, loc)
or
namespace_decls(_, e, loc, _)
|
result = getLocationFilePath(loc)
)
}
/**
* A local helper predicate that holds in the base variant and never in the
* overlay variant.
*/
overlay[local]
private predicate isBase() { not isOverlay() }
/**
* Holds if `path` was extracted in the overlay database.
*/
overlay[local]
private predicate overlayHasFile(string path) {
isOverlay() and
files(_, path) and
path != ""
private predicate locallyInTrapOrTag(boolean isOverlayVariant, @element e, @trap_or_tag t) {
(if isOverlay() then isOverlayVariant = true else isOverlayVariant = false) and
in_trap_or_tag(e, t)
}
/**
* Discards an element from the base variant if:
* - It has a single location in a file extracted in the overlay, or
* - All of its locations are in files extracted in the overlay.
* - We have knowledge about what TRAP file or tag it is in (in the base).
* - It is not in any overlay TRAP file or tag that is reachable from an overlay source file.
* - For every base TRAP file or tag that contains it and is reachable from a base source file,
* either the source file has changed, or the overlay has redefined the TRAP file or tag,
* or the overlay runner has re-extracted the same source file.
*/
overlay[discard_entity]
private predicate discardElement(@element e) {
isBase() and
(
overlayHasFile(getSingleLocationFilePath(e))
or
forex(string path | path = getMultiLocationFilePath(e) | overlayHasFile(path))
// If we don't have any knowledge about what TRAP file something
// is in, then we don't want to discard it, so we only consider
// entities that are known to be in a base TRAP file or tag.
locallyInTrapOrTag(false, e, _) and
// Anything that is reachable from an overlay source file should
// not be discarded.
not exists(@trap_or_tag t | locallyInTrapOrTag(true, e, t) |
locallyReachableTrapOrTag(true, _, t)
) and
// Finally, we have to make sure the base variant does not retain it.
// If it is reachable from a base source file, then that is
// sufficient unless either the base source file has changed (in
// particular, been deleted), or the overlay has redefined the TRAP
// file or tag it is in, or the overlay runner has re-extracted the same
// source file (e.g. because a header it includes has changed).
forall(@trap_or_tag t, string sourceFile |
locallyInTrapOrTag(false, e, t) and
locallyReachableTrapOrTag(false, sourceFile, t)
|
overlayChangedFiles(sourceFile) or
locallyReachableTrapOrTag(true, _, t) or
locallyReachableTrapOrTag(true, sourceFile, _)
)
}

View File

@@ -1,3 +1,7 @@
## 1.5.13
No user-facing changes.
## 1.5.12
No user-facing changes.

View File

@@ -0,0 +1,3 @@
## 1.5.13
No user-facing changes.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 1.5.12
lastReleaseVersion: 1.5.13

View File

@@ -1,5 +1,5 @@
name: codeql/cpp-queries
version: 1.5.13-dev
version: 1.5.13
groups:
- cpp
- queries

View File

@@ -1,3 +1,7 @@
## 1.7.61
No user-facing changes.
## 1.7.60
No user-facing changes.

View File

@@ -0,0 +1,3 @@
## 1.7.61
No user-facing changes.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 1.7.60
lastReleaseVersion: 1.7.61

View File

@@ -1,5 +1,5 @@
name: codeql/csharp-solorigate-all
version: 1.7.61-dev
version: 1.7.61
groups:
- csharp
- solorigate

View File

@@ -1,3 +1,7 @@
## 1.7.61
No user-facing changes.
## 1.7.60
No user-facing changes.

View File

@@ -0,0 +1,3 @@
## 1.7.61
No user-facing changes.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 1.7.60
lastReleaseVersion: 1.7.61

View File

@@ -1,5 +1,5 @@
name: codeql/csharp-solorigate-queries
version: 1.7.61-dev
version: 1.7.61
groups:
- csharp
- solorigate

View File

@@ -1,63 +1,5 @@
import csharp
import semmle.code.csharp.controlflow.internal.Completion
import semmle.code.csharp.controlflow.internal.PreBasicBlocks
import ControlFlow
import semmle.code.csharp.controlflow.internal.ControlFlowGraphImpl::Consistency
import semmle.code.csharp.controlflow.internal.Splitting
private predicate splitBB(ControlFlow::BasicBlock bb) {
exists(ControlFlow::Node first |
first = bb.getFirstNode() and
first.isJoin() and
strictcount(first.getAPredecessor().getAstNode()) = 1
)
}
private class RelevantBasicBlock extends ControlFlow::BasicBlock {
RelevantBasicBlock() { not splitBB(this) }
}
predicate bbStartInconsistency(ControlFlowElement cfe) {
exists(RelevantBasicBlock bb | bb.getFirstNode() = cfe.getAControlFlowNode()) and
not cfe = any(PreBasicBlock bb).getFirstElement()
}
predicate bbSuccInconsistency(ControlFlowElement pred, ControlFlowElement succ) {
exists(RelevantBasicBlock predBB, RelevantBasicBlock succBB |
predBB.getLastNode() = pred.getAControlFlowNode() and
succBB = predBB.getASuccessor() and
succBB.getFirstNode() = succ.getAControlFlowNode()
) and
not exists(PreBasicBlock predBB, PreBasicBlock succBB |
predBB.getLastNode() = pred and
succBB = predBB.getASuccessor() and
succBB.getFirstElement() = succ
)
}
predicate bbIntraSuccInconsistency(ControlFlowElement pred, ControlFlowElement succ) {
exists(ControlFlow::BasicBlock bb, int i |
pred.getAControlFlowNode() = bb.getNode(i) and
succ.getAControlFlowNode() = bb.getNode(i + 1)
) and
not exists(PreBasicBlock bb |
bb.getLastNode() = pred and
bb.getASuccessor().getFirstElement() = succ
) and
not exists(PreBasicBlock bb, int i |
bb.getNode(i) = pred and
bb.getNode(i + 1) = succ
)
}
query predicate preBasicBlockConsistency(ControlFlowElement cfe1, ControlFlowElement cfe2, string s) {
bbStartInconsistency(cfe1) and
cfe2 = cfe1 and
s = "start inconsistency"
or
bbSuccInconsistency(cfe1, cfe2) and
s = "succ inconsistency"
or
bbIntraSuccInconsistency(cfe1, cfe2) and
s = "intra succ inconsistency"
}

View File

@@ -35,9 +35,7 @@ private module Input implements InputSig<Location, CsharpDataFlow> {
or
n.asExpr().(ObjectCreation).hasInitializer()
or
exists(
n.(PostUpdateNode).getPreUpdateNode().asExprAtNode(LocalFlow::getPostUpdateReverseStep(_))
)
n.(PostUpdateNode).getPreUpdateNode().asExpr() = LocalFlow::getPostUpdateReverseStep(_)
}
predicate argHasPostUpdateExclude(ArgumentNode n) {

View File

@@ -1,3 +1,13 @@
## 5.4.9
### Minor Analysis Improvements
* Inline expectations test comments, which are of the form `// $ tag` or `// $ tag=value`, are now parsed more strictly and will not be recognized if there isn't a space after the `$` symbol.
* Added `System.Net.WebSockets::ReceiveAsync` as a remote flow source.
* Added reverse taint flow from implicit conversion operator calls to their arguments.
* Added post-update nodes for struct-type arguments, allowing data flow out of method calls via those arguments.
* C# 14: Added support for partial constructors.
## 5.4.8
### Minor Analysis Improvements

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* C# 14: Added support for partial constructors.

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* Added post-update nodes for struct-type arguments, allowing data flow out of method calls via those arguments.

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* Added reverse taint flow from implicit conversion operator calls to their arguments.

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* Added `System.Net.WebSockets::ReceiveAsync` as a remote flow source.

View File

@@ -0,0 +1,9 @@
## 5.4.9
### Minor Analysis Improvements
* Inline expectations test comments, which are of the form `// $ tag` or `// $ tag=value`, are now parsed more strictly and will not be recognized if there isn't a space after the `$` symbol.
* Added `System.Net.WebSockets::ReceiveAsync` as a remote flow source.
* Added reverse taint flow from implicit conversion operator calls to their arguments.
* Added post-update nodes for struct-type arguments, allowing data flow out of method calls via those arguments.
* C# 14: Added support for partial constructors.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 5.4.8
lastReleaseVersion: 5.4.9

View File

@@ -1,5 +1,5 @@
name: codeql/csharp-all
version: 5.4.9-dev
version: 5.4.9
groups: csharp
dbscheme: semmlecode.csharp.dbscheme
extractor: csharp

View File

@@ -183,9 +183,10 @@ class SwitchStmt extends SelectionStmt, Switch, @switch_stmt {
* return 3;
* }
* ```
* Note that this reorders the `default` case to always be at the end.
*/
override CaseStmt getCase(int i) { result = SwithStmtInternal::getCase(this, i) }
override CaseStmt getCase(int i) {
result = rank[i + 1](CaseStmt cs, int idx | cs = this.getChildStmt(idx) | cs order by idx)
}
/** Gets a case of this `switch` statement. */
override CaseStmt getACase() { result = this.getCase(_) }
@@ -208,87 +209,29 @@ class SwitchStmt extends SelectionStmt, Switch, @switch_stmt {
* ```csharp
* switch (x) {
* case "abc": // i = 0
* return 0;
* case int i when i > 0: // i = 1
* return 1;
* case string s: // i = 2
* Console.WriteLine(s);
* return 2; // i = 3
* default: // i = 4
* return 3; // i = 5
* return 0; // i = 1
* case int i when i > 0: // i = 2
* return 1; // i = 3
* case string s: // i = 4
* Console.WriteLine(s); // i = 5
* return 2; // i = 6
* default: // i = 7
* return 3; // i = 8
* }
* ```
*
* Note that each non-`default` case is a labeled statement, so the statement
* that follows is a child of the labeled statement, and not the `switch` block.
*/
Stmt getStmt(int i) { result = SwithStmtInternal::getStmt(this, i) }
Stmt getStmt(int i) { result = this.getChildStmt(i) }
/** Gets a statement in the body of this `switch` statement. */
Stmt getAStmt() { result = this.getStmt(_) }
}
cached
private module SwithStmtInternal {
cached
CaseStmt getCase(SwitchStmt ss, int i) {
exists(int index, int rankIndex |
caseIndex(ss, result, index) and
rankIndex = i + 1 and
index = rank[rankIndex](int j, CaseStmt cs | caseIndex(ss, cs, j) | j)
)
}
/** Implicitly reorder case statements to put the default case last if needed. */
private predicate caseIndex(SwitchStmt ss, CaseStmt case, int index) {
exists(int i | case = ss.getChildStmt(i) |
if case instanceof DefaultCase
then index = max(int j | exists(ss.getChildStmt(j))) + 1
else index = i
)
}
cached
Stmt getStmt(SwitchStmt ss, int i) {
exists(int index, int rankIndex |
result = ss.getChildStmt(index) and
rankIndex = i + 1 and
index =
rank[rankIndex](int j, Stmt s |
// `getChild` includes both labeled statements and the targeted
// statements of labeled statement as separate children, but we
// only want the labeled statement
s = getLabeledStmt(ss, j)
|
j
)
)
}
private Stmt getLabeledStmt(SwitchStmt ss, int i) {
result = ss.getChildStmt(i) and
not result = any(CaseStmt cs).getBody()
}
}
/** A `case` statement. */
class CaseStmt extends Case, @case_stmt {
override Expr getExpr() { result = any(SwitchStmt ss | ss.getACase() = this).getExpr() }
override PatternExpr getPattern() { result = this.getChild(0) }
override Stmt getBody() {
exists(int i, Stmt next |
this = this.getParent().getChild(i) and
next = this.getParent().getChild(i + 1)
|
result = next and
not result instanceof CaseStmt
or
result = next.(CaseStmt).getBody()
)
}
/**
* Gets the condition on this case, if any. For example, the type case on line 3
* has no condition, and the type case on line 4 has condition `s.Length > 0`, in

View File

@@ -308,6 +308,93 @@ private class ConstructorTree extends ControlFlowTree instanceof Constructor {
}
}
cached
private module SwithStmtInternal {
// Reorders default to be last if needed
cached
CaseStmt getCase(SwitchStmt ss, int i) {
exists(int index, int rankIndex |
caseIndex(ss, result, index) and
rankIndex = i + 1 and
index = rank[rankIndex](int j, CaseStmt cs | caseIndex(ss, cs, j) | j)
)
}
/** Implicitly reorder case statements to put the default case last if needed. */
private predicate caseIndex(SwitchStmt ss, CaseStmt case, int index) {
exists(int i | case = ss.getChildStmt(i) |
if case instanceof DefaultCase
then index = max(int j | exists(ss.getChildStmt(j))) + 1
else index = i
)
}
/**
* Gets the `i`th statement in the body of this `switch` statement.
*
* Example:
*
* ```csharp
* switch (x) {
* case "abc": // i = 0
* return 0;
* case int i when i > 0: // i = 1
* return 1;
* case string s: // i = 2
* Console.WriteLine(s);
* return 2; // i = 3
* default: // i = 4
* return 3; // i = 5
* }
* ```
*
* Note that each non-`default` case is a labeled statement, so the statement
* that follows is a child of the labeled statement, and not the `switch` block.
*/
cached
Stmt getStmt(SwitchStmt ss, int i) {
exists(int index, int rankIndex |
result = ss.getChildStmt(index) and
rankIndex = i + 1 and
index =
rank[rankIndex](int j, Stmt s |
// `getChild` includes both labeled statements and the targeted
// statements of labeled statement as separate children, but we
// only want the labeled statement
s = getLabeledStmt(ss, j)
|
j
)
)
}
private Stmt getLabeledStmt(SwitchStmt ss, int i) {
result = ss.getChildStmt(i) and
not result = caseStmtGetBody(_)
}
}
private ControlFlowElement caseGetBody(Case c) {
result = c.getBody() or result = caseStmtGetBody(c)
}
private ControlFlowElement caseStmtGetBody(CaseStmt c) {
exists(int i, Stmt next |
c = c.getParent().getChild(i) and
next = c.getParent().getChild(i + 1)
|
result = next and
not result instanceof CaseStmt
or
result = caseStmtGetBody(next)
)
}
// Reorders default to be last if needed
private Case switchGetCase(Switch s, int i) {
result = s.(SwitchExpr).getCase(i) or result = SwithStmtInternal::getCase(s, i)
}
abstract private class SwitchTree extends ControlFlowTree instanceof Switch {
override predicate propagatesAbnormal(AstNode child) { child = super.getExpr() }
@@ -315,27 +402,27 @@ abstract private class SwitchTree extends ControlFlowTree instanceof Switch {
// Flow from last element of switch expression to first element of first case
last(super.getExpr(), pred, c) and
c instanceof NormalCompletion and
first(super.getCase(0), succ)
first(switchGetCase(this, 0), succ)
or
// Flow from last element of case pattern to next case
exists(Case case, int i | case = super.getCase(i) |
exists(Case case, int i | case = switchGetCase(this, i) |
last(case.getPattern(), pred, c) and
c.(MatchingCompletion).isNonMatch() and
first(super.getCase(i + 1), succ)
first(switchGetCase(this, i + 1), succ)
)
or
// Flow from last element of condition to next case
exists(Case case, int i | case = super.getCase(i) |
exists(Case case, int i | case = switchGetCase(this, i) |
last(case.getCondition(), pred, c) and
c instanceof FalseCompletion and
first(super.getCase(i + 1), succ)
first(switchGetCase(this, i + 1), succ)
)
}
}
abstract private class CaseTree extends ControlFlowTree instanceof Case {
final override predicate propagatesAbnormal(AstNode child) {
child in [super.getPattern().(ControlFlowElement), super.getCondition(), super.getBody()]
child in [super.getPattern().(ControlFlowElement), super.getCondition(), caseGetBody(this)]
}
override predicate succ(AstNode pred, AstNode succ, Completion c) {
@@ -348,13 +435,13 @@ abstract private class CaseTree extends ControlFlowTree instanceof Case {
first(super.getCondition(), succ)
else
// Flow from last element of pattern to first element of body
first(super.getBody(), succ)
first(caseGetBody(this), succ)
)
or
// Flow from last element of condition to first element of body
last(super.getCondition(), pred, c) and
c instanceof TrueCompletion and
first(super.getBody(), succ)
first(caseGetBody(this), succ)
}
}
@@ -1226,10 +1313,11 @@ module Statements {
c instanceof NormalCompletion
or
// A statement exits with a `break` completion
last(super.getStmt(_), last, c.(NestedBreakCompletion).getAnInnerCompatibleCompletion())
last(SwithStmtInternal::getStmt(this, _), last,
c.(NestedBreakCompletion).getAnInnerCompatibleCompletion())
or
// A statement exits abnormally
last(super.getStmt(_), last, c) and
last(SwithStmtInternal::getStmt(this, _), last, c) and
not c instanceof BreakCompletion and
not c instanceof NormalCompletion and
not any(LabeledStmtTree t |
@@ -1238,8 +1326,8 @@ module Statements {
or
// Last case exits with a non-match
exists(CaseStmt cs, int last_ |
last_ = max(int i | exists(super.getCase(i))) and
cs = super.getCase(last_)
last_ = max(int i | exists(SwithStmtInternal::getCase(this, i))) and
cs = SwithStmtInternal::getCase(this, last_)
|
last(cs.getPattern(), last, c) and
not c.(MatchingCompletion).isMatch()
@@ -1258,22 +1346,22 @@ module Statements {
c instanceof SimpleCompletion
or
// Flow from last element of non-`case` statement `i` to first element of statement `i+1`
exists(int i | last(super.getStmt(i), pred, c) |
not super.getStmt(i) instanceof CaseStmt and
exists(int i | last(SwithStmtInternal::getStmt(this, i), pred, c) |
not SwithStmtInternal::getStmt(this, i) instanceof CaseStmt and
c instanceof NormalCompletion and
first(super.getStmt(i + 1), succ)
first(SwithStmtInternal::getStmt(this, i + 1), succ)
)
or
// Flow from last element of `case` statement `i` to first element of statement `i+1`
exists(int i, Stmt body |
body = super.getStmt(i).(CaseStmt).getBody() and
body = caseStmtGetBody(SwithStmtInternal::getStmt(this, i)) and
// in case of fall-through cases, make sure to not jump from their shared body back
// to one of the fall-through cases
not body = super.getStmt(i + 1).(CaseStmt).getBody() and
not body = caseStmtGetBody(SwithStmtInternal::getStmt(this, i + 1)) and
last(body, pred, c)
|
c instanceof NormalCompletion and
first(super.getStmt(i + 1), succ)
first(SwithStmtInternal::getStmt(this, i + 1), succ)
)
}
}
@@ -1289,7 +1377,7 @@ module Statements {
not c.(MatchingCompletion).isMatch()
or
// Case body exits with any completion
last(super.getBody(), last, c)
last(caseStmtGetBody(this), last, c)
}
final override predicate succ(AstNode pred, AstNode succ, Completion c) {

View File

@@ -1,175 +0,0 @@
/**
* INTERNAL: Do not use.
*
* Provides a basic block implementation on control flow elements. That is,
* a "pre-CFG" where the nodes are (unsplit) control flow elements and the
* successor relation is `succ = succ(pred, _)`.
*
* The logic is duplicated from the implementation in `BasicBlocks.qll`, and
* being an internal class, all predicate documentation has been removed.
*/
import csharp
private import Completion
private import ControlFlowGraphImpl
private import semmle.code.csharp.controlflow.ControlFlowGraph::ControlFlow as Cfg
private import codeql.controlflow.BasicBlock as BB
private predicate startsBB(ControlFlowElement cfe) {
not succ(_, cfe, _) and
(
succ(cfe, _, _)
or
scopeLast(_, cfe, _)
)
or
strictcount(ControlFlowElement pred, Completion c | succ(pred, cfe, c)) > 1
or
succ(_, cfe, any(ConditionalCompletion c))
or
exists(ControlFlowElement pred, int i |
succ(pred, cfe, _) and
i = count(ControlFlowElement succ, Completion c | succ(pred, succ, c))
|
i > 1
or
i = 1 and
scopeLast(_, pred, _)
)
}
private predicate intraBBSucc(ControlFlowElement pred, ControlFlowElement succ) {
succ(pred, succ, _) and
not startsBB(succ)
}
private predicate bbIndex(ControlFlowElement bbStart, ControlFlowElement cfe, int i) =
shortestDistances(startsBB/1, intraBBSucc/2)(bbStart, cfe, i)
private predicate succBB(PreBasicBlock pred, PreBasicBlock succ) { succ = pred.getASuccessor() }
private predicate entryBB(PreBasicBlock bb) { scopeFirst(_, bb) }
private predicate bbIDominates(PreBasicBlock dom, PreBasicBlock bb) =
idominance(entryBB/1, succBB/2)(_, dom, bb)
class PreBasicBlock extends ControlFlowElement {
PreBasicBlock() { startsBB(this) }
PreBasicBlock getASuccessor(Cfg::SuccessorType t) {
succ(this.getLastNode(), result, any(Completion c | t = c.getAMatchingSuccessorType()))
}
deprecated PreBasicBlock getASuccessorByType(Cfg::SuccessorType t) {
result = this.getASuccessor(t)
}
PreBasicBlock getASuccessor() { result = this.getASuccessor(_) }
PreBasicBlock getAPredecessor() { result.getASuccessor() = this }
ControlFlowElement getNode(int pos) { bbIndex(this, result, pos) }
deprecated ControlFlowElement getElement(int pos) { result = this.getNode(pos) }
ControlFlowElement getAnElement() { result = this.getNode(_) }
ControlFlowElement getFirstElement() { result = this }
ControlFlowElement getLastNode() { result = this.getNode(this.length() - 1) }
deprecated ControlFlowElement getLastElement() { result = this.getLastNode() }
int length() { result = strictcount(this.getAnElement()) }
PreBasicBlock getImmediateDominator() { bbIDominates(result, this) }
predicate immediatelyDominates(PreBasicBlock bb) { bbIDominates(this, bb) }
pragma[inline]
predicate strictlyDominates(PreBasicBlock bb) { this.immediatelyDominates+(bb) }
pragma[inline]
predicate dominates(PreBasicBlock bb) {
bb = this
or
this.strictlyDominates(bb)
}
predicate inDominanceFrontier(PreBasicBlock df) {
this = df.getAPredecessor() and not bbIDominates(this, df)
or
exists(PreBasicBlock prev | prev.inDominanceFrontier(df) |
bbIDominates(this, prev) and
not bbIDominates(this, df)
)
}
/** Unsupported. Do not use. */
predicate strictlyPostDominates(PreBasicBlock bb) { none() }
/** Unsupported. Do not use. */
predicate postDominates(PreBasicBlock bb) {
this.strictlyPostDominates(bb) or
this = bb
}
}
private Completion getConditionalCompletion(ConditionalCompletion cc) {
result.getInnerCompletion() = cc
}
pragma[nomagic]
private predicate conditionBlockImmediatelyControls(
ConditionBlock cond, PreBasicBlock succ, ConditionalCompletion cc
) {
exists(ControlFlowElement last, Completion c |
last = cond.getLastNode() and
c = getConditionalCompletion(cc) and
succ(last, succ, c) and
// In the pre-CFG, we need to account for case where one predecessor node has
// two edges to the same successor node. Assertion expressions are examples of
// such nodes.
not exists(Completion other |
succ(last, succ, other) and
other != c
) and
forall(PreBasicBlock pred | pred = succ.getAPredecessor() and pred != cond |
succ.dominates(pred)
)
)
}
class ConditionBlock extends PreBasicBlock {
ConditionBlock() {
exists(Completion c | c = getConditionalCompletion(_) |
succ(this.getLastNode(), _, c)
or
scopeLast(_, this.getLastNode(), c)
)
}
pragma[nomagic]
predicate controls(PreBasicBlock controlled, Cfg::ConditionalSuccessor s) {
exists(PreBasicBlock succ, ConditionalCompletion c |
conditionBlockImmediatelyControls(this, succ, c)
|
succ.dominates(controlled) and
s = c.getAMatchingSuccessorType()
)
}
}
module PreCfg implements BB::CfgSig<Location> {
class ControlFlowNode = ControlFlowElement;
class BasicBlock = PreBasicBlock;
class EntryBasicBlock extends BasicBlock {
EntryBasicBlock() { entryBB(this) }
}
predicate dominatingEdge(BasicBlock bb1, BasicBlock bb2) {
conditionBlockImmediatelyControls(bb1, bb2, _)
}
}

View File

@@ -1,246 +0,0 @@
import csharp
private class ControlFlowScope extends ControlFlowElement {
private boolean exactScope;
ControlFlowScope() {
exists(ControlFlowReachabilityConfiguration c |
c.candidate(_, _, this, exactScope, _) or
c.candidateDef(_, _, this, exactScope, _)
)
}
predicate isExact() { exactScope = true }
predicate isNonExact() { exactScope = false }
}
private newtype TControlFlowElementOrBasicBlock =
TControlFlowElement(ControlFlowElement cfe) or
TBasicBlock(ControlFlow::BasicBlock bb)
class ControlFlowElementOrBasicBlock extends TControlFlowElementOrBasicBlock {
ControlFlowElement asControlFlowElement() { this = TControlFlowElement(result) }
ControlFlow::BasicBlock asBasicBlock() { this = TBasicBlock(result) }
string toString() {
result = this.asControlFlowElement().toString()
or
result = this.asBasicBlock().toString()
}
Location getLocation() {
result = this.asControlFlowElement().getLocation()
or
result = this.asBasicBlock().getLocation()
}
}
private predicate isBasicBlock(ControlFlowElementOrBasicBlock c) { c instanceof TBasicBlock }
private predicate isNonExactScope(ControlFlowElementOrBasicBlock c) {
c.asControlFlowElement().(ControlFlowScope).isNonExact()
}
private predicate step(ControlFlowElementOrBasicBlock pred, ControlFlowElementOrBasicBlock succ) {
pred.asBasicBlock().getANode().getAstNode() = succ.asControlFlowElement()
or
pred.asControlFlowElement() = succ.asControlFlowElement().getAChild()
}
private predicate basicBlockInNonExactScope(
ControlFlowElementOrBasicBlock bb, ControlFlowElementOrBasicBlock scope
) = doublyBoundedFastTC(step/2, isBasicBlock/1, isNonExactScope/1)(bb, scope)
pragma[noinline]
private ControlFlow::BasicBlock getABasicBlockInScope(ControlFlowScope scope, boolean exactScope) {
basicBlockInNonExactScope(TBasicBlock(result), TControlFlowElement(scope)) and
exactScope = false
or
scope.isExact() and
result.getANode().getAstNode() = scope and
exactScope = true
}
/**
* A helper class for determining control-flow reachability for pairs of
* elements.
*
* This is useful when defining for example expression-based data-flow steps in
* the presence of control-flow splitting, where a data-flow step should make
* sure to stay in the same split.
*
* For example, in
*
* ```csharp
* if (b)
* ....
* var x = "foo";
* if (b)
* ....
* ```
*
* there should only be steps from `[b = true] "foo"` to `[b = true] SSA def(x)`
* and `[b = false] "foo"` to `[b = false] SSA def(x)`, and for example not from
* `[b = true] "foo"` to `[b = false] SSA def(x)`
*/
abstract class ControlFlowReachabilityConfiguration extends string {
bindingset[this]
ControlFlowReachabilityConfiguration() { any() }
/**
* Holds if `e1` and `e2` are expressions for which we want to find a
* control-flow path that follows control flow successors (resp.
* predecessors, as specified by `isSuccessor`) inside the syntactic scope
* `scope`. The Boolean `exactScope` indicates whether a transitive child
* of `scope` is allowed (`exactScope = false`).
*/
predicate candidate(
Expr e1, Expr e2, ControlFlowElement scope, boolean exactScope, boolean isSuccessor
) {
none()
}
/**
* Holds if `e` and `def` are elements for which we want to find a
* control-flow path that follows control flow successors (resp.
* predecessors, as specified by `isSuccessor`) inside the syntactic scope
* `scope`. The Boolean `exactScope` indicates whether a transitive child
* of `scope` is allowed (`exactScope = false`).
*/
predicate candidateDef(
Expr e, AssignableDefinition def, ControlFlowElement scope, boolean exactScope,
boolean isSuccessor
) {
none()
}
pragma[nomagic]
private predicate reachesBasicBlockExprBase(
Expr e1, Expr e2, boolean isSuccessor, ControlFlow::Nodes::ElementNode cfn1, int i,
ControlFlow::BasicBlock bb
) {
this.candidate(e1, e2, _, _, isSuccessor) and
cfn1 = e1.getAControlFlowNode() and
bb.getNode(i) = cfn1
}
pragma[nomagic]
private predicate reachesBasicBlockExprRec(
Expr e1, Expr e2, boolean isSuccessor, ControlFlow::Nodes::ElementNode cfn1,
ControlFlow::BasicBlock bb
) {
exists(ControlFlow::BasicBlock mid |
this.reachesBasicBlockExpr(e1, e2, isSuccessor, cfn1, mid)
|
isSuccessor = true and
bb = mid.getASuccessor()
or
isSuccessor = false and
bb = mid.getAPredecessor()
)
}
pragma[nomagic]
private predicate reachesBasicBlockExpr(
Expr e1, Expr e2, boolean isSuccessor, ControlFlow::Nodes::ElementNode cfn1,
ControlFlow::BasicBlock bb
) {
this.reachesBasicBlockExprBase(e1, e2, isSuccessor, cfn1, _, bb)
or
exists(ControlFlowElement scope, boolean exactScope |
this.candidate(e1, e2, scope, exactScope, isSuccessor) and
this.reachesBasicBlockExprRec(e1, e2, isSuccessor, cfn1, bb) and
bb = getABasicBlockInScope(scope, exactScope)
)
}
pragma[nomagic]
private predicate reachesBasicBlockDefinitionBase(
Expr e, AssignableDefinition def, boolean isSuccessor, ControlFlow::Nodes::ElementNode cfn,
int i, ControlFlow::BasicBlock bb
) {
this.candidateDef(e, def, _, _, isSuccessor) and
cfn = e.getAControlFlowNode() and
bb.getNode(i) = cfn
}
pragma[nomagic]
private predicate reachesBasicBlockDefinitionRec(
Expr e, AssignableDefinition def, boolean isSuccessor, ControlFlow::Nodes::ElementNode cfn,
ControlFlow::BasicBlock bb
) {
exists(ControlFlow::BasicBlock mid |
this.reachesBasicBlockDefinition(e, def, isSuccessor, cfn, mid)
|
isSuccessor = true and
bb = mid.getASuccessor()
or
isSuccessor = false and
bb = mid.getAPredecessor()
)
}
pragma[nomagic]
private predicate reachesBasicBlockDefinition(
Expr e, AssignableDefinition def, boolean isSuccessor, ControlFlow::Nodes::ElementNode cfn,
ControlFlow::BasicBlock bb
) {
this.reachesBasicBlockDefinitionBase(e, def, isSuccessor, cfn, _, bb)
or
exists(ControlFlowElement scope, boolean exactScope |
this.candidateDef(e, def, scope, exactScope, isSuccessor) and
this.reachesBasicBlockDefinitionRec(e, def, isSuccessor, cfn, bb) and
bb = getABasicBlockInScope(scope, exactScope)
)
}
/**
* Holds if there is a control-flow path from `cfn1` to `cfn2`, where `cfn1` is a
* control-flow node for `e1` and `cfn2` is a control-flow node for `e2`.
*/
pragma[nomagic]
predicate hasExprPath(Expr e1, ControlFlow::Node cfn1, Expr e2, ControlFlow::Node cfn2) {
exists(ControlFlow::BasicBlock bb, boolean isSuccessor, int i, int j |
this.reachesBasicBlockExprBase(e1, e2, isSuccessor, cfn1, i, bb) and
cfn2 = bb.getNode(j) and
cfn2 = e2.getAControlFlowNode()
|
isSuccessor = true and j >= i
or
isSuccessor = false and i >= j
)
or
exists(ControlFlow::BasicBlock bb |
this.reachesBasicBlockExprRec(e1, e2, _, cfn1, bb) and
cfn2 = bb.getANode() and
cfn2 = e2.getAControlFlowNode()
)
}
/**
* Holds if there is a control-flow path from `cfn` to `cfnDef`, where `cfn` is a
* control-flow node for `e` and `cfnDef` is a control-flow node for `def`.
*/
pragma[nomagic]
predicate hasDefPath(
Expr e, ControlFlow::Node cfn, AssignableDefinition def, ControlFlow::Node cfnDef
) {
exists(ControlFlow::BasicBlock bb, boolean isSuccessor, int i, int j |
this.reachesBasicBlockDefinitionBase(e, def, isSuccessor, cfn, i, bb) and
cfnDef = bb.getNode(j) and
def.getExpr().getAControlFlowNode() = cfnDef
|
isSuccessor = true and j >= i
or
isSuccessor = false and i >= j
)
or
exists(ControlFlow::BasicBlock bb |
this.reachesBasicBlockDefinitionRec(e, def, _, cfn, bb) and
def.getExpr().getAControlFlowNode() = cfnDef and
cfnDef = bb.getANode()
)
}
}

View File

@@ -2,7 +2,6 @@ private import csharp
private import DataFlowPublic
private import DataFlowDispatch
private import DataFlowImplCommon
private import ControlFlowReachability
private import FlowSummaryImpl as FlowSummaryImpl
private import semmle.code.csharp.dataflow.FlowSummary as FlowSummary
private import semmle.code.csharp.dataflow.internal.ExternalFlow
@@ -259,34 +258,16 @@ private module ThisFlow {
}
}
/**
* Holds if there is a control-flow path from `n1` to `n2`. `n2` is either an
* expression node or an SSA definition node.
*/
pragma[nomagic]
predicate hasNodePath(ControlFlowReachabilityConfiguration conf, ExprNode n1, Node n2) {
exists(ControlFlow::Node cfn1, ControlFlow::Node cfn2 | conf.hasExprPath(_, cfn1, _, cfn2) |
cfn1 = n1.getControlFlowNode() and
cfn2 = n2.(ExprNode).getControlFlowNode()
)
or
exists(ControlFlow::Node cfn, AssignableDefinition def, ControlFlow::Node cfnDef |
conf.hasDefPath(_, cfn, def, cfnDef) and
cfn = n1.getControlFlowNode() and
n2 = TAssignableDefinitionNode(def, cfnDef)
)
}
/** Provides logic related to captured variables. */
module VariableCapture {
private import codeql.dataflow.VariableCapture as Shared
private import semmle.code.csharp.controlflow.BasicBlocks as BasicBlocks
private predicate closureFlowStep(ControlFlow::Nodes::ExprNode e1, ControlFlow::Nodes::ExprNode e2) {
e1 = LocalFlow::getALastEvalNode(e2)
e1.getExpr() = LocalFlow::getALastEvalNode(e2.getExpr())
or
exists(Ssa::Definition def, AssignableDefinition adef |
LocalFlow::defAssigns(adef, _, e1) and
LocalFlow::defAssigns(adef, _, _, e1) and
def.getAnUltimateDefinition().(Ssa::ExplicitDefinition).getADefinition() = adef and
exists(def.getAReadAtNode(e2))
)
@@ -379,7 +360,7 @@ module VariableCapture {
this = def.getExpr().getAControlFlowNode()
}
ControlFlow::Node getRhs() { LocalFlow::defAssigns(def, this, result) }
ControlFlow::Node getRhs() { LocalFlow::defAssigns(def, this, _, result) }
CapturedVariable getVariable() { result = v }
}
@@ -528,127 +509,74 @@ module SsaFlow {
/** Provides predicates related to local data flow. */
module LocalFlow {
class LocalExprStepConfiguration extends ControlFlowReachabilityConfiguration {
LocalExprStepConfiguration() { this = "LocalExprStepConfiguration" }
override predicate candidate(
Expr e1, Expr e2, ControlFlowElement scope, boolean exactScope, boolean isSuccessor
) {
exactScope = false and
(
e1 = e2.(ParenthesizedExpr).getExpr() and
scope = e2 and
isSuccessor = true
or
e1 = e2.(NullCoalescingExpr).getAnOperand() and
scope = e2 and
isSuccessor = true
or
e1 = e2.(SuppressNullableWarningExpr).getExpr() and
scope = e2 and
isSuccessor = true
or
e2 =
any(ConditionalExpr ce |
e1 = ce.getThen() or
e1 = ce.getElse()
) and
scope = e2 and
isSuccessor = true
or
e1 = e2.(Cast).getExpr() and
scope = e2 and
isSuccessor = true
or
// An `=` expression, where the result of the expression is used
e2 =
any(AssignExpr ae |
ae.getParent() = any(ControlFlowElement cfe | not cfe instanceof ExprStmt) and
e1 = ae.getRValue()
) and
scope = e2 and
isSuccessor = true
or
e1 = e2.(ObjectCreation).getInitializer() and
scope = e2 and
isSuccessor = false
or
e1 = e2.(ArrayCreation).getInitializer() and
scope = e2 and
isSuccessor = false
or
e1 = e2.(SwitchExpr).getACase().getBody() and
scope = e2 and
isSuccessor = true
or
e1 = e2.(CheckedExpr).getExpr() and
scope = e2 and
isSuccessor = true
or
e1 = e2.(UncheckedExpr).getExpr() and
scope = e2 and
isSuccessor = true
or
e1 = e2.(CollectionExpression).getAnElement() and
e1 instanceof SpreadElementExpr and
scope = e2 and
isSuccessor = true
or
e1 = e2.(SpreadElementExpr).getExpr() and
scope = e2 and
isSuccessor = true
or
exists(WithExpr we |
scope = we and
isSuccessor = true
|
e1 = we.getExpr() and
e2 = we.getInitializer()
or
e1 = we.getInitializer() and
e2 = we
)
or
scope = any(AssignExpr ae | ae.getLValue().(TupleExpr) = e2 and ae.getRValue() = e1) and
isSuccessor = false
or
isSuccessor = true and
exists(ControlFlowElement cfe | cfe = e2.(TupleExpr).(PatternExpr).getPatternMatch() |
cfe.(IsExpr).getExpr() = e1 and scope = cfe
or
exists(Switch sw | sw.getACase() = cfe and sw.getExpr() = e1 and scope = sw)
)
predicate localExprStep(Expr e1, Expr e2) {
e1 = e2.(ParenthesizedExpr).getExpr()
or
e1 = e2.(NullCoalescingExpr).getAnOperand()
or
e1 = e2.(SuppressNullableWarningExpr).getExpr()
or
e2 =
any(ConditionalExpr ce |
e1 = ce.getThen() or
e1 = ce.getElse()
)
}
override predicate candidateDef(
Expr e, AssignableDefinition def, ControlFlowElement scope, boolean exactScope,
boolean isSuccessor
) {
// Flow from source to definition
exactScope = false and
def.getSource() = e and
(
scope = def.getExpr() and
isSuccessor = true
or
scope = def.(AssignableDefinitions::PatternDefinition).getMatch().(IsExpr) and
isSuccessor = false
or
exists(Switch s |
s.getACase() = def.(AssignableDefinitions::PatternDefinition).getMatch() and
isSuccessor = true
|
scope = s.getExpr()
or
scope = s.getACase()
)
or
e1 = e2.(Cast).getExpr()
or
// An `=` expression, where the result of the expression is used
e2 =
any(AssignExpr ae |
ae.getParent() = any(ControlFlowElement cfe | not cfe instanceof ExprStmt) and
e1 = ae.getRValue()
)
}
or
e1 = e2.(ObjectCreation).getInitializer()
or
e1 = e2.(ArrayCreation).getInitializer()
or
e1 = e2.(SwitchExpr).getACase().getBody()
or
e1 = e2.(CheckedExpr).getExpr()
or
e1 = e2.(UncheckedExpr).getExpr()
or
e1 = e2.(CollectionExpression).getAnElement() and
e1 instanceof SpreadElementExpr
or
e1 = e2.(SpreadElementExpr).getExpr()
or
exists(WithExpr we |
e1 = we.getExpr() and
e2 = we.getInitializer()
or
e1 = we.getInitializer() and
e2 = we
)
or
exists(AssignExpr ae | ae.getLValue().(TupleExpr) = e2 and ae.getRValue() = e1)
or
exists(ControlFlowElement cfe | cfe = e2.(TupleExpr).(PatternExpr).getPatternMatch() |
cfe.(IsExpr).getExpr() = e1
or
exists(Switch sw | sw.getACase() = cfe and sw.getExpr() = e1)
)
}
predicate defAssigns(AssignableDefinition def, ControlFlow::Node cfnDef, ControlFlow::Node value) {
any(LocalExprStepConfiguration x).hasDefPath(_, value, def, cfnDef)
predicate defAssigns(
AssignableDefinition def, ControlFlow::Node cfnDef, Expr value, ControlFlow::Node valueCfn
) {
def.getSource() = value and
valueCfn = value.getControlFlowNode() and
cfnDef = def.getExpr().getAControlFlowNode()
}
private predicate defAssigns(ExprNode value, AssignableDefinitionNode defNode) {
exists(ControlFlow::Node cfn, AssignableDefinition def, ControlFlow::Node cfnDef |
defAssigns(def, cfnDef, value.getExpr(), _) and
cfn = value.getControlFlowNode() and
defNode = TAssignableDefinitionNode(def, cfnDef)
)
}
/**
@@ -659,7 +587,9 @@ module LocalFlow {
}
predicate localFlowStepCommon(Node nodeFrom, Node nodeTo) {
hasNodePath(any(LocalExprStepConfiguration x), nodeFrom, nodeTo)
localExprStep(nodeFrom.asExpr(), nodeTo.asExpr())
or
defAssigns(nodeFrom, nodeTo)
or
ThisFlow::adjacentThisRefs(nodeFrom, nodeTo) and
nodeFrom != nodeTo
@@ -685,11 +615,12 @@ module LocalFlow {
}
/**
* Gets a node that may execute last in `n`, and which, when it executes last,
* will be the value of `n`.
* Gets a node that may execute last in `e`, and which, when it executes last,
* will be the value of `e`.
*/
ControlFlow::Nodes::ExprNode getALastEvalNode(ControlFlow::Nodes::ExprNode cfn) {
exists(Expr e | any(LocalExprStepConfiguration x).hasExprPath(_, result, e, cfn) |
Expr getALastEvalNode(Expr e) {
localExprStep(result, e) and
(
e instanceof ConditionalExpr or
e instanceof Cast or
e instanceof NullCoalescingExpr or
@@ -713,9 +644,7 @@ module LocalFlow {
* we add a reverse flow step from `[post] b ? x : y` to `[post] x` and to
* `[post] y`, in order for the side-effect of `m` to reach both `x` and `y`.
*/
ControlFlow::Nodes::ExprNode getPostUpdateReverseStep(ControlFlow::Nodes::ExprNode e) {
result = getALastEvalNode(e)
}
Expr getPostUpdateReverseStep(Expr e) { result = getALastEvalNode(e) }
/**
* Holds if the value of `node2` is given by `node1`.
@@ -729,9 +658,10 @@ module LocalFlow {
e instanceof ThisAccess or e instanceof BaseAccess
)
or
hasNodePath(any(LocalExprStepConfiguration x), node1, node2) and
defAssigns(node1, node2)
or
localExprStep(node1.asExpr(), node2.asExpr()) and
(
node2 instanceof AssignableDefinitionNode or
node2.asExpr() instanceof Cast or
node2.asExpr() instanceof AssignExpr
)
@@ -775,12 +705,8 @@ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo, string model) {
or
nodeTo = nodeFrom.(LocalFunctionCreationNode).getAnAccess(true)
or
nodeTo.(PostUpdateNode).getPreUpdateNode().(ExprNode).getControlFlowNode() =
LocalFlow::getPostUpdateReverseStep(nodeFrom
.(PostUpdateNode)
.getPreUpdateNode()
.(ExprNode)
.getControlFlowNode())
nodeTo.(PostUpdateNode).getPreUpdateNode().asExpr() =
LocalFlow::getPostUpdateReverseStep(nodeFrom.(PostUpdateNode).getPreUpdateNode().asExpr())
) and
model = ""
or
@@ -834,11 +760,11 @@ private class Argument extends Expr {
}
/**
* Holds if `e` is an assignment of `src` to field or property `c` of `q`.
* Holds if there is an assignment of `src` to field or property `c` of `q`.
*
* `postUpdate` indicates whether the store targets a post-update node.
*/
private predicate fieldOrPropertyStore(Expr e, ContentSet c, Expr src, Expr q, boolean postUpdate) {
private predicate fieldOrPropertyStore(ContentSet c, Expr src, Expr q, boolean postUpdate) {
exists(FieldOrProperty f |
c = f.getContentSet() and
(
@@ -861,25 +787,20 @@ private predicate fieldOrPropertyStore(Expr e, ContentSet c, Expr src, Expr q, b
f = fa.getTarget() and
src = def.getSource() and
q = fa.getQualifier() and
e = def.getExpr() and
postUpdate = true
)
or
// `with` expression initializer, `x with { f = src }`
e =
any(WithExpr we |
exists(MemberInitializer mi |
q = we and
mi = we.getInitializer().getAMemberInitializer() and
f = mi.getInitializedMember() and
src = mi.getRValue() and
postUpdate = false
)
)
exists(WithExpr we, MemberInitializer mi |
q = we and
mi = we.getInitializer().getAMemberInitializer() and
f = mi.getInitializedMember() and
src = mi.getRValue() and
postUpdate = false
)
or
// Object initializer, `new C() { f = src }`
exists(MemberInitializer mi |
e = q and
mi = q.(ObjectInitializer).getAMemberInitializer() and
q.getParent() instanceof ObjectCreation and
f = mi.getInitializedMember() and
@@ -888,16 +809,13 @@ private predicate fieldOrPropertyStore(Expr e, ContentSet c, Expr src, Expr q, b
)
or
// Tuple element, `(..., src, ...)` `f` is `ItemX` of tuple `q`
e =
any(TupleExpr te |
exists(int i |
e = q and
src = te.getArgument(i) and
te.isConstruction() and
f = q.getType().(TupleType).getElement(i) and
postUpdate = false
)
)
exists(TupleExpr te, int i |
te = q and
src = te.getArgument(i) and
te.isConstruction() and
f = q.getType().(TupleType).getElement(i) and
postUpdate = false
)
)
or
// A write to a dynamic property
@@ -907,7 +825,6 @@ private predicate fieldOrPropertyStore(Expr e, ContentSet c, Expr src, Expr q, b
c.isDynamicProperty(dp) and
src = def.getSource() and
q = dma.getQualifier() and
e = def.getExpr() and
postUpdate = true
)
}
@@ -943,22 +860,20 @@ private predicate collectionStore(Expr src, CollectionExpression ce) {
}
/**
* Holds if `e` is an expression that adds `src` to array `a`.
* Holds if there is an expression that adds `src` to array `a`.
*
* `postUpdate` indicates whether the store targets a post-update node.
*/
private predicate arrayStore(Expr e, Expr src, Expr a, boolean postUpdate) {
private predicate arrayStore(Expr src, Expr a, boolean postUpdate) {
// Direct assignment, `a[i] = src`
exists(AssignableDefinition def |
a = def.getTargetAccess().(ArrayWrite).getQualifier() and
src = def.getSource() and
e = def.getExpr() and
postUpdate = true
)
or
// Array initializer, `new [] { src }`
src = a.(ArrayInitializer).getAnElement() and
e = a and
postUpdate = false
or
// Member initializer, `new C { Array = { [i] = src } }`
@@ -966,7 +881,6 @@ private predicate arrayStore(Expr e, Expr src, Expr a, boolean postUpdate) {
mi = a.(ObjectInitializer).getAMemberInitializer() and
mi.getLValue() instanceof ArrayAccess and
mi.getRValue() = src and
e = a and
postUpdate = false
)
}
@@ -1141,17 +1055,17 @@ private module Cached {
(
cfn.getExpr() instanceof Argument
or
cfn =
LocalFlow::getPostUpdateReverseStep(any(ControlFlow::Nodes::ExprNode e |
exists(any(SourcePostUpdateNode p).getPreUpdateNode().asExprAtNode(e))
))
cfn.getExpr() =
LocalFlow::getPostUpdateReverseStep(any(SourcePostUpdateNode p)
.getPreUpdateNode()
.asExpr())
) and
exprMayHavePostUpdateNode(cfn.getExpr())
or
exists(Expr e | e = cfn.getExpr() |
fieldOrPropertyStore(_, _, _, e, true)
fieldOrPropertyStore(_, _, e, true)
or
arrayStore(_, _, e, true)
arrayStore(_, e, true)
or
// needed for reverse stores; e.g. `x.f1.f2 = y` induces
// a store step of `f1` into `x`
@@ -1166,7 +1080,7 @@ private module Cached {
)
)
or
lambdaCallExpr(_, cfn)
lambdaCallExpr(_, _, cfn)
} or
TFlowSummaryNode(FlowSummaryImpl::Private::SummaryNode sn) {
sn.getSummarizedCallable() instanceof CallableUsedInSource
@@ -1563,35 +1477,15 @@ abstract private class ArgumentNodeImpl extends Node {
}
private module ArgumentNodes {
private class ArgumentConfiguration extends ControlFlowReachabilityConfiguration {
ArgumentConfiguration() { this = "ArgumentConfiguration" }
override predicate candidate(
Expr e1, Expr e2, ControlFlowElement scope, boolean exactScope, boolean isSuccessor
) {
e1.(Argument).isArgumentOf(e2, _) and
exactScope = false and
isSuccessor = true and
if e2 instanceof PropertyWrite
then
exists(AssignableDefinition def |
def.getTargetAccess() = e2 and
scope = def.getExpr()
)
else scope = e2
}
}
/** A data-flow node that represents an explicit call argument. */
class ExplicitArgumentNode extends ArgumentNodeImpl {
ExplicitArgumentNode() { this.asExpr() instanceof Argument }
override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
exists(ArgumentConfiguration x, Expr c, Argument arg |
exists(Expr c, Argument arg |
arg = this.asExpr() and
c = call.getExpr() and
arg.isArgumentOf(c, pos) and
x.hasExprPath(_, this.getControlFlowNode(), _, call.getControlFlowNode())
arg.isArgumentOf(c, pos)
)
}
}
@@ -1600,7 +1494,7 @@ private module ArgumentNodes {
class DelegateSelfArgumentNode extends ArgumentNodeImpl, ExprNode {
private DataFlowCall call_;
DelegateSelfArgumentNode() { lambdaCallExpr(call_, this.getControlFlowNode()) }
DelegateSelfArgumentNode() { lambdaCallExpr(call_, this.getExpr(), _) }
override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
call = call_ and
@@ -1857,27 +1751,6 @@ private module OutNodes {
}
}
class ObjectOrCollectionInitializerConfiguration extends ControlFlowReachabilityConfiguration {
ObjectOrCollectionInitializerConfiguration() {
this = "ObjectOrCollectionInitializerConfiguration"
}
override predicate candidate(
Expr e1, Expr e2, ControlFlowElement scope, boolean exactScope, boolean isSuccessor
) {
exactScope = false and
scope = e1 and
isSuccessor = true and
exists(ObjectOrCollectionInitializer init | init = e1.(ObjectCreation).getInitializer() |
// E.g. `new Dictionary<int, string>{ {0, "a"}, {1, "b"} }`
e2 = init.(CollectionInitializer).getAnElementInitializer()
or
// E.g. `new Dictionary<int, string>() { [0] = "a", [1] = "b" }`
e2 = init.(ObjectInitializer).getAMemberInitializer().getLValue()
)
}
}
/**
* A data-flow node that reads a value returned by a callable using an
* `out` or `ref` parameter.
@@ -2236,30 +2109,6 @@ predicate jumpStep(Node pred, Node succ) {
succ = pred.(LocalFunctionCreationNode).getAnAccess(false)
}
private class StoreStepConfiguration extends ControlFlowReachabilityConfiguration {
StoreStepConfiguration() { this = "StoreStepConfiguration" }
override predicate candidate(
Expr e1, Expr e2, ControlFlowElement scope, boolean exactScope, boolean isSuccessor
) {
exactScope = false and
fieldOrPropertyStore(scope, _, e1, e2, isSuccessor.booleanNot())
or
exactScope = false and
arrayStore(scope, e1, e2, isSuccessor.booleanNot())
or
exactScope = false and
isSuccessor = true and
collectionStore(e1, e2) and
scope = e2
or
exactScope = false and
isSuccessor = true and
isParamsArg(e2, e1, _) and
scope = e2
}
}
pragma[nomagic]
private ContentSet getResultContent() {
result.isProperty(any(SystemThreadingTasksTaskTClass c_).getResultProperty())
@@ -2282,21 +2131,17 @@ private predicate recordParameter(RecordType t, Parameter p, string name) {
}
private predicate storeContentStep(Node node1, Content c, Node node2) {
exists(StoreStepConfiguration x, ExprNode node, boolean postUpdate |
hasNodePath(x, node1, node) and
exists(ExprNode node, boolean postUpdate |
if postUpdate = true then node = node2.(PostUpdateNode).getPreUpdateNode() else node = node2
|
arrayStore(_, node1.asExpr(), node.getExpr(), postUpdate) and c instanceof ElementContent
arrayStore(node1.asExpr(), node.getExpr(), postUpdate) and c instanceof ElementContent
)
or
exists(StoreStepConfiguration x | hasNodePath(x, node1, node2) |
collectionStore(node1.asExpr(), node2.asExpr()) and c instanceof ElementContent
)
collectionStore(node1.asExpr(), node2.asExpr()) and c instanceof ElementContent
or
exists(StoreStepConfiguration x, Expr arg, ControlFlow::Node callCfn |
x.hasExprPath(arg, node1.(ExprNode).getControlFlowNode(), _, callCfn) and
node2 = TParamsArgumentNode(callCfn) and
isParamsArg(_, arg, _) and
exists(Call call |
node2 = TParamsArgumentNode(call.getControlFlowNode()) and
isParamsArg(call, node1.asExpr(), _) and
c instanceof ElementContent
)
or
@@ -2352,11 +2197,10 @@ predicate storeStep(Node node1, ContentSet c, Node node2) {
c.isSingleton(cont)
)
or
exists(StoreStepConfiguration x, ExprNode node, boolean postUpdate |
hasNodePath(x, node1, node) and
exists(ExprNode node, boolean postUpdate |
if postUpdate = true then node = node2.(PostUpdateNode).getPreUpdateNode() else node = node2
|
fieldOrPropertyStore(_, c, node1.asExpr(), node.getExpr(), postUpdate)
fieldOrPropertyStore(c, node1.asExpr(), node.getExpr(), postUpdate)
)
or
exists(Expr e |
@@ -2378,133 +2222,51 @@ predicate storeStep(Node node1, ContentSet c, Node node2) {
storeStepDelegateCall(node1, c, node2)
}
pragma[nomagic]
private predicate isAssignExprLValueDescendant(Expr e) {
e = any(AssignExpr ae).getLValue()
or
exists(Expr parent |
isAssignExprLValueDescendant(parent) and
e = parent.getAChildExpr()
)
}
private class ReadStepConfiguration extends ControlFlowReachabilityConfiguration {
ReadStepConfiguration() { this = "ReadStepConfiguration" }
override predicate candidate(
Expr e1, Expr e2, ControlFlowElement scope, boolean exactScope, boolean isSuccessor
) {
exactScope = false and
isSuccessor = true and
fieldOrPropertyRead(e1, _, e2) and
scope = e2
or
exactScope = false and
isSuccessor = true and
dynamicPropertyRead(e1, _, e2) and
scope = e2
or
exactScope = false and
isSuccessor = true and
arrayRead(e1, e2) and
scope = e2
or
exactScope = false and
e1 = e2.(AwaitExpr).getExpr() and
scope = e2 and
isSuccessor = true
or
exactScope = false and
e2 = e1.(TupleExpr).getAnArgument() and
scope = e1 and
isSuccessor = false
}
override predicate candidateDef(
Expr e, AssignableDefinition defTo, ControlFlowElement scope, boolean exactScope,
boolean isSuccessor
) {
exists(ForeachStmt fs |
e = fs.getIterableExpr() and
defTo.(AssignableDefinitions::LocalVariableDefinition).getDeclaration() =
fs.getVariableDeclExpr() and
isSuccessor = true
|
scope = fs and
exactScope = true
or
scope = fs.getIterableExpr() and
exactScope = false
or
scope = fs.getVariableDeclExpr() and
exactScope = false
)
or
scope =
any(AssignExpr ae |
ae = defTo.(AssignableDefinitions::TupleAssignmentDefinition).getAssignment() and
isAssignExprLValueDescendant(e.(TupleExpr)) and
exactScope = false and
isSuccessor = true
)
or
scope =
any(TupleExpr te |
te.getAnArgument() = defTo.(AssignableDefinitions::LocalVariableDefinition).getDeclaration() and
e = te and
exactScope = false and
isSuccessor = false
)
}
}
private predicate readContentStep(Node node1, Content c, Node node2) {
exists(ReadStepConfiguration x |
hasNodePath(x, node1, node2) and
arrayRead(node1.asExpr(), node2.asExpr()) and
arrayRead(node1.asExpr(), node2.asExpr()) and
c instanceof ElementContent
or
exists(
ForeachStmt fs, Ssa::ExplicitDefinition def,
AssignableDefinitions::LocalVariableDefinition defTo
|
node1.asExpr() = fs.getIterableExpr() and
defTo.getDeclaration() = fs.getVariableDeclExpr() and
def.getADefinition() = defTo and
node2.(SsaDefinitionNode).getDefinition() = def and
c instanceof ElementContent
)
or
node1 =
any(InstanceParameterAccessPreNode n |
n.getUnderlyingControlFlowNode() = node2.(ExprNode).getControlFlowNode() and
n.getParameter() = c.(PrimaryConstructorParameterContent).getParameter()
) and
node2.asExpr() instanceof ParameterRead
or
// node1 = (..., node2, ...)
// node1.ItemX flows to node2
exists(TupleExpr te, int i, Expr item |
te = node1.asExpr() and
not te.isConstruction() and
c.(FieldContent).getField() = te.getType().(TupleType).getElement(i).getUnboundDeclaration() and
// node1 = (..., item, ...)
te.getArgument(i) = item
|
// item = (..., ..., ...) in node1 = (..., (..., ..., ...), ...)
node2.asExpr().(TupleExpr) = item
or
exists(ForeachStmt fs, Ssa::ExplicitDefinition def |
x.hasDefPath(fs.getIterableExpr(), node1.getControlFlowNode(), def.getADefinition(),
def.getControlFlowNode()) and
node2.(SsaDefinitionNode).getDefinition() = def and
c instanceof ElementContent
// item = variable in node1 = (..., variable, ...)
exists(AssignableDefinitions::TupleAssignmentDefinition tad |
node2.(AssignableDefinitionNode).getDefinition() = tad and
tad.getLeaf() = item
)
or
node1 =
any(InstanceParameterAccessPreNode n |
n.getUnderlyingControlFlowNode() = node2.(ExprNode).getControlFlowNode() and
n.getParameter() = c.(PrimaryConstructorParameterContent).getParameter()
) and
node2.asExpr() instanceof ParameterRead
or
// node1 = (..., node2, ...)
// node1.ItemX flows to node2
exists(TupleExpr te, int i, Expr item |
te = node1.asExpr() and
not te.isConstruction() and
c.(FieldContent).getField() = te.getType().(TupleType).getElement(i).getUnboundDeclaration() and
// node1 = (..., item, ...)
te.getArgument(i) = item
|
// item = (..., ..., ...) in node1 = (..., (..., ..., ...), ...)
node2.asExpr().(TupleExpr) = item and
hasNodePath(x, node1, node2)
or
// item = variable in node1 = (..., variable, ...)
exists(AssignableDefinitions::TupleAssignmentDefinition tad |
node2.(AssignableDefinitionNode).getDefinition() = tad and
tad.getLeaf() = item and
hasNodePath(x, node1, node2)
)
or
// item = variable in node1 = (..., variable, ...) in a case/is var (..., ...)
isPatternExprDescendant(te) and
exists(AssignableDefinitions::LocalVariableDefinition lvd |
node2.(AssignableDefinitionNode).getDefinition() = lvd and
lvd.getDeclaration() = item and
hasNodePath(x, node1, node2)
)
// item = variable in node1 = (..., variable, ...) in a case/is var (..., ...)
isPatternExprDescendant(te) and
exists(AssignableDefinitions::LocalVariableDefinition lvd |
node2.(AssignableDefinitionNode).getDefinition() = lvd and
lvd.getDeclaration() = item
)
)
or
@@ -2535,14 +2297,12 @@ predicate readStep(Node node1, ContentSet c, Node node2) {
c.isSingleton(cont)
)
or
exists(ReadStepConfiguration x | hasNodePath(x, node1, node2) |
fieldOrPropertyRead(node1.asExpr(), c, node2.asExpr())
or
dynamicPropertyRead(node1.asExpr(), c, node2.asExpr())
or
node2.asExpr().(AwaitExpr).getExpr() = node1.asExpr() and
c = getResultContent()
)
fieldOrPropertyRead(node1.asExpr(), c, node2.asExpr())
or
dynamicPropertyRead(node1.asExpr(), c, node2.asExpr())
or
node2.asExpr().(AwaitExpr).getExpr() = node1.asExpr() and
c = getResultContent()
or
FlowSummaryImpl::Private::Steps::summaryReadStep(node1.(FlowSummaryNode).getSummaryNode(), c,
node2.(FlowSummaryNode).getSummaryNode())
@@ -2576,9 +2336,9 @@ predicate clearsContent(Node n, ContentSet c) {
c.isSingleton(cont)
)
or
fieldOrPropertyStore(_, c, _, n.asExpr(), true)
fieldOrPropertyStore(c, _, n.asExpr(), true)
or
fieldOrPropertyStore(_, c, _, n.(ObjectInitializerNode).getInitializer(), false)
fieldOrPropertyStore(c, _, n.(ObjectInitializerNode).getInitializer(), false)
or
FlowSummaryImpl::Private::Steps::summaryClearsContent(n.(FlowSummaryNode).getSummaryNode(), c)
or
@@ -2817,8 +2577,13 @@ module PostUpdateNodes {
override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
pos.isQualifier() and
any(ObjectOrCollectionInitializerConfiguration x)
.hasExprPath(_, cfn, _, call.getControlFlowNode())
exists(ObjectOrCollectionInitializer init | init = oc.getInitializer() |
// E.g. `new Dictionary<int, string>{ {0, "a"}, {1, "b"} }`
call.getExpr() = init.(CollectionInitializer).getAnElementInitializer()
or
// E.g. `new Dictionary<int, string>() { [0] = "a", [1] = "b" }`
call.getExpr() = init.(ObjectInitializer).getAMemberInitializer().getLValue()
)
}
override DataFlowCallable getEnclosingCallableImpl() {
@@ -2980,45 +2745,26 @@ private predicate isLocalFunctionCallReceiver(
f = receiver.getTarget().getUnboundDeclaration()
}
private class LambdaConfiguration extends ControlFlowReachabilityConfiguration {
LambdaConfiguration() { this = "LambdaConfiguration" }
override predicate candidate(
Expr e1, Expr e2, ControlFlowElement scope, boolean exactScope, boolean isSuccessor
) {
e1 = e2.(DelegateLikeCall).getExpr() and
exactScope = false and
scope = e2 and
isSuccessor = true
or
e1 = e2.(DelegateCreation).getArgument() and
exactScope = false and
scope = e2 and
isSuccessor = true
or
isLocalFunctionCallReceiver(e2, e1, _) and
exactScope = false and
scope = e2 and
isSuccessor = true
}
}
private predicate lambdaCallExpr(DataFlowCall call, ControlFlow::Node receiver) {
exists(LambdaConfiguration x, DelegateLikeCall dc |
x.hasExprPath(dc.getExpr(), receiver, dc, call.getControlFlowNode())
private predicate lambdaCallExpr(DataFlowCall call, Expr receiver, ControlFlow::Node receiverCfn) {
exists(DelegateLikeCall dc |
call.(ExplicitDelegateLikeDataFlowCall).getCall() = dc and
receiver = dc.getExpr() and
receiverCfn = receiver.getControlFlowNode()
)
or
// In local function calls, `F()`, we use the local function access `F`
// to represent the receiver. Only needed for flow through captured variables.
exists(LambdaConfiguration x, LocalFunctionCall fc |
x.hasExprPath(fc.getAChild(), receiver, fc, call.getControlFlowNode())
exists(LocalFunctionCall fc |
receiver = fc.getAChild() and
receiverCfn = receiver.getControlFlowNode() and
fc.getControlFlowNode() = call.getControlFlowNode()
)
}
/** Holds if `call` is a lambda call where `receiver` is the lambda expression. */
predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) {
(
lambdaCallExpr(call, receiver.(ExprNode).getControlFlowNode()) and
lambdaCallExpr(call, receiver.asExpr(), _) and
// local function calls can be resolved directly without a flow analysis
not call.getControlFlowNode().getAstNode() instanceof LocalFunctionCall
or
@@ -3028,9 +2774,9 @@ predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) {
}
private predicate delegateCreationStep(Node nodeFrom, Node nodeTo) {
exists(LambdaConfiguration x, DelegateCreation dc |
x.hasExprPath(dc.getArgument(), nodeFrom.(ExprNode).getControlFlowNode(), dc,
nodeTo.(ExprNode).getControlFlowNode())
exists(DelegateCreation dc |
dc.getArgument() = nodeFrom.asExpr() and
dc = nodeTo.asExpr()
)
}

View File

@@ -239,7 +239,7 @@ module ModelValidation {
)
}
string getIncorrectConstructorSummaryOutput() {
private string getIncorrectConstructorSummaryOutput() {
exists(string namespace, string type, string name, string output |
type = name or
type = name + "<" + any(string s)

View File

@@ -4,7 +4,6 @@ private import FlowSummaryImpl as FlowSummaryImpl
private import semmle.code.csharp.Caching
private import semmle.code.csharp.dataflow.internal.DataFlowDispatch
private import semmle.code.csharp.dataflow.internal.DataFlowPrivate
private import semmle.code.csharp.dataflow.internal.ControlFlowReachability
private import semmle.code.csharp.dispatch.Dispatch
private import semmle.code.csharp.commons.ComparisonTest
// import `TaintedMember` definitions from other files to avoid potential reevaluation
@@ -45,82 +44,58 @@ predicate defaultImplicitTaintRead(DataFlow::Node node, DataFlow::ContentSet c)
)
}
private class LocalTaintExprStepConfiguration extends ControlFlowReachabilityConfiguration {
LocalTaintExprStepConfiguration() { this = "LocalTaintExprStepConfiguration" }
override predicate candidate(
Expr e1, Expr e2, ControlFlowElement scope, boolean exactScope, boolean isSuccessor
) {
exactScope = false and
isSuccessor = true and
(
e1 = e2.(ElementAccess).getQualifier() and
scope = e2
or
e1 = e2.(AddExpr).getAnOperand() and
scope = e2
or
// A comparison expression where taint can flow from one of the
// operands if the other operand is a constant value.
exists(ComparisonTest ct, Expr other |
ct.getExpr() = e2 and
e1 = ct.getAnArgument() and
other = ct.getAnArgument() and
other.stripCasts().hasValue() and
e1 != other and
scope = e2
)
or
e1 = e2.(UnaryLogicalOperation).getAnOperand() and
scope = e2
or
e1 = e2.(BinaryLogicalOperation).getAnOperand() and
scope = e2
or
e1 = e2.(InterpolatedStringExpr).getAChild() and
scope = e2
or
e1 = e2.(InterpolatedStringInsertExpr).getInsert() and
scope = e2
or
e2 =
any(OperatorCall oc |
oc.getTarget().(ConversionOperator).fromLibrary() and
e1 = oc.getAnArgument() and
scope = e2
)
or
e1 = e2.(AwaitExpr).getExpr() and
scope = e2
or
// Taint flows from the operand of a cast to the cast expression if the cast is to an interpolated string handler.
e2 =
any(CastExpr ce |
e1 = ce.getExpr() and
scope = ce and
ce.getTargetType()
.(Attributable)
.getAnAttribute()
.getType()
.hasFullyQualifiedName("System.Runtime.CompilerServices",
"InterpolatedStringHandlerAttribute")
)
)
}
}
private ControlFlow::Nodes::ExprNode getALastEvalNode(ControlFlow::Nodes::ExprNode cfn) {
exists(OperatorCall oc | any(LocalTaintExprStepConfiguration x).hasExprPath(_, result, oc, cfn) |
oc.getTarget() instanceof ImplicitConversionOperator
private predicate localTaintExprStep(Expr e1, Expr e2) {
e1 = e2.(ElementAccess).getQualifier()
or
e1 = e2.(AddExpr).getAnOperand()
or
// A comparison expression where taint can flow from one of the
// operands if the other operand is a constant value.
exists(ComparisonTest ct, Expr other |
ct.getExpr() = e2 and
e1 = ct.getAnArgument() and
other = ct.getAnArgument() and
other.stripCasts().hasValue() and
e1 != other
)
or
e1 = e2.(UnaryLogicalOperation).getAnOperand()
or
e1 = e2.(BinaryLogicalOperation).getAnOperand()
or
e1 = e2.(InterpolatedStringExpr).getAChild()
or
e1 = e2.(InterpolatedStringInsertExpr).getInsert()
or
e2 =
any(OperatorCall oc |
oc.getTarget().(ConversionOperator).fromLibrary() and
e1 = oc.getAnArgument()
)
or
e1 = e2.(AwaitExpr).getExpr()
or
// Taint flows from the operand of a cast to the cast expression if the cast is to an interpolated string handler.
e2 =
any(CastExpr ce |
e1 = ce.getExpr() and
ce.getTargetType()
.(Attributable)
.getAnAttribute()
.getType()
.hasFullyQualifiedName("System.Runtime.CompilerServices",
"InterpolatedStringHandlerAttribute")
)
}
private ControlFlow::Nodes::ExprNode getPostUpdateReverseStep(ControlFlow::Nodes::ExprNode e) {
result = getALastEvalNode(e)
private Expr getALastEvalNode(OperatorCall oc) {
localTaintExprStep(result, oc) and oc.getTarget() instanceof ImplicitConversionOperator
}
private Expr getPostUpdateReverseStep(Expr e) { result = getALastEvalNode(e) }
private predicate localTaintStepCommon(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
hasNodePath(any(LocalTaintExprStepConfiguration x), nodeFrom, nodeTo)
localTaintExprStep(nodeFrom.asExpr(), nodeTo.asExpr())
}
cached
@@ -191,12 +166,8 @@ private module Cached {
// Allow reverse update flow for implicit conversion operator calls.
// This is needed to support flow out of method call arguments, where an implicit conversion is applied
// to a call argument.
nodeTo.(PostUpdateNode).getPreUpdateNode().(DataFlow::ExprNode).getControlFlowNode() =
getPostUpdateReverseStep(nodeFrom
.(PostUpdateNode)
.getPreUpdateNode()
.(DataFlow::ExprNode)
.getControlFlowNode())
nodeTo.(PostUpdateNode).getPreUpdateNode().asExpr() =
getPostUpdateReverseStep(nodeFrom.(PostUpdateNode).getPreUpdateNode().asExpr())
) and
model = ""
or

View File

@@ -1,246 +0,0 @@
import csharp
private class ControlFlowScope extends ControlFlowElement {
private boolean exactScope;
ControlFlowScope() {
exists(ControlFlowReachabilityConfiguration c |
c.candidate(_, _, this, exactScope, _) or
c.candidateDef(_, _, this, exactScope, _)
)
}
predicate isExact() { exactScope = true }
predicate isNonExact() { exactScope = false }
}
private newtype TControlFlowElementOrBasicBlock =
TControlFlowElement(ControlFlowElement cfe) or
TBasicBlock(ControlFlow::BasicBlock bb)
class ControlFlowElementOrBasicBlock extends TControlFlowElementOrBasicBlock {
ControlFlowElement asControlFlowElement() { this = TControlFlowElement(result) }
ControlFlow::BasicBlock asBasicBlock() { this = TBasicBlock(result) }
string toString() {
result = this.asControlFlowElement().toString()
or
result = this.asBasicBlock().toString()
}
Location getLocation() {
result = this.asControlFlowElement().getLocation()
or
result = this.asBasicBlock().getLocation()
}
}
private predicate isBasicBlock(ControlFlowElementOrBasicBlock c) { c instanceof TBasicBlock }
private predicate isNonExactScope(ControlFlowElementOrBasicBlock c) {
c.asControlFlowElement().(ControlFlowScope).isNonExact()
}
private predicate step(ControlFlowElementOrBasicBlock pred, ControlFlowElementOrBasicBlock succ) {
pred.asBasicBlock().getANode().getAstNode() = succ.asControlFlowElement()
or
pred.asControlFlowElement() = succ.asControlFlowElement().getAChild()
}
private predicate basicBlockInNonExactScope(
ControlFlowElementOrBasicBlock bb, ControlFlowElementOrBasicBlock scope
) = doublyBoundedFastTC(step/2, isBasicBlock/1, isNonExactScope/1)(bb, scope)
pragma[noinline]
private ControlFlow::BasicBlock getABasicBlockInScope(ControlFlowScope scope, boolean exactScope) {
basicBlockInNonExactScope(TBasicBlock(result), TControlFlowElement(scope)) and
exactScope = false
or
scope.isExact() and
result.getANode().getAstNode() = scope and
exactScope = true
}
/**
* A helper class for determining control-flow reachability for pairs of
* elements.
*
* This is useful when defining for example expression-based data-flow steps in
* the presence of control-flow splitting, where a data-flow step should make
* sure to stay in the same split.
*
* For example, in
*
* ```csharp
* if (b)
* ....
* var x = "foo";
* if (b)
* ....
* ```
*
* there should only be steps from `[b = true] "foo"` to `[b = true] SSA def(x)`
* and `[b = false] "foo"` to `[b = false] SSA def(x)`, and for example not from
* `[b = true] "foo"` to `[b = false] SSA def(x)`
*/
abstract class ControlFlowReachabilityConfiguration extends string {
bindingset[this]
ControlFlowReachabilityConfiguration() { any() }
/**
* Holds if `e1` and `e2` are expressions for which we want to find a
* control-flow path that follows control flow successors (resp.
* predecessors, as specified by `isSuccessor`) inside the syntactic scope
* `scope`. The Boolean `exactScope` indicates whether a transitive child
* of `scope` is allowed (`exactScope = false`).
*/
predicate candidate(
Expr e1, Expr e2, ControlFlowElement scope, boolean exactScope, boolean isSuccessor
) {
none()
}
/**
* Holds if `e` and `def` are elements for which we want to find a
* control-flow path that follows control flow successors (resp.
* predecessors, as specified by `isSuccessor`) inside the syntactic scope
* `scope`. The Boolean `exactScope` indicates whether a transitive child
* of `scope` is allowed (`exactScope = false`).
*/
predicate candidateDef(
Expr e, AssignableDefinition def, ControlFlowElement scope, boolean exactScope,
boolean isSuccessor
) {
none()
}
pragma[nomagic]
private predicate reachesBasicBlockExprBase(
Expr e1, Expr e2, boolean isSuccessor, ControlFlow::Nodes::ElementNode cfn1, int i,
ControlFlow::BasicBlock bb
) {
this.candidate(e1, e2, _, _, isSuccessor) and
cfn1 = e1.getAControlFlowNode() and
bb.getNode(i) = cfn1
}
pragma[nomagic]
private predicate reachesBasicBlockExprRec(
Expr e1, Expr e2, boolean isSuccessor, ControlFlow::Nodes::ElementNode cfn1,
ControlFlow::BasicBlock bb
) {
exists(ControlFlow::BasicBlock mid |
this.reachesBasicBlockExpr(e1, e2, isSuccessor, cfn1, mid)
|
isSuccessor = true and
bb = mid.getASuccessor()
or
isSuccessor = false and
bb = mid.getAPredecessor()
)
}
pragma[nomagic]
private predicate reachesBasicBlockExpr(
Expr e1, Expr e2, boolean isSuccessor, ControlFlow::Nodes::ElementNode cfn1,
ControlFlow::BasicBlock bb
) {
this.reachesBasicBlockExprBase(e1, e2, isSuccessor, cfn1, _, bb)
or
exists(ControlFlowElement scope, boolean exactScope |
this.candidate(e1, e2, scope, exactScope, isSuccessor) and
this.reachesBasicBlockExprRec(e1, e2, isSuccessor, cfn1, bb) and
bb = getABasicBlockInScope(scope, exactScope)
)
}
pragma[nomagic]
private predicate reachesBasicBlockDefinitionBase(
Expr e, AssignableDefinition def, boolean isSuccessor, ControlFlow::Nodes::ElementNode cfn,
int i, ControlFlow::BasicBlock bb
) {
this.candidateDef(e, def, _, _, isSuccessor) and
cfn = e.getAControlFlowNode() and
bb.getNode(i) = cfn
}
pragma[nomagic]
private predicate reachesBasicBlockDefinitionRec(
Expr e, AssignableDefinition def, boolean isSuccessor, ControlFlow::Nodes::ElementNode cfn,
ControlFlow::BasicBlock bb
) {
exists(ControlFlow::BasicBlock mid |
this.reachesBasicBlockDefinition(e, def, isSuccessor, cfn, mid)
|
isSuccessor = true and
bb = mid.getASuccessor()
or
isSuccessor = false and
bb = mid.getAPredecessor()
)
}
pragma[nomagic]
private predicate reachesBasicBlockDefinition(
Expr e, AssignableDefinition def, boolean isSuccessor, ControlFlow::Nodes::ElementNode cfn,
ControlFlow::BasicBlock bb
) {
this.reachesBasicBlockDefinitionBase(e, def, isSuccessor, cfn, _, bb)
or
exists(ControlFlowElement scope, boolean exactScope |
this.candidateDef(e, def, scope, exactScope, isSuccessor) and
this.reachesBasicBlockDefinitionRec(e, def, isSuccessor, cfn, bb) and
bb = getABasicBlockInScope(scope, exactScope)
)
}
/**
* Holds if there is a control-flow path from `cfn1` to `cfn2`, where `cfn1` is a
* control-flow node for `e1` and `cfn2` is a control-flow node for `e2`.
*/
pragma[nomagic]
predicate hasExprPath(Expr e1, ControlFlow::Node cfn1, Expr e2, ControlFlow::Node cfn2) {
exists(ControlFlow::BasicBlock bb, boolean isSuccessor, int i, int j |
this.reachesBasicBlockExprBase(e1, e2, isSuccessor, cfn1, i, bb) and
cfn2 = bb.getNode(j) and
cfn2 = e2.getAControlFlowNode()
|
isSuccessor = true and j >= i
or
isSuccessor = false and i >= j
)
or
exists(ControlFlow::BasicBlock bb |
this.reachesBasicBlockExprRec(e1, e2, _, cfn1, bb) and
cfn2 = bb.getANode() and
cfn2 = e2.getAControlFlowNode()
)
}
/**
* Holds if there is a control-flow path from `cfn` to `cfnDef`, where `cfn` is a
* control-flow node for `e` and `cfnDef` is a control-flow node for `def`.
*/
pragma[nomagic]
predicate hasDefPath(
Expr e, ControlFlow::Node cfn, AssignableDefinition def, ControlFlow::Node cfnDef
) {
exists(ControlFlow::BasicBlock bb, boolean isSuccessor, int i, int j |
this.reachesBasicBlockDefinitionBase(e, def, isSuccessor, cfn, i, bb) and
cfnDef = bb.getNode(j) and
def.getExpr().getAControlFlowNode() = cfnDef
|
isSuccessor = true and j >= i
or
isSuccessor = false and i >= j
)
or
exists(ControlFlow::BasicBlock bb |
this.reachesBasicBlockDefinitionRec(e, def, _, cfn, bb) and
def.getExpr().getAControlFlowNode() = cfnDef and
cfnDef = bb.getANode()
)
}
}

View File

@@ -8,26 +8,14 @@ private module Impl {
private import ConstantUtils
private import SsaReadPositionCommon
private import semmle.code.csharp.controlflow.Guards as G
private import ControlFlowReachability
private class ExprNode = ControlFlow::Nodes::ExprNode;
private class ExprChildReachability extends ControlFlowReachabilityConfiguration {
ExprChildReachability() { this = "ExprChildReachability" }
override predicate candidate(
Expr e1, Expr e2, ControlFlowElement scope, boolean exactScope, boolean isSuccessor
) {
e2 = e1.getAChild() and
scope = e1 and
exactScope = false and
isSuccessor in [false, true]
}
}
/** Holds if `parent` having child `child` implies `parentNode` having child `childNode`. */
predicate hasChild(Expr parent, Expr child, ExprNode parentNode, ExprNode childNode) {
any(ExprChildReachability x).hasExprPath(parent, parentNode, child, childNode)
parent.getAChild() = child and
parentNode = parent.getControlFlowNode() and
childNode = child.getControlFlowNode()
}
/** Holds if SSA definition `def` equals `e + delta`. */

View File

@@ -1,3 +1,7 @@
## 1.6.4
No user-facing changes.
## 1.6.3
No user-facing changes.

View File

@@ -0,0 +1,3 @@
## 1.6.4
No user-facing changes.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 1.6.3
lastReleaseVersion: 1.6.4

View File

@@ -1,5 +1,5 @@
name: codeql/csharp-queries
version: 1.6.4-dev
version: 1.6.4
groups:
- csharp
- queries

View File

@@ -26,6 +26,7 @@
| CSharp7.cs:31:16:31:16 | access to parameter i | CSharp7.cs:31:16:31:20 | ... > ... |
| CSharp7.cs:31:16:31:16 | access to parameter i | CSharp7.cs:31:24:31:24 | access to parameter i |
| CSharp7.cs:31:24:31:24 | access to parameter i | CSharp7.cs:31:16:31:59 | ... ? ... : ... |
| CSharp7.cs:31:28:31:59 | throw ... | CSharp7.cs:31:16:31:59 | ... ? ... : ... |
| CSharp7.cs:35:7:35:18 | this | CSharp7.cs:35:7:35:18 | this access |
| CSharp7.cs:39:9:39:9 | access to parameter x | CSharp7.cs:39:9:39:21 | SSA def(x) |
| CSharp7.cs:39:13:39:21 | "tainted" | CSharp7.cs:39:9:39:9 | access to parameter x |
@@ -253,6 +254,7 @@
| CSharp7.cs:233:13:233:13 | access to local variable o | CSharp7.cs:235:13:235:42 | [input] SSA phi read(o) |
| CSharp7.cs:233:13:233:13 | access to local variable o | CSharp7.cs:237:18:237:18 | access to local variable o |
| CSharp7.cs:233:13:233:23 | [false] ... is ... | CSharp7.cs:233:13:233:33 | [false] ... && ... |
| CSharp7.cs:233:13:233:23 | [false] ... is ... | CSharp7.cs:233:13:233:33 | [true] ... && ... |
| CSharp7.cs:233:13:233:23 | [true] ... is ... | CSharp7.cs:233:13:233:33 | [false] ... && ... |
| CSharp7.cs:233:13:233:23 | [true] ... is ... | CSharp7.cs:233:13:233:33 | [true] ... && ... |
| CSharp7.cs:233:18:233:23 | Int32 i1 | CSharp7.cs:233:18:233:23 | SSA def(i1) |
@@ -338,6 +340,8 @@
| CSharp7.cs:297:35:297:35 | access to local variable x | CSharp7.cs:297:40:297:44 | Int32 y |
| CSharp7.cs:297:35:297:35 | access to local variable x | CSharp7.cs:297:49:297:49 | access to local variable x |
| CSharp7.cs:297:35:297:44 | [false] ... is ... | CSharp7.cs:297:25:297:44 | [false] ... && ... |
| CSharp7.cs:297:35:297:44 | [false] ... is ... | CSharp7.cs:297:25:297:44 | [true] ... && ... |
| CSharp7.cs:297:35:297:44 | [true] ... is ... | CSharp7.cs:297:25:297:44 | [false] ... && ... |
| CSharp7.cs:297:35:297:44 | [true] ... is ... | CSharp7.cs:297:25:297:44 | [true] ... && ... |
| CSharp7.cs:297:40:297:44 | Int32 y | CSharp7.cs:297:40:297:44 | SSA def(y) |
| CSharp7.cs:297:40:297:44 | SSA def(y) | CSharp7.cs:299:31:299:31 | access to local variable y |

View File

@@ -406,7 +406,7 @@ Adds a new taint source. Most taint-tracking queries will use the new source.
- **type**: Name of a type from which to evaluate **path**.
- **path**: Access path leading to the source.
- **kind**: Kind of source to add. Currently only **remote** is used.
- **kind**: Kind of source to add. See the section on source kinds for a list of supported kinds.
Example:
@@ -553,7 +553,16 @@ Kinds
Source kinds
~~~~~~~~~~~~
See documentation below for :ref:`Threat models <threat-models-javascript>`.
- **remote**: A general source of remote flow.
- **browser**: A source in the browser environment that does not fit a more specific browser kind.
- **browser-url-query**: A source derived from the query parameters of the browser URL, such as ``location.search``.
- **browser-url-fragment**: A source derived from the fragment part of the browser URL, such as ``location.hash``.
- **browser-url-path**: A source derived from the pathname of the browser URL, such as ``location.pathname``.
- **browser-url**: A source derived from the browser URL, where the untrusted part is prefixed by trusted data such as the scheme and hostname.
- **browser-window-name**: A source derived from the window name, such as ``window.name``.
- **browser-message-event**: A source derived from cross-window message passing, such as ``event`` in ``window.onmessage = event => {...}``.
See also :ref:`Threat models <threat-models-javascript>`.
Sink kinds
~~~~~~~~~~

View File

@@ -9,8 +9,8 @@ toolchain go1.26.0
// when adding or removing dependencies, run
// bazel mod tidy
require (
golang.org/x/mod v0.33.0
golang.org/x/tools v0.42.0
golang.org/x/mod v0.34.0
golang.org/x/tools v0.43.0
)
require github.com/stretchr/testify v1.11.1
@@ -18,6 +18,6 @@ require github.com/stretchr/testify v1.11.1
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/sync v0.19.0 // indirect
golang.org/x/sync v0.20.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

View File

@@ -6,12 +6,12 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8=
golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w=
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k=
golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0=
golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI=
golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY=
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s=
golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

View File

@@ -1,3 +1,7 @@
## 1.0.44
No user-facing changes.
## 1.0.43
No user-facing changes.

View File

@@ -0,0 +1,3 @@
## 1.0.44
No user-facing changes.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 1.0.43
lastReleaseVersion: 1.0.44

View File

@@ -1,5 +1,5 @@
name: codeql-go-consistency-queries
version: 1.0.44-dev
version: 1.0.44
groups:
- go
- queries

View File

@@ -1,3 +1,9 @@
## 7.0.2
### Minor Analysis Improvements
* Inline expectations test comments, which are of the form `// $ tag` or `// $ tag=value`, are now parsed more strictly and will not be recognized if there isn't a space after the `$` symbol.
## 7.0.1
No user-facing changes.

View File

@@ -8,5 +8,7 @@
* `FileSystemAccess`, or the `Source` and `Sink` classes associated with the security queries
* to model frameworks that are not covered by the standard library.
*/
overlay[local?]
module;
import go

View File

@@ -1,4 +1,5 @@
---
category: minorAnalysis
---
## 7.0.2
### Minor Analysis Improvements
* Inline expectations test comments, which are of the form `// $ tag` or `// $ tag=value`, are now parsed more strictly and will not be recognized if there isn't a space after the `$` symbol.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 7.0.1
lastReleaseVersion: 7.0.2

View File

@@ -1,6 +1,8 @@
/**
* Provides classes for working with Go programs.
*/
overlay[local?]
module;
import Customizations
import semmle.go.Architectures

View File

@@ -2,6 +2,8 @@
* Provides classes and predicates related to contextual queries
* in the code viewer.
*/
overlay[local?]
module;
import go
private import codeql.util.FileSystem

View File

@@ -1,5 +1,5 @@
name: codeql/go-all
version: 7.0.2-dev
version: 7.0.2
groups: go
dbscheme: go.dbscheme
extractor: go

View File

@@ -1,6 +1,8 @@
/**
* Provides classes for working with AST nodes.
*/
overlay[local]
module;
import go

View File

@@ -1,4 +1,6 @@
/** Provides classes for working with architectures. */
overlay[local]
module;
import go

View File

@@ -1,6 +1,8 @@
/**
* Provides classes for working with code comments.
*/
overlay[local]
module;
import go

View File

@@ -3,6 +3,8 @@
* access or system command execution, for which individual framework libraries
* provide concrete subclasses.
*/
overlay[local?]
module;
import go
import semmle.go.dataflow.FunctionInputsAndOutputs

View File

@@ -1,6 +1,8 @@
/**
* Provides classes for working with declarations.
*/
overlay[local]
module;
import go

View File

@@ -1,4 +1,6 @@
/** Provides classes for working with errors and warnings recorded during extraction. */
overlay[local]
module;
import go

View File

@@ -1,4 +1,6 @@
/** Provides classes for working with Go frontend errors recorded during extraction. */
overlay[local]
module;
import go

View File

@@ -1,6 +1,8 @@
/**
* Provides classes for working with expressions.
*/
overlay[local]
module;
import go
@@ -829,7 +831,7 @@ class ConversionExpr extends CallOrConversionExpr {
/**
* A function call expression.
*
* On snapshots with incomplete type information, type conversions may be misclassified
* On databases with incomplete type information, type conversions may be misclassified
* as function call expressions.
*
* Examples:
@@ -2035,6 +2037,9 @@ class ConstantName extends ValueName {
override string getAPrimaryQlClass() { result = "ConstantName" }
}
/** Holds if `e` is an expression that refers to the `nil` constant. */
predicate exprRefersToNil(Expr e) { e.(ConstantName).getTarget() = Builtin::nil() }
/**
* A name referring to a variable.
*
@@ -2093,7 +2098,7 @@ class LabelName extends Name {
* Holds if `e` is a type expression, as determined by a bottom-up syntactic
* analysis starting with `TypeName`s.
*
* On a snapshot with full type information, this predicate covers all type
* On a database with full type information, this predicate covers all type
* expressions. However, if type information is missing then not all type names
* may be identified as such, so not all type expressions can be determined by
* a bottom-up analysis. In such cases, `isTypeExprTopDown` below is useful.
@@ -2131,11 +2136,12 @@ private predicate isTypeExprBottomUp(Expr e) {
* Holds if `e` must be a type expression because it either occurs in a syntactic
* position where a type is expected, or it is part of a larger type expression.
*
* This predicate is only needed on snapshots for which type information is
* incomplete. It is an underapproximation; in cases where it is syntactically ambiguous
* whether an expression refers to a type or a value, we conservatively assume that
* it may be the latter and so this predicate does not consider the expression to be
* a type expression.
* This predicate is only needed on databases for which type information is
* incomplete - for example, when some dependencies could not be reached during
* extraction. It is an underapproximation; in cases where it is syntactically
* ambiguous whether an expression refers to a type or a value, we conservatively
* assume that it may be the latter and so this predicate does not consider the
* expression to be a type expression.
*/
pragma[nomagic]
private predicate isTypeExprTopDown(Expr e) {
@@ -2145,20 +2151,12 @@ private predicate isTypeExprTopDown(Expr e) {
or
e = any(ArrayTypeExpr ae).getElement()
or
e = any(FieldDecl f).getTypeExpr()
or
e = any(ParameterDecl pd).getTypeExpr()
e = any(FieldBase fb).getTypeExpr()
or
e = any(TypeParamDecl tpd).getTypeConstraintExpr()
or
e = any(TypeParamDecl tpd).getNameExpr(_)
or
e = any(ReceiverDecl rd).getTypeExpr()
or
e = any(ResultVariableDecl rvd).getTypeExpr()
or
e = any(MethodSpec md).getTypeExpr()
or
e = any(MapTypeExpr mt).getKeyTypeExpr()
or
e = any(MapTypeExpr mt).getValueTypeExpr()
@@ -2175,7 +2173,7 @@ private predicate isTypeExprTopDown(Expr e) {
or
e = any(TypeSwitchStmt s).getACase().getExpr(_) and
// special case: `nil` is allowed in a type case but isn't a type
not e = Builtin::nil().getAReference()
not exprRefersToNil(e)
or
e = any(SelectorExpr sel | isTypeExprTopDown(sel)).getBase()
or

View File

@@ -1,4 +1,6 @@
/** Provides classes for working with files and folders. */
overlay[local]
module;
import go
private import codeql.util.FileSystem

View File

@@ -1,6 +1,8 @@
/**
* Provides classes for working with go.mod files.
*/
overlay[local]
module;
import go

View File

@@ -1,4 +1,6 @@
/** Provides classes for working with HTML documents. */
overlay[local]
module;
import go

View File

@@ -1,4 +1,6 @@
/** Provides classes for working with locations and program elements that have locations. */
overlay[local]
module;
import go
private import semmle.go.Overlay

View File

@@ -1,6 +1,8 @@
/**
* Provides classes for working with packages.
*/
overlay[local]
module;
import go

View File

@@ -1,6 +1,8 @@
/**
* Provides queries to pretty-print a Go AST as a graph.
*/
overlay[local]
module;
import go

View File

@@ -1,6 +1,8 @@
/**
* Provides classes for working with scopes and declared objects.
*/
overlay[local]
module;
import go

View File

@@ -1,6 +1,8 @@
/**
* Provides classes for working with statements.
*/
overlay[local]
module;
import go

View File

@@ -1,6 +1,8 @@
/**
* Provides predicates and classes for working with string operations.
*/
overlay[local?]
module;
import go

View File

@@ -1,6 +1,8 @@
/**
* Provides classes for working with Go types.
*/
overlay[local]
module;
import go

View File

@@ -1,4 +1,6 @@
/** This module provides general utility classes and predicates. */
overlay[local]
module;
/**
* A Boolean value.

View File

@@ -1,4 +1,6 @@
/** Provides the `VariableWithFields` class, for working with variables with a chain of field or element accesses chained to it. */
overlay[local]
module;
import go

View File

@@ -1,4 +1,6 @@
/** Provides a class for generated files. */
overlay[local]
module;
import go

View File

@@ -1,6 +1,8 @@
/**
* Provides classes for working with HTTP-related concepts such as requests and responses.
*/
overlay[local?]
module;
import go

View File

@@ -1,6 +1,8 @@
/**
* Provides classes for working with basic blocks.
*/
overlay[local]
module;
import go
private import ControlFlowGraphImpl

View File

@@ -1,6 +1,8 @@
/**
* Provides classes for working with a CFG-based program representation.
*/
overlay[local]
module;
import go
private import ControlFlowGraphImpl
@@ -62,6 +64,7 @@ module ControlFlow {
BasicBlock getBasicBlock() { result.getANode() = this }
/** Holds if this node dominates `dominee` in the control-flow graph. */
overlay[caller?]
pragma[inline]
predicate dominatesNode(ControlFlow::Node dominee) {
exists(ReachableBasicBlock thisbb, ReachableBasicBlock dbb, int i, int j |

View File

@@ -3,6 +3,8 @@
*
* Provides predicates for building intra-procedural CFGs.
*/
overlay[local]
module;
import go

View File

@@ -9,6 +9,8 @@
* Each instruction is also a control-flow node, but there are control-flow nodes that are not
* instructions (synthetic entry and exit nodes, as well as no-op skip nodes).
*/
overlay[local]
module;
import go
private import semmle.go.controlflow.ControlFlowGraphImpl
@@ -294,7 +296,7 @@ module IR {
/**
* An IR instruction that reads the value of a field.
*
* On snapshots with incomplete type information, method expressions may sometimes be
* On databases with incomplete type information, method expressions may sometimes be
* misclassified as field reads.
*/
class FieldReadInstruction extends ComponentReadInstruction {

View File

@@ -14,6 +14,8 @@
* (intraprocedural) data flow, invoke `DataFlow::localFlow` or
* `DataFlow::LocalFlowStep` with arguments of type `DataFlow::Node`.
*/
overlay[local?]
module;
import go

View File

@@ -84,6 +84,8 @@
* "taint" indicates a default additional taint step and "value" indicates a
* globally applicable value-preserving step.
*/
overlay[local?]
module;
private import go
private import internal.ExternalFlowExtensions::Extensions as Extensions

View File

@@ -1,6 +1,8 @@
/**
* Provides classes and predicates for defining flow summaries.
*/
overlay[local?]
module;
import go
private import internal.FlowSummaryImpl as Impl

View File

@@ -2,6 +2,8 @@
* Provides QL classes for indicating data flow through a function parameter, return value,
* or receiver.
*/
overlay[local]
module;
import go
private import semmle.go.dataflow.internal.DataFlowPrivate

View File

@@ -29,6 +29,8 @@
* common reason for this is that the analysis cannot prove that there
* are no side-effects that might cause the computed value to change.
*/
overlay[local]
module;
/*
* Note to developers: the correctness of this module depends on the

View File

@@ -1,6 +1,8 @@
/**
* Provides a class for representing and reasoning about properties of data-flow nodes.
*/
overlay[local]
module;
import go
@@ -22,7 +24,7 @@ class Property extends TProperty {
isTrue = eq.getPolarity().booleanXor(e.getBoolValue().booleanXor(outcome))
or
this = IsNil(isTrue) and
e = Builtin::nil().getAReference() and
exprRefersToNil(e) and
isTrue = eq.getPolarity().booleanXor(outcome).booleanNot()
)
or

Some files were not shown because too many files have changed in this diff Show More