Compare commits

..

388 Commits

Author SHA1 Message Date
Mariusz Kliber
005434d2f6 Merge branch 'fossjunkie/internal-ci-checks' into fossjunkie-patch-1 2022-11-30 17:56:19 +00:00
Mariusz Kliber
c56609b290 Debug: testing original API call 2022-11-30 17:55:03 +00:00
Mariusz Kliber
802fc8c9d0 Debug: removed grep 2022-11-30 17:48:37 +00:00
Mariusz Kliber
596cb72e07 Debug: testing API calls 2022-11-30 17:48:37 +00:00
Mariusz Kliber
fd64b87c45 Debug: dump user scope 2022-11-30 17:48:37 +00:00
Mariusz Kliber
337b85a2d3 Apply suggestions from aibaars
Co-authored-by: Arthur Baars <aibaars@github.com>
2022-11-30 17:48:37 +00:00
Mariusz Kliber
d2a80ce184 CI: Add Internal CI Checks workflow 2022-11-30 17:48:37 +00:00
Mariusz Kliber
143034bd88 Merge branch 'fossjunkie/internal-ci-checks' into fossjunkie-patch-1 2022-11-30 17:33:56 +00:00
Mariusz Kliber
b0e2405ba4 Debug: remove id-token 2022-11-30 17:31:58 +00:00
Mariusz Kliber
835e65f8a9 Merge branch 'fossjunkie/internal-ci-checks' into fossjunkie-patch-1 2022-11-30 17:29:31 +00:00
Mariusz Kliber
e78d066d88 Debug: Add max permissions 2022-11-30 17:29:06 +00:00
Mariusz Kliber
35b63b58b2 Merge branch 'fossjunkie/internal-ci-checks' into fossjunkie-patch-1 2022-11-30 17:05:06 +00:00
Mariusz Kliber
b5fe06cb2f Debug: removed grep 2022-11-30 17:04:25 +00:00
Arthur Baars
4420d6c642 Update README.md 2022-11-30 18:02:11 +01:00
Mariusz Kliber
e9311fcee4 Merge branch 'fossjunkie/internal-ci-checks' into fossjunkie-patch-1 2022-11-30 16:59:49 +00:00
Mariusz Kliber
67dba54bbd Debug: testing API calls 2022-11-30 16:59:31 +00:00
Mariusz Kliber
7f733c40fe Merge branch 'fossjunkie/internal-ci-checks' into fossjunkie-patch-1 2022-11-30 16:56:13 +00:00
Mariusz Kliber
17f0c44be8 Debug: dump user scope 2022-11-30 16:55:06 +00:00
Mariusz Kliber
408d128c34 Update README.md 2022-11-30 16:51:02 +00:00
Mariusz Kliber
aabdf0676f Apply suggestions from aibaars
Co-authored-by: Arthur Baars <aibaars@github.com>
2022-11-30 16:31:59 +00:00
Mariusz Kliber
b4d62ee4be CI: Add Internal CI Checks workflow 2022-11-30 16:31:59 +00:00
Tony Torralba
6ae10c5171 Merge pull request #11501 from atorralba/atorralba/swift/fix-data-test-expectations
Swift: Fix expectations in data.swift
2022-11-30 17:12:32 +01:00
Tony Torralba
d958a62bf2 Fix expectations in data.swift 2022-11-30 16:19:55 +01:00
Karim Ali
8f456295e0 Merge pull request #11185 from karimhamdanali/swift-string-taint-steps
Swift: add `String` taint steps
2022-11-30 17:03:15 +02:00
Karim Ali
f6bc88471a update the expected output for CWE-079
Now that we have support for taint through fields of String, we can now detect certain flows that we previously marked as [NOT DETECTED]. This commit updates the expected output of CWE-079 (and the in-code annotation of the accompanying test case) to reflect that update.
2022-11-30 16:34:24 +02:00
Paolo Tranquilli
b8c11de89c Merge pull request #11498 from github/redsun82/swift-codegen
Swift: enhance `codegen` UX
2022-11-30 15:17:18 +01:00
Owen Mansel-Chan
45e2a13c37 Merge pull request #11494 from owen-mc/dataflow/some-trivial-fixes
Dataflow: some trivial fixes
2022-11-30 13:59:41 +00:00
Anders Schack-Mulligen
1ce28540fb Merge pull request #11495 from intrigus-lgtm/patch-7
Docs: Add missing `language[monotonicAggregates]` annotation
2022-11-30 14:58:44 +01:00
Paolo Tranquilli
b4e3554af7 Merge pull request #11370 from github/alexdenisov/swift-5.7.1
Swift: upgrade to Swift 5.7.1
2022-11-30 14:23:12 +01:00
Paolo Tranquilli
bb3aa9e908 Swift: add --force to codegen 2022-11-30 14:19:33 +01:00
Paolo Tranquilli
d6aad13a98 Swift: make codegen run when no registry is there 2022-11-30 13:47:12 +01:00
Paolo Tranquilli
76db5f22b3 Swift: make codegen resilient to formatting errors
More in general, the managed renderer flow does things more sensibly
in case an exception is thrown:
* it will not remove any file
* it will drop already written files from the registry, so that codegen
  won't be skipped for those files during the next run
2022-11-30 13:43:29 +01:00
intrigus-lgtm
4e7e70f981 Docs: Add missing language[monotonicAggregates] annotation
This adds the `language[monotonicAggregates]` annotation so that the example compiles.
2022-11-30 13:12:06 +01:00
Alex Denisov
fe0ae6bf0b Swift: add 5.7.1 migration scripts 2022-11-30 12:52:26 +01:00
Alex Denisov
ad663533c7 Swift: bump setup Swift action 2022-11-30 12:40:42 +01:00
Alex Denisov
67fb56deb8 Swift: workaround an internal crash coming from Swift 5.7.1 2022-11-30 12:40:42 +01:00
Alex Denisov
f618d53302 Swift: add new implicit conversion 2022-11-30 12:40:34 +01:00
Alex Denisov
90d471b486 Swift: upgrade to Swift 5.7.1 2022-11-30 12:40:27 +01:00
Mathias Vorreiter Pedersen
d53d275bba Merge pull request #11450 from d10c/swift/missing-enum-cases
Swift: extract missing cases of `AccessorKind` and `AccessSemantics` enums
2022-11-30 11:18:52 +00:00
Owen Mansel-Chan
75940dc8b1 Remove @codeql-go from code owners for dataflow 2022-11-30 11:16:53 +00:00
Owen Mansel-Chan
635c202ced Use ArgumentPosition instead of int
This matches what all of the other languages do.
2022-11-30 11:16:52 +00:00
Owen Mansel-Chan
55c4643b20 Dataflow: Sync. 2022-11-30 11:00:07 +00:00
Anders Schack-Mulligen
3d04b267ef Merge pull request #11492 from aschackmull/shared/util
Shared: Add Util qlpack.
2022-11-30 11:56:34 +01:00
Paolo Tranquilli
2f4cf592a7 Merge pull request #11490 from github/redsun82/cache
CI: add workaround for nested composite actions issue
2022-11-30 11:55:46 +01:00
Owen Mansel-Chan
ce8a20cfd1 Fix variable name (source should be sink) 2022-11-30 10:51:59 +00:00
Anders Schack-Mulligen
758cb8b412 Shared: Fix trailing and non-ascii whitespace. 2022-11-30 11:14:43 +01:00
Owen Mansel-Chan
ab276fc5d8 Merge pull request #11481 from owen-mc/dataflow/sync-go-libraries-2
Sync go libraries
2022-11-30 10:02:43 +00:00
Anders Schack-Mulligen
ba56565125 Update shared/util/codeql/util/Option.qll
Co-authored-by: Erik Krogh Kristensen <erik-krogh@github.com>
2022-11-30 10:55:07 +01:00
Paolo Tranquilli
e12e86b520 Restore previous cache key
Co-authored-by: Erik Krogh Kristensen <erik-krogh@github.com>
2022-11-30 10:47:29 +01:00
Paolo Tranquilli
d165c4963d CI: add workaround for nested composite actions issue
Because of https://github.com/actions/runner/issues/2009 the deeply
nested action cache was failing to save the cache in the post run phase.

For the moment we just avoid the nesting with a copy-pasted action
snippet.
2022-11-30 10:47:29 +01:00
Paolo Tranquilli
22eb619235 Merge pull request #11467 from erik-krogh/test-ci
CI: fix moving the compilation cache
2022-11-30 10:47:07 +01:00
Anders Schack-Mulligen
71f5c8aa88 Shared: Add Util qlpack. 2022-11-30 10:43:33 +01:00
erik-krogh
045e6ef148 remove unused environment variable 2022-11-30 09:59:20 +01:00
erik-krogh
3d0c9c4642 Merge branch 'main' into test-ci 2022-11-30 09:47:08 +01:00
Tony Torralba
594b7efb84 Merge pull request #11485 from github/workflow/coverage/update
Update CSV framework coverage reports
2022-11-30 09:16:05 +01:00
Paolo Tranquilli
da5730706d Merge pull request #11483 from github/redsun82/cache
Swift: tentative fix for the bazel cache
2022-11-30 08:25:39 +01:00
Tiferet Gazit
e2e3667698 Merge pull request #11323 from github/tiferet/simplify-configs
ATM: Simplify query configurations
2022-11-29 17:39:11 -08:00
github-actions[bot]
13f4a0e284 Add changed framework coverage reports 2022-11-30 00:18:26 +00:00
tiferet
c5184d37e7 Suggestion from code review:
Name the query configuration e.g. `NosqlInjectionATMConfig` rather than `Configuration`.
2022-11-29 15:46:05 -08:00
Harry Maclean
375403fb9d Merge pull request #11114 from hmac/case-barrier-guard-3
Ruby: Add case string comparison barrier guard
2022-11-30 11:21:07 +13:00
tiferet
6f807e9d43 Doc suggestion from code review 2022-11-29 13:20:47 -08:00
tiferet
75cd7a9ebc Remove code duplication in query .ql files:
Define the query for finding ATM alerts in the base class `AtmConfig`, and call it from each query's .ql file.
2022-11-29 13:20:47 -08:00
tiferet
a710b723d1 Move the definition of isSink to the base class:
Holds if `sink` is a known taint sink or an "effective" sink.
2022-11-29 13:20:47 -08:00
tiferet
cd24ec88d6 Move the definition of isSource to the base class:
A long as we're not boosting sources, `isSource` is identical to `isKnownSource`.
2022-11-29 13:20:47 -08:00
tiferet
50291c7b7c AtmConfig inherits from TaintTracking::Configuration.
That way the specific configs which inherit from `AtmConfig` also inherit from `TaintTracking::Configuration`.

This removes the need for two separate config classes for each query.
2022-11-29 13:20:47 -08:00
tiferet
05a943c9b5 Delete StandardEndpointFilters.
All remaining functionality in `StandardEndpointFilters` is only being used in `EndpointCharacteristics`, so it can be moved there as a small set of helper predicates.
2022-11-29 13:20:47 -08:00
tiferet
5402f047bf Delete CoreKnowledge.
All remaining functionality in `CoreKnowledge` is only being used in `EndpointCharacteristics`, so it can be moved there as a small set of helper predicates.
2022-11-29 13:20:47 -08:00
Tiferet Gazit
2241252202 Merge pull request #11321 from github/tiferet/complexity-reduction
ATM: Remove redundant code
2022-11-29 13:17:55 -08:00
tiferet
1d4b2ccab4 Merge branch 'main' into tiferet/complexity-reduction 2022-11-29 12:47:18 -08:00
Tiferet Gazit
f375b0cc1b Merge pull request #11281 from github/tiferet/endpoint-filters
ATM: Implement the current endpoint filters as EndpointCharacteristics
2022-11-29 12:38:12 -08:00
Nora Dimitrijević
91edeacb9f Swift: update .expected test files 2022-11-29 19:39:48 +01:00
Ian Lynagh
7eaef0cd3d Merge pull request #11436 from igfoo/igfoo/NamingConventionsRefTypes
Kotlin: Enable java/misnamed-type query
2022-11-29 18:39:18 +00:00
Paolo Tranquilli
47702b9e14 Swift: tentative fix for the bazel cache 2022-11-29 18:16:33 +01:00
Owen Mansel-Chan
083a3bae6e Correct headings in identical-files.json 2022-11-29 16:15:01 +00:00
Owen Mansel-Chan
5c5ec8f66a Add go files to identical-files.json 2022-11-29 16:15:01 +00:00
Owen Mansel-Chan
1731d39119 Remove sync-dataflow-libraries from go makefile 2022-11-29 16:15:01 +00:00
Owen Mansel-Chan
818f02826c Update shared library files for go to f3dca95 2022-11-29 16:15:01 +00:00
Owen Mansel-Chan
d9f8420c86 Update shared library files for go to PR #11183
Merge commit: 94bca43
2022-11-29 16:15:00 +00:00
Owen Mansel-Chan
b63d0892ab Update shared library files for go to PR #10777
Merge commit: 9c6875e
2022-11-29 16:15:00 +00:00
Owen Mansel-Chan
309ab772da Update shared library files for go to PR #10886
Merge commit: 99ca28e
2022-11-29 16:15:00 +00:00
Owen Mansel-Chan
804d131d3b Update shared library files for go to PR #11060
Merge commit: 587e673
2022-11-29 16:14:59 +00:00
Owen Mansel-Chan
40eb422524 Update shared library files for go to PR #10814
Merge commit: 6ef5fac
2022-11-29 16:14:58 +00:00
Owen Mansel-Chan
3006551eb1 Update shared library files for go to PR #10806
Merge commit: d79a7e8
2022-11-29 16:14:58 +00:00
Owen Mansel-Chan
5c7f7328ff Update shared library files for go to PR #10754
Merge commit: d6df69d
2022-11-29 16:14:58 +00:00
Owen Mansel-Chan
c19ab7bc85 Update shared library files for go to PR #10744
Merge commit: 60fe370
2022-11-29 16:14:57 +00:00
Owen Mansel-Chan
8957437a4c Update shared library files for go to PR #10691
Merge commit: 0e6735b
2022-11-29 16:14:57 +00:00
Owen Mansel-Chan
d63f161f06 Update shared library files for go to PR #10577
Merge commit: df2b586
2022-11-29 16:14:57 +00:00
Owen Mansel-Chan
c9aef4ac9f Update shared library files for go to PR #10575
Merge commit: 9f1bbf2
2022-11-29 16:14:56 +00:00
Owen Mansel-Chan
6ccfb4b4ba Update shared library files for go to PR #10505
Merge commit: 8b424d1
2022-11-29 16:14:56 +00:00
Owen Mansel-Chan
628230f14c Update shared library files for go to PR #10360
Merge commit: 569fad6
2022-11-29 16:14:55 +00:00
Owen Mansel-Chan
569da2da60 Update shared library files for go to PR #10127
Merge commit: e265b07
2022-11-29 16:14:55 +00:00
Owen Mansel-Chan
c2b64d4545 Update shared library files for go to PR #9867
Merge commit: c514c88
2022-11-29 16:14:54 +00:00
Owen Mansel-Chan
f05da69392 Update shared library files for go to PR #10007
Merge commit: a3fb54c
2022-11-29 16:14:54 +00:00
Owen Mansel-Chan
ae408290dd Update shared library files for go to PR #9823
Merge commit: aa36556
2022-11-29 16:14:54 +00:00
Owen Mansel-Chan
20b9c60d58 Remove DataFlowImplConsistency.qll from makefile 2022-11-29 16:14:53 +00:00
Paolo Tranquilli
c779b8f711 Merge pull request #11479 from erik-krogh/swift-ignore-errors
Swift: don't crash on non-existing cache
2022-11-29 17:04:11 +01:00
Nora Dimitrijević
c5b4e87f6d Swift: AccessorDecl tests for new cases 2022-11-29 16:00:00 +01:00
Nora Dimitrijević
e8fe0b0213 Merge pull request #11473 from github/d10c-preparedbupgrade-md
Update docs/prepare-db-upgrade.md
2022-11-29 15:27:58 +01:00
erik-krogh
e5f1fe86e4 don't crash on non-existing cache in swift 2022-11-29 15:02:08 +01:00
Karim Ali
9048d5d79b fix expected output for LocalTaint 2022-11-29 15:38:44 +02:00
Tom Hvitved
5bcb9b285a Merge pull request #11142 from JarLob/const
C#: Extend `Constant Condition` query with `String.IsNullOrEmpty`.
2022-11-29 14:32:48 +01:00
Paolo Tranquilli
d7313082c9 Merge pull request #11466 from github/redsun82/cache
Swift: optimize bazel caching in CI
2022-11-29 14:31:16 +01:00
Karim Ali
9d17fae00c fix expected output for TaintInline 2022-11-29 15:24:12 +02:00
Karim Ali
c0085cbb1a fix expected output for Taint.ql 2022-11-29 15:18:39 +02:00
Ian Lynagh
3b31b50983 Kotlin: Rename compilerGeneratedKind to compilerGeneratedKindOverride 2022-11-29 13:07:47 +00:00
Arthur Baars
442c4e9a4e Merge pull request #11474 from github/aibaars-patch-2
Ruby: fix upgrade script
2022-11-29 13:45:06 +01:00
Tom Hvitved
b075c55a60 Merge pull request #11449 from hvitved/csharp/autobuild-env-vars-integration-test
C#: Add integration test that checks whether env vars are passed through autobuilder
2022-11-29 13:43:15 +01:00
Arthur Baars
52cf27653f Ruby: fix upgrade script 2022-11-29 13:12:14 +01:00
Paolo Tranquilli
a3a68fe83d Tweak comment in incremental-cache action 2022-11-29 13:09:45 +01:00
Arthur Baars
cf7ebe2fa8 Merge pull request #11471 from github/rc/3.8
Merge rc/3.8 into main
2022-11-29 12:57:34 +01:00
Michael Nebel
bc6f0c1622 Merge pull request #11243 from michaelnebel/java/dataextensions
Java: Use data extensions for MaD models.
2022-11-29 12:43:26 +01:00
Nora Dimitrijević
1ee04dc020 Update prepare-db-upgrade.md
Clarify the test steps based on my experience, where I got a "database may be too new" error when the `--search-path=<codeql-root>` directory was not specified.
2022-11-29 12:36:51 +01:00
erik-krogh
cd770e0906 Merge branch 'main' into test-ci 2022-11-29 12:17:05 +01:00
erik-krogh
7c1435b7c6 use withFileTypes in move-caches.js some more 2022-11-29 12:16:38 +01:00
Erik Krogh Kristensen
3cb31ef030 use withFileTypes in move-caches.js
Co-authored-by: Arthur Baars <aibaars@github.com>
2022-11-29 12:10:00 +01:00
Geoffrey White
3304e9f422 Merge pull request #11459 from geoffw0/constructor
Swift: Constructor and destructor related cleanup
2022-11-29 11:00:22 +00:00
Paolo Tranquilli
52a117aaf5 Swift: optimize bazel caching in CI
Previously the cache would become stale. Now the same incremental
cache mechanism in use for the QL cache is adopted (and factored out
in a separate action).

Namely, pushes on main will populate the cache using the commit hash as
key, while PRs will try to use the cache of their merge base, read-only.

To avoid the cache growing out of control, a simple cache eviction is
done on pushes.
2022-11-29 11:43:54 +01:00
Geoffrey White
63c139fdbe Merge branch 'main' into constructor 2022-11-29 10:40:19 +00:00
AlexDenisov
0f87eb45db Merge pull request #11470 from github/redsun82/swift-fix-ci
Swift: revert `swift-actions/setup-swift`
2022-11-29 11:39:08 +01:00
Nora Dimitrijević
93cce0f4c2 Swift: upgrade script 2022-11-29 11:31:08 +01:00
Nora Dimitrijević
13bbee73d8 Swift: Add .generated.list file from codegen 2022-11-29 11:31:07 +01:00
Nora Dimitrijević
931173350f Swift: extract missing cases of AccessorKind and AccessSemantics
This resolves the warnings that were showing up during extractor-pack
compilation.
2022-11-29 11:31:07 +01:00
Nora Dimitrijević
36e7235493 Swift: fix -Wmissing-braces warning in extractor
By initializing va_list the standard way,
i.e. leaving it uninitialized until va_start().
2022-11-29 11:31:07 +01:00
Nora Dimitrijević
c89c449a2c Swift: silence cmake/vscode intellisense Problem
i.e. "TrapLabel not found"
2022-11-29 11:27:33 +01:00
Paolo Tranquilli
9a25de3ef1 Swift: revert swift-actions/setup-swift 2022-11-29 11:17:08 +01:00
Karim Ali
f0d9dabca2 updated expected output for LocalTaint and Tain 2022-11-29 12:13:10 +02:00
Karim Ali
9b3c4e8db2 add test case for unicodeScalars 2022-11-29 12:12:10 +02:00
Karim Ali
7541b01a86 add test case for utf8CString 2022-11-29 12:12:10 +02:00
Karim Ali
58a87396ba add taint steps for fields of String
if a String is tainted, then all its fields (including those declared in extensions) should be tainted as well
2022-11-29 12:12:10 +02:00
Mathias Vorreiter Pedersen
4233c91a7e Merge pull request #11469 from atorralba/atorralba/swift/fix-local-taint
Swift: Fix local taint
2022-11-29 10:00:12 +00:00
erik-krogh
915d680fcc use a node script instead of bash to move the compilation cache 2022-11-29 10:41:08 +01:00
erik-krogh
67e9841bf3 place the compilation cache in the temp dir 2022-11-29 10:40:54 +01:00
Tony Torralba
0e59257442 Fix local taint steps
Local taint should include local flow and simple summaries through library code
2022-11-29 10:36:56 +01:00
Tom Hvitved
f3dca95958 Merge pull request #11087 from hvitved/dataflow/summary-ctx
Data flow: Add summary/return context to pruning stages 2-4
2022-11-29 10:36:53 +01:00
Geoffrey White
c3dc9672f7 Merge branch 'main' into constructor 2022-11-29 09:30:03 +00:00
Geoffrey White
e0c8a8ecff Merge pull request #11458 from geoffw0/simplify
Swift: Simplify some QL.
2022-11-29 09:29:12 +00:00
Felicity Chapman
5898615f5a Merge pull request #11420 from github/felicitymay-8441-query-guides-java
LGTM deprecation: updates to CodeQL for Java articles
2022-11-29 09:23:21 +00:00
Felicity Chapman
9780990836 Merge pull request #11433 from github/8441-general-changes
LGTM deprecation: miscellaneous changes
2022-11-29 09:06:31 +00:00
Felicity Chapman
434c5ea188 Merge pull request #11422 from github/felicitymay-8441-query-guides-python
LGTM deprecation: updates to CodeQL for Python articles
2022-11-29 09:04:48 +00:00
Felicity Chapman
114d337210 Merge pull request #11421 from github/felicitymay-8441-query-guides-c
LGTM deprecation: updates to CodeQL for C/C++ articles
2022-11-29 09:04:31 +00:00
Felicity Chapman
0624324962 Merge branch 'rc/3.8' into felicitymay-8441-query-guides-java 2022-11-29 09:03:32 +00:00
Erik Krogh Kristensen
0cd50aac40 Merge pull request #11398 from erik-krogh/splat-stuff
Rb: add some more flow through splat parameters
2022-11-28 22:31:25 +01:00
Erik Krogh Kristensen
ae40b0aba2 Merge pull request #11419 from github/felicitymay-8441-query-guides-javascript
LGTM deprecation: updates to CodeQL for JavaScript articles
2022-11-28 21:57:37 +01:00
tiferet
4580b55673 Oops -- forgot to stage one file in the previous commit :) 2022-11-28 11:34:34 -08:00
tiferet
210644e87d Delete StandardEndpointFilters.
All remaining functionality in `StandardEndpointFilters` is only being used in `EndpointCharacteristics`, so it can be moved there as a small set of helper predicates.
2022-11-28 11:34:34 -08:00
tiferet
15121931b4 Delete CoreKnowledge.
All remaining functionality in `CoreKnowledge` is only being used in `EndpointCharacteristics`, so it can be moved there as a small set of helper predicates.
2022-11-28 11:34:34 -08:00
tiferet
1c679378e7 FilteringReason is no longer being used and can be deleted 2022-11-28 11:34:33 -08:00
tiferet
99de397a5f Remove redundant code
`isOtherModeledArgument` and `isArgumentToBuiltinFunction` contained the old logic for selecting negative endpoints for training.

These can now be deleted, and replaced by a single base class that collects all EndpointCharacteristics that are currently used to indicate negative training samples: `OtherModeledArgumentCharacteristic`.

This in turn lets us delete code from `StandardEndpointFilters` that effectively said that endpoints that are high-confidence non-sinks shouldn't be scored at inference time, either.
2022-11-28 11:34:33 -08:00
tiferet
7b0269c999 Fix British spelling that code scanning didn't like.
I've been working with Brits for too long :)
2022-11-28 11:28:08 -08:00
Tiferet Gazit
72c46c662c Merge pull request #11462 from github/tiferet/endpoint-filters-sidebar
Endpoint filters added commits
2022-11-28 11:18:53 -08:00
tiferet
963407de4c Update the documentation 2022-11-28 11:16:06 -08:00
Geoffrey White
349a10c013 Swift: codegen. 2022-11-28 17:41:41 +00:00
Geoffrey White
96e04e7f63 Swift: Use ConstructorDecl in place of name matching. 2022-11-28 17:39:45 +00:00
Geoffrey White
e97aee5d9d Swift: QLDoc. 2022-11-28 17:39:44 +00:00
Geoffrey White
410609fed4 Swift: Make ConstructorDecl, DestructorDecl into MethodDecls. 2022-11-28 17:20:43 +00:00
Geoffrey White
edb6325117 Swift: Fix comment. 2022-11-28 17:07:34 +00:00
Geoffrey White
aa5c893d5e Swift: Further simplify. 2022-11-28 17:07:34 +00:00
Geoffrey White
97bd91ed19 Swift: Simplify using ApplyExpr.getArgumentWithLabel. 2022-11-28 16:51:46 +00:00
Paolo Tranquilli
28bf0c9e03 Merge pull request #11365 from github/redsun82/swift-integration-test-pack
Swift: fix extractor tests pack
2022-11-28 17:02:54 +01:00
Paolo Tranquilli
ace7146164 Merge pull request #11454 from erik-krogh/swift-test
Swift: CI cache fixes
2022-11-28 16:24:22 +01:00
Felicity Chapman
59b6d657cc Apply suggestions from code review
Co-authored-by: hubwriter <hubwriter@github.com>
2022-11-28 15:45:05 +01:00
Felicity Chapman
7e5a9fbe2e Update note for review comments 2022-11-28 15:41:39 +01:00
Felicity Chapman
179941daab First set of updates for JavaScript articles 2022-11-28 15:41:39 +01:00
Felicity Chapman
33ae086861 Apply suggestions from code review
Co-authored-by: Tony Torralba <atorralba@users.noreply.github.com>
2022-11-28 15:38:03 +01:00
Felicity Chapman
d6ae5c898a Respond to review feedback 2022-11-28 15:38:03 +01:00
Felicity Chapman
a9b6a12317 Updates for Java articles 2022-11-28 15:38:03 +01:00
Felicity Chapman
85961f5dce Update docs/codeql/codeql-language-guides/functions-in-cpp.rst 2022-11-28 15:36:05 +01:00
Felicity Chapman
8ec06d45e0 Replace LGTM description with VS Code 2022-11-28 15:36:05 +01:00
Felicity Chapman
b22ccc114e Minor changes to Go and Ruby article 2022-11-28 15:36:05 +01:00
Felicity Chapman
0ac0277639 Minor change to C# article 2022-11-28 15:36:05 +01:00
Felicity Chapman
a407f0a4ac Update main C/C++ articles 2022-11-28 15:36:04 +01:00
Felicity Chapman
da4c178534 Update main Python articles 2022-11-28 15:34:12 +01:00
Felicity Chapman
c1e6d4c82a Update .github/ISSUE_TEMPLATE/ql---general.md 2022-11-28 15:26:24 +01:00
Felicity Chapman
c451fa8ad4 Update cpp/ql/src/Likely Bugs/RedundantNullCheckSimple.ql
Co-authored-by: Taus <tausbn@github.com>
2022-11-28 15:26:24 +01:00
Felicity Chapman
ea127c3d99 A few more references 2022-11-28 15:26:24 +01:00
Felicity Chapman
b5f849463b Update QL library references 2022-11-28 15:26:24 +01:00
Felicity Chapman
c06db6b67c Remove LGTM support info 2022-11-28 15:26:22 +01:00
Felicity Chapman
fb0959bcea Update QL reference 2022-11-28 15:25:38 +01:00
Felicity Chapman
5f835da838 Update HTML comment in query 2022-11-28 15:25:38 +01:00
Felicity Chapman
36a6ccba8b Remove reference in template 2022-11-28 15:25:38 +01:00
Felicity Chapman
a76d47681d Replace references in Qhelp files 2022-11-28 15:25:37 +01:00
Mathias Vorreiter Pedersen
3716d67cc9 Merge pull request #11451 from geoffw0/wkuserscript
Swift: models for WKUserScript
2022-11-28 14:24:19 +00:00
Geoffrey White
f291320655 Merge pull request #11447 from geoffw0/localflow2
Swift: Update queries to use LocalFlowSource
2022-11-28 14:18:01 +00:00
erik-krogh
d2824413db skip the only remaining macos job running on main 2022-11-28 14:44:34 +01:00
erik-krogh
751ffbd9c8 use different keys for different caches 2022-11-28 14:44:07 +01:00
Erik Krogh Kristensen
7049532227 Merge pull request #11364 from github/redsun82/swift-ci-cache
Swift: cache more aggressively in CI
2022-11-28 14:34:43 +01:00
Geoffrey White
30468dd419 Swift: Implement field content as well. 2022-11-28 12:27:33 +00:00
Geoffrey White
ffbd201450 Swift: Implement basic model of WKUserScript. 2022-11-28 12:20:29 +00:00
Geoffrey White
116d9667e7 Swift: Remove special case from query. 2022-11-28 12:15:38 +00:00
Ian Lynagh
a32363de79 Kotlin: Avoid giving a single class 2 compiler-generated kinds 2022-11-28 12:14:50 +00:00
Ian Lynagh
7863bc2c99 Kotlin: Accept test output 2022-11-28 12:14:36 +00:00
Tom Hvitved
1e63893411 C#: Add integration test that checks whether env vars are passed through autobuilder 2022-11-28 13:12:24 +01:00
Geoffrey White
3971cbf294 Swift: Extend the taint test with WKUserScript. 2022-11-28 12:11:38 +00:00
Erik Krogh Kristensen
477a32831b Merge pull request #11448 from github/erik-krogh/lgtm-codeql-js-extractor
JS: update `javascript/extractor/README.md` to mention CodeQL instead of LGTM
2022-11-28 13:10:25 +01:00
Arthur Baars
a879fd519a Merge pull request #11295 from github/aibaars-patch-2
"CodeQL False positive" -> "CodeQL false positive"
2022-11-28 13:06:39 +01:00
Michael Nebel
d7e656a32a Java: Add change note. 2022-11-28 12:30:36 +01:00
Michael Nebel
43a63d6373 Java: Convert all models to data extensions in testcases. 2022-11-28 12:30:36 +01:00
Michael Nebel
74f02cf855 Java: Allow empty package name in model definitions. 2022-11-28 12:30:35 +01:00
Michael Nebel
b96540c937 Java: Convert permissve-dot-regex-query to data extensions. 2022-11-28 12:30:35 +01:00
Michael Nebel
91840c613e Java: Convert unsafe-url-forward to data extensions. 2022-11-28 12:30:35 +01:00
Michael Nebel
aed5ee4edc Java: Convert thread-resource-abuse to data extensions. 2022-11-28 12:30:35 +01:00
Michael Nebel
07578f11d4 Java: Convert hardcoded-jwt-key models to data extensions. 2022-11-28 12:30:35 +01:00
Michael Nebel
ab12b6cc2b Java: Convert android-web-resource-response to data extensions. 2022-11-28 12:30:35 +01:00
Michael Nebel
5c15ad412c Java: Convert log4j-injection to data extensions. 2022-11-28 12:30:35 +01:00
Michael Nebel
665d40dc4b Java: Convert file-path-injection to data extensions. 2022-11-28 12:30:35 +01:00
Michael Nebel
b61f515af2 Jave: Make support for query specific models. 2022-11-28 12:30:35 +01:00
Michael Nebel
805430983c Java: Convert commons-io to data extensions. 2022-11-28 12:30:35 +01:00
Michael Nebel
d7aafbfe64 Java: Add model generator script that emits data extensions. 2022-11-28 12:30:35 +01:00
Michael Nebel
9c93402b26 Java: Convert integration test to use data extensions instead of inlined models. 2022-11-28 12:30:35 +01:00
Michael Nebel
157a228088 Java: Add query packs with names to enable dependency resolution. 2022-11-28 12:30:35 +01:00
Michael Nebel
2d309bb8f8 Java: Include the library query pack to get the data extensions included. 2022-11-28 12:30:35 +01:00
Michael Nebel
b3a3b676ba Java: Remove manual models from QL code. 2022-11-28 12:30:34 +01:00
Michael Nebel
f4e1867d28 Java: Define extensible predicates to at least be empty. 2022-11-28 12:30:34 +01:00
Michael Nebel
0abeb831c7 Java: Move summaryModel predicate, which constructs a CSV row to the testcode where it is used. 2022-11-28 12:30:34 +01:00
Michael Nebel
663d091776 Java: Invert dependencies and use the extensible predicates. 2022-11-28 12:30:34 +01:00
Michael Nebel
9cb5ff1cdc Java: Add data extensions for all manual models. 2022-11-28 12:30:34 +01:00
Michael Nebel
8e25cac653 Java: Add extensible predicates. 2022-11-28 12:30:34 +01:00
Michael Nebel
7b6f202f23 Java: Renaming. 2022-11-28 12:30:34 +01:00
Michael Nebel
63e2206d16 Java: Prepare QL pack for data extensions. 2022-11-28 12:30:34 +01:00
Michael Nebel
fc4b9827b9 Java: Add script for converting extensions. 2022-11-28 12:30:34 +01:00
Michael Nebel
9f7103c4fb Java: Add queries for extracting sources, sinks and summaries. 2022-11-28 12:30:34 +01:00
Michael Nebel
a8ee878356 Java: Add bi-directional import of FragmentInjection. 2022-11-28 12:30:33 +01:00
Michael Nebel
663112576a Java: Update commons-io models. 2022-11-28 12:30:33 +01:00
Erik Krogh Kristensen
7a3898168f Update README.md 2022-11-28 12:12:36 +01:00
Tom Hvitved
cde05e1190 Data flow: Sync files 2022-11-28 12:11:38 +01:00
Tom Hvitved
c65780ee99 Data flow: Inline revFlowInNotToReturn 2022-11-28 12:11:18 +01:00
Tom Hvitved
bdb205a318 Data flow: Track return kind instead of return position in pruning stages 2-4 2022-11-28 12:11:18 +01:00
Tom Hvitved
4346a7f426 Data flow: Inline fwdFlowOutNotFromArg 2022-11-28 12:11:18 +01:00
Tom Hvitved
70d2a0df8a Data flow: Track parameter position instead of parameter in pruning stages 2-4 2022-11-28 12:11:12 +01:00
Taus
530b795eaa Merge pull request #11402 from yoff/python/port-super-not-enclosing-class
Python: port `py/super-not-enclosing-class`
2022-11-28 11:52:57 +01:00
Nick Rolfe
8a94cabdbf Merge pull request #11250 from github/nickrolfe/stack-trace-exposure
Ruby: add stack-trace exposure query
2022-11-28 10:45:59 +00:00
Geoffrey White
b3d2e759a6 Swift: Update swift/sql-injection to include local flow sources. 2022-11-28 10:11:44 +00:00
Geoffrey White
a5a459fe0a Swift: Update swift/unsafe-js-eval to include local flow sources. 2022-11-28 10:11:44 +00:00
Michael Nebel
7456f3750d Merge pull request #11432 from michaelnebel/java/flowtestgeneratortools
Java: Flow test case generator
2022-11-28 10:41:42 +01:00
erik-krogh
0c2ff98dc2 add flow from the first splat argument to the first splat parameter 2022-11-28 09:54:05 +01:00
erik-krogh
d5725255fe add failing test for splat parameter flow 2022-11-28 09:53:03 +01:00
Michael Nebel
24e830f91d Java: Fix some minor bugs in the CSV printing of summaries for flow test generation. 2022-11-28 09:07:40 +01:00
Michael Nebel
abe4d99e12 Java: Make some rudimentary tooling for testing the flow test case generator script. 2022-11-28 09:07:40 +01:00
Chris Smowton
5a0cce2a18 Merge pull request #11430 from erik-krogh/go-go
GO: get the Go CI to go fast!
2022-11-26 11:47:04 +00:00
erik-krogh
2b0ecec0c8 only run other-os on non-ql changes 2022-11-26 10:43:33 +01:00
erik-krogh
dcfa0b38c1 use a non-xl mac runner because the XL runners keep queing 2022-11-26 10:43:33 +01:00
erik-krogh
2b996f11cc use query compilation cache 2022-11-26 10:43:33 +01:00
erik-krogh
1e732ad4d7 use XL workers, and update the windows runner 2022-11-26 10:43:33 +01:00
erik-krogh
d7763f236f use more threads for codeql test 2022-11-26 10:43:33 +01:00
erik-krogh
b6034b4935 delete language specific format check 2022-11-26 10:43:30 +01:00
Chris Smowton
fb1f22144d Merge pull request #11352 from smowton/smowton/admin/kotlin-tests-1.7.20
Kotlin: bump default CI version to 1.7.20
2022-11-25 21:05:10 +00:00
Chris Smowton
0879f02db6 Adapt custom_plugin test to Kotlin 1.7.20 2022-11-25 17:54:53 +00:00
Ian Lynagh
a423f5f695 Kotlin: Enable java/misnamed-type query
We used to get alerts for the class around a local function, a lambda,
or a function reference, which we give name "". Now those are marked as
compiler-generated, and the query ignores compiler-generated types.
2022-11-25 17:11:40 +00:00
Jeroen Ketema
53b86fd53b Merge pull request #11428 from jketema/default-taint-tests
C++: Add more tests that exercise the default taint barrier implementation
2022-11-25 12:13:18 +01:00
Chris Smowton
180c3cee44 Accept integration test changes caused by variable location changes 2022-11-25 10:54:50 +00:00
Chris Smowton
32847c125a Accept more test changes due to variable locations changing
There is also one non-location change: kotlin.Byte (and likely other primitives) now have real equals and toString overrides, which matches their native source and documentation; before they appeared to have fake overrides.
2022-11-25 10:47:48 +00:00
Alex Ford
8362caa9d9 Merge pull request #11417 from alexrford/ruby/activesupport-json_escape
Ruby: model ActiveSupport `json_escape` flow
2022-11-25 10:46:34 +00:00
Chris Smowton
8ec681e61c Kotlin: bump default CI version to 1.7.20
A bunch of test expectations change because 7f531d8426 means that we now see (a) local variable declarations with source locations covering only their identifier, not the whole statement, and (b) more SYNTHETIC_OFFSET values for the parts of a destructuring assignment
or initialiser, which show up as file.kt:0:0:0:0 in DbLocation form.
2022-11-25 10:19:26 +00:00
Jeroen Ketema
4607f5990e C++: Add more tests that exercise the default taint barrier implementation 2022-11-25 10:19:45 +01:00
Paolo Tranquilli
8df7d465cb Swift: cache more aggressively in CI
* the QL compilation cache action is used for ql and integration tests
* all caches (Bazel and QL) are populated on push
2022-11-25 08:52:46 +01:00
Rasmus Lerchedahl Petersen
77d98b217e Python: add import 2022-11-25 08:52:35 +01:00
yoff
d804acdef7 Apply suggestions from code review
Co-authored-by: Taus <tausbn@github.com>
2022-11-25 08:50:37 +01:00
Paolo Tranquilli
9b4b29cab7 Merge branch 'main' into redsun82/swift-integration-test-pack 2022-11-25 08:49:36 +01:00
Harry Maclean
2822c94aa7 Ruby: Minor refactor of barrier guard code 2022-11-25 09:12:51 +13:00
Harry Maclean
6897fb46cb Ruby: Clean up WhenClause CFG 2022-11-25 09:12:51 +13:00
Alexander Eyers-Taylor
3c7f7511be Merge pull request #11418 from alexet/alexet/mockito-class-rename
Java: Add new Mockito runner class location.
2022-11-24 17:49:31 +00:00
alexet
e24e3bf13f Java: Add new Mockito runner class location. 2022-11-24 16:43:57 +00:00
Henry Mercer
df3dc6fadc Merge pull request #11414 from github/codeql-ci/atm/release-0.4.2
JS: Bump version numbers of ML-powered packs after 0.4.2 release
2022-11-24 16:07:28 +00:00
Alex Ford
e6446e501c Ruby: fix docs failure 2022-11-24 15:37:03 +00:00
Alex Ford
893c8763bb Ruby: model ActiveSupport json_escape flow 2022-11-24 15:33:08 +00:00
Edoardo Pirovano
9071acea01 Merge pull request #11416 from github/edoardo/mergeback-3.8
Merge `rc/3.8` into `main`
2022-11-24 15:05:28 +00:00
Erik Krogh Kristensen
03737543d4 Merge pull request #11403 from erik-krogh/additional
ReDoS: add missing additional keywords
2022-11-24 15:53:51 +01:00
Edoardo Pirovano
8eeba92a47 Merge pull request #11415 from github/edoardo/mergeback-2.11.4
Merge `codeql-cli-2.11.4` into `rc/3.8`
2022-11-24 14:42:36 +00:00
Henry Mercer
56e5f01ce0 Merge branch 'main' into codeql-ci/atm/release-0.4.2 2022-11-24 14:41:49 +00:00
github-actions[bot]
78d49e44b1 JS: Bump version of ML-powered library and query packs to 0.4.3 2022-11-24 14:22:14 +00:00
github-actions[bot]
8d96bfe973 JS: Bump patch version of ML-powered library and query packs 2022-11-24 14:18:13 +00:00
Nick Rolfe
50b10be2db Ruby: StackTraceExposure: add test for a specific rescue type 2022-11-24 14:08:34 +00:00
Nick Rolfe
1c407a28cd Apply suggestions from code review
Co-authored-by: Harry Maclean <hmac@github.com>
2022-11-24 14:02:32 +00:00
Tony Torralba
adf905d838 Merge pull request #11368 from ka1n4t/main
Java: Add binding between annotation and sink-param in MyBatis SQL Injection query
2022-11-24 14:34:57 +01:00
Tony Torralba
1d57663343 Merge pull request #11345 from atorralba/atorralba/swift/data-models
Swift: Add taint models for the Data class
2022-11-24 14:23:41 +01:00
Geoffrey White
32442a33de Merge pull request #11270 from geoffw0/optionals2
Swift: Dataflow through ?? and ? :
2022-11-24 13:20:54 +00:00
Ian Lynagh
184c903ec7 Merge pull request #11401 from igfoo/igfoo/unused
Kotlin: Remove an unused argument
2022-11-24 12:47:50 +00:00
Ian Lynagh
501ea31c25 Merge pull request #11404 from igfoo/igfoo/build_refactor
Kotlin build system: Refactor jar-finder
2022-11-24 12:47:34 +00:00
Anders Schack-Mulligen
f2897f5bfc Merge pull request #11392 from aschackmull/java/adjust-mad-dispatch-priority
Java: Adjust the prioritisation between MaD and source dispatch.
2022-11-24 13:10:48 +01:00
Tony Torralba
e4e5291511 Fix more test expectations after rebase 2022-11-24 13:03:12 +01:00
Erik Krogh Kristensen
6464135800 Merge pull request #11409 from erik-krogh/cache-more
Ruby: cache the compiled extractor in the build tests
2022-11-24 12:42:33 +01:00
Tony Torralba
fc7c66dab2 Remove now unnecessary additional taint step in UnsafeJsEval 2022-11-24 12:35:52 +01:00
Tony Torralba
e67b72d954 Update test expectations 2022-11-24 12:35:51 +01:00
Tony Torralba
25354d2dd8 Apply code review suggestions 2022-11-24 12:35:51 +01:00
Tony Torralba
6a8b9fde78 Add data flowsources test 2022-11-24 12:35:51 +01:00
Tony Torralba
d6b14a1395 Update test expectations 2022-11-24 12:35:50 +01:00
Tony Torralba
2284127650 Add MaD rows for the Data class 2022-11-24 12:35:50 +01:00
Tony Torralba
4bbc1dc734 Update test expectations 2022-11-24 12:34:48 +01:00
Tony Torralba
cac6e946ab Merge pull request #11411 from atorralba/atorralba/swift/fix-nsdata-test
Swift: Fix expectation in NSData tests
2022-11-24 12:34:28 +01:00
Ben Ahmady
7f5df4fc0e Merge pull request #11384 from github/subatoi/codeql-cli-2.11.4
Adds Kotlin (beta) content
2022-11-24 11:33:46 +00:00
Geoffrey White
c6835cd270 Swift: Update .generated.list. 2022-11-24 11:16:56 +00:00
Tony Torralba
4f8ef13cd8 Fix expectation in NSData tests 2022-11-24 12:13:46 +01:00
Tony Torralba
17218fa663 Formatting 2022-11-24 11:14:16 +01:00
Ben Ahmady
42259ef8d1 Update docs/codeql/reusables/kotlin-java-differences.rst 2022-11-24 10:10:42 +00:00
Tony Torralba
443d0f50c1 Apply suggestions from code review 2022-11-24 11:10:07 +01:00
erik-krogh
c9a600d496 Ruby: cache the compiled extractor, because that's way smaller than the cargo cache 2022-11-24 10:55:38 +01:00
Tom Hvitved
4e4ee32dbc Data flow: Join on one more column in flowThroughIntoCall 2022-11-24 10:48:29 +01:00
Harry Maclean
57f689401e Ruby: SplatExprCfgNode extends UnaryOperationCfgNode 2022-11-24 17:33:57 +13:00
Ian Lynagh
2d92cee26a Kotlin build system: Refactor jar-finder
We were globbing with a * in the filename, but that is not necessary.
2022-11-23 21:43:32 +00:00
erik-krogh
95f35196e4 add missing additional keywords 2022-11-23 20:45:51 +01:00
Ian Lynagh
5b8b9044a5 Kotlin: Remove an unused argument 2022-11-23 18:47:53 +00:00
tiferet
03b8e649f1 Filter endpoints by confidence
Select endpoints to score at inference time base purely on their confidence level, and not on whether they fit the historical definition of endpoint filters.
2022-11-23 10:46:27 -08:00
Ben Ahmady
357c823b92 Changes after feedback 2022-11-23 16:59:52 +00:00
Ian Lynagh
d03817ce2d Merge pull request #11399 from igfoo/igfoo/kotlin-basic
Java/Kotlin: Make the basic query in docs work for both languages
2022-11-23 16:01:04 +00:00
Ian Lynagh
277b5b483d Java/Kotlin docs: Tweak text 2022-11-23 15:51:40 +00:00
Ian Lynagh
950c4c811c Java/Kotlin: Make the basic query in docs work for both languages 2022-11-23 15:18:29 +00:00
Ian Lynagh
9ee36215bd Java: Fix basic query in docs 2022-11-23 15:14:46 +00:00
Ben Ahmady
ee0811df26 Update docs/codeql/codeql-language-guides/codeql-for-java.rst 2022-11-23 15:13:40 +00:00
Ben Ahmady
f9215ec5ca Update docs/codeql/reusables/kotlin-java-differences.rst
Co-authored-by: Felicity Chapman <felicitymay@github.com>
2022-11-23 15:10:24 +00:00
Ben Ahmady
72999c7af1 Update docs/codeql/reusables/kotlin-java-differences.rst
Co-authored-by: Felicity Chapman <felicitymay@github.com>
2022-11-23 15:10:14 +00:00
Ben Ahmady
3c3442d8f0 Update docs/codeql/reusables/kotlin-java-differences.rst
Co-authored-by: Felicity Chapman <felicitymay@github.com>
2022-11-23 15:09:58 +00:00
Rasmus Lerchedahl Petersen
91198524cd Python: port py/super-not-enclosing-class 2022-11-23 14:37:45 +01:00
Ben Ahmady
dcd082e955 Possible build fix, else needs new parameter 2022-11-23 12:48:47 +00:00
Anders Schack-Mulligen
807f87e01f Java: Adjust the prioritisation between MaD and source dispatch. 2022-11-23 12:56:32 +01:00
Ben Ahmady
40c2b3c43b Merge branch 'codeql-cli-2.11.4' into subatoi/codeql-cli-2.11.4 2022-11-23 10:18:46 +00:00
Ben Ahmady
093ff4061d Update docs/codeql/reusables/kotlin-java-differences.rst
Co-authored-by: Felicity Chapman <felicitymay@github.com>
2022-11-23 10:08:45 +00:00
Ben Ahmady
7644ecad52 Update docs/codeql/reusables/kotlin-java-differences.rst
Co-authored-by: Felicity Chapman <felicitymay@github.com>
2022-11-23 10:08:29 +00:00
Ben Ahmady
0a91ee1019 Update docs/codeql/codeql-language-guides/abstract-syntax-tree-classes-for-working-with-java-programs.rst 2022-11-23 10:08:20 +00:00
Ben Ahmady
df7f0cf9a9 Update docs/codeql/codeql-language-guides/analyzing-data-flow-in-java.rst 2022-11-23 10:08:10 +00:00
Ben Ahmady
c06b8a68e5 Update docs/codeql/reusables/kotlin-java-differences.rst
Co-authored-by: Felicity Chapman <felicitymay@github.com>
2022-11-23 10:08:00 +00:00
Ben Ahmady
c663da5be6 Update docs/codeql/reusables/kotlin-java-differences.rst
Co-authored-by: Felicity Chapman <felicitymay@github.com>
2022-11-23 10:07:03 +00:00
Ben Ahmady
4bd7e24b5f Update docs/codeql/reusables/kotlin-java-differences.rst
Co-authored-by: Felicity Chapman <felicitymay@github.com>
2022-11-23 10:06:57 +00:00
Ben Ahmady
605c7113a2 Update docs/codeql/reusables/kotlin-java-differences.rst
Co-authored-by: Felicity Chapman <felicitymay@github.com>
2022-11-23 10:06:48 +00:00
Ben Ahmady
e2240abc78 Update docs/codeql/codeql-language-guides/codeql-for-java.rst
Co-authored-by: Felicity Chapman <felicitymay@github.com>
2022-11-23 10:06:34 +00:00
Paolo Tranquilli
545c2f67e9 Merge pull request #11382 from github/alexdenisov/swift-relax-file-archiving
Swift: do not abort if cannot archive a source file
2022-11-23 06:56:26 +01:00
Paolo Tranquilli
d345cec339 Merge pull request #11381 from github/alexdenisov/swift-fix-remapping-regression
Swift: fix remapping bug
2022-11-23 06:56:05 +01:00
ka1n4t
d113fb23c8 Add test case for PR-11368 2022-11-23 11:05:58 +08:00
Ben Ahmady
24c413fbf9 Adds Kotlin (beta) content 2022-11-22 18:33:24 +00:00
Geoffrey White
7d45ca6293 Merge branch 'main' into optionals2 2022-11-22 17:35:27 +00:00
Alex Denisov
8f02463411 Swift: fix remapping bug
This issue has slipped during a recent refactoring:
https://github.com/github/codeql/pull/10987/files#diff-c5ab26a06a93c4507a834859a6a56878d5bfe16c4d7cbac4afc4f081d46f461aL63-R64
2022-11-22 17:25:07 +01:00
Alex Denisov
a2ac1384cb Swift: do not abort if cannot archive a source file 2022-11-22 17:18:40 +01:00
ka1n4t
ce2ba21240 Add binding between annotation and sink-param 2022-11-22 18:32:14 +08:00
Paolo Tranquilli
414f18fc97 Swift: fix extractor tests pack
This allows `codeql query compile` to run on integration test queries.
2022-11-22 10:58:57 +01:00
tiferet
1c9545e49a Address comment from code review:
Make `SyntacticHeuristics` an explicit import
2022-11-21 08:00:31 -08:00
Tom Hvitved
99e70e9a50 Data flow: Sync files 2022-11-20 10:19:23 +01:00
Tom Hvitved
a3a3b46d54 Data flow: Account for return nodes with multiple return kinds when restricting flow through
For example, flow out via parameters allows for return nodes with multiple
return kinds:

```csharp
void SetXOrY(C x, C y, bool b)
{
    C c = x;
    if (b)
        c = y;
    c.Field = taint; // post-update node for `c` has two return kinds
}
```
2022-11-20 10:18:46 +01:00
Tom Hvitved
5adf10fcba Data flow: Add return context to pruning stages 2-4 2022-11-20 10:18:46 +01:00
Tom Hvitved
ca17c5b053 Data flow: Add summary context to pruning stages 2-4 2022-11-20 10:18:40 +01:00
tiferet
8d22fd25f1 Suggestions from code review 2022-11-18 15:57:46 -08:00
Harry Maclean
376d4e03a1 Ruby: Cache some barrier guard predicates 2022-11-18 18:17:02 +13:00
Harry Maclean
5deb16e58c Ruby: Remove redundant predicate
The existing barrier guard machinery recognises guards such as `if x and y`,
so there's no need to explicitly model them.
2022-11-18 18:14:55 +13:00
Geoffrey White
2780d9a9a3 Merge branch 'main' into optionals2 2022-11-17 18:16:46 +00:00
Tom Hvitved
f24fa402f3 Adjust CFG 2022-11-17 10:32:28 +01:00
tiferet
4a1382925e Remove some imports that are no longer used 2022-11-16 14:01:16 -08:00
tiferet
ccbf1ca2a9 Add a comment 2022-11-16 13:05:06 -08:00
tiferet
38c40a7192 isEffectiveSink can't be final because ExtractMisclassifiedEndpointFeatures overrides it. 2022-11-16 12:12:50 -08:00
tiferet
8fee9cb0d5 Fix CodeQL warnings 2022-11-16 12:06:52 -08:00
tiferet
c2035e85d2 Be explicit in requiring that each ATM config set its endpoint type. 2022-11-16 11:55:23 -08:00
tiferet
0fd013f9fd Update the reason names in FilteredTruePositives.expected.
This is needed because we changed the names of three endpoint filters that were all called "not a direct argument to a likely external library call or a heuristic sink" in order to disambiguate them (fc56c5a022).
2022-11-16 11:54:10 -08:00
tiferet
eab270eb84 Move the definitions of isEffectiveSink and getAReasonSinkExcluded to the base class.
They can now be implemented generically for all sink types.
2022-11-16 11:47:24 -08:00
tiferet
fc56c5a022 Implement the type-specific endpoint filters as EndpointCharacteristics.
Also disambiguate three filters from three different sink types that all have the same name, "not a direct argument to a likely external library call or a heuristic sink".
2022-11-16 11:14:25 -08:00
Geoffrey White
c8630bbe4f Merge branch 'main' into optionals2 2022-11-16 13:31:08 +00:00
Arthur Baars
6fb014b34d "CodeQL False positive" -> "CodeQL false positive" 2022-11-16 13:09:47 +01:00
tiferet
13cb0ab554 Fix CodeQL warning 2022-11-15 17:32:30 -08:00
tiferet
2ecdfd1ff6 Delete some code that's no longer in use 2022-11-15 17:29:03 -08:00
tiferet
fedb98ddb5 Implement the standard getAReasonSinkExcluded using StandardEndpointFilterCharacteristics 2022-11-15 17:22:00 -08:00
tiferet
cf4e37a0ab Implement the standard endpoint filters as EndpointCharacteristics 2022-11-15 17:20:20 -08:00
tiferet
cb632b3534 Delete the file ExtractEndpointData.expected which was leftover in the last PR 2022-11-15 17:11:34 -08:00
Geoffrey White
e81c62e402 Swift: Correct comment. 2022-11-15 18:34:51 +00:00
Geoffrey White
7c1fb5d4a6 Swift: Add qldoc for AutoClosureExpr. 2022-11-15 18:31:30 +00:00
Geoffrey White
4eee375296 Swift: Add AutoClosureExpr.getExpr. 2022-11-15 18:31:30 +00:00
Geoffrey White
13d6deb9c4 Swift: Make similar enhancements to unary operations. 2022-11-15 18:31:30 +00:00
Geoffrey White
70075e2832 Swift: Add qldoc for BinaryExpr. 2022-11-15 18:31:21 +00:00
Geoffrey White
3c46b0cad1 Swift: Add BinaryExpr.getOperator(). 2022-11-15 18:04:48 +00:00
Geoffrey White
fa86e75330 Update swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll
Co-authored-by: Mathias Vorreiter Pedersen <mathiasvp@github.com>
2022-11-15 17:49:23 +00:00
Geoffrey White
1b6317d584 Update swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll
Co-authored-by: Mathias Vorreiter Pedersen <mathiasvp@github.com>
2022-11-15 17:19:32 +00:00
Geoffrey White
403f3caf4b Merge branch 'main' into optionals2 2022-11-15 12:12:01 +00:00
Geoffrey White
ae5689b295 Swift: Update comment. 2022-11-15 12:04:10 +00:00
Geoffrey White
4c806a442a Swift: Dataflow through ? :. 2022-11-15 12:01:09 +00:00
Geoffrey White
52d5578fb5 Swift: Dataflow through second argument of ??. 2022-11-15 11:43:43 +00:00
Geoffrey White
2dbb891942 Swift: Dataflow through ??. 2022-11-15 11:42:43 +00:00
Geoffrey White
9b38e1102a Swift: Add more tests of optionals. 2022-11-15 11:41:04 +00:00
Nick Rolfe
c660ea100b Ruby: add changenote for rb/stack-trace-exposure 2022-11-14 12:26:40 +00:00
Nick Rolfe
b39e2ef71c Ruby: add stacktrace exposure query 2022-11-14 12:26:40 +00:00
Harry Maclean
2b4217b8a4 Ruby: Update test fixture 2022-11-11 18:41:55 +13:00
Harry Maclean
b16cecc8db Ruby: Add missing doc 2022-11-11 18:41:42 +13:00
Harry Maclean
62ea1f0a05 Ruby: Fix performance of string comparison guard
The `or` case ran extremely slowly before this change. Also exclude
string interpolations from consideration, for correctness, and add some
more tests.
2022-11-11 18:24:20 +13:00
Harry Maclean
e25e192ef3 Ruby: Change the CFG for while clauses
The `when` node now acts as a join point for patterns in the when
clause, with match/no-match completions. This is similar to how `or`
expressions work.

The result of this is that the `when` clause "controls" the body of the
`when`, which allows us to model barrier guards for multi-pattern when
clauses.

For this code

case x
when 1, 2
  y
end

The old CFG was

x --> when --> 1 --no-match--> 2 ---no-match---> case
                \               \                  ^
                  \               \                |
                   \                --match----+   |
                     \                         |   |
                       \                       |   |
                         ------match---------> y --+

The new CFG is

x --> 1 --no-match--> 2 --no-match--> [no-match] when --no-match--> case
       \               \                                             ^
         \               \                                           |
           \               --match--> [match] when --match--> y -----+
             \                       /
               \                   /
                 -------match-----

i.e. all patterns flow to the `when` node, which is split based on
whether the pattern matched or not. The body of the when clause then has
a single predecessor `[match] when`, which acts as condition block that
controls `y`.
2022-11-11 11:52:27 +13:00
Harry Maclean
a8b0d298ff Ruby: More string comparison guards
Recognise if statements with conditionals made up or logical `and` or
`or` clauses as barrier guards.
2022-11-10 16:38:09 +13:00
Harry Maclean
ad7b5ae7ed Ruby: Add inline barrier guard test 2022-11-09 16:35:28 +13:00
Harry Maclean
f1b63c4df3 Ruby: Fix in clause barrier guard 2022-11-09 16:10:17 +13:00
Harry Maclean
0ab88c2e29 Ruby: Handle simple in clauses in barrier guard 2022-11-09 16:01:33 +13:00
Harry Maclean
87944a3a75 Ruby: Add test for another case guard variant 2022-11-09 15:05:03 +13:00
Harry Maclean
25ceeaf241 Ruby: Fix SplatExprCfgNode 2022-11-09 15:03:15 +13:00
Harry Maclean
4bc9096446 Ruby: Add case string comparison barrier guard
This recognises barriers of the form

    STRINGS = ["foo", "bar"]

    case foo
    when "some string literal"
      foo
    when *["other", "strings"]
      foo
    when *STRINGS
      foo
    end

where the reads of `foo` inside each `when` are guarded by the comparison
of `foo` with the string literals.

We don't yet recognise this construct:

    case foo
    when "foo", "bar"
      foo
    end

This is due to a limitation in the shared barrier guard logic.
2022-11-09 15:03:13 +13:00
JarLob
d865f2ecf5 Remove import 2022-11-07 14:19:24 +01:00
JarLob
e122f94c1c Move to isBooleanConstant 2022-11-07 13:38:05 +01:00
JarLob
74ee101592 Extend Constant Condition query with String.IsNullOrEmpty. 2022-11-07 13:05:37 +01:00
712 changed files with 52330 additions and 74911 deletions

View File

@@ -10,5 +10,5 @@ assignees: ''
**Description of the issue**
<!-- Please explain briefly what is the problem.
If it is about an LGTM project, please include its URL.-->
If it is about a GitHub project, please include its URL. -->

View File

@@ -1,5 +1,5 @@
---
name: CodeQL False positive
name: CodeQL false positive
about: Report CodeQL alerts that you think should not have been detected (not applicable, not exploitable, etc.)
title: False positive
labels: false-positive

View File

@@ -14,8 +14,7 @@ outputs:
runs:
using: composite
steps:
# Cache the query compilation caches.
# calculate the merge-base with main, in a way that works both on PRs and pushes to main.
# calculate the merge-base with main, in a way that works both on PRs and pushes to main.
- name: Calculate merge-base
shell: bash
if: ${{ github.event_name == 'pull_request' }}
@@ -24,38 +23,33 @@ runs:
run: |
MERGE_BASE=$(git cat-file commit $GITHUB_SHA | grep '^parent ' | head -1 | cut -f 2 -d " ")
echo "merge_base=$MERGE_BASE" >> $GITHUB_ENV
- name: Read CodeQL query compilation - PR
- name: Restore read-only cache (PR)
if: ${{ github.event_name == 'pull_request' }}
uses: erik-krogh/actions-cache@a88d0603fe5fb5606db9f002dfcadeb32b5f84c6
with:
path: '**/.cache'
read-only: true
key: codeql-compile-${{ inputs.key }}-pr-${{ github.sha }} # deliberately not using the `compile-compile-main` keys here.
key: codeql-compile-${{ inputs.key }}-pr-${{ github.sha }}
restore-keys: |
codeql-compile-${{ inputs.key }}-${{ github.base_ref }}-${{ env.merge_base }}
codeql-compile-${{ inputs.key }}-${{ github.base_ref }}-
codeql-compile-${{ inputs.key }}-main-
- name: Fill CodeQL query compilation cache - main
- name: Fill cache (push)
if: ${{ github.event_name != 'pull_request' }}
uses: erik-krogh/actions-cache@a88d0603fe5fb5606db9f002dfcadeb32b5f84c6
with:
path: '**/.cache'
key: codeql-compile-${{ inputs.key }}-${{ github.ref_name }}-${{ github.sha }} # just fill on main
restore-keys: | # restore from another random commit, to speed up compilation.
codeql-compile-${{ inputs.key }}-${{ github.ref_name }}-
restore-keys: | # restore the latest cache if the exact cache is unavailable, to speed up compilation.
codeql-compile-${{ inputs.key }}-${{ github.ref_name }}-
codeql-compile-${{ inputs.key }}-main-
- name: Fill compilation cache directory
id: fill-compilation-dir
shell: bash
shell: bash
run: |
# Move all the existing cache into another folder, so we only preserve the cache for the current queries.
mkdir -p ${COMBINED_CACHE_DIR}
rm -f **/.cache/{lock,size} # -f to avoid errors if the cache is empty.
# copy the contents of the .cache folders into the combined cache folder.
cp -r **/.cache/* ${COMBINED_CACHE_DIR}/ || : # ignore missing files
# clean up the .cache folders
rm -rf **/.cache/*
node $GITHUB_WORKSPACE/.github/actions/cache-query-compilation/move-caches.js ${COMBINED_CACHE_DIR}
echo "compdir=${COMBINED_CACHE_DIR}" >> $GITHUB_OUTPUT
env:
COMBINED_CACHE_DIR: ${{ github.workspace }}/compilation-dir
env:
COMBINED_CACHE_DIR: ${{ runner.temp }}/compilation-dir

View File

@@ -0,0 +1,75 @@
// # Move all the existing cache into another folder, so we only preserve the cache for the current queries.
// mkdir -p ${COMBINED_CACHE_DIR}
// rm -f **/.cache/{lock,size} # -f to avoid errors if the cache is empty.
// # copy the contents of the .cache folders into the combined cache folder.
// cp -r **/.cache/* ${COMBINED_CACHE_DIR}/ || : # ignore missing files
// # clean up the .cache folders
// rm -rf **/.cache/*
const fs = require("fs");
const path = require("path");
// the first argv is the cache folder to create.
const COMBINED_CACHE_DIR = process.argv[2];
function* walkCaches(dir) {
const files = fs.readdirSync(dir, { withFileTypes: true });
for (const file of files) {
if (file.isDirectory()) {
const filePath = path.join(dir, file.name);
yield* walkCaches(filePath);
if (file.name === ".cache") {
yield filePath;
}
}
}
}
async function copyDir(src, dest) {
for await (const file of await fs.promises.readdir(src, { withFileTypes: true })) {
const srcPath = path.join(src, file.name);
const destPath = path.join(dest, file.name);
if (file.isDirectory()) {
if (!fs.existsSync(destPath)) {
fs.mkdirSync(destPath);
}
await copyDir(srcPath, destPath);
} else {
await fs.promises.copyFile(srcPath, destPath);
}
}
}
async function main() {
const cacheDirs = [...walkCaches(".")];
for (const dir of cacheDirs) {
console.log(`Found .cache dir at ${dir}`);
}
// mkdir -p ${COMBINED_CACHE_DIR}
fs.mkdirSync(COMBINED_CACHE_DIR, { recursive: true });
// rm -f **/.cache/{lock,size} # -f to avoid errors if the cache is empty.
await Promise.all(
cacheDirs.map((cacheDir) =>
(async function () {
await fs.promises.rm(path.join(cacheDir, "lock"), { force: true });
await fs.promises.rm(path.join(cacheDir, "size"), { force: true });
})()
)
);
// # copy the contents of the .cache folders into the combined cache folder.
// cp -r **/.cache/* ${COMBINED_CACHE_DIR}/ || : # ignore missing files
await Promise.all(
cacheDirs.map((cacheDir) => copyDir(cacheDir, COMBINED_CACHE_DIR))
);
// # clean up the .cache folders
// rm -rf **/.cache/*
await Promise.all(
cacheDirs.map((cacheDir) => fs.promises.rm(cacheDir, { recursive: true }))
);
}
main();

View File

@@ -35,5 +35,3 @@ jobs:
if : ${{ github.event_name != 'pull_request' }}
shell: bash
run: codeql query compile -j0 */ql/{src,examples} --keep-going --warnings=error --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}"
env:
COMBINED_CACHE_DIR: ${{ github.workspace }}/compilation-dir

80
.github/workflows/go-tests-other-os.yml vendored Normal file
View File

@@ -0,0 +1,80 @@
name: "Go: Run Tests - Other OS"
on:
pull_request:
paths:
- "go/**"
- "!go/ql/**" # don't run other-os if only ql/ files changed
- .github/workflows/go-tests-other-os.yml
- .github/actions/**
- codeql-workspace.yml
jobs:
test-mac:
name: Test MacOS
runs-on: macos-latest
steps:
- name: Set up Go 1.19
uses: actions/setup-go@v3
with:
go-version: 1.19
id: go
- name: Check out code
uses: actions/checkout@v2
- name: Set up CodeQL CLI
uses: ./.github/actions/fetch-codeql
- name: Enable problem matchers in repository
shell: bash
run: 'find .github/problem-matchers -name \*.json -exec echo "::add-matcher::{}" \;'
- name: Build
run: |
cd go
make
- name: Cache compilation cache
id: query-cache
uses: ./.github/actions/cache-query-compilation
with:
key: go-qltest
- name: Test
run: |
cd go
make test cache="${{ steps.query-cache.outputs.cache-dir }}"
test-win:
name: Test Windows
runs-on: windows-latest-xl
steps:
- name: Set up Go 1.19
uses: actions/setup-go@v3
with:
go-version: 1.19
id: go
- name: Check out code
uses: actions/checkout@v2
- name: Set up CodeQL CLI
uses: ./.github/actions/fetch-codeql
- name: Enable problem matchers in repository
shell: bash
run: 'find .github/problem-matchers -name \*.json -exec echo "::add-matcher::{}" \;'
- name: Build
run: |
cd go
make
- name: Cache compilation cache
id: query-cache
uses: ./.github/actions/cache-query-compilation
with:
key: go-qltest
- name: Test
run: |
cd go
make test cache="${{ steps.query-cache.outputs.cache-dir }}"

View File

@@ -1,15 +1,24 @@
name: "Go: Run Tests"
on:
push:
paths:
- "go/**"
- .github/workflows/go-tests.yml
- .github/actions/**
- codeql-workspace.yml
branches:
- main
- "rc/*"
pull_request:
paths:
- "go/**"
- .github/workflows/go-tests.yml
- .github/actions/fetch-codeql/action.yml
- .github/actions/**
- codeql-workspace.yml
jobs:
test-linux:
name: Test Linux (Ubuntu)
runs-on: ubuntu-latest
runs-on: ubuntu-latest-xl
steps:
- name: Set up Go 1.19
uses: actions/setup-go@v3
@@ -32,7 +41,7 @@ jobs:
cd go
make
- name: Check that all QL and Go code is autoformatted
- name: Check that all Go code is autoformatted
run: |
cd go
make check-formatting
@@ -48,67 +57,13 @@ jobs:
name: qhelp-markdown
path: go/qhelp-out/**/*.md
- name: Test
run: |
cd go
make test
test-mac:
name: Test MacOS
runs-on: macos-latest
steps:
- name: Set up Go 1.19
uses: actions/setup-go@v3
- name: Cache compilation cache
id: query-cache
uses: ./.github/actions/cache-query-compilation
with:
go-version: 1.19
id: go
- name: Check out code
uses: actions/checkout@v2
- name: Set up CodeQL CLI
uses: ./.github/actions/fetch-codeql
- name: Enable problem matchers in repository
shell: bash
run: 'find .github/problem-matchers -name \*.json -exec echo "::add-matcher::{}" \;'
- name: Build
run: |
cd go
make
key: go-qltest
- name: Test
run: |
cd go
make test
test-win:
name: Test Windows
runs-on: windows-2019
steps:
- name: Set up Go 1.19
uses: actions/setup-go@v3
with:
go-version: 1.19
id: go
- name: Check out code
uses: actions/checkout@v2
- name: Set up CodeQL CLI
uses: ./.github/actions/fetch-codeql
- name: Enable problem matchers in repository
shell: bash
run: 'find .github/problem-matchers -name \*.json -exec echo "::add-matcher::{}" \;'
- name: Build
run: |
cd go
make
- name: Test
run: |
cd go
make test
make test cache="${{ steps.query-cache.outputs.cache-dir }}"

View File

@@ -0,0 +1,31 @@
name: Internal CI Checks
# This workflows checks if the author of a PR is a member of the CodeQL team
# and adds `ready-for-internal-ci` label to trigger the internal CI
on:
pull_request_target:
jobs:
set-label:
runs-on: ubuntu-latest
steps:
- name: Set a label to trigger internal CI checks
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
USERNAME: ${{ github.event.pull_request.user.login }}
PR_NUMBER: ${{ github.event.pull_request.number }}
LABEL: "ready-for-internal-ci"
run: |
set +eo pipefail
echo "Testing API calls"
gh api -i /repos/github/codeql
echo "Checking if user $USERNAME is a member of the CodeQL team"
gh api -H "Accept: application/vnd.github+json" /orgs/github/teams/codeql/memberships/$USERNAME > /dev/null 2>&1
if [ "$?" == 0 ]; then
echo "User $USERNAME is a member of the CodeQL team"
echo "Adding '${LABEL}' label"
gh pr edit "${PR_NUMBER}" --repo "$GITHUB_REPOSITORY" --add-label "$LABEL"
else
echo "User $USERNAME is not a member of the CodeQL team"
echo "To trigger the internal CI, a maintainer needs to add the '${LABEL}' label"
fi

View File

@@ -48,7 +48,19 @@ jobs:
run: |
brew install gnu-tar
echo "/usr/local/opt/gnu-tar/libexec/gnubin" >> $GITHUB_PATH
- name: Cache entire extractor
uses: actions/cache@v3
id: cache-extractor
with:
path: |
ruby/target/release/ruby-autobuilder
ruby/target/release/ruby-autobuilder.exe
ruby/target/release/ruby-extractor
ruby/target/release/ruby-extractor.exe
ruby/ql/lib/codeql/ruby/ast/internal/TreeSitter.qll
key: ${{ runner.os }}-ruby-extractor-${{ hashFiles('ruby/rust-toolchain.toml', 'ruby/**/Cargo.lock') }}--${{ hashFiles('ruby/**/*.rs') }}
- uses: actions/cache@v3
if: steps.cache-extractor.outputs.cache-hit != 'true'
with:
path: |
~/.cargo/registry
@@ -56,15 +68,19 @@ jobs:
ruby/target
key: ${{ runner.os }}-ruby-rust-cargo-${{ hashFiles('ruby/rust-toolchain.toml', 'ruby/**/Cargo.lock') }}
- name: Check formatting
if: steps.cache-extractor.outputs.cache-hit != 'true'
run: cargo fmt --all -- --check
- name: Build
if: steps.cache-extractor.outputs.cache-hit != 'true'
run: cargo build --verbose
- name: Run tests
if: steps.cache-extractor.outputs.cache-hit != 'true'
run: cargo test --verbose
- name: Release build
if: steps.cache-extractor.outputs.cache-hit != 'true'
run: cargo build --release
- name: Generate dbscheme
if: ${{ matrix.os == 'ubuntu-latest' }}
if: ${{ matrix.os == 'ubuntu-latest' && steps.cache-extractor.outputs.cache-hit != 'true'}}
run: target/release/ruby-generator --dbscheme ql/lib/ruby.dbscheme --library ql/lib/codeql/ruby/ast/internal/TreeSitter.qll
- uses: actions/upload-artifact@v3
if: ${{ matrix.os == 'ubuntu-latest' }}

View File

@@ -7,96 +7,76 @@ on:
- "misc/bazel/**"
- "*.bazel*"
- .github/workflows/swift.yml
- .github/actions/fetch-codeql/action.yml
- .github/actions/**
- codeql-workspace.yml
- .pre-commit-config.yaml
- "!**/*.md"
- "!**/*.qhelp"
branches:
- main
- rc/*
push:
paths:
- "swift/**"
- "misc/bazel/**"
- "*.bazel*"
- .github/workflows/swift.yml
- .github/actions/**
- codeql-workspace.yml
- "!**/*.md"
- "!**/*.qhelp"
branches:
- main
- rc/*
jobs:
changes:
runs-on: ubuntu-latest
outputs:
codegen: ${{ steps.filter.outputs.codegen }}
ql: ${{ steps.filter.outputs.ql }}
steps:
- uses: dorny/paths-filter@4512585405083f25c027a35db413c2b3b9006d50
id: filter
with:
filters: |
codegen:
- '.github/workflows/swift.yml'
- "misc/bazel/**"
- "*.bazel*"
- 'swift/actions/setup-env/**'
- '.pre-commit-config.yaml'
- 'swift/codegen/**'
- 'swift/schema.py'
- 'swift/**/*.dbscheme'
- 'swift/ql/lib/codeql/swift/elements.qll'
- 'swift/ql/lib/codeql/swift/elements/**'
- 'swift/ql/lib/codeql/swift/generated/**'
- 'swift/ql/test/extractor-tests/generated/**'
- 'swift/ql/.generated.list'
ql:
- 'github/workflows/swift.yml'
- 'swift/**/*.ql'
- 'swift/**/*.qll'
# not using a matrix as you cannot depend on a specific job in a matrix, and we want to start linux checks
# without waiting for the macOS build
build-and-test-macos:
runs-on: macos-12-xl
steps:
- uses: actions/checkout@v3
- uses: ./swift/actions/create-extractor-pack
- uses: ./swift/actions/run-quick-tests
- uses: ./swift/actions/print-unextracted
- uses: ./swift/actions/build-and-test
build-and-test-linux:
runs-on: ubuntu-20.04
runs-on: ubuntu-latest-xl
steps:
- uses: actions/checkout@v3
- uses: ./swift/actions/create-extractor-pack
- uses: ./swift/actions/run-quick-tests
- uses: ./swift/actions/print-unextracted
- uses: ./swift/actions/build-and-test
qltests-linux:
needs: build-and-test-linux
runs-on: ubuntu-latest
runs-on: ubuntu-latest-xl
steps:
- uses: actions/checkout@v3
- uses: ./swift/actions/run-ql-tests
qltests-macos:
if : ${{ github.event_name == 'pull_request' }}
needs: build-and-test-macos
runs-on: macos-12-xl
strategy:
fail-fast: false
matrix:
slice: ["1/2", "2/2"]
steps:
- uses: actions/checkout@v3
- uses: ./swift/actions/run-ql-tests
with:
flags: --slice ${{ matrix.slice }}
integration-tests-linux:
needs: build-and-test-linux
runs-on: ubuntu-latest
runs-on: ubuntu-latest-xl
steps:
- uses: actions/checkout@v3
- uses: ./swift/actions/run-integration-tests
integration-tests-macos:
if : ${{ github.event_name == 'pull_request' }}
needs: build-and-test-macos
runs-on: macos-12-xl
steps:
- uses: actions/checkout@v3
- uses: ./swift/actions/run-integration-tests
codegen:
if : ${{ github.event_name == 'pull_request' }}
runs-on: ubuntu-latest
needs: changes
if: ${{ needs.changes.outputs.codegen == 'true' }}
steps:
- uses: actions/checkout@v3
- uses: ./swift/actions/setup-env
- uses: bazelbuild/setup-bazelisk@v2
- uses: actions/setup-python@v4
with:
python-version-file: 'swift/.python-version'
- uses: pre-commit/action@v3.0.0
name: Check that python code is properly formatted
with:
@@ -114,6 +94,7 @@ jobs:
name: swift-generated-cpp-files
path: generated-cpp-files/**
database-upgrade-scripts:
if : ${{ github.event_name == 'pull_request' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

View File

@@ -12,13 +12,6 @@
# ML-powered queries
/javascript/ql/experimental/adaptivethreatmodeling/ @github/codeql-ml-powered-queries-reviewers
# Notify members of codeql-go about PRs to the shared data-flow library files
/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll @github/codeql-java @github/codeql-go
/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll @github/codeql-java @github/codeql-go
/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll @github/codeql-java @github/codeql-go
/java/ql/src/semmle/code/java/dataflow/internal/tainttracking1/TaintTrackingImpl.qll @github/codeql-java @github/codeql-go
/java/ql/src/semmle/code/java/dataflow/internal/tainttracking2/TaintTrackingImpl.qll @github/codeql-java @github/codeql-go
# CodeQL tools and associated docs
/docs/codeql/codeql-cli/ @github/codeql-cli-reviewers
/docs/codeql/codeql-for-visual-studio-code/ @github/codeql-vscode-reviewers

View File

@@ -1,5 +1,7 @@
# CodeQL
This open source repository contains the standard CodeQL libraries and queries that power [GitHub Advanced Security](https://github.com/features/security/code) and the other application security products that [GitHub](https://github.com/features/security/) makes available to its customers worldwide.
## How do I learn CodeQL and run queries?

View File

@@ -25,6 +25,7 @@ provide:
- "misc/suite-helpers/qlpack.yml"
- "ruby/extractor-pack/codeql-extractor.yml"
- "swift/extractor-pack/codeql-extractor.yml"
- "swift/integration-tests/qlpack.yml"
- "ql/extractor-pack/codeql-extractor.yml"
versionPolicies:

View File

@@ -1,5 +1,5 @@
{
"DataFlow Java/C++/C#/Python": [
"DataFlow Java/C++/C#/Go/Python/Ruby/Swift": [
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl.qll",
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl2.qll",
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl3.qll",
@@ -27,6 +27,8 @@
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll",
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll",
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplForContentDataFlow.qll",
"go/ql/lib/semmle/go/dataflow/internal/DataFlowImpl.qll",
"go/ql/lib/semmle/go/dataflow/internal/DataFlowImpl2.qll",
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl.qll",
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl2.qll",
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl3.qll",
@@ -38,17 +40,18 @@
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForPathname.qll",
"swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImpl.qll"
],
"DataFlow Java/C++/C#/Python Common": [
"DataFlow Java/C++/C#/Go/Python/Ruby/Swift Common": [
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll",
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll",
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll",
"cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll",
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll",
"go/ql/lib/semmle/go/dataflow/internal/DataFlowImplCommon.qll",
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll",
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplCommon.qll",
"swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImplCommon.qll"
],
"TaintTracking::Configuration Java/C++/C#/Python": [
"TaintTracking::Configuration Java/C++/C#/Go/Python/Ruby/Swift": [
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll",
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
@@ -62,6 +65,8 @@
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking3/TaintTrackingImpl.qll",
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking4/TaintTrackingImpl.qll",
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking5/TaintTrackingImpl.qll",
"go/ql/lib/semmle/go/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
"go/ql/lib/semmle/go/dataflow/internal/tainttracking2/TaintTrackingImpl.qll",
"java/ql/lib/semmle/code/java/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
"java/ql/lib/semmle/code/java/dataflow/internal/tainttracking2/TaintTrackingImpl.qll",
"java/ql/lib/semmle/code/java/dataflow/internal/tainttracking3/TaintTrackingImpl.qll",
@@ -72,7 +77,7 @@
"ruby/ql/lib/codeql/ruby/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
"swift/ql/lib/codeql/swift/dataflow/internal/tainttracking1/TaintTrackingImpl.qll"
],
"DataFlow Java/C++/C#/Python Consistency checks": [
"DataFlow Java/C++/C#/Python/Ruby/Swift Consistency checks": [
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplConsistency.qll",
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplConsistency.qll",
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll",
@@ -82,9 +87,10 @@
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplConsistency.qll",
"swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImplConsistency.qll"
],
"DataFlow Java/C#/Ruby/Python/Swift Flow Summaries": [
"DataFlow Java/C#/Go/Ruby/Python/Swift Flow Summaries": [
"java/ql/lib/semmle/code/java/dataflow/internal/FlowSummaryImpl.qll",
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll",
"go/ql/lib/semmle/go/dataflow/internal/FlowSummaryImpl.qll",
"ruby/ql/lib/codeql/ruby/dataflow/internal/FlowSummaryImpl.qll",
"python/ql/lib/semmle/python/dataflow/new/internal/FlowSummaryImpl.qll",
"swift/ql/lib/codeql/swift/dataflow/internal/FlowSummaryImpl.qll"
@@ -505,6 +511,7 @@
],
"AccessPathSyntax": [
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/AccessPathSyntax.qll",
"go/ql/lib/semmle/go/dataflow/internal/AccessPathSyntax.qll",
"java/ql/lib/semmle/code/java/dataflow/internal/AccessPathSyntax.qll",
"javascript/ql/lib/semmle/javascript/frameworks/data/internal/AccessPathSyntax.qll",
"ruby/ql/lib/codeql/ruby/dataflow/internal/AccessPathSyntax.qll",

View File

@@ -12,8 +12,8 @@ import IDEContextual
*
* In some cases it is preferable to modify locations (the
* `hasLocationInfo()` predicate) so that they are short, and
* non-overlapping with other locations that might be highlighted in
* the LGTM interface.
* non-overlapping with other locations that might be reported as
* code scanning alerts on GitHub.
*
* We need to give locations that may not be in the database, so
* we use `hasLocationInfo()` rather than `getLocation()`.

View File

@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node source, FlowState state) { none() }
predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes
@@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
}
ParameterPosition getPosition() { this.isParameterOf(_, result) }
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
}
private class RetNodeEx extends NodeEx {
@@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) {
)
}
/**
* Holds if flow from `p` to a return node of kind `kind` is allowed.
*
* We don't expect a parameter to return stored in itself, unless
* explicitly allowed
*/
bindingset[p, kind]
private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) {
exists(ParameterPosition pos | p.isParameterOf(_, pos) |
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
allowParameterReturnInSelfCached(p.asNode())
)
}
/**
* Holds if flow from a parameter at position `pos` inside `c` to a return node of
* kind `kind` is allowed.
*
* We don't expect a parameter to return stored in itself, unless
* explicitly allowed
*/
bindingset[c, pos, kind]
private predicate parameterFlowThroughAllowed(
DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
) {
exists(ParamNodeEx p |
p.isParameterOf(c, pos) and
parameterFlowThroughAllowed(p, kind)
)
}
private module Stage1 implements StageSig {
class Ap = Unit;
@@ -981,21 +1011,22 @@ private module Stage1 implements StageSig {
* candidate for the origin of a summary.
*/
pragma[nomagic]
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
exists(ReturnKindExt kind |
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
exists(DataFlowCallable c, ReturnKindExt kind |
throughFlowNodeCand(p, config) and
returnFlowCallableNodeCand(c, kind, config) and
p.getEnclosingCallable() = c and
exists(ap) and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
or
p.allowParameterReturnInSelf()
)
parameterFlowThroughAllowed(p, kind)
)
}
pragma[nomagic]
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
throughFlowNodeCand(ret, config) and
kind = ret.getKind()
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(ArgNodeEx arg, boolean toReturn |
@@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1(
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config
) {
viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
Stage1::revFlow(ret, config) and
not outBarrier(ret, config) and
not inBarrier(out, config)
exists(ReturnPosition pos |
viableReturnPosOutNodeCand1(call, pos, out, config) and
pos = ret.getReturnPosition() and
kind = pos.getKind() and
Stage1::revFlow(ret, config) and
not outBarrier(ret, config) and
not inBarrier(out, config)
)
}
pragma[nomagic]
@@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1(
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
pragma[nomagic]
private int branch(NodeEx n1, Configuration conf) {
result =
strictcount(NodeEx n |
flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
)
}
@@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) {
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
pragma[nomagic]
private int join(NodeEx n2, Configuration conf) {
result =
strictcount(NodeEx n |
flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
)
}
@@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) {
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
Configuration config
) {
flowOutOfCallNodeCand1(call, ret, out, config) and
flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and
exists(int b, int j |
b = branch(ret, config) and
j = join(out, config) and
b = branch(ret, pragma[only_bind_into](config)) and
j = join(out, pragma[only_bind_into](config)) and
if b.minimum(j) <= config.fieldFlowBranchLimit()
then allowsFieldFlow = true
else allowsFieldFlow = false
@@ -1136,10 +1174,10 @@ pragma[nomagic]
private predicate flowIntoCallNodeCand1(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
flowIntoCallNodeCand1(call, arg, p, config) and
flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and
exists(int b, int j |
b = branch(arg, config) and
j = join(p, config) and
b = branch(arg, pragma[only_bind_into](config)) and
j = join(p, pragma[only_bind_into](config)) and
if b.minimum(j) <= config.fieldFlowBranchLimit()
then allowsFieldFlow = true
else allowsFieldFlow = false
@@ -1156,7 +1194,9 @@ private signature module StageSig {
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
predicate storeStepCand(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
@@ -1222,7 +1262,8 @@ private module MkStage<StageSig PrevStage> {
);
predicate flowOutOfCall(
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
Configuration config
);
predicate flowIntoCall(
@@ -1247,14 +1288,14 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate flowThroughOutOfCall(
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
Configuration config
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
NodeEx out, boolean allowsFieldFlow, Configuration config
) {
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
pragma[only_bind_into](config)) and
matchesCall(ccc, call)
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
matchesCall(ccc, call) and
c = ret.getEnclosingCallable()
}
/**
@@ -1262,29 +1303,32 @@ private module MkStage<StageSig PrevStage> {
* configuration `config`.
*
* The call context `cc` records whether the node is reached through an
* argument in a call, and if so, `argAp` records the access path of that
* argument.
* argument in a call, and if so, `summaryCtx` and `argAp` record the
* corresponding parameter position and access path of that argument, respectively.
*/
pragma[nomagic]
additional predicate fwdFlow(
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, Configuration config
) {
fwdFlow0(node, state, cc, argAp, ap, config) and
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
filter(node, state, ap, config)
}
pragma[nomagic]
private predicate fwdFlow0(
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, Configuration config
) {
sourceNode(node, state, config) and
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
summaryCtx = TParameterPositionNone() and
ap = getApNil(node)
or
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
fwdFlow(mid, state0, cc, argAp, ap0, config) and
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
@@ -1295,65 +1339,82 @@ private module MkStage<StageSig PrevStage> {
)
or
exists(NodeEx mid |
fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone()
)
or
exists(NodeEx mid, ApNil nil |
fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and
fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and
additionalJumpStateStep(mid, state0, node, state, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
or
// store
exists(TypedContent tc, Ap ap0 |
fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
ap = apCons(tc, ap0)
)
or
// read
exists(Ap ap0, Content c |
fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
fwdFlowConsCand(ap0, c, ap, config)
)
or
// flow into a callable
exists(ApApprox apa |
fwdFlowIn(_, node, state, _, cc, _, ap, config) and
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
apa = getApprox(ap) and
if PrevStage::parameterMayFlowThrough(node, _, apa, config)
then argAp = apSome(ap)
else argAp = apNone()
if PrevStage::parameterMayFlowThrough(node, apa, config)
then (
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
argAp = apSome(ap)
) else (
summaryCtx = TParameterPositionNone() and argAp = apNone()
)
)
or
// flow out of a callable
fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
exists(
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
DataFlowCallable inner
|
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and
flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and
inner = ret.getEnclosingCallable() and
cc = getCallContextReturn(inner, call, innercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
or
exists(DataFlowCall call, Ap argAp0 |
fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
fwdFlowIsEntered(call, cc, argAp, argAp0, config)
// flow through a callable
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
)
}
pragma[nomagic]
private predicate fwdFlowStore(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
Configuration config
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
fwdFlow(node1, state, cc, argAp, ap1, config) and
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
@@ -1366,7 +1427,7 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
exists(TypedContent tc |
fwdFlowStore(_, tail, tc, _, _, _, _, config) and
fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and
tc.getContent() = c and
cons = apCons(tc, tail)
)
@@ -1374,21 +1435,21 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate fwdFlowRead(
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
Configuration config
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
fwdFlow(node1, state, cc, argAp, ap, config) and
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::readStepCand(node1, c, node2, config) and
getHeadContent(ap) = c
}
pragma[nomagic]
private predicate fwdFlowIn(
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp,
Ap ap, Configuration config
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc,
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
) {
exists(ArgNodeEx arg, boolean allowsFieldFlow |
fwdFlow(arg, state, outercc, argAp, ap, config) and
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
@@ -1396,29 +1457,19 @@ private module MkStage<StageSig PrevStage> {
}
pragma[nomagic]
private predicate fwdFlowOutNotFromArg(
NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
private predicate fwdFlowOutFromArg(
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
Configuration config
) {
exists(
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
DataFlowCallable inner
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
fwdFlow(ret, state, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
inner = ret.getEnclosingCallable() and
ccOut = getCallContextReturn(inner, call, innercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate fwdFlowOutFromArg(
DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config
) {
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
)
}
@@ -1428,11 +1479,13 @@ private module MkStage<StageSig PrevStage> {
*/
pragma[nomagic]
private predicate fwdFlowIsEntered(
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
ParameterPosition pos, Ap ap, Configuration config
) {
exists(ParamNodeEx p |
fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
exists(ParamNodeEx param |
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
pos = param.getPosition()
)
}
@@ -1440,27 +1493,40 @@ private module MkStage<StageSig PrevStage> {
private predicate storeStepFwd(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
) {
fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and
ap2 = apCons(tc, ap1) and
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config)
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config)
}
private predicate readStepFwd(
NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
) {
fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and
fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and
fwdFlowConsCand(ap1, c, ap2, config)
}
pragma[nomagic]
private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
private predicate returnFlowsThrough0(
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
) {
exists(boolean allowsFieldFlow |
fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
pragma[only_bind_into](config))
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate returnFlowsThrough(
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp,
Ap ap, Configuration config
) {
exists(DataFlowCallable c, ParameterPosition ppos |
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
p.isParameterOf(c, ppos) and
parameterFlowThroughAllowed(p, kind)
)
}
@@ -1468,118 +1534,130 @@ private module MkStage<StageSig PrevStage> {
private predicate flowThroughIntoCall(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
}
pragma[nomagic]
private predicate returnNodeMayFlowThrough(
RetNodeEx ret, FlowState state, Ap ap, Configuration config
) {
fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
exists(Ap argAp |
flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
pragma[only_bind_into](config)) and
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
pragma[only_bind_into](config))
)
}
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
*
* The Boolean `toReturn` records whether the node must be returned from the
* enclosing callable in order to reach a sink, and if so, `returnAp` records
* the access path of the returned value.
* The parameter `returnCtx` records whether (and how) the node must be returned
* from the enclosing callable in order to reach a sink, and if so, `returnAp`
* records the access path of the returned value.
*/
pragma[nomagic]
additional predicate revFlow(
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
Configuration config
) {
revFlow0(node, state, toReturn, returnAp, ap, config) and
fwdFlow(node, state, _, _, ap, config)
revFlow0(node, state, returnCtx, returnAp, ap, config) and
fwdFlow(node, state, _, _, _, ap, config)
}
pragma[nomagic]
private predicate revFlow0(
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
Configuration config
) {
fwdFlow(node, state, _, _, ap, config) and
fwdFlow(node, state, _, _, _, ap, config) and
sinkNode(node, state, config) and
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
(
if hasSinkCallCtx(config)
then returnCtx = TReturnCtxNoFlowThrough()
else returnCtx = TReturnCtxNone()
) and
returnAp = apNone() and
ap instanceof ApNil
or
exists(NodeEx mid, FlowState state0 |
localStep(node, state, mid, state0, true, _, config, _) and
revFlow(mid, state0, toReturn, returnAp, ap, config)
revFlow(mid, state0, returnCtx, returnAp, ap, config)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and
revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, state, _, _, ap, config) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone()
)
or
exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone() and
ap instanceof ApNil
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStateStep(node, state, mid, state0, config) and
revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil,
pragma[only_bind_into](config)) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone() and
ap instanceof ApNil
)
or
// store
exists(Ap ap0, Content c |
revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and
revFlowConsCand(ap0, c, ap, config)
)
or
// read
exists(NodeEx mid, Ap ap0 |
revFlow(mid, state, toReturn, returnAp, ap0, config) and
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
readStepFwd(node, ap, _, mid, ap0, config)
)
or
// flow into a callable
revFlowInNotToReturn(node, state, returnAp, ap, config) and
toReturn = false
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
flowIntoCall(_, node, p, allowsFieldFlow, config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
returnCtx = TReturnCtxNone()
)
or
exists(DataFlowCall call, Ap returnAp0 |
revFlowInToReturn(call, node, state, returnAp0, ap, config) and
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
// flow through a callable
exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 |
revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
or
// flow out of a callable
revFlowOut(_, node, state, _, _, ap, config) and
toReturn = true and
if returnNodeMayFlowThrough(node, state, ap, config)
then returnAp = apSome(ap)
else returnAp = apNone()
exists(ReturnKindExt kind |
revFlowOut(_, node, kind, state, _, _, ap, config) and
if returnFlowsThrough(node, kind, state, _, _, _, ap, config)
then (
returnCtx = TReturnCtxMaybeFlowThrough(kind) and
returnAp = apSome(ap)
) else (
returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone()
)
)
}
pragma[nomagic]
private predicate revFlowStore(
Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid,
boolean toReturn, ApOption returnAp, Configuration config
ReturnCtx returnCtx, ApOption returnAp, Configuration config
) {
revFlow(mid, state, toReturn, returnAp, ap0, config) and
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
storeStepFwd(node, ap, tc, mid, ap0, config) and
tc.getContent() = c
}
@@ -1599,35 +1677,27 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate revFlowOut(
DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap,
Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx,
ApOption returnAp, Ap ap, Configuration config
) {
exists(NodeEx out, boolean allowsFieldFlow |
revFlow(out, state, toReturn, returnAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate revFlowInNotToReturn(
ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, false, returnAp, ap, config) and
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
revFlow(out, state, returnCtx, returnAp, ap, config) and
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate revFlowInToReturn(
DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, true, apSome(returnAp), ap, config) and
revFlow(pragma[only_bind_into](p), state,
TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
parameterFlowThroughAllowed(p, kind)
)
}
@@ -1638,11 +1708,12 @@ private module MkStage<StageSig PrevStage> {
*/
pragma[nomagic]
private predicate revFlowIsReturned(
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap,
Configuration config
) {
exists(RetNodeEx ret, FlowState state, CcCall ccc |
revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and
fwdFlow(ret, state, ccc, apSome(_), ap, config) and
revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and
returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and
matchesCall(ccc, call)
)
}
@@ -1713,40 +1784,39 @@ private module MkStage<StageSig PrevStage> {
validAp(ap, config)
}
pragma[noinline]
private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
pragma[nomagic]
private predicate parameterFlowsThroughRev(
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
) {
revFlow(p, _, true, apSome(ap0), ap, config) and
c = p.getEnclosingCallable()
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
parameterFlowThroughAllowed(p, kind)
}
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
parameterFlow(p, ap, ap0, c, config) and
c = ret.getEnclosingCallable() and
revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_),
pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and
fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and
kind = ret.getKind() and
p.getPosition() = pos and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
p.allowParameterReturnInSelf()
)
pragma[nomagic]
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
exists(RetNodeEx ret, ReturnKindExt kind |
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
parameterFlowsThroughRev(p, ap, kind, config)
)
}
pragma[nomagic]
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
exists(ParamNodeEx p, Ap ap |
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
parameterFlowsThroughRev(p, ap, kind, config)
)
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(
Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
revFlow(arg, state, toReturn, returnAp, ap, config) and
revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
revFlow(arg, state, returnCtx, returnAp, ap, config) and
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
}
@@ -1754,14 +1824,13 @@ private module MkStage<StageSig PrevStage> {
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) {
fwd = true and
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and
states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and
tuples =
count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
fwdFlow(n, state, cc, argAp, ap, config)
)
count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config))
or
fwd = false and
nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
@@ -1769,8 +1838,8 @@ private module MkStage<StageSig PrevStage> {
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
states = count(FlowState state | revFlow(_, state, _, _, _, config)) and
tuples =
count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap |
revFlow(n, state, b, retAp, ap, config)
count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap |
revFlow(n, state, returnCtx, retAp, ap, config)
)
}
/* End: Stage logic. */
@@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
exists(lcc)
}
predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
predicate flowIntoCall = flowIntoCallNodeCand1/5;
@@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig {
pragma[nomagic]
private predicate flowOutOfCallNodeCand2(
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
Stage2::revFlowAlias(node1, pragma[only_bind_into](config))
}
@@ -2021,8 +2091,8 @@ private module LocalFlowBigStep {
exists(NodeEx next | Stage2::revFlow(next, state, config) |
jumpStep(node, next, config) or
additionalJumpStep(node, next, config) or
flowIntoCallNodeCand1(_, node, next, config) or
flowOutOfCallNodeCand1(_, node, next, config) or
flowIntoCallNodeCand2(_, node, next, _, config) or
flowOutOfCallNodeCand2(_, node, _, next, _, config) or
Stage2::storeStepCand(node, _, _, next, _, config) or
Stage2::readStepCand(node, _, next, config)
)
@@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
}
predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
predicate flowIntoCall = flowIntoCallNodeCand2/5;
@@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx(
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
) {
exists(AccessPathFront apf |
Stage3::revFlow(node, state, true, _, apf, config) and
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
config)
)
}
@@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
pragma[nomagic]
predicate flowOutOfCall(
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
exists(FlowState state |
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
pragma[only_bind_into](config))
@@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) {
pragma[nomagic]
private predicate nodeMayUseSummary0(
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa,
Configuration config
) {
exists(AccessPathApprox apa0 |
Stage4::parameterMayFlowThrough(_, c, _, _) and
Stage4::revFlow(n, state, true, _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
n.getEnclosingCallable() = c
c = n.getEnclosingCallable() and
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
TAccessPathApproxSome(apa), apa0, config)
)
}
@@ -2522,9 +2595,10 @@ pragma[nomagic]
private predicate nodeMayUseSummary(
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
) {
exists(DataFlowCallable c |
Stage4::parameterMayFlowThrough(_, c, apa, config) and
nodeMayUseSummary0(n, c, state, apa, config)
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
Stage4::parameterMayFlowThrough(p, apa, config) and
nodeMayUseSummary0(n, c, pos, state, apa, config) and
p.isParameterOf(c, pos)
)
}
@@ -2532,7 +2606,7 @@ private newtype TSummaryCtx =
TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
Stage4::revFlow(p, state, _, config)
)
}
@@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough(
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
AccessPathApprox apa, Configuration config
) {
exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos |
exists(PathNodeMid mid, RetNodeEx ret |
pathNode(mid, ret, state, cc, sc, ap, config, _) and
kind = ret.getKind() and
apa = ap.getApprox() and
pos = sc.getParameterPos() and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
sc.getParamNode().allowParameterReturnInSelf()
)
parameterFlowThroughAllowed(sc.getParamNode(), kind)
)
}

View File

@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node source, FlowState state) { none() }
predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes
@@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
}
ParameterPosition getPosition() { this.isParameterOf(_, result) }
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
}
private class RetNodeEx extends NodeEx {
@@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) {
)
}
/**
* Holds if flow from `p` to a return node of kind `kind` is allowed.
*
* We don't expect a parameter to return stored in itself, unless
* explicitly allowed
*/
bindingset[p, kind]
private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) {
exists(ParameterPosition pos | p.isParameterOf(_, pos) |
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
allowParameterReturnInSelfCached(p.asNode())
)
}
/**
* Holds if flow from a parameter at position `pos` inside `c` to a return node of
* kind `kind` is allowed.
*
* We don't expect a parameter to return stored in itself, unless
* explicitly allowed
*/
bindingset[c, pos, kind]
private predicate parameterFlowThroughAllowed(
DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
) {
exists(ParamNodeEx p |
p.isParameterOf(c, pos) and
parameterFlowThroughAllowed(p, kind)
)
}
private module Stage1 implements StageSig {
class Ap = Unit;
@@ -981,21 +1011,22 @@ private module Stage1 implements StageSig {
* candidate for the origin of a summary.
*/
pragma[nomagic]
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
exists(ReturnKindExt kind |
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
exists(DataFlowCallable c, ReturnKindExt kind |
throughFlowNodeCand(p, config) and
returnFlowCallableNodeCand(c, kind, config) and
p.getEnclosingCallable() = c and
exists(ap) and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
or
p.allowParameterReturnInSelf()
)
parameterFlowThroughAllowed(p, kind)
)
}
pragma[nomagic]
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
throughFlowNodeCand(ret, config) and
kind = ret.getKind()
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(ArgNodeEx arg, boolean toReturn |
@@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1(
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config
) {
viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
Stage1::revFlow(ret, config) and
not outBarrier(ret, config) and
not inBarrier(out, config)
exists(ReturnPosition pos |
viableReturnPosOutNodeCand1(call, pos, out, config) and
pos = ret.getReturnPosition() and
kind = pos.getKind() and
Stage1::revFlow(ret, config) and
not outBarrier(ret, config) and
not inBarrier(out, config)
)
}
pragma[nomagic]
@@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1(
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
pragma[nomagic]
private int branch(NodeEx n1, Configuration conf) {
result =
strictcount(NodeEx n |
flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
)
}
@@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) {
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
pragma[nomagic]
private int join(NodeEx n2, Configuration conf) {
result =
strictcount(NodeEx n |
flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
)
}
@@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) {
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
Configuration config
) {
flowOutOfCallNodeCand1(call, ret, out, config) and
flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and
exists(int b, int j |
b = branch(ret, config) and
j = join(out, config) and
b = branch(ret, pragma[only_bind_into](config)) and
j = join(out, pragma[only_bind_into](config)) and
if b.minimum(j) <= config.fieldFlowBranchLimit()
then allowsFieldFlow = true
else allowsFieldFlow = false
@@ -1136,10 +1174,10 @@ pragma[nomagic]
private predicate flowIntoCallNodeCand1(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
flowIntoCallNodeCand1(call, arg, p, config) and
flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and
exists(int b, int j |
b = branch(arg, config) and
j = join(p, config) and
b = branch(arg, pragma[only_bind_into](config)) and
j = join(p, pragma[only_bind_into](config)) and
if b.minimum(j) <= config.fieldFlowBranchLimit()
then allowsFieldFlow = true
else allowsFieldFlow = false
@@ -1156,7 +1194,9 @@ private signature module StageSig {
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
predicate storeStepCand(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
@@ -1222,7 +1262,8 @@ private module MkStage<StageSig PrevStage> {
);
predicate flowOutOfCall(
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
Configuration config
);
predicate flowIntoCall(
@@ -1247,14 +1288,14 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate flowThroughOutOfCall(
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
Configuration config
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
NodeEx out, boolean allowsFieldFlow, Configuration config
) {
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
pragma[only_bind_into](config)) and
matchesCall(ccc, call)
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
matchesCall(ccc, call) and
c = ret.getEnclosingCallable()
}
/**
@@ -1262,29 +1303,32 @@ private module MkStage<StageSig PrevStage> {
* configuration `config`.
*
* The call context `cc` records whether the node is reached through an
* argument in a call, and if so, `argAp` records the access path of that
* argument.
* argument in a call, and if so, `summaryCtx` and `argAp` record the
* corresponding parameter position and access path of that argument, respectively.
*/
pragma[nomagic]
additional predicate fwdFlow(
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, Configuration config
) {
fwdFlow0(node, state, cc, argAp, ap, config) and
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
filter(node, state, ap, config)
}
pragma[nomagic]
private predicate fwdFlow0(
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, Configuration config
) {
sourceNode(node, state, config) and
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
summaryCtx = TParameterPositionNone() and
ap = getApNil(node)
or
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
fwdFlow(mid, state0, cc, argAp, ap0, config) and
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
@@ -1295,65 +1339,82 @@ private module MkStage<StageSig PrevStage> {
)
or
exists(NodeEx mid |
fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone()
)
or
exists(NodeEx mid, ApNil nil |
fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and
fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and
additionalJumpStateStep(mid, state0, node, state, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
or
// store
exists(TypedContent tc, Ap ap0 |
fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
ap = apCons(tc, ap0)
)
or
// read
exists(Ap ap0, Content c |
fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
fwdFlowConsCand(ap0, c, ap, config)
)
or
// flow into a callable
exists(ApApprox apa |
fwdFlowIn(_, node, state, _, cc, _, ap, config) and
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
apa = getApprox(ap) and
if PrevStage::parameterMayFlowThrough(node, _, apa, config)
then argAp = apSome(ap)
else argAp = apNone()
if PrevStage::parameterMayFlowThrough(node, apa, config)
then (
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
argAp = apSome(ap)
) else (
summaryCtx = TParameterPositionNone() and argAp = apNone()
)
)
or
// flow out of a callable
fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
exists(
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
DataFlowCallable inner
|
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and
flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and
inner = ret.getEnclosingCallable() and
cc = getCallContextReturn(inner, call, innercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
or
exists(DataFlowCall call, Ap argAp0 |
fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
fwdFlowIsEntered(call, cc, argAp, argAp0, config)
// flow through a callable
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
)
}
pragma[nomagic]
private predicate fwdFlowStore(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
Configuration config
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
fwdFlow(node1, state, cc, argAp, ap1, config) and
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
@@ -1366,7 +1427,7 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
exists(TypedContent tc |
fwdFlowStore(_, tail, tc, _, _, _, _, config) and
fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and
tc.getContent() = c and
cons = apCons(tc, tail)
)
@@ -1374,21 +1435,21 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate fwdFlowRead(
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
Configuration config
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
fwdFlow(node1, state, cc, argAp, ap, config) and
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::readStepCand(node1, c, node2, config) and
getHeadContent(ap) = c
}
pragma[nomagic]
private predicate fwdFlowIn(
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp,
Ap ap, Configuration config
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc,
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
) {
exists(ArgNodeEx arg, boolean allowsFieldFlow |
fwdFlow(arg, state, outercc, argAp, ap, config) and
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
@@ -1396,29 +1457,19 @@ private module MkStage<StageSig PrevStage> {
}
pragma[nomagic]
private predicate fwdFlowOutNotFromArg(
NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
private predicate fwdFlowOutFromArg(
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
Configuration config
) {
exists(
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
DataFlowCallable inner
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
fwdFlow(ret, state, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
inner = ret.getEnclosingCallable() and
ccOut = getCallContextReturn(inner, call, innercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate fwdFlowOutFromArg(
DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config
) {
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
)
}
@@ -1428,11 +1479,13 @@ private module MkStage<StageSig PrevStage> {
*/
pragma[nomagic]
private predicate fwdFlowIsEntered(
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
ParameterPosition pos, Ap ap, Configuration config
) {
exists(ParamNodeEx p |
fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
exists(ParamNodeEx param |
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
pos = param.getPosition()
)
}
@@ -1440,27 +1493,40 @@ private module MkStage<StageSig PrevStage> {
private predicate storeStepFwd(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
) {
fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and
ap2 = apCons(tc, ap1) and
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config)
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config)
}
private predicate readStepFwd(
NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
) {
fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and
fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and
fwdFlowConsCand(ap1, c, ap2, config)
}
pragma[nomagic]
private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
private predicate returnFlowsThrough0(
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
) {
exists(boolean allowsFieldFlow |
fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
pragma[only_bind_into](config))
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate returnFlowsThrough(
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp,
Ap ap, Configuration config
) {
exists(DataFlowCallable c, ParameterPosition ppos |
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
p.isParameterOf(c, ppos) and
parameterFlowThroughAllowed(p, kind)
)
}
@@ -1468,118 +1534,130 @@ private module MkStage<StageSig PrevStage> {
private predicate flowThroughIntoCall(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
}
pragma[nomagic]
private predicate returnNodeMayFlowThrough(
RetNodeEx ret, FlowState state, Ap ap, Configuration config
) {
fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
exists(Ap argAp |
flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
pragma[only_bind_into](config)) and
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
pragma[only_bind_into](config))
)
}
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
*
* The Boolean `toReturn` records whether the node must be returned from the
* enclosing callable in order to reach a sink, and if so, `returnAp` records
* the access path of the returned value.
* The parameter `returnCtx` records whether (and how) the node must be returned
* from the enclosing callable in order to reach a sink, and if so, `returnAp`
* records the access path of the returned value.
*/
pragma[nomagic]
additional predicate revFlow(
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
Configuration config
) {
revFlow0(node, state, toReturn, returnAp, ap, config) and
fwdFlow(node, state, _, _, ap, config)
revFlow0(node, state, returnCtx, returnAp, ap, config) and
fwdFlow(node, state, _, _, _, ap, config)
}
pragma[nomagic]
private predicate revFlow0(
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
Configuration config
) {
fwdFlow(node, state, _, _, ap, config) and
fwdFlow(node, state, _, _, _, ap, config) and
sinkNode(node, state, config) and
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
(
if hasSinkCallCtx(config)
then returnCtx = TReturnCtxNoFlowThrough()
else returnCtx = TReturnCtxNone()
) and
returnAp = apNone() and
ap instanceof ApNil
or
exists(NodeEx mid, FlowState state0 |
localStep(node, state, mid, state0, true, _, config, _) and
revFlow(mid, state0, toReturn, returnAp, ap, config)
revFlow(mid, state0, returnCtx, returnAp, ap, config)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and
revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, state, _, _, ap, config) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone()
)
or
exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone() and
ap instanceof ApNil
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStateStep(node, state, mid, state0, config) and
revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil,
pragma[only_bind_into](config)) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone() and
ap instanceof ApNil
)
or
// store
exists(Ap ap0, Content c |
revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and
revFlowConsCand(ap0, c, ap, config)
)
or
// read
exists(NodeEx mid, Ap ap0 |
revFlow(mid, state, toReturn, returnAp, ap0, config) and
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
readStepFwd(node, ap, _, mid, ap0, config)
)
or
// flow into a callable
revFlowInNotToReturn(node, state, returnAp, ap, config) and
toReturn = false
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
flowIntoCall(_, node, p, allowsFieldFlow, config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
returnCtx = TReturnCtxNone()
)
or
exists(DataFlowCall call, Ap returnAp0 |
revFlowInToReturn(call, node, state, returnAp0, ap, config) and
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
// flow through a callable
exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 |
revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
or
// flow out of a callable
revFlowOut(_, node, state, _, _, ap, config) and
toReturn = true and
if returnNodeMayFlowThrough(node, state, ap, config)
then returnAp = apSome(ap)
else returnAp = apNone()
exists(ReturnKindExt kind |
revFlowOut(_, node, kind, state, _, _, ap, config) and
if returnFlowsThrough(node, kind, state, _, _, _, ap, config)
then (
returnCtx = TReturnCtxMaybeFlowThrough(kind) and
returnAp = apSome(ap)
) else (
returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone()
)
)
}
pragma[nomagic]
private predicate revFlowStore(
Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid,
boolean toReturn, ApOption returnAp, Configuration config
ReturnCtx returnCtx, ApOption returnAp, Configuration config
) {
revFlow(mid, state, toReturn, returnAp, ap0, config) and
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
storeStepFwd(node, ap, tc, mid, ap0, config) and
tc.getContent() = c
}
@@ -1599,35 +1677,27 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate revFlowOut(
DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap,
Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx,
ApOption returnAp, Ap ap, Configuration config
) {
exists(NodeEx out, boolean allowsFieldFlow |
revFlow(out, state, toReturn, returnAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate revFlowInNotToReturn(
ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, false, returnAp, ap, config) and
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
revFlow(out, state, returnCtx, returnAp, ap, config) and
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate revFlowInToReturn(
DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, true, apSome(returnAp), ap, config) and
revFlow(pragma[only_bind_into](p), state,
TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
parameterFlowThroughAllowed(p, kind)
)
}
@@ -1638,11 +1708,12 @@ private module MkStage<StageSig PrevStage> {
*/
pragma[nomagic]
private predicate revFlowIsReturned(
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap,
Configuration config
) {
exists(RetNodeEx ret, FlowState state, CcCall ccc |
revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and
fwdFlow(ret, state, ccc, apSome(_), ap, config) and
revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and
returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and
matchesCall(ccc, call)
)
}
@@ -1713,40 +1784,39 @@ private module MkStage<StageSig PrevStage> {
validAp(ap, config)
}
pragma[noinline]
private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
pragma[nomagic]
private predicate parameterFlowsThroughRev(
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
) {
revFlow(p, _, true, apSome(ap0), ap, config) and
c = p.getEnclosingCallable()
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
parameterFlowThroughAllowed(p, kind)
}
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
parameterFlow(p, ap, ap0, c, config) and
c = ret.getEnclosingCallable() and
revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_),
pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and
fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and
kind = ret.getKind() and
p.getPosition() = pos and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
p.allowParameterReturnInSelf()
)
pragma[nomagic]
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
exists(RetNodeEx ret, ReturnKindExt kind |
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
parameterFlowsThroughRev(p, ap, kind, config)
)
}
pragma[nomagic]
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
exists(ParamNodeEx p, Ap ap |
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
parameterFlowsThroughRev(p, ap, kind, config)
)
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(
Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
revFlow(arg, state, toReturn, returnAp, ap, config) and
revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
revFlow(arg, state, returnCtx, returnAp, ap, config) and
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
}
@@ -1754,14 +1824,13 @@ private module MkStage<StageSig PrevStage> {
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) {
fwd = true and
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and
states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and
tuples =
count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
fwdFlow(n, state, cc, argAp, ap, config)
)
count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config))
or
fwd = false and
nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
@@ -1769,8 +1838,8 @@ private module MkStage<StageSig PrevStage> {
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
states = count(FlowState state | revFlow(_, state, _, _, _, config)) and
tuples =
count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap |
revFlow(n, state, b, retAp, ap, config)
count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap |
revFlow(n, state, returnCtx, retAp, ap, config)
)
}
/* End: Stage logic. */
@@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
exists(lcc)
}
predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
predicate flowIntoCall = flowIntoCallNodeCand1/5;
@@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig {
pragma[nomagic]
private predicate flowOutOfCallNodeCand2(
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
Stage2::revFlowAlias(node1, pragma[only_bind_into](config))
}
@@ -2021,8 +2091,8 @@ private module LocalFlowBigStep {
exists(NodeEx next | Stage2::revFlow(next, state, config) |
jumpStep(node, next, config) or
additionalJumpStep(node, next, config) or
flowIntoCallNodeCand1(_, node, next, config) or
flowOutOfCallNodeCand1(_, node, next, config) or
flowIntoCallNodeCand2(_, node, next, _, config) or
flowOutOfCallNodeCand2(_, node, _, next, _, config) or
Stage2::storeStepCand(node, _, _, next, _, config) or
Stage2::readStepCand(node, _, next, config)
)
@@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
}
predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
predicate flowIntoCall = flowIntoCallNodeCand2/5;
@@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx(
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
) {
exists(AccessPathFront apf |
Stage3::revFlow(node, state, true, _, apf, config) and
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
config)
)
}
@@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
pragma[nomagic]
predicate flowOutOfCall(
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
exists(FlowState state |
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
pragma[only_bind_into](config))
@@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) {
pragma[nomagic]
private predicate nodeMayUseSummary0(
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa,
Configuration config
) {
exists(AccessPathApprox apa0 |
Stage4::parameterMayFlowThrough(_, c, _, _) and
Stage4::revFlow(n, state, true, _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
n.getEnclosingCallable() = c
c = n.getEnclosingCallable() and
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
TAccessPathApproxSome(apa), apa0, config)
)
}
@@ -2522,9 +2595,10 @@ pragma[nomagic]
private predicate nodeMayUseSummary(
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
) {
exists(DataFlowCallable c |
Stage4::parameterMayFlowThrough(_, c, apa, config) and
nodeMayUseSummary0(n, c, state, apa, config)
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
Stage4::parameterMayFlowThrough(p, apa, config) and
nodeMayUseSummary0(n, c, pos, state, apa, config) and
p.isParameterOf(c, pos)
)
}
@@ -2532,7 +2606,7 @@ private newtype TSummaryCtx =
TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
Stage4::revFlow(p, state, _, config)
)
}
@@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough(
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
AccessPathApprox apa, Configuration config
) {
exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos |
exists(PathNodeMid mid, RetNodeEx ret |
pathNode(mid, ret, state, cc, sc, ap, config, _) and
kind = ret.getKind() and
apa = ap.getApprox() and
pos = sc.getParameterPos() and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
sc.getParamNode().allowParameterReturnInSelf()
)
parameterFlowThroughAllowed(sc.getParamNode(), kind)
)
}

View File

@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node source, FlowState state) { none() }
predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes
@@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
}
ParameterPosition getPosition() { this.isParameterOf(_, result) }
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
}
private class RetNodeEx extends NodeEx {
@@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) {
)
}
/**
* Holds if flow from `p` to a return node of kind `kind` is allowed.
*
* We don't expect a parameter to return stored in itself, unless
* explicitly allowed
*/
bindingset[p, kind]
private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) {
exists(ParameterPosition pos | p.isParameterOf(_, pos) |
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
allowParameterReturnInSelfCached(p.asNode())
)
}
/**
* Holds if flow from a parameter at position `pos` inside `c` to a return node of
* kind `kind` is allowed.
*
* We don't expect a parameter to return stored in itself, unless
* explicitly allowed
*/
bindingset[c, pos, kind]
private predicate parameterFlowThroughAllowed(
DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
) {
exists(ParamNodeEx p |
p.isParameterOf(c, pos) and
parameterFlowThroughAllowed(p, kind)
)
}
private module Stage1 implements StageSig {
class Ap = Unit;
@@ -981,21 +1011,22 @@ private module Stage1 implements StageSig {
* candidate for the origin of a summary.
*/
pragma[nomagic]
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
exists(ReturnKindExt kind |
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
exists(DataFlowCallable c, ReturnKindExt kind |
throughFlowNodeCand(p, config) and
returnFlowCallableNodeCand(c, kind, config) and
p.getEnclosingCallable() = c and
exists(ap) and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
or
p.allowParameterReturnInSelf()
)
parameterFlowThroughAllowed(p, kind)
)
}
pragma[nomagic]
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
throughFlowNodeCand(ret, config) and
kind = ret.getKind()
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(ArgNodeEx arg, boolean toReturn |
@@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1(
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config
) {
viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
Stage1::revFlow(ret, config) and
not outBarrier(ret, config) and
not inBarrier(out, config)
exists(ReturnPosition pos |
viableReturnPosOutNodeCand1(call, pos, out, config) and
pos = ret.getReturnPosition() and
kind = pos.getKind() and
Stage1::revFlow(ret, config) and
not outBarrier(ret, config) and
not inBarrier(out, config)
)
}
pragma[nomagic]
@@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1(
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
pragma[nomagic]
private int branch(NodeEx n1, Configuration conf) {
result =
strictcount(NodeEx n |
flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
)
}
@@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) {
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
pragma[nomagic]
private int join(NodeEx n2, Configuration conf) {
result =
strictcount(NodeEx n |
flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
)
}
@@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) {
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
Configuration config
) {
flowOutOfCallNodeCand1(call, ret, out, config) and
flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and
exists(int b, int j |
b = branch(ret, config) and
j = join(out, config) and
b = branch(ret, pragma[only_bind_into](config)) and
j = join(out, pragma[only_bind_into](config)) and
if b.minimum(j) <= config.fieldFlowBranchLimit()
then allowsFieldFlow = true
else allowsFieldFlow = false
@@ -1136,10 +1174,10 @@ pragma[nomagic]
private predicate flowIntoCallNodeCand1(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
flowIntoCallNodeCand1(call, arg, p, config) and
flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and
exists(int b, int j |
b = branch(arg, config) and
j = join(p, config) and
b = branch(arg, pragma[only_bind_into](config)) and
j = join(p, pragma[only_bind_into](config)) and
if b.minimum(j) <= config.fieldFlowBranchLimit()
then allowsFieldFlow = true
else allowsFieldFlow = false
@@ -1156,7 +1194,9 @@ private signature module StageSig {
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
predicate storeStepCand(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
@@ -1222,7 +1262,8 @@ private module MkStage<StageSig PrevStage> {
);
predicate flowOutOfCall(
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
Configuration config
);
predicate flowIntoCall(
@@ -1247,14 +1288,14 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate flowThroughOutOfCall(
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
Configuration config
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
NodeEx out, boolean allowsFieldFlow, Configuration config
) {
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
pragma[only_bind_into](config)) and
matchesCall(ccc, call)
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
matchesCall(ccc, call) and
c = ret.getEnclosingCallable()
}
/**
@@ -1262,29 +1303,32 @@ private module MkStage<StageSig PrevStage> {
* configuration `config`.
*
* The call context `cc` records whether the node is reached through an
* argument in a call, and if so, `argAp` records the access path of that
* argument.
* argument in a call, and if so, `summaryCtx` and `argAp` record the
* corresponding parameter position and access path of that argument, respectively.
*/
pragma[nomagic]
additional predicate fwdFlow(
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, Configuration config
) {
fwdFlow0(node, state, cc, argAp, ap, config) and
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
filter(node, state, ap, config)
}
pragma[nomagic]
private predicate fwdFlow0(
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, Configuration config
) {
sourceNode(node, state, config) and
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
summaryCtx = TParameterPositionNone() and
ap = getApNil(node)
or
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
fwdFlow(mid, state0, cc, argAp, ap0, config) and
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
@@ -1295,65 +1339,82 @@ private module MkStage<StageSig PrevStage> {
)
or
exists(NodeEx mid |
fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone()
)
or
exists(NodeEx mid, ApNil nil |
fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and
fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and
additionalJumpStateStep(mid, state0, node, state, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
or
// store
exists(TypedContent tc, Ap ap0 |
fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
ap = apCons(tc, ap0)
)
or
// read
exists(Ap ap0, Content c |
fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
fwdFlowConsCand(ap0, c, ap, config)
)
or
// flow into a callable
exists(ApApprox apa |
fwdFlowIn(_, node, state, _, cc, _, ap, config) and
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
apa = getApprox(ap) and
if PrevStage::parameterMayFlowThrough(node, _, apa, config)
then argAp = apSome(ap)
else argAp = apNone()
if PrevStage::parameterMayFlowThrough(node, apa, config)
then (
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
argAp = apSome(ap)
) else (
summaryCtx = TParameterPositionNone() and argAp = apNone()
)
)
or
// flow out of a callable
fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
exists(
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
DataFlowCallable inner
|
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and
flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and
inner = ret.getEnclosingCallable() and
cc = getCallContextReturn(inner, call, innercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
or
exists(DataFlowCall call, Ap argAp0 |
fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
fwdFlowIsEntered(call, cc, argAp, argAp0, config)
// flow through a callable
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
)
}
pragma[nomagic]
private predicate fwdFlowStore(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
Configuration config
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
fwdFlow(node1, state, cc, argAp, ap1, config) and
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
@@ -1366,7 +1427,7 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
exists(TypedContent tc |
fwdFlowStore(_, tail, tc, _, _, _, _, config) and
fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and
tc.getContent() = c and
cons = apCons(tc, tail)
)
@@ -1374,21 +1435,21 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate fwdFlowRead(
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
Configuration config
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
fwdFlow(node1, state, cc, argAp, ap, config) and
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::readStepCand(node1, c, node2, config) and
getHeadContent(ap) = c
}
pragma[nomagic]
private predicate fwdFlowIn(
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp,
Ap ap, Configuration config
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc,
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
) {
exists(ArgNodeEx arg, boolean allowsFieldFlow |
fwdFlow(arg, state, outercc, argAp, ap, config) and
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
@@ -1396,29 +1457,19 @@ private module MkStage<StageSig PrevStage> {
}
pragma[nomagic]
private predicate fwdFlowOutNotFromArg(
NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
private predicate fwdFlowOutFromArg(
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
Configuration config
) {
exists(
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
DataFlowCallable inner
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
fwdFlow(ret, state, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
inner = ret.getEnclosingCallable() and
ccOut = getCallContextReturn(inner, call, innercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate fwdFlowOutFromArg(
DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config
) {
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
)
}
@@ -1428,11 +1479,13 @@ private module MkStage<StageSig PrevStage> {
*/
pragma[nomagic]
private predicate fwdFlowIsEntered(
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
ParameterPosition pos, Ap ap, Configuration config
) {
exists(ParamNodeEx p |
fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
exists(ParamNodeEx param |
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
pos = param.getPosition()
)
}
@@ -1440,27 +1493,40 @@ private module MkStage<StageSig PrevStage> {
private predicate storeStepFwd(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
) {
fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and
ap2 = apCons(tc, ap1) and
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config)
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config)
}
private predicate readStepFwd(
NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
) {
fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and
fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and
fwdFlowConsCand(ap1, c, ap2, config)
}
pragma[nomagic]
private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
private predicate returnFlowsThrough0(
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
) {
exists(boolean allowsFieldFlow |
fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
pragma[only_bind_into](config))
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate returnFlowsThrough(
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp,
Ap ap, Configuration config
) {
exists(DataFlowCallable c, ParameterPosition ppos |
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
p.isParameterOf(c, ppos) and
parameterFlowThroughAllowed(p, kind)
)
}
@@ -1468,118 +1534,130 @@ private module MkStage<StageSig PrevStage> {
private predicate flowThroughIntoCall(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
}
pragma[nomagic]
private predicate returnNodeMayFlowThrough(
RetNodeEx ret, FlowState state, Ap ap, Configuration config
) {
fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
exists(Ap argAp |
flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
pragma[only_bind_into](config)) and
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
pragma[only_bind_into](config))
)
}
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
*
* The Boolean `toReturn` records whether the node must be returned from the
* enclosing callable in order to reach a sink, and if so, `returnAp` records
* the access path of the returned value.
* The parameter `returnCtx` records whether (and how) the node must be returned
* from the enclosing callable in order to reach a sink, and if so, `returnAp`
* records the access path of the returned value.
*/
pragma[nomagic]
additional predicate revFlow(
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
Configuration config
) {
revFlow0(node, state, toReturn, returnAp, ap, config) and
fwdFlow(node, state, _, _, ap, config)
revFlow0(node, state, returnCtx, returnAp, ap, config) and
fwdFlow(node, state, _, _, _, ap, config)
}
pragma[nomagic]
private predicate revFlow0(
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
Configuration config
) {
fwdFlow(node, state, _, _, ap, config) and
fwdFlow(node, state, _, _, _, ap, config) and
sinkNode(node, state, config) and
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
(
if hasSinkCallCtx(config)
then returnCtx = TReturnCtxNoFlowThrough()
else returnCtx = TReturnCtxNone()
) and
returnAp = apNone() and
ap instanceof ApNil
or
exists(NodeEx mid, FlowState state0 |
localStep(node, state, mid, state0, true, _, config, _) and
revFlow(mid, state0, toReturn, returnAp, ap, config)
revFlow(mid, state0, returnCtx, returnAp, ap, config)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and
revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, state, _, _, ap, config) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone()
)
or
exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone() and
ap instanceof ApNil
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStateStep(node, state, mid, state0, config) and
revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil,
pragma[only_bind_into](config)) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone() and
ap instanceof ApNil
)
or
// store
exists(Ap ap0, Content c |
revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and
revFlowConsCand(ap0, c, ap, config)
)
or
// read
exists(NodeEx mid, Ap ap0 |
revFlow(mid, state, toReturn, returnAp, ap0, config) and
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
readStepFwd(node, ap, _, mid, ap0, config)
)
or
// flow into a callable
revFlowInNotToReturn(node, state, returnAp, ap, config) and
toReturn = false
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
flowIntoCall(_, node, p, allowsFieldFlow, config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
returnCtx = TReturnCtxNone()
)
or
exists(DataFlowCall call, Ap returnAp0 |
revFlowInToReturn(call, node, state, returnAp0, ap, config) and
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
// flow through a callable
exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 |
revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
or
// flow out of a callable
revFlowOut(_, node, state, _, _, ap, config) and
toReturn = true and
if returnNodeMayFlowThrough(node, state, ap, config)
then returnAp = apSome(ap)
else returnAp = apNone()
exists(ReturnKindExt kind |
revFlowOut(_, node, kind, state, _, _, ap, config) and
if returnFlowsThrough(node, kind, state, _, _, _, ap, config)
then (
returnCtx = TReturnCtxMaybeFlowThrough(kind) and
returnAp = apSome(ap)
) else (
returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone()
)
)
}
pragma[nomagic]
private predicate revFlowStore(
Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid,
boolean toReturn, ApOption returnAp, Configuration config
ReturnCtx returnCtx, ApOption returnAp, Configuration config
) {
revFlow(mid, state, toReturn, returnAp, ap0, config) and
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
storeStepFwd(node, ap, tc, mid, ap0, config) and
tc.getContent() = c
}
@@ -1599,35 +1677,27 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate revFlowOut(
DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap,
Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx,
ApOption returnAp, Ap ap, Configuration config
) {
exists(NodeEx out, boolean allowsFieldFlow |
revFlow(out, state, toReturn, returnAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate revFlowInNotToReturn(
ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, false, returnAp, ap, config) and
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
revFlow(out, state, returnCtx, returnAp, ap, config) and
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate revFlowInToReturn(
DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, true, apSome(returnAp), ap, config) and
revFlow(pragma[only_bind_into](p), state,
TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
parameterFlowThroughAllowed(p, kind)
)
}
@@ -1638,11 +1708,12 @@ private module MkStage<StageSig PrevStage> {
*/
pragma[nomagic]
private predicate revFlowIsReturned(
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap,
Configuration config
) {
exists(RetNodeEx ret, FlowState state, CcCall ccc |
revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and
fwdFlow(ret, state, ccc, apSome(_), ap, config) and
revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and
returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and
matchesCall(ccc, call)
)
}
@@ -1713,40 +1784,39 @@ private module MkStage<StageSig PrevStage> {
validAp(ap, config)
}
pragma[noinline]
private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
pragma[nomagic]
private predicate parameterFlowsThroughRev(
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
) {
revFlow(p, _, true, apSome(ap0), ap, config) and
c = p.getEnclosingCallable()
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
parameterFlowThroughAllowed(p, kind)
}
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
parameterFlow(p, ap, ap0, c, config) and
c = ret.getEnclosingCallable() and
revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_),
pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and
fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and
kind = ret.getKind() and
p.getPosition() = pos and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
p.allowParameterReturnInSelf()
)
pragma[nomagic]
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
exists(RetNodeEx ret, ReturnKindExt kind |
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
parameterFlowsThroughRev(p, ap, kind, config)
)
}
pragma[nomagic]
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
exists(ParamNodeEx p, Ap ap |
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
parameterFlowsThroughRev(p, ap, kind, config)
)
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(
Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
revFlow(arg, state, toReturn, returnAp, ap, config) and
revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
revFlow(arg, state, returnCtx, returnAp, ap, config) and
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
}
@@ -1754,14 +1824,13 @@ private module MkStage<StageSig PrevStage> {
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) {
fwd = true and
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and
states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and
tuples =
count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
fwdFlow(n, state, cc, argAp, ap, config)
)
count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config))
or
fwd = false and
nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
@@ -1769,8 +1838,8 @@ private module MkStage<StageSig PrevStage> {
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
states = count(FlowState state | revFlow(_, state, _, _, _, config)) and
tuples =
count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap |
revFlow(n, state, b, retAp, ap, config)
count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap |
revFlow(n, state, returnCtx, retAp, ap, config)
)
}
/* End: Stage logic. */
@@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
exists(lcc)
}
predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
predicate flowIntoCall = flowIntoCallNodeCand1/5;
@@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig {
pragma[nomagic]
private predicate flowOutOfCallNodeCand2(
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
Stage2::revFlowAlias(node1, pragma[only_bind_into](config))
}
@@ -2021,8 +2091,8 @@ private module LocalFlowBigStep {
exists(NodeEx next | Stage2::revFlow(next, state, config) |
jumpStep(node, next, config) or
additionalJumpStep(node, next, config) or
flowIntoCallNodeCand1(_, node, next, config) or
flowOutOfCallNodeCand1(_, node, next, config) or
flowIntoCallNodeCand2(_, node, next, _, config) or
flowOutOfCallNodeCand2(_, node, _, next, _, config) or
Stage2::storeStepCand(node, _, _, next, _, config) or
Stage2::readStepCand(node, _, next, config)
)
@@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
}
predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
predicate flowIntoCall = flowIntoCallNodeCand2/5;
@@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx(
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
) {
exists(AccessPathFront apf |
Stage3::revFlow(node, state, true, _, apf, config) and
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
config)
)
}
@@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
pragma[nomagic]
predicate flowOutOfCall(
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
exists(FlowState state |
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
pragma[only_bind_into](config))
@@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) {
pragma[nomagic]
private predicate nodeMayUseSummary0(
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa,
Configuration config
) {
exists(AccessPathApprox apa0 |
Stage4::parameterMayFlowThrough(_, c, _, _) and
Stage4::revFlow(n, state, true, _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
n.getEnclosingCallable() = c
c = n.getEnclosingCallable() and
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
TAccessPathApproxSome(apa), apa0, config)
)
}
@@ -2522,9 +2595,10 @@ pragma[nomagic]
private predicate nodeMayUseSummary(
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
) {
exists(DataFlowCallable c |
Stage4::parameterMayFlowThrough(_, c, apa, config) and
nodeMayUseSummary0(n, c, state, apa, config)
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
Stage4::parameterMayFlowThrough(p, apa, config) and
nodeMayUseSummary0(n, c, pos, state, apa, config) and
p.isParameterOf(c, pos)
)
}
@@ -2532,7 +2606,7 @@ private newtype TSummaryCtx =
TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
Stage4::revFlow(p, state, _, config)
)
}
@@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough(
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
AccessPathApprox apa, Configuration config
) {
exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos |
exists(PathNodeMid mid, RetNodeEx ret |
pathNode(mid, ret, state, cc, sc, ap, config, _) and
kind = ret.getKind() and
apa = ap.getApprox() and
pos = sc.getParameterPos() and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
sc.getParamNode().allowParameterReturnInSelf()
)
parameterFlowThroughAllowed(sc.getParamNode(), kind)
)
}

View File

@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node source, FlowState state) { none() }
predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes
@@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
}
ParameterPosition getPosition() { this.isParameterOf(_, result) }
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
}
private class RetNodeEx extends NodeEx {
@@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) {
)
}
/**
* Holds if flow from `p` to a return node of kind `kind` is allowed.
*
* We don't expect a parameter to return stored in itself, unless
* explicitly allowed
*/
bindingset[p, kind]
private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) {
exists(ParameterPosition pos | p.isParameterOf(_, pos) |
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
allowParameterReturnInSelfCached(p.asNode())
)
}
/**
* Holds if flow from a parameter at position `pos` inside `c` to a return node of
* kind `kind` is allowed.
*
* We don't expect a parameter to return stored in itself, unless
* explicitly allowed
*/
bindingset[c, pos, kind]
private predicate parameterFlowThroughAllowed(
DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
) {
exists(ParamNodeEx p |
p.isParameterOf(c, pos) and
parameterFlowThroughAllowed(p, kind)
)
}
private module Stage1 implements StageSig {
class Ap = Unit;
@@ -981,21 +1011,22 @@ private module Stage1 implements StageSig {
* candidate for the origin of a summary.
*/
pragma[nomagic]
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
exists(ReturnKindExt kind |
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
exists(DataFlowCallable c, ReturnKindExt kind |
throughFlowNodeCand(p, config) and
returnFlowCallableNodeCand(c, kind, config) and
p.getEnclosingCallable() = c and
exists(ap) and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
or
p.allowParameterReturnInSelf()
)
parameterFlowThroughAllowed(p, kind)
)
}
pragma[nomagic]
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
throughFlowNodeCand(ret, config) and
kind = ret.getKind()
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(ArgNodeEx arg, boolean toReturn |
@@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1(
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config
) {
viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
Stage1::revFlow(ret, config) and
not outBarrier(ret, config) and
not inBarrier(out, config)
exists(ReturnPosition pos |
viableReturnPosOutNodeCand1(call, pos, out, config) and
pos = ret.getReturnPosition() and
kind = pos.getKind() and
Stage1::revFlow(ret, config) and
not outBarrier(ret, config) and
not inBarrier(out, config)
)
}
pragma[nomagic]
@@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1(
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
pragma[nomagic]
private int branch(NodeEx n1, Configuration conf) {
result =
strictcount(NodeEx n |
flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
)
}
@@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) {
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
pragma[nomagic]
private int join(NodeEx n2, Configuration conf) {
result =
strictcount(NodeEx n |
flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
)
}
@@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) {
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
Configuration config
) {
flowOutOfCallNodeCand1(call, ret, out, config) and
flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and
exists(int b, int j |
b = branch(ret, config) and
j = join(out, config) and
b = branch(ret, pragma[only_bind_into](config)) and
j = join(out, pragma[only_bind_into](config)) and
if b.minimum(j) <= config.fieldFlowBranchLimit()
then allowsFieldFlow = true
else allowsFieldFlow = false
@@ -1136,10 +1174,10 @@ pragma[nomagic]
private predicate flowIntoCallNodeCand1(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
flowIntoCallNodeCand1(call, arg, p, config) and
flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and
exists(int b, int j |
b = branch(arg, config) and
j = join(p, config) and
b = branch(arg, pragma[only_bind_into](config)) and
j = join(p, pragma[only_bind_into](config)) and
if b.minimum(j) <= config.fieldFlowBranchLimit()
then allowsFieldFlow = true
else allowsFieldFlow = false
@@ -1156,7 +1194,9 @@ private signature module StageSig {
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
predicate storeStepCand(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
@@ -1222,7 +1262,8 @@ private module MkStage<StageSig PrevStage> {
);
predicate flowOutOfCall(
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
Configuration config
);
predicate flowIntoCall(
@@ -1247,14 +1288,14 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate flowThroughOutOfCall(
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
Configuration config
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
NodeEx out, boolean allowsFieldFlow, Configuration config
) {
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
pragma[only_bind_into](config)) and
matchesCall(ccc, call)
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
matchesCall(ccc, call) and
c = ret.getEnclosingCallable()
}
/**
@@ -1262,29 +1303,32 @@ private module MkStage<StageSig PrevStage> {
* configuration `config`.
*
* The call context `cc` records whether the node is reached through an
* argument in a call, and if so, `argAp` records the access path of that
* argument.
* argument in a call, and if so, `summaryCtx` and `argAp` record the
* corresponding parameter position and access path of that argument, respectively.
*/
pragma[nomagic]
additional predicate fwdFlow(
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, Configuration config
) {
fwdFlow0(node, state, cc, argAp, ap, config) and
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
filter(node, state, ap, config)
}
pragma[nomagic]
private predicate fwdFlow0(
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, Configuration config
) {
sourceNode(node, state, config) and
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
summaryCtx = TParameterPositionNone() and
ap = getApNil(node)
or
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
fwdFlow(mid, state0, cc, argAp, ap0, config) and
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
@@ -1295,65 +1339,82 @@ private module MkStage<StageSig PrevStage> {
)
or
exists(NodeEx mid |
fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone()
)
or
exists(NodeEx mid, ApNil nil |
fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and
fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and
additionalJumpStateStep(mid, state0, node, state, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
or
// store
exists(TypedContent tc, Ap ap0 |
fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
ap = apCons(tc, ap0)
)
or
// read
exists(Ap ap0, Content c |
fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
fwdFlowConsCand(ap0, c, ap, config)
)
or
// flow into a callable
exists(ApApprox apa |
fwdFlowIn(_, node, state, _, cc, _, ap, config) and
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
apa = getApprox(ap) and
if PrevStage::parameterMayFlowThrough(node, _, apa, config)
then argAp = apSome(ap)
else argAp = apNone()
if PrevStage::parameterMayFlowThrough(node, apa, config)
then (
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
argAp = apSome(ap)
) else (
summaryCtx = TParameterPositionNone() and argAp = apNone()
)
)
or
// flow out of a callable
fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
exists(
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
DataFlowCallable inner
|
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and
flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and
inner = ret.getEnclosingCallable() and
cc = getCallContextReturn(inner, call, innercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
or
exists(DataFlowCall call, Ap argAp0 |
fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
fwdFlowIsEntered(call, cc, argAp, argAp0, config)
// flow through a callable
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
)
}
pragma[nomagic]
private predicate fwdFlowStore(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
Configuration config
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
fwdFlow(node1, state, cc, argAp, ap1, config) and
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
@@ -1366,7 +1427,7 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
exists(TypedContent tc |
fwdFlowStore(_, tail, tc, _, _, _, _, config) and
fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and
tc.getContent() = c and
cons = apCons(tc, tail)
)
@@ -1374,21 +1435,21 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate fwdFlowRead(
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
Configuration config
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
fwdFlow(node1, state, cc, argAp, ap, config) and
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::readStepCand(node1, c, node2, config) and
getHeadContent(ap) = c
}
pragma[nomagic]
private predicate fwdFlowIn(
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp,
Ap ap, Configuration config
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc,
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
) {
exists(ArgNodeEx arg, boolean allowsFieldFlow |
fwdFlow(arg, state, outercc, argAp, ap, config) and
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
@@ -1396,29 +1457,19 @@ private module MkStage<StageSig PrevStage> {
}
pragma[nomagic]
private predicate fwdFlowOutNotFromArg(
NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
private predicate fwdFlowOutFromArg(
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
Configuration config
) {
exists(
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
DataFlowCallable inner
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
fwdFlow(ret, state, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
inner = ret.getEnclosingCallable() and
ccOut = getCallContextReturn(inner, call, innercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate fwdFlowOutFromArg(
DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config
) {
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
)
}
@@ -1428,11 +1479,13 @@ private module MkStage<StageSig PrevStage> {
*/
pragma[nomagic]
private predicate fwdFlowIsEntered(
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
ParameterPosition pos, Ap ap, Configuration config
) {
exists(ParamNodeEx p |
fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
exists(ParamNodeEx param |
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
pos = param.getPosition()
)
}
@@ -1440,27 +1493,40 @@ private module MkStage<StageSig PrevStage> {
private predicate storeStepFwd(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
) {
fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and
ap2 = apCons(tc, ap1) and
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config)
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config)
}
private predicate readStepFwd(
NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
) {
fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and
fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and
fwdFlowConsCand(ap1, c, ap2, config)
}
pragma[nomagic]
private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
private predicate returnFlowsThrough0(
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
) {
exists(boolean allowsFieldFlow |
fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
pragma[only_bind_into](config))
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate returnFlowsThrough(
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp,
Ap ap, Configuration config
) {
exists(DataFlowCallable c, ParameterPosition ppos |
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
p.isParameterOf(c, ppos) and
parameterFlowThroughAllowed(p, kind)
)
}
@@ -1468,118 +1534,130 @@ private module MkStage<StageSig PrevStage> {
private predicate flowThroughIntoCall(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
}
pragma[nomagic]
private predicate returnNodeMayFlowThrough(
RetNodeEx ret, FlowState state, Ap ap, Configuration config
) {
fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
exists(Ap argAp |
flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
pragma[only_bind_into](config)) and
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
pragma[only_bind_into](config))
)
}
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
*
* The Boolean `toReturn` records whether the node must be returned from the
* enclosing callable in order to reach a sink, and if so, `returnAp` records
* the access path of the returned value.
* The parameter `returnCtx` records whether (and how) the node must be returned
* from the enclosing callable in order to reach a sink, and if so, `returnAp`
* records the access path of the returned value.
*/
pragma[nomagic]
additional predicate revFlow(
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
Configuration config
) {
revFlow0(node, state, toReturn, returnAp, ap, config) and
fwdFlow(node, state, _, _, ap, config)
revFlow0(node, state, returnCtx, returnAp, ap, config) and
fwdFlow(node, state, _, _, _, ap, config)
}
pragma[nomagic]
private predicate revFlow0(
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
Configuration config
) {
fwdFlow(node, state, _, _, ap, config) and
fwdFlow(node, state, _, _, _, ap, config) and
sinkNode(node, state, config) and
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
(
if hasSinkCallCtx(config)
then returnCtx = TReturnCtxNoFlowThrough()
else returnCtx = TReturnCtxNone()
) and
returnAp = apNone() and
ap instanceof ApNil
or
exists(NodeEx mid, FlowState state0 |
localStep(node, state, mid, state0, true, _, config, _) and
revFlow(mid, state0, toReturn, returnAp, ap, config)
revFlow(mid, state0, returnCtx, returnAp, ap, config)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and
revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, state, _, _, ap, config) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone()
)
or
exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone() and
ap instanceof ApNil
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStateStep(node, state, mid, state0, config) and
revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil,
pragma[only_bind_into](config)) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone() and
ap instanceof ApNil
)
or
// store
exists(Ap ap0, Content c |
revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and
revFlowConsCand(ap0, c, ap, config)
)
or
// read
exists(NodeEx mid, Ap ap0 |
revFlow(mid, state, toReturn, returnAp, ap0, config) and
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
readStepFwd(node, ap, _, mid, ap0, config)
)
or
// flow into a callable
revFlowInNotToReturn(node, state, returnAp, ap, config) and
toReturn = false
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
flowIntoCall(_, node, p, allowsFieldFlow, config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
returnCtx = TReturnCtxNone()
)
or
exists(DataFlowCall call, Ap returnAp0 |
revFlowInToReturn(call, node, state, returnAp0, ap, config) and
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
// flow through a callable
exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 |
revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
or
// flow out of a callable
revFlowOut(_, node, state, _, _, ap, config) and
toReturn = true and
if returnNodeMayFlowThrough(node, state, ap, config)
then returnAp = apSome(ap)
else returnAp = apNone()
exists(ReturnKindExt kind |
revFlowOut(_, node, kind, state, _, _, ap, config) and
if returnFlowsThrough(node, kind, state, _, _, _, ap, config)
then (
returnCtx = TReturnCtxMaybeFlowThrough(kind) and
returnAp = apSome(ap)
) else (
returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone()
)
)
}
pragma[nomagic]
private predicate revFlowStore(
Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid,
boolean toReturn, ApOption returnAp, Configuration config
ReturnCtx returnCtx, ApOption returnAp, Configuration config
) {
revFlow(mid, state, toReturn, returnAp, ap0, config) and
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
storeStepFwd(node, ap, tc, mid, ap0, config) and
tc.getContent() = c
}
@@ -1599,35 +1677,27 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate revFlowOut(
DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap,
Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx,
ApOption returnAp, Ap ap, Configuration config
) {
exists(NodeEx out, boolean allowsFieldFlow |
revFlow(out, state, toReturn, returnAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate revFlowInNotToReturn(
ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, false, returnAp, ap, config) and
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
revFlow(out, state, returnCtx, returnAp, ap, config) and
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate revFlowInToReturn(
DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, true, apSome(returnAp), ap, config) and
revFlow(pragma[only_bind_into](p), state,
TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
parameterFlowThroughAllowed(p, kind)
)
}
@@ -1638,11 +1708,12 @@ private module MkStage<StageSig PrevStage> {
*/
pragma[nomagic]
private predicate revFlowIsReturned(
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap,
Configuration config
) {
exists(RetNodeEx ret, FlowState state, CcCall ccc |
revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and
fwdFlow(ret, state, ccc, apSome(_), ap, config) and
revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and
returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and
matchesCall(ccc, call)
)
}
@@ -1713,40 +1784,39 @@ private module MkStage<StageSig PrevStage> {
validAp(ap, config)
}
pragma[noinline]
private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
pragma[nomagic]
private predicate parameterFlowsThroughRev(
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
) {
revFlow(p, _, true, apSome(ap0), ap, config) and
c = p.getEnclosingCallable()
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
parameterFlowThroughAllowed(p, kind)
}
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
parameterFlow(p, ap, ap0, c, config) and
c = ret.getEnclosingCallable() and
revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_),
pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and
fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and
kind = ret.getKind() and
p.getPosition() = pos and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
p.allowParameterReturnInSelf()
)
pragma[nomagic]
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
exists(RetNodeEx ret, ReturnKindExt kind |
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
parameterFlowsThroughRev(p, ap, kind, config)
)
}
pragma[nomagic]
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
exists(ParamNodeEx p, Ap ap |
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
parameterFlowsThroughRev(p, ap, kind, config)
)
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(
Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
revFlow(arg, state, toReturn, returnAp, ap, config) and
revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
revFlow(arg, state, returnCtx, returnAp, ap, config) and
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
}
@@ -1754,14 +1824,13 @@ private module MkStage<StageSig PrevStage> {
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) {
fwd = true and
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and
states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and
tuples =
count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
fwdFlow(n, state, cc, argAp, ap, config)
)
count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config))
or
fwd = false and
nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
@@ -1769,8 +1838,8 @@ private module MkStage<StageSig PrevStage> {
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
states = count(FlowState state | revFlow(_, state, _, _, _, config)) and
tuples =
count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap |
revFlow(n, state, b, retAp, ap, config)
count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap |
revFlow(n, state, returnCtx, retAp, ap, config)
)
}
/* End: Stage logic. */
@@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
exists(lcc)
}
predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
predicate flowIntoCall = flowIntoCallNodeCand1/5;
@@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig {
pragma[nomagic]
private predicate flowOutOfCallNodeCand2(
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
Stage2::revFlowAlias(node1, pragma[only_bind_into](config))
}
@@ -2021,8 +2091,8 @@ private module LocalFlowBigStep {
exists(NodeEx next | Stage2::revFlow(next, state, config) |
jumpStep(node, next, config) or
additionalJumpStep(node, next, config) or
flowIntoCallNodeCand1(_, node, next, config) or
flowOutOfCallNodeCand1(_, node, next, config) or
flowIntoCallNodeCand2(_, node, next, _, config) or
flowOutOfCallNodeCand2(_, node, _, next, _, config) or
Stage2::storeStepCand(node, _, _, next, _, config) or
Stage2::readStepCand(node, _, next, config)
)
@@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
}
predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
predicate flowIntoCall = flowIntoCallNodeCand2/5;
@@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx(
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
) {
exists(AccessPathFront apf |
Stage3::revFlow(node, state, true, _, apf, config) and
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
config)
)
}
@@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
pragma[nomagic]
predicate flowOutOfCall(
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
exists(FlowState state |
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
pragma[only_bind_into](config))
@@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) {
pragma[nomagic]
private predicate nodeMayUseSummary0(
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa,
Configuration config
) {
exists(AccessPathApprox apa0 |
Stage4::parameterMayFlowThrough(_, c, _, _) and
Stage4::revFlow(n, state, true, _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
n.getEnclosingCallable() = c
c = n.getEnclosingCallable() and
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
TAccessPathApproxSome(apa), apa0, config)
)
}
@@ -2522,9 +2595,10 @@ pragma[nomagic]
private predicate nodeMayUseSummary(
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
) {
exists(DataFlowCallable c |
Stage4::parameterMayFlowThrough(_, c, apa, config) and
nodeMayUseSummary0(n, c, state, apa, config)
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
Stage4::parameterMayFlowThrough(p, apa, config) and
nodeMayUseSummary0(n, c, pos, state, apa, config) and
p.isParameterOf(c, pos)
)
}
@@ -2532,7 +2606,7 @@ private newtype TSummaryCtx =
TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
Stage4::revFlow(p, state, _, config)
)
}
@@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough(
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
AccessPathApprox apa, Configuration config
) {
exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos |
exists(PathNodeMid mid, RetNodeEx ret |
pathNode(mid, ret, state, cc, sc, ap, config, _) and
kind = ret.getKind() and
apa = ap.getApprox() and
pos = sc.getParameterPos() and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
sc.getParamNode().allowParameterReturnInSelf()
)
parameterFlowThroughAllowed(sc.getParamNode(), kind)
)
}

View File

@@ -915,6 +915,17 @@ private module Cached {
TDataFlowCallNone() or
TDataFlowCallSome(DataFlowCall call)
cached
newtype TParameterPositionOption =
TParameterPositionNone() or
TParameterPositionSome(ParameterPosition pos)
cached
newtype TReturnCtx =
TReturnCtxNone() or
TReturnCtxNoFlowThrough() or
TReturnCtxMaybeFlowThrough(ReturnKindExt kind)
cached
newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) }
@@ -1304,6 +1315,44 @@ class DataFlowCallOption extends TDataFlowCallOption {
}
}
/** An optional `ParameterPosition`. */
class ParameterPositionOption extends TParameterPositionOption {
string toString() {
this = TParameterPositionNone() and
result = "(none)"
or
exists(ParameterPosition pos |
this = TParameterPositionSome(pos) and
result = pos.toString()
)
}
}
/**
* A return context used to calculate flow summaries in reverse flow.
*
* The possible values are:
*
* - `TReturnCtxNone()`: no return flow.
* - `TReturnCtxNoFlowThrough()`: return flow, but flow through is not possible.
* - `TReturnCtxMaybeFlowThrough(ReturnKindExt kind)`: return flow, of kind `kind`, and
* flow through may be possible.
*/
class ReturnCtx extends TReturnCtx {
string toString() {
this = TReturnCtxNone() and
result = "(none)"
or
this = TReturnCtxNoFlowThrough() and
result = "(no flow through)"
or
exists(ReturnKindExt kind |
this = TReturnCtxMaybeFlowThrough(kind) and
result = kind.toString()
)
}
}
/** A `Content` tagged with the type of a containing object. */
class TypedContent extends MkTypedContent {
private Content c;

View File

@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node source, FlowState state) { none() }
predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes
@@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
}
ParameterPosition getPosition() { this.isParameterOf(_, result) }
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
}
private class RetNodeEx extends NodeEx {
@@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) {
)
}
/**
* Holds if flow from `p` to a return node of kind `kind` is allowed.
*
* We don't expect a parameter to return stored in itself, unless
* explicitly allowed
*/
bindingset[p, kind]
private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) {
exists(ParameterPosition pos | p.isParameterOf(_, pos) |
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
allowParameterReturnInSelfCached(p.asNode())
)
}
/**
* Holds if flow from a parameter at position `pos` inside `c` to a return node of
* kind `kind` is allowed.
*
* We don't expect a parameter to return stored in itself, unless
* explicitly allowed
*/
bindingset[c, pos, kind]
private predicate parameterFlowThroughAllowed(
DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
) {
exists(ParamNodeEx p |
p.isParameterOf(c, pos) and
parameterFlowThroughAllowed(p, kind)
)
}
private module Stage1 implements StageSig {
class Ap = Unit;
@@ -981,21 +1011,22 @@ private module Stage1 implements StageSig {
* candidate for the origin of a summary.
*/
pragma[nomagic]
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
exists(ReturnKindExt kind |
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
exists(DataFlowCallable c, ReturnKindExt kind |
throughFlowNodeCand(p, config) and
returnFlowCallableNodeCand(c, kind, config) and
p.getEnclosingCallable() = c and
exists(ap) and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
or
p.allowParameterReturnInSelf()
)
parameterFlowThroughAllowed(p, kind)
)
}
pragma[nomagic]
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
throughFlowNodeCand(ret, config) and
kind = ret.getKind()
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(ArgNodeEx arg, boolean toReturn |
@@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1(
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config
) {
viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
Stage1::revFlow(ret, config) and
not outBarrier(ret, config) and
not inBarrier(out, config)
exists(ReturnPosition pos |
viableReturnPosOutNodeCand1(call, pos, out, config) and
pos = ret.getReturnPosition() and
kind = pos.getKind() and
Stage1::revFlow(ret, config) and
not outBarrier(ret, config) and
not inBarrier(out, config)
)
}
pragma[nomagic]
@@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1(
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
pragma[nomagic]
private int branch(NodeEx n1, Configuration conf) {
result =
strictcount(NodeEx n |
flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
)
}
@@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) {
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
pragma[nomagic]
private int join(NodeEx n2, Configuration conf) {
result =
strictcount(NodeEx n |
flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
)
}
@@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) {
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
Configuration config
) {
flowOutOfCallNodeCand1(call, ret, out, config) and
flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and
exists(int b, int j |
b = branch(ret, config) and
j = join(out, config) and
b = branch(ret, pragma[only_bind_into](config)) and
j = join(out, pragma[only_bind_into](config)) and
if b.minimum(j) <= config.fieldFlowBranchLimit()
then allowsFieldFlow = true
else allowsFieldFlow = false
@@ -1136,10 +1174,10 @@ pragma[nomagic]
private predicate flowIntoCallNodeCand1(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
flowIntoCallNodeCand1(call, arg, p, config) and
flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and
exists(int b, int j |
b = branch(arg, config) and
j = join(p, config) and
b = branch(arg, pragma[only_bind_into](config)) and
j = join(p, pragma[only_bind_into](config)) and
if b.minimum(j) <= config.fieldFlowBranchLimit()
then allowsFieldFlow = true
else allowsFieldFlow = false
@@ -1156,7 +1194,9 @@ private signature module StageSig {
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
predicate storeStepCand(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
@@ -1222,7 +1262,8 @@ private module MkStage<StageSig PrevStage> {
);
predicate flowOutOfCall(
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
Configuration config
);
predicate flowIntoCall(
@@ -1247,14 +1288,14 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate flowThroughOutOfCall(
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
Configuration config
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
NodeEx out, boolean allowsFieldFlow, Configuration config
) {
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
pragma[only_bind_into](config)) and
matchesCall(ccc, call)
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
matchesCall(ccc, call) and
c = ret.getEnclosingCallable()
}
/**
@@ -1262,29 +1303,32 @@ private module MkStage<StageSig PrevStage> {
* configuration `config`.
*
* The call context `cc` records whether the node is reached through an
* argument in a call, and if so, `argAp` records the access path of that
* argument.
* argument in a call, and if so, `summaryCtx` and `argAp` record the
* corresponding parameter position and access path of that argument, respectively.
*/
pragma[nomagic]
additional predicate fwdFlow(
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, Configuration config
) {
fwdFlow0(node, state, cc, argAp, ap, config) and
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
filter(node, state, ap, config)
}
pragma[nomagic]
private predicate fwdFlow0(
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, Configuration config
) {
sourceNode(node, state, config) and
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
summaryCtx = TParameterPositionNone() and
ap = getApNil(node)
or
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
fwdFlow(mid, state0, cc, argAp, ap0, config) and
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
@@ -1295,65 +1339,82 @@ private module MkStage<StageSig PrevStage> {
)
or
exists(NodeEx mid |
fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone()
)
or
exists(NodeEx mid, ApNil nil |
fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and
fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and
additionalJumpStateStep(mid, state0, node, state, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
or
// store
exists(TypedContent tc, Ap ap0 |
fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
ap = apCons(tc, ap0)
)
or
// read
exists(Ap ap0, Content c |
fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
fwdFlowConsCand(ap0, c, ap, config)
)
or
// flow into a callable
exists(ApApprox apa |
fwdFlowIn(_, node, state, _, cc, _, ap, config) and
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
apa = getApprox(ap) and
if PrevStage::parameterMayFlowThrough(node, _, apa, config)
then argAp = apSome(ap)
else argAp = apNone()
if PrevStage::parameterMayFlowThrough(node, apa, config)
then (
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
argAp = apSome(ap)
) else (
summaryCtx = TParameterPositionNone() and argAp = apNone()
)
)
or
// flow out of a callable
fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
exists(
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
DataFlowCallable inner
|
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and
flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and
inner = ret.getEnclosingCallable() and
cc = getCallContextReturn(inner, call, innercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
or
exists(DataFlowCall call, Ap argAp0 |
fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
fwdFlowIsEntered(call, cc, argAp, argAp0, config)
// flow through a callable
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
)
}
pragma[nomagic]
private predicate fwdFlowStore(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
Configuration config
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
fwdFlow(node1, state, cc, argAp, ap1, config) and
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
@@ -1366,7 +1427,7 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
exists(TypedContent tc |
fwdFlowStore(_, tail, tc, _, _, _, _, config) and
fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and
tc.getContent() = c and
cons = apCons(tc, tail)
)
@@ -1374,21 +1435,21 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate fwdFlowRead(
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
Configuration config
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
fwdFlow(node1, state, cc, argAp, ap, config) and
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::readStepCand(node1, c, node2, config) and
getHeadContent(ap) = c
}
pragma[nomagic]
private predicate fwdFlowIn(
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp,
Ap ap, Configuration config
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc,
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
) {
exists(ArgNodeEx arg, boolean allowsFieldFlow |
fwdFlow(arg, state, outercc, argAp, ap, config) and
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
@@ -1396,29 +1457,19 @@ private module MkStage<StageSig PrevStage> {
}
pragma[nomagic]
private predicate fwdFlowOutNotFromArg(
NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
private predicate fwdFlowOutFromArg(
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
Configuration config
) {
exists(
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
DataFlowCallable inner
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
fwdFlow(ret, state, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
inner = ret.getEnclosingCallable() and
ccOut = getCallContextReturn(inner, call, innercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate fwdFlowOutFromArg(
DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config
) {
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
)
}
@@ -1428,11 +1479,13 @@ private module MkStage<StageSig PrevStage> {
*/
pragma[nomagic]
private predicate fwdFlowIsEntered(
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
ParameterPosition pos, Ap ap, Configuration config
) {
exists(ParamNodeEx p |
fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
exists(ParamNodeEx param |
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
pos = param.getPosition()
)
}
@@ -1440,27 +1493,40 @@ private module MkStage<StageSig PrevStage> {
private predicate storeStepFwd(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
) {
fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and
ap2 = apCons(tc, ap1) and
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config)
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config)
}
private predicate readStepFwd(
NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
) {
fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and
fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and
fwdFlowConsCand(ap1, c, ap2, config)
}
pragma[nomagic]
private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
private predicate returnFlowsThrough0(
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
) {
exists(boolean allowsFieldFlow |
fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
pragma[only_bind_into](config))
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate returnFlowsThrough(
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp,
Ap ap, Configuration config
) {
exists(DataFlowCallable c, ParameterPosition ppos |
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
p.isParameterOf(c, ppos) and
parameterFlowThroughAllowed(p, kind)
)
}
@@ -1468,118 +1534,130 @@ private module MkStage<StageSig PrevStage> {
private predicate flowThroughIntoCall(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
}
pragma[nomagic]
private predicate returnNodeMayFlowThrough(
RetNodeEx ret, FlowState state, Ap ap, Configuration config
) {
fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
exists(Ap argAp |
flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
pragma[only_bind_into](config)) and
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
pragma[only_bind_into](config))
)
}
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
*
* The Boolean `toReturn` records whether the node must be returned from the
* enclosing callable in order to reach a sink, and if so, `returnAp` records
* the access path of the returned value.
* The parameter `returnCtx` records whether (and how) the node must be returned
* from the enclosing callable in order to reach a sink, and if so, `returnAp`
* records the access path of the returned value.
*/
pragma[nomagic]
additional predicate revFlow(
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
Configuration config
) {
revFlow0(node, state, toReturn, returnAp, ap, config) and
fwdFlow(node, state, _, _, ap, config)
revFlow0(node, state, returnCtx, returnAp, ap, config) and
fwdFlow(node, state, _, _, _, ap, config)
}
pragma[nomagic]
private predicate revFlow0(
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
Configuration config
) {
fwdFlow(node, state, _, _, ap, config) and
fwdFlow(node, state, _, _, _, ap, config) and
sinkNode(node, state, config) and
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
(
if hasSinkCallCtx(config)
then returnCtx = TReturnCtxNoFlowThrough()
else returnCtx = TReturnCtxNone()
) and
returnAp = apNone() and
ap instanceof ApNil
or
exists(NodeEx mid, FlowState state0 |
localStep(node, state, mid, state0, true, _, config, _) and
revFlow(mid, state0, toReturn, returnAp, ap, config)
revFlow(mid, state0, returnCtx, returnAp, ap, config)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and
revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, state, _, _, ap, config) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone()
)
or
exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone() and
ap instanceof ApNil
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStateStep(node, state, mid, state0, config) and
revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil,
pragma[only_bind_into](config)) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone() and
ap instanceof ApNil
)
or
// store
exists(Ap ap0, Content c |
revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and
revFlowConsCand(ap0, c, ap, config)
)
or
// read
exists(NodeEx mid, Ap ap0 |
revFlow(mid, state, toReturn, returnAp, ap0, config) and
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
readStepFwd(node, ap, _, mid, ap0, config)
)
or
// flow into a callable
revFlowInNotToReturn(node, state, returnAp, ap, config) and
toReturn = false
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
flowIntoCall(_, node, p, allowsFieldFlow, config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
returnCtx = TReturnCtxNone()
)
or
exists(DataFlowCall call, Ap returnAp0 |
revFlowInToReturn(call, node, state, returnAp0, ap, config) and
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
// flow through a callable
exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 |
revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
or
// flow out of a callable
revFlowOut(_, node, state, _, _, ap, config) and
toReturn = true and
if returnNodeMayFlowThrough(node, state, ap, config)
then returnAp = apSome(ap)
else returnAp = apNone()
exists(ReturnKindExt kind |
revFlowOut(_, node, kind, state, _, _, ap, config) and
if returnFlowsThrough(node, kind, state, _, _, _, ap, config)
then (
returnCtx = TReturnCtxMaybeFlowThrough(kind) and
returnAp = apSome(ap)
) else (
returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone()
)
)
}
pragma[nomagic]
private predicate revFlowStore(
Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid,
boolean toReturn, ApOption returnAp, Configuration config
ReturnCtx returnCtx, ApOption returnAp, Configuration config
) {
revFlow(mid, state, toReturn, returnAp, ap0, config) and
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
storeStepFwd(node, ap, tc, mid, ap0, config) and
tc.getContent() = c
}
@@ -1599,35 +1677,27 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate revFlowOut(
DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap,
Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx,
ApOption returnAp, Ap ap, Configuration config
) {
exists(NodeEx out, boolean allowsFieldFlow |
revFlow(out, state, toReturn, returnAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate revFlowInNotToReturn(
ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, false, returnAp, ap, config) and
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
revFlow(out, state, returnCtx, returnAp, ap, config) and
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate revFlowInToReturn(
DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, true, apSome(returnAp), ap, config) and
revFlow(pragma[only_bind_into](p), state,
TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
parameterFlowThroughAllowed(p, kind)
)
}
@@ -1638,11 +1708,12 @@ private module MkStage<StageSig PrevStage> {
*/
pragma[nomagic]
private predicate revFlowIsReturned(
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap,
Configuration config
) {
exists(RetNodeEx ret, FlowState state, CcCall ccc |
revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and
fwdFlow(ret, state, ccc, apSome(_), ap, config) and
revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and
returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and
matchesCall(ccc, call)
)
}
@@ -1713,40 +1784,39 @@ private module MkStage<StageSig PrevStage> {
validAp(ap, config)
}
pragma[noinline]
private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
pragma[nomagic]
private predicate parameterFlowsThroughRev(
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
) {
revFlow(p, _, true, apSome(ap0), ap, config) and
c = p.getEnclosingCallable()
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
parameterFlowThroughAllowed(p, kind)
}
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
parameterFlow(p, ap, ap0, c, config) and
c = ret.getEnclosingCallable() and
revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_),
pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and
fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and
kind = ret.getKind() and
p.getPosition() = pos and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
p.allowParameterReturnInSelf()
)
pragma[nomagic]
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
exists(RetNodeEx ret, ReturnKindExt kind |
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
parameterFlowsThroughRev(p, ap, kind, config)
)
}
pragma[nomagic]
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
exists(ParamNodeEx p, Ap ap |
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
parameterFlowsThroughRev(p, ap, kind, config)
)
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(
Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
revFlow(arg, state, toReturn, returnAp, ap, config) and
revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
revFlow(arg, state, returnCtx, returnAp, ap, config) and
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
}
@@ -1754,14 +1824,13 @@ private module MkStage<StageSig PrevStage> {
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) {
fwd = true and
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and
states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and
tuples =
count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
fwdFlow(n, state, cc, argAp, ap, config)
)
count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config))
or
fwd = false and
nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
@@ -1769,8 +1838,8 @@ private module MkStage<StageSig PrevStage> {
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
states = count(FlowState state | revFlow(_, state, _, _, _, config)) and
tuples =
count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap |
revFlow(n, state, b, retAp, ap, config)
count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap |
revFlow(n, state, returnCtx, retAp, ap, config)
)
}
/* End: Stage logic. */
@@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
exists(lcc)
}
predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
predicate flowIntoCall = flowIntoCallNodeCand1/5;
@@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig {
pragma[nomagic]
private predicate flowOutOfCallNodeCand2(
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
Stage2::revFlowAlias(node1, pragma[only_bind_into](config))
}
@@ -2021,8 +2091,8 @@ private module LocalFlowBigStep {
exists(NodeEx next | Stage2::revFlow(next, state, config) |
jumpStep(node, next, config) or
additionalJumpStep(node, next, config) or
flowIntoCallNodeCand1(_, node, next, config) or
flowOutOfCallNodeCand1(_, node, next, config) or
flowIntoCallNodeCand2(_, node, next, _, config) or
flowOutOfCallNodeCand2(_, node, _, next, _, config) or
Stage2::storeStepCand(node, _, _, next, _, config) or
Stage2::readStepCand(node, _, next, config)
)
@@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
}
predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
predicate flowIntoCall = flowIntoCallNodeCand2/5;
@@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx(
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
) {
exists(AccessPathFront apf |
Stage3::revFlow(node, state, true, _, apf, config) and
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
config)
)
}
@@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
pragma[nomagic]
predicate flowOutOfCall(
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
exists(FlowState state |
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
pragma[only_bind_into](config))
@@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) {
pragma[nomagic]
private predicate nodeMayUseSummary0(
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa,
Configuration config
) {
exists(AccessPathApprox apa0 |
Stage4::parameterMayFlowThrough(_, c, _, _) and
Stage4::revFlow(n, state, true, _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
n.getEnclosingCallable() = c
c = n.getEnclosingCallable() and
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
TAccessPathApproxSome(apa), apa0, config)
)
}
@@ -2522,9 +2595,10 @@ pragma[nomagic]
private predicate nodeMayUseSummary(
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
) {
exists(DataFlowCallable c |
Stage4::parameterMayFlowThrough(_, c, apa, config) and
nodeMayUseSummary0(n, c, state, apa, config)
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
Stage4::parameterMayFlowThrough(p, apa, config) and
nodeMayUseSummary0(n, c, pos, state, apa, config) and
p.isParameterOf(c, pos)
)
}
@@ -2532,7 +2606,7 @@ private newtype TSummaryCtx =
TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
Stage4::revFlow(p, state, _, config)
)
}
@@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough(
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
AccessPathApprox apa, Configuration config
) {
exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos |
exists(PathNodeMid mid, RetNodeEx ret |
pathNode(mid, ret, state, cc, sc, ap, config, _) and
kind = ret.getKind() and
apa = ap.getApprox() and
pos = sc.getParameterPos() and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
sc.getParamNode().allowParameterReturnInSelf()
)
parameterFlowThroughAllowed(sc.getParamNode(), kind)
)
}

View File

@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node source, FlowState state) { none() }
predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes
@@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
}
ParameterPosition getPosition() { this.isParameterOf(_, result) }
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
}
private class RetNodeEx extends NodeEx {
@@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) {
)
}
/**
* Holds if flow from `p` to a return node of kind `kind` is allowed.
*
* We don't expect a parameter to return stored in itself, unless
* explicitly allowed
*/
bindingset[p, kind]
private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) {
exists(ParameterPosition pos | p.isParameterOf(_, pos) |
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
allowParameterReturnInSelfCached(p.asNode())
)
}
/**
* Holds if flow from a parameter at position `pos` inside `c` to a return node of
* kind `kind` is allowed.
*
* We don't expect a parameter to return stored in itself, unless
* explicitly allowed
*/
bindingset[c, pos, kind]
private predicate parameterFlowThroughAllowed(
DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
) {
exists(ParamNodeEx p |
p.isParameterOf(c, pos) and
parameterFlowThroughAllowed(p, kind)
)
}
private module Stage1 implements StageSig {
class Ap = Unit;
@@ -981,21 +1011,22 @@ private module Stage1 implements StageSig {
* candidate for the origin of a summary.
*/
pragma[nomagic]
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
exists(ReturnKindExt kind |
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
exists(DataFlowCallable c, ReturnKindExt kind |
throughFlowNodeCand(p, config) and
returnFlowCallableNodeCand(c, kind, config) and
p.getEnclosingCallable() = c and
exists(ap) and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
or
p.allowParameterReturnInSelf()
)
parameterFlowThroughAllowed(p, kind)
)
}
pragma[nomagic]
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
throughFlowNodeCand(ret, config) and
kind = ret.getKind()
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(ArgNodeEx arg, boolean toReturn |
@@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1(
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config
) {
viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
Stage1::revFlow(ret, config) and
not outBarrier(ret, config) and
not inBarrier(out, config)
exists(ReturnPosition pos |
viableReturnPosOutNodeCand1(call, pos, out, config) and
pos = ret.getReturnPosition() and
kind = pos.getKind() and
Stage1::revFlow(ret, config) and
not outBarrier(ret, config) and
not inBarrier(out, config)
)
}
pragma[nomagic]
@@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1(
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
pragma[nomagic]
private int branch(NodeEx n1, Configuration conf) {
result =
strictcount(NodeEx n |
flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
)
}
@@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) {
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
pragma[nomagic]
private int join(NodeEx n2, Configuration conf) {
result =
strictcount(NodeEx n |
flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
)
}
@@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) {
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
Configuration config
) {
flowOutOfCallNodeCand1(call, ret, out, config) and
flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and
exists(int b, int j |
b = branch(ret, config) and
j = join(out, config) and
b = branch(ret, pragma[only_bind_into](config)) and
j = join(out, pragma[only_bind_into](config)) and
if b.minimum(j) <= config.fieldFlowBranchLimit()
then allowsFieldFlow = true
else allowsFieldFlow = false
@@ -1136,10 +1174,10 @@ pragma[nomagic]
private predicate flowIntoCallNodeCand1(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
flowIntoCallNodeCand1(call, arg, p, config) and
flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and
exists(int b, int j |
b = branch(arg, config) and
j = join(p, config) and
b = branch(arg, pragma[only_bind_into](config)) and
j = join(p, pragma[only_bind_into](config)) and
if b.minimum(j) <= config.fieldFlowBranchLimit()
then allowsFieldFlow = true
else allowsFieldFlow = false
@@ -1156,7 +1194,9 @@ private signature module StageSig {
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
predicate storeStepCand(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
@@ -1222,7 +1262,8 @@ private module MkStage<StageSig PrevStage> {
);
predicate flowOutOfCall(
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
Configuration config
);
predicate flowIntoCall(
@@ -1247,14 +1288,14 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate flowThroughOutOfCall(
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
Configuration config
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
NodeEx out, boolean allowsFieldFlow, Configuration config
) {
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
pragma[only_bind_into](config)) and
matchesCall(ccc, call)
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
matchesCall(ccc, call) and
c = ret.getEnclosingCallable()
}
/**
@@ -1262,29 +1303,32 @@ private module MkStage<StageSig PrevStage> {
* configuration `config`.
*
* The call context `cc` records whether the node is reached through an
* argument in a call, and if so, `argAp` records the access path of that
* argument.
* argument in a call, and if so, `summaryCtx` and `argAp` record the
* corresponding parameter position and access path of that argument, respectively.
*/
pragma[nomagic]
additional predicate fwdFlow(
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, Configuration config
) {
fwdFlow0(node, state, cc, argAp, ap, config) and
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
filter(node, state, ap, config)
}
pragma[nomagic]
private predicate fwdFlow0(
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, Configuration config
) {
sourceNode(node, state, config) and
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
summaryCtx = TParameterPositionNone() and
ap = getApNil(node)
or
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
fwdFlow(mid, state0, cc, argAp, ap0, config) and
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
@@ -1295,65 +1339,82 @@ private module MkStage<StageSig PrevStage> {
)
or
exists(NodeEx mid |
fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone()
)
or
exists(NodeEx mid, ApNil nil |
fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and
fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and
additionalJumpStateStep(mid, state0, node, state, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
or
// store
exists(TypedContent tc, Ap ap0 |
fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
ap = apCons(tc, ap0)
)
or
// read
exists(Ap ap0, Content c |
fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
fwdFlowConsCand(ap0, c, ap, config)
)
or
// flow into a callable
exists(ApApprox apa |
fwdFlowIn(_, node, state, _, cc, _, ap, config) and
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
apa = getApprox(ap) and
if PrevStage::parameterMayFlowThrough(node, _, apa, config)
then argAp = apSome(ap)
else argAp = apNone()
if PrevStage::parameterMayFlowThrough(node, apa, config)
then (
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
argAp = apSome(ap)
) else (
summaryCtx = TParameterPositionNone() and argAp = apNone()
)
)
or
// flow out of a callable
fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
exists(
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
DataFlowCallable inner
|
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and
flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and
inner = ret.getEnclosingCallable() and
cc = getCallContextReturn(inner, call, innercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
or
exists(DataFlowCall call, Ap argAp0 |
fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
fwdFlowIsEntered(call, cc, argAp, argAp0, config)
// flow through a callable
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
)
}
pragma[nomagic]
private predicate fwdFlowStore(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
Configuration config
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
fwdFlow(node1, state, cc, argAp, ap1, config) and
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
@@ -1366,7 +1427,7 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
exists(TypedContent tc |
fwdFlowStore(_, tail, tc, _, _, _, _, config) and
fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and
tc.getContent() = c and
cons = apCons(tc, tail)
)
@@ -1374,21 +1435,21 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate fwdFlowRead(
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
Configuration config
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
fwdFlow(node1, state, cc, argAp, ap, config) and
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::readStepCand(node1, c, node2, config) and
getHeadContent(ap) = c
}
pragma[nomagic]
private predicate fwdFlowIn(
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp,
Ap ap, Configuration config
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc,
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
) {
exists(ArgNodeEx arg, boolean allowsFieldFlow |
fwdFlow(arg, state, outercc, argAp, ap, config) and
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
@@ -1396,29 +1457,19 @@ private module MkStage<StageSig PrevStage> {
}
pragma[nomagic]
private predicate fwdFlowOutNotFromArg(
NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
private predicate fwdFlowOutFromArg(
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
Configuration config
) {
exists(
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
DataFlowCallable inner
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
fwdFlow(ret, state, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
inner = ret.getEnclosingCallable() and
ccOut = getCallContextReturn(inner, call, innercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate fwdFlowOutFromArg(
DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config
) {
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
)
}
@@ -1428,11 +1479,13 @@ private module MkStage<StageSig PrevStage> {
*/
pragma[nomagic]
private predicate fwdFlowIsEntered(
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
ParameterPosition pos, Ap ap, Configuration config
) {
exists(ParamNodeEx p |
fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
exists(ParamNodeEx param |
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
pos = param.getPosition()
)
}
@@ -1440,27 +1493,40 @@ private module MkStage<StageSig PrevStage> {
private predicate storeStepFwd(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
) {
fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and
ap2 = apCons(tc, ap1) and
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config)
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config)
}
private predicate readStepFwd(
NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
) {
fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and
fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and
fwdFlowConsCand(ap1, c, ap2, config)
}
pragma[nomagic]
private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
private predicate returnFlowsThrough0(
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
) {
exists(boolean allowsFieldFlow |
fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
pragma[only_bind_into](config))
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate returnFlowsThrough(
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp,
Ap ap, Configuration config
) {
exists(DataFlowCallable c, ParameterPosition ppos |
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
p.isParameterOf(c, ppos) and
parameterFlowThroughAllowed(p, kind)
)
}
@@ -1468,118 +1534,130 @@ private module MkStage<StageSig PrevStage> {
private predicate flowThroughIntoCall(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
}
pragma[nomagic]
private predicate returnNodeMayFlowThrough(
RetNodeEx ret, FlowState state, Ap ap, Configuration config
) {
fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
exists(Ap argAp |
flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
pragma[only_bind_into](config)) and
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
pragma[only_bind_into](config))
)
}
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
*
* The Boolean `toReturn` records whether the node must be returned from the
* enclosing callable in order to reach a sink, and if so, `returnAp` records
* the access path of the returned value.
* The parameter `returnCtx` records whether (and how) the node must be returned
* from the enclosing callable in order to reach a sink, and if so, `returnAp`
* records the access path of the returned value.
*/
pragma[nomagic]
additional predicate revFlow(
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
Configuration config
) {
revFlow0(node, state, toReturn, returnAp, ap, config) and
fwdFlow(node, state, _, _, ap, config)
revFlow0(node, state, returnCtx, returnAp, ap, config) and
fwdFlow(node, state, _, _, _, ap, config)
}
pragma[nomagic]
private predicate revFlow0(
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
Configuration config
) {
fwdFlow(node, state, _, _, ap, config) and
fwdFlow(node, state, _, _, _, ap, config) and
sinkNode(node, state, config) and
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
(
if hasSinkCallCtx(config)
then returnCtx = TReturnCtxNoFlowThrough()
else returnCtx = TReturnCtxNone()
) and
returnAp = apNone() and
ap instanceof ApNil
or
exists(NodeEx mid, FlowState state0 |
localStep(node, state, mid, state0, true, _, config, _) and
revFlow(mid, state0, toReturn, returnAp, ap, config)
revFlow(mid, state0, returnCtx, returnAp, ap, config)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and
revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, state, _, _, ap, config) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone()
)
or
exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone() and
ap instanceof ApNil
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStateStep(node, state, mid, state0, config) and
revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil,
pragma[only_bind_into](config)) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone() and
ap instanceof ApNil
)
or
// store
exists(Ap ap0, Content c |
revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and
revFlowConsCand(ap0, c, ap, config)
)
or
// read
exists(NodeEx mid, Ap ap0 |
revFlow(mid, state, toReturn, returnAp, ap0, config) and
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
readStepFwd(node, ap, _, mid, ap0, config)
)
or
// flow into a callable
revFlowInNotToReturn(node, state, returnAp, ap, config) and
toReturn = false
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
flowIntoCall(_, node, p, allowsFieldFlow, config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
returnCtx = TReturnCtxNone()
)
or
exists(DataFlowCall call, Ap returnAp0 |
revFlowInToReturn(call, node, state, returnAp0, ap, config) and
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
// flow through a callable
exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 |
revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
or
// flow out of a callable
revFlowOut(_, node, state, _, _, ap, config) and
toReturn = true and
if returnNodeMayFlowThrough(node, state, ap, config)
then returnAp = apSome(ap)
else returnAp = apNone()
exists(ReturnKindExt kind |
revFlowOut(_, node, kind, state, _, _, ap, config) and
if returnFlowsThrough(node, kind, state, _, _, _, ap, config)
then (
returnCtx = TReturnCtxMaybeFlowThrough(kind) and
returnAp = apSome(ap)
) else (
returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone()
)
)
}
pragma[nomagic]
private predicate revFlowStore(
Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid,
boolean toReturn, ApOption returnAp, Configuration config
ReturnCtx returnCtx, ApOption returnAp, Configuration config
) {
revFlow(mid, state, toReturn, returnAp, ap0, config) and
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
storeStepFwd(node, ap, tc, mid, ap0, config) and
tc.getContent() = c
}
@@ -1599,35 +1677,27 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate revFlowOut(
DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap,
Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx,
ApOption returnAp, Ap ap, Configuration config
) {
exists(NodeEx out, boolean allowsFieldFlow |
revFlow(out, state, toReturn, returnAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate revFlowInNotToReturn(
ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, false, returnAp, ap, config) and
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
revFlow(out, state, returnCtx, returnAp, ap, config) and
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate revFlowInToReturn(
DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, true, apSome(returnAp), ap, config) and
revFlow(pragma[only_bind_into](p), state,
TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
parameterFlowThroughAllowed(p, kind)
)
}
@@ -1638,11 +1708,12 @@ private module MkStage<StageSig PrevStage> {
*/
pragma[nomagic]
private predicate revFlowIsReturned(
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap,
Configuration config
) {
exists(RetNodeEx ret, FlowState state, CcCall ccc |
revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and
fwdFlow(ret, state, ccc, apSome(_), ap, config) and
revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and
returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and
matchesCall(ccc, call)
)
}
@@ -1713,40 +1784,39 @@ private module MkStage<StageSig PrevStage> {
validAp(ap, config)
}
pragma[noinline]
private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
pragma[nomagic]
private predicate parameterFlowsThroughRev(
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
) {
revFlow(p, _, true, apSome(ap0), ap, config) and
c = p.getEnclosingCallable()
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
parameterFlowThroughAllowed(p, kind)
}
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
parameterFlow(p, ap, ap0, c, config) and
c = ret.getEnclosingCallable() and
revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_),
pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and
fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and
kind = ret.getKind() and
p.getPosition() = pos and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
p.allowParameterReturnInSelf()
)
pragma[nomagic]
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
exists(RetNodeEx ret, ReturnKindExt kind |
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
parameterFlowsThroughRev(p, ap, kind, config)
)
}
pragma[nomagic]
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
exists(ParamNodeEx p, Ap ap |
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
parameterFlowsThroughRev(p, ap, kind, config)
)
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(
Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
revFlow(arg, state, toReturn, returnAp, ap, config) and
revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
revFlow(arg, state, returnCtx, returnAp, ap, config) and
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
}
@@ -1754,14 +1824,13 @@ private module MkStage<StageSig PrevStage> {
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) {
fwd = true and
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and
states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and
tuples =
count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
fwdFlow(n, state, cc, argAp, ap, config)
)
count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config))
or
fwd = false and
nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
@@ -1769,8 +1838,8 @@ private module MkStage<StageSig PrevStage> {
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
states = count(FlowState state | revFlow(_, state, _, _, _, config)) and
tuples =
count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap |
revFlow(n, state, b, retAp, ap, config)
count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap |
revFlow(n, state, returnCtx, retAp, ap, config)
)
}
/* End: Stage logic. */
@@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
exists(lcc)
}
predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
predicate flowIntoCall = flowIntoCallNodeCand1/5;
@@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig {
pragma[nomagic]
private predicate flowOutOfCallNodeCand2(
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
Stage2::revFlowAlias(node1, pragma[only_bind_into](config))
}
@@ -2021,8 +2091,8 @@ private module LocalFlowBigStep {
exists(NodeEx next | Stage2::revFlow(next, state, config) |
jumpStep(node, next, config) or
additionalJumpStep(node, next, config) or
flowIntoCallNodeCand1(_, node, next, config) or
flowOutOfCallNodeCand1(_, node, next, config) or
flowIntoCallNodeCand2(_, node, next, _, config) or
flowOutOfCallNodeCand2(_, node, _, next, _, config) or
Stage2::storeStepCand(node, _, _, next, _, config) or
Stage2::readStepCand(node, _, next, config)
)
@@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
}
predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
predicate flowIntoCall = flowIntoCallNodeCand2/5;
@@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx(
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
) {
exists(AccessPathFront apf |
Stage3::revFlow(node, state, true, _, apf, config) and
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
config)
)
}
@@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
pragma[nomagic]
predicate flowOutOfCall(
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
exists(FlowState state |
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
pragma[only_bind_into](config))
@@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) {
pragma[nomagic]
private predicate nodeMayUseSummary0(
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa,
Configuration config
) {
exists(AccessPathApprox apa0 |
Stage4::parameterMayFlowThrough(_, c, _, _) and
Stage4::revFlow(n, state, true, _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
n.getEnclosingCallable() = c
c = n.getEnclosingCallable() and
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
TAccessPathApproxSome(apa), apa0, config)
)
}
@@ -2522,9 +2595,10 @@ pragma[nomagic]
private predicate nodeMayUseSummary(
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
) {
exists(DataFlowCallable c |
Stage4::parameterMayFlowThrough(_, c, apa, config) and
nodeMayUseSummary0(n, c, state, apa, config)
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
Stage4::parameterMayFlowThrough(p, apa, config) and
nodeMayUseSummary0(n, c, pos, state, apa, config) and
p.isParameterOf(c, pos)
)
}
@@ -2532,7 +2606,7 @@ private newtype TSummaryCtx =
TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
Stage4::revFlow(p, state, _, config)
)
}
@@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough(
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
AccessPathApprox apa, Configuration config
) {
exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos |
exists(PathNodeMid mid, RetNodeEx ret |
pathNode(mid, ret, state, cc, sc, ap, config, _) and
kind = ret.getKind() and
apa = ap.getApprox() and
pos = sc.getParameterPos() and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
sc.getParamNode().allowParameterReturnInSelf()
)
parameterFlowThroughAllowed(sc.getParamNode(), kind)
)
}

View File

@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node source, FlowState state) { none() }
predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes
@@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
}
ParameterPosition getPosition() { this.isParameterOf(_, result) }
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
}
private class RetNodeEx extends NodeEx {
@@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) {
)
}
/**
* Holds if flow from `p` to a return node of kind `kind` is allowed.
*
* We don't expect a parameter to return stored in itself, unless
* explicitly allowed
*/
bindingset[p, kind]
private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) {
exists(ParameterPosition pos | p.isParameterOf(_, pos) |
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
allowParameterReturnInSelfCached(p.asNode())
)
}
/**
* Holds if flow from a parameter at position `pos` inside `c` to a return node of
* kind `kind` is allowed.
*
* We don't expect a parameter to return stored in itself, unless
* explicitly allowed
*/
bindingset[c, pos, kind]
private predicate parameterFlowThroughAllowed(
DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
) {
exists(ParamNodeEx p |
p.isParameterOf(c, pos) and
parameterFlowThroughAllowed(p, kind)
)
}
private module Stage1 implements StageSig {
class Ap = Unit;
@@ -981,21 +1011,22 @@ private module Stage1 implements StageSig {
* candidate for the origin of a summary.
*/
pragma[nomagic]
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
exists(ReturnKindExt kind |
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
exists(DataFlowCallable c, ReturnKindExt kind |
throughFlowNodeCand(p, config) and
returnFlowCallableNodeCand(c, kind, config) and
p.getEnclosingCallable() = c and
exists(ap) and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
or
p.allowParameterReturnInSelf()
)
parameterFlowThroughAllowed(p, kind)
)
}
pragma[nomagic]
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
throughFlowNodeCand(ret, config) and
kind = ret.getKind()
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(ArgNodeEx arg, boolean toReturn |
@@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1(
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config
) {
viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
Stage1::revFlow(ret, config) and
not outBarrier(ret, config) and
not inBarrier(out, config)
exists(ReturnPosition pos |
viableReturnPosOutNodeCand1(call, pos, out, config) and
pos = ret.getReturnPosition() and
kind = pos.getKind() and
Stage1::revFlow(ret, config) and
not outBarrier(ret, config) and
not inBarrier(out, config)
)
}
pragma[nomagic]
@@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1(
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
pragma[nomagic]
private int branch(NodeEx n1, Configuration conf) {
result =
strictcount(NodeEx n |
flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
)
}
@@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) {
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
pragma[nomagic]
private int join(NodeEx n2, Configuration conf) {
result =
strictcount(NodeEx n |
flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
)
}
@@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) {
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
Configuration config
) {
flowOutOfCallNodeCand1(call, ret, out, config) and
flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and
exists(int b, int j |
b = branch(ret, config) and
j = join(out, config) and
b = branch(ret, pragma[only_bind_into](config)) and
j = join(out, pragma[only_bind_into](config)) and
if b.minimum(j) <= config.fieldFlowBranchLimit()
then allowsFieldFlow = true
else allowsFieldFlow = false
@@ -1136,10 +1174,10 @@ pragma[nomagic]
private predicate flowIntoCallNodeCand1(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
flowIntoCallNodeCand1(call, arg, p, config) and
flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and
exists(int b, int j |
b = branch(arg, config) and
j = join(p, config) and
b = branch(arg, pragma[only_bind_into](config)) and
j = join(p, pragma[only_bind_into](config)) and
if b.minimum(j) <= config.fieldFlowBranchLimit()
then allowsFieldFlow = true
else allowsFieldFlow = false
@@ -1156,7 +1194,9 @@ private signature module StageSig {
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
predicate storeStepCand(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
@@ -1222,7 +1262,8 @@ private module MkStage<StageSig PrevStage> {
);
predicate flowOutOfCall(
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
Configuration config
);
predicate flowIntoCall(
@@ -1247,14 +1288,14 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate flowThroughOutOfCall(
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
Configuration config
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
NodeEx out, boolean allowsFieldFlow, Configuration config
) {
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
pragma[only_bind_into](config)) and
matchesCall(ccc, call)
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
matchesCall(ccc, call) and
c = ret.getEnclosingCallable()
}
/**
@@ -1262,29 +1303,32 @@ private module MkStage<StageSig PrevStage> {
* configuration `config`.
*
* The call context `cc` records whether the node is reached through an
* argument in a call, and if so, `argAp` records the access path of that
* argument.
* argument in a call, and if so, `summaryCtx` and `argAp` record the
* corresponding parameter position and access path of that argument, respectively.
*/
pragma[nomagic]
additional predicate fwdFlow(
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, Configuration config
) {
fwdFlow0(node, state, cc, argAp, ap, config) and
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
filter(node, state, ap, config)
}
pragma[nomagic]
private predicate fwdFlow0(
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, Configuration config
) {
sourceNode(node, state, config) and
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
summaryCtx = TParameterPositionNone() and
ap = getApNil(node)
or
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
fwdFlow(mid, state0, cc, argAp, ap0, config) and
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
@@ -1295,65 +1339,82 @@ private module MkStage<StageSig PrevStage> {
)
or
exists(NodeEx mid |
fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone()
)
or
exists(NodeEx mid, ApNil nil |
fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and
fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and
additionalJumpStateStep(mid, state0, node, state, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
or
// store
exists(TypedContent tc, Ap ap0 |
fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
ap = apCons(tc, ap0)
)
or
// read
exists(Ap ap0, Content c |
fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
fwdFlowConsCand(ap0, c, ap, config)
)
or
// flow into a callable
exists(ApApprox apa |
fwdFlowIn(_, node, state, _, cc, _, ap, config) and
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
apa = getApprox(ap) and
if PrevStage::parameterMayFlowThrough(node, _, apa, config)
then argAp = apSome(ap)
else argAp = apNone()
if PrevStage::parameterMayFlowThrough(node, apa, config)
then (
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
argAp = apSome(ap)
) else (
summaryCtx = TParameterPositionNone() and argAp = apNone()
)
)
or
// flow out of a callable
fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
exists(
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
DataFlowCallable inner
|
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and
flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and
inner = ret.getEnclosingCallable() and
cc = getCallContextReturn(inner, call, innercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
or
exists(DataFlowCall call, Ap argAp0 |
fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
fwdFlowIsEntered(call, cc, argAp, argAp0, config)
// flow through a callable
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
)
}
pragma[nomagic]
private predicate fwdFlowStore(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
Configuration config
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
fwdFlow(node1, state, cc, argAp, ap1, config) and
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
@@ -1366,7 +1427,7 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
exists(TypedContent tc |
fwdFlowStore(_, tail, tc, _, _, _, _, config) and
fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and
tc.getContent() = c and
cons = apCons(tc, tail)
)
@@ -1374,21 +1435,21 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate fwdFlowRead(
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
Configuration config
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
fwdFlow(node1, state, cc, argAp, ap, config) and
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::readStepCand(node1, c, node2, config) and
getHeadContent(ap) = c
}
pragma[nomagic]
private predicate fwdFlowIn(
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp,
Ap ap, Configuration config
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc,
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
) {
exists(ArgNodeEx arg, boolean allowsFieldFlow |
fwdFlow(arg, state, outercc, argAp, ap, config) and
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
@@ -1396,29 +1457,19 @@ private module MkStage<StageSig PrevStage> {
}
pragma[nomagic]
private predicate fwdFlowOutNotFromArg(
NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
private predicate fwdFlowOutFromArg(
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
Configuration config
) {
exists(
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
DataFlowCallable inner
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
fwdFlow(ret, state, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
inner = ret.getEnclosingCallable() and
ccOut = getCallContextReturn(inner, call, innercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate fwdFlowOutFromArg(
DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config
) {
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
)
}
@@ -1428,11 +1479,13 @@ private module MkStage<StageSig PrevStage> {
*/
pragma[nomagic]
private predicate fwdFlowIsEntered(
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
ParameterPosition pos, Ap ap, Configuration config
) {
exists(ParamNodeEx p |
fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
exists(ParamNodeEx param |
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
pos = param.getPosition()
)
}
@@ -1440,27 +1493,40 @@ private module MkStage<StageSig PrevStage> {
private predicate storeStepFwd(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
) {
fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and
ap2 = apCons(tc, ap1) and
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config)
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config)
}
private predicate readStepFwd(
NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
) {
fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and
fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and
fwdFlowConsCand(ap1, c, ap2, config)
}
pragma[nomagic]
private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
private predicate returnFlowsThrough0(
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
) {
exists(boolean allowsFieldFlow |
fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
pragma[only_bind_into](config))
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate returnFlowsThrough(
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp,
Ap ap, Configuration config
) {
exists(DataFlowCallable c, ParameterPosition ppos |
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
p.isParameterOf(c, ppos) and
parameterFlowThroughAllowed(p, kind)
)
}
@@ -1468,118 +1534,130 @@ private module MkStage<StageSig PrevStage> {
private predicate flowThroughIntoCall(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
}
pragma[nomagic]
private predicate returnNodeMayFlowThrough(
RetNodeEx ret, FlowState state, Ap ap, Configuration config
) {
fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
exists(Ap argAp |
flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
pragma[only_bind_into](config)) and
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
pragma[only_bind_into](config))
)
}
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
*
* The Boolean `toReturn` records whether the node must be returned from the
* enclosing callable in order to reach a sink, and if so, `returnAp` records
* the access path of the returned value.
* The parameter `returnCtx` records whether (and how) the node must be returned
* from the enclosing callable in order to reach a sink, and if so, `returnAp`
* records the access path of the returned value.
*/
pragma[nomagic]
additional predicate revFlow(
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
Configuration config
) {
revFlow0(node, state, toReturn, returnAp, ap, config) and
fwdFlow(node, state, _, _, ap, config)
revFlow0(node, state, returnCtx, returnAp, ap, config) and
fwdFlow(node, state, _, _, _, ap, config)
}
pragma[nomagic]
private predicate revFlow0(
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
Configuration config
) {
fwdFlow(node, state, _, _, ap, config) and
fwdFlow(node, state, _, _, _, ap, config) and
sinkNode(node, state, config) and
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
(
if hasSinkCallCtx(config)
then returnCtx = TReturnCtxNoFlowThrough()
else returnCtx = TReturnCtxNone()
) and
returnAp = apNone() and
ap instanceof ApNil
or
exists(NodeEx mid, FlowState state0 |
localStep(node, state, mid, state0, true, _, config, _) and
revFlow(mid, state0, toReturn, returnAp, ap, config)
revFlow(mid, state0, returnCtx, returnAp, ap, config)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and
revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, state, _, _, ap, config) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone()
)
or
exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone() and
ap instanceof ApNil
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStateStep(node, state, mid, state0, config) and
revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil,
pragma[only_bind_into](config)) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone() and
ap instanceof ApNil
)
or
// store
exists(Ap ap0, Content c |
revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and
revFlowConsCand(ap0, c, ap, config)
)
or
// read
exists(NodeEx mid, Ap ap0 |
revFlow(mid, state, toReturn, returnAp, ap0, config) and
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
readStepFwd(node, ap, _, mid, ap0, config)
)
or
// flow into a callable
revFlowInNotToReturn(node, state, returnAp, ap, config) and
toReturn = false
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
flowIntoCall(_, node, p, allowsFieldFlow, config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
returnCtx = TReturnCtxNone()
)
or
exists(DataFlowCall call, Ap returnAp0 |
revFlowInToReturn(call, node, state, returnAp0, ap, config) and
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
// flow through a callable
exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 |
revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
or
// flow out of a callable
revFlowOut(_, node, state, _, _, ap, config) and
toReturn = true and
if returnNodeMayFlowThrough(node, state, ap, config)
then returnAp = apSome(ap)
else returnAp = apNone()
exists(ReturnKindExt kind |
revFlowOut(_, node, kind, state, _, _, ap, config) and
if returnFlowsThrough(node, kind, state, _, _, _, ap, config)
then (
returnCtx = TReturnCtxMaybeFlowThrough(kind) and
returnAp = apSome(ap)
) else (
returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone()
)
)
}
pragma[nomagic]
private predicate revFlowStore(
Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid,
boolean toReturn, ApOption returnAp, Configuration config
ReturnCtx returnCtx, ApOption returnAp, Configuration config
) {
revFlow(mid, state, toReturn, returnAp, ap0, config) and
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
storeStepFwd(node, ap, tc, mid, ap0, config) and
tc.getContent() = c
}
@@ -1599,35 +1677,27 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate revFlowOut(
DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap,
Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx,
ApOption returnAp, Ap ap, Configuration config
) {
exists(NodeEx out, boolean allowsFieldFlow |
revFlow(out, state, toReturn, returnAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate revFlowInNotToReturn(
ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, false, returnAp, ap, config) and
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
revFlow(out, state, returnCtx, returnAp, ap, config) and
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate revFlowInToReturn(
DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, true, apSome(returnAp), ap, config) and
revFlow(pragma[only_bind_into](p), state,
TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
parameterFlowThroughAllowed(p, kind)
)
}
@@ -1638,11 +1708,12 @@ private module MkStage<StageSig PrevStage> {
*/
pragma[nomagic]
private predicate revFlowIsReturned(
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap,
Configuration config
) {
exists(RetNodeEx ret, FlowState state, CcCall ccc |
revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and
fwdFlow(ret, state, ccc, apSome(_), ap, config) and
revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and
returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and
matchesCall(ccc, call)
)
}
@@ -1713,40 +1784,39 @@ private module MkStage<StageSig PrevStage> {
validAp(ap, config)
}
pragma[noinline]
private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
pragma[nomagic]
private predicate parameterFlowsThroughRev(
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
) {
revFlow(p, _, true, apSome(ap0), ap, config) and
c = p.getEnclosingCallable()
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
parameterFlowThroughAllowed(p, kind)
}
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
parameterFlow(p, ap, ap0, c, config) and
c = ret.getEnclosingCallable() and
revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_),
pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and
fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and
kind = ret.getKind() and
p.getPosition() = pos and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
p.allowParameterReturnInSelf()
)
pragma[nomagic]
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
exists(RetNodeEx ret, ReturnKindExt kind |
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
parameterFlowsThroughRev(p, ap, kind, config)
)
}
pragma[nomagic]
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
exists(ParamNodeEx p, Ap ap |
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
parameterFlowsThroughRev(p, ap, kind, config)
)
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(
Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
revFlow(arg, state, toReturn, returnAp, ap, config) and
revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
revFlow(arg, state, returnCtx, returnAp, ap, config) and
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
}
@@ -1754,14 +1824,13 @@ private module MkStage<StageSig PrevStage> {
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) {
fwd = true and
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and
states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and
tuples =
count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
fwdFlow(n, state, cc, argAp, ap, config)
)
count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config))
or
fwd = false and
nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
@@ -1769,8 +1838,8 @@ private module MkStage<StageSig PrevStage> {
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
states = count(FlowState state | revFlow(_, state, _, _, _, config)) and
tuples =
count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap |
revFlow(n, state, b, retAp, ap, config)
count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap |
revFlow(n, state, returnCtx, retAp, ap, config)
)
}
/* End: Stage logic. */
@@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
exists(lcc)
}
predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
predicate flowIntoCall = flowIntoCallNodeCand1/5;
@@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig {
pragma[nomagic]
private predicate flowOutOfCallNodeCand2(
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
Stage2::revFlowAlias(node1, pragma[only_bind_into](config))
}
@@ -2021,8 +2091,8 @@ private module LocalFlowBigStep {
exists(NodeEx next | Stage2::revFlow(next, state, config) |
jumpStep(node, next, config) or
additionalJumpStep(node, next, config) or
flowIntoCallNodeCand1(_, node, next, config) or
flowOutOfCallNodeCand1(_, node, next, config) or
flowIntoCallNodeCand2(_, node, next, _, config) or
flowOutOfCallNodeCand2(_, node, _, next, _, config) or
Stage2::storeStepCand(node, _, _, next, _, config) or
Stage2::readStepCand(node, _, next, config)
)
@@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
}
predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
predicate flowIntoCall = flowIntoCallNodeCand2/5;
@@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx(
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
) {
exists(AccessPathFront apf |
Stage3::revFlow(node, state, true, _, apf, config) and
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
config)
)
}
@@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
pragma[nomagic]
predicate flowOutOfCall(
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
exists(FlowState state |
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
pragma[only_bind_into](config))
@@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) {
pragma[nomagic]
private predicate nodeMayUseSummary0(
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa,
Configuration config
) {
exists(AccessPathApprox apa0 |
Stage4::parameterMayFlowThrough(_, c, _, _) and
Stage4::revFlow(n, state, true, _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
n.getEnclosingCallable() = c
c = n.getEnclosingCallable() and
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
TAccessPathApproxSome(apa), apa0, config)
)
}
@@ -2522,9 +2595,10 @@ pragma[nomagic]
private predicate nodeMayUseSummary(
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
) {
exists(DataFlowCallable c |
Stage4::parameterMayFlowThrough(_, c, apa, config) and
nodeMayUseSummary0(n, c, state, apa, config)
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
Stage4::parameterMayFlowThrough(p, apa, config) and
nodeMayUseSummary0(n, c, pos, state, apa, config) and
p.isParameterOf(c, pos)
)
}
@@ -2532,7 +2606,7 @@ private newtype TSummaryCtx =
TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
Stage4::revFlow(p, state, _, config)
)
}
@@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough(
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
AccessPathApprox apa, Configuration config
) {
exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos |
exists(PathNodeMid mid, RetNodeEx ret |
pathNode(mid, ret, state, cc, sc, ap, config, _) and
kind = ret.getKind() and
apa = ap.getApprox() and
pos = sc.getParameterPos() and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
sc.getParamNode().allowParameterReturnInSelf()
)
parameterFlowThroughAllowed(sc.getParamNode(), kind)
)
}

View File

@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node source, FlowState state) { none() }
predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes
@@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
}
ParameterPosition getPosition() { this.isParameterOf(_, result) }
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
}
private class RetNodeEx extends NodeEx {
@@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) {
)
}
/**
* Holds if flow from `p` to a return node of kind `kind` is allowed.
*
* We don't expect a parameter to return stored in itself, unless
* explicitly allowed
*/
bindingset[p, kind]
private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) {
exists(ParameterPosition pos | p.isParameterOf(_, pos) |
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
allowParameterReturnInSelfCached(p.asNode())
)
}
/**
* Holds if flow from a parameter at position `pos` inside `c` to a return node of
* kind `kind` is allowed.
*
* We don't expect a parameter to return stored in itself, unless
* explicitly allowed
*/
bindingset[c, pos, kind]
private predicate parameterFlowThroughAllowed(
DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
) {
exists(ParamNodeEx p |
p.isParameterOf(c, pos) and
parameterFlowThroughAllowed(p, kind)
)
}
private module Stage1 implements StageSig {
class Ap = Unit;
@@ -981,21 +1011,22 @@ private module Stage1 implements StageSig {
* candidate for the origin of a summary.
*/
pragma[nomagic]
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
exists(ReturnKindExt kind |
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
exists(DataFlowCallable c, ReturnKindExt kind |
throughFlowNodeCand(p, config) and
returnFlowCallableNodeCand(c, kind, config) and
p.getEnclosingCallable() = c and
exists(ap) and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
or
p.allowParameterReturnInSelf()
)
parameterFlowThroughAllowed(p, kind)
)
}
pragma[nomagic]
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
throughFlowNodeCand(ret, config) and
kind = ret.getKind()
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(ArgNodeEx arg, boolean toReturn |
@@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1(
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config
) {
viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
Stage1::revFlow(ret, config) and
not outBarrier(ret, config) and
not inBarrier(out, config)
exists(ReturnPosition pos |
viableReturnPosOutNodeCand1(call, pos, out, config) and
pos = ret.getReturnPosition() and
kind = pos.getKind() and
Stage1::revFlow(ret, config) and
not outBarrier(ret, config) and
not inBarrier(out, config)
)
}
pragma[nomagic]
@@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1(
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
pragma[nomagic]
private int branch(NodeEx n1, Configuration conf) {
result =
strictcount(NodeEx n |
flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
)
}
@@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) {
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
pragma[nomagic]
private int join(NodeEx n2, Configuration conf) {
result =
strictcount(NodeEx n |
flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
)
}
@@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) {
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
Configuration config
) {
flowOutOfCallNodeCand1(call, ret, out, config) and
flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and
exists(int b, int j |
b = branch(ret, config) and
j = join(out, config) and
b = branch(ret, pragma[only_bind_into](config)) and
j = join(out, pragma[only_bind_into](config)) and
if b.minimum(j) <= config.fieldFlowBranchLimit()
then allowsFieldFlow = true
else allowsFieldFlow = false
@@ -1136,10 +1174,10 @@ pragma[nomagic]
private predicate flowIntoCallNodeCand1(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
flowIntoCallNodeCand1(call, arg, p, config) and
flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and
exists(int b, int j |
b = branch(arg, config) and
j = join(p, config) and
b = branch(arg, pragma[only_bind_into](config)) and
j = join(p, pragma[only_bind_into](config)) and
if b.minimum(j) <= config.fieldFlowBranchLimit()
then allowsFieldFlow = true
else allowsFieldFlow = false
@@ -1156,7 +1194,9 @@ private signature module StageSig {
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
predicate storeStepCand(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
@@ -1222,7 +1262,8 @@ private module MkStage<StageSig PrevStage> {
);
predicate flowOutOfCall(
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
Configuration config
);
predicate flowIntoCall(
@@ -1247,14 +1288,14 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate flowThroughOutOfCall(
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
Configuration config
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
NodeEx out, boolean allowsFieldFlow, Configuration config
) {
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
pragma[only_bind_into](config)) and
matchesCall(ccc, call)
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
matchesCall(ccc, call) and
c = ret.getEnclosingCallable()
}
/**
@@ -1262,29 +1303,32 @@ private module MkStage<StageSig PrevStage> {
* configuration `config`.
*
* The call context `cc` records whether the node is reached through an
* argument in a call, and if so, `argAp` records the access path of that
* argument.
* argument in a call, and if so, `summaryCtx` and `argAp` record the
* corresponding parameter position and access path of that argument, respectively.
*/
pragma[nomagic]
additional predicate fwdFlow(
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, Configuration config
) {
fwdFlow0(node, state, cc, argAp, ap, config) and
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
filter(node, state, ap, config)
}
pragma[nomagic]
private predicate fwdFlow0(
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, Configuration config
) {
sourceNode(node, state, config) and
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
summaryCtx = TParameterPositionNone() and
ap = getApNil(node)
or
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
fwdFlow(mid, state0, cc, argAp, ap0, config) and
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
@@ -1295,65 +1339,82 @@ private module MkStage<StageSig PrevStage> {
)
or
exists(NodeEx mid |
fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone()
)
or
exists(NodeEx mid, ApNil nil |
fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and
fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and
additionalJumpStateStep(mid, state0, node, state, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
or
// store
exists(TypedContent tc, Ap ap0 |
fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
ap = apCons(tc, ap0)
)
or
// read
exists(Ap ap0, Content c |
fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
fwdFlowConsCand(ap0, c, ap, config)
)
or
// flow into a callable
exists(ApApprox apa |
fwdFlowIn(_, node, state, _, cc, _, ap, config) and
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
apa = getApprox(ap) and
if PrevStage::parameterMayFlowThrough(node, _, apa, config)
then argAp = apSome(ap)
else argAp = apNone()
if PrevStage::parameterMayFlowThrough(node, apa, config)
then (
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
argAp = apSome(ap)
) else (
summaryCtx = TParameterPositionNone() and argAp = apNone()
)
)
or
// flow out of a callable
fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
exists(
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
DataFlowCallable inner
|
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and
flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and
inner = ret.getEnclosingCallable() and
cc = getCallContextReturn(inner, call, innercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
or
exists(DataFlowCall call, Ap argAp0 |
fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
fwdFlowIsEntered(call, cc, argAp, argAp0, config)
// flow through a callable
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
)
}
pragma[nomagic]
private predicate fwdFlowStore(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
Configuration config
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
fwdFlow(node1, state, cc, argAp, ap1, config) and
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
@@ -1366,7 +1427,7 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
exists(TypedContent tc |
fwdFlowStore(_, tail, tc, _, _, _, _, config) and
fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and
tc.getContent() = c and
cons = apCons(tc, tail)
)
@@ -1374,21 +1435,21 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate fwdFlowRead(
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
Configuration config
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
fwdFlow(node1, state, cc, argAp, ap, config) and
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::readStepCand(node1, c, node2, config) and
getHeadContent(ap) = c
}
pragma[nomagic]
private predicate fwdFlowIn(
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp,
Ap ap, Configuration config
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc,
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
) {
exists(ArgNodeEx arg, boolean allowsFieldFlow |
fwdFlow(arg, state, outercc, argAp, ap, config) and
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
@@ -1396,29 +1457,19 @@ private module MkStage<StageSig PrevStage> {
}
pragma[nomagic]
private predicate fwdFlowOutNotFromArg(
NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
private predicate fwdFlowOutFromArg(
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
Configuration config
) {
exists(
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
DataFlowCallable inner
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
fwdFlow(ret, state, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
inner = ret.getEnclosingCallable() and
ccOut = getCallContextReturn(inner, call, innercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate fwdFlowOutFromArg(
DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config
) {
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
)
}
@@ -1428,11 +1479,13 @@ private module MkStage<StageSig PrevStage> {
*/
pragma[nomagic]
private predicate fwdFlowIsEntered(
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
ParameterPosition pos, Ap ap, Configuration config
) {
exists(ParamNodeEx p |
fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
exists(ParamNodeEx param |
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
pos = param.getPosition()
)
}
@@ -1440,27 +1493,40 @@ private module MkStage<StageSig PrevStage> {
private predicate storeStepFwd(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
) {
fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and
ap2 = apCons(tc, ap1) and
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config)
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config)
}
private predicate readStepFwd(
NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
) {
fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and
fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and
fwdFlowConsCand(ap1, c, ap2, config)
}
pragma[nomagic]
private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
private predicate returnFlowsThrough0(
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
) {
exists(boolean allowsFieldFlow |
fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
pragma[only_bind_into](config))
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate returnFlowsThrough(
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp,
Ap ap, Configuration config
) {
exists(DataFlowCallable c, ParameterPosition ppos |
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
p.isParameterOf(c, ppos) and
parameterFlowThroughAllowed(p, kind)
)
}
@@ -1468,118 +1534,130 @@ private module MkStage<StageSig PrevStage> {
private predicate flowThroughIntoCall(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
}
pragma[nomagic]
private predicate returnNodeMayFlowThrough(
RetNodeEx ret, FlowState state, Ap ap, Configuration config
) {
fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
exists(Ap argAp |
flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
pragma[only_bind_into](config)) and
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
pragma[only_bind_into](config))
)
}
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
*
* The Boolean `toReturn` records whether the node must be returned from the
* enclosing callable in order to reach a sink, and if so, `returnAp` records
* the access path of the returned value.
* The parameter `returnCtx` records whether (and how) the node must be returned
* from the enclosing callable in order to reach a sink, and if so, `returnAp`
* records the access path of the returned value.
*/
pragma[nomagic]
additional predicate revFlow(
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
Configuration config
) {
revFlow0(node, state, toReturn, returnAp, ap, config) and
fwdFlow(node, state, _, _, ap, config)
revFlow0(node, state, returnCtx, returnAp, ap, config) and
fwdFlow(node, state, _, _, _, ap, config)
}
pragma[nomagic]
private predicate revFlow0(
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
Configuration config
) {
fwdFlow(node, state, _, _, ap, config) and
fwdFlow(node, state, _, _, _, ap, config) and
sinkNode(node, state, config) and
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
(
if hasSinkCallCtx(config)
then returnCtx = TReturnCtxNoFlowThrough()
else returnCtx = TReturnCtxNone()
) and
returnAp = apNone() and
ap instanceof ApNil
or
exists(NodeEx mid, FlowState state0 |
localStep(node, state, mid, state0, true, _, config, _) and
revFlow(mid, state0, toReturn, returnAp, ap, config)
revFlow(mid, state0, returnCtx, returnAp, ap, config)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and
revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, state, _, _, ap, config) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone()
)
or
exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone() and
ap instanceof ApNil
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStateStep(node, state, mid, state0, config) and
revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil,
pragma[only_bind_into](config)) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone() and
ap instanceof ApNil
)
or
// store
exists(Ap ap0, Content c |
revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and
revFlowConsCand(ap0, c, ap, config)
)
or
// read
exists(NodeEx mid, Ap ap0 |
revFlow(mid, state, toReturn, returnAp, ap0, config) and
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
readStepFwd(node, ap, _, mid, ap0, config)
)
or
// flow into a callable
revFlowInNotToReturn(node, state, returnAp, ap, config) and
toReturn = false
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
flowIntoCall(_, node, p, allowsFieldFlow, config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
returnCtx = TReturnCtxNone()
)
or
exists(DataFlowCall call, Ap returnAp0 |
revFlowInToReturn(call, node, state, returnAp0, ap, config) and
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
// flow through a callable
exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 |
revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
or
// flow out of a callable
revFlowOut(_, node, state, _, _, ap, config) and
toReturn = true and
if returnNodeMayFlowThrough(node, state, ap, config)
then returnAp = apSome(ap)
else returnAp = apNone()
exists(ReturnKindExt kind |
revFlowOut(_, node, kind, state, _, _, ap, config) and
if returnFlowsThrough(node, kind, state, _, _, _, ap, config)
then (
returnCtx = TReturnCtxMaybeFlowThrough(kind) and
returnAp = apSome(ap)
) else (
returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone()
)
)
}
pragma[nomagic]
private predicate revFlowStore(
Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid,
boolean toReturn, ApOption returnAp, Configuration config
ReturnCtx returnCtx, ApOption returnAp, Configuration config
) {
revFlow(mid, state, toReturn, returnAp, ap0, config) and
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
storeStepFwd(node, ap, tc, mid, ap0, config) and
tc.getContent() = c
}
@@ -1599,35 +1677,27 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate revFlowOut(
DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap,
Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx,
ApOption returnAp, Ap ap, Configuration config
) {
exists(NodeEx out, boolean allowsFieldFlow |
revFlow(out, state, toReturn, returnAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate revFlowInNotToReturn(
ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, false, returnAp, ap, config) and
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
revFlow(out, state, returnCtx, returnAp, ap, config) and
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate revFlowInToReturn(
DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, true, apSome(returnAp), ap, config) and
revFlow(pragma[only_bind_into](p), state,
TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
parameterFlowThroughAllowed(p, kind)
)
}
@@ -1638,11 +1708,12 @@ private module MkStage<StageSig PrevStage> {
*/
pragma[nomagic]
private predicate revFlowIsReturned(
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap,
Configuration config
) {
exists(RetNodeEx ret, FlowState state, CcCall ccc |
revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and
fwdFlow(ret, state, ccc, apSome(_), ap, config) and
revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and
returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and
matchesCall(ccc, call)
)
}
@@ -1713,40 +1784,39 @@ private module MkStage<StageSig PrevStage> {
validAp(ap, config)
}
pragma[noinline]
private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
pragma[nomagic]
private predicate parameterFlowsThroughRev(
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
) {
revFlow(p, _, true, apSome(ap0), ap, config) and
c = p.getEnclosingCallable()
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
parameterFlowThroughAllowed(p, kind)
}
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
parameterFlow(p, ap, ap0, c, config) and
c = ret.getEnclosingCallable() and
revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_),
pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and
fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and
kind = ret.getKind() and
p.getPosition() = pos and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
p.allowParameterReturnInSelf()
)
pragma[nomagic]
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
exists(RetNodeEx ret, ReturnKindExt kind |
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
parameterFlowsThroughRev(p, ap, kind, config)
)
}
pragma[nomagic]
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
exists(ParamNodeEx p, Ap ap |
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
parameterFlowsThroughRev(p, ap, kind, config)
)
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(
Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
revFlow(arg, state, toReturn, returnAp, ap, config) and
revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
revFlow(arg, state, returnCtx, returnAp, ap, config) and
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
}
@@ -1754,14 +1824,13 @@ private module MkStage<StageSig PrevStage> {
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) {
fwd = true and
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and
states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and
tuples =
count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
fwdFlow(n, state, cc, argAp, ap, config)
)
count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config))
or
fwd = false and
nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
@@ -1769,8 +1838,8 @@ private module MkStage<StageSig PrevStage> {
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
states = count(FlowState state | revFlow(_, state, _, _, _, config)) and
tuples =
count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap |
revFlow(n, state, b, retAp, ap, config)
count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap |
revFlow(n, state, returnCtx, retAp, ap, config)
)
}
/* End: Stage logic. */
@@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
exists(lcc)
}
predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
predicate flowIntoCall = flowIntoCallNodeCand1/5;
@@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig {
pragma[nomagic]
private predicate flowOutOfCallNodeCand2(
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
Stage2::revFlowAlias(node1, pragma[only_bind_into](config))
}
@@ -2021,8 +2091,8 @@ private module LocalFlowBigStep {
exists(NodeEx next | Stage2::revFlow(next, state, config) |
jumpStep(node, next, config) or
additionalJumpStep(node, next, config) or
flowIntoCallNodeCand1(_, node, next, config) or
flowOutOfCallNodeCand1(_, node, next, config) or
flowIntoCallNodeCand2(_, node, next, _, config) or
flowOutOfCallNodeCand2(_, node, _, next, _, config) or
Stage2::storeStepCand(node, _, _, next, _, config) or
Stage2::readStepCand(node, _, next, config)
)
@@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
}
predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
predicate flowIntoCall = flowIntoCallNodeCand2/5;
@@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx(
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
) {
exists(AccessPathFront apf |
Stage3::revFlow(node, state, true, _, apf, config) and
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
config)
)
}
@@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
pragma[nomagic]
predicate flowOutOfCall(
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
exists(FlowState state |
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
pragma[only_bind_into](config))
@@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) {
pragma[nomagic]
private predicate nodeMayUseSummary0(
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa,
Configuration config
) {
exists(AccessPathApprox apa0 |
Stage4::parameterMayFlowThrough(_, c, _, _) and
Stage4::revFlow(n, state, true, _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
n.getEnclosingCallable() = c
c = n.getEnclosingCallable() and
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
TAccessPathApproxSome(apa), apa0, config)
)
}
@@ -2522,9 +2595,10 @@ pragma[nomagic]
private predicate nodeMayUseSummary(
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
) {
exists(DataFlowCallable c |
Stage4::parameterMayFlowThrough(_, c, apa, config) and
nodeMayUseSummary0(n, c, state, apa, config)
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
Stage4::parameterMayFlowThrough(p, apa, config) and
nodeMayUseSummary0(n, c, pos, state, apa, config) and
p.isParameterOf(c, pos)
)
}
@@ -2532,7 +2606,7 @@ private newtype TSummaryCtx =
TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
Stage4::revFlow(p, state, _, config)
)
}
@@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough(
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
AccessPathApprox apa, Configuration config
) {
exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos |
exists(PathNodeMid mid, RetNodeEx ret |
pathNode(mid, ret, state, cc, sc, ap, config, _) and
kind = ret.getKind() and
apa = ap.getApprox() and
pos = sc.getParameterPos() and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
sc.getParamNode().allowParameterReturnInSelf()
)
parameterFlowThroughAllowed(sc.getParamNode(), kind)
)
}

View File

@@ -915,6 +915,17 @@ private module Cached {
TDataFlowCallNone() or
TDataFlowCallSome(DataFlowCall call)
cached
newtype TParameterPositionOption =
TParameterPositionNone() or
TParameterPositionSome(ParameterPosition pos)
cached
newtype TReturnCtx =
TReturnCtxNone() or
TReturnCtxNoFlowThrough() or
TReturnCtxMaybeFlowThrough(ReturnKindExt kind)
cached
newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) }
@@ -1304,6 +1315,44 @@ class DataFlowCallOption extends TDataFlowCallOption {
}
}
/** An optional `ParameterPosition`. */
class ParameterPositionOption extends TParameterPositionOption {
string toString() {
this = TParameterPositionNone() and
result = "(none)"
or
exists(ParameterPosition pos |
this = TParameterPositionSome(pos) and
result = pos.toString()
)
}
}
/**
* A return context used to calculate flow summaries in reverse flow.
*
* The possible values are:
*
* - `TReturnCtxNone()`: no return flow.
* - `TReturnCtxNoFlowThrough()`: return flow, but flow through is not possible.
* - `TReturnCtxMaybeFlowThrough(ReturnKindExt kind)`: return flow, of kind `kind`, and
* flow through may be possible.
*/
class ReturnCtx extends TReturnCtx {
string toString() {
this = TReturnCtxNone() and
result = "(none)"
or
this = TReturnCtxNoFlowThrough() and
result = "(no flow through)"
or
exists(ReturnKindExt kind |
this = TReturnCtxMaybeFlowThrough(kind) and
result = kind.toString()
)
}
}
/** A `Content` tagged with the type of a containing object. */
class TypedContent extends MkTypedContent {
private Content c;

View File

@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node source, FlowState state) { none() }
predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes
@@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
}
ParameterPosition getPosition() { this.isParameterOf(_, result) }
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
}
private class RetNodeEx extends NodeEx {
@@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) {
)
}
/**
* Holds if flow from `p` to a return node of kind `kind` is allowed.
*
* We don't expect a parameter to return stored in itself, unless
* explicitly allowed
*/
bindingset[p, kind]
private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) {
exists(ParameterPosition pos | p.isParameterOf(_, pos) |
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
allowParameterReturnInSelfCached(p.asNode())
)
}
/**
* Holds if flow from a parameter at position `pos` inside `c` to a return node of
* kind `kind` is allowed.
*
* We don't expect a parameter to return stored in itself, unless
* explicitly allowed
*/
bindingset[c, pos, kind]
private predicate parameterFlowThroughAllowed(
DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
) {
exists(ParamNodeEx p |
p.isParameterOf(c, pos) and
parameterFlowThroughAllowed(p, kind)
)
}
private module Stage1 implements StageSig {
class Ap = Unit;
@@ -981,21 +1011,22 @@ private module Stage1 implements StageSig {
* candidate for the origin of a summary.
*/
pragma[nomagic]
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
exists(ReturnKindExt kind |
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
exists(DataFlowCallable c, ReturnKindExt kind |
throughFlowNodeCand(p, config) and
returnFlowCallableNodeCand(c, kind, config) and
p.getEnclosingCallable() = c and
exists(ap) and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
or
p.allowParameterReturnInSelf()
)
parameterFlowThroughAllowed(p, kind)
)
}
pragma[nomagic]
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
throughFlowNodeCand(ret, config) and
kind = ret.getKind()
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(ArgNodeEx arg, boolean toReturn |
@@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1(
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config
) {
viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
Stage1::revFlow(ret, config) and
not outBarrier(ret, config) and
not inBarrier(out, config)
exists(ReturnPosition pos |
viableReturnPosOutNodeCand1(call, pos, out, config) and
pos = ret.getReturnPosition() and
kind = pos.getKind() and
Stage1::revFlow(ret, config) and
not outBarrier(ret, config) and
not inBarrier(out, config)
)
}
pragma[nomagic]
@@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1(
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
pragma[nomagic]
private int branch(NodeEx n1, Configuration conf) {
result =
strictcount(NodeEx n |
flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
)
}
@@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) {
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
pragma[nomagic]
private int join(NodeEx n2, Configuration conf) {
result =
strictcount(NodeEx n |
flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
)
}
@@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) {
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
Configuration config
) {
flowOutOfCallNodeCand1(call, ret, out, config) and
flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and
exists(int b, int j |
b = branch(ret, config) and
j = join(out, config) and
b = branch(ret, pragma[only_bind_into](config)) and
j = join(out, pragma[only_bind_into](config)) and
if b.minimum(j) <= config.fieldFlowBranchLimit()
then allowsFieldFlow = true
else allowsFieldFlow = false
@@ -1136,10 +1174,10 @@ pragma[nomagic]
private predicate flowIntoCallNodeCand1(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
flowIntoCallNodeCand1(call, arg, p, config) and
flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and
exists(int b, int j |
b = branch(arg, config) and
j = join(p, config) and
b = branch(arg, pragma[only_bind_into](config)) and
j = join(p, pragma[only_bind_into](config)) and
if b.minimum(j) <= config.fieldFlowBranchLimit()
then allowsFieldFlow = true
else allowsFieldFlow = false
@@ -1156,7 +1194,9 @@ private signature module StageSig {
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
predicate storeStepCand(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
@@ -1222,7 +1262,8 @@ private module MkStage<StageSig PrevStage> {
);
predicate flowOutOfCall(
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
Configuration config
);
predicate flowIntoCall(
@@ -1247,14 +1288,14 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate flowThroughOutOfCall(
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
Configuration config
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
NodeEx out, boolean allowsFieldFlow, Configuration config
) {
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
pragma[only_bind_into](config)) and
matchesCall(ccc, call)
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
matchesCall(ccc, call) and
c = ret.getEnclosingCallable()
}
/**
@@ -1262,29 +1303,32 @@ private module MkStage<StageSig PrevStage> {
* configuration `config`.
*
* The call context `cc` records whether the node is reached through an
* argument in a call, and if so, `argAp` records the access path of that
* argument.
* argument in a call, and if so, `summaryCtx` and `argAp` record the
* corresponding parameter position and access path of that argument, respectively.
*/
pragma[nomagic]
additional predicate fwdFlow(
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, Configuration config
) {
fwdFlow0(node, state, cc, argAp, ap, config) and
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
filter(node, state, ap, config)
}
pragma[nomagic]
private predicate fwdFlow0(
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, Configuration config
) {
sourceNode(node, state, config) and
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
summaryCtx = TParameterPositionNone() and
ap = getApNil(node)
or
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
fwdFlow(mid, state0, cc, argAp, ap0, config) and
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
@@ -1295,65 +1339,82 @@ private module MkStage<StageSig PrevStage> {
)
or
exists(NodeEx mid |
fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone()
)
or
exists(NodeEx mid, ApNil nil |
fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and
fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and
additionalJumpStateStep(mid, state0, node, state, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
or
// store
exists(TypedContent tc, Ap ap0 |
fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
ap = apCons(tc, ap0)
)
or
// read
exists(Ap ap0, Content c |
fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
fwdFlowConsCand(ap0, c, ap, config)
)
or
// flow into a callable
exists(ApApprox apa |
fwdFlowIn(_, node, state, _, cc, _, ap, config) and
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
apa = getApprox(ap) and
if PrevStage::parameterMayFlowThrough(node, _, apa, config)
then argAp = apSome(ap)
else argAp = apNone()
if PrevStage::parameterMayFlowThrough(node, apa, config)
then (
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
argAp = apSome(ap)
) else (
summaryCtx = TParameterPositionNone() and argAp = apNone()
)
)
or
// flow out of a callable
fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
exists(
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
DataFlowCallable inner
|
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and
flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and
inner = ret.getEnclosingCallable() and
cc = getCallContextReturn(inner, call, innercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
or
exists(DataFlowCall call, Ap argAp0 |
fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
fwdFlowIsEntered(call, cc, argAp, argAp0, config)
// flow through a callable
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
)
}
pragma[nomagic]
private predicate fwdFlowStore(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
Configuration config
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
fwdFlow(node1, state, cc, argAp, ap1, config) and
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
@@ -1366,7 +1427,7 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
exists(TypedContent tc |
fwdFlowStore(_, tail, tc, _, _, _, _, config) and
fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and
tc.getContent() = c and
cons = apCons(tc, tail)
)
@@ -1374,21 +1435,21 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate fwdFlowRead(
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
Configuration config
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
fwdFlow(node1, state, cc, argAp, ap, config) and
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::readStepCand(node1, c, node2, config) and
getHeadContent(ap) = c
}
pragma[nomagic]
private predicate fwdFlowIn(
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp,
Ap ap, Configuration config
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc,
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
) {
exists(ArgNodeEx arg, boolean allowsFieldFlow |
fwdFlow(arg, state, outercc, argAp, ap, config) and
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
@@ -1396,29 +1457,19 @@ private module MkStage<StageSig PrevStage> {
}
pragma[nomagic]
private predicate fwdFlowOutNotFromArg(
NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
private predicate fwdFlowOutFromArg(
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
Configuration config
) {
exists(
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
DataFlowCallable inner
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
fwdFlow(ret, state, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
inner = ret.getEnclosingCallable() and
ccOut = getCallContextReturn(inner, call, innercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate fwdFlowOutFromArg(
DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config
) {
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
)
}
@@ -1428,11 +1479,13 @@ private module MkStage<StageSig PrevStage> {
*/
pragma[nomagic]
private predicate fwdFlowIsEntered(
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
ParameterPosition pos, Ap ap, Configuration config
) {
exists(ParamNodeEx p |
fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
exists(ParamNodeEx param |
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
pos = param.getPosition()
)
}
@@ -1440,27 +1493,40 @@ private module MkStage<StageSig PrevStage> {
private predicate storeStepFwd(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
) {
fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and
ap2 = apCons(tc, ap1) and
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config)
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config)
}
private predicate readStepFwd(
NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
) {
fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and
fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and
fwdFlowConsCand(ap1, c, ap2, config)
}
pragma[nomagic]
private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
private predicate returnFlowsThrough0(
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
) {
exists(boolean allowsFieldFlow |
fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
pragma[only_bind_into](config))
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate returnFlowsThrough(
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp,
Ap ap, Configuration config
) {
exists(DataFlowCallable c, ParameterPosition ppos |
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
p.isParameterOf(c, ppos) and
parameterFlowThroughAllowed(p, kind)
)
}
@@ -1468,118 +1534,130 @@ private module MkStage<StageSig PrevStage> {
private predicate flowThroughIntoCall(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
}
pragma[nomagic]
private predicate returnNodeMayFlowThrough(
RetNodeEx ret, FlowState state, Ap ap, Configuration config
) {
fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
exists(Ap argAp |
flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
pragma[only_bind_into](config)) and
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
pragma[only_bind_into](config))
)
}
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
*
* The Boolean `toReturn` records whether the node must be returned from the
* enclosing callable in order to reach a sink, and if so, `returnAp` records
* the access path of the returned value.
* The parameter `returnCtx` records whether (and how) the node must be returned
* from the enclosing callable in order to reach a sink, and if so, `returnAp`
* records the access path of the returned value.
*/
pragma[nomagic]
additional predicate revFlow(
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
Configuration config
) {
revFlow0(node, state, toReturn, returnAp, ap, config) and
fwdFlow(node, state, _, _, ap, config)
revFlow0(node, state, returnCtx, returnAp, ap, config) and
fwdFlow(node, state, _, _, _, ap, config)
}
pragma[nomagic]
private predicate revFlow0(
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
Configuration config
) {
fwdFlow(node, state, _, _, ap, config) and
fwdFlow(node, state, _, _, _, ap, config) and
sinkNode(node, state, config) and
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
(
if hasSinkCallCtx(config)
then returnCtx = TReturnCtxNoFlowThrough()
else returnCtx = TReturnCtxNone()
) and
returnAp = apNone() and
ap instanceof ApNil
or
exists(NodeEx mid, FlowState state0 |
localStep(node, state, mid, state0, true, _, config, _) and
revFlow(mid, state0, toReturn, returnAp, ap, config)
revFlow(mid, state0, returnCtx, returnAp, ap, config)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and
revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, state, _, _, ap, config) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone()
)
or
exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone() and
ap instanceof ApNil
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStateStep(node, state, mid, state0, config) and
revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil,
pragma[only_bind_into](config)) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone() and
ap instanceof ApNil
)
or
// store
exists(Ap ap0, Content c |
revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and
revFlowConsCand(ap0, c, ap, config)
)
or
// read
exists(NodeEx mid, Ap ap0 |
revFlow(mid, state, toReturn, returnAp, ap0, config) and
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
readStepFwd(node, ap, _, mid, ap0, config)
)
or
// flow into a callable
revFlowInNotToReturn(node, state, returnAp, ap, config) and
toReturn = false
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
flowIntoCall(_, node, p, allowsFieldFlow, config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
returnCtx = TReturnCtxNone()
)
or
exists(DataFlowCall call, Ap returnAp0 |
revFlowInToReturn(call, node, state, returnAp0, ap, config) and
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
// flow through a callable
exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 |
revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
or
// flow out of a callable
revFlowOut(_, node, state, _, _, ap, config) and
toReturn = true and
if returnNodeMayFlowThrough(node, state, ap, config)
then returnAp = apSome(ap)
else returnAp = apNone()
exists(ReturnKindExt kind |
revFlowOut(_, node, kind, state, _, _, ap, config) and
if returnFlowsThrough(node, kind, state, _, _, _, ap, config)
then (
returnCtx = TReturnCtxMaybeFlowThrough(kind) and
returnAp = apSome(ap)
) else (
returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone()
)
)
}
pragma[nomagic]
private predicate revFlowStore(
Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid,
boolean toReturn, ApOption returnAp, Configuration config
ReturnCtx returnCtx, ApOption returnAp, Configuration config
) {
revFlow(mid, state, toReturn, returnAp, ap0, config) and
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
storeStepFwd(node, ap, tc, mid, ap0, config) and
tc.getContent() = c
}
@@ -1599,35 +1677,27 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate revFlowOut(
DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap,
Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx,
ApOption returnAp, Ap ap, Configuration config
) {
exists(NodeEx out, boolean allowsFieldFlow |
revFlow(out, state, toReturn, returnAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate revFlowInNotToReturn(
ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, false, returnAp, ap, config) and
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
revFlow(out, state, returnCtx, returnAp, ap, config) and
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate revFlowInToReturn(
DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, true, apSome(returnAp), ap, config) and
revFlow(pragma[only_bind_into](p), state,
TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
parameterFlowThroughAllowed(p, kind)
)
}
@@ -1638,11 +1708,12 @@ private module MkStage<StageSig PrevStage> {
*/
pragma[nomagic]
private predicate revFlowIsReturned(
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap,
Configuration config
) {
exists(RetNodeEx ret, FlowState state, CcCall ccc |
revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and
fwdFlow(ret, state, ccc, apSome(_), ap, config) and
revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and
returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and
matchesCall(ccc, call)
)
}
@@ -1713,40 +1784,39 @@ private module MkStage<StageSig PrevStage> {
validAp(ap, config)
}
pragma[noinline]
private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
pragma[nomagic]
private predicate parameterFlowsThroughRev(
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
) {
revFlow(p, _, true, apSome(ap0), ap, config) and
c = p.getEnclosingCallable()
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
parameterFlowThroughAllowed(p, kind)
}
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
parameterFlow(p, ap, ap0, c, config) and
c = ret.getEnclosingCallable() and
revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_),
pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and
fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and
kind = ret.getKind() and
p.getPosition() = pos and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
p.allowParameterReturnInSelf()
)
pragma[nomagic]
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
exists(RetNodeEx ret, ReturnKindExt kind |
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
parameterFlowsThroughRev(p, ap, kind, config)
)
}
pragma[nomagic]
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
exists(ParamNodeEx p, Ap ap |
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
parameterFlowsThroughRev(p, ap, kind, config)
)
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(
Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
revFlow(arg, state, toReturn, returnAp, ap, config) and
revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
revFlow(arg, state, returnCtx, returnAp, ap, config) and
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
}
@@ -1754,14 +1824,13 @@ private module MkStage<StageSig PrevStage> {
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) {
fwd = true and
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and
states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and
tuples =
count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
fwdFlow(n, state, cc, argAp, ap, config)
)
count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config))
or
fwd = false and
nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
@@ -1769,8 +1838,8 @@ private module MkStage<StageSig PrevStage> {
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
states = count(FlowState state | revFlow(_, state, _, _, _, config)) and
tuples =
count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap |
revFlow(n, state, b, retAp, ap, config)
count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap |
revFlow(n, state, returnCtx, retAp, ap, config)
)
}
/* End: Stage logic. */
@@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
exists(lcc)
}
predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
predicate flowIntoCall = flowIntoCallNodeCand1/5;
@@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig {
pragma[nomagic]
private predicate flowOutOfCallNodeCand2(
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
Stage2::revFlowAlias(node1, pragma[only_bind_into](config))
}
@@ -2021,8 +2091,8 @@ private module LocalFlowBigStep {
exists(NodeEx next | Stage2::revFlow(next, state, config) |
jumpStep(node, next, config) or
additionalJumpStep(node, next, config) or
flowIntoCallNodeCand1(_, node, next, config) or
flowOutOfCallNodeCand1(_, node, next, config) or
flowIntoCallNodeCand2(_, node, next, _, config) or
flowOutOfCallNodeCand2(_, node, _, next, _, config) or
Stage2::storeStepCand(node, _, _, next, _, config) or
Stage2::readStepCand(node, _, next, config)
)
@@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
}
predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
predicate flowIntoCall = flowIntoCallNodeCand2/5;
@@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx(
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
) {
exists(AccessPathFront apf |
Stage3::revFlow(node, state, true, _, apf, config) and
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
config)
)
}
@@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
pragma[nomagic]
predicate flowOutOfCall(
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
exists(FlowState state |
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
pragma[only_bind_into](config))
@@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) {
pragma[nomagic]
private predicate nodeMayUseSummary0(
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa,
Configuration config
) {
exists(AccessPathApprox apa0 |
Stage4::parameterMayFlowThrough(_, c, _, _) and
Stage4::revFlow(n, state, true, _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
n.getEnclosingCallable() = c
c = n.getEnclosingCallable() and
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
TAccessPathApproxSome(apa), apa0, config)
)
}
@@ -2522,9 +2595,10 @@ pragma[nomagic]
private predicate nodeMayUseSummary(
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
) {
exists(DataFlowCallable c |
Stage4::parameterMayFlowThrough(_, c, apa, config) and
nodeMayUseSummary0(n, c, state, apa, config)
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
Stage4::parameterMayFlowThrough(p, apa, config) and
nodeMayUseSummary0(n, c, pos, state, apa, config) and
p.isParameterOf(c, pos)
)
}
@@ -2532,7 +2606,7 @@ private newtype TSummaryCtx =
TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
Stage4::revFlow(p, state, _, config)
)
}
@@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough(
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
AccessPathApprox apa, Configuration config
) {
exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos |
exists(PathNodeMid mid, RetNodeEx ret |
pathNode(mid, ret, state, cc, sc, ap, config, _) and
kind = ret.getKind() and
apa = ap.getApprox() and
pos = sc.getParameterPos() and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
sc.getParamNode().allowParameterReturnInSelf()
)
parameterFlowThroughAllowed(sc.getParamNode(), kind)
)
}

View File

@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node source, FlowState state) { none() }
predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes
@@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
}
ParameterPosition getPosition() { this.isParameterOf(_, result) }
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
}
private class RetNodeEx extends NodeEx {
@@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) {
)
}
/**
* Holds if flow from `p` to a return node of kind `kind` is allowed.
*
* We don't expect a parameter to return stored in itself, unless
* explicitly allowed
*/
bindingset[p, kind]
private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) {
exists(ParameterPosition pos | p.isParameterOf(_, pos) |
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
allowParameterReturnInSelfCached(p.asNode())
)
}
/**
* Holds if flow from a parameter at position `pos` inside `c` to a return node of
* kind `kind` is allowed.
*
* We don't expect a parameter to return stored in itself, unless
* explicitly allowed
*/
bindingset[c, pos, kind]
private predicate parameterFlowThroughAllowed(
DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
) {
exists(ParamNodeEx p |
p.isParameterOf(c, pos) and
parameterFlowThroughAllowed(p, kind)
)
}
private module Stage1 implements StageSig {
class Ap = Unit;
@@ -981,21 +1011,22 @@ private module Stage1 implements StageSig {
* candidate for the origin of a summary.
*/
pragma[nomagic]
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
exists(ReturnKindExt kind |
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
exists(DataFlowCallable c, ReturnKindExt kind |
throughFlowNodeCand(p, config) and
returnFlowCallableNodeCand(c, kind, config) and
p.getEnclosingCallable() = c and
exists(ap) and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
or
p.allowParameterReturnInSelf()
)
parameterFlowThroughAllowed(p, kind)
)
}
pragma[nomagic]
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
throughFlowNodeCand(ret, config) and
kind = ret.getKind()
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(ArgNodeEx arg, boolean toReturn |
@@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1(
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config
) {
viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
Stage1::revFlow(ret, config) and
not outBarrier(ret, config) and
not inBarrier(out, config)
exists(ReturnPosition pos |
viableReturnPosOutNodeCand1(call, pos, out, config) and
pos = ret.getReturnPosition() and
kind = pos.getKind() and
Stage1::revFlow(ret, config) and
not outBarrier(ret, config) and
not inBarrier(out, config)
)
}
pragma[nomagic]
@@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1(
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
pragma[nomagic]
private int branch(NodeEx n1, Configuration conf) {
result =
strictcount(NodeEx n |
flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
)
}
@@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) {
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
pragma[nomagic]
private int join(NodeEx n2, Configuration conf) {
result =
strictcount(NodeEx n |
flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
)
}
@@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) {
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
Configuration config
) {
flowOutOfCallNodeCand1(call, ret, out, config) and
flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and
exists(int b, int j |
b = branch(ret, config) and
j = join(out, config) and
b = branch(ret, pragma[only_bind_into](config)) and
j = join(out, pragma[only_bind_into](config)) and
if b.minimum(j) <= config.fieldFlowBranchLimit()
then allowsFieldFlow = true
else allowsFieldFlow = false
@@ -1136,10 +1174,10 @@ pragma[nomagic]
private predicate flowIntoCallNodeCand1(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
flowIntoCallNodeCand1(call, arg, p, config) and
flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and
exists(int b, int j |
b = branch(arg, config) and
j = join(p, config) and
b = branch(arg, pragma[only_bind_into](config)) and
j = join(p, pragma[only_bind_into](config)) and
if b.minimum(j) <= config.fieldFlowBranchLimit()
then allowsFieldFlow = true
else allowsFieldFlow = false
@@ -1156,7 +1194,9 @@ private signature module StageSig {
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
predicate storeStepCand(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
@@ -1222,7 +1262,8 @@ private module MkStage<StageSig PrevStage> {
);
predicate flowOutOfCall(
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
Configuration config
);
predicate flowIntoCall(
@@ -1247,14 +1288,14 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate flowThroughOutOfCall(
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
Configuration config
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
NodeEx out, boolean allowsFieldFlow, Configuration config
) {
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
pragma[only_bind_into](config)) and
matchesCall(ccc, call)
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
matchesCall(ccc, call) and
c = ret.getEnclosingCallable()
}
/**
@@ -1262,29 +1303,32 @@ private module MkStage<StageSig PrevStage> {
* configuration `config`.
*
* The call context `cc` records whether the node is reached through an
* argument in a call, and if so, `argAp` records the access path of that
* argument.
* argument in a call, and if so, `summaryCtx` and `argAp` record the
* corresponding parameter position and access path of that argument, respectively.
*/
pragma[nomagic]
additional predicate fwdFlow(
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, Configuration config
) {
fwdFlow0(node, state, cc, argAp, ap, config) and
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
filter(node, state, ap, config)
}
pragma[nomagic]
private predicate fwdFlow0(
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, Configuration config
) {
sourceNode(node, state, config) and
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
summaryCtx = TParameterPositionNone() and
ap = getApNil(node)
or
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
fwdFlow(mid, state0, cc, argAp, ap0, config) and
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
@@ -1295,65 +1339,82 @@ private module MkStage<StageSig PrevStage> {
)
or
exists(NodeEx mid |
fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone()
)
or
exists(NodeEx mid, ApNil nil |
fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and
fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and
additionalJumpStateStep(mid, state0, node, state, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
or
// store
exists(TypedContent tc, Ap ap0 |
fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
ap = apCons(tc, ap0)
)
or
// read
exists(Ap ap0, Content c |
fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
fwdFlowConsCand(ap0, c, ap, config)
)
or
// flow into a callable
exists(ApApprox apa |
fwdFlowIn(_, node, state, _, cc, _, ap, config) and
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
apa = getApprox(ap) and
if PrevStage::parameterMayFlowThrough(node, _, apa, config)
then argAp = apSome(ap)
else argAp = apNone()
if PrevStage::parameterMayFlowThrough(node, apa, config)
then (
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
argAp = apSome(ap)
) else (
summaryCtx = TParameterPositionNone() and argAp = apNone()
)
)
or
// flow out of a callable
fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
exists(
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
DataFlowCallable inner
|
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and
flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and
inner = ret.getEnclosingCallable() and
cc = getCallContextReturn(inner, call, innercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
or
exists(DataFlowCall call, Ap argAp0 |
fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
fwdFlowIsEntered(call, cc, argAp, argAp0, config)
// flow through a callable
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
)
}
pragma[nomagic]
private predicate fwdFlowStore(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
Configuration config
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
fwdFlow(node1, state, cc, argAp, ap1, config) and
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
@@ -1366,7 +1427,7 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
exists(TypedContent tc |
fwdFlowStore(_, tail, tc, _, _, _, _, config) and
fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and
tc.getContent() = c and
cons = apCons(tc, tail)
)
@@ -1374,21 +1435,21 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate fwdFlowRead(
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
Configuration config
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
fwdFlow(node1, state, cc, argAp, ap, config) and
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::readStepCand(node1, c, node2, config) and
getHeadContent(ap) = c
}
pragma[nomagic]
private predicate fwdFlowIn(
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp,
Ap ap, Configuration config
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc,
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
) {
exists(ArgNodeEx arg, boolean allowsFieldFlow |
fwdFlow(arg, state, outercc, argAp, ap, config) and
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
@@ -1396,29 +1457,19 @@ private module MkStage<StageSig PrevStage> {
}
pragma[nomagic]
private predicate fwdFlowOutNotFromArg(
NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
private predicate fwdFlowOutFromArg(
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
Configuration config
) {
exists(
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
DataFlowCallable inner
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
fwdFlow(ret, state, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
inner = ret.getEnclosingCallable() and
ccOut = getCallContextReturn(inner, call, innercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate fwdFlowOutFromArg(
DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config
) {
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
)
}
@@ -1428,11 +1479,13 @@ private module MkStage<StageSig PrevStage> {
*/
pragma[nomagic]
private predicate fwdFlowIsEntered(
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
ParameterPosition pos, Ap ap, Configuration config
) {
exists(ParamNodeEx p |
fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
exists(ParamNodeEx param |
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
pos = param.getPosition()
)
}
@@ -1440,27 +1493,40 @@ private module MkStage<StageSig PrevStage> {
private predicate storeStepFwd(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
) {
fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and
ap2 = apCons(tc, ap1) and
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config)
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config)
}
private predicate readStepFwd(
NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
) {
fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and
fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and
fwdFlowConsCand(ap1, c, ap2, config)
}
pragma[nomagic]
private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
private predicate returnFlowsThrough0(
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
) {
exists(boolean allowsFieldFlow |
fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
pragma[only_bind_into](config))
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate returnFlowsThrough(
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp,
Ap ap, Configuration config
) {
exists(DataFlowCallable c, ParameterPosition ppos |
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
p.isParameterOf(c, ppos) and
parameterFlowThroughAllowed(p, kind)
)
}
@@ -1468,118 +1534,130 @@ private module MkStage<StageSig PrevStage> {
private predicate flowThroughIntoCall(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
}
pragma[nomagic]
private predicate returnNodeMayFlowThrough(
RetNodeEx ret, FlowState state, Ap ap, Configuration config
) {
fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
exists(Ap argAp |
flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
pragma[only_bind_into](config)) and
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
pragma[only_bind_into](config))
)
}
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
*
* The Boolean `toReturn` records whether the node must be returned from the
* enclosing callable in order to reach a sink, and if so, `returnAp` records
* the access path of the returned value.
* The parameter `returnCtx` records whether (and how) the node must be returned
* from the enclosing callable in order to reach a sink, and if so, `returnAp`
* records the access path of the returned value.
*/
pragma[nomagic]
additional predicate revFlow(
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
Configuration config
) {
revFlow0(node, state, toReturn, returnAp, ap, config) and
fwdFlow(node, state, _, _, ap, config)
revFlow0(node, state, returnCtx, returnAp, ap, config) and
fwdFlow(node, state, _, _, _, ap, config)
}
pragma[nomagic]
private predicate revFlow0(
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
Configuration config
) {
fwdFlow(node, state, _, _, ap, config) and
fwdFlow(node, state, _, _, _, ap, config) and
sinkNode(node, state, config) and
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
(
if hasSinkCallCtx(config)
then returnCtx = TReturnCtxNoFlowThrough()
else returnCtx = TReturnCtxNone()
) and
returnAp = apNone() and
ap instanceof ApNil
or
exists(NodeEx mid, FlowState state0 |
localStep(node, state, mid, state0, true, _, config, _) and
revFlow(mid, state0, toReturn, returnAp, ap, config)
revFlow(mid, state0, returnCtx, returnAp, ap, config)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and
revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, state, _, _, ap, config) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone()
)
or
exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone() and
ap instanceof ApNil
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStateStep(node, state, mid, state0, config) and
revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil,
pragma[only_bind_into](config)) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone() and
ap instanceof ApNil
)
or
// store
exists(Ap ap0, Content c |
revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and
revFlowConsCand(ap0, c, ap, config)
)
or
// read
exists(NodeEx mid, Ap ap0 |
revFlow(mid, state, toReturn, returnAp, ap0, config) and
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
readStepFwd(node, ap, _, mid, ap0, config)
)
or
// flow into a callable
revFlowInNotToReturn(node, state, returnAp, ap, config) and
toReturn = false
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
flowIntoCall(_, node, p, allowsFieldFlow, config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
returnCtx = TReturnCtxNone()
)
or
exists(DataFlowCall call, Ap returnAp0 |
revFlowInToReturn(call, node, state, returnAp0, ap, config) and
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
// flow through a callable
exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 |
revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
or
// flow out of a callable
revFlowOut(_, node, state, _, _, ap, config) and
toReturn = true and
if returnNodeMayFlowThrough(node, state, ap, config)
then returnAp = apSome(ap)
else returnAp = apNone()
exists(ReturnKindExt kind |
revFlowOut(_, node, kind, state, _, _, ap, config) and
if returnFlowsThrough(node, kind, state, _, _, _, ap, config)
then (
returnCtx = TReturnCtxMaybeFlowThrough(kind) and
returnAp = apSome(ap)
) else (
returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone()
)
)
}
pragma[nomagic]
private predicate revFlowStore(
Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid,
boolean toReturn, ApOption returnAp, Configuration config
ReturnCtx returnCtx, ApOption returnAp, Configuration config
) {
revFlow(mid, state, toReturn, returnAp, ap0, config) and
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
storeStepFwd(node, ap, tc, mid, ap0, config) and
tc.getContent() = c
}
@@ -1599,35 +1677,27 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate revFlowOut(
DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap,
Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx,
ApOption returnAp, Ap ap, Configuration config
) {
exists(NodeEx out, boolean allowsFieldFlow |
revFlow(out, state, toReturn, returnAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate revFlowInNotToReturn(
ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, false, returnAp, ap, config) and
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
revFlow(out, state, returnCtx, returnAp, ap, config) and
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate revFlowInToReturn(
DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, true, apSome(returnAp), ap, config) and
revFlow(pragma[only_bind_into](p), state,
TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
parameterFlowThroughAllowed(p, kind)
)
}
@@ -1638,11 +1708,12 @@ private module MkStage<StageSig PrevStage> {
*/
pragma[nomagic]
private predicate revFlowIsReturned(
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap,
Configuration config
) {
exists(RetNodeEx ret, FlowState state, CcCall ccc |
revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and
fwdFlow(ret, state, ccc, apSome(_), ap, config) and
revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and
returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and
matchesCall(ccc, call)
)
}
@@ -1713,40 +1784,39 @@ private module MkStage<StageSig PrevStage> {
validAp(ap, config)
}
pragma[noinline]
private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
pragma[nomagic]
private predicate parameterFlowsThroughRev(
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
) {
revFlow(p, _, true, apSome(ap0), ap, config) and
c = p.getEnclosingCallable()
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
parameterFlowThroughAllowed(p, kind)
}
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
parameterFlow(p, ap, ap0, c, config) and
c = ret.getEnclosingCallable() and
revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_),
pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and
fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and
kind = ret.getKind() and
p.getPosition() = pos and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
p.allowParameterReturnInSelf()
)
pragma[nomagic]
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
exists(RetNodeEx ret, ReturnKindExt kind |
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
parameterFlowsThroughRev(p, ap, kind, config)
)
}
pragma[nomagic]
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
exists(ParamNodeEx p, Ap ap |
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
parameterFlowsThroughRev(p, ap, kind, config)
)
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(
Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
revFlow(arg, state, toReturn, returnAp, ap, config) and
revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
revFlow(arg, state, returnCtx, returnAp, ap, config) and
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
}
@@ -1754,14 +1824,13 @@ private module MkStage<StageSig PrevStage> {
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) {
fwd = true and
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and
states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and
tuples =
count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
fwdFlow(n, state, cc, argAp, ap, config)
)
count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config))
or
fwd = false and
nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
@@ -1769,8 +1838,8 @@ private module MkStage<StageSig PrevStage> {
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
states = count(FlowState state | revFlow(_, state, _, _, _, config)) and
tuples =
count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap |
revFlow(n, state, b, retAp, ap, config)
count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap |
revFlow(n, state, returnCtx, retAp, ap, config)
)
}
/* End: Stage logic. */
@@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
exists(lcc)
}
predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
predicate flowIntoCall = flowIntoCallNodeCand1/5;
@@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig {
pragma[nomagic]
private predicate flowOutOfCallNodeCand2(
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
Stage2::revFlowAlias(node1, pragma[only_bind_into](config))
}
@@ -2021,8 +2091,8 @@ private module LocalFlowBigStep {
exists(NodeEx next | Stage2::revFlow(next, state, config) |
jumpStep(node, next, config) or
additionalJumpStep(node, next, config) or
flowIntoCallNodeCand1(_, node, next, config) or
flowOutOfCallNodeCand1(_, node, next, config) or
flowIntoCallNodeCand2(_, node, next, _, config) or
flowOutOfCallNodeCand2(_, node, _, next, _, config) or
Stage2::storeStepCand(node, _, _, next, _, config) or
Stage2::readStepCand(node, _, next, config)
)
@@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
}
predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
predicate flowIntoCall = flowIntoCallNodeCand2/5;
@@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx(
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
) {
exists(AccessPathFront apf |
Stage3::revFlow(node, state, true, _, apf, config) and
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
config)
)
}
@@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
pragma[nomagic]
predicate flowOutOfCall(
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
exists(FlowState state |
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
pragma[only_bind_into](config))
@@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) {
pragma[nomagic]
private predicate nodeMayUseSummary0(
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa,
Configuration config
) {
exists(AccessPathApprox apa0 |
Stage4::parameterMayFlowThrough(_, c, _, _) and
Stage4::revFlow(n, state, true, _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
n.getEnclosingCallable() = c
c = n.getEnclosingCallable() and
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
TAccessPathApproxSome(apa), apa0, config)
)
}
@@ -2522,9 +2595,10 @@ pragma[nomagic]
private predicate nodeMayUseSummary(
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
) {
exists(DataFlowCallable c |
Stage4::parameterMayFlowThrough(_, c, apa, config) and
nodeMayUseSummary0(n, c, state, apa, config)
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
Stage4::parameterMayFlowThrough(p, apa, config) and
nodeMayUseSummary0(n, c, pos, state, apa, config) and
p.isParameterOf(c, pos)
)
}
@@ -2532,7 +2606,7 @@ private newtype TSummaryCtx =
TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
Stage4::revFlow(p, state, _, config)
)
}
@@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough(
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
AccessPathApprox apa, Configuration config
) {
exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos |
exists(PathNodeMid mid, RetNodeEx ret |
pathNode(mid, ret, state, cc, sc, ap, config, _) and
kind = ret.getKind() and
apa = ap.getApprox() and
pos = sc.getParameterPos() and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
sc.getParamNode().allowParameterReturnInSelf()
)
parameterFlowThroughAllowed(sc.getParamNode(), kind)
)
}

View File

@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node source, FlowState state) { none() }
predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes
@@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
}
ParameterPosition getPosition() { this.isParameterOf(_, result) }
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
}
private class RetNodeEx extends NodeEx {
@@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) {
)
}
/**
* Holds if flow from `p` to a return node of kind `kind` is allowed.
*
* We don't expect a parameter to return stored in itself, unless
* explicitly allowed
*/
bindingset[p, kind]
private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) {
exists(ParameterPosition pos | p.isParameterOf(_, pos) |
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
allowParameterReturnInSelfCached(p.asNode())
)
}
/**
* Holds if flow from a parameter at position `pos` inside `c` to a return node of
* kind `kind` is allowed.
*
* We don't expect a parameter to return stored in itself, unless
* explicitly allowed
*/
bindingset[c, pos, kind]
private predicate parameterFlowThroughAllowed(
DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
) {
exists(ParamNodeEx p |
p.isParameterOf(c, pos) and
parameterFlowThroughAllowed(p, kind)
)
}
private module Stage1 implements StageSig {
class Ap = Unit;
@@ -981,21 +1011,22 @@ private module Stage1 implements StageSig {
* candidate for the origin of a summary.
*/
pragma[nomagic]
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
exists(ReturnKindExt kind |
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
exists(DataFlowCallable c, ReturnKindExt kind |
throughFlowNodeCand(p, config) and
returnFlowCallableNodeCand(c, kind, config) and
p.getEnclosingCallable() = c and
exists(ap) and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
or
p.allowParameterReturnInSelf()
)
parameterFlowThroughAllowed(p, kind)
)
}
pragma[nomagic]
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
throughFlowNodeCand(ret, config) and
kind = ret.getKind()
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(ArgNodeEx arg, boolean toReturn |
@@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1(
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config
) {
viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
Stage1::revFlow(ret, config) and
not outBarrier(ret, config) and
not inBarrier(out, config)
exists(ReturnPosition pos |
viableReturnPosOutNodeCand1(call, pos, out, config) and
pos = ret.getReturnPosition() and
kind = pos.getKind() and
Stage1::revFlow(ret, config) and
not outBarrier(ret, config) and
not inBarrier(out, config)
)
}
pragma[nomagic]
@@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1(
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
pragma[nomagic]
private int branch(NodeEx n1, Configuration conf) {
result =
strictcount(NodeEx n |
flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
)
}
@@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) {
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
pragma[nomagic]
private int join(NodeEx n2, Configuration conf) {
result =
strictcount(NodeEx n |
flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
)
}
@@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) {
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
Configuration config
) {
flowOutOfCallNodeCand1(call, ret, out, config) and
flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and
exists(int b, int j |
b = branch(ret, config) and
j = join(out, config) and
b = branch(ret, pragma[only_bind_into](config)) and
j = join(out, pragma[only_bind_into](config)) and
if b.minimum(j) <= config.fieldFlowBranchLimit()
then allowsFieldFlow = true
else allowsFieldFlow = false
@@ -1136,10 +1174,10 @@ pragma[nomagic]
private predicate flowIntoCallNodeCand1(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
flowIntoCallNodeCand1(call, arg, p, config) and
flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and
exists(int b, int j |
b = branch(arg, config) and
j = join(p, config) and
b = branch(arg, pragma[only_bind_into](config)) and
j = join(p, pragma[only_bind_into](config)) and
if b.minimum(j) <= config.fieldFlowBranchLimit()
then allowsFieldFlow = true
else allowsFieldFlow = false
@@ -1156,7 +1194,9 @@ private signature module StageSig {
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
predicate storeStepCand(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
@@ -1222,7 +1262,8 @@ private module MkStage<StageSig PrevStage> {
);
predicate flowOutOfCall(
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
Configuration config
);
predicate flowIntoCall(
@@ -1247,14 +1288,14 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate flowThroughOutOfCall(
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
Configuration config
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
NodeEx out, boolean allowsFieldFlow, Configuration config
) {
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
pragma[only_bind_into](config)) and
matchesCall(ccc, call)
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
matchesCall(ccc, call) and
c = ret.getEnclosingCallable()
}
/**
@@ -1262,29 +1303,32 @@ private module MkStage<StageSig PrevStage> {
* configuration `config`.
*
* The call context `cc` records whether the node is reached through an
* argument in a call, and if so, `argAp` records the access path of that
* argument.
* argument in a call, and if so, `summaryCtx` and `argAp` record the
* corresponding parameter position and access path of that argument, respectively.
*/
pragma[nomagic]
additional predicate fwdFlow(
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, Configuration config
) {
fwdFlow0(node, state, cc, argAp, ap, config) and
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
filter(node, state, ap, config)
}
pragma[nomagic]
private predicate fwdFlow0(
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, Configuration config
) {
sourceNode(node, state, config) and
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
summaryCtx = TParameterPositionNone() and
ap = getApNil(node)
or
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
fwdFlow(mid, state0, cc, argAp, ap0, config) and
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
@@ -1295,65 +1339,82 @@ private module MkStage<StageSig PrevStage> {
)
or
exists(NodeEx mid |
fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone()
)
or
exists(NodeEx mid, ApNil nil |
fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and
fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and
additionalJumpStateStep(mid, state0, node, state, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
or
// store
exists(TypedContent tc, Ap ap0 |
fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
ap = apCons(tc, ap0)
)
or
// read
exists(Ap ap0, Content c |
fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
fwdFlowConsCand(ap0, c, ap, config)
)
or
// flow into a callable
exists(ApApprox apa |
fwdFlowIn(_, node, state, _, cc, _, ap, config) and
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
apa = getApprox(ap) and
if PrevStage::parameterMayFlowThrough(node, _, apa, config)
then argAp = apSome(ap)
else argAp = apNone()
if PrevStage::parameterMayFlowThrough(node, apa, config)
then (
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
argAp = apSome(ap)
) else (
summaryCtx = TParameterPositionNone() and argAp = apNone()
)
)
or
// flow out of a callable
fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
exists(
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
DataFlowCallable inner
|
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and
flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and
inner = ret.getEnclosingCallable() and
cc = getCallContextReturn(inner, call, innercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
or
exists(DataFlowCall call, Ap argAp0 |
fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
fwdFlowIsEntered(call, cc, argAp, argAp0, config)
// flow through a callable
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
)
}
pragma[nomagic]
private predicate fwdFlowStore(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
Configuration config
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
fwdFlow(node1, state, cc, argAp, ap1, config) and
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
@@ -1366,7 +1427,7 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
exists(TypedContent tc |
fwdFlowStore(_, tail, tc, _, _, _, _, config) and
fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and
tc.getContent() = c and
cons = apCons(tc, tail)
)
@@ -1374,21 +1435,21 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate fwdFlowRead(
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
Configuration config
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
fwdFlow(node1, state, cc, argAp, ap, config) and
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::readStepCand(node1, c, node2, config) and
getHeadContent(ap) = c
}
pragma[nomagic]
private predicate fwdFlowIn(
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp,
Ap ap, Configuration config
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc,
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
) {
exists(ArgNodeEx arg, boolean allowsFieldFlow |
fwdFlow(arg, state, outercc, argAp, ap, config) and
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
@@ -1396,29 +1457,19 @@ private module MkStage<StageSig PrevStage> {
}
pragma[nomagic]
private predicate fwdFlowOutNotFromArg(
NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
private predicate fwdFlowOutFromArg(
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
Configuration config
) {
exists(
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
DataFlowCallable inner
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
fwdFlow(ret, state, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
inner = ret.getEnclosingCallable() and
ccOut = getCallContextReturn(inner, call, innercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate fwdFlowOutFromArg(
DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config
) {
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
)
}
@@ -1428,11 +1479,13 @@ private module MkStage<StageSig PrevStage> {
*/
pragma[nomagic]
private predicate fwdFlowIsEntered(
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
ParameterPosition pos, Ap ap, Configuration config
) {
exists(ParamNodeEx p |
fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
exists(ParamNodeEx param |
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
pos = param.getPosition()
)
}
@@ -1440,27 +1493,40 @@ private module MkStage<StageSig PrevStage> {
private predicate storeStepFwd(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
) {
fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and
ap2 = apCons(tc, ap1) and
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config)
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config)
}
private predicate readStepFwd(
NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
) {
fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and
fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and
fwdFlowConsCand(ap1, c, ap2, config)
}
pragma[nomagic]
private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
private predicate returnFlowsThrough0(
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
) {
exists(boolean allowsFieldFlow |
fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
pragma[only_bind_into](config))
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate returnFlowsThrough(
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp,
Ap ap, Configuration config
) {
exists(DataFlowCallable c, ParameterPosition ppos |
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
p.isParameterOf(c, ppos) and
parameterFlowThroughAllowed(p, kind)
)
}
@@ -1468,118 +1534,130 @@ private module MkStage<StageSig PrevStage> {
private predicate flowThroughIntoCall(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
}
pragma[nomagic]
private predicate returnNodeMayFlowThrough(
RetNodeEx ret, FlowState state, Ap ap, Configuration config
) {
fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
exists(Ap argAp |
flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
pragma[only_bind_into](config)) and
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
pragma[only_bind_into](config))
)
}
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
*
* The Boolean `toReturn` records whether the node must be returned from the
* enclosing callable in order to reach a sink, and if so, `returnAp` records
* the access path of the returned value.
* The parameter `returnCtx` records whether (and how) the node must be returned
* from the enclosing callable in order to reach a sink, and if so, `returnAp`
* records the access path of the returned value.
*/
pragma[nomagic]
additional predicate revFlow(
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
Configuration config
) {
revFlow0(node, state, toReturn, returnAp, ap, config) and
fwdFlow(node, state, _, _, ap, config)
revFlow0(node, state, returnCtx, returnAp, ap, config) and
fwdFlow(node, state, _, _, _, ap, config)
}
pragma[nomagic]
private predicate revFlow0(
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
Configuration config
) {
fwdFlow(node, state, _, _, ap, config) and
fwdFlow(node, state, _, _, _, ap, config) and
sinkNode(node, state, config) and
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
(
if hasSinkCallCtx(config)
then returnCtx = TReturnCtxNoFlowThrough()
else returnCtx = TReturnCtxNone()
) and
returnAp = apNone() and
ap instanceof ApNil
or
exists(NodeEx mid, FlowState state0 |
localStep(node, state, mid, state0, true, _, config, _) and
revFlow(mid, state0, toReturn, returnAp, ap, config)
revFlow(mid, state0, returnCtx, returnAp, ap, config)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and
revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, state, _, _, ap, config) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone()
)
or
exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone() and
ap instanceof ApNil
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStateStep(node, state, mid, state0, config) and
revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil,
pragma[only_bind_into](config)) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone() and
ap instanceof ApNil
)
or
// store
exists(Ap ap0, Content c |
revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and
revFlowConsCand(ap0, c, ap, config)
)
or
// read
exists(NodeEx mid, Ap ap0 |
revFlow(mid, state, toReturn, returnAp, ap0, config) and
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
readStepFwd(node, ap, _, mid, ap0, config)
)
or
// flow into a callable
revFlowInNotToReturn(node, state, returnAp, ap, config) and
toReturn = false
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
flowIntoCall(_, node, p, allowsFieldFlow, config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
returnCtx = TReturnCtxNone()
)
or
exists(DataFlowCall call, Ap returnAp0 |
revFlowInToReturn(call, node, state, returnAp0, ap, config) and
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
// flow through a callable
exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 |
revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
or
// flow out of a callable
revFlowOut(_, node, state, _, _, ap, config) and
toReturn = true and
if returnNodeMayFlowThrough(node, state, ap, config)
then returnAp = apSome(ap)
else returnAp = apNone()
exists(ReturnKindExt kind |
revFlowOut(_, node, kind, state, _, _, ap, config) and
if returnFlowsThrough(node, kind, state, _, _, _, ap, config)
then (
returnCtx = TReturnCtxMaybeFlowThrough(kind) and
returnAp = apSome(ap)
) else (
returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone()
)
)
}
pragma[nomagic]
private predicate revFlowStore(
Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid,
boolean toReturn, ApOption returnAp, Configuration config
ReturnCtx returnCtx, ApOption returnAp, Configuration config
) {
revFlow(mid, state, toReturn, returnAp, ap0, config) and
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
storeStepFwd(node, ap, tc, mid, ap0, config) and
tc.getContent() = c
}
@@ -1599,35 +1677,27 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate revFlowOut(
DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap,
Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx,
ApOption returnAp, Ap ap, Configuration config
) {
exists(NodeEx out, boolean allowsFieldFlow |
revFlow(out, state, toReturn, returnAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate revFlowInNotToReturn(
ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, false, returnAp, ap, config) and
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
revFlow(out, state, returnCtx, returnAp, ap, config) and
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate revFlowInToReturn(
DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, true, apSome(returnAp), ap, config) and
revFlow(pragma[only_bind_into](p), state,
TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
parameterFlowThroughAllowed(p, kind)
)
}
@@ -1638,11 +1708,12 @@ private module MkStage<StageSig PrevStage> {
*/
pragma[nomagic]
private predicate revFlowIsReturned(
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap,
Configuration config
) {
exists(RetNodeEx ret, FlowState state, CcCall ccc |
revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and
fwdFlow(ret, state, ccc, apSome(_), ap, config) and
revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and
returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and
matchesCall(ccc, call)
)
}
@@ -1713,40 +1784,39 @@ private module MkStage<StageSig PrevStage> {
validAp(ap, config)
}
pragma[noinline]
private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
pragma[nomagic]
private predicate parameterFlowsThroughRev(
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
) {
revFlow(p, _, true, apSome(ap0), ap, config) and
c = p.getEnclosingCallable()
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
parameterFlowThroughAllowed(p, kind)
}
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
parameterFlow(p, ap, ap0, c, config) and
c = ret.getEnclosingCallable() and
revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_),
pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and
fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and
kind = ret.getKind() and
p.getPosition() = pos and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
p.allowParameterReturnInSelf()
)
pragma[nomagic]
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
exists(RetNodeEx ret, ReturnKindExt kind |
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
parameterFlowsThroughRev(p, ap, kind, config)
)
}
pragma[nomagic]
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
exists(ParamNodeEx p, Ap ap |
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
parameterFlowsThroughRev(p, ap, kind, config)
)
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(
Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
revFlow(arg, state, toReturn, returnAp, ap, config) and
revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
revFlow(arg, state, returnCtx, returnAp, ap, config) and
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
}
@@ -1754,14 +1824,13 @@ private module MkStage<StageSig PrevStage> {
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) {
fwd = true and
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and
states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and
tuples =
count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
fwdFlow(n, state, cc, argAp, ap, config)
)
count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config))
or
fwd = false and
nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
@@ -1769,8 +1838,8 @@ private module MkStage<StageSig PrevStage> {
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
states = count(FlowState state | revFlow(_, state, _, _, _, config)) and
tuples =
count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap |
revFlow(n, state, b, retAp, ap, config)
count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap |
revFlow(n, state, returnCtx, retAp, ap, config)
)
}
/* End: Stage logic. */
@@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
exists(lcc)
}
predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
predicate flowIntoCall = flowIntoCallNodeCand1/5;
@@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig {
pragma[nomagic]
private predicate flowOutOfCallNodeCand2(
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
Stage2::revFlowAlias(node1, pragma[only_bind_into](config))
}
@@ -2021,8 +2091,8 @@ private module LocalFlowBigStep {
exists(NodeEx next | Stage2::revFlow(next, state, config) |
jumpStep(node, next, config) or
additionalJumpStep(node, next, config) or
flowIntoCallNodeCand1(_, node, next, config) or
flowOutOfCallNodeCand1(_, node, next, config) or
flowIntoCallNodeCand2(_, node, next, _, config) or
flowOutOfCallNodeCand2(_, node, _, next, _, config) or
Stage2::storeStepCand(node, _, _, next, _, config) or
Stage2::readStepCand(node, _, next, config)
)
@@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
}
predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
predicate flowIntoCall = flowIntoCallNodeCand2/5;
@@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx(
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
) {
exists(AccessPathFront apf |
Stage3::revFlow(node, state, true, _, apf, config) and
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
config)
)
}
@@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
pragma[nomagic]
predicate flowOutOfCall(
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
exists(FlowState state |
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
pragma[only_bind_into](config))
@@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) {
pragma[nomagic]
private predicate nodeMayUseSummary0(
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa,
Configuration config
) {
exists(AccessPathApprox apa0 |
Stage4::parameterMayFlowThrough(_, c, _, _) and
Stage4::revFlow(n, state, true, _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
n.getEnclosingCallable() = c
c = n.getEnclosingCallable() and
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
TAccessPathApproxSome(apa), apa0, config)
)
}
@@ -2522,9 +2595,10 @@ pragma[nomagic]
private predicate nodeMayUseSummary(
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
) {
exists(DataFlowCallable c |
Stage4::parameterMayFlowThrough(_, c, apa, config) and
nodeMayUseSummary0(n, c, state, apa, config)
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
Stage4::parameterMayFlowThrough(p, apa, config) and
nodeMayUseSummary0(n, c, pos, state, apa, config) and
p.isParameterOf(c, pos)
)
}
@@ -2532,7 +2606,7 @@ private newtype TSummaryCtx =
TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
Stage4::revFlow(p, state, _, config)
)
}
@@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough(
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
AccessPathApprox apa, Configuration config
) {
exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos |
exists(PathNodeMid mid, RetNodeEx ret |
pathNode(mid, ret, state, cc, sc, ap, config, _) and
kind = ret.getKind() and
apa = ap.getApprox() and
pos = sc.getParameterPos() and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
sc.getParamNode().allowParameterReturnInSelf()
)
parameterFlowThroughAllowed(sc.getParamNode(), kind)
)
}

View File

@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node source, FlowState state) { none() }
predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes
@@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
}
ParameterPosition getPosition() { this.isParameterOf(_, result) }
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
}
private class RetNodeEx extends NodeEx {
@@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) {
)
}
/**
* Holds if flow from `p` to a return node of kind `kind` is allowed.
*
* We don't expect a parameter to return stored in itself, unless
* explicitly allowed
*/
bindingset[p, kind]
private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) {
exists(ParameterPosition pos | p.isParameterOf(_, pos) |
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
allowParameterReturnInSelfCached(p.asNode())
)
}
/**
* Holds if flow from a parameter at position `pos` inside `c` to a return node of
* kind `kind` is allowed.
*
* We don't expect a parameter to return stored in itself, unless
* explicitly allowed
*/
bindingset[c, pos, kind]
private predicate parameterFlowThroughAllowed(
DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
) {
exists(ParamNodeEx p |
p.isParameterOf(c, pos) and
parameterFlowThroughAllowed(p, kind)
)
}
private module Stage1 implements StageSig {
class Ap = Unit;
@@ -981,21 +1011,22 @@ private module Stage1 implements StageSig {
* candidate for the origin of a summary.
*/
pragma[nomagic]
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
exists(ReturnKindExt kind |
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
exists(DataFlowCallable c, ReturnKindExt kind |
throughFlowNodeCand(p, config) and
returnFlowCallableNodeCand(c, kind, config) and
p.getEnclosingCallable() = c and
exists(ap) and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
or
p.allowParameterReturnInSelf()
)
parameterFlowThroughAllowed(p, kind)
)
}
pragma[nomagic]
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
throughFlowNodeCand(ret, config) and
kind = ret.getKind()
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(ArgNodeEx arg, boolean toReturn |
@@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1(
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config
) {
viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
Stage1::revFlow(ret, config) and
not outBarrier(ret, config) and
not inBarrier(out, config)
exists(ReturnPosition pos |
viableReturnPosOutNodeCand1(call, pos, out, config) and
pos = ret.getReturnPosition() and
kind = pos.getKind() and
Stage1::revFlow(ret, config) and
not outBarrier(ret, config) and
not inBarrier(out, config)
)
}
pragma[nomagic]
@@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1(
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
pragma[nomagic]
private int branch(NodeEx n1, Configuration conf) {
result =
strictcount(NodeEx n |
flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
)
}
@@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) {
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
pragma[nomagic]
private int join(NodeEx n2, Configuration conf) {
result =
strictcount(NodeEx n |
flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
)
}
@@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) {
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
Configuration config
) {
flowOutOfCallNodeCand1(call, ret, out, config) and
flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and
exists(int b, int j |
b = branch(ret, config) and
j = join(out, config) and
b = branch(ret, pragma[only_bind_into](config)) and
j = join(out, pragma[only_bind_into](config)) and
if b.minimum(j) <= config.fieldFlowBranchLimit()
then allowsFieldFlow = true
else allowsFieldFlow = false
@@ -1136,10 +1174,10 @@ pragma[nomagic]
private predicate flowIntoCallNodeCand1(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
flowIntoCallNodeCand1(call, arg, p, config) and
flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and
exists(int b, int j |
b = branch(arg, config) and
j = join(p, config) and
b = branch(arg, pragma[only_bind_into](config)) and
j = join(p, pragma[only_bind_into](config)) and
if b.minimum(j) <= config.fieldFlowBranchLimit()
then allowsFieldFlow = true
else allowsFieldFlow = false
@@ -1156,7 +1194,9 @@ private signature module StageSig {
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
predicate storeStepCand(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
@@ -1222,7 +1262,8 @@ private module MkStage<StageSig PrevStage> {
);
predicate flowOutOfCall(
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
Configuration config
);
predicate flowIntoCall(
@@ -1247,14 +1288,14 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate flowThroughOutOfCall(
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
Configuration config
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
NodeEx out, boolean allowsFieldFlow, Configuration config
) {
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
pragma[only_bind_into](config)) and
matchesCall(ccc, call)
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
matchesCall(ccc, call) and
c = ret.getEnclosingCallable()
}
/**
@@ -1262,29 +1303,32 @@ private module MkStage<StageSig PrevStage> {
* configuration `config`.
*
* The call context `cc` records whether the node is reached through an
* argument in a call, and if so, `argAp` records the access path of that
* argument.
* argument in a call, and if so, `summaryCtx` and `argAp` record the
* corresponding parameter position and access path of that argument, respectively.
*/
pragma[nomagic]
additional predicate fwdFlow(
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, Configuration config
) {
fwdFlow0(node, state, cc, argAp, ap, config) and
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
filter(node, state, ap, config)
}
pragma[nomagic]
private predicate fwdFlow0(
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, Configuration config
) {
sourceNode(node, state, config) and
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
summaryCtx = TParameterPositionNone() and
ap = getApNil(node)
or
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
fwdFlow(mid, state0, cc, argAp, ap0, config) and
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
@@ -1295,65 +1339,82 @@ private module MkStage<StageSig PrevStage> {
)
or
exists(NodeEx mid |
fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone()
)
or
exists(NodeEx mid, ApNil nil |
fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and
fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and
additionalJumpStateStep(mid, state0, node, state, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
or
// store
exists(TypedContent tc, Ap ap0 |
fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
ap = apCons(tc, ap0)
)
or
// read
exists(Ap ap0, Content c |
fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
fwdFlowConsCand(ap0, c, ap, config)
)
or
// flow into a callable
exists(ApApprox apa |
fwdFlowIn(_, node, state, _, cc, _, ap, config) and
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
apa = getApprox(ap) and
if PrevStage::parameterMayFlowThrough(node, _, apa, config)
then argAp = apSome(ap)
else argAp = apNone()
if PrevStage::parameterMayFlowThrough(node, apa, config)
then (
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
argAp = apSome(ap)
) else (
summaryCtx = TParameterPositionNone() and argAp = apNone()
)
)
or
// flow out of a callable
fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
exists(
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
DataFlowCallable inner
|
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and
flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and
inner = ret.getEnclosingCallable() and
cc = getCallContextReturn(inner, call, innercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
or
exists(DataFlowCall call, Ap argAp0 |
fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
fwdFlowIsEntered(call, cc, argAp, argAp0, config)
// flow through a callable
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
)
}
pragma[nomagic]
private predicate fwdFlowStore(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
Configuration config
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
fwdFlow(node1, state, cc, argAp, ap1, config) and
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
@@ -1366,7 +1427,7 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
exists(TypedContent tc |
fwdFlowStore(_, tail, tc, _, _, _, _, config) and
fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and
tc.getContent() = c and
cons = apCons(tc, tail)
)
@@ -1374,21 +1435,21 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate fwdFlowRead(
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
Configuration config
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
fwdFlow(node1, state, cc, argAp, ap, config) and
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::readStepCand(node1, c, node2, config) and
getHeadContent(ap) = c
}
pragma[nomagic]
private predicate fwdFlowIn(
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp,
Ap ap, Configuration config
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc,
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
) {
exists(ArgNodeEx arg, boolean allowsFieldFlow |
fwdFlow(arg, state, outercc, argAp, ap, config) and
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
@@ -1396,29 +1457,19 @@ private module MkStage<StageSig PrevStage> {
}
pragma[nomagic]
private predicate fwdFlowOutNotFromArg(
NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
private predicate fwdFlowOutFromArg(
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
Configuration config
) {
exists(
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
DataFlowCallable inner
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
fwdFlow(ret, state, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
inner = ret.getEnclosingCallable() and
ccOut = getCallContextReturn(inner, call, innercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate fwdFlowOutFromArg(
DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config
) {
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
)
}
@@ -1428,11 +1479,13 @@ private module MkStage<StageSig PrevStage> {
*/
pragma[nomagic]
private predicate fwdFlowIsEntered(
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
ParameterPosition pos, Ap ap, Configuration config
) {
exists(ParamNodeEx p |
fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
exists(ParamNodeEx param |
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
pos = param.getPosition()
)
}
@@ -1440,27 +1493,40 @@ private module MkStage<StageSig PrevStage> {
private predicate storeStepFwd(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
) {
fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and
ap2 = apCons(tc, ap1) and
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config)
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config)
}
private predicate readStepFwd(
NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
) {
fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and
fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and
fwdFlowConsCand(ap1, c, ap2, config)
}
pragma[nomagic]
private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
private predicate returnFlowsThrough0(
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
) {
exists(boolean allowsFieldFlow |
fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
pragma[only_bind_into](config))
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate returnFlowsThrough(
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp,
Ap ap, Configuration config
) {
exists(DataFlowCallable c, ParameterPosition ppos |
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
p.isParameterOf(c, ppos) and
parameterFlowThroughAllowed(p, kind)
)
}
@@ -1468,118 +1534,130 @@ private module MkStage<StageSig PrevStage> {
private predicate flowThroughIntoCall(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
}
pragma[nomagic]
private predicate returnNodeMayFlowThrough(
RetNodeEx ret, FlowState state, Ap ap, Configuration config
) {
fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
exists(Ap argAp |
flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
pragma[only_bind_into](config)) and
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
pragma[only_bind_into](config))
)
}
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
*
* The Boolean `toReturn` records whether the node must be returned from the
* enclosing callable in order to reach a sink, and if so, `returnAp` records
* the access path of the returned value.
* The parameter `returnCtx` records whether (and how) the node must be returned
* from the enclosing callable in order to reach a sink, and if so, `returnAp`
* records the access path of the returned value.
*/
pragma[nomagic]
additional predicate revFlow(
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
Configuration config
) {
revFlow0(node, state, toReturn, returnAp, ap, config) and
fwdFlow(node, state, _, _, ap, config)
revFlow0(node, state, returnCtx, returnAp, ap, config) and
fwdFlow(node, state, _, _, _, ap, config)
}
pragma[nomagic]
private predicate revFlow0(
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
Configuration config
) {
fwdFlow(node, state, _, _, ap, config) and
fwdFlow(node, state, _, _, _, ap, config) and
sinkNode(node, state, config) and
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
(
if hasSinkCallCtx(config)
then returnCtx = TReturnCtxNoFlowThrough()
else returnCtx = TReturnCtxNone()
) and
returnAp = apNone() and
ap instanceof ApNil
or
exists(NodeEx mid, FlowState state0 |
localStep(node, state, mid, state0, true, _, config, _) and
revFlow(mid, state0, toReturn, returnAp, ap, config)
revFlow(mid, state0, returnCtx, returnAp, ap, config)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and
revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, state, _, _, ap, config) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone()
)
or
exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone() and
ap instanceof ApNil
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStateStep(node, state, mid, state0, config) and
revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil,
pragma[only_bind_into](config)) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone() and
ap instanceof ApNil
)
or
// store
exists(Ap ap0, Content c |
revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and
revFlowConsCand(ap0, c, ap, config)
)
or
// read
exists(NodeEx mid, Ap ap0 |
revFlow(mid, state, toReturn, returnAp, ap0, config) and
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
readStepFwd(node, ap, _, mid, ap0, config)
)
or
// flow into a callable
revFlowInNotToReturn(node, state, returnAp, ap, config) and
toReturn = false
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
flowIntoCall(_, node, p, allowsFieldFlow, config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
returnCtx = TReturnCtxNone()
)
or
exists(DataFlowCall call, Ap returnAp0 |
revFlowInToReturn(call, node, state, returnAp0, ap, config) and
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
// flow through a callable
exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 |
revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
or
// flow out of a callable
revFlowOut(_, node, state, _, _, ap, config) and
toReturn = true and
if returnNodeMayFlowThrough(node, state, ap, config)
then returnAp = apSome(ap)
else returnAp = apNone()
exists(ReturnKindExt kind |
revFlowOut(_, node, kind, state, _, _, ap, config) and
if returnFlowsThrough(node, kind, state, _, _, _, ap, config)
then (
returnCtx = TReturnCtxMaybeFlowThrough(kind) and
returnAp = apSome(ap)
) else (
returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone()
)
)
}
pragma[nomagic]
private predicate revFlowStore(
Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid,
boolean toReturn, ApOption returnAp, Configuration config
ReturnCtx returnCtx, ApOption returnAp, Configuration config
) {
revFlow(mid, state, toReturn, returnAp, ap0, config) and
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
storeStepFwd(node, ap, tc, mid, ap0, config) and
tc.getContent() = c
}
@@ -1599,35 +1677,27 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate revFlowOut(
DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap,
Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx,
ApOption returnAp, Ap ap, Configuration config
) {
exists(NodeEx out, boolean allowsFieldFlow |
revFlow(out, state, toReturn, returnAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate revFlowInNotToReturn(
ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, false, returnAp, ap, config) and
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
revFlow(out, state, returnCtx, returnAp, ap, config) and
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate revFlowInToReturn(
DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, true, apSome(returnAp), ap, config) and
revFlow(pragma[only_bind_into](p), state,
TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
parameterFlowThroughAllowed(p, kind)
)
}
@@ -1638,11 +1708,12 @@ private module MkStage<StageSig PrevStage> {
*/
pragma[nomagic]
private predicate revFlowIsReturned(
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap,
Configuration config
) {
exists(RetNodeEx ret, FlowState state, CcCall ccc |
revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and
fwdFlow(ret, state, ccc, apSome(_), ap, config) and
revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and
returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and
matchesCall(ccc, call)
)
}
@@ -1713,40 +1784,39 @@ private module MkStage<StageSig PrevStage> {
validAp(ap, config)
}
pragma[noinline]
private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
pragma[nomagic]
private predicate parameterFlowsThroughRev(
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
) {
revFlow(p, _, true, apSome(ap0), ap, config) and
c = p.getEnclosingCallable()
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
parameterFlowThroughAllowed(p, kind)
}
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
parameterFlow(p, ap, ap0, c, config) and
c = ret.getEnclosingCallable() and
revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_),
pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and
fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and
kind = ret.getKind() and
p.getPosition() = pos and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
p.allowParameterReturnInSelf()
)
pragma[nomagic]
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
exists(RetNodeEx ret, ReturnKindExt kind |
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
parameterFlowsThroughRev(p, ap, kind, config)
)
}
pragma[nomagic]
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
exists(ParamNodeEx p, Ap ap |
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
parameterFlowsThroughRev(p, ap, kind, config)
)
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(
Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
revFlow(arg, state, toReturn, returnAp, ap, config) and
revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
revFlow(arg, state, returnCtx, returnAp, ap, config) and
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
}
@@ -1754,14 +1824,13 @@ private module MkStage<StageSig PrevStage> {
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) {
fwd = true and
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and
states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and
tuples =
count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
fwdFlow(n, state, cc, argAp, ap, config)
)
count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config))
or
fwd = false and
nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
@@ -1769,8 +1838,8 @@ private module MkStage<StageSig PrevStage> {
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
states = count(FlowState state | revFlow(_, state, _, _, _, config)) and
tuples =
count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap |
revFlow(n, state, b, retAp, ap, config)
count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap |
revFlow(n, state, returnCtx, retAp, ap, config)
)
}
/* End: Stage logic. */
@@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
exists(lcc)
}
predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
predicate flowIntoCall = flowIntoCallNodeCand1/5;
@@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig {
pragma[nomagic]
private predicate flowOutOfCallNodeCand2(
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
Stage2::revFlowAlias(node1, pragma[only_bind_into](config))
}
@@ -2021,8 +2091,8 @@ private module LocalFlowBigStep {
exists(NodeEx next | Stage2::revFlow(next, state, config) |
jumpStep(node, next, config) or
additionalJumpStep(node, next, config) or
flowIntoCallNodeCand1(_, node, next, config) or
flowOutOfCallNodeCand1(_, node, next, config) or
flowIntoCallNodeCand2(_, node, next, _, config) or
flowOutOfCallNodeCand2(_, node, _, next, _, config) or
Stage2::storeStepCand(node, _, _, next, _, config) or
Stage2::readStepCand(node, _, next, config)
)
@@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
}
predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
predicate flowIntoCall = flowIntoCallNodeCand2/5;
@@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx(
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
) {
exists(AccessPathFront apf |
Stage3::revFlow(node, state, true, _, apf, config) and
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
config)
)
}
@@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
pragma[nomagic]
predicate flowOutOfCall(
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
exists(FlowState state |
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
pragma[only_bind_into](config))
@@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) {
pragma[nomagic]
private predicate nodeMayUseSummary0(
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa,
Configuration config
) {
exists(AccessPathApprox apa0 |
Stage4::parameterMayFlowThrough(_, c, _, _) and
Stage4::revFlow(n, state, true, _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
n.getEnclosingCallable() = c
c = n.getEnclosingCallable() and
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
TAccessPathApproxSome(apa), apa0, config)
)
}
@@ -2522,9 +2595,10 @@ pragma[nomagic]
private predicate nodeMayUseSummary(
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
) {
exists(DataFlowCallable c |
Stage4::parameterMayFlowThrough(_, c, apa, config) and
nodeMayUseSummary0(n, c, state, apa, config)
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
Stage4::parameterMayFlowThrough(p, apa, config) and
nodeMayUseSummary0(n, c, pos, state, apa, config) and
p.isParameterOf(c, pos)
)
}
@@ -2532,7 +2606,7 @@ private newtype TSummaryCtx =
TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
Stage4::revFlow(p, state, _, config)
)
}
@@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough(
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
AccessPathApprox apa, Configuration config
) {
exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos |
exists(PathNodeMid mid, RetNodeEx ret |
pathNode(mid, ret, state, cc, sc, ap, config, _) and
kind = ret.getKind() and
apa = ap.getApprox() and
pos = sc.getParameterPos() and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
sc.getParamNode().allowParameterReturnInSelf()
)
parameterFlowThroughAllowed(sc.getParamNode(), kind)
)
}

View File

@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node source, FlowState state) { none() }
predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes
@@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
}
ParameterPosition getPosition() { this.isParameterOf(_, result) }
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
}
private class RetNodeEx extends NodeEx {
@@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) {
)
}
/**
* Holds if flow from `p` to a return node of kind `kind` is allowed.
*
* We don't expect a parameter to return stored in itself, unless
* explicitly allowed
*/
bindingset[p, kind]
private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) {
exists(ParameterPosition pos | p.isParameterOf(_, pos) |
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
allowParameterReturnInSelfCached(p.asNode())
)
}
/**
* Holds if flow from a parameter at position `pos` inside `c` to a return node of
* kind `kind` is allowed.
*
* We don't expect a parameter to return stored in itself, unless
* explicitly allowed
*/
bindingset[c, pos, kind]
private predicate parameterFlowThroughAllowed(
DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
) {
exists(ParamNodeEx p |
p.isParameterOf(c, pos) and
parameterFlowThroughAllowed(p, kind)
)
}
private module Stage1 implements StageSig {
class Ap = Unit;
@@ -981,21 +1011,22 @@ private module Stage1 implements StageSig {
* candidate for the origin of a summary.
*/
pragma[nomagic]
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
exists(ReturnKindExt kind |
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
exists(DataFlowCallable c, ReturnKindExt kind |
throughFlowNodeCand(p, config) and
returnFlowCallableNodeCand(c, kind, config) and
p.getEnclosingCallable() = c and
exists(ap) and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
or
p.allowParameterReturnInSelf()
)
parameterFlowThroughAllowed(p, kind)
)
}
pragma[nomagic]
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
throughFlowNodeCand(ret, config) and
kind = ret.getKind()
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(ArgNodeEx arg, boolean toReturn |
@@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1(
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config
) {
viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
Stage1::revFlow(ret, config) and
not outBarrier(ret, config) and
not inBarrier(out, config)
exists(ReturnPosition pos |
viableReturnPosOutNodeCand1(call, pos, out, config) and
pos = ret.getReturnPosition() and
kind = pos.getKind() and
Stage1::revFlow(ret, config) and
not outBarrier(ret, config) and
not inBarrier(out, config)
)
}
pragma[nomagic]
@@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1(
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
pragma[nomagic]
private int branch(NodeEx n1, Configuration conf) {
result =
strictcount(NodeEx n |
flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
)
}
@@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) {
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
pragma[nomagic]
private int join(NodeEx n2, Configuration conf) {
result =
strictcount(NodeEx n |
flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
)
}
@@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) {
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
Configuration config
) {
flowOutOfCallNodeCand1(call, ret, out, config) and
flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and
exists(int b, int j |
b = branch(ret, config) and
j = join(out, config) and
b = branch(ret, pragma[only_bind_into](config)) and
j = join(out, pragma[only_bind_into](config)) and
if b.minimum(j) <= config.fieldFlowBranchLimit()
then allowsFieldFlow = true
else allowsFieldFlow = false
@@ -1136,10 +1174,10 @@ pragma[nomagic]
private predicate flowIntoCallNodeCand1(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
flowIntoCallNodeCand1(call, arg, p, config) and
flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and
exists(int b, int j |
b = branch(arg, config) and
j = join(p, config) and
b = branch(arg, pragma[only_bind_into](config)) and
j = join(p, pragma[only_bind_into](config)) and
if b.minimum(j) <= config.fieldFlowBranchLimit()
then allowsFieldFlow = true
else allowsFieldFlow = false
@@ -1156,7 +1194,9 @@ private signature module StageSig {
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
predicate storeStepCand(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
@@ -1222,7 +1262,8 @@ private module MkStage<StageSig PrevStage> {
);
predicate flowOutOfCall(
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
Configuration config
);
predicate flowIntoCall(
@@ -1247,14 +1288,14 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate flowThroughOutOfCall(
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
Configuration config
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
NodeEx out, boolean allowsFieldFlow, Configuration config
) {
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
pragma[only_bind_into](config)) and
matchesCall(ccc, call)
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
matchesCall(ccc, call) and
c = ret.getEnclosingCallable()
}
/**
@@ -1262,29 +1303,32 @@ private module MkStage<StageSig PrevStage> {
* configuration `config`.
*
* The call context `cc` records whether the node is reached through an
* argument in a call, and if so, `argAp` records the access path of that
* argument.
* argument in a call, and if so, `summaryCtx` and `argAp` record the
* corresponding parameter position and access path of that argument, respectively.
*/
pragma[nomagic]
additional predicate fwdFlow(
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, Configuration config
) {
fwdFlow0(node, state, cc, argAp, ap, config) and
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
filter(node, state, ap, config)
}
pragma[nomagic]
private predicate fwdFlow0(
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, Configuration config
) {
sourceNode(node, state, config) and
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
summaryCtx = TParameterPositionNone() and
ap = getApNil(node)
or
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
fwdFlow(mid, state0, cc, argAp, ap0, config) and
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
@@ -1295,65 +1339,82 @@ private module MkStage<StageSig PrevStage> {
)
or
exists(NodeEx mid |
fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone()
)
or
exists(NodeEx mid, ApNil nil |
fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and
fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and
additionalJumpStateStep(mid, state0, node, state, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
or
// store
exists(TypedContent tc, Ap ap0 |
fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
ap = apCons(tc, ap0)
)
or
// read
exists(Ap ap0, Content c |
fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
fwdFlowConsCand(ap0, c, ap, config)
)
or
// flow into a callable
exists(ApApprox apa |
fwdFlowIn(_, node, state, _, cc, _, ap, config) and
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
apa = getApprox(ap) and
if PrevStage::parameterMayFlowThrough(node, _, apa, config)
then argAp = apSome(ap)
else argAp = apNone()
if PrevStage::parameterMayFlowThrough(node, apa, config)
then (
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
argAp = apSome(ap)
) else (
summaryCtx = TParameterPositionNone() and argAp = apNone()
)
)
or
// flow out of a callable
fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
exists(
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
DataFlowCallable inner
|
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and
flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and
inner = ret.getEnclosingCallable() and
cc = getCallContextReturn(inner, call, innercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
or
exists(DataFlowCall call, Ap argAp0 |
fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
fwdFlowIsEntered(call, cc, argAp, argAp0, config)
// flow through a callable
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
)
}
pragma[nomagic]
private predicate fwdFlowStore(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
Configuration config
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
fwdFlow(node1, state, cc, argAp, ap1, config) and
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
@@ -1366,7 +1427,7 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
exists(TypedContent tc |
fwdFlowStore(_, tail, tc, _, _, _, _, config) and
fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and
tc.getContent() = c and
cons = apCons(tc, tail)
)
@@ -1374,21 +1435,21 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate fwdFlowRead(
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
Configuration config
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
fwdFlow(node1, state, cc, argAp, ap, config) and
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::readStepCand(node1, c, node2, config) and
getHeadContent(ap) = c
}
pragma[nomagic]
private predicate fwdFlowIn(
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp,
Ap ap, Configuration config
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc,
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
) {
exists(ArgNodeEx arg, boolean allowsFieldFlow |
fwdFlow(arg, state, outercc, argAp, ap, config) and
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
@@ -1396,29 +1457,19 @@ private module MkStage<StageSig PrevStage> {
}
pragma[nomagic]
private predicate fwdFlowOutNotFromArg(
NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
private predicate fwdFlowOutFromArg(
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
Configuration config
) {
exists(
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
DataFlowCallable inner
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
fwdFlow(ret, state, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
inner = ret.getEnclosingCallable() and
ccOut = getCallContextReturn(inner, call, innercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate fwdFlowOutFromArg(
DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config
) {
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
)
}
@@ -1428,11 +1479,13 @@ private module MkStage<StageSig PrevStage> {
*/
pragma[nomagic]
private predicate fwdFlowIsEntered(
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
ParameterPosition pos, Ap ap, Configuration config
) {
exists(ParamNodeEx p |
fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
exists(ParamNodeEx param |
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
pos = param.getPosition()
)
}
@@ -1440,27 +1493,40 @@ private module MkStage<StageSig PrevStage> {
private predicate storeStepFwd(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
) {
fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and
ap2 = apCons(tc, ap1) and
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config)
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config)
}
private predicate readStepFwd(
NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
) {
fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and
fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and
fwdFlowConsCand(ap1, c, ap2, config)
}
pragma[nomagic]
private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
private predicate returnFlowsThrough0(
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
) {
exists(boolean allowsFieldFlow |
fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
pragma[only_bind_into](config))
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate returnFlowsThrough(
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp,
Ap ap, Configuration config
) {
exists(DataFlowCallable c, ParameterPosition ppos |
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
p.isParameterOf(c, ppos) and
parameterFlowThroughAllowed(p, kind)
)
}
@@ -1468,118 +1534,130 @@ private module MkStage<StageSig PrevStage> {
private predicate flowThroughIntoCall(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
}
pragma[nomagic]
private predicate returnNodeMayFlowThrough(
RetNodeEx ret, FlowState state, Ap ap, Configuration config
) {
fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
exists(Ap argAp |
flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
pragma[only_bind_into](config)) and
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
pragma[only_bind_into](config))
)
}
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
*
* The Boolean `toReturn` records whether the node must be returned from the
* enclosing callable in order to reach a sink, and if so, `returnAp` records
* the access path of the returned value.
* The parameter `returnCtx` records whether (and how) the node must be returned
* from the enclosing callable in order to reach a sink, and if so, `returnAp`
* records the access path of the returned value.
*/
pragma[nomagic]
additional predicate revFlow(
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
Configuration config
) {
revFlow0(node, state, toReturn, returnAp, ap, config) and
fwdFlow(node, state, _, _, ap, config)
revFlow0(node, state, returnCtx, returnAp, ap, config) and
fwdFlow(node, state, _, _, _, ap, config)
}
pragma[nomagic]
private predicate revFlow0(
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
Configuration config
) {
fwdFlow(node, state, _, _, ap, config) and
fwdFlow(node, state, _, _, _, ap, config) and
sinkNode(node, state, config) and
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
(
if hasSinkCallCtx(config)
then returnCtx = TReturnCtxNoFlowThrough()
else returnCtx = TReturnCtxNone()
) and
returnAp = apNone() and
ap instanceof ApNil
or
exists(NodeEx mid, FlowState state0 |
localStep(node, state, mid, state0, true, _, config, _) and
revFlow(mid, state0, toReturn, returnAp, ap, config)
revFlow(mid, state0, returnCtx, returnAp, ap, config)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and
revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, state, _, _, ap, config) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone()
)
or
exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone() and
ap instanceof ApNil
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStateStep(node, state, mid, state0, config) and
revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil,
pragma[only_bind_into](config)) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone() and
ap instanceof ApNil
)
or
// store
exists(Ap ap0, Content c |
revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and
revFlowConsCand(ap0, c, ap, config)
)
or
// read
exists(NodeEx mid, Ap ap0 |
revFlow(mid, state, toReturn, returnAp, ap0, config) and
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
readStepFwd(node, ap, _, mid, ap0, config)
)
or
// flow into a callable
revFlowInNotToReturn(node, state, returnAp, ap, config) and
toReturn = false
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
flowIntoCall(_, node, p, allowsFieldFlow, config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
returnCtx = TReturnCtxNone()
)
or
exists(DataFlowCall call, Ap returnAp0 |
revFlowInToReturn(call, node, state, returnAp0, ap, config) and
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
// flow through a callable
exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 |
revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
or
// flow out of a callable
revFlowOut(_, node, state, _, _, ap, config) and
toReturn = true and
if returnNodeMayFlowThrough(node, state, ap, config)
then returnAp = apSome(ap)
else returnAp = apNone()
exists(ReturnKindExt kind |
revFlowOut(_, node, kind, state, _, _, ap, config) and
if returnFlowsThrough(node, kind, state, _, _, _, ap, config)
then (
returnCtx = TReturnCtxMaybeFlowThrough(kind) and
returnAp = apSome(ap)
) else (
returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone()
)
)
}
pragma[nomagic]
private predicate revFlowStore(
Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid,
boolean toReturn, ApOption returnAp, Configuration config
ReturnCtx returnCtx, ApOption returnAp, Configuration config
) {
revFlow(mid, state, toReturn, returnAp, ap0, config) and
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
storeStepFwd(node, ap, tc, mid, ap0, config) and
tc.getContent() = c
}
@@ -1599,35 +1677,27 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate revFlowOut(
DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap,
Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx,
ApOption returnAp, Ap ap, Configuration config
) {
exists(NodeEx out, boolean allowsFieldFlow |
revFlow(out, state, toReturn, returnAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate revFlowInNotToReturn(
ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, false, returnAp, ap, config) and
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
revFlow(out, state, returnCtx, returnAp, ap, config) and
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate revFlowInToReturn(
DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, true, apSome(returnAp), ap, config) and
revFlow(pragma[only_bind_into](p), state,
TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
parameterFlowThroughAllowed(p, kind)
)
}
@@ -1638,11 +1708,12 @@ private module MkStage<StageSig PrevStage> {
*/
pragma[nomagic]
private predicate revFlowIsReturned(
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap,
Configuration config
) {
exists(RetNodeEx ret, FlowState state, CcCall ccc |
revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and
fwdFlow(ret, state, ccc, apSome(_), ap, config) and
revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and
returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and
matchesCall(ccc, call)
)
}
@@ -1713,40 +1784,39 @@ private module MkStage<StageSig PrevStage> {
validAp(ap, config)
}
pragma[noinline]
private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
pragma[nomagic]
private predicate parameterFlowsThroughRev(
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
) {
revFlow(p, _, true, apSome(ap0), ap, config) and
c = p.getEnclosingCallable()
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
parameterFlowThroughAllowed(p, kind)
}
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
parameterFlow(p, ap, ap0, c, config) and
c = ret.getEnclosingCallable() and
revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_),
pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and
fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and
kind = ret.getKind() and
p.getPosition() = pos and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
p.allowParameterReturnInSelf()
)
pragma[nomagic]
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
exists(RetNodeEx ret, ReturnKindExt kind |
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
parameterFlowsThroughRev(p, ap, kind, config)
)
}
pragma[nomagic]
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
exists(ParamNodeEx p, Ap ap |
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
parameterFlowsThroughRev(p, ap, kind, config)
)
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(
Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
revFlow(arg, state, toReturn, returnAp, ap, config) and
revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
revFlow(arg, state, returnCtx, returnAp, ap, config) and
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
}
@@ -1754,14 +1824,13 @@ private module MkStage<StageSig PrevStage> {
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) {
fwd = true and
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and
states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and
tuples =
count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
fwdFlow(n, state, cc, argAp, ap, config)
)
count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config))
or
fwd = false and
nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
@@ -1769,8 +1838,8 @@ private module MkStage<StageSig PrevStage> {
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
states = count(FlowState state | revFlow(_, state, _, _, _, config)) and
tuples =
count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap |
revFlow(n, state, b, retAp, ap, config)
count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap |
revFlow(n, state, returnCtx, retAp, ap, config)
)
}
/* End: Stage logic. */
@@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
exists(lcc)
}
predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
predicate flowIntoCall = flowIntoCallNodeCand1/5;
@@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig {
pragma[nomagic]
private predicate flowOutOfCallNodeCand2(
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
Stage2::revFlowAlias(node1, pragma[only_bind_into](config))
}
@@ -2021,8 +2091,8 @@ private module LocalFlowBigStep {
exists(NodeEx next | Stage2::revFlow(next, state, config) |
jumpStep(node, next, config) or
additionalJumpStep(node, next, config) or
flowIntoCallNodeCand1(_, node, next, config) or
flowOutOfCallNodeCand1(_, node, next, config) or
flowIntoCallNodeCand2(_, node, next, _, config) or
flowOutOfCallNodeCand2(_, node, _, next, _, config) or
Stage2::storeStepCand(node, _, _, next, _, config) or
Stage2::readStepCand(node, _, next, config)
)
@@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
}
predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
predicate flowIntoCall = flowIntoCallNodeCand2/5;
@@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx(
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
) {
exists(AccessPathFront apf |
Stage3::revFlow(node, state, true, _, apf, config) and
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
config)
)
}
@@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
pragma[nomagic]
predicate flowOutOfCall(
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
exists(FlowState state |
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
pragma[only_bind_into](config))
@@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) {
pragma[nomagic]
private predicate nodeMayUseSummary0(
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa,
Configuration config
) {
exists(AccessPathApprox apa0 |
Stage4::parameterMayFlowThrough(_, c, _, _) and
Stage4::revFlow(n, state, true, _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
n.getEnclosingCallable() = c
c = n.getEnclosingCallable() and
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
TAccessPathApproxSome(apa), apa0, config)
)
}
@@ -2522,9 +2595,10 @@ pragma[nomagic]
private predicate nodeMayUseSummary(
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
) {
exists(DataFlowCallable c |
Stage4::parameterMayFlowThrough(_, c, apa, config) and
nodeMayUseSummary0(n, c, state, apa, config)
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
Stage4::parameterMayFlowThrough(p, apa, config) and
nodeMayUseSummary0(n, c, pos, state, apa, config) and
p.isParameterOf(c, pos)
)
}
@@ -2532,7 +2606,7 @@ private newtype TSummaryCtx =
TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
Stage4::revFlow(p, state, _, config)
)
}
@@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough(
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
AccessPathApprox apa, Configuration config
) {
exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos |
exists(PathNodeMid mid, RetNodeEx ret |
pathNode(mid, ret, state, cc, sc, ap, config, _) and
kind = ret.getKind() and
apa = ap.getApprox() and
pos = sc.getParameterPos() and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
sc.getParamNode().allowParameterReturnInSelf()
)
parameterFlowThroughAllowed(sc.getParamNode(), kind)
)
}

View File

@@ -915,6 +915,17 @@ private module Cached {
TDataFlowCallNone() or
TDataFlowCallSome(DataFlowCall call)
cached
newtype TParameterPositionOption =
TParameterPositionNone() or
TParameterPositionSome(ParameterPosition pos)
cached
newtype TReturnCtx =
TReturnCtxNone() or
TReturnCtxNoFlowThrough() or
TReturnCtxMaybeFlowThrough(ReturnKindExt kind)
cached
newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) }
@@ -1304,6 +1315,44 @@ class DataFlowCallOption extends TDataFlowCallOption {
}
}
/** An optional `ParameterPosition`. */
class ParameterPositionOption extends TParameterPositionOption {
string toString() {
this = TParameterPositionNone() and
result = "(none)"
or
exists(ParameterPosition pos |
this = TParameterPositionSome(pos) and
result = pos.toString()
)
}
}
/**
* A return context used to calculate flow summaries in reverse flow.
*
* The possible values are:
*
* - `TReturnCtxNone()`: no return flow.
* - `TReturnCtxNoFlowThrough()`: return flow, but flow through is not possible.
* - `TReturnCtxMaybeFlowThrough(ReturnKindExt kind)`: return flow, of kind `kind`, and
* flow through may be possible.
*/
class ReturnCtx extends TReturnCtx {
string toString() {
this = TReturnCtxNone() and
result = "(none)"
or
this = TReturnCtxNoFlowThrough() and
result = "(no flow through)"
or
exists(ReturnKindExt kind |
this = TReturnCtxMaybeFlowThrough(kind) and
result = kind.toString()
)
}
}
/** A `Content` tagged with the type of a containing object. */
class TypedContent extends MkTypedContent {
private Content c;

View File

@@ -12,8 +12,8 @@
*/
/*
* Note: this query is not assigned a precision yet because we don't want it on
* LGTM until its performance is well understood.
* Note: this query is not assigned a precision yet because we don't want it
* to be included in query suites until its performance is well understood.
*/
import cpp

View File

@@ -52,7 +52,7 @@ class Library extends LibraryT {
// The versions reported for C/C++ dependencies are just the versions that
// happen to be installed on the system where the build takes place.
// Reporting those versions is likely to cause misunderstandings, both for
// people reading them and for the vulnerability checker of lgtm.
// people reading them and for vulnerability checkers.
result = "unknown"
}

View File

@@ -332,4 +332,13 @@ void ptr_diff_case() {
char* admin_begin_pos = strstr(user, "ADMIN");
int offset = admin_begin_pos ? user - admin_begin_pos : 0;
malloc(offset); // GOOD
}
}
void equality_barrier() {
int size1 = atoi(getenv("USER"));
int size2 = atoi(getenv("USER"));
if (size1 == size2) {
int* a = (int*)malloc(size1 * sizeof(int)); // GOOD
}
}

View File

@@ -95,5 +95,12 @@ int main(int argc, char** argv) {
}
}
// GOOD: check the user input first
int maxConnections3 = atoi(argv[1]);
int maxConnections4 = atoi(argv[1]);
if (maxConnections3 == maxConnections4) {
startServer(maxConnections3 * 1000);
}
return 0;
}

View File

@@ -0,0 +1 @@
Console.WriteLine(args[0]);

View File

@@ -0,0 +1,2 @@
cp $PROJECT_TO_BUILD temp.csproj
dotnet build temp.csproj

View File

@@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,6 @@
from create_database_utils import *
import os
os.environ["PROJECT_TO_BUILD"] = "proj.csproj.no_auto"
run_codeql_database_create([], test_db="default-db", db=None, lang="csharp")

View File

@@ -198,6 +198,17 @@ private predicate isBooleanConstant(Expr e, boolean value) {
value = false
or
isConstantComparison(e, value)
or
exists(Method m, Call c, Expr expr |
m = any(SystemStringClass s).getIsNullOrEmptyMethod() and
c.getTarget() = m and
e = c and
expr = c.getArgument(0) and
expr.hasValue() and
if expr.getValue().length() > 0 and not expr instanceof NullLiteral
then value = false
else value = true
)
)
}

View File

@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node source, FlowState state) { none() }
predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes
@@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
}
ParameterPosition getPosition() { this.isParameterOf(_, result) }
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
}
private class RetNodeEx extends NodeEx {
@@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) {
)
}
/**
* Holds if flow from `p` to a return node of kind `kind` is allowed.
*
* We don't expect a parameter to return stored in itself, unless
* explicitly allowed
*/
bindingset[p, kind]
private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) {
exists(ParameterPosition pos | p.isParameterOf(_, pos) |
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
allowParameterReturnInSelfCached(p.asNode())
)
}
/**
* Holds if flow from a parameter at position `pos` inside `c` to a return node of
* kind `kind` is allowed.
*
* We don't expect a parameter to return stored in itself, unless
* explicitly allowed
*/
bindingset[c, pos, kind]
private predicate parameterFlowThroughAllowed(
DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
) {
exists(ParamNodeEx p |
p.isParameterOf(c, pos) and
parameterFlowThroughAllowed(p, kind)
)
}
private module Stage1 implements StageSig {
class Ap = Unit;
@@ -981,21 +1011,22 @@ private module Stage1 implements StageSig {
* candidate for the origin of a summary.
*/
pragma[nomagic]
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
exists(ReturnKindExt kind |
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
exists(DataFlowCallable c, ReturnKindExt kind |
throughFlowNodeCand(p, config) and
returnFlowCallableNodeCand(c, kind, config) and
p.getEnclosingCallable() = c and
exists(ap) and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
or
p.allowParameterReturnInSelf()
)
parameterFlowThroughAllowed(p, kind)
)
}
pragma[nomagic]
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
throughFlowNodeCand(ret, config) and
kind = ret.getKind()
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(ArgNodeEx arg, boolean toReturn |
@@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1(
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config
) {
viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
Stage1::revFlow(ret, config) and
not outBarrier(ret, config) and
not inBarrier(out, config)
exists(ReturnPosition pos |
viableReturnPosOutNodeCand1(call, pos, out, config) and
pos = ret.getReturnPosition() and
kind = pos.getKind() and
Stage1::revFlow(ret, config) and
not outBarrier(ret, config) and
not inBarrier(out, config)
)
}
pragma[nomagic]
@@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1(
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
pragma[nomagic]
private int branch(NodeEx n1, Configuration conf) {
result =
strictcount(NodeEx n |
flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
)
}
@@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) {
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
pragma[nomagic]
private int join(NodeEx n2, Configuration conf) {
result =
strictcount(NodeEx n |
flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
)
}
@@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) {
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
Configuration config
) {
flowOutOfCallNodeCand1(call, ret, out, config) and
flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and
exists(int b, int j |
b = branch(ret, config) and
j = join(out, config) and
b = branch(ret, pragma[only_bind_into](config)) and
j = join(out, pragma[only_bind_into](config)) and
if b.minimum(j) <= config.fieldFlowBranchLimit()
then allowsFieldFlow = true
else allowsFieldFlow = false
@@ -1136,10 +1174,10 @@ pragma[nomagic]
private predicate flowIntoCallNodeCand1(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
flowIntoCallNodeCand1(call, arg, p, config) and
flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and
exists(int b, int j |
b = branch(arg, config) and
j = join(p, config) and
b = branch(arg, pragma[only_bind_into](config)) and
j = join(p, pragma[only_bind_into](config)) and
if b.minimum(j) <= config.fieldFlowBranchLimit()
then allowsFieldFlow = true
else allowsFieldFlow = false
@@ -1156,7 +1194,9 @@ private signature module StageSig {
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
predicate storeStepCand(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
@@ -1222,7 +1262,8 @@ private module MkStage<StageSig PrevStage> {
);
predicate flowOutOfCall(
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
Configuration config
);
predicate flowIntoCall(
@@ -1247,14 +1288,14 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate flowThroughOutOfCall(
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
Configuration config
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
NodeEx out, boolean allowsFieldFlow, Configuration config
) {
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
pragma[only_bind_into](config)) and
matchesCall(ccc, call)
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
matchesCall(ccc, call) and
c = ret.getEnclosingCallable()
}
/**
@@ -1262,29 +1303,32 @@ private module MkStage<StageSig PrevStage> {
* configuration `config`.
*
* The call context `cc` records whether the node is reached through an
* argument in a call, and if so, `argAp` records the access path of that
* argument.
* argument in a call, and if so, `summaryCtx` and `argAp` record the
* corresponding parameter position and access path of that argument, respectively.
*/
pragma[nomagic]
additional predicate fwdFlow(
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, Configuration config
) {
fwdFlow0(node, state, cc, argAp, ap, config) and
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
filter(node, state, ap, config)
}
pragma[nomagic]
private predicate fwdFlow0(
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, Configuration config
) {
sourceNode(node, state, config) and
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
summaryCtx = TParameterPositionNone() and
ap = getApNil(node)
or
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
fwdFlow(mid, state0, cc, argAp, ap0, config) and
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
@@ -1295,65 +1339,82 @@ private module MkStage<StageSig PrevStage> {
)
or
exists(NodeEx mid |
fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone()
)
or
exists(NodeEx mid, ApNil nil |
fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and
fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and
additionalJumpStateStep(mid, state0, node, state, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
or
// store
exists(TypedContent tc, Ap ap0 |
fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
ap = apCons(tc, ap0)
)
or
// read
exists(Ap ap0, Content c |
fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
fwdFlowConsCand(ap0, c, ap, config)
)
or
// flow into a callable
exists(ApApprox apa |
fwdFlowIn(_, node, state, _, cc, _, ap, config) and
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
apa = getApprox(ap) and
if PrevStage::parameterMayFlowThrough(node, _, apa, config)
then argAp = apSome(ap)
else argAp = apNone()
if PrevStage::parameterMayFlowThrough(node, apa, config)
then (
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
argAp = apSome(ap)
) else (
summaryCtx = TParameterPositionNone() and argAp = apNone()
)
)
or
// flow out of a callable
fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
exists(
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
DataFlowCallable inner
|
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and
flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and
inner = ret.getEnclosingCallable() and
cc = getCallContextReturn(inner, call, innercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
or
exists(DataFlowCall call, Ap argAp0 |
fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
fwdFlowIsEntered(call, cc, argAp, argAp0, config)
// flow through a callable
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
)
}
pragma[nomagic]
private predicate fwdFlowStore(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
Configuration config
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
fwdFlow(node1, state, cc, argAp, ap1, config) and
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
@@ -1366,7 +1427,7 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
exists(TypedContent tc |
fwdFlowStore(_, tail, tc, _, _, _, _, config) and
fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and
tc.getContent() = c and
cons = apCons(tc, tail)
)
@@ -1374,21 +1435,21 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate fwdFlowRead(
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
Configuration config
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
fwdFlow(node1, state, cc, argAp, ap, config) and
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::readStepCand(node1, c, node2, config) and
getHeadContent(ap) = c
}
pragma[nomagic]
private predicate fwdFlowIn(
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp,
Ap ap, Configuration config
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc,
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
) {
exists(ArgNodeEx arg, boolean allowsFieldFlow |
fwdFlow(arg, state, outercc, argAp, ap, config) and
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
@@ -1396,29 +1457,19 @@ private module MkStage<StageSig PrevStage> {
}
pragma[nomagic]
private predicate fwdFlowOutNotFromArg(
NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
private predicate fwdFlowOutFromArg(
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
Configuration config
) {
exists(
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
DataFlowCallable inner
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
fwdFlow(ret, state, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
inner = ret.getEnclosingCallable() and
ccOut = getCallContextReturn(inner, call, innercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate fwdFlowOutFromArg(
DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config
) {
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
)
}
@@ -1428,11 +1479,13 @@ private module MkStage<StageSig PrevStage> {
*/
pragma[nomagic]
private predicate fwdFlowIsEntered(
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
ParameterPosition pos, Ap ap, Configuration config
) {
exists(ParamNodeEx p |
fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
exists(ParamNodeEx param |
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
pos = param.getPosition()
)
}
@@ -1440,27 +1493,40 @@ private module MkStage<StageSig PrevStage> {
private predicate storeStepFwd(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
) {
fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and
ap2 = apCons(tc, ap1) and
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config)
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config)
}
private predicate readStepFwd(
NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
) {
fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and
fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and
fwdFlowConsCand(ap1, c, ap2, config)
}
pragma[nomagic]
private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
private predicate returnFlowsThrough0(
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
) {
exists(boolean allowsFieldFlow |
fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
pragma[only_bind_into](config))
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate returnFlowsThrough(
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp,
Ap ap, Configuration config
) {
exists(DataFlowCallable c, ParameterPosition ppos |
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
p.isParameterOf(c, ppos) and
parameterFlowThroughAllowed(p, kind)
)
}
@@ -1468,118 +1534,130 @@ private module MkStage<StageSig PrevStage> {
private predicate flowThroughIntoCall(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
}
pragma[nomagic]
private predicate returnNodeMayFlowThrough(
RetNodeEx ret, FlowState state, Ap ap, Configuration config
) {
fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
exists(Ap argAp |
flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
pragma[only_bind_into](config)) and
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
pragma[only_bind_into](config))
)
}
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
*
* The Boolean `toReturn` records whether the node must be returned from the
* enclosing callable in order to reach a sink, and if so, `returnAp` records
* the access path of the returned value.
* The parameter `returnCtx` records whether (and how) the node must be returned
* from the enclosing callable in order to reach a sink, and if so, `returnAp`
* records the access path of the returned value.
*/
pragma[nomagic]
additional predicate revFlow(
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
Configuration config
) {
revFlow0(node, state, toReturn, returnAp, ap, config) and
fwdFlow(node, state, _, _, ap, config)
revFlow0(node, state, returnCtx, returnAp, ap, config) and
fwdFlow(node, state, _, _, _, ap, config)
}
pragma[nomagic]
private predicate revFlow0(
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
Configuration config
) {
fwdFlow(node, state, _, _, ap, config) and
fwdFlow(node, state, _, _, _, ap, config) and
sinkNode(node, state, config) and
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
(
if hasSinkCallCtx(config)
then returnCtx = TReturnCtxNoFlowThrough()
else returnCtx = TReturnCtxNone()
) and
returnAp = apNone() and
ap instanceof ApNil
or
exists(NodeEx mid, FlowState state0 |
localStep(node, state, mid, state0, true, _, config, _) and
revFlow(mid, state0, toReturn, returnAp, ap, config)
revFlow(mid, state0, returnCtx, returnAp, ap, config)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and
revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, state, _, _, ap, config) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone()
)
or
exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone() and
ap instanceof ApNil
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStateStep(node, state, mid, state0, config) and
revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil,
pragma[only_bind_into](config)) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone() and
ap instanceof ApNil
)
or
// store
exists(Ap ap0, Content c |
revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and
revFlowConsCand(ap0, c, ap, config)
)
or
// read
exists(NodeEx mid, Ap ap0 |
revFlow(mid, state, toReturn, returnAp, ap0, config) and
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
readStepFwd(node, ap, _, mid, ap0, config)
)
or
// flow into a callable
revFlowInNotToReturn(node, state, returnAp, ap, config) and
toReturn = false
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
flowIntoCall(_, node, p, allowsFieldFlow, config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
returnCtx = TReturnCtxNone()
)
or
exists(DataFlowCall call, Ap returnAp0 |
revFlowInToReturn(call, node, state, returnAp0, ap, config) and
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
// flow through a callable
exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 |
revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
or
// flow out of a callable
revFlowOut(_, node, state, _, _, ap, config) and
toReturn = true and
if returnNodeMayFlowThrough(node, state, ap, config)
then returnAp = apSome(ap)
else returnAp = apNone()
exists(ReturnKindExt kind |
revFlowOut(_, node, kind, state, _, _, ap, config) and
if returnFlowsThrough(node, kind, state, _, _, _, ap, config)
then (
returnCtx = TReturnCtxMaybeFlowThrough(kind) and
returnAp = apSome(ap)
) else (
returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone()
)
)
}
pragma[nomagic]
private predicate revFlowStore(
Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid,
boolean toReturn, ApOption returnAp, Configuration config
ReturnCtx returnCtx, ApOption returnAp, Configuration config
) {
revFlow(mid, state, toReturn, returnAp, ap0, config) and
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
storeStepFwd(node, ap, tc, mid, ap0, config) and
tc.getContent() = c
}
@@ -1599,35 +1677,27 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate revFlowOut(
DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap,
Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx,
ApOption returnAp, Ap ap, Configuration config
) {
exists(NodeEx out, boolean allowsFieldFlow |
revFlow(out, state, toReturn, returnAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate revFlowInNotToReturn(
ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, false, returnAp, ap, config) and
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
revFlow(out, state, returnCtx, returnAp, ap, config) and
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate revFlowInToReturn(
DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, true, apSome(returnAp), ap, config) and
revFlow(pragma[only_bind_into](p), state,
TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
parameterFlowThroughAllowed(p, kind)
)
}
@@ -1638,11 +1708,12 @@ private module MkStage<StageSig PrevStage> {
*/
pragma[nomagic]
private predicate revFlowIsReturned(
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap,
Configuration config
) {
exists(RetNodeEx ret, FlowState state, CcCall ccc |
revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and
fwdFlow(ret, state, ccc, apSome(_), ap, config) and
revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and
returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and
matchesCall(ccc, call)
)
}
@@ -1713,40 +1784,39 @@ private module MkStage<StageSig PrevStage> {
validAp(ap, config)
}
pragma[noinline]
private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
pragma[nomagic]
private predicate parameterFlowsThroughRev(
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
) {
revFlow(p, _, true, apSome(ap0), ap, config) and
c = p.getEnclosingCallable()
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
parameterFlowThroughAllowed(p, kind)
}
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
parameterFlow(p, ap, ap0, c, config) and
c = ret.getEnclosingCallable() and
revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_),
pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and
fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and
kind = ret.getKind() and
p.getPosition() = pos and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
p.allowParameterReturnInSelf()
)
pragma[nomagic]
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
exists(RetNodeEx ret, ReturnKindExt kind |
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
parameterFlowsThroughRev(p, ap, kind, config)
)
}
pragma[nomagic]
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
exists(ParamNodeEx p, Ap ap |
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
parameterFlowsThroughRev(p, ap, kind, config)
)
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(
Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
revFlow(arg, state, toReturn, returnAp, ap, config) and
revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
revFlow(arg, state, returnCtx, returnAp, ap, config) and
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
}
@@ -1754,14 +1824,13 @@ private module MkStage<StageSig PrevStage> {
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) {
fwd = true and
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and
states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and
tuples =
count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
fwdFlow(n, state, cc, argAp, ap, config)
)
count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config))
or
fwd = false and
nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
@@ -1769,8 +1838,8 @@ private module MkStage<StageSig PrevStage> {
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
states = count(FlowState state | revFlow(_, state, _, _, _, config)) and
tuples =
count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap |
revFlow(n, state, b, retAp, ap, config)
count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap |
revFlow(n, state, returnCtx, retAp, ap, config)
)
}
/* End: Stage logic. */
@@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
exists(lcc)
}
predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
predicate flowIntoCall = flowIntoCallNodeCand1/5;
@@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig {
pragma[nomagic]
private predicate flowOutOfCallNodeCand2(
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
Stage2::revFlowAlias(node1, pragma[only_bind_into](config))
}
@@ -2021,8 +2091,8 @@ private module LocalFlowBigStep {
exists(NodeEx next | Stage2::revFlow(next, state, config) |
jumpStep(node, next, config) or
additionalJumpStep(node, next, config) or
flowIntoCallNodeCand1(_, node, next, config) or
flowOutOfCallNodeCand1(_, node, next, config) or
flowIntoCallNodeCand2(_, node, next, _, config) or
flowOutOfCallNodeCand2(_, node, _, next, _, config) or
Stage2::storeStepCand(node, _, _, next, _, config) or
Stage2::readStepCand(node, _, next, config)
)
@@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
}
predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
predicate flowIntoCall = flowIntoCallNodeCand2/5;
@@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx(
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
) {
exists(AccessPathFront apf |
Stage3::revFlow(node, state, true, _, apf, config) and
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
config)
)
}
@@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
pragma[nomagic]
predicate flowOutOfCall(
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
exists(FlowState state |
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
pragma[only_bind_into](config))
@@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) {
pragma[nomagic]
private predicate nodeMayUseSummary0(
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa,
Configuration config
) {
exists(AccessPathApprox apa0 |
Stage4::parameterMayFlowThrough(_, c, _, _) and
Stage4::revFlow(n, state, true, _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
n.getEnclosingCallable() = c
c = n.getEnclosingCallable() and
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
TAccessPathApproxSome(apa), apa0, config)
)
}
@@ -2522,9 +2595,10 @@ pragma[nomagic]
private predicate nodeMayUseSummary(
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
) {
exists(DataFlowCallable c |
Stage4::parameterMayFlowThrough(_, c, apa, config) and
nodeMayUseSummary0(n, c, state, apa, config)
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
Stage4::parameterMayFlowThrough(p, apa, config) and
nodeMayUseSummary0(n, c, pos, state, apa, config) and
p.isParameterOf(c, pos)
)
}
@@ -2532,7 +2606,7 @@ private newtype TSummaryCtx =
TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
Stage4::revFlow(p, state, _, config)
)
}
@@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough(
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
AccessPathApprox apa, Configuration config
) {
exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos |
exists(PathNodeMid mid, RetNodeEx ret |
pathNode(mid, ret, state, cc, sc, ap, config, _) and
kind = ret.getKind() and
apa = ap.getApprox() and
pos = sc.getParameterPos() and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
sc.getParamNode().allowParameterReturnInSelf()
)
parameterFlowThroughAllowed(sc.getParamNode(), kind)
)
}

View File

@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node source, FlowState state) { none() }
predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes
@@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
}
ParameterPosition getPosition() { this.isParameterOf(_, result) }
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
}
private class RetNodeEx extends NodeEx {
@@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) {
)
}
/**
* Holds if flow from `p` to a return node of kind `kind` is allowed.
*
* We don't expect a parameter to return stored in itself, unless
* explicitly allowed
*/
bindingset[p, kind]
private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) {
exists(ParameterPosition pos | p.isParameterOf(_, pos) |
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
allowParameterReturnInSelfCached(p.asNode())
)
}
/**
* Holds if flow from a parameter at position `pos` inside `c` to a return node of
* kind `kind` is allowed.
*
* We don't expect a parameter to return stored in itself, unless
* explicitly allowed
*/
bindingset[c, pos, kind]
private predicate parameterFlowThroughAllowed(
DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
) {
exists(ParamNodeEx p |
p.isParameterOf(c, pos) and
parameterFlowThroughAllowed(p, kind)
)
}
private module Stage1 implements StageSig {
class Ap = Unit;
@@ -981,21 +1011,22 @@ private module Stage1 implements StageSig {
* candidate for the origin of a summary.
*/
pragma[nomagic]
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
exists(ReturnKindExt kind |
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
exists(DataFlowCallable c, ReturnKindExt kind |
throughFlowNodeCand(p, config) and
returnFlowCallableNodeCand(c, kind, config) and
p.getEnclosingCallable() = c and
exists(ap) and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
or
p.allowParameterReturnInSelf()
)
parameterFlowThroughAllowed(p, kind)
)
}
pragma[nomagic]
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
throughFlowNodeCand(ret, config) and
kind = ret.getKind()
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(ArgNodeEx arg, boolean toReturn |
@@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1(
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config
) {
viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
Stage1::revFlow(ret, config) and
not outBarrier(ret, config) and
not inBarrier(out, config)
exists(ReturnPosition pos |
viableReturnPosOutNodeCand1(call, pos, out, config) and
pos = ret.getReturnPosition() and
kind = pos.getKind() and
Stage1::revFlow(ret, config) and
not outBarrier(ret, config) and
not inBarrier(out, config)
)
}
pragma[nomagic]
@@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1(
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
pragma[nomagic]
private int branch(NodeEx n1, Configuration conf) {
result =
strictcount(NodeEx n |
flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
)
}
@@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) {
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
pragma[nomagic]
private int join(NodeEx n2, Configuration conf) {
result =
strictcount(NodeEx n |
flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
)
}
@@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) {
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
Configuration config
) {
flowOutOfCallNodeCand1(call, ret, out, config) and
flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and
exists(int b, int j |
b = branch(ret, config) and
j = join(out, config) and
b = branch(ret, pragma[only_bind_into](config)) and
j = join(out, pragma[only_bind_into](config)) and
if b.minimum(j) <= config.fieldFlowBranchLimit()
then allowsFieldFlow = true
else allowsFieldFlow = false
@@ -1136,10 +1174,10 @@ pragma[nomagic]
private predicate flowIntoCallNodeCand1(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
flowIntoCallNodeCand1(call, arg, p, config) and
flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and
exists(int b, int j |
b = branch(arg, config) and
j = join(p, config) and
b = branch(arg, pragma[only_bind_into](config)) and
j = join(p, pragma[only_bind_into](config)) and
if b.minimum(j) <= config.fieldFlowBranchLimit()
then allowsFieldFlow = true
else allowsFieldFlow = false
@@ -1156,7 +1194,9 @@ private signature module StageSig {
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
predicate storeStepCand(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
@@ -1222,7 +1262,8 @@ private module MkStage<StageSig PrevStage> {
);
predicate flowOutOfCall(
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
Configuration config
);
predicate flowIntoCall(
@@ -1247,14 +1288,14 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate flowThroughOutOfCall(
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
Configuration config
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
NodeEx out, boolean allowsFieldFlow, Configuration config
) {
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
pragma[only_bind_into](config)) and
matchesCall(ccc, call)
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
matchesCall(ccc, call) and
c = ret.getEnclosingCallable()
}
/**
@@ -1262,29 +1303,32 @@ private module MkStage<StageSig PrevStage> {
* configuration `config`.
*
* The call context `cc` records whether the node is reached through an
* argument in a call, and if so, `argAp` records the access path of that
* argument.
* argument in a call, and if so, `summaryCtx` and `argAp` record the
* corresponding parameter position and access path of that argument, respectively.
*/
pragma[nomagic]
additional predicate fwdFlow(
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, Configuration config
) {
fwdFlow0(node, state, cc, argAp, ap, config) and
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
filter(node, state, ap, config)
}
pragma[nomagic]
private predicate fwdFlow0(
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, Configuration config
) {
sourceNode(node, state, config) and
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
summaryCtx = TParameterPositionNone() and
ap = getApNil(node)
or
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
fwdFlow(mid, state0, cc, argAp, ap0, config) and
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
@@ -1295,65 +1339,82 @@ private module MkStage<StageSig PrevStage> {
)
or
exists(NodeEx mid |
fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone()
)
or
exists(NodeEx mid, ApNil nil |
fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and
fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and
additionalJumpStateStep(mid, state0, node, state, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
or
// store
exists(TypedContent tc, Ap ap0 |
fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
ap = apCons(tc, ap0)
)
or
// read
exists(Ap ap0, Content c |
fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
fwdFlowConsCand(ap0, c, ap, config)
)
or
// flow into a callable
exists(ApApprox apa |
fwdFlowIn(_, node, state, _, cc, _, ap, config) and
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
apa = getApprox(ap) and
if PrevStage::parameterMayFlowThrough(node, _, apa, config)
then argAp = apSome(ap)
else argAp = apNone()
if PrevStage::parameterMayFlowThrough(node, apa, config)
then (
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
argAp = apSome(ap)
) else (
summaryCtx = TParameterPositionNone() and argAp = apNone()
)
)
or
// flow out of a callable
fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
exists(
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
DataFlowCallable inner
|
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and
flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and
inner = ret.getEnclosingCallable() and
cc = getCallContextReturn(inner, call, innercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
or
exists(DataFlowCall call, Ap argAp0 |
fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
fwdFlowIsEntered(call, cc, argAp, argAp0, config)
// flow through a callable
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
)
}
pragma[nomagic]
private predicate fwdFlowStore(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
Configuration config
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
fwdFlow(node1, state, cc, argAp, ap1, config) and
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
@@ -1366,7 +1427,7 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
exists(TypedContent tc |
fwdFlowStore(_, tail, tc, _, _, _, _, config) and
fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and
tc.getContent() = c and
cons = apCons(tc, tail)
)
@@ -1374,21 +1435,21 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate fwdFlowRead(
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
Configuration config
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
fwdFlow(node1, state, cc, argAp, ap, config) and
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::readStepCand(node1, c, node2, config) and
getHeadContent(ap) = c
}
pragma[nomagic]
private predicate fwdFlowIn(
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp,
Ap ap, Configuration config
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc,
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
) {
exists(ArgNodeEx arg, boolean allowsFieldFlow |
fwdFlow(arg, state, outercc, argAp, ap, config) and
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
@@ -1396,29 +1457,19 @@ private module MkStage<StageSig PrevStage> {
}
pragma[nomagic]
private predicate fwdFlowOutNotFromArg(
NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
private predicate fwdFlowOutFromArg(
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
Configuration config
) {
exists(
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
DataFlowCallable inner
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
fwdFlow(ret, state, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
inner = ret.getEnclosingCallable() and
ccOut = getCallContextReturn(inner, call, innercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate fwdFlowOutFromArg(
DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config
) {
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
)
}
@@ -1428,11 +1479,13 @@ private module MkStage<StageSig PrevStage> {
*/
pragma[nomagic]
private predicate fwdFlowIsEntered(
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
ParameterPosition pos, Ap ap, Configuration config
) {
exists(ParamNodeEx p |
fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
exists(ParamNodeEx param |
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
pos = param.getPosition()
)
}
@@ -1440,27 +1493,40 @@ private module MkStage<StageSig PrevStage> {
private predicate storeStepFwd(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
) {
fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and
ap2 = apCons(tc, ap1) and
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config)
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config)
}
private predicate readStepFwd(
NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
) {
fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and
fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and
fwdFlowConsCand(ap1, c, ap2, config)
}
pragma[nomagic]
private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
private predicate returnFlowsThrough0(
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
) {
exists(boolean allowsFieldFlow |
fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
pragma[only_bind_into](config))
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate returnFlowsThrough(
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp,
Ap ap, Configuration config
) {
exists(DataFlowCallable c, ParameterPosition ppos |
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
p.isParameterOf(c, ppos) and
parameterFlowThroughAllowed(p, kind)
)
}
@@ -1468,118 +1534,130 @@ private module MkStage<StageSig PrevStage> {
private predicate flowThroughIntoCall(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
}
pragma[nomagic]
private predicate returnNodeMayFlowThrough(
RetNodeEx ret, FlowState state, Ap ap, Configuration config
) {
fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
exists(Ap argAp |
flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
pragma[only_bind_into](config)) and
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
pragma[only_bind_into](config))
)
}
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
*
* The Boolean `toReturn` records whether the node must be returned from the
* enclosing callable in order to reach a sink, and if so, `returnAp` records
* the access path of the returned value.
* The parameter `returnCtx` records whether (and how) the node must be returned
* from the enclosing callable in order to reach a sink, and if so, `returnAp`
* records the access path of the returned value.
*/
pragma[nomagic]
additional predicate revFlow(
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
Configuration config
) {
revFlow0(node, state, toReturn, returnAp, ap, config) and
fwdFlow(node, state, _, _, ap, config)
revFlow0(node, state, returnCtx, returnAp, ap, config) and
fwdFlow(node, state, _, _, _, ap, config)
}
pragma[nomagic]
private predicate revFlow0(
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
Configuration config
) {
fwdFlow(node, state, _, _, ap, config) and
fwdFlow(node, state, _, _, _, ap, config) and
sinkNode(node, state, config) and
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
(
if hasSinkCallCtx(config)
then returnCtx = TReturnCtxNoFlowThrough()
else returnCtx = TReturnCtxNone()
) and
returnAp = apNone() and
ap instanceof ApNil
or
exists(NodeEx mid, FlowState state0 |
localStep(node, state, mid, state0, true, _, config, _) and
revFlow(mid, state0, toReturn, returnAp, ap, config)
revFlow(mid, state0, returnCtx, returnAp, ap, config)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and
revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, state, _, _, ap, config) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone()
)
or
exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone() and
ap instanceof ApNil
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStateStep(node, state, mid, state0, config) and
revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil,
pragma[only_bind_into](config)) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone() and
ap instanceof ApNil
)
or
// store
exists(Ap ap0, Content c |
revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and
revFlowConsCand(ap0, c, ap, config)
)
or
// read
exists(NodeEx mid, Ap ap0 |
revFlow(mid, state, toReturn, returnAp, ap0, config) and
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
readStepFwd(node, ap, _, mid, ap0, config)
)
or
// flow into a callable
revFlowInNotToReturn(node, state, returnAp, ap, config) and
toReturn = false
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
flowIntoCall(_, node, p, allowsFieldFlow, config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
returnCtx = TReturnCtxNone()
)
or
exists(DataFlowCall call, Ap returnAp0 |
revFlowInToReturn(call, node, state, returnAp0, ap, config) and
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
// flow through a callable
exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 |
revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
or
// flow out of a callable
revFlowOut(_, node, state, _, _, ap, config) and
toReturn = true and
if returnNodeMayFlowThrough(node, state, ap, config)
then returnAp = apSome(ap)
else returnAp = apNone()
exists(ReturnKindExt kind |
revFlowOut(_, node, kind, state, _, _, ap, config) and
if returnFlowsThrough(node, kind, state, _, _, _, ap, config)
then (
returnCtx = TReturnCtxMaybeFlowThrough(kind) and
returnAp = apSome(ap)
) else (
returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone()
)
)
}
pragma[nomagic]
private predicate revFlowStore(
Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid,
boolean toReturn, ApOption returnAp, Configuration config
ReturnCtx returnCtx, ApOption returnAp, Configuration config
) {
revFlow(mid, state, toReturn, returnAp, ap0, config) and
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
storeStepFwd(node, ap, tc, mid, ap0, config) and
tc.getContent() = c
}
@@ -1599,35 +1677,27 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate revFlowOut(
DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap,
Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx,
ApOption returnAp, Ap ap, Configuration config
) {
exists(NodeEx out, boolean allowsFieldFlow |
revFlow(out, state, toReturn, returnAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate revFlowInNotToReturn(
ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, false, returnAp, ap, config) and
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
revFlow(out, state, returnCtx, returnAp, ap, config) and
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate revFlowInToReturn(
DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, true, apSome(returnAp), ap, config) and
revFlow(pragma[only_bind_into](p), state,
TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
parameterFlowThroughAllowed(p, kind)
)
}
@@ -1638,11 +1708,12 @@ private module MkStage<StageSig PrevStage> {
*/
pragma[nomagic]
private predicate revFlowIsReturned(
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap,
Configuration config
) {
exists(RetNodeEx ret, FlowState state, CcCall ccc |
revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and
fwdFlow(ret, state, ccc, apSome(_), ap, config) and
revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and
returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and
matchesCall(ccc, call)
)
}
@@ -1713,40 +1784,39 @@ private module MkStage<StageSig PrevStage> {
validAp(ap, config)
}
pragma[noinline]
private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
pragma[nomagic]
private predicate parameterFlowsThroughRev(
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
) {
revFlow(p, _, true, apSome(ap0), ap, config) and
c = p.getEnclosingCallable()
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
parameterFlowThroughAllowed(p, kind)
}
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
parameterFlow(p, ap, ap0, c, config) and
c = ret.getEnclosingCallable() and
revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_),
pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and
fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and
kind = ret.getKind() and
p.getPosition() = pos and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
p.allowParameterReturnInSelf()
)
pragma[nomagic]
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
exists(RetNodeEx ret, ReturnKindExt kind |
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
parameterFlowsThroughRev(p, ap, kind, config)
)
}
pragma[nomagic]
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
exists(ParamNodeEx p, Ap ap |
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
parameterFlowsThroughRev(p, ap, kind, config)
)
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(
Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
revFlow(arg, state, toReturn, returnAp, ap, config) and
revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
revFlow(arg, state, returnCtx, returnAp, ap, config) and
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
}
@@ -1754,14 +1824,13 @@ private module MkStage<StageSig PrevStage> {
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) {
fwd = true and
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and
states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and
tuples =
count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
fwdFlow(n, state, cc, argAp, ap, config)
)
count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config))
or
fwd = false and
nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
@@ -1769,8 +1838,8 @@ private module MkStage<StageSig PrevStage> {
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
states = count(FlowState state | revFlow(_, state, _, _, _, config)) and
tuples =
count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap |
revFlow(n, state, b, retAp, ap, config)
count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap |
revFlow(n, state, returnCtx, retAp, ap, config)
)
}
/* End: Stage logic. */
@@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
exists(lcc)
}
predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
predicate flowIntoCall = flowIntoCallNodeCand1/5;
@@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig {
pragma[nomagic]
private predicate flowOutOfCallNodeCand2(
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
Stage2::revFlowAlias(node1, pragma[only_bind_into](config))
}
@@ -2021,8 +2091,8 @@ private module LocalFlowBigStep {
exists(NodeEx next | Stage2::revFlow(next, state, config) |
jumpStep(node, next, config) or
additionalJumpStep(node, next, config) or
flowIntoCallNodeCand1(_, node, next, config) or
flowOutOfCallNodeCand1(_, node, next, config) or
flowIntoCallNodeCand2(_, node, next, _, config) or
flowOutOfCallNodeCand2(_, node, _, next, _, config) or
Stage2::storeStepCand(node, _, _, next, _, config) or
Stage2::readStepCand(node, _, next, config)
)
@@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
}
predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
predicate flowIntoCall = flowIntoCallNodeCand2/5;
@@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx(
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
) {
exists(AccessPathFront apf |
Stage3::revFlow(node, state, true, _, apf, config) and
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
config)
)
}
@@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
pragma[nomagic]
predicate flowOutOfCall(
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
exists(FlowState state |
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
pragma[only_bind_into](config))
@@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) {
pragma[nomagic]
private predicate nodeMayUseSummary0(
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa,
Configuration config
) {
exists(AccessPathApprox apa0 |
Stage4::parameterMayFlowThrough(_, c, _, _) and
Stage4::revFlow(n, state, true, _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
n.getEnclosingCallable() = c
c = n.getEnclosingCallable() and
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
TAccessPathApproxSome(apa), apa0, config)
)
}
@@ -2522,9 +2595,10 @@ pragma[nomagic]
private predicate nodeMayUseSummary(
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
) {
exists(DataFlowCallable c |
Stage4::parameterMayFlowThrough(_, c, apa, config) and
nodeMayUseSummary0(n, c, state, apa, config)
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
Stage4::parameterMayFlowThrough(p, apa, config) and
nodeMayUseSummary0(n, c, pos, state, apa, config) and
p.isParameterOf(c, pos)
)
}
@@ -2532,7 +2606,7 @@ private newtype TSummaryCtx =
TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
Stage4::revFlow(p, state, _, config)
)
}
@@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough(
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
AccessPathApprox apa, Configuration config
) {
exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos |
exists(PathNodeMid mid, RetNodeEx ret |
pathNode(mid, ret, state, cc, sc, ap, config, _) and
kind = ret.getKind() and
apa = ap.getApprox() and
pos = sc.getParameterPos() and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
sc.getParamNode().allowParameterReturnInSelf()
)
parameterFlowThroughAllowed(sc.getParamNode(), kind)
)
}

View File

@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node source, FlowState state) { none() }
predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes
@@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
}
ParameterPosition getPosition() { this.isParameterOf(_, result) }
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
}
private class RetNodeEx extends NodeEx {
@@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) {
)
}
/**
* Holds if flow from `p` to a return node of kind `kind` is allowed.
*
* We don't expect a parameter to return stored in itself, unless
* explicitly allowed
*/
bindingset[p, kind]
private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) {
exists(ParameterPosition pos | p.isParameterOf(_, pos) |
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
allowParameterReturnInSelfCached(p.asNode())
)
}
/**
* Holds if flow from a parameter at position `pos` inside `c` to a return node of
* kind `kind` is allowed.
*
* We don't expect a parameter to return stored in itself, unless
* explicitly allowed
*/
bindingset[c, pos, kind]
private predicate parameterFlowThroughAllowed(
DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
) {
exists(ParamNodeEx p |
p.isParameterOf(c, pos) and
parameterFlowThroughAllowed(p, kind)
)
}
private module Stage1 implements StageSig {
class Ap = Unit;
@@ -981,21 +1011,22 @@ private module Stage1 implements StageSig {
* candidate for the origin of a summary.
*/
pragma[nomagic]
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
exists(ReturnKindExt kind |
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
exists(DataFlowCallable c, ReturnKindExt kind |
throughFlowNodeCand(p, config) and
returnFlowCallableNodeCand(c, kind, config) and
p.getEnclosingCallable() = c and
exists(ap) and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
or
p.allowParameterReturnInSelf()
)
parameterFlowThroughAllowed(p, kind)
)
}
pragma[nomagic]
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
throughFlowNodeCand(ret, config) and
kind = ret.getKind()
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(ArgNodeEx arg, boolean toReturn |
@@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1(
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config
) {
viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
Stage1::revFlow(ret, config) and
not outBarrier(ret, config) and
not inBarrier(out, config)
exists(ReturnPosition pos |
viableReturnPosOutNodeCand1(call, pos, out, config) and
pos = ret.getReturnPosition() and
kind = pos.getKind() and
Stage1::revFlow(ret, config) and
not outBarrier(ret, config) and
not inBarrier(out, config)
)
}
pragma[nomagic]
@@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1(
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
pragma[nomagic]
private int branch(NodeEx n1, Configuration conf) {
result =
strictcount(NodeEx n |
flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
)
}
@@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) {
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
pragma[nomagic]
private int join(NodeEx n2, Configuration conf) {
result =
strictcount(NodeEx n |
flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
)
}
@@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) {
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
Configuration config
) {
flowOutOfCallNodeCand1(call, ret, out, config) and
flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and
exists(int b, int j |
b = branch(ret, config) and
j = join(out, config) and
b = branch(ret, pragma[only_bind_into](config)) and
j = join(out, pragma[only_bind_into](config)) and
if b.minimum(j) <= config.fieldFlowBranchLimit()
then allowsFieldFlow = true
else allowsFieldFlow = false
@@ -1136,10 +1174,10 @@ pragma[nomagic]
private predicate flowIntoCallNodeCand1(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
flowIntoCallNodeCand1(call, arg, p, config) and
flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and
exists(int b, int j |
b = branch(arg, config) and
j = join(p, config) and
b = branch(arg, pragma[only_bind_into](config)) and
j = join(p, pragma[only_bind_into](config)) and
if b.minimum(j) <= config.fieldFlowBranchLimit()
then allowsFieldFlow = true
else allowsFieldFlow = false
@@ -1156,7 +1194,9 @@ private signature module StageSig {
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
predicate storeStepCand(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
@@ -1222,7 +1262,8 @@ private module MkStage<StageSig PrevStage> {
);
predicate flowOutOfCall(
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
Configuration config
);
predicate flowIntoCall(
@@ -1247,14 +1288,14 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate flowThroughOutOfCall(
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
Configuration config
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
NodeEx out, boolean allowsFieldFlow, Configuration config
) {
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
pragma[only_bind_into](config)) and
matchesCall(ccc, call)
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
matchesCall(ccc, call) and
c = ret.getEnclosingCallable()
}
/**
@@ -1262,29 +1303,32 @@ private module MkStage<StageSig PrevStage> {
* configuration `config`.
*
* The call context `cc` records whether the node is reached through an
* argument in a call, and if so, `argAp` records the access path of that
* argument.
* argument in a call, and if so, `summaryCtx` and `argAp` record the
* corresponding parameter position and access path of that argument, respectively.
*/
pragma[nomagic]
additional predicate fwdFlow(
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, Configuration config
) {
fwdFlow0(node, state, cc, argAp, ap, config) and
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
filter(node, state, ap, config)
}
pragma[nomagic]
private predicate fwdFlow0(
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, Configuration config
) {
sourceNode(node, state, config) and
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
summaryCtx = TParameterPositionNone() and
ap = getApNil(node)
or
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
fwdFlow(mid, state0, cc, argAp, ap0, config) and
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
@@ -1295,65 +1339,82 @@ private module MkStage<StageSig PrevStage> {
)
or
exists(NodeEx mid |
fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone()
)
or
exists(NodeEx mid, ApNil nil |
fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and
fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and
additionalJumpStateStep(mid, state0, node, state, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
or
// store
exists(TypedContent tc, Ap ap0 |
fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
ap = apCons(tc, ap0)
)
or
// read
exists(Ap ap0, Content c |
fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
fwdFlowConsCand(ap0, c, ap, config)
)
or
// flow into a callable
exists(ApApprox apa |
fwdFlowIn(_, node, state, _, cc, _, ap, config) and
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
apa = getApprox(ap) and
if PrevStage::parameterMayFlowThrough(node, _, apa, config)
then argAp = apSome(ap)
else argAp = apNone()
if PrevStage::parameterMayFlowThrough(node, apa, config)
then (
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
argAp = apSome(ap)
) else (
summaryCtx = TParameterPositionNone() and argAp = apNone()
)
)
or
// flow out of a callable
fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
exists(
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
DataFlowCallable inner
|
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and
flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and
inner = ret.getEnclosingCallable() and
cc = getCallContextReturn(inner, call, innercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
or
exists(DataFlowCall call, Ap argAp0 |
fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
fwdFlowIsEntered(call, cc, argAp, argAp0, config)
// flow through a callable
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
)
}
pragma[nomagic]
private predicate fwdFlowStore(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
Configuration config
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
fwdFlow(node1, state, cc, argAp, ap1, config) and
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
@@ -1366,7 +1427,7 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
exists(TypedContent tc |
fwdFlowStore(_, tail, tc, _, _, _, _, config) and
fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and
tc.getContent() = c and
cons = apCons(tc, tail)
)
@@ -1374,21 +1435,21 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate fwdFlowRead(
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
Configuration config
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
fwdFlow(node1, state, cc, argAp, ap, config) and
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::readStepCand(node1, c, node2, config) and
getHeadContent(ap) = c
}
pragma[nomagic]
private predicate fwdFlowIn(
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp,
Ap ap, Configuration config
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc,
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
) {
exists(ArgNodeEx arg, boolean allowsFieldFlow |
fwdFlow(arg, state, outercc, argAp, ap, config) and
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
@@ -1396,29 +1457,19 @@ private module MkStage<StageSig PrevStage> {
}
pragma[nomagic]
private predicate fwdFlowOutNotFromArg(
NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
private predicate fwdFlowOutFromArg(
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
Configuration config
) {
exists(
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
DataFlowCallable inner
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
fwdFlow(ret, state, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
inner = ret.getEnclosingCallable() and
ccOut = getCallContextReturn(inner, call, innercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate fwdFlowOutFromArg(
DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config
) {
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
)
}
@@ -1428,11 +1479,13 @@ private module MkStage<StageSig PrevStage> {
*/
pragma[nomagic]
private predicate fwdFlowIsEntered(
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
ParameterPosition pos, Ap ap, Configuration config
) {
exists(ParamNodeEx p |
fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
exists(ParamNodeEx param |
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
pos = param.getPosition()
)
}
@@ -1440,27 +1493,40 @@ private module MkStage<StageSig PrevStage> {
private predicate storeStepFwd(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
) {
fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and
ap2 = apCons(tc, ap1) and
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config)
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config)
}
private predicate readStepFwd(
NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
) {
fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and
fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and
fwdFlowConsCand(ap1, c, ap2, config)
}
pragma[nomagic]
private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
private predicate returnFlowsThrough0(
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
) {
exists(boolean allowsFieldFlow |
fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
pragma[only_bind_into](config))
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate returnFlowsThrough(
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp,
Ap ap, Configuration config
) {
exists(DataFlowCallable c, ParameterPosition ppos |
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
p.isParameterOf(c, ppos) and
parameterFlowThroughAllowed(p, kind)
)
}
@@ -1468,118 +1534,130 @@ private module MkStage<StageSig PrevStage> {
private predicate flowThroughIntoCall(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
}
pragma[nomagic]
private predicate returnNodeMayFlowThrough(
RetNodeEx ret, FlowState state, Ap ap, Configuration config
) {
fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
exists(Ap argAp |
flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
pragma[only_bind_into](config)) and
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
pragma[only_bind_into](config))
)
}
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
*
* The Boolean `toReturn` records whether the node must be returned from the
* enclosing callable in order to reach a sink, and if so, `returnAp` records
* the access path of the returned value.
* The parameter `returnCtx` records whether (and how) the node must be returned
* from the enclosing callable in order to reach a sink, and if so, `returnAp`
* records the access path of the returned value.
*/
pragma[nomagic]
additional predicate revFlow(
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
Configuration config
) {
revFlow0(node, state, toReturn, returnAp, ap, config) and
fwdFlow(node, state, _, _, ap, config)
revFlow0(node, state, returnCtx, returnAp, ap, config) and
fwdFlow(node, state, _, _, _, ap, config)
}
pragma[nomagic]
private predicate revFlow0(
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
Configuration config
) {
fwdFlow(node, state, _, _, ap, config) and
fwdFlow(node, state, _, _, _, ap, config) and
sinkNode(node, state, config) and
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
(
if hasSinkCallCtx(config)
then returnCtx = TReturnCtxNoFlowThrough()
else returnCtx = TReturnCtxNone()
) and
returnAp = apNone() and
ap instanceof ApNil
or
exists(NodeEx mid, FlowState state0 |
localStep(node, state, mid, state0, true, _, config, _) and
revFlow(mid, state0, toReturn, returnAp, ap, config)
revFlow(mid, state0, returnCtx, returnAp, ap, config)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and
revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, state, _, _, ap, config) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone()
)
or
exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone() and
ap instanceof ApNil
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStateStep(node, state, mid, state0, config) and
revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil,
pragma[only_bind_into](config)) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone() and
ap instanceof ApNil
)
or
// store
exists(Ap ap0, Content c |
revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and
revFlowConsCand(ap0, c, ap, config)
)
or
// read
exists(NodeEx mid, Ap ap0 |
revFlow(mid, state, toReturn, returnAp, ap0, config) and
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
readStepFwd(node, ap, _, mid, ap0, config)
)
or
// flow into a callable
revFlowInNotToReturn(node, state, returnAp, ap, config) and
toReturn = false
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
flowIntoCall(_, node, p, allowsFieldFlow, config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
returnCtx = TReturnCtxNone()
)
or
exists(DataFlowCall call, Ap returnAp0 |
revFlowInToReturn(call, node, state, returnAp0, ap, config) and
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
// flow through a callable
exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 |
revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
or
// flow out of a callable
revFlowOut(_, node, state, _, _, ap, config) and
toReturn = true and
if returnNodeMayFlowThrough(node, state, ap, config)
then returnAp = apSome(ap)
else returnAp = apNone()
exists(ReturnKindExt kind |
revFlowOut(_, node, kind, state, _, _, ap, config) and
if returnFlowsThrough(node, kind, state, _, _, _, ap, config)
then (
returnCtx = TReturnCtxMaybeFlowThrough(kind) and
returnAp = apSome(ap)
) else (
returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone()
)
)
}
pragma[nomagic]
private predicate revFlowStore(
Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid,
boolean toReturn, ApOption returnAp, Configuration config
ReturnCtx returnCtx, ApOption returnAp, Configuration config
) {
revFlow(mid, state, toReturn, returnAp, ap0, config) and
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
storeStepFwd(node, ap, tc, mid, ap0, config) and
tc.getContent() = c
}
@@ -1599,35 +1677,27 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate revFlowOut(
DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap,
Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx,
ApOption returnAp, Ap ap, Configuration config
) {
exists(NodeEx out, boolean allowsFieldFlow |
revFlow(out, state, toReturn, returnAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate revFlowInNotToReturn(
ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, false, returnAp, ap, config) and
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
revFlow(out, state, returnCtx, returnAp, ap, config) and
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate revFlowInToReturn(
DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, true, apSome(returnAp), ap, config) and
revFlow(pragma[only_bind_into](p), state,
TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
parameterFlowThroughAllowed(p, kind)
)
}
@@ -1638,11 +1708,12 @@ private module MkStage<StageSig PrevStage> {
*/
pragma[nomagic]
private predicate revFlowIsReturned(
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap,
Configuration config
) {
exists(RetNodeEx ret, FlowState state, CcCall ccc |
revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and
fwdFlow(ret, state, ccc, apSome(_), ap, config) and
revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and
returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and
matchesCall(ccc, call)
)
}
@@ -1713,40 +1784,39 @@ private module MkStage<StageSig PrevStage> {
validAp(ap, config)
}
pragma[noinline]
private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
pragma[nomagic]
private predicate parameterFlowsThroughRev(
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
) {
revFlow(p, _, true, apSome(ap0), ap, config) and
c = p.getEnclosingCallable()
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
parameterFlowThroughAllowed(p, kind)
}
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
parameterFlow(p, ap, ap0, c, config) and
c = ret.getEnclosingCallable() and
revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_),
pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and
fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and
kind = ret.getKind() and
p.getPosition() = pos and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
p.allowParameterReturnInSelf()
)
pragma[nomagic]
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
exists(RetNodeEx ret, ReturnKindExt kind |
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
parameterFlowsThroughRev(p, ap, kind, config)
)
}
pragma[nomagic]
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
exists(ParamNodeEx p, Ap ap |
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
parameterFlowsThroughRev(p, ap, kind, config)
)
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(
Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
revFlow(arg, state, toReturn, returnAp, ap, config) and
revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
revFlow(arg, state, returnCtx, returnAp, ap, config) and
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
}
@@ -1754,14 +1824,13 @@ private module MkStage<StageSig PrevStage> {
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) {
fwd = true and
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and
states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and
tuples =
count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
fwdFlow(n, state, cc, argAp, ap, config)
)
count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config))
or
fwd = false and
nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
@@ -1769,8 +1838,8 @@ private module MkStage<StageSig PrevStage> {
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
states = count(FlowState state | revFlow(_, state, _, _, _, config)) and
tuples =
count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap |
revFlow(n, state, b, retAp, ap, config)
count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap |
revFlow(n, state, returnCtx, retAp, ap, config)
)
}
/* End: Stage logic. */
@@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
exists(lcc)
}
predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
predicate flowIntoCall = flowIntoCallNodeCand1/5;
@@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig {
pragma[nomagic]
private predicate flowOutOfCallNodeCand2(
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
Stage2::revFlowAlias(node1, pragma[only_bind_into](config))
}
@@ -2021,8 +2091,8 @@ private module LocalFlowBigStep {
exists(NodeEx next | Stage2::revFlow(next, state, config) |
jumpStep(node, next, config) or
additionalJumpStep(node, next, config) or
flowIntoCallNodeCand1(_, node, next, config) or
flowOutOfCallNodeCand1(_, node, next, config) or
flowIntoCallNodeCand2(_, node, next, _, config) or
flowOutOfCallNodeCand2(_, node, _, next, _, config) or
Stage2::storeStepCand(node, _, _, next, _, config) or
Stage2::readStepCand(node, _, next, config)
)
@@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
}
predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
predicate flowIntoCall = flowIntoCallNodeCand2/5;
@@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx(
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
) {
exists(AccessPathFront apf |
Stage3::revFlow(node, state, true, _, apf, config) and
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
config)
)
}
@@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
pragma[nomagic]
predicate flowOutOfCall(
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
exists(FlowState state |
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
pragma[only_bind_into](config))
@@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) {
pragma[nomagic]
private predicate nodeMayUseSummary0(
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa,
Configuration config
) {
exists(AccessPathApprox apa0 |
Stage4::parameterMayFlowThrough(_, c, _, _) and
Stage4::revFlow(n, state, true, _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
n.getEnclosingCallable() = c
c = n.getEnclosingCallable() and
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
TAccessPathApproxSome(apa), apa0, config)
)
}
@@ -2522,9 +2595,10 @@ pragma[nomagic]
private predicate nodeMayUseSummary(
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
) {
exists(DataFlowCallable c |
Stage4::parameterMayFlowThrough(_, c, apa, config) and
nodeMayUseSummary0(n, c, state, apa, config)
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
Stage4::parameterMayFlowThrough(p, apa, config) and
nodeMayUseSummary0(n, c, pos, state, apa, config) and
p.isParameterOf(c, pos)
)
}
@@ -2532,7 +2606,7 @@ private newtype TSummaryCtx =
TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
Stage4::revFlow(p, state, _, config)
)
}
@@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough(
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
AccessPathApprox apa, Configuration config
) {
exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos |
exists(PathNodeMid mid, RetNodeEx ret |
pathNode(mid, ret, state, cc, sc, ap, config, _) and
kind = ret.getKind() and
apa = ap.getApprox() and
pos = sc.getParameterPos() and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
sc.getParamNode().allowParameterReturnInSelf()
)
parameterFlowThroughAllowed(sc.getParamNode(), kind)
)
}

View File

@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node source, FlowState state) { none() }
predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes
@@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
}
ParameterPosition getPosition() { this.isParameterOf(_, result) }
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
}
private class RetNodeEx extends NodeEx {
@@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) {
)
}
/**
* Holds if flow from `p` to a return node of kind `kind` is allowed.
*
* We don't expect a parameter to return stored in itself, unless
* explicitly allowed
*/
bindingset[p, kind]
private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) {
exists(ParameterPosition pos | p.isParameterOf(_, pos) |
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
allowParameterReturnInSelfCached(p.asNode())
)
}
/**
* Holds if flow from a parameter at position `pos` inside `c` to a return node of
* kind `kind` is allowed.
*
* We don't expect a parameter to return stored in itself, unless
* explicitly allowed
*/
bindingset[c, pos, kind]
private predicate parameterFlowThroughAllowed(
DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
) {
exists(ParamNodeEx p |
p.isParameterOf(c, pos) and
parameterFlowThroughAllowed(p, kind)
)
}
private module Stage1 implements StageSig {
class Ap = Unit;
@@ -981,21 +1011,22 @@ private module Stage1 implements StageSig {
* candidate for the origin of a summary.
*/
pragma[nomagic]
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
exists(ReturnKindExt kind |
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
exists(DataFlowCallable c, ReturnKindExt kind |
throughFlowNodeCand(p, config) and
returnFlowCallableNodeCand(c, kind, config) and
p.getEnclosingCallable() = c and
exists(ap) and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
or
p.allowParameterReturnInSelf()
)
parameterFlowThroughAllowed(p, kind)
)
}
pragma[nomagic]
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
throughFlowNodeCand(ret, config) and
kind = ret.getKind()
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(ArgNodeEx arg, boolean toReturn |
@@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1(
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config
) {
viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
Stage1::revFlow(ret, config) and
not outBarrier(ret, config) and
not inBarrier(out, config)
exists(ReturnPosition pos |
viableReturnPosOutNodeCand1(call, pos, out, config) and
pos = ret.getReturnPosition() and
kind = pos.getKind() and
Stage1::revFlow(ret, config) and
not outBarrier(ret, config) and
not inBarrier(out, config)
)
}
pragma[nomagic]
@@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1(
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
pragma[nomagic]
private int branch(NodeEx n1, Configuration conf) {
result =
strictcount(NodeEx n |
flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
)
}
@@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) {
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
pragma[nomagic]
private int join(NodeEx n2, Configuration conf) {
result =
strictcount(NodeEx n |
flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
)
}
@@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) {
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
Configuration config
) {
flowOutOfCallNodeCand1(call, ret, out, config) and
flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and
exists(int b, int j |
b = branch(ret, config) and
j = join(out, config) and
b = branch(ret, pragma[only_bind_into](config)) and
j = join(out, pragma[only_bind_into](config)) and
if b.minimum(j) <= config.fieldFlowBranchLimit()
then allowsFieldFlow = true
else allowsFieldFlow = false
@@ -1136,10 +1174,10 @@ pragma[nomagic]
private predicate flowIntoCallNodeCand1(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
flowIntoCallNodeCand1(call, arg, p, config) and
flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and
exists(int b, int j |
b = branch(arg, config) and
j = join(p, config) and
b = branch(arg, pragma[only_bind_into](config)) and
j = join(p, pragma[only_bind_into](config)) and
if b.minimum(j) <= config.fieldFlowBranchLimit()
then allowsFieldFlow = true
else allowsFieldFlow = false
@@ -1156,7 +1194,9 @@ private signature module StageSig {
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
predicate storeStepCand(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
@@ -1222,7 +1262,8 @@ private module MkStage<StageSig PrevStage> {
);
predicate flowOutOfCall(
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
Configuration config
);
predicate flowIntoCall(
@@ -1247,14 +1288,14 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate flowThroughOutOfCall(
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
Configuration config
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
NodeEx out, boolean allowsFieldFlow, Configuration config
) {
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
pragma[only_bind_into](config)) and
matchesCall(ccc, call)
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
matchesCall(ccc, call) and
c = ret.getEnclosingCallable()
}
/**
@@ -1262,29 +1303,32 @@ private module MkStage<StageSig PrevStage> {
* configuration `config`.
*
* The call context `cc` records whether the node is reached through an
* argument in a call, and if so, `argAp` records the access path of that
* argument.
* argument in a call, and if so, `summaryCtx` and `argAp` record the
* corresponding parameter position and access path of that argument, respectively.
*/
pragma[nomagic]
additional predicate fwdFlow(
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, Configuration config
) {
fwdFlow0(node, state, cc, argAp, ap, config) and
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
filter(node, state, ap, config)
}
pragma[nomagic]
private predicate fwdFlow0(
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, Configuration config
) {
sourceNode(node, state, config) and
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
summaryCtx = TParameterPositionNone() and
ap = getApNil(node)
or
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
fwdFlow(mid, state0, cc, argAp, ap0, config) and
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
@@ -1295,65 +1339,82 @@ private module MkStage<StageSig PrevStage> {
)
or
exists(NodeEx mid |
fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone()
)
or
exists(NodeEx mid, ApNil nil |
fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and
fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and
additionalJumpStateStep(mid, state0, node, state, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
or
// store
exists(TypedContent tc, Ap ap0 |
fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
ap = apCons(tc, ap0)
)
or
// read
exists(Ap ap0, Content c |
fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
fwdFlowConsCand(ap0, c, ap, config)
)
or
// flow into a callable
exists(ApApprox apa |
fwdFlowIn(_, node, state, _, cc, _, ap, config) and
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
apa = getApprox(ap) and
if PrevStage::parameterMayFlowThrough(node, _, apa, config)
then argAp = apSome(ap)
else argAp = apNone()
if PrevStage::parameterMayFlowThrough(node, apa, config)
then (
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
argAp = apSome(ap)
) else (
summaryCtx = TParameterPositionNone() and argAp = apNone()
)
)
or
// flow out of a callable
fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
exists(
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
DataFlowCallable inner
|
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and
flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and
inner = ret.getEnclosingCallable() and
cc = getCallContextReturn(inner, call, innercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
or
exists(DataFlowCall call, Ap argAp0 |
fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
fwdFlowIsEntered(call, cc, argAp, argAp0, config)
// flow through a callable
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
)
}
pragma[nomagic]
private predicate fwdFlowStore(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
Configuration config
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
fwdFlow(node1, state, cc, argAp, ap1, config) and
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
@@ -1366,7 +1427,7 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
exists(TypedContent tc |
fwdFlowStore(_, tail, tc, _, _, _, _, config) and
fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and
tc.getContent() = c and
cons = apCons(tc, tail)
)
@@ -1374,21 +1435,21 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate fwdFlowRead(
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
Configuration config
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
fwdFlow(node1, state, cc, argAp, ap, config) and
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::readStepCand(node1, c, node2, config) and
getHeadContent(ap) = c
}
pragma[nomagic]
private predicate fwdFlowIn(
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp,
Ap ap, Configuration config
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc,
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
) {
exists(ArgNodeEx arg, boolean allowsFieldFlow |
fwdFlow(arg, state, outercc, argAp, ap, config) and
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
@@ -1396,29 +1457,19 @@ private module MkStage<StageSig PrevStage> {
}
pragma[nomagic]
private predicate fwdFlowOutNotFromArg(
NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
private predicate fwdFlowOutFromArg(
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
Configuration config
) {
exists(
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
DataFlowCallable inner
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
fwdFlow(ret, state, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
inner = ret.getEnclosingCallable() and
ccOut = getCallContextReturn(inner, call, innercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate fwdFlowOutFromArg(
DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config
) {
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
)
}
@@ -1428,11 +1479,13 @@ private module MkStage<StageSig PrevStage> {
*/
pragma[nomagic]
private predicate fwdFlowIsEntered(
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
ParameterPosition pos, Ap ap, Configuration config
) {
exists(ParamNodeEx p |
fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
exists(ParamNodeEx param |
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
pos = param.getPosition()
)
}
@@ -1440,27 +1493,40 @@ private module MkStage<StageSig PrevStage> {
private predicate storeStepFwd(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
) {
fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and
ap2 = apCons(tc, ap1) and
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config)
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config)
}
private predicate readStepFwd(
NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
) {
fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and
fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and
fwdFlowConsCand(ap1, c, ap2, config)
}
pragma[nomagic]
private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
private predicate returnFlowsThrough0(
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
) {
exists(boolean allowsFieldFlow |
fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
pragma[only_bind_into](config))
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate returnFlowsThrough(
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp,
Ap ap, Configuration config
) {
exists(DataFlowCallable c, ParameterPosition ppos |
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
p.isParameterOf(c, ppos) and
parameterFlowThroughAllowed(p, kind)
)
}
@@ -1468,118 +1534,130 @@ private module MkStage<StageSig PrevStage> {
private predicate flowThroughIntoCall(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
}
pragma[nomagic]
private predicate returnNodeMayFlowThrough(
RetNodeEx ret, FlowState state, Ap ap, Configuration config
) {
fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
exists(Ap argAp |
flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
pragma[only_bind_into](config)) and
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
pragma[only_bind_into](config))
)
}
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
*
* The Boolean `toReturn` records whether the node must be returned from the
* enclosing callable in order to reach a sink, and if so, `returnAp` records
* the access path of the returned value.
* The parameter `returnCtx` records whether (and how) the node must be returned
* from the enclosing callable in order to reach a sink, and if so, `returnAp`
* records the access path of the returned value.
*/
pragma[nomagic]
additional predicate revFlow(
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
Configuration config
) {
revFlow0(node, state, toReturn, returnAp, ap, config) and
fwdFlow(node, state, _, _, ap, config)
revFlow0(node, state, returnCtx, returnAp, ap, config) and
fwdFlow(node, state, _, _, _, ap, config)
}
pragma[nomagic]
private predicate revFlow0(
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
Configuration config
) {
fwdFlow(node, state, _, _, ap, config) and
fwdFlow(node, state, _, _, _, ap, config) and
sinkNode(node, state, config) and
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
(
if hasSinkCallCtx(config)
then returnCtx = TReturnCtxNoFlowThrough()
else returnCtx = TReturnCtxNone()
) and
returnAp = apNone() and
ap instanceof ApNil
or
exists(NodeEx mid, FlowState state0 |
localStep(node, state, mid, state0, true, _, config, _) and
revFlow(mid, state0, toReturn, returnAp, ap, config)
revFlow(mid, state0, returnCtx, returnAp, ap, config)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and
revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, state, _, _, ap, config) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone()
)
or
exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone() and
ap instanceof ApNil
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStateStep(node, state, mid, state0, config) and
revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil,
pragma[only_bind_into](config)) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone() and
ap instanceof ApNil
)
or
// store
exists(Ap ap0, Content c |
revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and
revFlowConsCand(ap0, c, ap, config)
)
or
// read
exists(NodeEx mid, Ap ap0 |
revFlow(mid, state, toReturn, returnAp, ap0, config) and
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
readStepFwd(node, ap, _, mid, ap0, config)
)
or
// flow into a callable
revFlowInNotToReturn(node, state, returnAp, ap, config) and
toReturn = false
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
flowIntoCall(_, node, p, allowsFieldFlow, config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
returnCtx = TReturnCtxNone()
)
or
exists(DataFlowCall call, Ap returnAp0 |
revFlowInToReturn(call, node, state, returnAp0, ap, config) and
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
// flow through a callable
exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 |
revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
or
// flow out of a callable
revFlowOut(_, node, state, _, _, ap, config) and
toReturn = true and
if returnNodeMayFlowThrough(node, state, ap, config)
then returnAp = apSome(ap)
else returnAp = apNone()
exists(ReturnKindExt kind |
revFlowOut(_, node, kind, state, _, _, ap, config) and
if returnFlowsThrough(node, kind, state, _, _, _, ap, config)
then (
returnCtx = TReturnCtxMaybeFlowThrough(kind) and
returnAp = apSome(ap)
) else (
returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone()
)
)
}
pragma[nomagic]
private predicate revFlowStore(
Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid,
boolean toReturn, ApOption returnAp, Configuration config
ReturnCtx returnCtx, ApOption returnAp, Configuration config
) {
revFlow(mid, state, toReturn, returnAp, ap0, config) and
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
storeStepFwd(node, ap, tc, mid, ap0, config) and
tc.getContent() = c
}
@@ -1599,35 +1677,27 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate revFlowOut(
DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap,
Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx,
ApOption returnAp, Ap ap, Configuration config
) {
exists(NodeEx out, boolean allowsFieldFlow |
revFlow(out, state, toReturn, returnAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate revFlowInNotToReturn(
ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, false, returnAp, ap, config) and
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
revFlow(out, state, returnCtx, returnAp, ap, config) and
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate revFlowInToReturn(
DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, true, apSome(returnAp), ap, config) and
revFlow(pragma[only_bind_into](p), state,
TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
parameterFlowThroughAllowed(p, kind)
)
}
@@ -1638,11 +1708,12 @@ private module MkStage<StageSig PrevStage> {
*/
pragma[nomagic]
private predicate revFlowIsReturned(
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap,
Configuration config
) {
exists(RetNodeEx ret, FlowState state, CcCall ccc |
revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and
fwdFlow(ret, state, ccc, apSome(_), ap, config) and
revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and
returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and
matchesCall(ccc, call)
)
}
@@ -1713,40 +1784,39 @@ private module MkStage<StageSig PrevStage> {
validAp(ap, config)
}
pragma[noinline]
private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
pragma[nomagic]
private predicate parameterFlowsThroughRev(
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
) {
revFlow(p, _, true, apSome(ap0), ap, config) and
c = p.getEnclosingCallable()
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
parameterFlowThroughAllowed(p, kind)
}
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
parameterFlow(p, ap, ap0, c, config) and
c = ret.getEnclosingCallable() and
revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_),
pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and
fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and
kind = ret.getKind() and
p.getPosition() = pos and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
p.allowParameterReturnInSelf()
)
pragma[nomagic]
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
exists(RetNodeEx ret, ReturnKindExt kind |
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
parameterFlowsThroughRev(p, ap, kind, config)
)
}
pragma[nomagic]
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
exists(ParamNodeEx p, Ap ap |
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
parameterFlowsThroughRev(p, ap, kind, config)
)
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(
Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
revFlow(arg, state, toReturn, returnAp, ap, config) and
revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
revFlow(arg, state, returnCtx, returnAp, ap, config) and
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
}
@@ -1754,14 +1824,13 @@ private module MkStage<StageSig PrevStage> {
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) {
fwd = true and
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and
states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and
tuples =
count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
fwdFlow(n, state, cc, argAp, ap, config)
)
count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config))
or
fwd = false and
nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
@@ -1769,8 +1838,8 @@ private module MkStage<StageSig PrevStage> {
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
states = count(FlowState state | revFlow(_, state, _, _, _, config)) and
tuples =
count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap |
revFlow(n, state, b, retAp, ap, config)
count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap |
revFlow(n, state, returnCtx, retAp, ap, config)
)
}
/* End: Stage logic. */
@@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
exists(lcc)
}
predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
predicate flowIntoCall = flowIntoCallNodeCand1/5;
@@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig {
pragma[nomagic]
private predicate flowOutOfCallNodeCand2(
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
Stage2::revFlowAlias(node1, pragma[only_bind_into](config))
}
@@ -2021,8 +2091,8 @@ private module LocalFlowBigStep {
exists(NodeEx next | Stage2::revFlow(next, state, config) |
jumpStep(node, next, config) or
additionalJumpStep(node, next, config) or
flowIntoCallNodeCand1(_, node, next, config) or
flowOutOfCallNodeCand1(_, node, next, config) or
flowIntoCallNodeCand2(_, node, next, _, config) or
flowOutOfCallNodeCand2(_, node, _, next, _, config) or
Stage2::storeStepCand(node, _, _, next, _, config) or
Stage2::readStepCand(node, _, next, config)
)
@@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
}
predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
predicate flowIntoCall = flowIntoCallNodeCand2/5;
@@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx(
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
) {
exists(AccessPathFront apf |
Stage3::revFlow(node, state, true, _, apf, config) and
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
config)
)
}
@@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
pragma[nomagic]
predicate flowOutOfCall(
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
exists(FlowState state |
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
pragma[only_bind_into](config))
@@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) {
pragma[nomagic]
private predicate nodeMayUseSummary0(
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa,
Configuration config
) {
exists(AccessPathApprox apa0 |
Stage4::parameterMayFlowThrough(_, c, _, _) and
Stage4::revFlow(n, state, true, _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
n.getEnclosingCallable() = c
c = n.getEnclosingCallable() and
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
TAccessPathApproxSome(apa), apa0, config)
)
}
@@ -2522,9 +2595,10 @@ pragma[nomagic]
private predicate nodeMayUseSummary(
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
) {
exists(DataFlowCallable c |
Stage4::parameterMayFlowThrough(_, c, apa, config) and
nodeMayUseSummary0(n, c, state, apa, config)
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
Stage4::parameterMayFlowThrough(p, apa, config) and
nodeMayUseSummary0(n, c, pos, state, apa, config) and
p.isParameterOf(c, pos)
)
}
@@ -2532,7 +2606,7 @@ private newtype TSummaryCtx =
TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
Stage4::revFlow(p, state, _, config)
)
}
@@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough(
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
AccessPathApprox apa, Configuration config
) {
exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos |
exists(PathNodeMid mid, RetNodeEx ret |
pathNode(mid, ret, state, cc, sc, ap, config, _) and
kind = ret.getKind() and
apa = ap.getApprox() and
pos = sc.getParameterPos() and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
sc.getParamNode().allowParameterReturnInSelf()
)
parameterFlowThroughAllowed(sc.getParamNode(), kind)
)
}

View File

@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node source, FlowState state) { none() }
predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes
@@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
}
ParameterPosition getPosition() { this.isParameterOf(_, result) }
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
}
private class RetNodeEx extends NodeEx {
@@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) {
)
}
/**
* Holds if flow from `p` to a return node of kind `kind` is allowed.
*
* We don't expect a parameter to return stored in itself, unless
* explicitly allowed
*/
bindingset[p, kind]
private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) {
exists(ParameterPosition pos | p.isParameterOf(_, pos) |
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
allowParameterReturnInSelfCached(p.asNode())
)
}
/**
* Holds if flow from a parameter at position `pos` inside `c` to a return node of
* kind `kind` is allowed.
*
* We don't expect a parameter to return stored in itself, unless
* explicitly allowed
*/
bindingset[c, pos, kind]
private predicate parameterFlowThroughAllowed(
DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
) {
exists(ParamNodeEx p |
p.isParameterOf(c, pos) and
parameterFlowThroughAllowed(p, kind)
)
}
private module Stage1 implements StageSig {
class Ap = Unit;
@@ -981,21 +1011,22 @@ private module Stage1 implements StageSig {
* candidate for the origin of a summary.
*/
pragma[nomagic]
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
exists(ReturnKindExt kind |
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
exists(DataFlowCallable c, ReturnKindExt kind |
throughFlowNodeCand(p, config) and
returnFlowCallableNodeCand(c, kind, config) and
p.getEnclosingCallable() = c and
exists(ap) and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
or
p.allowParameterReturnInSelf()
)
parameterFlowThroughAllowed(p, kind)
)
}
pragma[nomagic]
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
throughFlowNodeCand(ret, config) and
kind = ret.getKind()
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(ArgNodeEx arg, boolean toReturn |
@@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1(
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config
) {
viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
Stage1::revFlow(ret, config) and
not outBarrier(ret, config) and
not inBarrier(out, config)
exists(ReturnPosition pos |
viableReturnPosOutNodeCand1(call, pos, out, config) and
pos = ret.getReturnPosition() and
kind = pos.getKind() and
Stage1::revFlow(ret, config) and
not outBarrier(ret, config) and
not inBarrier(out, config)
)
}
pragma[nomagic]
@@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1(
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
pragma[nomagic]
private int branch(NodeEx n1, Configuration conf) {
result =
strictcount(NodeEx n |
flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
)
}
@@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) {
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
pragma[nomagic]
private int join(NodeEx n2, Configuration conf) {
result =
strictcount(NodeEx n |
flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
)
}
@@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) {
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
Configuration config
) {
flowOutOfCallNodeCand1(call, ret, out, config) and
flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and
exists(int b, int j |
b = branch(ret, config) and
j = join(out, config) and
b = branch(ret, pragma[only_bind_into](config)) and
j = join(out, pragma[only_bind_into](config)) and
if b.minimum(j) <= config.fieldFlowBranchLimit()
then allowsFieldFlow = true
else allowsFieldFlow = false
@@ -1136,10 +1174,10 @@ pragma[nomagic]
private predicate flowIntoCallNodeCand1(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
flowIntoCallNodeCand1(call, arg, p, config) and
flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and
exists(int b, int j |
b = branch(arg, config) and
j = join(p, config) and
b = branch(arg, pragma[only_bind_into](config)) and
j = join(p, pragma[only_bind_into](config)) and
if b.minimum(j) <= config.fieldFlowBranchLimit()
then allowsFieldFlow = true
else allowsFieldFlow = false
@@ -1156,7 +1194,9 @@ private signature module StageSig {
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
predicate storeStepCand(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
@@ -1222,7 +1262,8 @@ private module MkStage<StageSig PrevStage> {
);
predicate flowOutOfCall(
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
Configuration config
);
predicate flowIntoCall(
@@ -1247,14 +1288,14 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate flowThroughOutOfCall(
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
Configuration config
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
NodeEx out, boolean allowsFieldFlow, Configuration config
) {
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
pragma[only_bind_into](config)) and
matchesCall(ccc, call)
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
matchesCall(ccc, call) and
c = ret.getEnclosingCallable()
}
/**
@@ -1262,29 +1303,32 @@ private module MkStage<StageSig PrevStage> {
* configuration `config`.
*
* The call context `cc` records whether the node is reached through an
* argument in a call, and if so, `argAp` records the access path of that
* argument.
* argument in a call, and if so, `summaryCtx` and `argAp` record the
* corresponding parameter position and access path of that argument, respectively.
*/
pragma[nomagic]
additional predicate fwdFlow(
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, Configuration config
) {
fwdFlow0(node, state, cc, argAp, ap, config) and
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
filter(node, state, ap, config)
}
pragma[nomagic]
private predicate fwdFlow0(
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, Configuration config
) {
sourceNode(node, state, config) and
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
summaryCtx = TParameterPositionNone() and
ap = getApNil(node)
or
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
fwdFlow(mid, state0, cc, argAp, ap0, config) and
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
@@ -1295,65 +1339,82 @@ private module MkStage<StageSig PrevStage> {
)
or
exists(NodeEx mid |
fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone()
)
or
exists(NodeEx mid, ApNil nil |
fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and
fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and
additionalJumpStateStep(mid, state0, node, state, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
or
// store
exists(TypedContent tc, Ap ap0 |
fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
ap = apCons(tc, ap0)
)
or
// read
exists(Ap ap0, Content c |
fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
fwdFlowConsCand(ap0, c, ap, config)
)
or
// flow into a callable
exists(ApApprox apa |
fwdFlowIn(_, node, state, _, cc, _, ap, config) and
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
apa = getApprox(ap) and
if PrevStage::parameterMayFlowThrough(node, _, apa, config)
then argAp = apSome(ap)
else argAp = apNone()
if PrevStage::parameterMayFlowThrough(node, apa, config)
then (
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
argAp = apSome(ap)
) else (
summaryCtx = TParameterPositionNone() and argAp = apNone()
)
)
or
// flow out of a callable
fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
exists(
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
DataFlowCallable inner
|
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and
flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and
inner = ret.getEnclosingCallable() and
cc = getCallContextReturn(inner, call, innercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
or
exists(DataFlowCall call, Ap argAp0 |
fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
fwdFlowIsEntered(call, cc, argAp, argAp0, config)
// flow through a callable
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
)
}
pragma[nomagic]
private predicate fwdFlowStore(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
Configuration config
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
fwdFlow(node1, state, cc, argAp, ap1, config) and
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
@@ -1366,7 +1427,7 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
exists(TypedContent tc |
fwdFlowStore(_, tail, tc, _, _, _, _, config) and
fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and
tc.getContent() = c and
cons = apCons(tc, tail)
)
@@ -1374,21 +1435,21 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate fwdFlowRead(
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
Configuration config
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
fwdFlow(node1, state, cc, argAp, ap, config) and
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::readStepCand(node1, c, node2, config) and
getHeadContent(ap) = c
}
pragma[nomagic]
private predicate fwdFlowIn(
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp,
Ap ap, Configuration config
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc,
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
) {
exists(ArgNodeEx arg, boolean allowsFieldFlow |
fwdFlow(arg, state, outercc, argAp, ap, config) and
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
@@ -1396,29 +1457,19 @@ private module MkStage<StageSig PrevStage> {
}
pragma[nomagic]
private predicate fwdFlowOutNotFromArg(
NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
private predicate fwdFlowOutFromArg(
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
Configuration config
) {
exists(
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
DataFlowCallable inner
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
fwdFlow(ret, state, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
inner = ret.getEnclosingCallable() and
ccOut = getCallContextReturn(inner, call, innercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate fwdFlowOutFromArg(
DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config
) {
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
)
}
@@ -1428,11 +1479,13 @@ private module MkStage<StageSig PrevStage> {
*/
pragma[nomagic]
private predicate fwdFlowIsEntered(
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
ParameterPosition pos, Ap ap, Configuration config
) {
exists(ParamNodeEx p |
fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
exists(ParamNodeEx param |
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
pos = param.getPosition()
)
}
@@ -1440,27 +1493,40 @@ private module MkStage<StageSig PrevStage> {
private predicate storeStepFwd(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
) {
fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and
ap2 = apCons(tc, ap1) and
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config)
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config)
}
private predicate readStepFwd(
NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
) {
fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and
fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and
fwdFlowConsCand(ap1, c, ap2, config)
}
pragma[nomagic]
private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
private predicate returnFlowsThrough0(
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
) {
exists(boolean allowsFieldFlow |
fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
pragma[only_bind_into](config))
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate returnFlowsThrough(
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp,
Ap ap, Configuration config
) {
exists(DataFlowCallable c, ParameterPosition ppos |
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
p.isParameterOf(c, ppos) and
parameterFlowThroughAllowed(p, kind)
)
}
@@ -1468,118 +1534,130 @@ private module MkStage<StageSig PrevStage> {
private predicate flowThroughIntoCall(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
}
pragma[nomagic]
private predicate returnNodeMayFlowThrough(
RetNodeEx ret, FlowState state, Ap ap, Configuration config
) {
fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
exists(Ap argAp |
flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
pragma[only_bind_into](config)) and
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
pragma[only_bind_into](config))
)
}
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
*
* The Boolean `toReturn` records whether the node must be returned from the
* enclosing callable in order to reach a sink, and if so, `returnAp` records
* the access path of the returned value.
* The parameter `returnCtx` records whether (and how) the node must be returned
* from the enclosing callable in order to reach a sink, and if so, `returnAp`
* records the access path of the returned value.
*/
pragma[nomagic]
additional predicate revFlow(
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
Configuration config
) {
revFlow0(node, state, toReturn, returnAp, ap, config) and
fwdFlow(node, state, _, _, ap, config)
revFlow0(node, state, returnCtx, returnAp, ap, config) and
fwdFlow(node, state, _, _, _, ap, config)
}
pragma[nomagic]
private predicate revFlow0(
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
Configuration config
) {
fwdFlow(node, state, _, _, ap, config) and
fwdFlow(node, state, _, _, _, ap, config) and
sinkNode(node, state, config) and
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
(
if hasSinkCallCtx(config)
then returnCtx = TReturnCtxNoFlowThrough()
else returnCtx = TReturnCtxNone()
) and
returnAp = apNone() and
ap instanceof ApNil
or
exists(NodeEx mid, FlowState state0 |
localStep(node, state, mid, state0, true, _, config, _) and
revFlow(mid, state0, toReturn, returnAp, ap, config)
revFlow(mid, state0, returnCtx, returnAp, ap, config)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and
revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, state, _, _, ap, config) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone()
)
or
exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone() and
ap instanceof ApNil
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStateStep(node, state, mid, state0, config) and
revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil,
pragma[only_bind_into](config)) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone() and
ap instanceof ApNil
)
or
// store
exists(Ap ap0, Content c |
revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and
revFlowConsCand(ap0, c, ap, config)
)
or
// read
exists(NodeEx mid, Ap ap0 |
revFlow(mid, state, toReturn, returnAp, ap0, config) and
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
readStepFwd(node, ap, _, mid, ap0, config)
)
or
// flow into a callable
revFlowInNotToReturn(node, state, returnAp, ap, config) and
toReturn = false
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
flowIntoCall(_, node, p, allowsFieldFlow, config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
returnCtx = TReturnCtxNone()
)
or
exists(DataFlowCall call, Ap returnAp0 |
revFlowInToReturn(call, node, state, returnAp0, ap, config) and
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
// flow through a callable
exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 |
revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
or
// flow out of a callable
revFlowOut(_, node, state, _, _, ap, config) and
toReturn = true and
if returnNodeMayFlowThrough(node, state, ap, config)
then returnAp = apSome(ap)
else returnAp = apNone()
exists(ReturnKindExt kind |
revFlowOut(_, node, kind, state, _, _, ap, config) and
if returnFlowsThrough(node, kind, state, _, _, _, ap, config)
then (
returnCtx = TReturnCtxMaybeFlowThrough(kind) and
returnAp = apSome(ap)
) else (
returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone()
)
)
}
pragma[nomagic]
private predicate revFlowStore(
Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid,
boolean toReturn, ApOption returnAp, Configuration config
ReturnCtx returnCtx, ApOption returnAp, Configuration config
) {
revFlow(mid, state, toReturn, returnAp, ap0, config) and
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
storeStepFwd(node, ap, tc, mid, ap0, config) and
tc.getContent() = c
}
@@ -1599,35 +1677,27 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate revFlowOut(
DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap,
Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx,
ApOption returnAp, Ap ap, Configuration config
) {
exists(NodeEx out, boolean allowsFieldFlow |
revFlow(out, state, toReturn, returnAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate revFlowInNotToReturn(
ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, false, returnAp, ap, config) and
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
revFlow(out, state, returnCtx, returnAp, ap, config) and
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate revFlowInToReturn(
DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, true, apSome(returnAp), ap, config) and
revFlow(pragma[only_bind_into](p), state,
TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
parameterFlowThroughAllowed(p, kind)
)
}
@@ -1638,11 +1708,12 @@ private module MkStage<StageSig PrevStage> {
*/
pragma[nomagic]
private predicate revFlowIsReturned(
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap,
Configuration config
) {
exists(RetNodeEx ret, FlowState state, CcCall ccc |
revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and
fwdFlow(ret, state, ccc, apSome(_), ap, config) and
revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and
returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and
matchesCall(ccc, call)
)
}
@@ -1713,40 +1784,39 @@ private module MkStage<StageSig PrevStage> {
validAp(ap, config)
}
pragma[noinline]
private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
pragma[nomagic]
private predicate parameterFlowsThroughRev(
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
) {
revFlow(p, _, true, apSome(ap0), ap, config) and
c = p.getEnclosingCallable()
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
parameterFlowThroughAllowed(p, kind)
}
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
parameterFlow(p, ap, ap0, c, config) and
c = ret.getEnclosingCallable() and
revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_),
pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and
fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and
kind = ret.getKind() and
p.getPosition() = pos and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
p.allowParameterReturnInSelf()
)
pragma[nomagic]
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
exists(RetNodeEx ret, ReturnKindExt kind |
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
parameterFlowsThroughRev(p, ap, kind, config)
)
}
pragma[nomagic]
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
exists(ParamNodeEx p, Ap ap |
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
parameterFlowsThroughRev(p, ap, kind, config)
)
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(
Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
revFlow(arg, state, toReturn, returnAp, ap, config) and
revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
revFlow(arg, state, returnCtx, returnAp, ap, config) and
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
}
@@ -1754,14 +1824,13 @@ private module MkStage<StageSig PrevStage> {
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) {
fwd = true and
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and
states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and
tuples =
count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
fwdFlow(n, state, cc, argAp, ap, config)
)
count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config))
or
fwd = false and
nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
@@ -1769,8 +1838,8 @@ private module MkStage<StageSig PrevStage> {
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
states = count(FlowState state | revFlow(_, state, _, _, _, config)) and
tuples =
count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap |
revFlow(n, state, b, retAp, ap, config)
count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap |
revFlow(n, state, returnCtx, retAp, ap, config)
)
}
/* End: Stage logic. */
@@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
exists(lcc)
}
predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
predicate flowIntoCall = flowIntoCallNodeCand1/5;
@@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig {
pragma[nomagic]
private predicate flowOutOfCallNodeCand2(
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
Stage2::revFlowAlias(node1, pragma[only_bind_into](config))
}
@@ -2021,8 +2091,8 @@ private module LocalFlowBigStep {
exists(NodeEx next | Stage2::revFlow(next, state, config) |
jumpStep(node, next, config) or
additionalJumpStep(node, next, config) or
flowIntoCallNodeCand1(_, node, next, config) or
flowOutOfCallNodeCand1(_, node, next, config) or
flowIntoCallNodeCand2(_, node, next, _, config) or
flowOutOfCallNodeCand2(_, node, _, next, _, config) or
Stage2::storeStepCand(node, _, _, next, _, config) or
Stage2::readStepCand(node, _, next, config)
)
@@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
}
predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
predicate flowIntoCall = flowIntoCallNodeCand2/5;
@@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx(
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
) {
exists(AccessPathFront apf |
Stage3::revFlow(node, state, true, _, apf, config) and
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
config)
)
}
@@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
pragma[nomagic]
predicate flowOutOfCall(
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
exists(FlowState state |
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
pragma[only_bind_into](config))
@@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) {
pragma[nomagic]
private predicate nodeMayUseSummary0(
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa,
Configuration config
) {
exists(AccessPathApprox apa0 |
Stage4::parameterMayFlowThrough(_, c, _, _) and
Stage4::revFlow(n, state, true, _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
n.getEnclosingCallable() = c
c = n.getEnclosingCallable() and
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
TAccessPathApproxSome(apa), apa0, config)
)
}
@@ -2522,9 +2595,10 @@ pragma[nomagic]
private predicate nodeMayUseSummary(
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
) {
exists(DataFlowCallable c |
Stage4::parameterMayFlowThrough(_, c, apa, config) and
nodeMayUseSummary0(n, c, state, apa, config)
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
Stage4::parameterMayFlowThrough(p, apa, config) and
nodeMayUseSummary0(n, c, pos, state, apa, config) and
p.isParameterOf(c, pos)
)
}
@@ -2532,7 +2606,7 @@ private newtype TSummaryCtx =
TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
Stage4::revFlow(p, state, _, config)
)
}
@@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough(
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
AccessPathApprox apa, Configuration config
) {
exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos |
exists(PathNodeMid mid, RetNodeEx ret |
pathNode(mid, ret, state, cc, sc, ap, config, _) and
kind = ret.getKind() and
apa = ap.getApprox() and
pos = sc.getParameterPos() and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
sc.getParamNode().allowParameterReturnInSelf()
)
parameterFlowThroughAllowed(sc.getParamNode(), kind)
)
}

View File

@@ -915,6 +915,17 @@ private module Cached {
TDataFlowCallNone() or
TDataFlowCallSome(DataFlowCall call)
cached
newtype TParameterPositionOption =
TParameterPositionNone() or
TParameterPositionSome(ParameterPosition pos)
cached
newtype TReturnCtx =
TReturnCtxNone() or
TReturnCtxNoFlowThrough() or
TReturnCtxMaybeFlowThrough(ReturnKindExt kind)
cached
newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) }
@@ -1304,6 +1315,44 @@ class DataFlowCallOption extends TDataFlowCallOption {
}
}
/** An optional `ParameterPosition`. */
class ParameterPositionOption extends TParameterPositionOption {
string toString() {
this = TParameterPositionNone() and
result = "(none)"
or
exists(ParameterPosition pos |
this = TParameterPositionSome(pos) and
result = pos.toString()
)
}
}
/**
* A return context used to calculate flow summaries in reverse flow.
*
* The possible values are:
*
* - `TReturnCtxNone()`: no return flow.
* - `TReturnCtxNoFlowThrough()`: return flow, but flow through is not possible.
* - `TReturnCtxMaybeFlowThrough(ReturnKindExt kind)`: return flow, of kind `kind`, and
* flow through may be possible.
*/
class ReturnCtx extends TReturnCtx {
string toString() {
this = TReturnCtxNone() and
result = "(none)"
or
this = TReturnCtxNoFlowThrough() and
result = "(no flow through)"
or
exists(ReturnKindExt kind |
this = TReturnCtxMaybeFlowThrough(kind) and
result = kind.toString()
)
}
}
/** A `Content` tagged with the type of a containing object. */
class TypedContent extends MkTypedContent {
private Content c;

View File

@@ -70,7 +70,7 @@ abstract class Configuration extends string {
/**
* Holds if `sink` is a relevant data flow sink accepting `state`.
*/
predicate isSink(Node source, FlowState state) { none() }
predicate isSink(Node sink, FlowState state) { none() }
/**
* Holds if data flow through `node` is prohibited. This completely removes
@@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
}
ParameterPosition getPosition() { this.isParameterOf(_, result) }
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
}
private class RetNodeEx extends NodeEx {
@@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) {
)
}
/**
* Holds if flow from `p` to a return node of kind `kind` is allowed.
*
* We don't expect a parameter to return stored in itself, unless
* explicitly allowed
*/
bindingset[p, kind]
private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) {
exists(ParameterPosition pos | p.isParameterOf(_, pos) |
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
allowParameterReturnInSelfCached(p.asNode())
)
}
/**
* Holds if flow from a parameter at position `pos` inside `c` to a return node of
* kind `kind` is allowed.
*
* We don't expect a parameter to return stored in itself, unless
* explicitly allowed
*/
bindingset[c, pos, kind]
private predicate parameterFlowThroughAllowed(
DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
) {
exists(ParamNodeEx p |
p.isParameterOf(c, pos) and
parameterFlowThroughAllowed(p, kind)
)
}
private module Stage1 implements StageSig {
class Ap = Unit;
@@ -981,21 +1011,22 @@ private module Stage1 implements StageSig {
* candidate for the origin of a summary.
*/
pragma[nomagic]
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
exists(ReturnKindExt kind |
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
exists(DataFlowCallable c, ReturnKindExt kind |
throughFlowNodeCand(p, config) and
returnFlowCallableNodeCand(c, kind, config) and
p.getEnclosingCallable() = c and
exists(ap) and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
or
p.allowParameterReturnInSelf()
)
parameterFlowThroughAllowed(p, kind)
)
}
pragma[nomagic]
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
throughFlowNodeCand(ret, config) and
kind = ret.getKind()
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(ArgNodeEx arg, boolean toReturn |
@@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1(
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config
) {
viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
Stage1::revFlow(ret, config) and
not outBarrier(ret, config) and
not inBarrier(out, config)
exists(ReturnPosition pos |
viableReturnPosOutNodeCand1(call, pos, out, config) and
pos = ret.getReturnPosition() and
kind = pos.getKind() and
Stage1::revFlow(ret, config) and
not outBarrier(ret, config) and
not inBarrier(out, config)
)
}
pragma[nomagic]
@@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1(
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
pragma[nomagic]
private int branch(NodeEx n1, Configuration conf) {
result =
strictcount(NodeEx n |
flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
)
}
@@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) {
* edge in the graph of paths between sources and sinks that ignores call
* contexts.
*/
pragma[nomagic]
private int join(NodeEx n2, Configuration conf) {
result =
strictcount(NodeEx n |
flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
)
}
@@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) {
*/
pragma[nomagic]
private predicate flowOutOfCallNodeCand1(
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
Configuration config
) {
flowOutOfCallNodeCand1(call, ret, out, config) and
flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and
exists(int b, int j |
b = branch(ret, config) and
j = join(out, config) and
b = branch(ret, pragma[only_bind_into](config)) and
j = join(out, pragma[only_bind_into](config)) and
if b.minimum(j) <= config.fieldFlowBranchLimit()
then allowsFieldFlow = true
else allowsFieldFlow = false
@@ -1136,10 +1174,10 @@ pragma[nomagic]
private predicate flowIntoCallNodeCand1(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
flowIntoCallNodeCand1(call, arg, p, config) and
flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and
exists(int b, int j |
b = branch(arg, config) and
j = join(p, config) and
b = branch(arg, pragma[only_bind_into](config)) and
j = join(p, pragma[only_bind_into](config)) and
if b.minimum(j) <= config.fieldFlowBranchLimit()
then allowsFieldFlow = true
else allowsFieldFlow = false
@@ -1156,7 +1194,9 @@ private signature module StageSig {
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
predicate storeStepCand(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
@@ -1222,7 +1262,8 @@ private module MkStage<StageSig PrevStage> {
);
predicate flowOutOfCall(
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
Configuration config
);
predicate flowIntoCall(
@@ -1247,14 +1288,14 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate flowThroughOutOfCall(
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
Configuration config
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
NodeEx out, boolean allowsFieldFlow, Configuration config
) {
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
pragma[only_bind_into](config)) and
matchesCall(ccc, call)
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
matchesCall(ccc, call) and
c = ret.getEnclosingCallable()
}
/**
@@ -1262,29 +1303,32 @@ private module MkStage<StageSig PrevStage> {
* configuration `config`.
*
* The call context `cc` records whether the node is reached through an
* argument in a call, and if so, `argAp` records the access path of that
* argument.
* argument in a call, and if so, `summaryCtx` and `argAp` record the
* corresponding parameter position and access path of that argument, respectively.
*/
pragma[nomagic]
additional predicate fwdFlow(
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, Configuration config
) {
fwdFlow0(node, state, cc, argAp, ap, config) and
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
filter(node, state, ap, config)
}
pragma[nomagic]
private predicate fwdFlow0(
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap, Configuration config
) {
sourceNode(node, state, config) and
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
argAp = apNone() and
summaryCtx = TParameterPositionNone() and
ap = getApNil(node)
or
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
fwdFlow(mid, state0, cc, argAp, ap0, config) and
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
@@ -1295,65 +1339,82 @@ private module MkStage<StageSig PrevStage> {
)
or
exists(NodeEx mid |
fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
jumpStep(mid, node, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone()
)
or
exists(NodeEx mid, ApNil nil |
fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and
additionalJumpStep(mid, node, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and
fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and
additionalJumpStateStep(mid, state0, node, state, config) and
cc = ccNone() and
summaryCtx = TParameterPositionNone() and
argAp = apNone() and
ap = getApNil(node)
)
or
// store
exists(TypedContent tc, Ap ap0 |
fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
ap = apCons(tc, ap0)
)
or
// read
exists(Ap ap0, Content c |
fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
fwdFlowConsCand(ap0, c, ap, config)
)
or
// flow into a callable
exists(ApApprox apa |
fwdFlowIn(_, node, state, _, cc, _, ap, config) and
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
apa = getApprox(ap) and
if PrevStage::parameterMayFlowThrough(node, _, apa, config)
then argAp = apSome(ap)
else argAp = apNone()
if PrevStage::parameterMayFlowThrough(node, apa, config)
then (
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
argAp = apSome(ap)
) else (
summaryCtx = TParameterPositionNone() and argAp = apNone()
)
)
or
// flow out of a callable
fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
exists(
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
DataFlowCallable inner
|
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and
flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and
inner = ret.getEnclosingCallable() and
cc = getCallContextReturn(inner, call, innercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
or
exists(DataFlowCall call, Ap argAp0 |
fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
fwdFlowIsEntered(call, cc, argAp, argAp0, config)
// flow through a callable
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
)
}
pragma[nomagic]
private predicate fwdFlowStore(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
Configuration config
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
exists(DataFlowType contentType |
fwdFlow(node1, state, cc, argAp, ap1, config) and
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
typecheckStore(ap1, contentType)
)
@@ -1366,7 +1427,7 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
exists(TypedContent tc |
fwdFlowStore(_, tail, tc, _, _, _, _, config) and
fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and
tc.getContent() = c and
cons = apCons(tc, tail)
)
@@ -1374,21 +1435,21 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate fwdFlowRead(
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
Configuration config
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
) {
fwdFlow(node1, state, cc, argAp, ap, config) and
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
PrevStage::readStepCand(node1, c, node2, config) and
getHeadContent(ap) = c
}
pragma[nomagic]
private predicate fwdFlowIn(
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp,
Ap ap, Configuration config
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc,
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
) {
exists(ArgNodeEx arg, boolean allowsFieldFlow |
fwdFlow(arg, state, outercc, argAp, ap, config) and
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
@@ -1396,29 +1457,19 @@ private module MkStage<StageSig PrevStage> {
}
pragma[nomagic]
private predicate fwdFlowOutNotFromArg(
NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
private predicate fwdFlowOutFromArg(
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
Configuration config
) {
exists(
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
DataFlowCallable inner
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
fwdFlow(ret, state, innercc, argAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
inner = ret.getEnclosingCallable() and
ccOut = getCallContextReturn(inner, call, innercc) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate fwdFlowOutFromArg(
DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config
) {
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
)
}
@@ -1428,11 +1479,13 @@ private module MkStage<StageSig PrevStage> {
*/
pragma[nomagic]
private predicate fwdFlowIsEntered(
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
ParameterPosition pos, Ap ap, Configuration config
) {
exists(ParamNodeEx p |
fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
exists(ParamNodeEx param |
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
pos = param.getPosition()
)
}
@@ -1440,27 +1493,40 @@ private module MkStage<StageSig PrevStage> {
private predicate storeStepFwd(
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
) {
fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and
ap2 = apCons(tc, ap1) and
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config)
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config)
}
private predicate readStepFwd(
NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
) {
fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and
fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and
fwdFlowConsCand(ap1, c, ap2, config)
}
pragma[nomagic]
private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
private predicate returnFlowsThrough0(
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
) {
exists(boolean allowsFieldFlow |
fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
pragma[only_bind_into](config)) and
fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
pragma[only_bind_into](config))
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate returnFlowsThrough(
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp,
Ap ap, Configuration config
) {
exists(DataFlowCallable c, ParameterPosition ppos |
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
p.isParameterOf(c, ppos) and
parameterFlowThroughAllowed(p, kind)
)
}
@@ -1468,118 +1534,130 @@ private module MkStage<StageSig PrevStage> {
private predicate flowThroughIntoCall(
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
) {
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
}
pragma[nomagic]
private predicate returnNodeMayFlowThrough(
RetNodeEx ret, FlowState state, Ap ap, Configuration config
) {
fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
exists(Ap argAp |
flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
pragma[only_bind_into](config)) and
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
pragma[only_bind_into](config))
)
}
/**
* Holds if `node` with access path `ap` is part of a path from a source to a
* sink in the configuration `config`.
*
* The Boolean `toReturn` records whether the node must be returned from the
* enclosing callable in order to reach a sink, and if so, `returnAp` records
* the access path of the returned value.
* The parameter `returnCtx` records whether (and how) the node must be returned
* from the enclosing callable in order to reach a sink, and if so, `returnAp`
* records the access path of the returned value.
*/
pragma[nomagic]
additional predicate revFlow(
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
Configuration config
) {
revFlow0(node, state, toReturn, returnAp, ap, config) and
fwdFlow(node, state, _, _, ap, config)
revFlow0(node, state, returnCtx, returnAp, ap, config) and
fwdFlow(node, state, _, _, _, ap, config)
}
pragma[nomagic]
private predicate revFlow0(
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
Configuration config
) {
fwdFlow(node, state, _, _, ap, config) and
fwdFlow(node, state, _, _, _, ap, config) and
sinkNode(node, state, config) and
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
(
if hasSinkCallCtx(config)
then returnCtx = TReturnCtxNoFlowThrough()
else returnCtx = TReturnCtxNone()
) and
returnAp = apNone() and
ap instanceof ApNil
or
exists(NodeEx mid, FlowState state0 |
localStep(node, state, mid, state0, true, _, config, _) and
revFlow(mid, state0, toReturn, returnAp, ap, config)
revFlow(mid, state0, returnCtx, returnAp, ap, config)
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and
revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and
ap instanceof ApNil
)
or
exists(NodeEx mid |
jumpStep(node, mid, config) and
revFlow(mid, state, _, _, ap, config) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone()
)
or
exists(NodeEx mid, ApNil nil |
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStep(node, mid, config) and
revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone() and
ap instanceof ApNil
)
or
exists(NodeEx mid, FlowState state0, ApNil nil |
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
additionalJumpStateStep(node, state, mid, state0, config) and
revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil,
pragma[only_bind_into](config)) and
toReturn = false and
returnCtx = TReturnCtxNone() and
returnAp = apNone() and
ap instanceof ApNil
)
or
// store
exists(Ap ap0, Content c |
revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and
revFlowConsCand(ap0, c, ap, config)
)
or
// read
exists(NodeEx mid, Ap ap0 |
revFlow(mid, state, toReturn, returnAp, ap0, config) and
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
readStepFwd(node, ap, _, mid, ap0, config)
)
or
// flow into a callable
revFlowInNotToReturn(node, state, returnAp, ap, config) and
toReturn = false
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
flowIntoCall(_, node, p, allowsFieldFlow, config) and
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
returnCtx = TReturnCtxNone()
)
or
exists(DataFlowCall call, Ap returnAp0 |
revFlowInToReturn(call, node, state, returnAp0, ap, config) and
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
// flow through a callable
exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 |
revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
or
// flow out of a callable
revFlowOut(_, node, state, _, _, ap, config) and
toReturn = true and
if returnNodeMayFlowThrough(node, state, ap, config)
then returnAp = apSome(ap)
else returnAp = apNone()
exists(ReturnKindExt kind |
revFlowOut(_, node, kind, state, _, _, ap, config) and
if returnFlowsThrough(node, kind, state, _, _, _, ap, config)
then (
returnCtx = TReturnCtxMaybeFlowThrough(kind) and
returnAp = apSome(ap)
) else (
returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone()
)
)
}
pragma[nomagic]
private predicate revFlowStore(
Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid,
boolean toReturn, ApOption returnAp, Configuration config
ReturnCtx returnCtx, ApOption returnAp, Configuration config
) {
revFlow(mid, state, toReturn, returnAp, ap0, config) and
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
storeStepFwd(node, ap, tc, mid, ap0, config) and
tc.getContent() = c
}
@@ -1599,35 +1677,27 @@ private module MkStage<StageSig PrevStage> {
pragma[nomagic]
private predicate revFlowOut(
DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap,
Configuration config
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx,
ApOption returnAp, Ap ap, Configuration config
) {
exists(NodeEx out, boolean allowsFieldFlow |
revFlow(out, state, toReturn, returnAp, ap, config) and
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate revFlowInNotToReturn(
ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, false, returnAp, ap, config) and
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
revFlow(out, state, returnCtx, returnAp, ap, config) and
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
)
}
pragma[nomagic]
private predicate revFlowInToReturn(
DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
Configuration config
) {
exists(ParamNodeEx p, boolean allowsFieldFlow |
revFlow(p, state, true, apSome(returnAp), ap, config) and
revFlow(pragma[only_bind_into](p), state,
TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
if allowsFieldFlow = false then ap instanceof ApNil else any()
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
parameterFlowThroughAllowed(p, kind)
)
}
@@ -1638,11 +1708,12 @@ private module MkStage<StageSig PrevStage> {
*/
pragma[nomagic]
private predicate revFlowIsReturned(
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap,
Configuration config
) {
exists(RetNodeEx ret, FlowState state, CcCall ccc |
revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and
fwdFlow(ret, state, ccc, apSome(_), ap, config) and
revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and
returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and
matchesCall(ccc, call)
)
}
@@ -1713,40 +1784,39 @@ private module MkStage<StageSig PrevStage> {
validAp(ap, config)
}
pragma[noinline]
private predicate parameterFlow(
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
pragma[nomagic]
private predicate parameterFlowsThroughRev(
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
) {
revFlow(p, _, true, apSome(ap0), ap, config) and
c = p.getEnclosingCallable()
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
parameterFlowThroughAllowed(p, kind)
}
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
parameterFlow(p, ap, ap0, c, config) and
c = ret.getEnclosingCallable() and
revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_),
pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and
fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and
kind = ret.getKind() and
p.getPosition() = pos and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
p.allowParameterReturnInSelf()
)
pragma[nomagic]
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
exists(RetNodeEx ret, ReturnKindExt kind |
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
parameterFlowsThroughRev(p, ap, kind, config)
)
}
pragma[nomagic]
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
exists(ParamNodeEx p, Ap ap |
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
parameterFlowsThroughRev(p, ap, kind, config)
)
}
pragma[nomagic]
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
exists(
Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
revFlow(arg, state, toReturn, returnAp, ap, config) and
revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
revFlow(arg, state, returnCtx, returnAp, ap, config) and
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
)
}
@@ -1754,14 +1824,13 @@ private module MkStage<StageSig PrevStage> {
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) {
fwd = true and
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and
states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and
tuples =
count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
fwdFlow(n, state, cc, argAp, ap, config)
)
count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config))
or
fwd = false and
nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
@@ -1769,8 +1838,8 @@ private module MkStage<StageSig PrevStage> {
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
states = count(FlowState state | revFlow(_, state, _, _, _, config)) and
tuples =
count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap |
revFlow(n, state, b, retAp, ap, config)
count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap |
revFlow(n, state, returnCtx, retAp, ap, config)
)
}
/* End: Stage logic. */
@@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
exists(lcc)
}
predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
predicate flowIntoCall = flowIntoCallNodeCand1/5;
@@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig {
pragma[nomagic]
private predicate flowOutOfCallNodeCand2(
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
Stage2::revFlowAlias(node1, pragma[only_bind_into](config))
}
@@ -2021,8 +2091,8 @@ private module LocalFlowBigStep {
exists(NodeEx next | Stage2::revFlow(next, state, config) |
jumpStep(node, next, config) or
additionalJumpStep(node, next, config) or
flowIntoCallNodeCand1(_, node, next, config) or
flowOutOfCallNodeCand1(_, node, next, config) or
flowIntoCallNodeCand2(_, node, next, _, config) or
flowOutOfCallNodeCand2(_, node, _, next, _, config) or
Stage2::storeStepCand(node, _, _, next, _, config) or
Stage2::readStepCand(node, _, next, config)
)
@@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
}
predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
predicate flowIntoCall = flowIntoCallNodeCand2/5;
@@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx(
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
) {
exists(AccessPathFront apf |
Stage3::revFlow(node, state, true, _, apf, config) and
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
config)
)
}
@@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
pragma[nomagic]
predicate flowOutOfCall(
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
exists(FlowState state |
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
pragma[only_bind_into](config))
@@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) {
pragma[nomagic]
private predicate nodeMayUseSummary0(
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa,
Configuration config
) {
exists(AccessPathApprox apa0 |
Stage4::parameterMayFlowThrough(_, c, _, _) and
Stage4::revFlow(n, state, true, _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
n.getEnclosingCallable() = c
c = n.getEnclosingCallable() and
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
TAccessPathApproxSome(apa), apa0, config)
)
}
@@ -2522,9 +2595,10 @@ pragma[nomagic]
private predicate nodeMayUseSummary(
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
) {
exists(DataFlowCallable c |
Stage4::parameterMayFlowThrough(_, c, apa, config) and
nodeMayUseSummary0(n, c, state, apa, config)
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
Stage4::parameterMayFlowThrough(p, apa, config) and
nodeMayUseSummary0(n, c, pos, state, apa, config) and
p.isParameterOf(c, pos)
)
}
@@ -2532,7 +2606,7 @@ private newtype TSummaryCtx =
TSummaryCtxNone() or
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
Stage4::revFlow(p, state, _, config)
)
}
@@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough(
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
AccessPathApprox apa, Configuration config
) {
exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos |
exists(PathNodeMid mid, RetNodeEx ret |
pathNode(mid, ret, state, cc, sc, ap, config, _) and
kind = ret.getKind() and
apa = ap.getApprox() and
pos = sc.getParameterPos() and
// we don't expect a parameter to return stored in itself, unless explicitly allowed
(
not kind.(ParamUpdateReturnKind).getPosition() = pos
or
sc.getParamNode().allowParameterReturnInSelf()
)
parameterFlowThroughAllowed(sc.getParamNode(), kind)
)
}

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* The `Constant Condition` query was extended to catch cases when `String.IsNullOrEmpty` returns a constant value.

View File

@@ -19,6 +19,10 @@
| ConstantIfCondition.cs:11:17:11:29 | ... == ... | Condition always evaluates to 'true'. |
| ConstantIfCondition.cs:14:17:14:21 | false | Condition always evaluates to 'false'. |
| ConstantIfCondition.cs:17:17:17:26 | ... == ... | Condition always evaluates to 'true'. |
| ConstantIsNullOrEmpty.cs:10:21:10:54 | call to method IsNullOrEmpty | Condition always evaluates to 'false'. |
| ConstantIsNullOrEmpty.cs:46:21:46:46 | call to method IsNullOrEmpty | Condition always evaluates to 'true'. |
| ConstantIsNullOrEmpty.cs:50:21:50:44 | call to method IsNullOrEmpty | Condition always evaluates to 'true'. |
| ConstantIsNullOrEmpty.cs:54:21:54:45 | call to method IsNullOrEmpty | Condition always evaluates to 'false'. |
| ConstantNullCoalescingLeftHandOperand.cs:11:24:11:34 | access to constant NULL_OBJECT | Expression is never 'null'. |
| ConstantNullCoalescingLeftHandOperand.cs:12:24:12:27 | null | Expression is always 'null'. |
| ConstantWhileCondition.cs:12:20:12:32 | ... == ... | Condition always evaluates to 'true'. |

View File

@@ -0,0 +1,60 @@
using System.Threading.Tasks;
namespace ConstantIsNullOrEmpty
{
internal class Program
{
static void Main(string[] args)
{
{
if (string.IsNullOrEmpty(nameof(args))) // bad: always false
{
}
string? x = null;
if (string.IsNullOrEmpty(x)) // would be nice... bad: always true
{
}
string y = "";
if (string.IsNullOrEmpty(y)) // would be nice... bad: always true
{
}
if (args[1] != null)
y = "b";
if (string.IsNullOrEmpty(y)) // good: non-constant
{
}
string z = " ";
if (string.IsNullOrEmpty(z)) // would be nice... bad: always false
{
}
string a = "a";
if (string.IsNullOrEmpty(a)) // would be nice... bad: always false
{
}
if (args[1] != null)
a = "";
if (string.IsNullOrEmpty(a)) // good: non-constant
{
}
if (string.IsNullOrEmpty(null)) // bad: always true
{
}
if (string.IsNullOrEmpty("")) // bad: always true
{
}
if (string.IsNullOrEmpty(" ")) // bad: always false
{
}
}
}
}
}

View File

@@ -168,7 +168,9 @@ generate a database, therefore the build method must be available to the CLI.
Detecting the build system
~~~~~~~~~~~~~~~~~~~~~~~~~~
The CodeQL CLI includes autobuilders for C/C++, C#, Go, and Java code. CodeQL
.. include:: ../reusables/kotlin-beta-note.rst
The CodeQL CLI includes autobuilders for C/C++, C#, Go, Java and Kotlin code. CodeQL
autobuilders allow you to build projects for compiled languages without
specifying any build commands. When an autobuilder is invoked, CodeQL examines
the source for evidence of a build system and attempts to run the optimal set of

View File

@@ -30,7 +30,7 @@ You can then use the CodeQL CLI to publish your pack to share with others. For m
Viewing CodeQL packs and their dependencies in Visual Studio Code
-----------------------------------------------------------------
To download a CodeQL pack that someone else has created, run the **CodeQL: Download Packs** command from the Command Palette.
You can download all the core CodeQL query packs, or enter the full name of a specific pack to download. For example, to download the core queries for analyzing Java, enter ``codeql/java-queries``.
You can download all the core CodeQL query packs, or enter the full name of a specific pack to download. For example, to download the core queries for analyzing Java or Kotlin, enter ``codeql/java-queries``.
Whether you have downloaded a CodeQL pack or created your own, you can open the ``qlpack.yml`` file in the root of a CodeQL pack directory in Visual Studio Code and view the dependencies section to see what libraries the pack depends on.

View File

@@ -7,6 +7,10 @@ CodeQL has a large selection of classes for representing the abstract syntax tre
.. include:: ../reusables/abstract-syntax-tree.rst
.. include:: ../reusables/kotlin-beta-note.rst
.. include:: ../reusables/kotlin-java-differences.rst
Statement classes
-----------------

View File

@@ -47,7 +47,7 @@ Example finding unreachable AST nodes
where not exists(node.getAFlowNode())
select node
`See this in the query console on LGTM.com <https://lgtm.com/query/669220024/>`__. The demo projects on LGTM.com all have some code that has no control flow node, and is therefore unreachable. However, since the ``Module`` class is also a subclass of the ``AstNode`` class, the query also finds any modules implemented in C or with no source code. Therefore, it is better to find all unreachable statements.
Many codebases have some code that has no control flow node, and is therefore unreachable. However, since the ``Module`` class is also a subclass of the ``AstNode`` class, the query also finds any modules implemented in C or with no source code. Therefore, it is better to find all unreachable statements.
Example finding unreachable statements
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -60,7 +60,7 @@ Example finding unreachable statements
where not exists(s.getAFlowNode())
select s
`See this in the query console on LGTM.com <https://lgtm.com/query/670720181/>`__. This query gives fewer results, but most of the projects have some unreachable nodes. These are also highlighted by the standard "Unreachable code" query. For more information, see `Unreachable code <https://lgtm.com/rules/3980095>`__ on LGTM.com.
This query should give fewer results. You can also find unreachable code using the standard "Unreachable code" query. For more information, see `Unreachable code <https://codeql.github.com/codeql-query-help/python/py-unreachable-statement/>`__.
The ``BasicBlock`` class
------------------------
@@ -114,7 +114,7 @@ Example finding mutually exclusive blocks within the same function
)
select b1, b2
`See this in the query console on LGTM.com <https://lgtm.com/query/671000028/>`__. This typically gives a very large number of results, because it is a common occurrence in normal control flow. It is, however, an example of the sort of control-flow analysis that is possible. Control-flow analyses such as this are an important aid to data flow analysis. For more information, see ":doc:`Analyzing data flow in Python <analyzing-data-flow-in-python>`."
This typically gives a very large number of results, because it is a common occurrence in normal control flow. It is, however, an example of the sort of control-flow analysis that is possible. Control-flow analyses such as this are an important aid to data flow analysis. For more information, see ":doc:`Analyzing data flow in Python <analyzing-data-flow-in-python>`."
Further reading
---------------

View File

@@ -5,6 +5,10 @@ Analyzing data flow in Java
You can use CodeQL to track the flow of data through a Java program to its use.
.. include:: ../reusables/kotlin-beta-note.rst
.. include:: ../reusables/kotlin-java-differences.rst
About this article
------------------

View File

@@ -97,11 +97,9 @@ Python has builtin functionality for reading and writing files, such as the func
call = API::moduleImport("os").getMember("open").getACall()
select call.getArg(0)
`See this in the query console on LGTM.com <https://lgtm.com/query/8635258505893505141/>`__. Two of the demo projects make use of this low-level API.
Notice the use of the ``API`` module for referring to library functions. For more information, see ":doc:`Using API graphs in Python <using-api-graphs-in-python>`."
Unfortunately this will only give the expression in the argument, not the values which could be passed to it. So we use local data flow to find all expressions that flow into the argument:
Unfortunately this query will only give the expression in the argument, not the values which could be passed to it. So we use local data flow to find all expressions that flow into the argument:
.. code-block:: ql
@@ -115,9 +113,7 @@ Unfortunately this will only give the expression in the argument, not the values
DataFlow::localFlow(expr, call.getArg(0))
select call, expr
`See this in the query console on LGTM.com <https://lgtm.com/query/8213643003890447109/>`__. Many expressions flow to the same call.
We see that we get several data-flow nodes for an expression as it flows towards a call (notice repeated locations in the ``call`` column). We are mostly interested in the "first" of these, what might be called the local source for the file name. To restrict attention to such local sources, and to simultaneously make the analysis more performant, we have the QL class ``LocalSourceNode``. We could demand that ``expr`` is such a node:
Typically, you will see several data-flow nodes for an expression as it flows towards a call (notice repeated locations in the ``call`` column). We are mostly interested in the "first" of these, what might be called the local source for the file name. To restrict attention to such local sources, and to simultaneously make the analysis more performant, we have the QL class ``LocalSourceNode``. We could demand that ``expr`` is such a node:
.. code-block:: ql
@@ -160,9 +156,9 @@ As an alternative, we can ask more directly that ``expr`` is a local source of t
expr = call.getArg(0).getALocalSource()
select call, expr
`See this in the query console on LGTM.com <https://lgtm.com/query/6602079735954016687/>`__. All these three queries give identical results. We now mostly have one expression per call.
These three queries all give identical results. We now mostly have one expression per call.
We still have some cases of more than one expression flowing to a call, but then they flow through different code paths (possibly due to control-flow splitting, as in the second case).
We still have some cases of more than one expression flowing to a call, but then they flow through different code paths (possibly due to control-flow splitting).
We might want to make the source more specific, for example a parameter to a function or method. This query finds instances where a parameter is used as the name when opening a file:
@@ -178,7 +174,7 @@ We might want to make the source more specific, for example a parameter to a fun
DataFlow::localFlow(p, call.getArg(0))
select call, p
`See this in the query console on LGTM.com <https://lgtm.com/query/3998032643497238063/>`__. Very few results now; these could feasibly be inspected manually.
For most codebases, this will return only a few results and these could be inspected manually.
Using the exact name supplied via the parameter may be too strict. If we want to know if the parameter influences the file name, we can use taint tracking instead of data flow. This query finds calls to ``os.open`` where the filename is derived from a parameter:
@@ -194,7 +190,7 @@ Using the exact name supplied via the parameter may be too strict. If we want to
TaintTracking::localTaint(p, call.getArg(0))
select call, p
`See this in the query console on LGTM.com <https://lgtm.com/query/2129957933670836953/>`__. Now we get more results and in more projects.
Typically, this finds more results.
Global data flow
----------------
@@ -369,8 +365,6 @@ This data flow configuration tracks data flow from environment variables to open
select fileOpen, "This call to 'os.open' uses data from $@.",
environment, "call to 'os.getenv'"
`Running this in the query console on LGTM.com <https://lgtm.com/query/6582374907796191895/>`__ unsurprisingly yields no results in the demo projects.
Further reading
---------------

View File

@@ -51,7 +51,7 @@ We could then write this query to find all ``@SuppressWarnings`` annotations att
anntp.hasQualifiedName("java.lang", "SuppressWarnings")
select ann, ann.getValue("value")
`See the full query in the query console on LGTM.com <https://lgtm.com/query/1775658606775222283/>`__. Several of the LGTM.com demo projects use the ``@SuppressWarnings`` annotation. Looking at the ``value``\ s of the annotation element returned by the query, we can see that the *apache/activemq* project uses the ``"rawtypes"`` value described above.
If the codebase you are analyzing uses the ``@SuppressWarnings`` annotation, you can check the ``value``\ s of the annotation element returned by the query. They should use the ``"rawtypes"`` value described above.
As another example, this query finds all annotation types that only have a single annotation element, which has name ``value``:
@@ -66,8 +66,6 @@ As another example, this query finds all annotation types that only have a singl
)
select anntp
`See the full query in the query console on LGTM.com <https://lgtm.com/query/2145264152490258283/>`__.
Example: Finding missing ``@Override`` annotations
--------------------------------------------------
@@ -124,7 +122,7 @@ This makes it very easy to write our query for finding methods that override ano
not overriding.getAnAnnotation() instanceof OverrideAnnotation
select overriding, "Method overrides another method, but does not have an @Override annotation."
`See this in the query console on LGTM.com <https://lgtm.com/query/7419756266089837339/>`__. In practice, this query may yield many results from compiled library code, which aren't very interesting. It's therefore a good idea to add another conjunct ``overriding.fromSource()`` to restrict the result to only report methods for which source code is available.
In practice, this query may yield many results from compiled library code, which aren't very interesting. It's therefore a good idea to add another conjunct ``overriding.fromSource()`` to restrict the result to only report methods for which source code is available.
Example: Finding calls to deprecated methods
--------------------------------------------
@@ -237,7 +235,7 @@ Now we can extend our query to filter out calls in methods carrying a ``Suppress
and not call.getCaller().getAnAnnotation() instanceof SuppressDeprecationWarningAnnotation
select call, "This call invokes a deprecated method."
`See this in the query console on LGTM.com <https://lgtm.com/query/8706367340403790260/>`__. It's fairly common for projects to contain calls to methods that appear to be deprecated.
It's fairly common for projects to contain calls to methods that appear to be deprecated.
Further reading
---------------

View File

@@ -1,18 +1,33 @@
.. _basic-query-for-java-code:
Basic query for Java code
=========================
Basic query for Java and Kotlin code
====================================
Learn to write and run a simple CodeQL query using LGTM.
About the query
---------------
The query we're going to run performs a basic search of the code for ``if`` statements that are redundant, in the sense that they have an empty then branch. For example, code such as:
The query we're going to run searches for inefficient tests for empty strings. For example, Java code such as:
.. code-block:: java
if (error) { }
public class TestJava {
void myJavaFun(String s) {
boolean b = s.equals("");
}
}
or Kotlin code such as:
.. code-block:: kotlin
void myKotlinFun(s: String) {
var b = s.equals("")
}
In either case, replacing ``s.equals("")`` with ``s.isEmpty()``
would be more efficient.
Running the query
-----------------
@@ -37,10 +52,13 @@ Running the query
import java
from IfStmt ifstmt, Block block
where ifstmt.getThen() = block and
block.getNumStmt() = 0
select ifstmt, "This 'if' statement is redundant."
from MethodAccess ma
where
ma.getMethod().hasName("equals") and
ma.getArgument(0).(StringLiteral).getValue() = ""
select ma, "This comparison to empty string is inefficient, use isEmpty() instead."
Note that CodeQL treats Java and Kotlin as part of the same language, so even though this query starts with ``import java``, it will work for both Java and Kotlin code.
LGTM checks whether your query compiles and, if all is well, the **Run** button changes to green to indicate that you can go ahead and run the query.
@@ -57,9 +75,9 @@ Running the query
Your query is always run against the most recently analyzed commit to the selected project.
The query will take a few moments to return results. When the query completes, the results are displayed below the project name. The query results are listed in two columns, corresponding to the two expressions in the ``select`` clause of the query. The first column corresponds to the expression ``ifstmt`` and is linked to the location in the source code of the project where ``ifstmt`` occurs. The second column is the alert message.
The query will take a few moments to return results. When the query completes, the results are displayed below the project name. The query results are listed in two columns, corresponding to the two expressions in the ``select`` clause of the query. The first column corresponds to the expression ``ma`` and is linked to the location in the source code of the project where ``ma`` occurs. The second column is the alert message.
`Example query results <https://lgtm.com/query/3235645104630320782/>`__
`Example query results <https://lgtm.com/query/6863787472564633674/>`__
.. pull-quote::
@@ -67,34 +85,33 @@ Running the query
An ellipsis (…) at the bottom of the table indicates that the entire list is not displayed—click it to show more results.
#. If any matching code is found, click a link in the ``ifstmt`` column to view the ``if`` statement in the code viewer.
#. If any matching code is found, click a link in the ``ma`` column to view the ``.equals`` expression in the code viewer.
The matching ``if`` statement is highlighted with a yellow background in the code viewer. If any code in the file also matches a query from the standard query library for that language, you will see a red alert message at the appropriate point within the code.
The matching ``.equals`` expression is highlighted with a yellow background in the code viewer. If any code in the file also matches a query from the standard query library for that language, you will see a red alert message at the appropriate point within the code.
About the query structure
~~~~~~~~~~~~~~~~~~~~~~~~~
After the initial ``import`` statement, this simple query comprises three parts that serve similar purposes to the FROM, WHERE, and SELECT parts of an SQL query.
+---------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------+
| Query part | Purpose | Details |
+===============================================================+===================================================================================================================+========================================================================================================================+
| ``import java`` | Imports the standard CodeQL libraries for Java. | Every query begins with one or more ``import`` statements. |
+---------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------+
| ``from IfStmt ifstmt, Block block`` | Defines the variables for the query. | We use: |
| | Declarations are of the form: | |
| | ``<type> <variable name>`` | - an ``IfStmt`` variable for ``if`` statements |
| | | - a ``Block`` variable for the then block |
+---------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------+
| ``where ifstmt.getThen() = block and block.getNumStmt() = 0`` | Defines a condition on the variables. | ``ifstmt.getThen() = block`` relates the two variables. The block must be the ``then`` branch of the ``if`` statement. |
| | | |
| | | ``block.getNumStmt() = 0`` states that the block must be empty (that is, it contains no statements). |
+---------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------+
| ``select ifstmt, "This 'if' statement is redundant."`` | Defines what to report for each match. | Reports the resulting ``if`` statement with a string that explains the problem. |
| | | |
| | ``select`` statements for queries that are used to find instances of poor coding practice are always in the form: | |
| | ``select <program element>, "<alert message>"`` | |
+---------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------+
+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------+
| Query part | Purpose | Details |
+==================================================================================================+===================================================================================================================+===================================================================================================+
| ``import java`` | Imports the standard CodeQL libraries for Java and Kotlin. | Every query begins with one or more ``import`` statements. |
+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------+
| ``from MethodAccess ma`` | Defines the variables for the query. | We use: |
| | Declarations are of the form: | |
| | ``<type> <variable name>`` | - a ``MethodAccess`` variable for call expressions |
+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------+
| ``where ma.getMethod().hasName("equals") and ma.getArgument(0).(StringLiteral).getValue() = ""`` | Defines a condition on the variables. | ``ma.getMethod().hasName("equals")`` restricts ``ma`` to only calls to methods call ``equals``. |
| | | |
| | | ``ma.getArgument(0).(StringLiteral).getValue() = ""`` says the argument must be literal ``""``. |
+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------+
| ``select ma, "This comparison to empty string is inefficient, use isEmpty() instead."`` | Defines what to report for each match. | Reports the resulting ``.equals`` expression with a string that explains the problem. |
| | | |
| | ``select`` statements for queries that are used to find instances of poor coding practice are always in the form: | |
| | ``select <program element>, "<alert message>"`` | |
+--------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------+
Extend the query
----------------
@@ -104,41 +121,38 @@ Query writing is an inherently iterative process. You write a simple query and t
Remove false positive results
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Browsing the results of our basic query shows that it could be improved. Among the results you are likely to find examples of ``if`` statements with an ``else`` branch, where an empty ``then`` branch does serve a purpose. For example:
Browsing the results of our basic query shows that it could be improved. For example, you may find results for code like:
.. code-block:: java
if (...) {
...
} else if ("-verbose".equals(option)) {
// nothing to do - handled earlier
} else {
error("unrecognized option");
}
public class TestJava {
void myJavaFun(Object o) {
boolean b = o.equals("");
}
}
In this case, identifying the ``if`` statement with the empty ``then`` branch as redundant is a false positive. One solution to this is to modify the query to ignore empty ``then`` branches if the ``if`` statement has an ``else`` branch.
To exclude ``if`` statements that have an ``else`` branch:
In this case, it is not possible to simply use ``o.isEmpty()`` instead, as ``o`` has type ``Object`` rather than ``String``. One solution to this is to modify the query to only return results where the expression being tested has type ``String``:
#. Extend the where clause to include the following extra condition:
.. code-block:: ql
and not exists(ifstmt.getElse())
ma.getQualifier().getType() instanceof TypeString
The ``where`` clause is now:
.. code-block:: ql
where ifstmt.getThen() = block and
block.getNumStmt() = 0 and
not exists(ifstmt.getElse())
where
ma.getQualifier().getType() instanceof TypeString and
ma.getMethod().hasName("equals") and
ma.getArgument(0).(StringLiteral).getValue() = ""
#. Click **Run**.
There are now fewer results because ``if`` statements with an ``else`` branch are no longer included.
There are now fewer results because ``.equals`` expressions with different types are no longer included.
`See this in the query console <https://lgtm.com/query/6382189874776576029/>`__
`See this in the query console <https://lgtm.com/query/3716567543394265485/>`__
Further reading
---------------

View File

@@ -21,7 +21,7 @@ Experiment and learn how to write effective and efficient queries for CodeQL dat
hash-consing-and-value-numbering
- :doc:`Basic query for C and C++ code <basic-query-for-cpp-code>`: Learn to write and run a simple CodeQL query using LGTM.
- :doc:`Basic query for C and C++ code <basic-query-for-cpp-code>`: Learn to write and run a simple CodeQL query.
- :doc:`CodeQL library for C and C++ <codeql-library-for-cpp>`: When analyzing C or C++ code, you can use the large collection of classes in the CodeQL library for C and C++.

View File

@@ -12,7 +12,7 @@ Experiment and learn how to write effective and efficient queries for CodeQL dat
codeql-library-for-csharp
analyzing-data-flow-in-csharp
- :doc:`Basic query for C# code <basic-query-for-csharp-code>`: Learn to write and run a simple CodeQL query using LGTM.
- :doc:`Basic query for C# code <basic-query-for-csharp-code>`: Learn to write and run a simple CodeQL query.
- :doc:`CodeQL library for C# <codeql-library-for-csharp>`: When you're analyzing a C# program, you can make use of the large collection of classes in the CodeQL library for C#.

View File

@@ -13,7 +13,7 @@ Experiment and learn how to write effective and efficient queries for CodeQL dat
abstract-syntax-tree-classes-for-working-with-go-programs
modeling-data-flow-in-go-libraries
- :doc:`Basic query for Go code <basic-query-for-go-code>`: Learn to write and run a simple CodeQL query using LGTM.
- :doc:`Basic query for Go code <basic-query-for-go-code>`: Learn to write and run a simple CodeQL query.
- :doc:`CodeQL library for Go <codeql-library-for-go>`: When you're analyzing a Go program, you can make use of the large collection of classes in the CodeQL library for Go.

View File

@@ -1,9 +1,16 @@
.. _codeql-for-java:
CodeQL for Java
===============
CodeQL for Java and Kotlin
==========================
Experiment and learn how to write effective and efficient queries for CodeQL databases generated from Java codebases.
Experiment and learn how to write effective and efficient queries for CodeQL databases generated from Java and Kotlin codebases.
.. include:: ../reusables/kotlin-beta-note.rst
.. pull-quote:: Enabling Kotlin support
CodeQL treats Java and Kotlin as parts of the same language, so to enable Kotlin support you should enable ``java`` as a language.
.. toctree::
:hidden:
@@ -19,7 +26,7 @@ Experiment and learn how to write effective and efficient queries for CodeQL dat
working-with-source-locations
abstract-syntax-tree-classes-for-working-with-java-programs
- :doc:`Basic query for Java code <basic-query-for-java-code>`: Learn to write and run a simple CodeQL query using LGTM.
- :doc:`Basic query for Java code <basic-query-for-java-code>`: Learn to write and run a simple CodeQL query.
- :doc:`CodeQL library for Java <codeql-library-for-java>`: When analyzing Java code, you can use the large collection of classes in the CodeQL library for Java.

View File

@@ -18,7 +18,7 @@ Experiment and learn how to write effective and efficient queries for CodeQL dat
abstract-syntax-tree-classes-for-working-with-javascript-and-typescript-programs
data-flow-cheat-sheet-for-javascript
- :doc:`Basic query for JavaScript code <basic-query-for-javascript-code>`: Learn to write and run a simple CodeQL query using LGTM.
- :doc:`Basic query for JavaScript code <basic-query-for-javascript-code>`: Learn to write and run a simple CodeQL query.
- :doc:`CodeQL library for JavaScript <codeql-library-for-javascript>`: When you're analyzing a JavaScript program, you can make use of the large collection of classes in the CodeQL library for JavaScript.

View File

@@ -16,7 +16,7 @@ Experiment and learn how to write effective and efficient queries for CodeQL dat
expressions-and-statements-in-python
analyzing-control-flow-in-python
- :doc:`Basic query for Python code <basic-query-for-python-code>`: Learn to write and run a simple CodeQL query using LGTM.
- :doc:`Basic query for Python code <basic-query-for-python-code>`: Learn to write and run a simple CodeQL query.
- :doc:`CodeQL library for Python <codeql-library-for-python>`: When you need to analyze a Python program, you can make use of the large collection of classes in the CodeQL library for Python.

View File

@@ -14,7 +14,7 @@ Experiment and learn how to write effective and efficient queries for CodeQL dat
analyzing-data-flow-in-ruby
using-api-graphs-in-ruby
- :doc:`Basic query for Ruby code <basic-query-for-ruby-code>`: Learn to write and run a simple CodeQL query using LGTM.
- :doc:`Basic query for Ruby code <basic-query-for-ruby-code>`: Learn to write and run a simple CodeQL query.
- :doc:`CodeQL library for Ruby <codeql-library-for-ruby>`: When you're analyzing a Ruby program, you can make use of the large collection of classes in the CodeQL library for Ruby.

View File

@@ -70,7 +70,7 @@ For example, the following query finds all variables of type ``int`` in the prog
pt.hasName("int")
select v
`See this in the query console on LGTM.com <https://lgtm.com/query/860076406167044435/>`__. You're likely to get many results when you run this query because most projects contain many variables of type ``int``.
You're likely to get many results when you run this query because most projects contain many variables of type ``int``.
Reference types are also categorized according to their declaration scope:
@@ -87,7 +87,7 @@ For instance, this query finds all top-level types whose name is not the same as
where tl.getName() != tl.getCompilationUnit().getName()
select tl
`See this in the query console on LGTM.com <https://lgtm.com/query/4340983612585284460/>`__. This pattern is seen in many projects. When we ran it on the LGTM.com demo projects, most of the projects had at least one instance of this problem in the source code. There were many more instances in the files referenced by the source code.
You will typically see this pattern in the source code of a repository, with many more instances in the files referenced by the source code.
Several more specialized classes are available as well:
@@ -109,7 +109,7 @@ As an example, we can write a query that finds all nested classes that directly
where nc.getASupertype() instanceof TypeObject
select nc
`See this in the query console on LGTM.com <https://lgtm.com/query/8482509736206423238/>`__. You're likely to get many results when you run this query because many projects include nested classes that extend ``Object`` directly.
You're likely to get many results when you run this query because many projects include nested classes that extend ``Object`` directly.
Generics
~~~~~~~~
@@ -143,8 +143,6 @@ For instance, we could use the following query to find all parameterized instanc
pt.getSourceDeclaration() = map
select pt
`See this in the query console on LGTM.com <https://lgtm.com/query/7863873821043873550/>`__. None of the LGTM.com demo projects contain parameterized instances of ``java.util.Map`` in their source code, but they all have results in reference files.
In general, generic types may restrict which types a type parameter can be bound to. For instance, a type of maps from strings to numbers could be declared as follows:
.. code-block:: java
@@ -166,8 +164,6 @@ As an example, the following query finds all type variables with type bound ``Nu
tb.getType().hasQualifiedName("java.lang", "Number")
select tv
`See this in the query console on LGTM.com <https://lgtm.com/query/6740696080876162817/>`__. When we ran it on the LGTM.com demo projects, the *neo4j/neo4j*, *hibernate/hibernate-orm* and *apache/hadoop* projects all contained examples of this pattern.
For dealing with legacy code that is unaware of generics, every generic type has a "raw" version without any type parameters. In the CodeQL libraries, raw types are represented using class ``RawType``, which has the expected subclasses ``RawClass`` and ``RawInterface``. Again, there is a predicate ``getSourceDeclaration`` for obtaining the corresponding generic type. As an example, we can find variables of (raw) type ``Map``:
.. code-block:: ql
@@ -179,8 +175,6 @@ For dealing with legacy code that is unaware of generics, every generic type has
rt.getSourceDeclaration().hasQualifiedName("java.util", "Map")
select v
`See this in the query console on LGTM.com <https://lgtm.com/query/4032913402499547882/>`__. Many projects have variables of raw type ``Map``.
For example, in the following code snippet this query would find ``m1``, but not ``m2``:
.. code-block:: java
@@ -230,7 +224,7 @@ For example, the following query finds all expressions whose parents are ``retur
where e.getParent() instanceof ReturnStmt
select e
`See this in the query console on LGTM.com <https://lgtm.com/query/1947757851560375919/>`__. Many projects have examples of ``return`` statements with child expressions.
Many projects have examples of ``return`` statements with child expressions.
Therefore, if the program contains a return statement ``return x + y;``, this query will return ``x + y``.
@@ -244,7 +238,7 @@ As another example, the following query finds statements whose parent is an ``if
where s.getParent() instanceof IfStmt
select s
`See this in the query console on LGTM.com <https://lgtm.com/query/1989464153689219612/>`__. Many projects have examples of ``if`` statements with child statements.
Many projects have examples of ``if`` statements with child statements.
This query will find both ``then`` branches and ``else`` branches of all ``if`` statements in the program.
@@ -258,8 +252,6 @@ Finally, here is a query that finds method bodies:
where s.getParent() instanceof Method
select s
`See this in the query console on LGTM.com <https://lgtm.com/query/1016821702972128245/>`__. Most projects have many method bodies.
As these examples show, the parent node of an expression is not always an expression: it may also be a statement, for example, an ``IfStmt``. Similarly, the parent node of a statement is not always a statement: it may also be a method or a constructor. To capture this, the QL Java library provides two abstract class ``ExprParent`` and ``StmtParent``, the former representing any node that may be the parent node of an expression, and the latter any node that may be the parent node of a statement.
For more information on working with AST classes, see the :doc:`article on overflow-prone comparisons in Java <overflow-prone-comparisons-in-java>`.
@@ -278,7 +270,7 @@ For annotations, class ``Annotatable`` is a superclass of all program elements t
from Constructor c
select c.getAnAnnotation()
`See this in the query console on LGTM.com <https://lgtm.com/query/3206112561297137365/>`__. The LGTM.com demo projects all use annotations, you can see examples where they are used to suppress warnings and mark code as deprecated.
You may see examples where annotations are used to suppress warnings or to mark code as deprecated.
These annotations are represented by class ``Annotation``. An annotation is simply an expression whose type is an ``AnnotationType``. For example, you can amend this query so that it only reports deprecated constructors:
@@ -292,7 +284,7 @@ These annotations are represented by class ``Annotation``. An annotation is simp
anntp.hasQualifiedName("java.lang", "Deprecated")
select ann
`See this in the query console on LGTM.com <https://lgtm.com/query/5393027107459215059/>`__. Only constructors with the ``@Deprecated`` annotation are reported this time.
Only constructors with the ``@Deprecated`` annotation are reported this time.
For more information on working with annotations, see the :doc:`article on annotations <annotations-in-java>`.
@@ -307,7 +299,7 @@ For Javadoc, class ``Element`` has a member predicate ``getDoc`` that returns a
jdoc = f.getDoc().getJavadoc()
select jdoc
`See this in the query console on LGTM.com <https://lgtm.com/query/6022769142134600659/>`__. You can see this pattern in many projects.
You can see this pattern in many projects.
Class ``Javadoc`` represents an entire Javadoc comment as a tree of ``JavadocElement`` nodes, which can be traversed using member predicates ``getAChild`` and ``getParent``. For instance, you could edit the query so that it finds all ``@author`` tags in Javadoc comments on private fields:
@@ -321,8 +313,6 @@ Class ``Javadoc`` represents an entire Javadoc comment as a tree of ``JavadocEle
at.getParent+() = jdoc
select at
`See this in the query console on LGTM.com <https://lgtm.com/query/2510220694395289111/>`__. None of the LGTM.com demo projects uses the ``@author`` tag on private fields.
.. pull-quote::
Note
@@ -349,7 +339,7 @@ For example, the following query finds methods with a `cyclomatic complexity <ht
mc.getCyclomaticComplexity() > 40
select m
`See this in the query console on LGTM.com <https://lgtm.com/query/6566950741051181919/>`__. Most large projects include some methods with a very high cyclomatic complexity. These methods are likely to be difficult to understand and test.
Most large projects include some methods with a very high cyclomatic complexity. These methods are likely to be difficult to understand and test.
Call graph
----------
@@ -369,8 +359,6 @@ We can use predicate ``Call.getCallee`` to find out which method or constructor
m.hasName("println")
select c
`See this in the query console on LGTM.com <https://lgtm.com/query/5861255162551917595/>`__. The LGTM.com demo projects all include many calls to methods of this name.
Conversely, ``Callable.getAReference`` returns a ``Call`` that refers to it. So we can find methods and constructors that are never called using this query:
.. code-block:: ql
@@ -381,7 +369,7 @@ Conversely, ``Callable.getAReference`` returns a ``Call`` that refers to it. So
where not exists(c.getAReference())
select c
`See this in the query console on LGTM.com <https://lgtm.com/query/7261739919657747703/>`__. The LGTM.com demo projects all appear to have many methods that are not called directly, but this is unlikely to be the whole story. To explore this area further, see ":doc:`Navigating the call graph <navigating-the-call-graph>`."
Codebases often have many methods that are not called directly, but this is unlikely to be the whole story. To explore this area further, see ":doc:`Navigating the call graph <navigating-the-call-graph>`."
For more information about callables and calls, see the :doc:`article on the call graph <navigating-the-call-graph>`.

View File

@@ -43,7 +43,7 @@ Textual level
At its most basic level, a JavaScript code base can simply be viewed as a collection of files organized into folders, where each file is composed of zero or more lines of text.
Note that the textual content of a program is not included in the CodeQL database unless you specifically request it during extraction. In particular, databases on LGTM (also known as "snapshots") do not normally include textual information.
Note that the textual content of a program is not included in the CodeQL database unless you specifically request it during extraction.
Files and folders
^^^^^^^^^^^^^^^^^
@@ -77,7 +77,7 @@ For example, the following query computes, for each folder, the number of JavaSc
from Folder d
select d.getRelativePath(), count(File f | f = d.getAFile() and f.getExtension() = "js")
`See this in the query console on LGTM.com <https://lgtm.com/query/1506075865985/>`__. When you run the query on most projects, the results include folders that contain files with a ``js`` extension and folders that don't.
When you run the query on most projects, the results include folders that contain files with a ``js`` extension and folders that don't.
Locations
^^^^^^^^^
@@ -138,7 +138,7 @@ As an example of a query operating entirely on the lexical level, consider the f
where comma.getNextToken() instanceof CommaToken
select comma, "Omitted array elements are bad style."
`See this in the query console on LGTM.com <https://lgtm.com/query/659662177/>`__. If the query returns no results, this pattern isn't used in the projects that you analyzed.
If the query returns no results, this pattern isn't used in the projects that you analyzed.
You can use predicate ``Locatable.getFirstToken()`` and ``Locatable.getLastToken()`` to access the first and last token (if any) belonging to an element with a source location.
@@ -179,8 +179,6 @@ As an example of a query using only lexical information, consider the following
from HtmlLineComment c
select c, "Do not use HTML comments."
`See this in the query console on LGTM.com <https://lgtm.com/query/686330023/>`__. When we ran this query on the *mozilla/pdf.js* project in LGTM.com, we found three HTML comments.
Syntactic level
~~~~~~~~~~~~~~~
@@ -230,7 +228,7 @@ The `TopLevel <https://codeql.github.com/codeql-standard-libraries/javascript/se
Note
By default, LGTM filters out alerts in minified top-levels, since they are often hard to interpret. When writing your own queries in the LGTM query console, this filtering is *not* done automatically, so you may want to explicitly add a condition of the form ``and not e.getTopLevel().isMinified()`` or similar to your query to exclude results in minified code.
By default, GitHub code scanning filters out alerts in minified top-levels, since they are often hard to interpret. When you write your own queries in Visual Studio Code, this filtering is *not* done automatically, so you may want to explicitly add a condition of the form ``and not e.getTopLevel().isMinified()`` or similar to your query to exclude results in minified code.
Statements and expressions
^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -351,8 +349,6 @@ As an example of how to use expression AST nodes, here is a query that finds exp
where add = shift.getAnOperand()
select add, "This expression should be bracketed to clarify precedence rules."
`See this in the query console on LGTM.com <https://lgtm.com/query/690010024/>`__. When we ran this query on the *meteor/meteor* project in LGTM.com, we found many results where precedence could be clarified using brackets.
Functions
^^^^^^^^^
@@ -373,8 +369,6 @@ As an example, here is a query that finds all expression closures:
where fe.getBody() instanceof Expr
select fe, "Use arrow expressions instead of expression closures."
`See this in the query console on LGTM.com <https://lgtm.com/query/668510056/>`__. None of the LGTM.com demo projects uses expression closures, but you may find this query gets results on other projects.
As another example, this query finds functions that have two parameters that bind the same variable:
.. code-block:: ql
@@ -388,8 +382,6 @@ As another example, this query finds functions that have two parameters that bin
p.getAVariable() = q.getAVariable()
select fun, "This function has two parameters that bind the same variable."
`See this in the query console on LGTM.com <https://lgtm.com/query/673860037/>`__. None of the LGTM.com demo projects has functions where two parameters bind the same variable.
Classes
^^^^^^^
@@ -444,7 +436,7 @@ Here is an example of a query to find declaration statements that declare the sa
not ds.getTopLevel().isMinified()
select ds, "Variable " + v.getName() + " is declared both $@ and $@.", d1, "here", d2, "here"
`See this in the query console on LGTM.com <https://lgtm.com/query/668700496/>`__. This is not a common problem, so you may not find any results in your own projects. The *angular/angular.js* project on LGTM.com has one instance of this problem at the time of writing.
This is not a common problem, so you may not find any results in your own projects.
Notice the use of ``not ... isMinified()`` here and in the next few queries. This excludes any results found in minified code. If you delete ``and not ds.getTopLevel().isMinified()`` and re-run the query, two results in minified code in the *meteor/meteor* project are reported.
@@ -471,8 +463,6 @@ As an example of a query involving properties, consider the following query that
not oe.getTopLevel().isMinified()
select oe, "Property " + p1.getName() + " is defined both $@ and $@.", p1, "here", p2, "here"
`See this in the query console on LGTM.com <https://lgtm.com/query/660700064/>`__. Many projects have a few instances of object expressions with two identically named properties.
Modules
^^^^^^^
@@ -537,7 +527,7 @@ As an example, consider the following query which finds distinct function declar
not g.getTopLevel().isMinified()
select f, g
`See this in the query console on LGTM.com <https://lgtm.com/query/667290067/>`__. Some projects declare conflicting functions of the same name and rely on platform-specific behavior to disambiguate the two declarations.
Some projects declare conflicting functions of the same name and rely on platform-specific behavior to disambiguate the two declarations.
Control flow
~~~~~~~~~~~~
@@ -574,7 +564,7 @@ As an example of an analysis using basic blocks, ``BasicBlock.isLiveAtEntry(v, u
not f.getStartBB().isLiveAtEntry(gv, _)
select f, "This function uses " + gv + " like a local variable."
`See this in the query console on LGTM.com <https://lgtm.com/query/686320048/>`__. Many projects have some variables which look as if they were intended to be local.
Many projects have some variables which look as if they were intended to be local.
Data flow
~~~~~~~~~
@@ -599,8 +589,6 @@ As an example, the following query finds definitions of local variables that are
not exists (VarUse use | def = use.getADef())
select def, "Dead store of local variable."
`See this in the query console on LGTM.com <https://lgtm.com/query/2086440429/>`__. Many projects have some examples of useless assignments to local variables.
SSA
^^^
@@ -642,8 +630,6 @@ For example, here is a query that finds all invocations of a method called ``sen
send.getMethodName() = "send"
select send
`See this in the query console on LGTM.com <https://lgtm.com/query/1506058347056/>`__. The query finds HTTP response sends in the `AMP HTML <https://lgtm.com/projects/g/ampproject/amphtml>`__ project.
Note that the data flow modeling in this library is intraprocedural, that is, flow across function calls and returns is *not* modeled. Likewise, flow through object properties and global variables is not modeled.
Type inference
@@ -707,8 +693,6 @@ As an example of a call-graph-based query, here is a query to find invocations f
not exists(invk.getACallee())
select invk, "Unable to find a callee for this invocation."
`See this in the query console on LGTM.com <https://lgtm.com/query/3260345690335671362/>`__
Inter-procedural data flow
~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -843,7 +827,7 @@ As an example of the use of these classes, here is a query that counts for every
from NodeModule m
select m, count(m.getAnImportedModule())
`See this in the query console on LGTM.com <https://lgtm.com/query/659662207/>`__. When you analyze a project, for each module you can see how many other modules it imports.
When you analyze a project, for each module you can see how many other modules it imports.
NPM
^^^
@@ -872,8 +856,6 @@ As an example of the use of these classes, here is a query that identifies unuse
not exists (Require req | req.getTopLevel() = pkg.getAModule() | name = req.getImportedPath().getValue())
select deps, "Unused dependency '" + name + "'."
`See this in the query console on LGTM.com <https://lgtm.com/query/666680077/>`__. It is not uncommon for projects to have some unused dependencies.
React
^^^^^
@@ -899,8 +881,6 @@ For example, here is a query to find SQL queries that use string concatenation (
where ss instanceof AddExpr
select ss, "Use templating instead of string concatenation."
`See this in the query console on LGTM.com <https://lgtm.com/query/1506076336224/>`__, showing two (benign) results on `strong-arc <https://lgtm.com/projects/g/strongloop/strong-arc/>`__.
Miscellaneous
~~~~~~~~~~~~~
@@ -965,8 +945,6 @@ As an example, here is a query that finds ``@param`` tags that do not specify th
not exists(t.getName())
select t, "@param tag is missing name."
`See this in the query console on LGTM.com <https://lgtm.com/query/673060054/>`__. Of the LGTM.com demo projects analyzed, only *Semantic-Org/Semantic-UI* has an example where the ``@param`` tag omits the name.
For full details on these and other classes representing JSDoc comments and type expressions, see `the API documentation <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/JSDoc.qll/module.JSDoc.html>`__.
JSX

View File

@@ -53,7 +53,7 @@ All scopes are basically a list of statements, although ``Scope`` classes have a
where f.getScope() instanceof Function
select f
`See this in the query console on LGTM.com <https://lgtm.com/query/665620040/>`__. Many projects have nested functions.
Many codebases use nested functions.
Statement
^^^^^^^^^
@@ -95,7 +95,7 @@ As an example, to find expressions of the form ``a+2`` where the left is a simpl
where bin.getLeft() instanceof Name and bin.getRight() instanceof Num
select bin
`See this in the query console on LGTM.com <https://lgtm.com/query/669950026/>`__. Many projects include examples of this pattern.
Many codebases include examples of this pattern.
Variable
^^^^^^^^
@@ -126,7 +126,7 @@ For our first example, we can find all ``finally`` blocks by using the ``Try`` c
from Try t
select t.getFinalbody()
`See this in the query console on LGTM.com <https://lgtm.com/query/659662193/>`__. Many projects include examples of this pattern.
Many codebases include examples of this pattern.
2. Finding ``except`` blocks that do nothing
''''''''''''''''''''''''''''''''''''''''''''
@@ -157,7 +157,7 @@ Both forms are equivalent. Using the positive expression, the whole query looks
where forall(Stmt s | s = ex.getAStmt() | s instanceof Pass)
select ex
`See this in the query console on LGTM.com <https://lgtm.com/query/690010036/>`__. Many projects include pass-only ``except`` blocks.
Many codebases include pass-only ``except`` blocks.
Summary
^^^^^^^
@@ -278,8 +278,6 @@ Using this predicate we can select the longest ``BasicBlock`` by selecting the `
where bb_length(b) = max(bb_length(_))
select b
`See this in the query console on LGTM.com <https://lgtm.com/query/666730036/>`__. When we ran it on the LGTM.com demo projects, the *openstack/nova* and *ytdl-org/youtube-dl* projects both contained source code results for this query.
.. pull-quote::
Note

View File

@@ -165,8 +165,6 @@ Our starting point for the query is pairs of a base class and a derived class, c
where derived.getABaseClass+() = base
select base, derived, "The second class is derived from the first."
`See this in the query console on LGTM.com <https://lgtm.com/query/1505902347211/>`__
Note that the transitive closure symbol ``+`` indicates that ``Class.getABaseClass()`` may be followed one or more times, rather than only accepting a direct base class.
A lot of the results are uninteresting template parameters. You can remove those results by updating the ``where`` clause as follows:
@@ -177,8 +175,6 @@ A lot of the results are uninteresting template parameters. You can remove those
and not exists(base.getATemplateArgument())
and not exists(derived.getATemplateArgument())
`See this in the query console on LGTM.com <https://lgtm.com/query/1505907047251/>`__
Finding derived classes with destructors
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -196,8 +192,6 @@ Now we can extend the query to find derived classes with destructors, using the
and d2 = derived.getDestructor()
select base, derived, "The second class is derived from the first, and both have a destructor."
`See this in the query console on LGTM.com <https://lgtm.com/query/1505901767389/>`__
Notice that getting the destructor implicitly asserts that one exists. As a result, this version of the query returns fewer results than before.
Finding base classes where the destructor is not virtual
@@ -216,11 +210,9 @@ Our last change is to use ``Function.isVirtual()`` to find cases where the base
and not d1.isVirtual()
select d1, "This destructor should probably be virtual."
`See this in the query console on LGTM.com <https://lgtm.com/query/1505908156827/>`__
That completes the query.
There is a similar built-in `query <https://lgtm.com/rules/2158670642/>`__ on LGTM.com that finds classes in a C/C++ project with virtual functions but no virtual destructor. You can take a look at the code for this query by clicking **Open in query console** at the top of that page.
There is a similar standard query `Non-virtual destructor in base class <https://codeql.github.com/codeql-query-help/cpp/cpp-virtual-destructor/>`__ that finds classes in a C/C++ project with virtual functions but no virtual destructor.
Further reading
---------------

View File

@@ -54,8 +54,6 @@ The ``global`` statement in Python declares a variable with a global (module-lev
where g.getScope() instanceof Module
select g
`See this in the query console on LGTM.com <https://lgtm.com/query/686330052/>`__. None of the demo projects on LGTM.com has a global statement that matches this pattern.
The line: ``g.getScope() instanceof Module`` ensures that the ``Scope`` of ``Global g`` is a ``Module``, rather than a class or function.
Example finding 'if' statements with redundant branches
@@ -81,7 +79,7 @@ To find statements like this that could be simplified we can write a query.
and forall(Stmt p | p = l.getAnItem() | p instanceof Pass)
select i
`See this in the query console on LGTM.com <https://lgtm.com/query/672230053/>`__. Many projects have some ``if`` statements that match this pattern.
Many codebases have some ``if`` statements that match this pattern.
The line: ``(l = i.getBody() or l = i.getOrelse())`` restricts the ``StmtList l`` to branches of the ``if`` statement.
@@ -150,8 +148,6 @@ We can check for these using a query.
and cmp.getOp(0) instanceof Is and cmp.getComparator(0) = literal
select cmp
`See this in the query console on LGTM.com <https://lgtm.com/query/688180010/>`__. Two of the demo projects on LGTM.com use this pattern: *saltstack/salt* and *openstack/nova*.
The clause ``cmp.getOp(0) instanceof Is and cmp.getComparator(0) = literal`` checks that the first comparison operator is "is" and that the first comparator is a literal.
.. pull-quote::
@@ -180,7 +176,7 @@ If there are duplicate keys in a Python dictionary, then the second key will ove
and k1 != k2 and same_key(k1, k2)
select k1, "Duplicate key in dict literal"
`See this in the query console on LGTM.com <https://lgtm.com/query/663330305/>`__. When we ran this query on LGTM.com, the source code of the *saltstack/salt* project contained an example of duplicate dictionary keys. The results were also highlighted as alerts by the standard "Duplicate key in dict literal" query. Two of the other demo projects on LGTM.com refer to duplicate dictionary keys in library files. For more information, see `Duplicate key in dict literal <https://lgtm.com/rules/3980087>`__ on LGTM.com.
When we ran this query on some test codebases, we found examples of duplicate dictionary keys. The results were also highlighted as alerts by the standard "Duplicate key in dict literal" query. For more information, see `Duplicate key in dict literal <https://codeql.github.com/codeql-query-help/python/py-duplicate-key-dict-literal/>`__.
The supporting predicate ``same_key`` checks that the keys have the same identifier. Separating this part of the logic into a supporting predicate, instead of directly including it in the query, makes it easier to understand the query as a whole. The casts defined in the predicate restrict the expression to the type specified and allow predicates to be called on the type that is cast-to. For example:
@@ -222,8 +218,6 @@ This basic query can be improved by checking that the one line of code is a Java
and attr.getObject() = self and self.getId() = "self"
select f, "This function is a Java-style getter."
`See this in the query console on LGTM.com <https://lgtm.com/query/669220054/>`__. Of the demo projects on LGTM.com, only the *openstack/nova* project has examples of functions that appear to be Java-style getters.
.. code-block:: ql
ret = f.getStmt(0) and ret.getValue() = attr

View File

@@ -23,8 +23,6 @@ In the following example we find instances of ``AssignExpr`` which assign the co
where e.getRValue().getValue().toInt() = 0
select e, "Assigning the value 0 to something."
`See this in the query console on LGTM.com <https://lgtm.com/query/1505908086530/>`__
The ``where`` clause in this example gets the expression on the right side of the assignment, ``getRValue()``, and compares it with zero. Notice that there are no checks to make sure that the right side of the assignment is an integer or that it has a value (that is, it is compile-time constant, rather than a variable). For expressions where either of these assumptions is wrong, the associated predicate simply does not return anything and the ``where`` clause will not produce a result. You could think of it as if there is an implicit ``exists(e.getRValue().getValue().toInt())`` at the beginning of this line.
It is also worth noting that the query above would find this C code:
@@ -33,7 +31,7 @@ It is also worth noting that the query above would find this C code:
yPtr = NULL;
This is because the database contains a representation of the code base after the preprocessor transforms have run. This means that any macro invocations, such as the ``NULL`` define used here, are expanded during the creation of the database. If you want to write queries about macros then there are some special library classes that have been designed specifically for this purpose (for example, the ``Macro``, ``MacroInvocation`` classes and predicates like ``Element.isInMacroExpansion()``). In this case, it is good that macros are expanded, but we do not want to find assignments to pointers. For more information, see `Database generation <https://lgtm.com/help/lgtm/generate-database>`__ on LGTM.com.
This is because the database contains a representation of the code base after the preprocessor transforms have run. This means that any macro invocations, such as the ``NULL`` define used here, are expanded during the creation of the database. If you want to write queries about macros then there are some special library classes that have been designed specifically for this purpose (for example, the ``Macro``, ``MacroInvocation`` classes and predicates like ``Element.isInMacroExpansion()``). In this case, it is good that macros are expanded, but we do not want to find assignments to pointers. For more information, see `Database creation <https://codeql.github.com/docs/codeql-overview/about-codeql/#database-creation>`__.
Finding assignments of 0 to an integer
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -49,8 +47,6 @@ We can make the query more specific by defining a condition for the left side of
and e.getLValue().getType().getUnspecifiedType() instanceof IntegralType
select e, "Assigning the value 0 to an integer."
`See this in the query console on LGTM.com <https://lgtm.com/query/1505906986578/>`__
This checks that the left side of the assignment has a type that is some kind of integer. Note the call to ``Type.getUnspecifiedType()``. This resolves ``typedef`` types to their underlying types so that the query finds assignments like this one:
.. code-block:: cpp
@@ -109,8 +105,6 @@ Unfortunately this would not quite work, because the loop initialization is actu
and e.getLValue().getType().getUnspecifiedType() instanceof IntegralType
select e, "Assigning the value 0 to an integer, inside a for loop initialization."
`See this in the query console on LGTM.com <https://lgtm.com/query/1505909016965/>`__
Finding assignments of 0 within the loop body
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -127,8 +121,6 @@ We can find assignments inside the loop body using similar code with the predica
and e.getLValue().getType().getUnderlyingType() instanceof IntegralType
select e, "Assigning the value 0 to an integer, inside a for loop body."
`See this in the query console on LGTM.com <https://lgtm.com/query/1505901437190/>`__
Note that we replaced ``e.getEnclosingStmt()`` with ``e.getEnclosingStmt().getParentStmt*()``, to find an assignment expression that is deeply nested inside the loop body. The transitive closure modifier ``*`` here indicates that ``Stmt.getParentStmt()`` may be followed zero or more times, rather than just once, giving us the statement, its parent statement, its parent's parent statement etc.
Further reading

View File

@@ -40,8 +40,6 @@ It might be more interesting to find functions that are not called, using the st
where not exists(FunctionCall fc | fc.getTarget() = f)
select f, "This function is never called."
`See this in the query console on LGTM.com <https://lgtm.com/query/1505891246456/>`__
The new query finds functions that are not the target of any ``FunctionCall``—in other words, functions that are never called. You may be surprised by how many results the query finds. However, if you examine the results, you can see that many of the functions it finds are used indirectly. To create a query that finds only unused functions, we need to refine the query and exclude other ways of using a function.
Excluding functions that are referenced with a function pointer
@@ -58,13 +56,11 @@ You can modify the query to remove functions where a function pointer is used to
and not exists(FunctionAccess fa | fa.getTarget() = f)
select f, "This function is never called, or referenced with a function pointer."
`See this in the query console on LGTM.com <https://lgtm.com/query/1505890446605/>`__
This query returns fewer results. However, if you examine the results then you can probably still find potential refinements.
For example, there is a more complicated LGTM `query <https://lgtm.com/rules/2152580467/>`__ that finds unused static functions. To see the code for this query, click **Open in query console** at the top of the page.
For example, there is a more complicated standard query, `Unused static function <https://codeql.github.com/codeql-query-help/cpp/cpp-unused-static-function/>`__, that finds unused static functions.
You can explore the definition of an element in the standard libraries and see what predicates are available. Use the keyboard **F3** button to open the definition of any element. Alternatively, hover over the element and click **Jump to definition** in the tooltip displayed. The library file is opened in a new tab with the definition highlighted.
You can explore the definition of an element in the standard libraries and see what predicates are available. Right-click the element to display the context menu, and click **Go to Definition**. The library file is opened in a new tab with the definition of the element highlighted.
Finding a specific function
---------------------------
@@ -80,8 +76,6 @@ This query uses ``Function`` and ``FunctionCall`` to find calls to the function
and not fc.getArgument(1) instanceof StringLiteral
select fc, "sprintf called with variable format string."
`See this in the query console on LGTM.com <https://lgtm.com/query/1505889506751/>`__
This uses:
- ``Declaration.getQualifiedName()`` to identify calls to the specific function ``sprintf``.
@@ -89,7 +83,7 @@ This uses:
Note that we could have used ``Declaration.getName()``, but ``Declaration.getQualifiedName()`` is a better choice because it includes the namespace. For example: ``getName()`` would return ``vector`` where ``getQualifiedName`` would return ``std::vector``.
The LGTM version of this query is considerably more complicated, but if you look carefully you will find that its structure is the same. See `Non-constant format string <https://lgtm.com/rules/2152810612/>`__ and click **Open in query console** at the top of the page.
The published version of this query is considerably more complicated, but if you look carefully you will find that its structure is the same. See `Non-constant format string <https://codeql.github.com/codeql-query-help/cpp/cpp-non-constant-format/>`__.
Further reading
---------------

View File

@@ -28,7 +28,7 @@ Using the member predicate ``Function.getName()``, we can list all of the getter
where f.getName().matches("get%")
select f, "This is a function called get..."
`See this in the query console on LGTM.com <https://lgtm.com/query/669220031/>`__. This query typically finds a large number of results. Usually, many of these results are for functions (rather than methods) which we are not interested in.
This query typically finds a large number of results. Usually, many of these results are for functions (rather than methods) which we are not interested in.
Finding all methods called "get..."
-----------------------------------
@@ -43,7 +43,7 @@ You can modify the query above to return more interesting results. As we are onl
where f.getName().matches("get%") and f.isMethod()
select f, "This is a method called get..."
`See this in the query console on LGTM.com <https://lgtm.com/query/690010035/>`__. This finds methods whose name starts with ``"get"``, but many of those are not the sort of simple getters we are interested in.
This finds methods whose name starts with ``"get"``, but many of those are not the sort of simple getters we are interested in.
Finding one line methods called "get..."
----------------------------------------
@@ -59,7 +59,7 @@ We can modify the query further to include only methods whose body consists of a
and count(f.getAStmt()) = 1
select f, "This function is (probably) a getter."
`See this in the query console on LGTM.com <https://lgtm.com/query/667290044/>`__. This query returns fewer results, but if you examine the results you can see that there are still refinements to be made. This is refined further in ":doc:`Expressions and statements in Python <expressions-and-statements-in-python>`."
This query returns fewer results, but if you examine the results you can see that there are still refinements to be made. This is refined further in ":doc:`Expressions and statements in Python <expressions-and-statements-in-python>`."
Finding a call to a specific function
-------------------------------------
@@ -74,8 +74,6 @@ This query uses ``Call`` and ``Name`` to find calls to the function ``eval`` - w
where call.getFunc() = name and name.getId() = "eval"
select call, "call to 'eval'."
`See this in the query console on LGTM.com <https://lgtm.com/query/6718356557331218618/>`__. Some of the demo projects on LGTM.com use this function.
The ``Call`` class represents calls in Python. The ``Call.getFunc()`` predicate gets the expression being called. ``Name.getId()`` gets the identifier (as a string) of the ``Name`` expression.
Due to the dynamic nature of Python, this query will select any call of the form ``eval(...)`` regardless of whether it is a call to the built-in function ``eval`` or not.
In a later tutorial we will see how to use the type-inference library to find calls to the built-in function ``eval`` regardless of name of the variable called.

View File

@@ -149,8 +149,6 @@ Now we can write a query for finding all callables ``c`` and ``@throws`` tags ``
not mayThrow(c, exn)
select tt, "Spurious @throws tag."
`See this in the query console on LGTM.com <https://lgtm.com/query/1258570917227966396/>`__. This finds several results in the LGTM.com demo projects.
Improvements
~~~~~~~~~~~~
@@ -216,7 +214,7 @@ The first case can be covered by changing ``getDocumentedException`` to use the
(result.hasName(tt.getExceptionName()) and visibleIn(tt.getFile(), result))
}
`See this in the query console on LGTM.com <https://lgtm.com/query/8016848987103345329/>`__. This finds many fewer, more interesting results in the LGTM.com demo projects.
This query should find many fewer, more interesting results.
Currently, ``visibleIn`` only considers single-type imports, but you could extend it with support for other kinds of imports.

View File

@@ -44,7 +44,7 @@ We'll start by writing a query that finds less-than expressions (CodeQL class ``
expr.getRightOperand().getType().hasName("long")
select expr
`See this in the query console on LGTM.com <https://lgtm.com/query/490866529746563234/>`__. This query usually finds results on most projects.
This query usually finds results on most codebases.
Notice that we use the predicate ``getType`` (available on all subclasses of ``Expr``) to determine the type of the operands. Types, in turn, define the ``hasName`` predicate, which allows us to identify the primitive types ``int`` and ``long``. As it stands, this query finds *all* less-than expressions comparing ``int`` and ``long``, but in fact we are only interested in comparisons that are part of a loop condition. Also, we want to filter out comparisons where either operand is constant, since these are less likely to be real bugs. The revised query looks like this:
@@ -59,7 +59,7 @@ Notice that we use the predicate ``getType`` (available on all subclasses of ``E
not expr.getAnOperand().isCompileTimeConstant()
select expr
`See this in the query console on LGTM.com <https://lgtm.com/query/4315986481180063825/>`__. Notice that fewer results are found.
Notice that fewer results are found.
The class ``LoopStmt`` is a common superclass of all loops, including, in particular, ``for`` loops as in our example above. While different kinds of loops have different syntax, they all have a loop condition, which can be accessed through predicate ``getCondition``. We use the reflexive transitive closure operator ``*`` applied to the ``getAChildExpr`` predicate to express the requirement that ``expr`` should be nested inside the loop condition. In particular, it can be the loop condition itself.
@@ -113,16 +113,44 @@ Now we rewrite our query to make use of these new classes:
.. code-block:: ql
import Java
import java
// Insert the class definitions from above
// Return the width (in bits) of a given integral type
int width(PrimitiveType pt) {
(pt.hasName("byte") and result=8) or
(pt.hasName("short") and result=16) or
(pt.hasName("char") and result=16) or
(pt.hasName("int") and result=32) or
(pt.hasName("long") and result=64)
}
from OverflowProneComparison expr
where exists(LoopStmt l | l.getCondition().getAChildExpr*() = expr) and
not expr.getAnOperand().isCompileTimeConstant()
select expr
// Find any comparison where the width of the type on the smaller end of
// the comparison is less than the width of the type on the greater end
abstract class OverflowProneComparison extends ComparisonExpr {
Expr getLesserOperand() { none() }
Expr getGreaterOperand() { none() }
}
`See the full query in the query console on LGTM.com <https://lgtm.com/query/506868054626167462/>`__.
// Return `<=` and `<` comparisons
class LTOverflowProneComparison extends OverflowProneComparison {
LTOverflowProneComparison() {
(this instanceof LEExpr or this instanceof LTExpr) and
width(this.getLeftOperand().getType()) < width(this.getRightOperand().getType())
}
}
// Return `>=` and `>` comparisons
class GTOverflowProneComparison extends OverflowProneComparison {
GTOverflowProneComparison() {
(this instanceof GEExpr or this instanceof GTExpr) and
width(this.getRightOperand().getType()) < width(this.getLeftOperand().getType())
}
}
from OverflowProneComparison expr
where exists(LoopStmt l | l.getCondition().getAChildExpr*() = expr) and
not expr.getAnOperand().isCompileTimeConstant()
select expr
Further reading
---------------

View File

@@ -146,8 +146,6 @@ Finally we can simplify the query by using the transitive closure operator. In t
and exists(c.getBlock())
select c, "Constructor does not initialize fields $@.", f, f.getName()
`See this in the query console on LGTM.com <https://lgtm.com/query/1505896968215/>`__
Further reading
---------------

View File

@@ -34,7 +34,7 @@ To determine ancestor types (including immediate super types, and also *their* s
where B.hasName("B")
select B.getASupertype+()
`See this in the query console on LGTM.com <https://lgtm.com/query/1506430738755934285/>`__. If this query were run on the example snippet above, the query would return ``A``, ``I``, and ``java.lang.Object``.
If we ran this query on the example snippet above, the query would return ``A``, ``I``, and ``java.lang.Object``.
.. pull-quote::
@@ -80,7 +80,7 @@ This recipe is not too difficult to translate into a query:
target.getElementType().(RefType).getASupertype+() = source.getElementType()
select ce, "Potentially problematic array downcast."
`See this in the query console on LGTM.com <https://lgtm.com/query/8378564667548381869/>`__. Many projects return results for this query.
Many projects return results for this query.
Note that by casting ``target.getElementType()`` to a ``RefType``, we eliminate all cases where the element type is a primitive type, that is, ``target`` is an array of primitive type: the problem we are looking for cannot arise in that case. Unlike in Java, a cast in QL never fails: if an expression cannot be cast to the desired type, it is simply excluded from the query results, which is exactly what we want.
@@ -143,7 +143,7 @@ Using these new classes we can extend our query to exclude calls to ``toArray``
not ce.getExpr().(CollectionToArrayCall).getActualReturnType() = target
select ce, "Potentially problematic array downcast."
`See this in the query console on LGTM.com <https://lgtm.com/query/3150404889854131463/>`__. Notice that fewer results are found by this improved query.
Notice that fewer results are found by this improved query.
Example: Finding mismatched contains checks
-------------------------------------------
@@ -269,8 +269,6 @@ Now we are ready to write a first version of our query:
not haveCommonDescendant(collEltType, argType)
select juccc, "Element type " + collEltType + " is incompatible with argument type " + argType
`See this in the query console on LGTM.com <https://lgtm.com/query/7947831380785106258/>`__.
Improvements
~~~~~~~~~~~~
@@ -284,19 +282,50 @@ Adding these three improvements, our final query becomes:
.. code-block:: ql
import java
import java
// Insert the class definitions from above
class JavaUtilCollection extends GenericInterface {
JavaUtilCollection() {
this.hasQualifiedName("java.util", "Collection")
}
}
from JavaUtilCollectionContainsCall juccc, Type collEltType, Type argType
where collEltType = juccc.getCollectionElementType() and argType = juccc.getArgumentType() and
not haveCommonDescendant(collEltType, argType) and
not collEltType instanceof TypeVariable and not argType instanceof TypeVariable and
not collEltType = argType.(PrimitiveType).getBoxedType() and
not argType.hasName("<nulltype>")
select juccc, "Element type " + collEltType + " is incompatible with argument type " + argType
class JavaUtilCollectionContains extends Method {
JavaUtilCollectionContains() {
this.getDeclaringType() instanceof JavaUtilCollection and
this.hasStringSignature("contains(Object)")
}
}
`See the full query in the query console on LGTM.com <https://lgtm.com/query/8846334903769538099/>`__.
class JavaUtilCollectionContainsCall extends MethodAccess {
JavaUtilCollectionContainsCall() {
exists(JavaUtilCollectionContains jucc |
this.getMethod().getSourceDeclaration().overrides*(jucc)
)
}
Type getArgumentType() {
result = this.getArgument(0).getType()
}
Type getCollectionElementType() {
exists(RefType D, ParameterizedInterface S |
D = this.getMethod().getDeclaringType() and
D.hasSupertype*(S) and S.getSourceDeclaration() instanceof JavaUtilCollection and
result = S.getTypeArgument(0)
)
}
}
predicate haveCommonDescendant(RefType tp1, RefType tp2) {
exists(RefType commondesc | commondesc.hasSupertype*(tp1) and commondesc.hasSupertype*(tp2))
}
from JavaUtilCollectionContainsCall juccc, Type collEltType, Type argType
where collEltType = juccc.getCollectionElementType() and argType = juccc.getArgumentType() and
not haveCommonDescendant(collEltType, argType) and
not collEltType instanceof TypeVariable and not argType instanceof TypeVariable and
not collEltType = argType.(PrimitiveType).getBoxedType() and
not argType.hasName("<nulltype>")
select juccc, "Element type " + collEltType + " is incompatible with argument type " + argType
Further reading
---------------

View File

@@ -29,8 +29,6 @@ following snippet demonstrates.
select API::moduleImport("re")
`See this in the query console on LGTM.com <https://lgtm.com/query/1876172022264324639/>`__.
This query selects the API graph node corresponding to the ``re`` module. This node represents the fact that the ``re`` module has been imported rather than a specific location in the program where the import happens. Therefore, there will be at most one result per project, and it will not have a useful location, so you'll have to click `Show 1 non-source result` in order to see it.
To find where the ``re`` module is referenced in the program, you can use the ``getAUse`` method. The following query selects all references to the ``re`` module in the current database.
@@ -42,8 +40,6 @@ To find where the ``re`` module is referenced in the program, you can use the ``
select API::moduleImport("re").getAUse()
`See this in the query console on LGTM.com <https://lgtm.com/query/8072356519514905526/>`__.
Note that the ``getAUse`` method accounts for local flow, so that ``my_re_compile``
in the following snippet is
correctly recognized as a reference to the ``re.compile`` function.
@@ -77,8 +73,6 @@ the above ``re.compile`` example, you can now find references to ``re.compile``.
select API::moduleImport("re").getMember("compile").getAUse()
`See this in the query console on LGTM.com <https://lgtm.com/query/7970570434725297676/>`__.
In addition to ``getMember``, you can use the ``getUnknownMember`` method to find references to API
components where the name is not known statically. You can use the ``getAMember`` method to
access all members, both known and unknown.
@@ -97,15 +91,11 @@ where the return value of ``re.compile`` is used:
select API::moduleImport("re").getMember("compile").getReturn().getAUse()
`See this in the query console on LGTM.com <https://lgtm.com/query/4346050399960356921/>`__.
Note that this includes all uses of the result of ``re.compile``, including those reachable via
local flow. To get just the *calls* to ``re.compile``, you can use ``getAnImmediateUse`` instead of
``getAUse``. As this is a common occurrence, you can use ``getACall`` instead of
``getReturn`` followed by ``getAnImmediateUse``.
`See this in the query console on LGTM.com <https://lgtm.com/query/8143347716552092926/>`__.
Note that the API graph does not distinguish between class instantiations and function calls. As far
as it's concerned, both are simply places where an API graph node is called.
@@ -134,8 +124,6 @@ all subclasses of ``View``, you must explicitly include the subclasses of ``Meth
select viewClass().getAUse()
`See this in the query console on LGTM.com <https://lgtm.com/query/288293322319747121/>`__.
Note the use of the set literal ``["View", "MethodView"]`` to match both classes simultaneously.
Built-in functions and classes

View File

@@ -16,7 +16,7 @@ CodeQL library packs (`source <https://github.com/github/codeql/tree/codeql-cli/
and CodeQL bundle (`releases <https://github.com/github/codeql-action/releases>`__)
support the following languages and compilers.
.. include:: ../support/reusables/versions-compilers.rst
.. include:: ../reusables/supported-versions-compilers.rst
Frameworks and libraries
########################
@@ -31,4 +31,4 @@ The current versions of the CodeQL library and query packs (`source <https://git
For example, by extending the data flow libraries to include data sources
and sinks for additional libraries or frameworks.
.. include:: ../support/reusables/frameworks.rst
.. include:: ../reusables/supported-frameworks.rst

View File

@@ -10,7 +10,9 @@ System requirements for running the latest version of CodeQL.
Supported platforms
#######################
.. include:: ../support/reusables/platforms.rst
.. include:: ../reusables/supported-platforms.rst
.. include:: ../reusables/kotlin-beta-note.rst
Additional software requirements
################################

View File

@@ -433,7 +433,6 @@ The ``bindingset`` annotation takes a comma-separated list of variables.
(for characteristic predicates and member predicates) and ``result`` (for predicates that return a result).
For more information, see ":ref:`predicate-binding`."
- When you annotate a class, each variable must be ``this`` or a field in the class.
Binding sets for classes are supported from release 2.3.0 of the CodeQL CLI, and release 1.26 of LGTM Enterprise.
.. Links to use in substitutions

View File

@@ -117,8 +117,6 @@ The values of the contained expressions need to be of :ref:`compatible types <ty
Furthermore, at least one of the set elements has to be of a type that is a supertype of the types of all
the other contained expressions.
Set literals are supported from release 2.1.0 of the CodeQL CLI, and release 1.24 of LGTM Enterprise.
.. index:: super
.. _super:
@@ -341,8 +339,6 @@ The following aggregates are available in QL:
where x in [-5 .. 5] and x != 0
select unique(int y | y = x or y = x.abs() | y)
The ``unique`` aggregate is supported from release 2.1.0 of the CodeQL CLI, and release 1.24 of LGTM Enterprise.
Evaluation of aggregates
========================
@@ -606,6 +602,7 @@ the distance of a node in a graph from the leaves as follows:
.. code-block:: ql
language[monotonicAggregates]
int depth(Node n) {
if not exists(n.getAChild())
then result = 0

View File

@@ -1356,9 +1356,7 @@ Set literals can be of any type, but the types within a set literal have to be c
The values of a set literal expression are all the values of all the contained element expressions.
Set literals are supported from release 2.1.0 of the CodeQL CLI, and release 1.24 of LGTM Enterprise.
Since release 2.7.1 of the CodeQL CLI, and release 1.30 of LGTM Enterprise, a trailing comma is allowed in a set literal.
Since release 2.7.1 of the CodeQL CLI, a trailing comma is allowed in a set literal.
Disambiguation of expressions
-----------------------------

View File

@@ -41,7 +41,7 @@ For this example you should download:
.. note::
Some notes about the project, perhaps a link to the project page on LGTM.
Some notes about the project.
.. Agenda slide. Explaining what is to be covered in the presentation

View File

@@ -3,6 +3,8 @@ CodeQL CWE coverage
You can view the full coverage of MITRE's Common Weakness Enumeration (CWE) or coverage by language for the latest release of CodeQL.
.. include:: ../reusables/kotlin-beta-note.rst
About CWEs
##########

View File

@@ -6,11 +6,13 @@ View the query help for the queries included in the ``code-scanning``, ``securit
- :doc:`CodeQL query help for C and C++ <cpp>`
- :doc:`CodeQL query help for C# <csharp>`
- :doc:`CodeQL query help for Go <go>`
- :doc:`CodeQL query help for Java <java>`
- :doc:`CodeQL query help for Java and Kotlin <java>`
- :doc:`CodeQL query help for JavaScript <javascript>`
- :doc:`CodeQL query help for Python <python>`
- :doc:`CodeQL query help for Ruby <ruby>`
.. include:: ../reusables/kotlin-beta-note.rst
.. pull-quote:: Information
Each query help article includes:

View File

@@ -10,7 +10,7 @@
- ``csharp``
* - Go
- ``go``
* - Java
* - Java/Kotlin
- ``java``
* - JavaScript/TypeScript
- ``javascript``

View File

@@ -0,0 +1,4 @@
.. pull-quote:: Note
CodeQL analysis for Kotlin is currently in beta. During the beta, analysis of Kotlin code,
and the accompanying documentation, will not be as comprehensive as for other languages.

View File

@@ -0,0 +1,20 @@
Writing CodeQL queries for Kotlin versus Java analysis
------------------------------------------------------
Generally you use the same classes to write queries for Kotlin and for Java. You use the same libraries such as DataFlow, TaintTracking, or SSA, and the same classes such as ``MethodAccess`` or ``Class`` for both languages. When you want to access Kotlin-specific elements (such as a ``WhenExpr``) youll need to use Kotlin-specific CodeQL classes.
There are however some important cases where writing queries for Kotlin can produce surprising results compared to writing queries for Java, as CodeQL works with the JVM bytecode representation of the Kotlin source code.
Be careful when you model code elements that dont exist in Java, such as ``NotNullExpr (expr!!)``, because they could interact in unexpected ways with common predicates. For example, ``MethodAccess.getQualifier()`` returns a ``NotNullExpr`` instead of a ``VarAccess`` in the following Kotlin code:
.. code-block:: kotlin
someVar!!.someMethodCall()
In that specific case, you can use the predicate ``Expr.getUnderlyingExpr()``. This goes directly to the underlying ``VarAccess`` to produce a more similar behavior to that in Java.
Nullable elements (``?``) can also produce unexpected behavior. To avoid a ``NullPointerException``, Kotlin may inline calls like ``expr.toString()`` to ``String.valueOf(expr)`` when ``expr`` is nullable. Make sure that you write CodeQL around the extracted code, which may not exactly match the code as written in the codebase.
Another example is that if-else expressions in Kotlin are translated into ``WhenExprs`` in CodeQL, instead of the more typical ``IfStmt`` seen in Java.
In general, you can debug these issues with the AST (you can use the ``CodeQL: View AST`` command from Visual Studio Codes CodeQL extension, or run the ``PrintAst.ql`` query) and see exactly what CodeQL is extracting from your code.

View File

@@ -93,9 +93,11 @@ and the CodeQL library pack ``codeql/go-all`` (`changelog <https://github.com/gi
`yaml <https://gopkg.in/yaml.v3>`_, Serialization
`zap <https://go.uber.org/zap>`_, Logging library
Java built-in support
Java and Kotlin built-in support
==================================
.. include:: ../reusables/kotlin-beta-note.rst
Provided by the current versions of the
CodeQL query pack ``codeql/java-queries`` (`changelog <https://github.com/github/codeql/tree/codeql-cli/latest/java/ql/src/CHANGELOG.md>`__, `source <https://github.com/github/codeql/tree/codeql-cli/latest/java/ql/src>`__)
and the CodeQL library pack ``codeql/java-all`` (`changelog <https://github.com/github/codeql/tree/codeql-cli/latest/java/ql/lib/CHANGELOG.md>`__, `source <https://github.com/github/codeql/tree/codeql-cli/latest/java/ql/lib>`__).

View File

@@ -37,4 +37,4 @@
.. [7] JSX and Flow code, YAML, JSON, HTML, and XML files may also be analyzed with JavaScript files.
.. [8] The extractor requires Python 3 to run. To analyze Python 2.7 you should install both versions of Python.
.. [9] Requires glibc 2.17.
.. [10] TypeScript analysis is performed by running the JavaScript extractor with TypeScript enabled. This is the default for LGTM.
.. [10] TypeScript analysis is performed by running the JavaScript extractor with TypeScript enabled. This is the default.

View File

@@ -1,101 +0,0 @@
# -*- coding: utf-8 -*-
#
# CodeQL analysis support for LGTM Enterprise docs build configuration file.
#
# This file is execfile()d with the current directory set to its
# containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
# For details of all possible config values,
# see https://www.sphinx-doc.org/en/master/usage/configuration.html
##############################################################################
#
# Modified 22032021.
# The configuration values below are specific to the supported languages and frameworks project
# To amend html_theme_options, update version/release number, or add more sphinx extensions,
# refer to code/documentation/ql-documentation/global-sphinx-files/global-conf.py
##############################################################################
# -- Project-specific configuration -----------------------------------
# Set QL as the default language for highlighting code. Set to none to disable
# syntax highlighting. If omitted or left blank, it defaults to Python 3.
highlight_language = 'none'
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# The master toctree document.
master_doc = 'index'
# Project-specific information.
project = u'Supported languages and frameworks for LGTM Enterprise'
# The version info for this project, if different from version and release in main conf.py file.
# The short X.Y version.
# LGTM Enterprise release
release = u'1.30'
# CodeQL CLI version used by LGTM Enterprise release
version = u'2.7.6'
# -- Project-specifc options for HTML output ----------------------------------------------
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
html_title = 'Supported languages and frameworks'
# Output file base name for HTML help builder.
htmlhelp_basename = 'Supported languages and frameworks'
# Add any paths that contain templates here, relative to this directory.
templates_path = ['../_templates']
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['../_static']
html_theme_options = {'font_size': '16px',
'body_text': '#333',
'link': '#2F1695',
'link_hover': '#2F1695',
'show_powered_by': False,
'nosidebar':True,
'head_font_family': '-apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"',
}
html_favicon = '../images/site/favicon.ico'
# -- Currently unused, but potentially useful, configs--------------------------------------
# Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = []
# A shorter title for the navigation bar. Default is the same as html_title.
#html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None
# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}
# Add any extra paths that contain custom files (such as robots.txt or
# .htaccess) here, relative to this directory. These files are copied
# directly to the root of the documentation.
#html_extra_path = []
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['read-me-project.rst', 'reusables/*']

View File

@@ -1,19 +0,0 @@
Frameworks and libraries
########################
LGTM Enterprise |release| includes CodeQL CLI |version|. The CodeQL libraries and queries used by this version of LGTM Enterprise have been explicitly checked against the libraries and frameworks listed below.
.. pull-quote::
Note
For details of framework and library support in the most recent release of the CodeQL CLI, see `Supported languages and frameworks <https://codeql.github.com/docs/codeql-overview/supported-languages-and-frameworks/>`__ in the CodeQL CLI documentation.
.. pull-quote::
Tip
If you're interested in other libraries or frameworks, you can extend the analysis to cover them.
For example, by extending the data flow libraries to include data sources and sinks for additional libraries or frameworks.
.. include:: reusables/frameworks.rst

View File

@@ -1,19 +0,0 @@
Supported languages and frameworks
##################################
These pages describe the languages and frameworks supported in the latest enterprise release of CodeQL and LGTM. (CodeQL was previously known as QL.)
Users of `LGTM.com <https://lgtm.com/>`_ may find that additional features are supported because it's updated more frequently.
For details see:
.. toctree::
language-support.rst
framework-support.rst
For details of the CodeQL libraries, see `CodeQL standard libraries <https://codeql.github.com/codeql-standard-libraries/>`_.
.. toctree::
:hidden:
ql-training

View File

@@ -1,16 +0,0 @@
Languages and compilers
#######################
LGTM Enterprise |release| includes CodeQL CLI |version|. LGTM Enterprise supports analysis of the following languages compiled by the following compilers.
.. pull-quote::
Note
For details of language and compiler support in the most recent release of the CodeQL CLI, see `Supported languages and frameworks <https://codeql.github.com/docs/codeql-overview/supported-languages-and-frameworks/>`__ in the CodeQL CLI documentation.
Note that where there are several versions or dialects of a language, the supported variants are listed.
If your code requires a particular version of a compiler, check that this version is included below.
If you have any questions about language and compiler support, you can find help on the `GitHub Security Lab discussions board <https://github.com/github/securitylab/discussions>`__.
.. include:: reusables/versions-compilers.rst

View File

@@ -1,63 +0,0 @@
CodeQL training and variant analysis examples
=============================================
CodeQL and variant analysis
---------------------------
Variant analysis is the process of using a known vulnerability as a seed to find similar problems in your code. Security engineers typically perform variant analysis to identify possible vulnerabilities and to ensure that these threats are properly fixed across multiple code bases.
CodeQL is the code analysis engine that underpins LGTM, the community driven security analysis platform. Together, CodeQL and LGTM provide continuous monitoring and scalable variant analysis for your projects, even if you dont have your own team of dedicated security engineers. You can read more about using CodeQL and LGTM in variant analysis on the `Security Lab research page <https://securitylab.github.com/research>`__.
CodeQL is easy to learn, and exploring code using CodeQL is the most efficient way to perform variant analysis.
Learning CodeQL for variant analysis
------------------------------------
Start learning how to use CodeQL in variant analysis for a specific language by looking at the topics below. Each topic links to a short presentation on CodeQL, its libraries, or an example variant discovered using CodeQL.
.. |arrow-l| unicode:: U+2190
.. |arrow-r| unicode:: U+2192
.. |info| unicode:: U+24D8
When you have selected a presentation, use |arrow-r| and |arrow-l| to navigate between slides.
Press **p** to view the additional notes on slides that have an information icon |info| in the top right corner, and press **f** to enter full-screen mode.
The presentations contain a number of query examples.
We recommend that you download `CodeQL for Visual Studio Code <https://codeql.github.com/docs/codeql-for-visual-studio-code/>`__ and add the example database for each presentation so that you can find the bugs mentioned in the slides.
.. pull-quote::
Information
The presentations listed below are used in CodeQL and variant analysis training sessions run by GitHub engineers.
Therefore, be aware that the slides are designed to be presented by an instructor.
If you are using the slides without an instructor, please use the additional notes to help guide you through the examples.
CodeQL and variant analysis for C/C++
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- `Introduction to variant analysis: CodeQL for C/C++ </QL/ql-training/cpp/intro-ql-cpp.html>`__an introduction to variant analysis and CodeQL for C/C++ programmers.
- `Example: Bad overflow guard </QL/ql-training/cpp/bad-overflow-guard.html>`__an example of iterative query development to find bad overflow guards in a C++ project.
- `Program representation: CodeQL for C/C++ </QL/ql-training/cpp/program-representation-cpp.html>`__information on how CodeQL analysis represents C/C++ programs.
- `Introduction to local data flow </QL/ql-training/cpp/data-flow-cpp.html>`__an introduction to analyzing local data flow in C/C++ using CodeQL, including an example demonstrating how to develop a query to find a real CVE.
- `Exercise: snprintf overflow </QL/ql-training/cpp/snprintf.html>`__an example demonstrating how to develop a data flow query.
- `Introduction to global data flow </QL/ql-training/cpp/global-data-flow-cpp.html>`__an introduction to analyzing global data flow in C/C++ using CodeQL.
- `Analyzing control flow: CodeQL for C/C++ </QL/ql-training/cpp/control-flow-cpp.html>`__an introduction to analyzing control flow in C/C++ using CodeQL.
CodeQL and variant analysis for Java
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- `Introduction to variant analysis: CodeQL for Java </QL/ql-training/java/intro-ql-java.html>`__an introduction to variant analysis and CodeQL for Java programmers.
- `Example: Query injection </QL/ql-training/java/query-injection-java.html>`__an example of iterative query development to find unsanitized SPARQL injections in a Java project.
- `Program representation: CodeQL for Java </QL/ql-training/java/program-representation-java.html>`__information on how CodeQL analysis represents Java programs.
- `Introduction to local data flow </QL/ql-training/java/data-flow-java.html>`__an introduction to analyzing local data flow in Java using CodeQL, including an example demonstrating how to develop a query to find a real CVE.
- `Exercise: Apache Struts </QL/ql-training/java/apache-struts-java.html>`__an example demonstrating how to develop a data flow query.
- `Introduction to global data flow </QL/ql-training/java/global-data-flow-java.html>`__an introduction to analyzing global data flow in Java using CodeQL.
Further reading
~~~~~~~~~~~~~~~
- `GitHub Security Lab <https://securitylab.github.com/research>`__

View File

@@ -1,15 +0,0 @@
Publishing this project for a new version
#########################################
To update this project for a new version:
1. Check with the language teams that all information in the ``ql/change-notes/support/`` directory is ready.
2. Open the ``global-conf.py`` file in the ``global-sphinx-files`` directory and change the following variables
to the correct value(s) if necessary:
* ``version =``
* ``release = ``
* If it's the first release of the year, ``copyright =``
3. Commit your changes. The output of the ``doc/sphinx`` PR check should be correct for the new version and ready to publish.

View File

@@ -9,7 +9,7 @@ About query metadata
--------------------
Any query that is run as part of an analysis includes a number of properties, known as query metadata. Metadata is included at the top of each query file as the content of a QLDoc comment.
This metadata tells LGTM and the CodeQL :ref:`extension for VS Code <codeql-for-visual-studio-code>` how to handle the query and display its results correctly.
This metadata tells the CodeQL :ref:`extension for VS Code <codeql-for-visual-studio-code>` and the `Code scanning feature in GitHub <https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/about-code-scanning-with-codeql>`__ how to handle the query and display its results correctly.
It also gives other users information about what the query results mean. For more information on query metadata, see the `query metadata style guide <https://github.com/github/codeql/blob/main/docs/query-metadata-style-guide.md>`__ in our `open source repository <https://github.com/github/codeql>`__ on GitHub.
.. pull-quote::
@@ -28,7 +28,7 @@ The following properties are supported by all query files:
+=======================+===========================+=======================================================================================================================================================================================================================================================================================================================================================================+
| ``@description`` | ``<text>`` | A sentence or short paragraph to describe the purpose of the query and *why* the result is useful or important. The description is written in plain text, and uses single quotes (``'``) to enclose code elements. |
+-----------------------+---------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| ``@id`` | ``<text>`` | A sequence of words composed of lowercase letters or digits, delimited by ``/`` or ``-``, identifying and classifying the query. Each query must have a **unique** ID. To ensure this, it may be helpful to use a fixed structure for each ID. For example, the standard LGTM queries have the following format: ``<language>/<brief-description>``. |
| ``@id`` | ``<text>`` | A sequence of words composed of lowercase letters or digits, delimited by ``/`` or ``-``, identifying and classifying the query. Each query must have a **unique** ID. To ensure this, it may be helpful to use a fixed structure for each ID. For example, the standard CodeQL queries have the following format: ``<language>/<brief-description>``. |
+-----------------------+---------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| ``@kind`` | | ``problem`` | Identifies the query is an alert (``@kind problem``) or a path (``@kind path-problem``). For more information on these query types, see ":doc:`About CodeQL queries <about-codeql-queries>`." |
| | | ``path-problem`` | |
@@ -40,12 +40,12 @@ The following properties are supported by all query files:
| | | ``readability`` | |
| | | ``security`` | |
+-----------------------+---------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| ``@precision`` | | ``low`` | Indicates the percentage of query results that are true positives (as opposed to false positive results). This, along with the ``@problem.severity`` property, determines whether the results are displayed by default on LGTM. |
| ``@precision`` | | ``low`` | Indicates the percentage of query results that are true positives (as opposed to false positive results). This, along with the ``@problem.severity`` property, determines how the results are displayed on GitHub. |
| | | ``medium`` | |
| | | ``high`` | |
| | | ``very-high`` | |
+-----------------------+---------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| ``@problem.severity`` | | ``error`` | Defines the level of severity of any alerts generated by a non-security query. This, along with the ``@precision`` property, determines whether the results are displayed by default on LGTM. |
| ``@problem.severity`` | | ``error`` | Defines the level of severity of any alerts generated by a non-security query. This, along with the ``@precision`` property, determines how the results are displayed on GitHub. |
| | | ``warning`` | |
| | | ``recommendation`` | |
+-----------------------+---------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+

View File

@@ -11,7 +11,7 @@ CodeQL includes mechanisms for extracting the location of elements in a codebase
About locations
---------------
When displaying information to the user, LGTM needs to be able to extract location information from the results of a query. In order to do this, all QL classes which can provide location information should do this by using one of the following mechanisms:
When displaying information to the user, applications need to be able to extract location information from the results of a query. In order to do this, all QL classes which can provide location information should do this by using one of the following mechanisms:
- `Providing URLs <#providing-urls>`__
- `Providing location information <#providing-location-information>`__
@@ -49,7 +49,7 @@ A custom URL can be provided by defining a QL predicate returning ``string`` wit
File URLs
^^^^^^^^^
LGTM supports the display of URLs which define a line and column in a source file.
The CodeQL extension for Visual Studio Code and the code scanning views in GitHub support the display of URLs which define a line and column in a source file.
The schema is ``file://``, which is followed by the absolute path to a file, followed by four numbers separated by colons. The numbers denote start line, start column, end line and end column. Both line and column numbers are **1-based**, for example:
@@ -57,12 +57,12 @@ The schema is ``file://``, which is followed by the absolute path to a file, fol
- ``file:///opt/src/my/file.java:1:1:2:1`` denotes the location that starts at the beginning of the file and extends to the first character of the second line (the range is inclusive).
- ``file:///opt/src/my/file.java:1:0:1:0`` is taken, by convention, to denote the entire first line of the file.
By convention, the location of an entire file may also be denoted by a ``file://`` URL without trailing numbers. Optionally, the location within a file can be denoted using three numbers to define the start line number, character offset and character length of the location respectively. Results of these types are not displayed in LGTM.
By convention, the location of an entire file may also be denoted by a ``file://`` URL without trailing numbers. Optionally, the location within a file can be denoted using three numbers to define the start line number, character offset and character length of the location respectively. Results of these types are not displayed as code scanning alerts.
Other types of URL
^^^^^^^^^^^^^^^^^^
The following, less-common types of URL are valid but are not supported by LGTM and will be omitted from any results:
The following, less-common types of URL are valid but are not interpreted as code scanning alerts and will be omitted from any results:
- **HTTP URLs** are supported in some client applications. For an example, see the code snippet above.
- **Folder URLs** can be useful, for example to provide folder-level metrics. They may use a file URL, for example ``file:///opt/src:0:0:0:0``, but they may also start with a scheme of ``folder://``, and no trailing numbers, for example ``folder:///opt/src``.

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