Compare commits

..

556 Commits

Author SHA1 Message Date
Tamás Vajk
5d994c9b08 Revert "C#: Extract relational patterns" 2020-11-27 12:44:07 +01:00
Geoffrey White
a94f244659 Merge pull request #4736 from jbj/downgrade-to-recommendations
C++: Downgrade two queries to recommendation
2020-11-27 10:58:20 +00:00
CodeQL CI
d3cded330e Merge pull request #4693 from RasmusWL/python-add-import-test-shadowing-stdlib-v2
Approved by tausbn
2020-11-27 10:32:21 +00:00
Tamás Vajk
5e75a4109c Merge pull request #4653 from tamasvajk/feature/csharp9-relational-pattern
C#: Extract relational patterns
2020-11-27 11:23:12 +01:00
Rasmus Wriedt Larsen
7b4e890e7b Python: Fix grammar
Co-authored-by: Taus <tausbn@github.com>
2020-11-27 11:00:30 +01:00
Jonas Jensen
8069e7b031 C++: Downgrade two queries to recommendation
The `cpp/local-variable-hides-global-variable` doesn't seem right as a
warning without some additional context. For example, is the local
variable and the global variable used in the same function body, and
do they have similar enough types that it would be possible to confuse
them.

The `cpp/missing-header-guard` query enforces good style and helps with
compilation speed, but AFAIK it has never flagged a correctness issue.
Therefore I think it should be a recommendation.
2020-11-27 10:45:03 +01:00
Tamas Vajk
07c989deb1 C#: Add upgrade folder 2020-11-27 10:21:17 +01:00
Tamas Vajk
5a808190d4 Address review comments 2020-11-27 10:16:57 +01:00
Tamas Vajk
b11fc2f957 C#: Extract relational patterns 2020-11-27 10:16:57 +01:00
Tamás Vajk
c751c516bf Merge pull request #4614 from tamasvajk/feature/csharp9-target-typed
C#: C#9 Add target typed conditional tests
2020-11-27 10:10:58 +01:00
Jonas Jensen
a48235e871 Merge pull request #4730 from MathiasVP/definitionHasPhiNode-join-order-fix
C++: Fix join order in definitionHasPhiNode
2020-11-27 09:54:37 +01:00
Jonas Jensen
edb57c2da0 Merge pull request #4731 from criemen/remove-cpp-abstract
C++: Remove uses of abstract from the standard library.
2020-11-27 09:53:24 +01:00
Tom Hvitved
bc08e47a4e Merge pull request #4664 from hvitved/csharp/cfg/refactor
C#: Refactor CFG implementation
2020-11-27 09:26:57 +01:00
Tamas Vajk
548f276e1f Add more tests 2020-11-27 08:58:37 +01:00
Tamas Vajk
144e9e6271 C#: C#9 Add target typed conditional tests 2020-11-27 08:43:02 +01:00
Jonas Jensen
ad4b2beafa Merge pull request #4727 from criemen/remove-abstract-classes
C++/C#/JS/Python/Java XML.qll: Remove abstract from class hierarchy.
2020-11-27 08:17:21 +01:00
yo-h
301f49a9d9 Merge pull request #4723 from github/yo-h/java-jsp
Java: add support for extraction of SMAP files
2020-11-26 16:09:17 -05:00
Anders Schack-Mulligen
2234d665ce Add manual magic 2020-11-26 13:55:20 -05:00
yo-h
9bb949a8b1 Java: make some SMAP predicates private and add QLDoc 2020-11-26 13:55:19 -05:00
yo-h
c077ca3fc9 Java: add dbscheme upgrade script for SMAP relations 2020-11-26 13:55:19 -05:00
yo-h
f9e78085ac Java: add dbscheme stats for SMAP relations 2020-11-26 13:55:18 -05:00
yo-h
edb41655b4 Java: incorporate SMAP locations into Top.hasLocationInfo 2020-11-26 13:55:17 -05:00
yo-h
e2419e8fed Java: add SMAP relations to dbscheme 2020-11-26 13:55:17 -05:00
Tamás Vajk
27aeb53f1e Merge pull request #4615 from tamasvajk/feature/csharp9-lambda-modifiers
C#: Extract modifiers for lambdas (async, static)
2020-11-26 19:35:51 +01:00
Tamas Vajk
864fce43bd C#: Add upgrade folder 2020-11-26 16:14:38 +01:00
Tamas Vajk
47ca4b0f3b Address review comments 2020-11-26 16:12:19 +01:00
Tamas Vajk
55d47a70f4 C#: Extract modifiers for lambdas (async, static) 2020-11-26 16:12:18 +01:00
Tamás Vajk
252692e5c1 Merge pull request #4616 from tamasvajk/feature/csharp9-attribute-local-function
C#: Allow attributes on local functions
2020-11-26 16:04:00 +01:00
Mathias Vorreiter Pedersen
a4c060a4c6 Merge pull request #4729 from MathiasVP/safe-external-api-function-use-model-interfaces-only
C++: Use model interfaces in DefaultSafeExternalAPIFunction
2020-11-26 16:00:19 +01:00
Tom Hvitved
cb91dc1308 C#: Rank StandardStmt::getChildElement() 2020-11-26 15:13:57 +01:00
Cornelius Riemenschneider
f6c3c2bdcc C++: Auto-format Type.qll. 2020-11-26 11:45:49 +01:00
Mathias Vorreiter Pedersen
b02ac7f523 C++: Use SideEffectFunction (instead of ArrayFunction) to define DefaultSafeExternalAPIFunction. 2020-11-26 11:02:47 +01:00
Cornelius Riemenschneider
5d80417854 Update cpp/ql/src/semmle/code/cpp/Type.qll
Co-authored-by: Mathias Vorreiter Pedersen <mathiasvp@github.com>
2020-11-26 10:39:17 +01:00
Tamas Vajk
18a757445d Add DB upgrade folder 2020-11-26 10:37:28 +01:00
Tamas Vajk
a931c59a28 C#: Allow attributes on local functions 2020-11-26 10:24:12 +01:00
Tamás Vajk
89a4cff5f8 Merge pull request #4662 from tamasvajk/feature/csharp9-type-pattern
C#: Extract type patterns
2020-11-26 10:18:55 +01:00
Cornelius Riemenschneider
ba32459adf C++: Remove uses of abstract from the standard library. 2020-11-26 10:17:40 +01:00
Mathias Vorreiter Pedersen
5f4ad3ad7d C++: Fix join order in definitionHasPhiNode 2020-11-26 10:07:44 +01:00
Tamas Vajk
3bd6807681 C#: Extract type patterns 2020-11-26 08:54:40 +01:00
Anders Schack-Mulligen
f70072a2db Merge pull request #3454 from porcupineyhairs/javaSSRf
Java : add request forgery query
2020-11-26 08:52:15 +01:00
Tamás Vajk
d29a6ec4c6 Merge pull request #4654 from tamasvajk/feature/csharp9-parens-pattern
C#: Handle parenthesized pattern extraction
2020-11-26 08:46:38 +01:00
Jonas Jensen
6fecc38004 Merge pull request #4726 from MathiasVP/revert-4558
C++: Revert 4558
2020-11-26 08:44:32 +01:00
Tamas Vajk
9b8d9771f8 C#: Handle parenthesized pattern extraction 2020-11-25 21:27:34 +01:00
Mathias Vorreiter Pedersen
c595baf1e3 C++: Remove INTERNAL from qldoc now that the Pure model implementations are private. 2020-11-25 21:07:47 +01:00
Geoffrey White
bc71c72084 Merge pull request #4728 from criemen/delete-magicdraw
C++: Remove MagicDraw.qll
2020-11-25 17:56:37 +00:00
Mathias Vorreiter Pedersen
7730f5dfcf C++: Use model interfaces in SafeExternalAPIFunction and make the three previosuly-used implementation models private. 2020-11-25 18:20:55 +01:00
Cornelius Riemenschneider
3bfb398516 Autoformat XML.qll. 2020-11-25 18:20:50 +01:00
Cornelius Riemenschneider
acf6c218bc C++: Remove MagicDraw.qll 2020-11-25 17:36:30 +01:00
Tamás Vajk
65dbb6e45f Merge pull request #4612 from tamasvajk/feature/csharp9-local-function-without-body
C#: C#9 Extract local functions without body
2020-11-25 17:28:03 +01:00
Cornelius Riemenschneider
7eec988fb5 XML.qll: Remove abstract from class hierarchy. 2020-11-25 17:22:03 +01:00
Jonas Jensen
dcc048139e Merge pull request #4717 from criemen/escapetree-temp-objects
C++: Improve EscapesTree.qll analysis in the presence of temporary objects
2020-11-25 17:11:44 +01:00
Cornelius Riemenschneider
0b8403fc05 C++: Add one more test. 2020-11-25 16:24:55 +01:00
Cornelius Riemenschneider
b4e45ad6cb C++: Address review. 2020-11-25 16:24:25 +01:00
Mathias Vorreiter Pedersen
4c3a26fea8 Revert "Merge pull request #4558 from rdmarsh2/rdmarsh2/cpp/remove-initialize-nonlocal"
This reverts commit 08efd7fbd9, reversing
changes made to cb8c5e8cca.
2020-11-25 15:51:52 +01:00
Tamas Vajk
19883302af C#: Extract local functions without body 2020-11-25 14:42:40 +01:00
Tamás Vajk
aa45920f31 Merge pull request #4613 from tamasvajk/feature/csharp9-multiple-discards
C#: C#9 add test cases for discards as lambda/delegate parameters
2020-11-25 14:33:55 +01:00
CodeQL CI
34ffcb5677 Merge pull request #4593 from asgerf/js/react-hot
Approved by erik-krogh
2020-11-25 12:01:38 +00:00
Tamas Vajk
6d9510591a C#: Adjust test for nint/nuint 2020-11-25 12:01:56 +01:00
Tamas Vajk
81b03bf876 C#: Add test cases for discards as lambda/delegate parameters 2020-11-25 11:56:25 +01:00
Tom Hvitved
7791ec3084 Merge pull request #4618 from hvitved/csharp/get-source-decl-rename
C#: Rename `getSourceDeclaration()` to `getUnboundDeclaration()`
2020-11-25 11:05:18 +01:00
Cornelius Riemenschneider
7f13d4c356 C++: Improve EscapesTree analysis in the presence of temporary objects. 2020-11-25 10:41:15 +01:00
Cornelius Riemenschneider
8d024c7ff1 C++: Add tests around references to pointers with temporary objects. 2020-11-25 10:40:33 +01:00
Cornelius Riemenschneider
e68352bcde C++: Add testcase for false positive. 2020-11-25 10:40:33 +01:00
Rasmus Wriedt Larsen
cafe55f5c7 Merge pull request #4701 from yoff/python-fix-return-node-enclosing-callable
Python: Use default `getEnclosingCallable` for `RetrunNode`
2020-11-25 10:36:41 +01:00
Jonas Jensen
5f0141953d Merge pull request #4644 from MathiasVP/unsafe-use-of-this-query
C++: Add 'unsafe use of this' query
2020-11-25 08:40:12 +01:00
Jonas Jensen
48460e3e45 Merge pull request #4716 from criemen/escapestree-code-cleanup
C++: Simplify and generalize EscapesTree::addressMayEscapeMutablyAt
2020-11-25 08:31:33 +01:00
Rasmus Lerchedahl Petersen
88643da01f Python: Use default getEnclosingCallable
for `RetrunNode`
2020-11-25 08:19:07 +01:00
Rasmus Lerchedahl Petersen
36d9097d03 Python: Add test for inner return 2020-11-25 07:09:27 +01:00
Rasmus Lerchedahl Petersen
34896ae0d7 Python: Fix failing test 2020-11-25 07:09:27 +01:00
Robert Marsh
9b07782d19 Merge pull request #4634 from geoffw0/modelchanges2
C++: Make classes in models.implementations private
2020-11-24 15:18:10 -05:00
CodeQL CI
395403789e Merge pull request #4585 from erik-krogh/moreReDoS
Approved by asgerf
2020-11-24 18:52:36 +00:00
Geoffrey White
71a8ac5183 C++: Autoformat. 2020-11-24 18:42:18 +00:00
porcupineyhairs
9c30b82116 Merge pull request #2 from aschackmull/java/ssrf-review
Java: Review fixes.
2020-11-24 23:14:50 +05:30
CodeQL CI
4be158b362 Merge pull request #4708 from erik-krogh/emptyName
Approved by asgerf
2020-11-24 17:34:55 +00:00
Geoffrey White
8184f76d1f C++: Sync identical files. 2020-11-24 16:29:14 +00:00
Mathias Vorreiter Pedersen
648acc3bfc C++: Prevent IR re-evaluation by importing GVN. 2020-11-24 17:04:54 +01:00
Cornelius Riemenschneider
026abae323 C++: Simplify some more. 2020-11-24 17:00:47 +01:00
Rasmus Wriedt Larsen
aa4345ac76 Merge pull request #4710 from yoff/python-dataflow-variable-capture
Python: Dataflow, variable capture
2020-11-24 15:04:38 +01:00
yoff
215986bce5 Apply suggestions from code review
Co-authored-by: Rasmus Wriedt Larsen <rasmuswriedtlarsen@gmail.com>
2020-11-24 14:12:23 +01:00
Rasmus Lerchedahl Petersen
05d156ba0f Python: add comments 2020-11-24 14:11:14 +01:00
CodeQL CI
8c68463e76 Merge pull request #4711 from erik-krogh/locType
Approved by asgerf
2020-11-24 13:10:32 +00:00
Anders Schack-Mulligen
3f04099c25 Update java/ql/src/experimental/CWE-918/RequestForgery.ql
Co-authored-by: Chris Smowton <smowton@github.com>
2020-11-24 13:18:58 +01:00
Jonas Jensen
260a8d4afb Merge pull request #4702 from MathiasVP/qualifier-as-parameter-for-callee
C++: Abstractions for treating qualifiers as parameters in IR
2020-11-24 12:58:27 +01:00
CodeQL CI
2277242196 Merge pull request #4692 from yoff/python-psycopg
Approved by RasmusWL
2020-11-24 10:59:04 +00:00
Geoffrey White
bc23809e1b Merge branch 'main' into modelchanges2 2020-11-24 10:49:14 +00:00
Cornelius Riemenschneider
7983b16e84 C++: Simplify and generalize EscapesTree::addressMayEscapeMutablyAt predicate. 2020-11-24 11:35:18 +01:00
Anders Schack-Mulligen
0450489022 Java: Review fixes. 2020-11-24 11:31:44 +01:00
Mathias Vorreiter Pedersen
9d21b226d2 Merge branch 'main' into qualifier-as-parameter-for-callee 2020-11-24 11:13:14 +01:00
Cornelius Riemenschneider
14a03e2f54 Merge pull request #4715 from MathiasVP/remove-failing-duplicate-tests
C++: Remove duplication-tests
2020-11-24 11:04:46 +01:00
Mathias Vorreiter Pedersen
08f8660b17 C++/C#: Add pragma[noinline] to hasIndex predicates. 2020-11-24 10:45:02 +01:00
Mathias Vorreiter Pedersen
8bd14c5af6 C++: Remove duplication-tests directory since we no longer detect duplication. 2020-11-24 10:10:31 +01:00
Erik Krogh Kristensen
f03429a4b8 change description for source root folder 2020-11-23 23:46:44 +01:00
CodeQL CI
9123f249ad Merge pull request #4705 from erik-krogh/bigString
Approved by asgerf
2020-11-23 22:40:42 +00:00
Erik Krogh Kristensen
33dab1717e treat nodes with type "Location" as a location source - but not if we can track it from an original node with type "Location" 2020-11-23 17:03:50 +01:00
Rasmus Lerchedahl Petersen
39c5e0d487 Python: update test expectations 2020-11-23 16:46:35 +01:00
Rasmus Lerchedahl Petersen
38bb06a207 Merge remote-tracking branch 'upstream/main' into python-dataflow-variable-capture 2020-11-23 16:40:20 +01:00
Tom Hvitved
129deb0a00 C#: Add change note 2020-11-23 16:09:33 +01:00
Tom Hvitved
cd77f14a75 C#: Rename getSourceDeclaration() to getUnboundDeclaration() 2020-11-23 16:09:33 +01:00
Geoffrey White
7015a9cf53 C++: Un-private a few classes that are now used by the current DefaultSafeExternalAPIFunction implementation. 2020-11-23 14:47:22 +00:00
Anders Schack-Mulligen
2cf10a7658 Merge pull request #4427 from aschackmull/java/fastjson
Java: Add support for FastJson in unsafe deserialization.
2020-11-23 14:40:14 +01:00
Erik Krogh Kristensen
f7f9beeefd avoid reporting empty names in js/exposure-of-private-files 2020-11-23 14:24:42 +01:00
Erik Krogh Kristensen
02d5fbf46b remove superfluous space 2020-11-23 14:22:16 +01:00
Rasmus Wriedt Larsen
9156163563 Merge pull request #4703 from github/hmakholm/pr/duplicate-code-2
Remove unit tests for duplicate-code detection, take II
2020-11-23 13:52:24 +01:00
Tom Hvitved
f0f5d44b33 C#: Replace BreakNormalCompletion with a nested completion 2020-11-23 11:38:24 +01:00
Tom Hvitved
17df059432 C#: Replace matchesCompletion() with getAMatchingSuccessorType() 2020-11-23 11:38:24 +01:00
Tom Hvitved
f3abaa406c C#: Refactor CFG implementation 2020-11-23 11:38:24 +01:00
Erik Krogh Kristensen
234730419b restrict computation of ConcatenationRoot::getConstantStringParts to results that are less than 1 million chars long 2020-11-23 10:29:47 +01:00
Tamás Vajk
7d38b2dd17 Merge pull request #4623 from tamasvajk/feature/csharp9-type-param-nullability
C#: Add type parameter ref/value type tests
2020-11-23 09:58:56 +01:00
Rasmus Lerchedahl Petersen
777100f25c Python: rename file, package, and class 2020-11-23 09:17:40 +01:00
Tom Hvitved
c571e42cd5 C#: Move internal CFG logic into separate file 2020-11-21 19:49:17 +01:00
Mathias Vorreiter Pedersen
a7644db762 C++: Use the new names in IR dataflow. Turns out DataFlowCall had its own implementation of getArgument already (which didn't handle qualifiers). The predicate wasn't used anywhere, so I simply removed it, as a better predicate is now available on the base class of DataFlowCall. 2020-11-21 01:00:59 +01:00
Mathias Vorreiter Pedersen
61bbceb201 C++/C#: Sync identical files 2020-11-21 00:55:07 +01:00
Mathias Vorreiter Pedersen
f173dc71c0 C++: Use shorter names for new IR predicates. This should hopefully guide users to use these predicates by default. 2020-11-21 00:54:50 +01:00
Geoffrey White
cc8d4b4c75 Merge branch 'main' into modelchanges2 2020-11-20 20:33:52 +00:00
Geoffrey White
fddd353155 C++: Updated autoformat. 2020-11-20 20:15:45 +00:00
Henning Makholm
a2a4938f60 Remove unit tests for duplicate-code detection, take II
In #4689 I forgot to remove the `.expected` files too, but they are
now of course useless.
2020-11-20 21:07:42 +01:00
Asger F
adc7bbfa4d Merge pull request #4694 from asgerf/js/flow-to-external-api
JS: Add UntrustedDataToExternalAPI query
2020-11-20 15:56:04 +00:00
Asger Feldthaus
f894cf2074 JS: Add support for react-hot-loader 2020-11-20 15:28:32 +00:00
Mathias Vorreiter Pedersen
6ead6c6d38 Merge branch 'main' into qualifier-as-parameter-for-callee 2020-11-20 16:17:10 +01:00
Tamas Vajk
0fa3cf7912 Simplify test predicates 2020-11-20 16:05:12 +01:00
Asger Feldthaus
16429c8ca4 JS: followed -> followed by 2020-11-20 14:44:25 +00:00
Mathias Vorreiter Pedersen
fd4f8c557c Merge branch 'main' into unsafe-use-of-this-query 2020-11-20 14:54:51 +01:00
Jonas Jensen
14aa6427ca Merge pull request #4696 from MathiasVP/get-result-memory-location-join-order-fix
C++: Fix bad join order in AliasedSSA::getResultMemoryLocation
2020-11-20 14:50:35 +01:00
Tamas Vajk
3e836ef671 C#: Add type parameter ref/value type tests 2020-11-20 13:08:38 +01:00
Tamás Vajk
77afd5a617 Merge pull request #4633 from tamasvajk/feature/csharp9-native-int
C#: Add test cases for native integers
2020-11-20 12:58:11 +01:00
Mathias Vorreiter Pedersen
c7efc91676 C++: Use the new predicates in IR dataflow. 2020-11-20 12:24:39 +01:00
Mathias Vorreiter Pedersen
f3b5d7b830 C++/C#: Sync identical files 2020-11-20 12:23:34 +01:00
Mathias Vorreiter Pedersen
416431a7c1 C++: Add convenience predicates for working with qualifiers as parameters. 2020-11-20 12:22:37 +01:00
Jonas Jensen
3342fac83e Merge pull request #4688 from criemen/printast-performance
C++: Speed up PrintAST.
2020-11-20 11:45:42 +01:00
Asger Feldthaus
7536c49c6f JS: Use getAParameter and not getReceiver instead of getASuccessor 2020-11-20 10:34:30 +00:00
Tamas Vajk
52680cd1dc C#: Add test cases for native integers 2020-11-20 11:31:20 +01:00
Asger F
405f07720a Apply suggestions from code review
Co-authored-by: Max Schaefer <54907921+max-schaefer@users.noreply.github.com>
2020-11-20 10:21:19 +00:00
Asger Feldthaus
b34df9ff33 JS: Autoformat 2020-11-20 10:15:35 +00:00
Cornelius Riemenschneider
1afd32c033 C++: Add comment, rename class. 2020-11-20 10:49:12 +01:00
yoff
b478a51d4e Apply suggestions from code review
Thanks for doing the work for me :-)

Co-authored-by: Rasmus Wriedt Larsen <rasmuswriedtlarsen@gmail.com>
2020-11-20 10:46:04 +01:00
Cornelius Riemenschneider
eeece5937d C++: One more speedup, address review. 2020-11-20 09:56:08 +01:00
Tamás Vajk
056b0c2627 Merge pull request #4626 from tamasvajk/feature/stats
C#: Add stats file from the jenkins job
2020-11-20 09:16:56 +01:00
Mathias Vorreiter Pedersen
490bba5c9f C++: Fix bad join order in AliasedSSA::getResultMemoryLocation. 2020-11-19 17:40:41 +01:00
Rasmus Lerchedahl Petersen
7cbbf3bbf7 Python: slightly nicer test 2020-11-19 16:20:57 +01:00
Rasmus Wriedt Larsen
7e407d43d2 Python: Change (single) test to match codeql database create 2020-11-19 14:56:18 +01:00
Rasmus Wriedt Larsen
8ffcff0824 Python: Add example of top-level module shadowing stdlib
Although this test is added under the `wrong` folder, the current results from
this CodeQL test is actually correct (compared with the Python
interpreter). However, they don't match what the extractor does when invoked
with `codeql database create`.

Since I deemed it "more than an easy fix" to change the extractor behavior for
`codeql database create` to match the real python behavior, and it turned out to
be quite a challenge to change the extractor behavior for all tests, I'm just
going to make THIS ONE test-case behave like the extractor will with `codeql
database create`...

This is a first commit, to show how the extractor works with qltest by default.

Inspired by the debugging in https://github.com/github/codeql/issues/4640
2020-11-19 14:56:17 +01:00
Asger Feldthaus
f737f34dcd JS: Add UntrustedDataToExternalApi query 2020-11-19 13:42:25 +00:00
Mathias Vorreiter Pedersen
2e6a3cd33b Merge branch 'main' into unsafe-use-of-this-query 2020-11-19 13:03:29 +01:00
Jonas Jensen
cd20163f6e Merge pull request #4676 from MathiasVP/untrusted-dataflow-to-external-api-query
C++: Untrusted data used in external APIs
2020-11-19 12:56:08 +01:00
Rasmus Lerchedahl Petersen
6cc8e5acf1 Python: support psycopg 2020-11-19 12:13:20 +01:00
Mathias Vorreiter Pedersen
072adaa279 C++: Require that no override of the called pure virtual function exists in any base class. This removes the false positive in the testcase. Based on the results on LGTM we have agreed to set the @precision to very-high. 2020-11-19 12:10:57 +01:00
Rasmus Lerchedahl Petersen
39f134c1c1 Python: reorganized and added to tests 2020-11-19 12:06:58 +01:00
Erik Krogh Kristensen
a3b21ad43b Apply suggestions from code review
Co-authored-by: Asger F <asgerf@github.com>
2020-11-19 11:42:12 +01:00
Tom Hvitved
02ec325380 C#: Fix bad join-orders following stats update 2020-11-19 10:42:35 +01:00
Tamas Vajk
866c98bbd9 C#: Add stats file from the jenkins job 2020-11-19 10:42:34 +01:00
Rasmus Wriedt Larsen
2945eada9e Merge pull request #4689 from github/hmakholm/pr/no-dup-code-tests
Remove unit tests for duplicate-code detection
2020-11-19 10:07:04 +01:00
Dave Bartolomeo
08efd7fbd9 Merge pull request #4558 from rdmarsh2/rdmarsh2/cpp/remove-initialize-nonlocal
Remove InitializeNonlocalInstruction
2020-11-18 20:23:08 -05:00
Porcupiney Hairs
ebc6c49555 include suggestions from review. 2020-11-19 03:37:00 +05:30
Erik Krogh Kristensen
cc1d797cef adjust top comment to reflect what the query does, and add comment about which kind of accepting state is assumed. 2020-11-18 21:32:31 +01:00
Robert Marsh
5753a2d401 C++: autoformat 2020-11-18 11:02:58 -08:00
Robert Marsh
693626e460 C++: accept value numbering test changes 2020-11-18 11:00:31 -08:00
Cornelius Riemenschneider
4405547b04 C++: Fix tests. 2020-11-18 17:41:08 +01:00
Cornelius Riemenschneider
cfe86bf53b C++: Remove default getAPrimaryQlClass(). 2020-11-18 17:34:56 +01:00
Cornelius Riemenschneider
f70f32e3ed C++: Fix tests for PrintAST. 2020-11-18 17:22:13 +01:00
Henning Makholm
a86679a377 Remove unit tests for duplicate-code detection
The old Semmle duplicate-code detection code has never been done when
extracting databases for the CodeQL CLI, except that `codeql test run`
will run it _just_ in order to support tests of the feature. With the
sunsetting of Odasa there's no need to even _test_ the feature anymore.

This commit removes those tests that fail when the duplicate-code
detector is turned off. Once it is merged and bumped, we can finally
remove it from `codeql`.
2020-11-18 16:37:29 +01:00
Cornelius Riemenschneider
429aa15b1b C++: Speed up PrintAST. 2020-11-18 16:08:51 +01:00
Tamás Vajk
cb8c5e8cca Merge pull request #4663 from tamasvajk/feature/patterns
C#: Refactor pattern extraction
2020-11-18 16:01:15 +01:00
Erik Krogh Kristensen
58c31f0eca prune more regexps initially in the ReDoS query 2020-11-18 15:14:46 +01:00
Erik Krogh Kristensen
c4153a617e remove duplicated test cases from ReDoS, and adjust variables names to match test output 2020-11-18 14:49:09 +01:00
Mathias Vorreiter Pedersen
4746320f3c Merge pull request #4684 from criemen/cleanup-api
C++: Clean up Type.qll, getSpecifierString() API.
2020-11-18 13:07:51 +01:00
Mathias Vorreiter Pedersen
715f233360 C++: Add a new model class describing pure memory functions, and use this new model in DefaultSafeExternalAPIFunction. 2020-11-18 12:47:33 +01:00
Geoffrey White
decbb08340 Merge pull request #4402 from github/matt-gretton-dann/cpp20-constinit-results
Update expected results for `constint' support
2020-11-18 11:46:41 +00:00
Cornelius Riemenschneider
959b8f39d2 C++: Autoformat. 2020-11-18 12:13:46 +01:00
Erik Krogh Kristensen
8270bf5bb9 make the character search skip unencodable characters 2020-11-18 11:55:49 +01:00
James Fletcher
47fa2d31cc Merge pull request #4685 from github/main-126-merge
Merge `rc/1.26` into `main`
2020-11-18 10:41:03 +00:00
yoff
04fc1e44f2 Merge pull request #4683 from RasmusWL/python-fix-bad-join-callStep-for-dist-upgrade
Python: Fix bad join order in TypeTracker::callStep
2020-11-18 11:12:38 +01:00
Rasmus Wriedt Larsen
ab856d6c01 Python: Show getCallableForArgument can have multiple results 2020-11-18 10:44:32 +01:00
james
8c9ea1632f Merge branch 'rc/1.26' into main-126-merge 2020-11-18 09:33:21 +00:00
Cornelius Riemenschneider
b670522a9f Clean up Type.qll, getSpecifierString() API. 2020-11-18 10:21:03 +01:00
Mathias Vorreiter Pedersen
09c5caa3bd C++: Move ExternalAPI files into query directory to prevent out-of-tree use. 2020-11-18 10:18:03 +01:00
Rasmus Wriedt Larsen
abf2902a69 Python: Fix QLDoc
Co-authored-by: yoff <lerchedahl@gmail.com>
2020-11-18 09:47:23 +01:00
Rasmus Wriedt Larsen
39590a39cb Python: Rename helper predicate based on review 2020-11-18 09:26:53 +01:00
Erik Krogh Kristensen
55f2f86a26 limit the search of state-pairs to the ones that are reachable within the given length 2020-11-18 09:23:35 +01:00
Mathias Vorreiter Pedersen
f16591dffc C++: Respond to qhelp review comments. 2020-11-18 09:18:14 +01:00
Rasmus Wriedt Larsen
14136154d6 Python: Fix bad join order in TypeTracker::callStep
From a local evaluation against flask DB, after
https://github.com/github/codeql/pull/4649 was merged we would get:

```
Tuple counts for TypeTracker::callStep#ff/2@a21b71:
9876     ~0%     {3} r1 = SCAN DataFlowPrivate::DataFlowCall::getArg_dispred#fff AS I OUTPUT I.<2>, I.<0>, I.<1>
9876     ~2%     {3} r2 = JOIN r1 WITH project#DataFlowPrivate::DataFlowCall::getArg_dispred#fff AS R ON FIRST 1 OUTPUT r1.<2>, R.<0>, r1.<1>
72388997 ~0%     {4} r3 = JOIN r2 WITH DataFlowPublic::ParameterNode::isParameterOf_dispred#fff_201#join_rhs AS R ON FIRST 1 OUTPUT r2.<2>, R.<2>, r2.<1>, R.<1>
4952     ~0%     {2} r4 = JOIN r3 WITH DataFlowPrivate::DataFlowCall::getCallable_dispred#ff AS R ON FIRST 2 OUTPUT r3.<2>, r3.<3>
                     return r4
```
2020-11-18 09:17:31 +01:00
Dave Bartolomeo
ab715ec302 Merge pull request #4680 from criemen/printast-performance
C++: Improve PrintAST performance.
2020-11-17 23:13:26 -05:00
Cornelius Riemenschneider
a92f7a4563 C++: Include trailing whitespace in Type.getSpecifierString(). 2020-11-17 18:50:34 +01:00
Cornelius Riemenschneider
5c3de06b6d C++: Improve PrintAST performance.
Co-authored-by: Jonas Jensen <jbj@github.com>
2020-11-17 17:47:16 +01:00
Aditya Sharad
b9b6a35564 Merge pull request #4629 from pwntester/improve_bean_validation_query
Java: add some improvements to the bean validation query
2020-11-17 08:35:49 -08:00
Erik Krogh Kristensen
c4d7533701 Merge branch 'main' into moreReDoS 2020-11-17 17:34:49 +01:00
Erik Krogh Kristensen
97acf1fd87 fix FP related to inverted character classes choosing a char that was not matched by the char class 2020-11-17 17:34:43 +01:00
Mathias Vorreiter Pedersen
52bbb326ca QLDoc: Wrap lines and disambiguate explanation. 2020-11-17 16:23:27 +01:00
Mathias Vorreiter Pedersen
d93d3c8699 C++: Use the getSourceType predicate on RemoteFlowSources for better alert messages. 2020-11-17 16:23:27 +01:00
Mathias Vorreiter Pedersen
d1272d3a79 C++: Use strictcount instead of count. 2020-11-17 16:23:27 +01:00
Mathias Vorreiter Pedersen
4cb25d8e18 C++: Add isParameterDerefOrQualifierObject helper predicate to FunctionInput and FunctionOutput. 2020-11-17 16:23:27 +01:00
Mathias Vorreiter Pedersen
dea16d4d62 QLDoc/C++: Rename {IR}ExternalAPIsUsedWithUntrustedData to {IR}CountUntrustedDataToExternalAPI 2020-11-17 16:23:13 +01:00
Mathias Vorreiter Pedersen
eabc69b98e C++: Autoformat 2020-11-17 16:09:25 +01:00
Matthew Gretton-Dann
62767e7e0d Update expected results for `constinit' support 2020-11-17 14:01:00 +00:00
Mathias Vorreiter Pedersen
5d2b85fcf5 Update cpp/ql/src/semmle/code/cpp/models/implementations/Getenv.qll
Co-authored-by: hubwriter <hubwriter@github.com>
2020-11-17 13:02:28 +01:00
Tom Hvitved
7f0ad2d232 Merge pull request #4646 from hvitved/csharp/cfg/post-order-exprs
C#: Represent all expressions in post-order in the CFG
2020-11-17 13:01:35 +01:00
Mathias Vorreiter Pedersen
c37093f4bc C++: Add copies of qhelp files for IR. 2020-11-17 12:28:31 +01:00
Mathias Vorreiter Pedersen
3b8580efaf C++: Add qhelp and example files (modeled after the Java examples). 2020-11-17 12:27:53 +01:00
Mathias Vorreiter Pedersen
c3c29b8dd0 C++: Add qldoc to new library files. 2020-11-17 12:27:53 +01:00
Mathias Vorreiter Pedersen
5c9b8f1cff C++: Update sync-identical-files. 2020-11-17 12:27:53 +01:00
Mathias Vorreiter Pedersen
5ad18eb748 C++: Add ExternalAPI query files (for AST and IR). 2020-11-17 12:27:40 +01:00
Jonas Jensen
55a38803cb Merge pull request #4673 from MathiasVP/ir-post-dominance
C++: IR post dominance
2020-11-17 09:35:51 +01:00
Tamás Vajk
f2259de5f1 Merge pull request #4666 from tamasvajk/feature/roslyn-3.8.0
C#: Upgrade Roslyn dependencies to 3.8.0
2020-11-17 08:59:55 +01:00
Rasmus Lerchedahl Petersen
71830abda0 Python: remaining c# tests, except lambdas
both via nonlocal and via dict
2020-11-17 08:28:11 +01:00
Mathias Vorreiter Pedersen
057bb14eee C++: Add ExternalAPI library files (for AST and IR). 2020-11-16 22:59:54 +01:00
Robert Marsh
a94826dc81 C++: common superclass for Remote/LocalFlowSource 2020-11-16 18:05:17 +01:00
Robert Marsh
31d3e94cec C++: Grammar/style fixes from code review
Co-authored-by: Jonas Jensen <jbj@github.com>
2020-11-16 18:03:44 +01:00
Robert Marsh
74e05c111e C++: add local flow sources 2020-11-16 18:02:19 +01:00
Rasmus Lerchedahl Petersen
27b4c67b9f Python: Start of tests for captured variables 2020-11-16 17:25:39 +01:00
Tamas Vajk
8bef5f417e C#: Upgrade Roslyn dependencies to 3.8.0 2020-11-16 16:44:14 +01:00
Mathias Vorreiter Pedersen
4a7f9100e4 C++: Respond to review comments. 2020-11-16 15:30:42 +01:00
Mathias Vorreiter Pedersen
27aab4062a C++/C#: Sync identical files. 2020-11-16 15:05:59 +01:00
Mathias Vorreiter Pedersen
088d5863fc C++: Implement IR post-dominance predicates. 2020-11-16 15:04:40 +01:00
Mathias Vorreiter Pedersen
10a9f7ba13 Update cpp/change-notes/2020-11-12-unsafe-use-of-this.md
Co-authored-by: hubwriter <hubwriter@github.com>
2020-11-16 12:28:57 +01:00
Anders Schack-Mulligen
4be731d2ab Java: Adjust reference to static method and add test. 2020-11-16 11:47:58 +01:00
Anders Schack-Mulligen
80ee92ae97 Java: Add support for FastJson in unsafe deserialization. 2020-11-16 11:47:58 +01:00
Mathias Vorreiter Pedersen
020af1c88c C++: Add qhelp. 2020-11-16 11:21:18 +01:00
Geoffrey White
4b8f338139 C++: Autoformat. 2020-11-16 10:19:06 +00:00
CodeQL CI
09cfb24afa Merge pull request #4648 from erik-krogh/regexpParse
Approved by asgerf
2020-11-16 08:20:40 +00:00
CodeQL CI
13edc3713d Merge pull request #4638 from erik-krogh/jwt
Approved by asgerf
2020-11-16 08:19:58 +00:00
Erik Krogh Kristensen
a49b99b18c autoformat 2020-11-13 20:06:17 +01:00
Erik Krogh Kristensen
affb11b0e3 changes based on review 2020-11-13 19:46:37 +01:00
Erik Krogh Kristensen
2f4fcc2f5e Apply suggestions from code review
Co-authored-by: Asger F <asgerf@github.com>
2020-11-13 18:03:07 +01:00
Mathias Vorreiter Pedersen
0a6a22562b C++: Respond to more review comments.
- Remove post-dominance requirement. It was really just hiding good
  results.
- Fix test annotations. Turns out Clang and GCC's 'undefined behavior'
  warning didn't align with the C++ standard.
2020-11-13 15:44:33 +01:00
Geoffrey White
dfcb0ae7c2 C++: Autoformat. 2020-11-13 14:39:33 +00:00
Jonas Jensen
8bb9e8a4af Merge pull request #4592 from geoffw0/varnotused
C++: Work around two false positive issues with the UnusedLocals.ql query
2020-11-13 14:53:58 +01:00
yoff
f7361cae31 Merge pull request #4667 from RasmusWL/python-add-code-snippets
Python: Add code snippets for VS Code
2020-11-13 13:48:37 +01:00
Porcupiney Hairs
402a320a55 include suggestions from review. 2020-11-13 18:07:42 +05:30
Porcupiney Hairs
4b25532b9f include suggestions from review. 2020-11-13 17:55:56 +05:30
Rasmus Wriedt Larsen
9f1d8cd1bb Python: Convert indentation to spaces for VS Code snippets 2020-11-13 13:05:23 +01:00
Rasmus Wriedt Larsen
5200af5244 Python: Add code snippets for VS Code
Notice that in this form, the filename doesn't matter, and you need to specify
`scope` to limit the snippet to only trigger for `ql`.
2020-11-13 10:57:17 +01:00
Mathias Vorreiter Pedersen
b249777bfb C++: Respond to review comments. 2020-11-13 10:44:06 +01:00
Tom Hvitved
708fca4a2f C#: Update ConstantCondition.ql 2020-11-13 10:23:09 +01:00
Erik Krogh Kristensen
7f68b07665 Merge branch 'main' into regexpParse 2020-11-13 09:33:16 +01:00
Tamás Vajk
19fac60e6d Merge pull request #4661 from tamasvajk/feature/fix-var-decl-type-mention
C#: Fix parent of 'TypeMention' for some variable declaration
2020-11-13 09:08:21 +01:00
Erik Krogh Kristensen
7cf7a44fda autoformat 2020-11-12 22:33:00 +01:00
Erik Krogh Kristensen
49be7e959f Merge branch 'main' into jwt 2020-11-12 21:36:09 +01:00
Erik Krogh Kristensen
99d03bab24 only flag the secret key in JWT 2020-11-12 21:36:05 +01:00
Porcupiney Hairs
eb6d6113d9 minor nit. 2020-11-13 00:39:09 +05:30
Tom Hvitved
94deed39a2 C#: Represent all expressions in post-order in the CFG 2020-11-12 20:04:48 +01:00
Tom Hvitved
202f7f07ec C#: Update modulus analysis tests 2020-11-12 20:04:48 +01:00
Porcupiney Hairs
f8de94e906 refactor SpringWebClient 2020-11-13 00:32:27 +05:30
Porcupiney Hairs
2525cfd786 include suggestions from review. 2020-11-13 00:28:06 +05:30
Geoffrey White
0804df42d1 C++: Autoformat. 2020-11-12 18:23:11 +00:00
Dave Bartolomeo
f43d9119c8 Merge pull request #4573 from MathiasVP/interleave-op-instr-field-flow
C++: instruction -> operand field flow
2020-11-12 10:14:19 -05:00
Mathias Vorreiter Pedersen
5e1f36e7ff Merge branch 'unsafe-use-of-this-query' of github.com:MathiasVP/ql into unsafe-use-of-this-query 2020-11-12 15:51:19 +01:00
Mathias Vorreiter Pedersen
f417831a23 C++: Add two more tests to 'unsafe use of this'. First, test that post-dominance removes some results. Second, that a cast to the pure virtual function's defining class (skipping past a derived class that overrides the function) followed by a call to the function still results in an alert. This is also undefined behavior. 2020-11-12 15:51:04 +01:00
Mathias Vorreiter Pedersen
896dee5fb7 C++: Fix dataflow pruning by evaluating successor before flowsToSink/flowsFromSource. 2020-11-12 15:47:48 +01:00
Mathias Vorreiter Pedersen
f2317aed55 C++/C#: Make post-dominator related predicates member predicates of IRBlock. 2020-11-12 15:41:52 +01:00
Tamas Vajk
dee1690748 C#: Remove duplicated pattern extraction logic 2020-11-12 14:34:54 +01:00
Geoffrey White
498177ce02 C++: Autoformat. 2020-11-12 13:30:58 +00:00
Tamas Vajk
3f150bb09b C#: Reorganize patterns 2020-11-12 13:47:38 +01:00
Tamas Vajk
8cbe8bc6b5 C#: Fix parent of 'TypeMention' for some variable declaration 2020-11-12 13:43:13 +01:00
CodeQL CI
6696d18f56 Merge pull request #4659 from asgerf/js/autoformat-date-functions
Approved by erik-krogh
2020-11-12 11:32:47 +00:00
Mathias Vorreiter Pedersen
00f067be84 Update cpp/change-notes/2020-11-12-unsafe-use-of-this.md
Co-authored-by: Jonas Jensen <jbj@github.com>
2020-11-12 11:28:12 +01:00
James Fletcher
19b974832b Merge pull request #4658 from github/main-126-merge
Merge rc/1.26 into main branch
2020-11-12 10:18:24 +00:00
Asger Feldthaus
4d3d982bde JS: Autoformat date-functions file 2020-11-12 10:07:30 +00:00
james
9fc84f8061 Merge branch 'rc/1.26' into main-126-merge 2020-11-12 09:55:32 +00:00
Mathias Vorreiter Pedersen
50e90510ef C++: Follow the new change-note protocol. 2020-11-12 10:27:11 +01:00
Mathias Vorreiter Pedersen
4c27c3757c C++: Respond to more review comments. 2020-11-12 10:27:03 +01:00
Tamás Vajk
b5ef3bd5ec Merge pull request #4516 from tamasvajk/feature/attributes
C#: Refactor attribute extraction
2020-11-12 09:57:46 +01:00
Erik Krogh Kristensen
044fbc0b49 optimize the regexp parser 2020-11-11 21:13:11 +01:00
Erik Krogh Kristensen
de6b21986c bump extractor version string 2020-11-11 21:10:03 +01:00
Erik Krogh Kristensen
74d933d475 move change note 2020-11-11 21:09:08 +01:00
Erik Krogh Kristensen
67d581edec Update javascript/2020-11-09-jwt.md
Co-authored-by: Asger F <asgerf@github.com>
2020-11-11 21:06:30 +01:00
Geoffrey White
2aa6ff6239 C++: Add comments about the temporary exclusions. 2020-11-11 17:33:16 +00:00
Mathias Vorreiter Pedersen
44e38ab6d2 Merge branch 'unsafe-use-of-this-query' of github.com:MathiasVP/ql into unsafe-use-of-this-query 2020-11-11 18:30:20 +01:00
Mathias Vorreiter Pedersen
e1ef41bd38 C++: Respond to review comments. 2020-11-11 18:29:51 +01:00
Mathias Vorreiter Pedersen
de0ca0aa74 Update cpp/ql/src/Likely Bugs/OO/UnsafeUseOfThis.cpp
Co-authored-by: Geoffrey White <40627776+geoffw0@users.noreply.github.com>
2020-11-11 18:12:49 +01:00
Geoffrey White
5359e13421 C++: Remove abstraction of OperatorNew/DeleteAllocationFunction. 2020-11-11 15:35:34 +00:00
yoff
86fc9e62ef Merge pull request #4650 from RasmusWL/python-set-literal-formatting
Python: Update set literal formatting
2020-11-11 15:35:12 +01:00
Alvaro Muñoz
30d8dce389 check that either there are no custom message interpolator configured, or there is at least one that is insecure 2020-11-11 12:53:54 +01:00
CodeQL CI
f9d62adcb2 Merge pull request #4567 from asgerf/js/date-functions
Approved by erik-krogh
2020-11-11 11:17:30 +00:00
Alvaro Muñoz
c3bc0d6c15 Apply formatting 2020-11-11 12:06:39 +01:00
Alvaro Muñoz
5b1858a514 Do not report the issue only if all message interpolators are secure 2020-11-11 11:50:15 +01:00
Rasmus Wriedt Larsen
611398586d Merge pull request #4649 from yoff/python-dataflow-cfgparameters
Python: Make `ParameterNode` a `CfgNode`
2020-11-11 10:22:12 +01:00
Tamas Vajk
453c97a8c3 Fix expected files of failing tests 2020-11-11 09:57:52 +01:00
Tom Hvitved
e57134e8a4 C#: Address comments 2020-11-11 09:54:51 +01:00
Tom Hvitved
d6515e7963 C#: Changes to extraction of attributes in assemblies
- Only cache on `AttributeData` and not the parent entity.
- Move `CreateGeneratedExpressionFromArgument` to `Expression.cs`.
- Restructure the various `CreateGenerated` methods so child entities are
  created inside them (and therefore no need to expose child index logic).
- Add locations to generated expressions.
- Avoid linear lookup in `CompilationUnit.cs`.
- Consolidate tests.
2020-11-11 09:54:51 +01:00
Tom Hvitved
b8d6070e00 C#: Remove redundant conjunct 2020-11-11 09:54:51 +01:00
Tom Hvitved
3048919fce C#: Introduce AssertionFailure class 2020-11-11 09:54:51 +01:00
Tom Hvitved
91dd67798a C#: Exclude assembly attribute expressions from CFG construction 2020-11-11 09:50:59 +01:00
Tamas Vajk
4b88b237b3 C#: Fix failing tests 2020-11-11 09:50:59 +01:00
Tamas Vajk
4d6ccb1ef4 C#: Add change note for attribute extraction refactoring 2020-11-11 09:46:18 +01:00
Tamas Vajk
ba730b6f02 Fix failing tests 2020-11-11 09:46:18 +01:00
Tamas Vajk
5f18840a9e C#: Extract attributes of referenced assembly 2020-11-11 09:46:18 +01:00
Tamas Vajk
cdea3afced C#: Change extraction of Attributes to have ID based on the location when location is from source 2020-11-11 09:46:18 +01:00
Tamas Vajk
c04d6f479c C#: Change attribute extraction to use AttributeData and created generated expressions when missing 2020-11-11 09:46:18 +01:00
Tamas Vajk
883795e126 C#: Add attribute test for extraction from symbols without syntax nodes 2020-11-11 09:46:18 +01:00
Tamas Vajk
c14a63488c C#: Add missing tests for attribute extraction 2020-11-11 09:46:18 +01:00
Rasmus Wriedt Larsen
9ed15732ed Python: Update set literal formatting
Now that auto-formatting rules have been updated
2020-11-11 09:38:25 +01:00
Rasmus Lerchedahl Petersen
0710963fc3 Python: update test expectations
EssaNode -> ControlFlowNode
2020-11-10 23:58:55 +01:00
Erik Krogh Kristensen
e01d4b104e update expected output 2020-11-10 23:42:38 +01:00
Mathias Vorreiter Pedersen
8d4b948acf Merge branch 'main' into interleave-op-instr-field-flow 2020-11-10 21:15:50 +01:00
Erik Krogh Kristensen
6b7f8dd505 fix parse errors related to char escapes and char ranges 2020-11-10 21:02:29 +01:00
Robert Marsh
14f1fa50f1 Merge branch 'main' into rdmarsh2/cpp/remove-initialize-nonlocal
Accept test changes from IR temporaries and block ordering
2020-11-10 11:14:26 -08:00
Geoffrey White
2c7a01952e C++: Improve the changes to Iterator. 2020-11-10 17:11:47 +00:00
Jonas Jensen
fc764db8e1 Merge pull request #4643 from nickrolfe/getFileBySourceArchiveName
Replace getEncodedFile with shared getFileBySourceArchiveName predicate
2020-11-10 17:36:29 +01:00
Geoffrey White
c635166079 Merge remote-tracking branch 'upstream/main' into modelchanges2 2020-11-10 16:30:03 +00:00
Mathias Vorreiter Pedersen
a330cae2f9 C++: Update change-notes. 2020-11-10 17:24:45 +01:00
Mathias Vorreiter Pedersen
4bcf1f498b C++: Add new query for unsafe use of this. 2020-11-10 17:23:55 +01:00
Mathias Vorreiter Pedersen
884554692f Merge pull request #4645 from geoffw0/oddsends2
C++: Odds and ends
2020-11-10 16:59:10 +01:00
Mathias Vorreiter Pedersen
618d9c33a1 Merge pull request #4642 from geoffw0/memberfn
C++: Improve MemberFunction.qll data flows
2020-11-10 16:54:42 +01:00
Jonas Jensen
da90717ef0 Merge pull request #4641 from aschackmull/autoformat-setliterals
C++/C#/Java/JavaScript/Python: Autoformat set literals.
2020-11-10 16:11:11 +01:00
Tom Hvitved
7e89e57f5d Merge pull request #4606 from hvitved/csharp/dataflow/ef
C#: Precise data flow for EntityFramework(Core)
2020-11-10 15:54:20 +01:00
Nick Rolfe
ac4a1f1d9b Update comment to be a QLDoc comment 2020-11-10 14:14:27 +00:00
Geoffrey White
be537a8021 C++: Move ThrowExpr, ReThrowExpr definitions to a more logical location. 2020-11-10 14:06:58 +00:00
Nick Rolfe
1e1eb7ee33 Replace getEncodedFile with shared getFileBySourceArchiveName predicate
While also making it work with paths for databases created on Windows.
2020-11-10 13:55:27 +00:00
Geoffrey White
2beb7824ec C++: Backticks. 2020-11-10 13:35:05 +00:00
Geoffrey White
599034668f C++: Fix typo. 2020-11-10 13:32:31 +00:00
Geoffrey White
f109e8ccbc C++: Remove duplicate import. 2020-11-10 13:31:47 +00:00
Geoffrey White
c4fe8df04c C++: Support data flow from parameter deref to return value. 2020-11-10 13:17:34 +00:00
Geoffrey White
4563e32e92 C++: Remove data flows we shouldn't need in MemberFunction. 2020-11-10 13:17:24 +00:00
Anders Schack-Mulligen
89ef6ea4eb C++/C#/Java/JavaScript/Python: Autoformat set literals. 2020-11-10 13:32:27 +01:00
Alvaro Muñoz
02cf49a773 apply codeql formatting 2020-11-10 11:46:42 +01:00
Rasmus Lerchedahl Petersen
109d55eb25 Python: Make ParameterNode a CfgNode
Add a step from that `CfgNode` to the corresponding `EssaNode`.
The intended effect is seen in `ImpliesDataflow.expected`.
The efeect seen in other `.expected`-files is that parameter nodes
change type, that the extra steps are seen, and that flow from
`EssaVar`s is mirrored in flow from `CfgNode`s.
There is one surprise, which is the `.0` node in
`coverage/localFlow.expected`.
2020-11-10 11:35:50 +01:00
Alvaro Muñoz
24a47fbb0f additional qldoc commentes 2020-11-10 10:48:47 +01:00
Alvaro Muñoz
3545edb92c address code review suggestions 2020-11-10 10:45:14 +01:00
Tom Hvitved
697b0ba4c1 C#: Address review comments 2020-11-10 10:42:05 +01:00
Erik Krogh Kristensen
ae7c7607f1 fix typos in documentation strings
Co-authored-by: intrigus-lgtm <60750685+intrigus-lgtm@users.noreply.github.com>
2020-11-10 10:41:39 +01:00
Erik Krogh Kristensen
b5b4d6d62a add change note 2020-11-10 10:41:39 +01:00
Erik Krogh Kristensen
5ecae55e77 add keys used by jsonwebtoken as CredentialsExpr 2020-11-10 10:41:39 +01:00
Erik Krogh Kristensen
e75259d3a6 model the verify function in jsonwebtoken 2020-11-10 10:41:39 +01:00
Erik Krogh Kristensen
6732493377 add model for jwt-decode 2020-11-10 10:41:36 +01:00
yoff
26286e534e Merge pull request #4174 from yoff/SharedDataflow_PointsToImpliesDataflow
Python: Dataflow, Test that `pointsTo` implies data flow
merging now, will fix `self` in a later PR
2020-11-10 10:25:29 +01:00
Robert Marsh
c00587d2cb C++/C#: Conflated memory as IR dump annotation
Removes the IR consistency checks for conflated memory and marks
instructions that have a conflated result with a percent sign (%)
instead. This avoids reimplementing part of the alias analysis logic
in the consistency check.
2020-11-09 14:55:47 -08:00
Porcupiney Hairs
38de9b6433 add request forgery query 2020-11-10 01:19:35 +05:30
CodeQL CI
1e048d8045 Merge pull request #4609 from asgerf/js/destructuring-export
Approved by erik-krogh
2020-11-09 15:47:00 +00:00
Anders Schack-Mulligen
31ec79819e Merge pull request #4631 from luchua-bc/java-nfe-library
Java: Factor NumberFormatException out into a library file
2020-11-09 13:50:31 +01:00
yoff
3215f50b73 Merge pull request #4632 from RasmusWL/python-move-configurations-out-of-queries
Python: move configurations out of queries
2020-11-09 13:10:33 +01:00
luchua-bc
d765c7bbb2 Update qldoc 2020-11-09 11:23:48 +00:00
Asger Feldthaus
32c5bc4836 JS: Update test case output (new flow is correct) 2020-11-09 11:19:47 +00:00
Mathias Vorreiter Pedersen
25ba6ca160 Merge pull request #4637 from jbj/temp-objects-perf-fixup
C++: pragma[noinline] parameter index predicates
2020-11-09 10:12:50 +01:00
Mathias Vorreiter Pedersen
3c2fb5a93f Merge branch 'main' into interleave-op-instr-field-flow 2020-11-09 09:58:19 +01:00
Rasmus Wriedt Larsen
fbe51c51bb Python: Add missing QLDoc 2020-11-09 09:05:08 +01:00
Jonas Jensen
b8b1f0c617 C++: pragma[noinline] parameter index predicates
A performance regression in `definitionByReferenceNodeFromArgument#ff`
was ultimately caused by a join on parameter indexes in
`DefinitionByReferenceNode.getArgument`. Joining on numbers in QL is
always fragile, and somehow the changes in #4432 had caused the join
order here to break.

Instead of tweaking the join order in the slow predicate itself, I added
`pragma[noinline]` to one of the predicates involved in the join on
parameter indexes. This should prevent us from getting similar
performance problems in the future when we write code that joins on
parameter numbers. Joining on indexes is always risky, but it's even
more risky when one of the predicates in the join is inlined by the
compiler and expands to further joins.

I tested performance by running `CgiXss.ql` on a ChakraCore snapshot.
Tuple counts before (I interrupted execution after five minutes or so):

    (626s) Tuple counts for DataFlowUtil::definitionByReferenceNodeFromArgument#ff:
    58162      ~0%     {3} r1 = SCAN DataFlowUtil::DefinitionByReferenceNode#class#ff AS I OUTPUT I.<1>, -1, I.<0>
    26934      ~0%     {2} r2 = JOIN r1 WITH Instruction::IndexedInstruction#ff AS R ON FIRST 2 OUTPUT r1.<0>, r1.<2>
    26934      ~1%     {2} r3 = JOIN r2 WITH Instruction::SideEffectInstruction::getPrimaryInstruction_dispred#3#ff AS R ON FIRST 1 OUTPUT R.<1>, r2.<1>
    26850      ~1%     {2} r4 = JOIN r3 WITH Instruction::CallInstruction::getThisArgumentOperand_dispred#ff AS R ON FIRST 1 OUTPUT R.<1>, r3.<1>
    26850      ~0%     {2} r5 = JOIN r4 WITH Operand::Operand::getDef_dispred#3#ff AS R ON FIRST 1 OUTPUT R.<1>, r4.<1>
    26850      ~1%     {2} r6 = JOIN r5 WITH Instruction::Instruction::getUnconvertedResultExpression_dispred#ff AS R ON FIRST 1 OUTPUT R.<1>, r5.<1>
    58162      ~0%     {2} r7 = SCAN DataFlowUtil::DefinitionByReferenceNode#class#ff AS I OUTPUT I.<1>, I.<0>
    58162      ~4%     {3} r8 = JOIN r7 WITH Instruction::IndexedInstruction#ff AS R ON FIRST 1 OUTPUT R.<1>, r7.<1>, r7.<0>
    4026581120 ~0%     {4} r9 = JOIN r8 WITH Instruction::CallInstruction::getPositionalArgumentOperand_dispred#fff_102#join_rhs AS R ON FIRST 1 OUTPUT r8.<2>, R.<1>, r8.<1>, R.<2>
    31154      ~4%     {2} r10 = JOIN r9 WITH Instruction::SideEffectInstruction::getPrimaryInstruction_dispred#3#ff AS R ON FIRST 2 OUTPUT r9.<3>, r9.<2>
    31154      ~8%     {2} r11 = JOIN r10 WITH Operand::Operand::getDef_dispred#3#ff AS R ON FIRST 1 OUTPUT R.<1>, r10.<1>
    31154      ~0%     {2} r12 = JOIN r11 WITH Instruction::Instruction::getUnconvertedResultExpression_dispred#ff AS R ON FIRST 1 OUTPUT R.<1>, r11.<1>
    58004      ~0%     {2} r13 = r6 \/ r12
                       return r13

Tuple counts after:

    (0s) Tuple counts for DataFlowUtil::definitionByReferenceNodeFromArgument#ff:
    385785  ~6%     {2} r1 = SCAN DataFlowUtil::DefinitionByReferenceNode#class#ff AS I OUTPUT I.<1>, I.<0>
    385785  ~0%     {3} r2 = JOIN r1 WITH Instruction::IndexedInstruction#ff AS R ON FIRST 1 OUTPUT r1.<0>, r1.<1>, R.<1>
    385785  ~1%     {3} r3 = JOIN r2 WITH Instruction::SideEffectInstruction::getPrimaryInstruction_dispred#3#ff AS R ON FIRST 1 OUTPUT R.<1>, r2.<2>, r2.<1>
    198736  ~4%     {2} r4 = JOIN r3 WITH Instruction::CallInstruction::getPositionalArgument#fff AS R ON FIRST 2 OUTPUT R.<2>, r3.<2>
    198736  ~0%     {2} r5 = JOIN r4 WITH Instruction::Instruction::getUnconvertedResultExpression_dispred#ff AS R ON FIRST 1 OUTPUT R.<1>, r4.<1>
    385785  ~1%     {3} r6 = SCAN DataFlowUtil::DefinitionByReferenceNode#class#ff AS I OUTPUT I.<1>, -1, I.<0>
    186891  ~1%     {2} r7 = JOIN r6 WITH Instruction::IndexedInstruction#ff AS R ON FIRST 2 OUTPUT r6.<0>, r6.<2>
    186891  ~2%     {2} r8 = JOIN r7 WITH Instruction::SideEffectInstruction::getPrimaryInstruction_dispred#3#ff AS R ON FIRST 1 OUTPUT R.<1>, r7.<1>
    183201  ~3%     {2} r9 = JOIN r8 WITH Instruction::CallInstruction::getThisArgumentOperand_dispred#ff AS R ON FIRST 1 OUTPUT R.<1>, r8.<1>
    183201  ~0%     {2} r10 = JOIN r9 WITH Operand::Operand::getDef_dispred#3#ff AS R ON FIRST 1 OUTPUT R.<1>, r9.<1>
    175449  ~8%     {2} r11 = JOIN r10 WITH Instruction::Instruction::getUnconvertedResultExpression_dispred#ff AS R ON FIRST 1 OUTPUT R.<1>, r10.<1>
    374185  ~3%     {2} r12 = r5 \/ r11
                    return r12
2020-11-09 09:01:22 +01:00
Rasmus Wriedt Larsen
ed0e4f8425 Python: reasoning about => detecting
Co-authored-by: yoff <lerchedahl@gmail.com>
2020-11-09 09:01:04 +01:00
Erik Krogh Kristensen
3ef5d89e39 update expected output 2020-11-08 23:27:38 +01:00
Erik Krogh Kristensen
17a687b344 testing many possible intersections, instead of a single intersection 2020-11-08 23:24:36 +01:00
Erik Krogh Kristensen
34fd0d89f5 finding the minimum that is not an FP - instead of finding the minimum and then checking if it was an FP. And detecting more FPs by finding when a witness pass through the accept state 2020-11-08 23:24:27 +01:00
Erik Krogh Kristensen
ac514b1739 remove false positives where the analysis would wrongly conclude that the accept state could not be reached 2020-11-08 23:24:03 +01:00
Erik Krogh Kristensen
5f199e8b1a improve performance by removing bindingset[char] 2020-11-08 23:24:03 +01:00
Erik Krogh Kristensen
d038e9c658 small performance improvements 2020-11-08 23:24:03 +01:00
Erik Krogh Kristensen
a5e75f53ff add support for escape char classes inside char classes 2020-11-08 23:22:49 +01:00
Erik Krogh Kristensen
0063cb140c add support for \W, \S, \D 2020-11-08 23:16:56 +01:00
Erik Krogh Kristensen
2dd8b6ffef support \f and \v in the \s class 2020-11-08 23:16:56 +01:00
Erik Krogh Kristensen
68fe03060d support \d \s and \w in ReDoS.ql 2020-11-08 23:16:56 +01:00
Erik Krogh Kristensen
fa54ad1a5e refactor character class implementation in ReDoS.ql - preparing support for RegExpCharacterClassEscape 2020-11-08 23:16:55 +01:00
Erik Krogh Kristensen
a09ffd5cda expand getAOverlapBetweenCharacterClasses to support overlap between more char classes 2020-11-08 23:16:37 +01:00
Erik Krogh Kristensen
4ede04f4d1 improve performance by pruning based on shared root 2020-11-08 23:16:37 +01:00
Erik Krogh Kristensen
82252c0f1c detect redos between charclass and inverted charclass 2020-11-08 23:16:34 +01:00
Geoffrey White
62a8427d37 C++: Change note. 2020-11-06 15:55:31 +00:00
Geoffrey White
74a4f5887b C++: Remove implementation import from printf.qll. 2020-11-06 15:44:11 +00:00
Geoffrey White
e065466180 C++: Give Snprintf a proper interface. 2020-11-06 15:38:57 +00:00
luchua-bc
d568eb635f Update qldoc 2020-11-06 15:33:26 +00:00
Geoffrey White
0790fb6324 Update cpp/change-notes/2020-11-02-unused-local-variable.md
Co-authored-by: Jonas Jensen <jbj@github.com>
2020-11-06 14:42:48 +00:00
yoff
45317bcec9 Update python/ql/test/library-tests/PointsTo/new/code/w_function_values.py
Co-authored-by: Rasmus Wriedt Larsen <rasmuswriedtlarsen@gmail.com>
2020-11-06 15:03:20 +01:00
Rasmus Wriedt Larsen
9ebe59d393 Python: Move UnsafeDeserialization configuration to own file 2020-11-06 14:27:37 +01:00
luchua-bc
450ff26694 Convert the query to a library 2020-11-06 13:25:00 +00:00
Rasmus Wriedt Larsen
d38c48d2c8 Python: Move ReflectedXSS configuration to own file 2020-11-06 14:24:31 +01:00
Rasmus Wriedt Larsen
1897a0d59a Python: Move PathInjection configuration to own file
This one required a bit more thought, but ended up pretty nicely. Had to write
some QLDoc, but I think it turned out OK.
2020-11-06 14:21:23 +01:00
Rasmus Wriedt Larsen
0c6bd8401a Python: Move SqlInjection configuration to own file 2020-11-06 14:09:46 +01:00
Rasmus Wriedt Larsen
6299b73a46 Python: Move CommandInjection configuration to own file 2020-11-06 14:07:06 +01:00
Rasmus Wriedt Larsen
7c04c59456 Python: Move CodeInjection configuration to own file
This makes it easy to extend the sources/sinks of the configuration and re-run
the query from the query console on LGTM.com.

File location in `semmle.<lang>.security.dataflow.<QueryName>.qll` is matching
what we currently do in other languages (JS and C# sampled).

I did not follow the pattern in other languages for wrapping all the code in a
`module CodeInjection`, since I didn't understand the value in doing so -- I
would like confirmation from the other teams if we _should_ actually do that,
before merging.
2020-11-06 13:58:06 +01:00
Rasmus Lerchedahl Petersen
fe186bf854 Python: Add test 2020-11-06 13:30:11 +01:00
Alvaro Muñoz
9db340c9ca add some improvements to the bean validation query 2020-11-06 13:08:45 +01:00
Asger Feldthaus
acb30e73bc JS: More precise handling of default import fallback 2020-11-06 12:04:41 +00:00
Rasmus Lerchedahl Petersen
64b9e9150e Python: only show results in extracted files 2020-11-06 12:01:16 +01:00
Erik Krogh Kristensen
16473fc2a4 matching a inverted char class with a char 2020-11-06 10:18:57 +01:00
Erik Krogh Kristensen
804aaf36f0 support inverted char class and dot 2020-11-06 10:18:57 +01:00
Erik Krogh Kristensen
64d680e2d3 support that an inverted char class can intersect with itself 2020-11-06 10:18:57 +01:00
Erik Krogh Kristensen
321cf09bd8 add redos support for the simplest possible inverted char class 2020-11-06 10:18:57 +01:00
Erik Krogh Kristensen
d04f3df1cd remove rendundant check 2020-11-06 10:18:57 +01:00
Asger Feldthaus
1e45bc75c4 JS: Add change note in new format 2020-11-06 09:14:03 +00:00
Asger Feldthaus
24714c41be JS: Update test output after rebase 2020-11-06 09:14:03 +00:00
Asger Feldthaus
9e25bbc4ed JS: Add support for moment-timezone as well 2020-11-06 09:13:52 +00:00
Asger Feldthaus
7bf21d80b2 JS: Shift line numbers in test file 2020-11-06 09:13:52 +00:00
Asger Feldthaus
9418c6c8fe JS: Add support for dateformat package 2020-11-06 09:13:52 +00:00
CodeQL CI
9f2eb84f2b Merge pull request #4624 from erik-krogh/concatFix
Approved by asgerf
2020-11-06 09:11:41 +00:00
Asger Feldthaus
39c8226fba JS: Autoformat 2020-11-06 09:06:20 +00:00
Asger Feldthaus
790526b529 JS: Some fixes and address review comments 2020-11-06 09:06:20 +00:00
Asger Feldthaus
8a3fba05e9 JS: Add steps through date-formatting functions 2020-11-06 09:06:18 +00:00
Anders Schack-Mulligen
cb77e460ae Merge pull request #4600 from porcupineyhairs/urirefactor
Java : Refactor all instances of `java.net.URI` into TypeUri
2020-11-06 09:35:09 +01:00
Asger Feldthaus
d07e69e529 JS: Improve handling of destructuring export declaration 2020-11-05 23:51:44 +00:00
CodeQL CI
a908e5938e Merge pull request #4574 from erik-krogh/jsdom
Approved by asgerf
2020-11-05 22:13:39 +00:00
Erik Krogh Kristensen
9137759d7c calculate the size of the concatenation before doing the actual concatenation in Expr.qll 2020-11-05 22:55:52 +01:00
Tom Hvitved
a3894be1c5 Merge pull request #4607 from hvitved/csharp/msbuild-mono-no-shared-compilation
C#: Disable shared compilation when building with Mono+MSBuild
2020-11-05 19:56:25 +01:00
Geoffrey White
c9f846e0d2 C++: Give Iterator a proper interface. 2020-11-05 16:43:50 +00:00
Geoffrey White
b5326b3937 C++: Give OperatorNewAllocationFunction, OperatorDeleteAllocationFunction proper interfaces. 2020-11-05 16:43:49 +00:00
Geoffrey White
7f54379a0c C++: Make more function models private (except a few that are used outside the library). 2020-11-05 16:43:42 +00:00
Erik Krogh Kristensen
e124ba66b4 moving jsdom sink to js/xss 2020-11-05 16:10:33 +01:00
Tom Hvitved
10ab3304c1 Merge pull request #4575 from hvitved/csharp/cfg/post-dominance
C#: Restrict post-dominance to normal execution
2020-11-05 15:31:13 +01:00
Anders Schack-Mulligen
45d117b68e Merge pull request #4603 from pwntester/new_deser_sink
New UnsafeDeserialization sink and improvements to SnakeYaml sink
2020-11-05 13:09:15 +01:00
Rasmus Lerchedahl Petersen
6cecd3ba83 Python: Move and rename query 2020-11-05 11:49:39 +01:00
Alvaro Muñoz
f103955f38 change qldoc formating according to LSP suggestion 2020-11-05 11:48:26 +01:00
CodeQL CI
89a808cafe Merge pull request #4552 from erik-krogh/tsImport
Approved by asgerf
2020-11-05 09:23:58 +00:00
CodeQL CI
b55f18bffd Merge pull request #4549 from erik-krogh/pruneReturn
Approved by asgerf
2020-11-05 09:13:21 +00:00
CodeQL CI
c85f817cee Merge pull request #4579 from erik-krogh/redos
Approved by asgerf
2020-11-05 08:38:44 +00:00
Robert Marsh
2f204869e7 Merge pull request #4604 from criemen/ir-block-sort-order
C++, C# IR: Stabilize sort order for basic blocks.
2020-11-04 18:22:23 -05:00
Erik Krogh Kristensen
342b6a4f2d Update javascript/ql/src/semmle/javascript/security/performance/SuperlinearBackTracking.qll
Co-authored-by: Asger F <asgerf@github.com>
2020-11-04 22:37:56 +01:00
Tom Hvitved
1ba9e29a40 C#: Precise data flow for EntityFramework(Core) 2020-11-04 19:48:03 +01:00
Alvaro Muñoz
302062b670 Merge branch 'new_deser_sink' of https://github.com/pwntester/ql into new_deser_sink 2020-11-04 18:58:57 +01:00
Alvaro Muñoz
6fef63306e add qldoc 2020-11-04 18:58:41 +01:00
Erik Krogh Kristensen
e16fa0668a update expected output 2020-11-04 18:24:31 +01:00
Asger Feldthaus
5eb3067f58 JS: Add test case 2020-11-04 16:50:23 +00:00
Geoffrey White
48628fa195 Merge pull request #4589 from criemen/model-vector-emplace
C++: Model std::vector emplace and emplace_back()
2020-11-04 16:09:31 +00:00
Tom Hvitved
b5063bbcb5 Merge pull request #4495 from hvitved/csharp/dataflow/summaries
C#: Shared interface/implementation for flow summaries
2020-11-04 17:02:19 +01:00
Cornelius Riemenschneider
78d885ee7d C++: Accept test output. 2020-11-04 16:45:07 +01:00
Cornelius Riemenschneider
44d6584fa2 C++, C#: Auto-format. 2020-11-04 16:26:56 +01:00
Cornelius Riemenschneider
a13947424a C++, C# IR: Stabilize sort order for basic blocks. 2020-11-04 16:26:56 +01:00
Cornelius Riemenschneider
e7e5754270 C++: Add taint model for std::vector::emplace/_back. 2020-11-04 16:20:01 +01:00
Erik Krogh Kristensen
03c46c9be0 autoformat 2020-11-04 16:18:24 +01:00
yoff
79fcf598f3 Merge pull request #4608 from RasmusWL/patch-1
Python: Remove unnecessary cached annotation from adjacentRefUse
2020-11-04 16:08:30 +01:00
Cornelius Riemenschneider
62a02cde73 C++: Add test to show missing taint steps for std::vector::emplace/_back 2020-11-04 16:07:15 +01:00
Cornelius Riemenschneider
081ab1e2bb C++: Add std::vector::emplace/_back to our STL model. 2020-11-04 16:07:08 +01:00
Rasmus Lerchedahl Petersen
38b2bb2828 Python: Add testfile with regressions 2020-11-04 15:55:59 +01:00
Ian Lynagh
6ff939db5c Merge pull request #4432 from dbartol/dbartol/temporaries/work
C++: Represent temporary object initialization in AST and IR
2020-11-04 14:38:45 +00:00
Rasmus Lerchedahl Petersen
6df3b8d524 Python: Update query and expectation 2020-11-04 15:17:38 +01:00
Rasmus Wriedt Larsen
31247739d7 Python: Remove unnecessary cached annotation from adjacentRefUse
As discussed in https://github.com/github/codeql/pull/4544#pullrequestreview-516575676
2020-11-04 15:16:08 +01:00
Rasmus Lerchedahl Petersen
9baa7b73da Merge branch 'main' of github.com:github/codeql into SharedDataflow_PointsToImpliesDataflow 2020-11-04 15:05:59 +01:00
yoff
62cb4ec974 Merge pull request #4605 from RasmusWL/python-fix-django-response-modeling
Python: fix django response modeling
2020-11-04 15:00:52 +01:00
Tom Hvitved
131a05563a C#: Disable shared compilation when building with Mono+MSBuild 2020-11-04 14:16:25 +01:00
Porcupiney Hairs
0a028dcb47 Java : Refactor all instances of java.net.URI into TypeUri 2020-11-04 18:23:26 +05:30
Rasmus Wriedt Larsen
5cf8285717 Python: Fix default mimetype for django FileResponse 2020-11-04 12:28:51 +01:00
Rasmus Wriedt Larsen
826aedeb85 Python: Remove resolved TODO 2020-11-04 12:17:31 +01:00
Rasmus Wriedt Larsen
353505ec6c Python: Handle content of Django redirects correctly 2020-11-04 12:10:58 +01:00
Taus
180373c41d Merge pull request #4597 from yoff/python-fix-ql-doc
Python: Fix ql doc
2020-11-04 11:37:32 +01:00
Rasmus Wriedt Larsen
92dc7dc2f3 Python: Use mimetype instead of content-type in django modeling
This enables the XSS query to actually find results from django responses.
2020-11-04 11:34:20 +01:00
Alvaro Muñoz
aa7b87aa33 Update java/change-notes/2020-11-04-commonslang-unsafe-deserialization-sinks.md
Co-authored-by: Anders Schack-Mulligen <aschackmull@users.noreply.github.com>
2020-11-04 10:58:27 +01:00
Alvaro Muñoz
b284141a16 Merge branch 'new_deser_sink' of https://github.com/pwntester/ql into new_deser_sink 2020-11-04 10:51:07 +01:00
Alvaro Muñoz
436563d914 ChangeNote for new unsafe deserialization sinks 2020-11-04 10:50:50 +01:00
Anders Schack-Mulligen
22b4df0f3c Merge pull request #4512 from luchua-bc/sensitive-broadcast
Java: Sensitive broadcast
2020-11-04 10:47:48 +01:00
Alvaro Muñoz
6f78b725e6 Apply suggestions from code review
Co-authored-by: Anders Schack-Mulligen <aschackmull@users.noreply.github.com>
2020-11-04 10:43:37 +01:00
Anders Schack-Mulligen
26495225e0 Update java/ql/src/experimental/Security/CWE/CWE-927/SensitiveBroadcast.qhelp
Co-authored-by: Marcono1234 <Marcono1234@users.noreply.github.com>
2020-11-04 10:05:55 +01:00
Tom Hvitved
c5abf29dfc C#: Update flow-summary test 2020-11-03 20:28:42 +01:00
Dave Bartolomeo
4cc9110dbd Fix test expectation 2020-11-03 13:39:33 -05:00
Dave Bartolomeo
f0b9794907 Merge remote-tracking branch 'upstream/main' into work 2020-11-03 11:33:44 -05:00
Anders Schack-Mulligen
92494441a7 Merge pull request #4554 from aschackmull/dataflow/reverse-partial
Dataflow: Add support reverse partial flow exploration.
2020-11-03 15:34:30 +01:00
Tom Hvitved
438b8dd273 C#: Fix typos 2020-11-03 14:57:07 +01:00
Erik Krogh Kristensen
b02004430c prune results that end with newline, where the input cannot contain newlines 2020-11-03 14:48:39 +01:00
Erik Krogh Kristensen
120faf9d1a add a code injection sink for JSDOM when "runScripts" is set to "dangerously" 2020-11-03 14:29:00 +01:00
Tom Hvitved
f4d1d73bcd C#: Shared interface/implementation for flow summaries 2020-11-03 13:47:28 +01:00
luchua-bc
f8fd2ea821 Add qldoc and autoformat query 2020-11-03 12:23:40 +00:00
Rasmus Lerchedahl Petersen
1023b239e4 Python: Simplify doc 2020-11-03 12:10:00 +01:00
yoff
d6a33a1253 Apply suggestions from code review
Co-authored-by: Taus <tausbn@github.com>
Co-authored-by: Rasmus Wriedt Larsen <rasmuswriedtlarsen@gmail.com>
2020-11-03 12:04:43 +01:00
Rasmus Lerchedahl Petersen
b71ea40dbd Python: QL doc for Werkzeug 2020-11-03 11:44:48 +01:00
Rasmus Lerchedahl Petersen
1773cc3a38 Python: QL doc for MySQLdb 2020-11-03 11:39:28 +01:00
Rasmus Lerchedahl Petersen
01783acca6 Python: QL doc for RemoteFlowSources 2020-11-03 11:37:34 +01:00
Rasmus Lerchedahl Petersen
f44cbf4b6c Python: QL doc for TypeTracker 2020-11-03 11:32:57 +01:00
Rasmus Lerchedahl Petersen
50eb51b6fe Python: QL doc for StepSummary 2020-11-03 11:30:52 +01:00
Rasmus Lerchedahl Petersen
6103dbcfff Python: QL doc for Node 2020-11-03 11:13:58 +01:00
Rasmus Lerchedahl Petersen
2bb1917733 Python: QlDoc for content 2020-11-03 11:10:33 +01:00
Anders Schack-Mulligen
2971784f9c Dataflow: Add missing qldoc and sync. 2020-11-03 09:21:48 +01:00
Anders Schack-Mulligen
7eb64aa998 Dataflow: Code review fixes. 2020-11-03 09:16:20 +01:00
Anders Schack-Mulligen
1ae76a80aa Dataflow: Fix qldoc. 2020-11-03 09:16:20 +01:00
Anders Schack-Mulligen
d5be4d7b92 Dataflow: Add support reverse partial flow exploration. 2020-11-03 09:16:19 +01:00
Dave Bartolomeo
0d1fbd1bdc Fix annotations 2020-11-02 18:03:40 -05:00
Dave Bartolomeo
e9d1f0dacf Merge remote-tracking branch 'upstream/main' into work 2020-11-02 17:31:15 -05:00
Robert Marsh
0e4d69709f C++/C#: autoformat 2020-11-02 14:00:53 -08:00
Robert Marsh
f917cf826f C++: accept test output 2020-11-02 13:59:23 -08:00
Erik Krogh Kristensen
e6e4a485c8 add JSDOM.fromUrl() as a request forgery sink 2020-11-02 17:05:56 +01:00
Geoffrey White
6629696e8a C++: Change note. 2020-11-02 15:46:38 +00:00
Geoffrey White
e9bde9b711 C++: Work around issue with ConditionalDeclExpr. 2020-11-02 15:46:38 +00:00
Geoffrey White
79bda0055c C++: Work around issue with 'Unknown literal'. 2020-11-02 15:46:36 +00:00
Geoffrey White
35f4646ee0 C++: Add test cases for UnusedLocals. 2020-11-02 10:40:42 +00:00
luchua-bc
c89ebeeb5e Text changes 2020-11-01 00:39:00 +00:00
Dave Bartolomeo
69dee154f3 Fix PR feedback 2020-10-31 09:03:51 -04:00
Dave Bartolomeo
be180aac25 Fixup after merge 2020-10-30 12:52:58 -04:00
Dave Bartolomeo
ec398b2a67 Merge remote-tracking branch 'upstream/main' into work 2020-10-30 12:36:33 -04:00
Dave Bartolomeo
42373417e2 Merge from main 2020-10-30 12:02:56 -04:00
Mathias Vorreiter Pedersen
177f94368e C++: Respond to review comments and accept test changes. 2020-10-30 15:59:39 +01:00
Erik Krogh Kristensen
39028f62a3 add test for outDir 2020-10-30 10:37:10 +01:00
Erik Krogh Kristensen
ebc4856456 detect more expensive regexps in js/polynomial-redos 2020-10-30 09:52:13 +01:00
Tom Hvitved
6723e5b31c C#: Restrict post-dominance to normal execution 2020-10-30 09:14:12 +01:00
Tom Hvitved
ade8ff9593 C#: Add more CFG tests 2020-10-30 09:14:12 +01:00
Robert Marsh
7e8770d731 C#: Remove reference to InitializeNonLocal in IR 2020-10-29 16:08:23 -07:00
Robert Marsh
5dfe04a7a7 C++: Respond to PR comments 2020-10-29 16:00:08 -07:00
luchua-bc
5a6339c1af Remove userid from the regex 2020-10-29 15:46:05 +00:00
luchua-bc
90d11812be Update the regex to be the original one 2020-10-29 13:04:15 +00:00
Mathias Vorreiter Pedersen
f3f9a044e0 C++: Accept more tests. 2020-10-29 13:55:45 +01:00
Mathias Vorreiter Pedersen
835d3fc2b7 C++: Accept tests 2020-10-29 09:27:03 +01:00
Mathias Vorreiter Pedersen
43a231f0a5 C++: Store steps now go from operands to instructions, and read steps now go from instructions and operands. There are a couple of read steps that still target instructions because I couldn't decide on an operand to target. 2020-10-29 09:26:51 +01:00
Dave Bartolomeo
c49e33f9cd Fixup after merge 2020-10-28 16:59:13 -04:00
luchua-bc
908d659906 Minor updates 2020-10-28 20:23:22 +00:00
Dave Bartolomeo
7a2c59c194 Merge from main 2020-10-28 15:35:46 -04:00
Dave Bartolomeo
5a6cd4aca9 Fix test expectations for new nodes and edges in path queries 2020-10-28 14:47:42 -04:00
Alvaro Muñoz
a57308a519 Fix SnakeYaml query to account for Yaml subclasses and compose methods 2020-10-28 14:52:14 +01:00
Alvaro Muñoz
c28856d3dc remove wicket taintstep from TaintTrackingUtil 2020-10-28 14:51:44 +01:00
luchua-bc
99c79f4aa3 Enhance the dataflow sink and update test cases 2020-10-28 03:07:01 +00:00
Robert Marsh
7d7b0eaa7b C++: accept test changes
The conflation-related changes result from aliased accesses for which a
precise Phi node is generated.
2020-10-27 09:33:28 -07:00
Alvaro Muñoz
a4a91eb1d2 new deserialization sink 2020-10-27 14:24:17 +01:00
Erik Krogh Kristensen
33465dbe6b refactor parameterPropRead and reachesReturn to get a slight performance improvement 2020-10-26 16:49:49 +01:00
luchua-bc
07830aae05 Fix typo 2020-10-25 22:34:15 +00:00
Erik Krogh Kristensen
0b41a59dbf add support for imports into "outDir" from tsconfig.json 2020-10-25 22:51:21 +01:00
luchua-bc
d9c140dc6c Enhance the query to use sanitizer and null/empty array flow 2020-10-25 15:33:09 +00:00
Dave Bartolomeo
3fce971f2d Fix taint propagation to qualifier objects and update test expectations 2020-10-23 17:48:37 -04:00
Dave Bartolomeo
86668058dc Avoid ODR violation in test code 2020-10-23 17:45:01 -04:00
Dave Bartolomeo
4d2f658ece Don't treat allocator argument as a string input 2020-10-23 17:44:07 -04:00
Dave Bartolomeo
1e96404ee0 Revert bad changes to basic_string 2020-10-23 13:46:27 -04:00
Dave Bartolomeo
35abcae5d3 Fix formatting 2020-10-23 13:43:29 -04:00
Dave Bartolomeo
bace0dca6d Handle more cases that require synthesizing temporary objects
- Parens around qualifier expressions
- Inheritance conversions involving class prvalues
2020-10-23 12:04:09 -04:00
Dave Bartolomeo
99072483b8 Fix PR feedback 2020-10-22 12:55:40 -04:00
Dave Bartolomeo
b62bda6c3a Fix regression due to primary instructions for side effects not being computed correctly in the presence of synthetic temporary objects. 2020-10-22 12:55:30 -04:00
Dave Bartolomeo
f7eeadadd9 Accept more diffs 2020-10-21 18:37:49 -04:00
Dave Bartolomeo
1de1ab65b7 Merge remote-tracking branch 'upstream/main' into work 2020-10-21 18:22:55 -04:00
Dave Bartolomeo
5259f86e32 Accept diff (needs further investigation, though) 2020-10-21 18:06:34 -04:00
Robert Marsh
1a365d2098 C++: remove InitializeNonLocalInstruction from IR
Instead, have AliasedDefinition initialize read-only nonlocal memory
2020-10-21 12:12:38 -07:00
Dave Bartolomeo
ee18db7b36 Fix IR for member accesses on prvalues
This fixes the IR generation for member accesses where the qualifier is a prvalue that is _not_ the load of a `TemporaryObjectExpr`. We synthesize a temporary variable during IR generation instead. It fits into the IR construction code at the same spot as `TranslatedLoad`, since it's basically the opposite of `TranslatedLoad` (prvalue->glvalue instead of vice versa). Note that array prvalues require special treatment.

This fixes some consistency errors in the `syntax-zoo`. It introduces three new ones in `dataflow-ir-consistency.expected`, but those are along the same lines as tons of existing failures.
2020-10-21 13:32:15 -04:00
luchua-bc
478771ccc5 Fix issues with method signature check 2020-10-21 02:49:53 +00:00
Dave Bartolomeo
98e0ae4865 Add tests for member accesses on temporary objects 2020-10-20 17:35:12 -04:00
Dave Bartolomeo
08af0803ff Add examples to QLDoc comment 2020-10-20 17:34:46 -04:00
Dave Bartolomeo
c739f98f7d Merge remote-tracking branch 'upstream/main' into work 2020-10-20 13:42:42 -04:00
Dave Bartolomeo
8c8daa38ec Update stats 2020-10-20 13:41:52 -04:00
Dave Bartolomeo
4ba281731c Fix IR generation for member access with a prvalue on the RHS
For historical reasons, the extractor marks the temporary object expression used as the qualifier of a member access as a prvalue(load), even though the current C++ standard says that the temporary object materialization results in a glvalue. Added some special handling to ignore the load for both field accesses and member function calls.

This fixes all of the consistency failures in our regular tests, and all of the related failures in `syntax-zoo` other than the ones that deal with pointers-to-member, which aren't really supported yet anyway.
2020-10-20 12:53:47 -04:00
Dave Bartolomeo
735c657326 IR consistency checks for FieldAddress and this arguments that are not actually addresses.
Exposes failures in existing tests. Also added a small test case for `FieldAddress` on a prvalue.
2020-10-20 10:32:28 -04:00
Dave Bartolomeo
7de6415d00 Accept test diffs after merge 2020-10-20 07:40:44 -04:00
Dave Bartolomeo
ade6d10e58 Merge remote-tracking branch 'upstream/main' into work 2020-10-20 07:24:42 -04:00
Dave Bartolomeo
2eaa4a4ecf Merge remote-tracking branch 'upstream/main' into work 2020-10-19 15:19:03 -04:00
Dave Bartolomeo
d0b93df4ec Merge from main 2020-10-19 15:17:19 -04:00
Dave Bartolomeo
2ba1ef9961 Merge remote-tracking branch 'upstream/main' into work 2020-10-19 13:28:59 -04:00
luchua-bc
2c2aab6ffc Sensitive broadcast 2020-10-19 16:16:13 +00:00
Dave Bartolomeo
ece20cdb5e Merge branch 'main' into dbartol/temporaries/work 2020-10-18 13:11:06 -04:00
Dave Bartolomeo
b73cb3a4ce Accept C# IR diffs 2020-10-18 11:11:05 -04:00
Dave Bartolomeo
2f34c78552 Fix formatting 2020-10-18 11:08:42 -04:00
Dave Bartolomeo
0b2acff837 Add upgrade script 2020-10-18 08:42:24 -04:00
Dave Bartolomeo
939bfae6e0 Fix formatting 2020-10-18 08:32:08 -04:00
Dave Bartolomeo
5f6ae32f1c Accept test output after merge 2020-10-17 18:16:21 -04:00
Dave Bartolomeo
129e250569 Update test expectations 2020-10-17 17:01:24 -04:00
Dave Bartolomeo
eb9cea48b8 Fix modeling of std::set::emplace 2020-10-17 17:00:29 -04:00
Dave Bartolomeo
40cd96eb1d Merge from main 2020-10-17 15:14:26 -04:00
Dave Bartolomeo
4e0afb0dc3 Print targets of Load and Store instructions in IR dump 2020-10-17 15:01:45 -04:00
Dave Bartolomeo
e4fdf699ad Accept improved test results 2020-10-17 14:33:10 -04:00
Dave Bartolomeo
4814dcf145 Print partial flow info in PrintIRLocalFlow.qll 2020-10-17 14:32:31 -04:00
Dave Bartolomeo
1dae8f62c1 Model copy-ish constructors for std::pair 2020-10-17 11:33:20 -04:00
Dave Bartolomeo
1b53c4684d Fix test expectations due to pair/make_pair fixes 2020-10-17 09:46:18 -04:00
Dave Bartolomeo
686f5aa8ed Handle parameter indirections in make_shared and make_unique 2020-10-17 08:53:55 -04:00
Dave Bartolomeo
7da4eef90d Fix subtle typing issue with std::makr_pair 2020-10-17 08:53:20 -04:00
Dave Bartolomeo
675256acab Accept test diffs from set.cpp (50 new good results!) 2020-10-16 17:14:41 -04:00
Dave Bartolomeo
af799a79da Accept good test diffs 2020-10-16 17:07:46 -04:00
Dave Bartolomeo
cf19fcf4c0 C++: Improve dataflow model for copy/move constructors 2020-10-16 16:55:59 -04:00
Dave Bartolomeo
9afddf0dad Insert a load of the temporary object for arguments passed by value 2020-10-16 12:56:46 -04:00
Dave Bartolomeo
6a9ecf7ba2 Dump static call target for Call instructions 2020-10-16 12:55:30 -04:00
Dave Bartolomeo
14ac9859c1 Remove more ODR violations from test code.
PrintAST now works on `library-tests/dataflow/taint-tests`.
2020-10-16 10:26:42 -04:00
Dave Bartolomeo
3767a52e9a Fix ODR violations in test code 2020-10-15 17:54:48 -04:00
Dave Bartolomeo
ceea5b3ec6 Fix test code that returns reference to local 2020-10-15 13:37:20 -04:00
Dave Bartolomeo
794a6729bc C++: Add ability to dump local dataflow info in IR dumps
This change adds a new module, `PrintIRLocalFlow.qll`, which can be imported into any query that uses both `PrintIR.qll` and the IR dataflow library. The IR dump printed by `PrintIR.qll` will be annotated with information about how each operand and instruction participates in dataflow.

For each operand and instruction, the following propeties are displayed:
- `flow`: Which local operands/instructions have flow to this node, and which local operands/instruction this node has flow to.
- `source`: `true` if this node is a source
- `sink`: `true` if this node is a sink
- `barrier`: Lists which kinds of barrier this node is. Can be zero or more of `full`, `in`, `out`, and `guard`. If the node is a guard barrier, the IR of the guarding instruction is also printed.

We already had a way to print additional properties for instructions and blocks, but not for operands. I added support for operand properties to `IRPropertyProvider`. These are now printed in a curly-brace-enclosed list immediately after the corresponding operand.

When printing flow, instructions are identified by their result ID (e.g., `m128`). Operands are identified by both the result ID of their instruction and their kind (e.g., `r145.left`). For flow from an operand to its use instruction, it just prints `result` at the operand, and prints only the operand kind on the instruction.

Example output:
```
#  344|     m344_34(vector<int, allocator<int>>)                                               = Chi                             : total:m344_20{flow:def->@, @->result}, partial:m344_33{flow:def->@, @->result}
#  344|         flow = total->@, partial->@, +m344_33->@, @->+r347_3, @->v347_7.side_effect, @->m347_9.total, @->m344_20.1
```
The `+` annotations indicate when the flow came from `isAdditionalFlowStep()`, rather than built-in local flow.
2020-10-14 18:02:45 -04:00
Dave Bartolomeo
fba4313457 Merge remote-tracking branch 'upstream/main' into work 2020-10-13 13:07:28 -04:00
Dave Bartolomeo
93f5ae4763 Clean up test formatting and accept new lines in results 2020-10-13 12:57:52 -04:00
Dave Bartolomeo
dfe69d8ada Update taint test to propagate through string constructor 2020-10-13 12:06:34 -04:00
Dave Bartolomeo
e4bfb75f90 C++: Fix pointer flow through temporary objects 2020-10-08 12:24:59 -04:00
Dave Bartolomeo
1e455f08a3 Fix test expectations 2020-10-07 13:14:54 -04:00
Dave Bartolomeo
bf8340f102 Fix test expectations in syntax-zoo 2020-10-07 10:26:02 -04:00
Dave Bartolomeo
22638fdfc7 Merge remote-tracking branch 'upstream/main' into work 2020-10-06 18:33:14 -04:00
Dave Bartolomeo
80b832eb49 Fix test expectations 2020-10-06 18:27:33 -04:00
Dave Bartolomeo
badb11750a AST and IR support for TemporaryObjectExpr 2020-10-05 17:53:35 -04:00
Rasmus Lerchedahl Petersen
8b13a429b7 Python: Address review comments 2020-09-01 14:00:41 +02:00
Rasmus Lerchedahl Petersen
6d23d7fa0e Python: Test that pointsTo implies data flow
Running the test on a larger database gives some interesting results.
2020-09-01 11:56:22 +02:00
935 changed files with 81069 additions and 43796 deletions

View File

@@ -1,57 +0,0 @@
name: Generate CodeQL query help documentation using Sphinx
on:
push:
branches:
- main
- 'rc/**'
- 'lgtm.com'
pull_request:
paths:
- '.github/workflows/generate-query-help-docs.yml'
- 'docs/codeql/query-help/**'
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Clone github/codeql
uses: actions/checkout@v2
with:
path: codeql
- name: Clone github/codeql-go
uses: actions/checkout@v2
with:
repository: 'github/codeql-go'
path: codeql-go
- name: Set up Python 3.8
uses: actions/setup-python@v2
with:
python-version: 3.8
- name: Download CodeQL CLI
uses: dsaltares/fetch-gh-release-asset@aa37ae5c44d3c9820bc12fe675e8670ecd93bd1c
with:
repo: "github/codeql-cli-binaries"
version: "latest"
file: "codeql-linux64.zip"
token: ${{ secrets.GITHUB_TOKEN }}
- name: Unzip CodeQL CLI
run: unzip -d codeql-cli codeql-linux64.zip
- name: Set up query help docs folder
run: |
cp -r codeql/docs/codeql/** .
- name: Query help to markdown
run: |
PATH="$PATH:codeql-cli/codeql" python codeql/docs/codeql/query-help-markdown.py
- name: Run Sphinx for query help
uses: ammaraskar/sphinx-action@8b4f60114d7fd1faeba1a712269168508d4750d2 # v0.4
with:
docs-folder: "query-help/"
pre-build-command: "python -m pip install --upgrade recommonmark"
build-command: "sphinx-build -b dirhtml . _build"
- name: Upload HTML artifacts
uses: actions/upload-artifact@v2
with:
name: query-help-html
path: query-help/_build

View File

@@ -4,34 +4,19 @@ The following changes in version 1.26 affect Python analysis in all applications
## General improvements
## New queries
| **Query** | **Tags** | **Purpose** |
|-----------------------------|-----------|--------------------------------------------------------------------|
## Changes to existing queries
| **Query** | **Expected impact** | **Change** |
|----------------------------|------------------------|------------------------------------------------------------------|
|`py/unsafe-deserialization` | Different results. | The underlying data flow library has been changed. See below for more details. |
|`py/path-injection` | Different results. | The underlying data flow library has been changed. See below for more details. |
|`py/command-line-injection` | Different results. | The underlying data flow library has been changed. See below for more details. |
|`py/reflective-xss` | Different results. | The underlying data flow library has been changed. See below for more details. |
|`py/sql-injection` | Different results. | The underlying data flow library has been changed. See below for more details. |
|`py/code-injection` | Different results. | The underlying data flow library has been changed. See below for more details. |
## Changes to libraries
* Some of the security queries now use the shared data flow library for data flow and taint tracking. This has resulted in an overall more robust and accurate analysis. The libraries mentioned below have been modelled in this new framework. Other libraries (e.g. the web framework `CherryPy`) have not been modelled yet, and this may lead to a temporary loss of results for these frameworks.
* Improved modelling of the following serialization libraries:
- `PyYAML`
- `dill`
- `pickle`
- `marshal`
* Improved modelling of the following web frameworks:
- `Django` (Note that modelling of class-based response handlers is currently incomplete.)
- `Flask`
* Support for Werkzeug `MultiDict`.
* Support for the [Python Database API Specification v2.0 (PEP-249)](https://www.python.org/dev/peps/pep-0249/), including the following libraries:
- `MySQLdb`
- `mysql-connector-python`
- `django.db`
* Improved modelling of the following command execution libraries:
- `Fabric`
- `Invoke`
* Improved modelling of security-related standard library modules, such as `os`, `popen2`, `platform`, and `base64`.
* The original versions of the updated queries have been preserved [here](https://github.com/github/codeql/tree/main/python/ql/src/experimental/Security-old-dataflow).
* Added taint tracking support for string formatting through f-strings.

View File

@@ -358,6 +358,14 @@
"cpp/ql/test/TestUtilities/InlineExpectationsTest.qll",
"python/ql/test/TestUtilities/InlineExpectationsTest.qll"
],
"C++ ExternalAPIs": [
"cpp/ql/src/Security/CWE/CWE-020/ExternalAPIs.qll",
"cpp/ql/src/Security/CWE/CWE-020/ir/ExternalAPIs.qll"
],
"C++ SafeExternalAPIFunction": [
"cpp/ql/src/Security/CWE/CWE-020/SafeExternalAPIFunction.qll",
"cpp/ql/src/Security/CWE/CWE-020/ir/SafeExternalAPIFunction.qll"
],
"XML": [
"cpp/ql/src/semmle/code/cpp/XML.qll",
"csharp/ql/src/semmle/code/csharp/XML.qll",
@@ -409,5 +417,12 @@
"java/ql/src/Metrics/Files/CommentedOutCodeReferences.qhelp",
"javascript/ql/src/Comments/CommentedOutCodeReferences.qhelp",
"python/ql/src/Lexical/CommentedOutCodeReferences.qhelp"
],
"IDE Contextual Queries": [
"cpp/ql/src/IDEContextual.qll",
"csharp/ql/src/IDEContextual.qll",
"java/ql/src/IDEContextual.qll",
"javascript/ql/src/IDEContextual.qll",
"python/ql/src/analysis/IDEContextual.qll"
]
}

View File

@@ -0,0 +1,2 @@
lgtm,codescanning
* Two issues causing the 'Unused local variable' query (`cpp/unused-local-variable`) to produce false positive results have been fixed.

View File

@@ -0,0 +1,3 @@
lgtm,codescanning
* Various classes in `semmle.code.cpp.models.implementations` have been made private. Users should not depend on library implementation details.
* The `OperatorNewAllocationFunction`, `OperatorDeleteDeallocationFunction`, `Iterator` and `Snprintf` classes now have interfaces in `semmle.code.cpp.models.interfaces`.

View File

@@ -0,0 +1,2 @@
lgtm,codescanning
* A new query (`cpp/unsafe-use-of-this`) has been added. The query finds pure virtual function calls whose qualifier is an object under construction.

View File

@@ -0,0 +1,2 @@
lgtm,codescanning
* The queries `cpp/local-variable-hides-global-variable` and `cpp/missing-header-guard` now have severity `recommendation` instead of `warning`.

View File

@@ -9,6 +9,7 @@
+ semmlecode-cpp-queries/Likely Bugs/Conversion/CastArrayPointerArithmetic.ql: /Correctness/Dangerous Conversions
+ semmlecode-cpp-queries/Likely Bugs/Underspecified Functions/MistypedFunctionArguments.ql: /Correctness/Dangerous Conversions
+ semmlecode-cpp-queries/Security/CWE/CWE-253/HResultBooleanConversion.ql: /Correctness/Dangerous Conversions
+ semmlecode-cpp-queries/Likely Bugs/OO/UnsafeUseOfThis.ql: /Correctness/Dangerous Conversions
# Consistent Use
+ semmlecode-cpp-queries/Critical/ReturnValueIgnored.ql: /Correctness/Consistent Use
+ semmlecode-cpp-queries/Likely Bugs/InconsistentCheckReturnNull.ql: /Correctness/Consistent Use

View File

@@ -2,7 +2,7 @@
* @name Local variable hides global variable
* @description A local variable or parameter that hides a global variable of the same name. This may be confusing. Consider renaming one of the variables.
* @kind problem
* @problem.severity warning
* @problem.severity recommendation
* @precision very-high
* @id cpp/local-variable-hides-global-variable
* @tags maintainability

View File

@@ -57,5 +57,12 @@ where
not declarationHasSideEffects(v) and
not exists(AsmStmt s | f = s.getEnclosingFunction()) and
not v.getAnAttribute().getName() = "unused" and
not any(ErrorExpr e).getEnclosingFunction() = f // unextracted expr likely used `v`
not any(ErrorExpr e).getEnclosingFunction() = f and // unextracted expr may use `v`
not exists(
Literal l // this case can be removed when the `myFunction2( [obj](){} );` test case doesn't depend on this exclusion
|
l.getEnclosingFunction() = f and
not exists(l.getValue())
) and
not any(ConditionDeclExpr cde).getEnclosingFunction() = f // this case can be removed when the `if (a = b; a)` test case doesn't depend on this exclusion
select v, "Variable " + v.getName() + " is not used"

View File

@@ -5,8 +5,6 @@
import cpp
import semmle.code.cpp.controlflow.SSA
import semmle.code.cpp.dataflow.DataFlow
import semmle.code.cpp.models.implementations.Allocation
import semmle.code.cpp.models.implementations.Deallocation
/**
* Holds if `alloc` is a use of `malloc` or `new`. `kind` is

View File

@@ -0,0 +1,22 @@
/**
* Provides shared predicates related to contextual queries in the code viewer.
*/
import semmle.files.FileSystem
/**
* Returns the `File` matching the given source file name as encoded by the VS
* Code extension.
*/
cached
File getFileBySourceArchiveName(string name) {
// The name provided for a file in the source archive by the VS Code extension
// has some differences from the absolute path in the database:
// 1. colons are replaced by underscores
// 2. there's a leading slash, even for Windows paths: "C:/foo/bar" ->
// "/C_/foo/bar"
// 3. double slashes in UNC prefixes are replaced with a single slash
// We can handle 2 and 3 together by unconditionally adding a leading slash
// before replacing double slashes.
name = ("/" + result.getAbsolutePath().replaceAll(":", "_")).replaceAll("//", "/")
}

View File

@@ -50,10 +50,12 @@ class SafeTimeGatheringFunction extends Function {
class TimeConversionFunction extends Function {
TimeConversionFunction() {
this.getQualifiedName() =
["FileTimeToSystemTime", "SystemTimeToFileTime", "SystemTimeToTzSpecificLocalTime",
"SystemTimeToTzSpecificLocalTimeEx", "TzSpecificLocalTimeToSystemTime",
"TzSpecificLocalTimeToSystemTimeEx", "RtlLocalTimeToSystemTime",
"RtlTimeToSecondsSince1970", "_mkgmtime"]
[
"FileTimeToSystemTime", "SystemTimeToFileTime", "SystemTimeToTzSpecificLocalTime",
"SystemTimeToTzSpecificLocalTimeEx", "TzSpecificLocalTimeToSystemTime",
"TzSpecificLocalTimeToSystemTimeEx", "RtlLocalTimeToSystemTime",
"RtlTimeToSecondsSince1970", "_mkgmtime"
]
}
}

View File

@@ -0,0 +1,20 @@
class Base {
private:
// pure virtual member function used for initialization of derived classes.
virtual void construct() = 0;
public:
Base() {
// wrong: the virtual table of `Derived` has not been initialized yet. So this
// call will resolve to `Base::construct`, which cannot be called as it is a pure
// virtual function.
construct();
}
};
class Derived : public Base {
int field;
void construct() override {
field = 1;
}
};

View File

@@ -0,0 +1,30 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>This rule finds calls to pure virtual member functions in constructors and destructors. When executing the body of a constructor of class <code>T</code>, the virtual table of <code>T</code> refers to the virtual table of one of <code>T</code>'s base classes. This can produce unexpected behavior, including program abort that can lead to denial of service attacks. The same problem exists during destruction of an object.</p>
</overview>
<recommendation>
<p>Do not rely on virtual dispatch in constructors and destructors. Instead, each class should be responsible for acquiring and releasing its resources. If a base class needs to refer to a derived class during initialization, use the Dynamic Binding During Initialization idiom.</p>
</recommendation>
<example><sample src="UnsafeUseOfThis.cpp" />
</example>
<references>
<li>ISO C++ FAQ: <a href="https://isocpp.org/wiki/faq/strange-inheritance#calling-virtuals-from-ctors">When my base class's constructor calls a virtual function on its this object, why doesn't my derived class's override of that virtual function get invoked?</a>
</li>
<li>SEI CERT C++ Coding Standard <a href="https://wiki.sei.cmu.edu/confluence/display/cplusplus/OOP50-CPP.+Do+not+invoke+virtual+functions+from+constructors+or+destructors">OOP50-CPP. Do not invoke virtual functions from constructors or destructors</a>
</li>
<li>ISO C++ FAQ: <a href="https://isocpp.org/wiki/faq/strange-inheritance#calling-virtuals-from-ctor-idiom">Okay, but is there a way to simulate that behavior as if dynamic binding worked on the this object within my base class's constructor?</a>
</li>
</references></qhelp>

View File

@@ -0,0 +1,212 @@
/**
* @name Unsafe use of this in constructor
* @description A call to a pure virtual function using a 'this'
* pointer of an object that is under construction
* may lead to undefined behavior.
* @kind path-problem
* @id cpp/unsafe-use-of-this
* @problem.severity error
* @precision very-high
* @tags correctness
* language-features
* security
*/
import cpp
// We don't actually use the global value numbering library in this query, but without it we end up
// recomputing the IR.
private import semmle.code.cpp.valuenumbering.GlobalValueNumbering
private import semmle.code.cpp.ir.IR
bindingset[n, result]
int unbind(int n) { result >= n and result <= n }
/** Holds if `p` is the `n`'th parameter of the non-virtual function `f`. */
predicate parameterOf(Parameter p, Function f, int n) {
not f.isVirtual() and f.getParameter(n) = p
}
/**
* Holds if `instr` is the `n`'th argument to a call to the non-virtual function `f`, and
* `init` is the corresponding initiazation instruction that receives the value of `instr` in `f`.
*/
predicate flowIntoParameter(
CallInstruction call, Instruction instr, Function f, int n, InitializeParameterInstruction init
) {
not f.isVirtual() and
call.getPositionalArgument(n) = instr and
f = call.getStaticCallTarget() and
getEnclosingNonVirtualFunctionInitializeParameter(init, f) and
init.getParameter().getIndex() = unbind(n)
}
/**
* Holds if `instr` is an argument to a call to the function `f`, and `init` is the
* corresponding initialization instruction that receives the value of `instr` in `f`.
*/
pragma[noinline]
predicate getPositionalArgumentInitParam(
CallInstruction call, Instruction instr, InitializeParameterInstruction init, Function f
) {
exists(int n |
parameterOf(_, f, n) and
flowIntoParameter(call, instr, f, unbind(n), init)
)
}
/**
* Holds if `instr` is the qualifier to a call to the non-virtual function `f`, and
* `init` is the corresponding initiazation instruction that receives the value of
* `instr` in `f`.
*/
pragma[noinline]
predicate getThisArgumentInitParam(
CallInstruction call, Instruction instr, InitializeParameterInstruction init, Function f
) {
not f.isVirtual() and
call.getStaticCallTarget() = f and
getEnclosingNonVirtualFunctionInitializeParameter(init, f) and
call.getThisArgument() = instr and
init.getIRVariable() instanceof IRThisVariable
}
/** Holds if `instr` is a `this` pointer used by the call instruction `call`. */
predicate isSink(Instruction instr, CallInstruction call) {
exists(PureVirtualFunction func |
call.getStaticCallTarget() = func and
call.getThisArgument() = instr and
// Weed out implicit calls to destructors of a base class
not func instanceof Destructor
)
}
/** Holds if `init` initializes the `this` pointer in class `c`. */
predicate isSource(InitializeParameterInstruction init, string msg, Class c) {
(
exists(Constructor func |
not func instanceof CopyConstructor and
not func instanceof MoveConstructor and
func = init.getEnclosingFunction() and
msg = "construction"
)
or
init.getEnclosingFunction() instanceof Destructor and msg = "destruction"
) and
init.getIRVariable() instanceof IRThisVariable and
init.getEnclosingFunction().getDeclaringType() = c
}
/**
* Holds if `instr` flows to a sink (which is a use of the value of `instr` as a `this` pointer).
*/
predicate flowsToSink(Instruction instr, Instruction sink) {
flowsFromSource(instr) and
(
isSink(instr, _) and instr = sink
or
exists(Instruction mid |
successor(instr, mid) and
flowsToSink(mid, sink)
)
)
}
/** Holds if `instr` flows from a source. */
predicate flowsFromSource(Instruction instr) {
isSource(instr, _, _)
or
exists(Instruction mid |
successor(mid, instr) and
flowsFromSource(mid)
)
}
/** Holds if `f` is the enclosing non-virtual function of `init`. */
predicate getEnclosingNonVirtualFunctionInitializeParameter(
InitializeParameterInstruction init, Function f
) {
not f.isVirtual() and
init.getEnclosingFunction() = f
}
/** Holds if `f` is the enclosing non-virtual function of `init`. */
predicate getEnclosingNonVirtualFunctionInitializeIndirection(
InitializeIndirectionInstruction init, Function f
) {
not f.isVirtual() and
init.getEnclosingFunction() = f
}
/**
* Holds if `instr` is an argument (or argument indirection) to a call, and
* `succ` is the corresponding initialization instruction in the call target.
*/
predicate flowThroughCallable(Instruction instr, Instruction succ) {
// Flow from an argument to a parameter
exists(CallInstruction call, InitializeParameterInstruction init | init = succ |
getPositionalArgumentInitParam(call, instr, init, call.getStaticCallTarget())
or
getThisArgumentInitParam(call, instr, init, call.getStaticCallTarget())
)
or
// Flow from argument indirection to parameter indirection
exists(
CallInstruction call, ReadSideEffectInstruction read, InitializeIndirectionInstruction init
|
init = succ and
read.getPrimaryInstruction() = call and
getEnclosingNonVirtualFunctionInitializeIndirection(init, call.getStaticCallTarget())
|
exists(int n |
read.getSideEffectOperand().getAnyDef() = instr and
read.getIndex() = n and
init.getParameter().getIndex() = unbind(n)
)
or
call.getThisArgument() = instr and
init.getIRVariable() instanceof IRThisVariable
)
}
/** Holds if `instr` flows to `succ`. */
predicate successor(Instruction instr, Instruction succ) {
succ.(CopyInstruction).getSourceValue() = instr or
succ.(CheckedConvertOrNullInstruction).getUnary() = instr or
succ.(ChiInstruction).getTotal() = instr or
succ.(ConvertInstruction).getUnary() = instr or
succ.(InheritanceConversionInstruction).getUnary() = instr or
flowThroughCallable(instr, succ)
}
/**
* Holds if:
* - `source` is an initialization of a `this` pointer of type `sourceClass`, and
* - `sink` is a use of the `this` pointer, and
* - `call` invokes a pure virtual function using `sink` as the `this` pointer, and
* - `msg` is a string describing whether `source` is from a constructor or destructor.
*/
predicate flows(
Instruction source, string msg, Class sourceClass, Instruction sink, CallInstruction call
) {
isSource(source, msg, sourceClass) and
flowsToSink(source, sink) and
isSink(sink, call)
}
query predicate edges(Instruction a, Instruction b) { successor(a, b) and flowsToSink(b, _) }
query predicate nodes(Instruction n, string key, string val) {
flowsToSink(n, _) and
key = "semmle.label" and
val = n.toString()
}
from Instruction source, Instruction sink, CallInstruction call, string msg, Class sourceClass
where
flows(source, msg, sourceClass, sink, call) and
// Only raise an alert if there is no override of the pure virtual function in any base class.
not exists(Class c | c = sourceClass.getABaseClass*() |
c.getAMemberFunction().getAnOverriddenFunction() = call.getStaticCallTarget()
)
select call.getUnconvertedResultExpression(), source, sink,
"Call to pure virtual function during " + msg

View File

@@ -0,0 +1,48 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>Using unsanitized untrusted data in an external API can cause a variety of security issues. This query reports
all external APIs that are used with untrusted data, along with how frequently the API is used, and how many
unique sources of untrusted data flow to this API. This query is designed primarily to help identify which APIs
may be relevant for security analysis of this application.</p>
<p>An external API is defined as a call to a function that is not defined in the source code, and is not
modeled as a taint step in the default taint library. External APIs may be from the C++ standard library,
third party dependencies or from internal dependencies. The query will report the function name, along with
either <code>[param x]</code>, where <code>x</code> indicates the position of the parameter receiving the
untrusted data or <code>[qualifier]</code> indicating the untrusted data is used as the qualifier to the
function call.</p>
</overview>
<recommendation>
<p>For each result:</p>
<ul>
<li>If the result highlights a known sink, no action is required.</li>
<li>If the result highlights an unknown sink for a problem, then add modeling for the sink to the relevant query.</li>
<li>If the result represents a call to an external API which transfers taint, add the appropriate modeling, and
re-run the query to determine what new results have appeared due to this additional modeling.</li>
</ul>
<p>Otherwise, the result is likely uninteresting. Custom versions of this query can extend the <code>SafeExternalAPIFunction</code>
class to exclude known safe external APIs from future analysis.</p>
</recommendation>
<example>
<p>If the query were to return the API <code>fputs [param 1]</code>
then we should first consider whether this a security relevant sink. In this case, this is writing to a <code>FILE*</code>, so we should
consider whether this is an XSS sink. If it is, we should confirm that it is handled by the XSS query.</p>
<p>If the query were to return the API <code>strcat [param 1]</code>, then this should be
reviewed as a possible taint step, because tainted data would flow from the 1st argument to the 0th argument of the call.</p>
<p>Note that both examples are correctly handled by the standard taint tracking library and XSS query.</p>
</example>
<references>
</references>
</qhelp>

View File

@@ -0,0 +1,17 @@
/**
* @name Frequency counts for external APIs that are used with untrusted data
* @description This reports the external APIs that are used with untrusted data, along with how
* frequently the API is called, and how many unique sources of untrusted data flow
* to it.
* @id cpp/count-untrusted-data-external-api
* @kind table
* @tags security external/cwe/cwe-20
*/
import cpp
import ExternalAPIs
from ExternalAPIUsedWithUntrustedData externalAPI
select externalAPI, count(externalAPI.getUntrustedDataNode()) as numberOfUses,
externalAPI.getNumberOfUntrustedSources() as numberOfUntrustedSources order by
numberOfUntrustedSources desc

View File

@@ -0,0 +1,13 @@
#include <cstdio>
void do_get(FILE* request, FILE* response) {
char page[1024];
fgets(page, 1024, request);
char buffer[1024];
strcat(buffer, "The page \"");
strcat(buffer, page);
strcat(buffer, "\" was not found.");
fputs(buffer, response);
}

View File

@@ -0,0 +1,13 @@
#include <cstdio>
void do_get(FILE* request, FILE* response) {
char user_id[1024];
fgets(user_id, 1024, request);
char buffer[1024];
strcat(buffer, "SELECT * FROM user WHERE user_id='");
strcat(buffer, user_id);
strcat(buffer, "'");
// ...
}

View File

@@ -0,0 +1,50 @@
/**
* Definitions for reasoning about untrusted data used in APIs defined outside the
* database.
*/
private import cpp
private import semmle.code.cpp.models.interfaces.DataFlow
private import semmle.code.cpp.models.interfaces.Taint
import ExternalAPIsSpecific
/** A node representing untrusted data being passed to an external API. */
class UntrustedExternalAPIDataNode extends ExternalAPIDataNode {
UntrustedExternalAPIDataNode() { any(UntrustedDataToExternalAPIConfig c).hasFlow(_, this) }
/** Gets a source of untrusted data which is passed to this external API data node. */
DataFlow::Node getAnUntrustedSource() {
any(UntrustedDataToExternalAPIConfig c).hasFlow(result, this)
}
}
private newtype TExternalAPI =
TExternalAPIParameter(Function f, int index) {
exists(UntrustedExternalAPIDataNode n |
f = n.getExternalFunction() and
index = n.getIndex()
)
}
/** An external API which is used with untrusted data. */
class ExternalAPIUsedWithUntrustedData extends TExternalAPI {
/** Gets a possibly untrusted use of this external API. */
UntrustedExternalAPIDataNode getUntrustedDataNode() {
this = TExternalAPIParameter(result.getExternalFunction(), result.getIndex())
}
/** Gets the number of untrusted sources used with this external API. */
int getNumberOfUntrustedSources() {
result = strictcount(getUntrustedDataNode().getAnUntrustedSource())
}
/** Gets a textual representation of this element. */
string toString() {
exists(Function f, int index, string indexString |
if index = -1 then indexString = "qualifier" else indexString = "param " + index
|
this = TExternalAPIParameter(f, index) and
result = f.toString() + " [" + indexString + "]"
)
}
}

View File

@@ -0,0 +1,56 @@
/**
* Provides AST-specific definitions for use in the `ExternalAPI` library.
*/
import semmle.code.cpp.dataflow.TaintTracking
import semmle.code.cpp.models.interfaces.FlowSource
import semmle.code.cpp.models.interfaces.DataFlow
import SafeExternalAPIFunction
/** A node representing untrusted data being passed to an external API. */
class ExternalAPIDataNode extends DataFlow::Node {
Call call;
int i;
ExternalAPIDataNode() {
// Argument to call to a function
(
this.asExpr() = call.getArgument(i)
or
i = -1 and this.asExpr() = call.getQualifier()
) and
exists(Function f |
f = call.getTarget() and
// Defined outside the source archive
not f.hasDefinition() and
// Not already modeled as a dataflow or taint step
not f instanceof DataFlowFunction and
not f instanceof TaintFunction and
// Not a call to a known safe external API
not f instanceof SafeExternalAPIFunction
)
}
/** Gets the called API `Function`. */
Function getExternalFunction() { result = call.getTarget() }
/** Gets the index which is passed untrusted data (where -1 indicates the qualifier). */
int getIndex() { result = i }
/** Gets the description of the function being called. */
string getFunctionDescription() { result = getExternalFunction().toString() }
}
/** A configuration for tracking flow from `RemoteFlowSource`s to `ExternalAPIDataNode`s. */
class UntrustedDataToExternalAPIConfig extends TaintTracking::Configuration {
UntrustedDataToExternalAPIConfig() { this = "UntrustedDataToExternalAPIConfig" }
override predicate isSource(DataFlow::Node source) {
exists(RemoteFlowFunction remoteFlow |
remoteFlow = source.asExpr().(Call).getTarget() and
remoteFlow.hasRemoteFlowSource(_, _)
)
}
override predicate isSink(DataFlow::Node sink) { sink instanceof ExternalAPIDataNode }
}

View File

@@ -0,0 +1,48 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>Using unsanitized untrusted data in an external API can cause a variety of security issues. This query reports
all external APIs that are used with untrusted data, along with how frequently the API is used, and how many
unique sources of untrusted data flow to this API. This query is designed primarily to help identify which APIs
may be relevant for security analysis of this application.</p>
<p>An external API is defined as a call to a function that is not defined in the source code, and is not
modeled as a taint step in the default taint library. External APIs may be from the C++ standard library,
third party dependencies or from internal dependencies. The query will report the function name, along with
either <code>[param x]</code>, where <code>x</code> indicates the position of the parameter receiving the
untrusted data or <code>[qualifier]</code> indicating the untrusted data is used as the qualifier to the
function call.</p>
</overview>
<recommendation>
<p>For each result:</p>
<ul>
<li>If the result highlights a known sink, no action is required.</li>
<li>If the result highlights an unknown sink for a problem, then add modeling for the sink to the relevant query.</li>
<li>If the result represents a call to an external API which transfers taint, add the appropriate modeling, and
re-run the query to determine what new results have appeared due to this additional modeling.</li>
</ul>
<p>Otherwise, the result is likely uninteresting. Custom versions of this query can extend the <code>SafeExternalAPIFunction</code>
class to exclude known safe external APIs from future analysis.</p>
</recommendation>
<example>
<p>If the query were to return the API <code>fputs [param 1]</code>
then we should first consider whether this a security relevant sink. In this case, this is writing to a <code>FILE*</code>, so we should
consider whether this is an XSS sink. If it is, we should confirm that it is handled by the XSS query.</p>
<p>If the query were to return the API <code>strcat [param 1]</code>, then this should be
reviewed as a possible taint step, because tainted data would flow from the 1st argument to the 0th argument of the call.</p>
<p>Note that both examples are correctly handled by the standard taint tracking library and XSS query.</p>
</example>
<references>
</references>
</qhelp>

View File

@@ -0,0 +1,17 @@
/**
* @name Frequency counts for external APIs that are used with untrusted data
* @description This reports the external APIs that are used with untrusted data, along with how
* frequently the API is called, and how many unique sources of untrusted data flow
* to it.
* @id cpp/count-untrusted-data-external-api-ir
* @kind table
* @tags security external/cwe/cwe-20
*/
import cpp
import ir.ExternalAPIs
from ExternalAPIUsedWithUntrustedData externalAPI
select externalAPI, count(externalAPI.getUntrustedDataNode()) as numberOfUses,
externalAPI.getNumberOfUntrustedSources() as numberOfUntrustedSources order by
numberOfUntrustedSources desc

View File

@@ -0,0 +1,59 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>Using unsanitized untrusted data in an external API can cause a variety of security issues. This query reports
external APIs that use untrusted data. The results are not filtered, so you can audit all examples.
The query provides data for security reviews of the application and you can also use it to identify external APIs
that should be modeled as either taint steps, or sinks for specific problems.</p>
<p>An external API is defined as a call to a function that is not defined in the source code, and is not modeled
as a taint step in the default taint library. External APIs may be from the
C++ standard library, third-party dependencies or from internal dependencies. The query reports uses of
untrusted data in either the qualifier or as one of the arguments of external APIs.</p>
</overview>
<recommendation>
<p>For each result:</p>
<ul>
<li>If the result highlights a known sink, confirm that the result is reported by the relevant query, or
that the result is a false positive because this data is sanitized.</li>
<li>If the result highlights an unknown sink for a problem, then add modeling for the sink to the relevant query,
and confirm that the result is either found, or is safe due to appropriate sanitization.</li>
<li>If the result represents a call to an external API that transfers taint, add the appropriate modeling, and
re-run the query to determine what new results have appeared due to this additional modeling.</li>
</ul>
<p>Otherwise, the result is likely uninteresting. Custom versions of this query can extend the <code>SafeExternalAPIFunction</code>
class to exclude known safe external APIs from future analysis.</p>
</recommendation>
<example>
<p>In this first example, input is read from <code>fgets</code> and then ultimately used in a call to the
<code>fputs</code> external API:</p>
<sample src="ExternalAPISinkExample.cpp" />
<p>This is an XSS sink. The XSS query should therefore be reviewed to confirm that this sink is appropriately modeled,
and if it is, to confirm that the query reports this particular result, or that the result is a false positive due to
some existing sanitization.</p>
<p>In this second example, again a request parameter is read from <code>fgets</code>.</p>
<sample src="ExternalAPITaintStepExample.cpp" />
<p>If the query reported the call to <code>strcat</code> on line 9, this would suggest that this external API is
not currently modeled as a taint step in the taint tracking library. The next step would be to model this as a taint step, then
re-run the query to determine what additional results might be found. In this example, it seems likely that <code>buffer</code>
will be executed as an SQL query, potentially leading to an SQL injection vulnerability.</p>
<p>Note that both examples are correctly handled by the standard taint tracking library and XSS query.</p>
</example>
<references>
</references>
</qhelp>

View File

@@ -0,0 +1,21 @@
/**
* @name Untrusted data passed to external API
* @description Data provided remotely is used in this external API without sanitization, which could be a security risk.
* @id cpp/untrusted-data-to-external-api-ir
* @kind path-problem
* @precision low
* @problem.severity error
* @tags security external/cwe/cwe-20
*/
import cpp
import semmle.code.cpp.ir.dataflow.TaintTracking
import ir.ExternalAPIs
import semmle.code.cpp.security.FlowSources
import DataFlow::PathGraph
from UntrustedDataToExternalAPIConfig config, DataFlow::PathNode source, DataFlow::PathNode sink
where config.hasFlowPath(source, sink)
select sink, source, sink,
"Call to " + sink.getNode().(ExternalAPIDataNode).getExternalFunction().toString() +
" with untrusted data from $@.", source, source.getNode().(RemoteFlowSource).getSourceType()

View File

@@ -0,0 +1,24 @@
/**
* Provides a class for modeling external functions that are "safe" from a security perspective.
*/
private import cpp
private import semmle.code.cpp.models.interfaces.SideEffect
/**
* A `Function` that is considered a "safe" external API from a security perspective.
*/
abstract class SafeExternalAPIFunction extends Function { }
/** The default set of "safe" external APIs. */
private class DefaultSafeExternalAPIFunction extends SafeExternalAPIFunction {
DefaultSafeExternalAPIFunction() {
// If a function does not write to any of its arguments, we consider it safe to
// pass untrusted data to it. This means that string functions such as `strcmp`
// and `strlen`, as well as memory functions such as `memcmp`, are considered safe.
exists(SideEffectFunction model | model = this |
model.hasOnlySpecificWriteSideEffects() and
not model.hasSpecificWriteSideEffect(_, _, _)
)
}
}

View File

@@ -0,0 +1,59 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>Using unsanitized untrusted data in an external API can cause a variety of security issues. This query reports
external APIs that use untrusted data. The results are not filtered, so you can audit all examples.
The query provides data for security reviews of the application and you can also use it to identify external APIs
that should be modeled as either taint steps, or sinks for specific problems.</p>
<p>An external API is defined as a call to a function that is not defined in the source code, and is not modeled
as a taint step in the default taint library. External APIs may be from the
C++ standard library, third-party dependencies or from internal dependencies. The query reports uses of
untrusted data in either the qualifier or as one of the arguments of external APIs.</p>
</overview>
<recommendation>
<p>For each result:</p>
<ul>
<li>If the result highlights a known sink, confirm that the result is reported by the relevant query, or
that the result is a false positive because this data is sanitized.</li>
<li>If the result highlights an unknown sink for a problem, then add modeling for the sink to the relevant query,
and confirm that the result is either found, or is safe due to appropriate sanitization.</li>
<li>If the result represents a call to an external API that transfers taint, add the appropriate modeling, and
re-run the query to determine what new results have appeared due to this additional modeling.</li>
</ul>
<p>Otherwise, the result is likely uninteresting. Custom versions of this query can extend the <code>SafeExternalAPIFunction</code>
class to exclude known safe external APIs from future analysis.</p>
</recommendation>
<example>
<p>In this first example, input is read from <code>fgets</code> and then ultimately used in a call to the
<code>fputs</code> external API:</p>
<sample src="ExternalAPISinkExample.cpp" />
<p>This is an XSS sink. The XSS query should therefore be reviewed to confirm that this sink is appropriately modeled,
and if it is, to confirm that the query reports this particular result, or that the result is a false positive due to
some existing sanitization.</p>
<p>In this second example, again a request parameter is read from <code>fgets</code>.</p>
<sample src="ExternalAPITaintStepExample.cpp" />
<p>If the query reported the call to <code>strcat</code> on line 9, this would suggest that this external API is
not currently modeled as a taint step in the taint tracking library. The next step would be to model this as a taint step, then
re-run the query to determine what additional results might be found. In this example, it seems likely that <code>buffer</code>
will be executed as an SQL query, potentially leading to an SQL injection vulnerability.</p>
<p>Note that both examples are correctly handled by the standard taint tracking library and XSS query.</p>
</example>
<references>
</references>
</qhelp>

View File

@@ -0,0 +1,20 @@
/**
* @name Untrusted data passed to external API
* @description Data provided remotely is used in this external API without sanitization, which could be a security risk.
* @id cpp/untrusted-data-to-external-api
* @kind path-problem
* @precision low
* @problem.severity error
* @tags security external/cwe/cwe-20
*/
import cpp
import semmle.code.cpp.dataflow.TaintTracking
import ExternalAPIs
import DataFlow::PathGraph
from UntrustedDataToExternalAPIConfig config, DataFlow::PathNode source, DataFlow::PathNode sink
where config.hasFlowPath(source, sink)
select sink, source, sink,
"Call to " + sink.getNode().(ExternalAPIDataNode).getExternalFunction().toString() +
" with untrusted data from $@.", source, source.toString()

View File

@@ -0,0 +1,50 @@
/**
* Definitions for reasoning about untrusted data used in APIs defined outside the
* database.
*/
private import cpp
private import semmle.code.cpp.models.interfaces.DataFlow
private import semmle.code.cpp.models.interfaces.Taint
import ExternalAPIsSpecific
/** A node representing untrusted data being passed to an external API. */
class UntrustedExternalAPIDataNode extends ExternalAPIDataNode {
UntrustedExternalAPIDataNode() { any(UntrustedDataToExternalAPIConfig c).hasFlow(_, this) }
/** Gets a source of untrusted data which is passed to this external API data node. */
DataFlow::Node getAnUntrustedSource() {
any(UntrustedDataToExternalAPIConfig c).hasFlow(result, this)
}
}
private newtype TExternalAPI =
TExternalAPIParameter(Function f, int index) {
exists(UntrustedExternalAPIDataNode n |
f = n.getExternalFunction() and
index = n.getIndex()
)
}
/** An external API which is used with untrusted data. */
class ExternalAPIUsedWithUntrustedData extends TExternalAPI {
/** Gets a possibly untrusted use of this external API. */
UntrustedExternalAPIDataNode getUntrustedDataNode() {
this = TExternalAPIParameter(result.getExternalFunction(), result.getIndex())
}
/** Gets the number of untrusted sources used with this external API. */
int getNumberOfUntrustedSources() {
result = strictcount(getUntrustedDataNode().getAnUntrustedSource())
}
/** Gets a textual representation of this element. */
string toString() {
exists(Function f, int index, string indexString |
if index = -1 then indexString = "qualifier" else indexString = "param " + index
|
this = TExternalAPIParameter(f, index) and
result = f.toString() + " [" + indexString + "]"
)
}
}

View File

@@ -0,0 +1,51 @@
/**
* Provides IR-specific definitions for use in the `ExternalAPI` library.
*/
import semmle.code.cpp.ir.dataflow.TaintTracking
private import semmle.code.cpp.security.FlowSources
private import semmle.code.cpp.models.interfaces.DataFlow
import SafeExternalAPIFunction
/** A node representing untrusted data being passed to an external API. */
class ExternalAPIDataNode extends DataFlow::Node {
Call call;
int i;
ExternalAPIDataNode() {
// Argument to call to a function
(
this.asExpr() = call.getArgument(i)
or
i = -1 and this.asExpr() = call.getQualifier()
) and
exists(Function f |
f = call.getTarget() and
// Defined outside the source archive
not f.hasDefinition() and
// Not already modeled as a dataflow or taint step
not f instanceof DataFlowFunction and
not f instanceof TaintFunction and
// Not a call to a known safe external API
not f instanceof SafeExternalAPIFunction
)
}
/** Gets the called API `Function`. */
Function getExternalFunction() { result = call.getTarget() }
/** Gets the index which is passed untrusted data (where -1 indicates the qualifier). */
int getIndex() { result = i }
/** Gets the description of the function being called. */
string getFunctionDescription() { result = getExternalFunction().toString() }
}
/** A configuration for tracking flow from `RemoteFlowSource`s to `ExternalAPIDataNode`s. */
class UntrustedDataToExternalAPIConfig extends TaintTracking::Configuration {
UntrustedDataToExternalAPIConfig() { this = "UntrustedDataToExternalAPIConfigIR" }
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
override predicate isSink(DataFlow::Node sink) { sink instanceof ExternalAPIDataNode }
}

View File

@@ -0,0 +1,24 @@
/**
* Provides a class for modeling external functions that are "safe" from a security perspective.
*/
private import cpp
private import semmle.code.cpp.models.interfaces.SideEffect
/**
* A `Function` that is considered a "safe" external API from a security perspective.
*/
abstract class SafeExternalAPIFunction extends Function { }
/** The default set of "safe" external APIs. */
private class DefaultSafeExternalAPIFunction extends SafeExternalAPIFunction {
DefaultSafeExternalAPIFunction() {
// If a function does not write to any of its arguments, we consider it safe to
// pass untrusted data to it. This means that string functions such as `strcmp`
// and `strlen`, as well as memory functions such as `memcmp`, are considered safe.
exists(SideEffectFunction model | model = this |
model.hasOnlySpecificWriteSideEffects() and
not model.hasSpecificWriteSideEffect(_, _, _)
)
}
}

View File

@@ -4,6 +4,7 @@
*/
import cpp
import IDEContextual
/**
* Any element that might be the source or target of a jump-to-definition
@@ -207,11 +208,3 @@ Top definitionOf(Top e, string kind) {
// later on.
strictcount(result.getLocation()) < 10
}
/**
* Returns an appropriately encoded version of a filename `name`
* passed by the VS Code extension in order to coincide with the
* output of `.getFile()` on locatable entities.
*/
cached
File getEncodedFile(string name) { result.getAbsolutePath().replaceAll(":", "_") = name }

View File

@@ -7,7 +7,6 @@ import semmle.code.cpp.dataflow.TaintTracking
import experimental.semmle.code.cpp.security.PrivateData
import semmle.code.cpp.security.FileWrite
import semmle.code.cpp.security.BufferWrite
import semmle.code.cpp.dataflow.TaintTracking
module PrivateCleartextWrite {
/**

View File

@@ -4,7 +4,7 @@
* the file from being included twice). This prevents errors and
* inefficiencies caused by repeated inclusion.
* @kind problem
* @problem.severity warning
* @problem.severity recommendation
* @precision high
* @id cpp/missing-header-guard
* @tags efficiency

View File

@@ -12,5 +12,5 @@ import definitions
external string selectedSourceFile();
from Top e, Top def, string kind
where def = definitionOf(e, kind) and e.getFile() = getEncodedFile(selectedSourceFile())
where def = definitionOf(e, kind) and e.getFile() = getFileBySourceArchiveName(selectedSourceFile())
select e, def, kind

View File

@@ -12,5 +12,6 @@ import definitions
external string selectedSourceFile();
from Top e, Top def, string kind
where def = definitionOf(e, kind) and def.getFile() = getEncodedFile(selectedSourceFile())
where
def = definitionOf(e, kind) and def.getFile() = getFileBySourceArchiveName(selectedSourceFile())
select e, def, kind

View File

@@ -22,6 +22,6 @@ class Cfg extends PrintASTConfiguration {
* Print All functions from the selected file.
*/
override predicate shouldPrintFunction(Function func) {
func.getFile() = getEncodedFile(selectedSourceFile())
func.getFile() = getFileBySourceArchiveName(selectedSourceFile())
}
}

View File

@@ -977,7 +977,12 @@ class ClassTemplateInstantiation extends Class {
* specialization - see `FullClassTemplateSpecialization` and
* `PartialClassTemplateSpecialization`).
*/
abstract class ClassTemplateSpecialization extends Class {
class ClassTemplateSpecialization extends Class {
ClassTemplateSpecialization() {
isFullClassTemplateSpecialization(this) or
isPartialClassTemplateSpecialization(this)
}
/**
* Gets the primary template for the specialization, for example on
* `S<T,int>`, the result is `S<T,U>`.
@@ -997,6 +1002,16 @@ abstract class ClassTemplateSpecialization extends Class {
override string getAPrimaryQlClass() { result = "ClassTemplateSpecialization" }
}
private predicate isFullClassTemplateSpecialization(Class c) {
// This class has template arguments, but none of them involves a template parameter.
exists(c.getATemplateArgument()) and
not exists(Type ta | ta = c.getATemplateArgument() and ta.involvesTemplateParameter()) and
// This class does not have any instantiations.
not exists(c.(TemplateClass).getAnInstantiation()) and
// This class is not an instantiation of a class template.
not c instanceof ClassTemplateInstantiation
}
/**
* A full specialization of a class template. For example `MyTemplateClass<int>`
* in the following code is a `FullClassTemplateSpecialization`:
@@ -1013,19 +1028,31 @@ abstract class ClassTemplateSpecialization extends Class {
* ```
*/
class FullClassTemplateSpecialization extends ClassTemplateSpecialization {
FullClassTemplateSpecialization() {
// This class has template arguments, but none of them involves a template parameter.
exists(getATemplateArgument()) and
not exists(Type ta | ta = getATemplateArgument() and ta.involvesTemplateParameter()) and
// This class does not have any instantiations.
not exists(this.(TemplateClass).getAnInstantiation()) and
// This class is not an instantiation of a class template.
not this instanceof ClassTemplateInstantiation
}
FullClassTemplateSpecialization() { isFullClassTemplateSpecialization(this) }
override string getAPrimaryQlClass() { result = "FullClassTemplateSpecialization" }
}
private predicate isPartialClassTemplateSpecialization(Class c) {
/*
* (a) At least one of this class's template arguments involves a
* template parameter in some respect, for example T, T*, etc.
*
* (b) It is not the case that the n template arguments of this class
* are a set of n distinct template parameters.
*
* template <typename T,U> class X {}; // class template
* template <typename T> class X<T,T> {}; // partial class template specialization
* template <typename T> class X<T,int> {}; // partial class template specialization
* template <typename T> class Y {}; // class template
* template <typename T> class Y<T*> {}; // partial class template specialization
*/
exists(Type ta | ta = c.getATemplateArgument() and ta.involvesTemplateParameter()) and
count(TemplateParameter tp | tp = c.getATemplateArgument()) !=
count(int i | exists(c.getTemplateArgument(i)))
}
/**
* A partial specialization of a class template. For example `MyTemplateClass<int, T>`
* in the following code is a `PartialClassTemplateSpecialization`:
@@ -1042,25 +1069,7 @@ class FullClassTemplateSpecialization extends ClassTemplateSpecialization {
* ```
*/
class PartialClassTemplateSpecialization extends ClassTemplateSpecialization {
PartialClassTemplateSpecialization() {
/*
* (a) At least one of this class's template arguments involves a
* template parameter in some respect, for example T, T*, etc.
*
* (b) It is not the case that the n template arguments of this class
* are a set of n distinct template parameters.
*
* template <typename T,U> class X {}; // class template
* template <typename T> class X<T,T> {}; // partial class template specialization
* template <typename T> class X<T,int> {}; // partial class template specialization
* template <typename T> class Y {}; // class template
* template <typename T> class Y<T*> {}; // partial class template specialization
*/
exists(Type ta | ta = getATemplateArgument() and ta.involvesTemplateParameter()) and
count(TemplateParameter tp | tp = getATemplateArgument()) !=
count(int i | exists(getTemplateArgument(i)))
}
PartialClassTemplateSpecialization() { isPartialClassTemplateSpecialization(this) }
override string getAPrimaryQlClass() { result = "PartialClassTemplateSpecialization" }
}

View File

@@ -65,11 +65,10 @@ class ElementBase extends @element {
* which they belong; for example, `AddExpr` is a primary class, but
* `BinaryOperation` is not.
*
* This predicate always has a result. If no primary class can be
* determined, the result is `"???"`. If multiple primary classes match,
* this predicate can have multiple results.
* This predicate can have multiple results if multiple primary classes match.
* For some elements, this predicate may not have a result.
*/
string getAPrimaryQlClass() { result = "???" }
string getAPrimaryQlClass() { none() }
}
/**

View File

@@ -205,12 +205,21 @@ class Constructor extends MemberFunction {
/**
* A function that defines an implicit conversion.
*/
abstract class ImplicitConversionFunction extends MemberFunction {
class ImplicitConversionFunction extends MemberFunction {
ImplicitConversionFunction() {
// ConversionOperator
functions(underlyingElement(this), _, 4)
or
// ConversionConstructor (deprecated)
strictcount(Parameter p | p = getAParameter() and not p.hasInitializer()) = 1 and
not hasSpecifier("explicit")
}
/** Gets the type this `ImplicitConversionFunction` takes as input. */
abstract Type getSourceType();
Type getSourceType() { none() } // overridden in subclasses
/** Gets the type this `ImplicitConversionFunction` converts to. */
abstract Type getDestType();
Type getDestType() { none() } // overridden in subclasses
}
/**

View File

@@ -60,7 +60,7 @@ private string getTemplateArgumentString(Declaration d, int i) {
/**
* A `Declaration` extended to add methods for generating strings useful only for dumps and debugging.
*/
abstract private class DumpDeclaration extends Declaration {
private class DumpDeclaration extends Declaration {
DumpDeclaration() { shouldPrintDeclaration(this) }
/**
@@ -304,7 +304,7 @@ private class SpecifiedDumpType extends DerivedDumpType, SpecifiedType {
basePrefix = getBaseType().(DumpType).getDeclaratorPrefix() and
if getBaseType().getUnspecifiedType() instanceof RoutineType
then result = basePrefix
else result = basePrefix + " " + getSpecifierString().trim()
else result = basePrefix + " " + getSpecifierString()
)
}
@@ -312,7 +312,7 @@ private class SpecifiedDumpType extends DerivedDumpType, SpecifiedType {
exists(string baseSuffix |
baseSuffix = getBaseType().(DumpType).getDeclaratorSuffixBeforeQualifiers() and
if getBaseType().getUnspecifiedType() instanceof RoutineType
then result = baseSuffix + " " + getSpecifierString().trim()
then result = baseSuffix + " " + getSpecifierString()
else result = baseSuffix
)
}

View File

@@ -91,7 +91,8 @@ private newtype TPrintASTNode =
TDeclarationEntryNode(DeclStmt stmt, DeclarationEntry entry) {
// We create a unique node for each pair of (stmt, entry), to avoid having one node with
// multiple parents due to extractor bug CPP-413.
stmt.getADeclarationEntry() = entry
stmt.getADeclarationEntry() = entry and
shouldPrintFunction(stmt.getEnclosingFunction())
} or
TParametersNode(Function func) { shouldPrintFunction(func) } or
TConstructorInitializersNode(Constructor ctor) {
@@ -234,11 +235,27 @@ class PrintASTNode extends TPrintASTNode {
private Function getEnclosingFunction() { result = getParent*().(FunctionNode).getFunction() }
}
/**
* Class that restricts the elements that we compute `qlClass` for.
*/
private class PrintableElement extends Element {
PrintableElement() {
exists(TASTNode(this))
or
exists(TDeclarationEntryNode(_, this))
or
this instanceof Type
}
pragma[noinline]
string getAPrimaryQlClass0() { result = getAPrimaryQlClass() }
}
/**
* Retrieves the canonical QL class(es) for entity `el`
*/
private string qlClass(ElementBase el) {
result = "[" + concat(el.getAPrimaryQlClass(), ",") + "] "
private string qlClass(PrintableElement el) {
result = "[" + concat(el.getAPrimaryQlClass0(), ",") + "] "
// Alternative implementation -- do not delete. It is useful for QL class discovery.
//result = "["+ concat(el.getAQlClass(), ",") + "] "
}

View File

@@ -577,7 +577,9 @@ class BoolType extends IntegralType {
* unsigned char e, f;
* ```
*/
abstract class CharType extends IntegralType { }
class CharType extends IntegralType {
CharType() { builtintypes(underlyingElement(this), _, [5, 6, 7], _, _, _) }
}
/**
* The C/C++ `char` type (which is distinct from `signed char` and
@@ -1304,14 +1306,16 @@ class SpecifiedType extends DerivedType {
}
/**
* INTERNAL: Do not use.
*
* Gets all the specifiers of this type as a string in a fixed order (the order
* only depends on the specifiers, not on the source program). This is intended
* for debugging queries only and is an expensive operation.
*/
string getSpecifierString() { internalSpecString(this, result, 1) }
string getSpecifierString() { result = concat(this.getASpecifier().getName(), " ") }
override string explain() {
result = this.getSpecifierString() + "{" + this.getBaseType().explain() + "}"
result = this.getSpecifierString() + " {" + this.getBaseType().explain() + "}"
}
override predicate isDeeplyConst() {
@@ -1710,28 +1714,6 @@ class AutoType extends TemplateParameter {
}
}
//
// Internal implementation predicates
//
private predicate allSpecifiers(int i, string s) { s = rank[i](string t | specifiers(_, t) | t) }
private predicate internalSpecString(Type t, string res, int i) {
(
if allSpecifiers(i, t.getASpecifier().getName())
then
exists(string spec, string rest |
allSpecifiers(i, spec) and
res = spec + " " + rest and
internalSpecString(t, rest, i + 1)
)
else (
allSpecifiers(i, _) and internalSpecString(t, res, i + 1)
)
)
or
i = count(Specifier s) + 1 and res = ""
}
private predicate suppressUnusedThis(Type t) { any() }
/** A source code location referring to a type */

View File

@@ -4,8 +4,11 @@
import semmle.files.FileSystem
private class TXMLLocatable =
@xmldtd or @xmlelement or @xmlattribute or @xmlnamespace or @xmlcomment or @xmlcharacters;
/** An XML element that has a location. */
abstract class XMLLocatable extends @xmllocatable {
class XMLLocatable extends @xmllocatable, TXMLLocatable {
/** Gets the source location for this element. */
Location getLocation() { xmllocations(this, result) }
@@ -33,7 +36,7 @@ abstract class XMLLocatable extends @xmllocatable {
}
/** Gets a textual representation of this element. */
abstract string toString();
string toString() { none() } // overridden in subclasses
}
/**
@@ -51,7 +54,7 @@ class XMLParent extends @xmlparent {
* Gets a printable representation of this XML parent.
* (Intended to be overridden in subclasses.)
*/
abstract string getName();
string getName() { none() } // overridden in subclasses
/** Gets the file to which this XML parent belongs. */
XMLFile getFile() { result = this or xmlElements(this, _, _, _, result) }

View File

@@ -6,7 +6,6 @@ import semmle.code.cpp.Type
import semmle.code.cpp.commons.CommonType
import semmle.code.cpp.commons.StringAnalysis
import semmle.code.cpp.models.interfaces.FormattingFunction
import semmle.code.cpp.models.implementations.Printf
class PrintfFormatAttribute extends FormatAttribute {
PrintfFormatAttribute() { getArchetype() = ["printf", "__printf__"] }

View File

@@ -9,12 +9,14 @@ import cpp
class StrcatFunction extends Function {
StrcatFunction() {
getName() =
["strcat", // strcat(dst, src)
"strncat", // strncat(dst, src, max_amount)
"wcscat", // wcscat(dst, src)
"_mbscat", // _mbscat(dst, src)
"wcsncat", // wcsncat(dst, src, max_amount)
"_mbsncat", // _mbsncat(dst, src, max_amount)
"_mbsncat_l"] // _mbsncat_l(dst, src, max_amount, locale)
[
"strcat", // strcat(dst, src)
"strncat", // strncat(dst, src, max_amount)
"wcscat", // wcscat(dst, src)
"_mbscat", // _mbscat(dst, src)
"wcsncat", // wcsncat(dst, src, max_amount)
"_mbsncat", // _mbsncat(dst, src, max_amount)
"_mbsncat_l" // _mbsncat_l(dst, src, max_amount, locale)
]
}
}

View File

@@ -83,6 +83,8 @@ private predicate pointerToPointerStep(Expr pointerIn, Expr pointerOut) {
or
pointerIn.getConversion() = pointerOut.(ParenthesisExpr)
or
pointerIn.getConversion() = pointerOut.(TemporaryObjectExpr)
or
pointerIn = pointerOut.(ConditionalExpr).getThen().getFullyConverted()
or
pointerIn = pointerOut.(ConditionalExpr).getElse().getFullyConverted()
@@ -210,14 +212,9 @@ private predicate addressMayEscapeAt(Expr e) {
private predicate addressMayEscapeMutablyAt(Expr e) {
addressMayEscapeAt(e) and
exists(Type t | t = e.getType().getUnderlyingType() |
exists(PointerType pt |
pt = t
or
pt = t.(SpecifiedType).getBaseType()
|
not pt.getBaseType().isConst()
)
exists(Type t | t = e.getType().stripTopLevelSpecifiers() |
t instanceof PointerType and
not t.(PointerType).getBaseType().isConst()
or
t instanceof ReferenceType and
not t.(ReferenceType).getBaseType().isConst()
@@ -225,6 +222,15 @@ private predicate addressMayEscapeMutablyAt(Expr e) {
// If the address has been cast to an integral type, conservatively assume that it may eventually be cast back to a
// pointer to non-const type.
t instanceof IntegralType
or
// If we go through a temporary object step, we can take a reference to a temporary const pointer
// object, where the pointer doesn't point to a const value
exists(TemporaryObjectExpr temp, PointerType pt |
temp.getConversion() = e.(ReferenceToExpr) and
pt = temp.getType().stripTopLevelSpecifiers()
|
not pt.getBaseType().isConst()
)
)
}
@@ -252,7 +258,7 @@ private predicate addressFromVariableAccess(VariableAccess va, Expr e) {
// `e` could be a pointer that is converted to a reference as the final step,
// meaning that we pass a value that is two dereferences away from referring
// to `va`. This happens, for example, with `void std::vector::push_back(T&&
// value);` when called as `v.push_back(&x)`, for a static variable `x`. It
// value);` when called as `v.push_back(&x)`, for a variable `x`. It
// can also happen when taking a reference to a const pointer to a
// (potentially non-const) value.
exists(Expr pointerValue |

View File

@@ -81,6 +81,8 @@ private predicate pointerToPointerStep(Expr pointerIn, Expr pointerOut) {
or
pointerIn.getConversion() = pointerOut.(ParenthesisExpr)
or
pointerIn.getConversion() = pointerOut.(TemporaryObjectExpr)
or
pointerIn = pointerOut.(ConditionalExpr).getThen().getFullyConverted()
or
pointerIn = pointerOut.(ConditionalExpr).getElse().getFullyConverted()

View File

@@ -112,8 +112,8 @@ abstract class Configuration extends string {
predicate hasFlowToExpr(DataFlowExpr sink) { hasFlowTo(exprNode(sink)) }
/**
* Gets the exploration limit for `hasPartialFlow` measured in approximate
* number of interprocedural steps.
* Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev`
* measured in approximate number of interprocedural steps.
*/
int explorationLimit() { none() }
@@ -123,7 +123,7 @@ abstract class Configuration extends string {
* is restricted to be less than or equal to `explorationLimit()`. This
* predicate completely disregards sink definitions.
*
* This predicate is intended for dataflow exploration and debugging and may
* This predicate is intended for data-flow exploration and debugging and may
* perform poorly if the number of sources is too big and/or the exploration
* limit is set too high without using barriers.
*
@@ -136,6 +136,29 @@ abstract class Configuration extends string {
partialFlow(source, node, this) and
dist = node.getSourceDistance()
}
/**
* Holds if there is a partial data flow path from `node` to `sink`. The
* approximate distance between `node` and the closest sink is `dist` and
* is restricted to be less than or equal to `explorationLimit()`. This
* predicate completely disregards source definitions.
*
* This predicate is intended for data-flow exploration and debugging and may
* perform poorly if the number of sinks is too big and/or the exploration
* limit is set too high without using barriers.
*
* This predicate is disabled (has no results) by default. Override
* `explorationLimit()` with a suitable number to enable this predicate.
*
* To use this in a `path-problem` query, import the module `PartialPathGraph`.
*
* Note that reverse flow has slightly lower precision than the corresponding
* forward flow, as reverse flow disregards type pruning among other features.
*/
final predicate hasPartialFlowRev(PartialPathNode node, PartialPathNode sink, int dist) {
revPartialFlow(node, sink, this) and
dist = node.getSinkDistance()
}
}
/**
@@ -2943,12 +2966,26 @@ private module FlowExploration {
)
}
private predicate interestingCallableSink(DataFlowCallable c, Configuration config) {
exists(Node n | config.isSink(n) and c = n.getEnclosingCallable())
or
exists(DataFlowCallable mid |
interestingCallableSink(mid, config) and callableStep(c, mid, config)
)
}
private newtype TCallableExt =
TCallable(DataFlowCallable c, Configuration config) { interestingCallableSrc(c, config) } or
TCallableSrc()
TCallable(DataFlowCallable c, Configuration config) {
interestingCallableSrc(c, config) or
interestingCallableSink(c, config)
} or
TCallableSrc() or
TCallableSink()
private predicate callableExtSrc(TCallableSrc src) { any() }
private predicate callableExtSink(TCallableSink sink) { any() }
private predicate callableExtStepFwd(TCallableExt ce1, TCallableExt ce2) {
exists(DataFlowCallable c1, DataFlowCallable c2, Configuration config |
callableStep(c1, c2, config) and
@@ -2961,15 +2998,32 @@ private module FlowExploration {
config.isSource(n) and
ce2 = TCallable(n.getEnclosingCallable(), config)
)
or
exists(Node n, Configuration config |
ce2 = TCallableSink() and
config.isSink(n) and
ce1 = TCallable(n.getEnclosingCallable(), config)
)
}
private predicate callableExtStepRev(TCallableExt ce1, TCallableExt ce2) {
callableExtStepFwd(ce2, ce1)
}
private int distSrcExt(TCallableExt c) =
shortestDistances(callableExtSrc/1, callableExtStepFwd/2)(_, c, result)
private int distSinkExt(TCallableExt c) =
shortestDistances(callableExtSink/1, callableExtStepRev/2)(_, c, result)
private int distSrc(DataFlowCallable c, Configuration config) {
result = distSrcExt(TCallable(c, config)) - 1
}
private int distSink(DataFlowCallable c, Configuration config) {
result = distSinkExt(TCallable(c, config)) - 1
}
private newtype TPartialAccessPath =
TPartialNil(DataFlowType t) or
TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] }
@@ -2997,18 +3051,12 @@ private module FlowExploration {
or
exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType())
}
abstract AccessPathFront getFront();
}
private class PartialAccessPathNil extends PartialAccessPath, TPartialNil {
override string toString() {
exists(DataFlowType t | this = TPartialNil(t) | result = concat(": " + ppReprType(t)))
}
override AccessPathFront getFront() {
exists(DataFlowType t | this = TPartialNil(t) | result = TFrontNil(t))
}
}
private class PartialAccessPathCons extends PartialAccessPath, TPartialCons {
@@ -3019,9 +3067,39 @@ private module FlowExploration {
else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]"
)
}
}
override AccessPathFront getFront() {
exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc))
private newtype TRevPartialAccessPath =
TRevPartialNil() or
TRevPartialCons(Content c, int len) { len in [1 .. accessPathLimit()] }
/**
* Conceptually a list of `Content`s, but only the first
* element of the list and its length are tracked.
*/
private class RevPartialAccessPath extends TRevPartialAccessPath {
abstract string toString();
Content getHead() { this = TRevPartialCons(result, _) }
int len() {
this = TRevPartialNil() and result = 0
or
this = TRevPartialCons(_, result)
}
}
private class RevPartialAccessPathNil extends RevPartialAccessPath, TRevPartialNil {
override string toString() { result = "" }
}
private class RevPartialAccessPathCons extends RevPartialAccessPath, TRevPartialCons {
override string toString() {
exists(Content c, int len | this = TRevPartialCons(c, len) |
if len = 1
then result = "[" + c.toString() + "]"
else result = "[" + c.toString() + ", ... (" + len.toString() + ")]"
)
}
}
@@ -3033,8 +3111,16 @@ private module FlowExploration {
TSummaryCtx2None() or
TSummaryCtx2Some(PartialAccessPath ap)
private newtype TRevSummaryCtx1 =
TRevSummaryCtx1None() or
TRevSummaryCtx1Some(ReturnPosition pos)
private newtype TRevSummaryCtx2 =
TRevSummaryCtx2None() or
TRevSummaryCtx2Some(RevPartialAccessPath ap)
private newtype TPartialPathNode =
TPartialPathNodeMk(
TPartialPathNodeFwd(
Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
Configuration config
) {
@@ -3048,6 +3134,23 @@ private module FlowExploration {
or
partialPathNodeMk0(node, cc, sc1, sc2, ap, config) and
distSrc(node.getEnclosingCallable(), config) <= config.explorationLimit()
} or
TPartialPathNodeRev(
Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, RevPartialAccessPath ap,
Configuration config
) {
config.isSink(node) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = TRevPartialNil() and
not fullBarrier(node, config) and
exists(config.explorationLimit())
or
exists(PartialPathNodeRev mid |
revPartialPathStep(mid, node, sc1, sc2, ap, config) and
not fullBarrier(node, config) and
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
)
}
pragma[nomagic]
@@ -3055,7 +3158,7 @@ private module FlowExploration {
Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
Configuration config
) {
exists(PartialPathNode mid |
exists(PartialPathNodeFwd mid |
partialPathStep(mid, node, cc, sc1, sc2, ap, config) and
not fullBarrier(node, config) and
if node instanceof CastingNode
@@ -3107,15 +3210,32 @@ private module FlowExploration {
result = distSrc(this.getNode().getEnclosingCallable(), this.getConfiguration())
}
/**
* Gets the approximate distance to the nearest sink measured in number
* of interprocedural steps.
*/
int getSinkDistance() {
result = distSink(this.getNode().getEnclosingCallable(), this.getConfiguration())
}
private string ppAp() {
exists(string s | s = this.(PartialPathNodePriv).getAp().toString() |
exists(string s |
s = this.(PartialPathNodeFwd).getAp().toString() or
s = this.(PartialPathNodeRev).getAp().toString()
|
if s = "" then result = "" else result = " " + s
)
}
private string ppCtx() {
result = " <" + this.(PartialPathNodePriv).getCallContext().toString() + ">"
result = " <" + this.(PartialPathNodeFwd).getCallContext().toString() + ">"
}
/** Holds if this is a source in a forward-flow path. */
predicate isFwdSource() { this.(PartialPathNodeFwd).isSource() }
/** Holds if this is a sink in a reverse-flow path. */
predicate isRevSink() { this.(PartialPathNodeRev).isSink() }
}
/**
@@ -3126,7 +3246,7 @@ private module FlowExploration {
query predicate edges(PartialPathNode a, PartialPathNode b) { a.getASuccessor() = b }
}
private class PartialPathNodePriv extends PartialPathNode {
private class PartialPathNodeFwd extends PartialPathNode, TPartialPathNodeFwd {
Node node;
CallContext cc;
TSummaryCtx1 sc1;
@@ -3134,7 +3254,7 @@ private module FlowExploration {
PartialAccessPath ap;
Configuration config;
PartialPathNodePriv() { this = TPartialPathNodeMk(node, cc, sc1, sc2, ap, config) }
PartialPathNodeFwd() { this = TPartialPathNodeFwd(node, cc, sc1, sc2, ap, config) }
override Node getNode() { result = node }
@@ -3148,14 +3268,54 @@ private module FlowExploration {
override Configuration getConfiguration() { result = config }
override PartialPathNodePriv getASuccessor() {
override PartialPathNodeFwd getASuccessor() {
partialPathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx1(),
result.getSummaryCtx2(), result.getAp(), result.getConfiguration())
}
predicate isSource() {
config.isSource(node) and
cc instanceof CallContextAny and
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
ap instanceof TPartialNil
}
}
private class PartialPathNodeRev extends PartialPathNode, TPartialPathNodeRev {
Node node;
TRevSummaryCtx1 sc1;
TRevSummaryCtx2 sc2;
RevPartialAccessPath ap;
Configuration config;
PartialPathNodeRev() { this = TPartialPathNodeRev(node, sc1, sc2, ap, config) }
override Node getNode() { result = node }
TRevSummaryCtx1 getSummaryCtx1() { result = sc1 }
TRevSummaryCtx2 getSummaryCtx2() { result = sc2 }
RevPartialAccessPath getAp() { result = ap }
override Configuration getConfiguration() { result = config }
override PartialPathNodeRev getASuccessor() {
revPartialPathStep(result, this.getNode(), this.getSummaryCtx1(), this.getSummaryCtx2(),
this.getAp(), this.getConfiguration())
}
predicate isSink() {
config.isSink(node) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = TRevPartialNil()
}
}
private predicate partialPathStep(
PartialPathNodePriv mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
PartialAccessPath ap, Configuration config
) {
not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and
@@ -3221,8 +3381,7 @@ private module FlowExploration {
pragma[inline]
private predicate partialPathStoreStep(
PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node,
PartialAccessPath ap2
PartialPathNodeFwd mid, PartialAccessPath ap1, TypedContent tc, Node node, PartialAccessPath ap2
) {
exists(Node midNode, DataFlowType contentType |
midNode = mid.getNode() and
@@ -3238,7 +3397,7 @@ private module FlowExploration {
private predicate apConsFwd(
PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config
) {
exists(PartialPathNodePriv mid |
exists(PartialPathNodeFwd mid |
partialPathStoreStep(mid, ap1, tc, _, ap2) and
config = mid.getConfiguration()
)
@@ -3246,7 +3405,7 @@ private module FlowExploration {
pragma[nomagic]
private predicate partialPathReadStep(
PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
PartialPathNodeFwd mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
Configuration config
) {
exists(Node midNode |
@@ -3260,7 +3419,7 @@ private module FlowExploration {
}
private predicate partialPathOutOfCallable0(
PartialPathNodePriv mid, ReturnPosition pos, CallContext innercc, PartialAccessPath ap,
PartialPathNodeFwd mid, ReturnPosition pos, CallContext innercc, PartialAccessPath ap,
Configuration config
) {
pos = getReturnPosition(mid.getNode()) and
@@ -3272,7 +3431,7 @@ private module FlowExploration {
pragma[nomagic]
private predicate partialPathOutOfCallable1(
PartialPathNodePriv mid, DataFlowCall call, ReturnKindExt kind, CallContext cc,
PartialPathNodeFwd mid, DataFlowCall call, ReturnKindExt kind, CallContext cc,
PartialAccessPath ap, Configuration config
) {
exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc |
@@ -3286,7 +3445,7 @@ private module FlowExploration {
}
private predicate partialPathOutOfCallable(
PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
PartialPathNodeFwd mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
) {
exists(ReturnKindExt kind, DataFlowCall call |
partialPathOutOfCallable1(mid, call, kind, cc, ap, config)
@@ -3297,7 +3456,7 @@ private module FlowExploration {
pragma[noinline]
private predicate partialPathIntoArg(
PartialPathNodePriv mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap,
PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap,
Configuration config
) {
exists(ArgumentNode arg |
@@ -3311,7 +3470,7 @@ private module FlowExploration {
pragma[nomagic]
private predicate partialPathIntoCallable0(
PartialPathNodePriv mid, DataFlowCallable callable, int i, CallContext outercc,
PartialPathNodeFwd mid, DataFlowCallable callable, int i, CallContext outercc,
DataFlowCall call, PartialAccessPath ap, Configuration config
) {
partialPathIntoArg(mid, i, outercc, call, ap, config) and
@@ -3319,7 +3478,7 @@ private module FlowExploration {
}
private predicate partialPathIntoCallable(
PartialPathNodePriv mid, ParameterNode p, CallContext outercc, CallContextCall innercc,
PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc,
TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap,
Configuration config
) {
@@ -3340,7 +3499,7 @@ private module FlowExploration {
ReturnKindExt kind, CallContextCall cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
PartialAccessPath ap, Configuration config
) {
exists(PartialPathNodePriv mid, ReturnNodeExt ret |
exists(PartialPathNodeFwd mid, ReturnNodeExt ret |
mid.getNode() = ret and
kind = ret.getKind() and
cc = mid.getCallContext() and
@@ -3353,7 +3512,7 @@ private module FlowExploration {
pragma[noinline]
private predicate partialPathThroughCallable0(
DataFlowCall call, PartialPathNodePriv mid, ReturnKindExt kind, CallContext cc,
DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc,
PartialAccessPath ap, Configuration config
) {
exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 |
@@ -3363,13 +3522,164 @@ private module FlowExploration {
}
private predicate partialPathThroughCallable(
PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
PartialPathNodeFwd mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
) {
exists(DataFlowCall call, ReturnKindExt kind |
partialPathThroughCallable0(call, mid, kind, cc, ap, config) and
out = kind.getAnOutNode(call)
)
}
private predicate revPartialPathStep(
PartialPathNodeRev mid, Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2,
RevPartialAccessPath ap, Configuration config
) {
localFlowStep(node, mid.getNode(), config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
ap = mid.getAp() and
config = mid.getConfiguration()
or
additionalLocalFlowStep(node, mid.getNode(), config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
mid.getAp() instanceof RevPartialAccessPathNil and
ap = TRevPartialNil() and
config = mid.getConfiguration()
or
jumpStep(node, mid.getNode(), config) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = mid.getAp() and
config = mid.getConfiguration()
or
additionalJumpStep(node, mid.getNode(), config) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
mid.getAp() instanceof RevPartialAccessPathNil and
ap = TRevPartialNil() and
config = mid.getConfiguration()
or
revPartialPathReadStep(mid, _, _, node, ap) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
config = mid.getConfiguration()
or
exists(RevPartialAccessPath ap0, Content c |
revPartialPathStoreStep(mid, ap0, c, node, config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
apConsRev(ap, c, ap0, config)
)
or
exists(ParameterNode p |
mid.getNode() = p and
viableParamArg(_, p, node) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = mid.getAp() and
config = mid.getConfiguration()
)
or
exists(ReturnPosition pos |
revPartialPathIntoReturn(mid, pos, sc1, sc2, _, ap, config) and
pos = getReturnPosition(node)
)
or
revPartialPathThroughCallable(mid, node, ap, config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2()
}
pragma[inline]
private predicate revPartialPathReadStep(
PartialPathNodeRev mid, RevPartialAccessPath ap1, Content c, Node node, RevPartialAccessPath ap2
) {
exists(Node midNode |
midNode = mid.getNode() and
ap1 = mid.getAp() and
read(node, c, midNode) and
ap2.getHead() = c and
ap2.len() = unbindInt(ap1.len() + 1)
)
}
pragma[nomagic]
private predicate apConsRev(
RevPartialAccessPath ap1, Content c, RevPartialAccessPath ap2, Configuration config
) {
exists(PartialPathNodeRev mid |
revPartialPathReadStep(mid, ap1, c, _, ap2) and
config = mid.getConfiguration()
)
}
pragma[nomagic]
private predicate revPartialPathStoreStep(
PartialPathNodeRev mid, RevPartialAccessPath ap, Content c, Node node, Configuration config
) {
exists(Node midNode, TypedContent tc |
midNode = mid.getNode() and
ap = mid.getAp() and
store(node, tc, midNode, _) and
ap.getHead() = c and
config = mid.getConfiguration() and
tc.getContent() = c
)
}
pragma[nomagic]
private predicate revPartialPathIntoReturn(
PartialPathNodeRev mid, ReturnPosition pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2,
DataFlowCall call, RevPartialAccessPath ap, Configuration config
) {
exists(Node out |
mid.getNode() = out and
viableReturnPosOut(call, pos, out) and
sc1 = TRevSummaryCtx1Some(pos) and
sc2 = TRevSummaryCtx2Some(ap) and
ap = mid.getAp() and
config = mid.getConfiguration()
)
}
pragma[nomagic]
private predicate revPartialPathFlowsThrough(
int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap,
Configuration config
) {
exists(PartialPathNodeRev mid, ParameterNode p |
mid.getNode() = p and
p.isParameterOf(_, pos) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
ap = mid.getAp() and
config = mid.getConfiguration()
)
}
pragma[nomagic]
private predicate revPartialPathThroughCallable0(
DataFlowCall call, PartialPathNodeRev mid, int pos, RevPartialAccessPath ap,
Configuration config
) {
exists(TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2 |
revPartialPathIntoReturn(mid, _, sc1, sc2, call, _, config) and
revPartialPathFlowsThrough(pos, sc1, sc2, ap, config)
)
}
pragma[nomagic]
private predicate revPartialPathThroughCallable(
PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config
) {
exists(DataFlowCall call, int pos |
revPartialPathThroughCallable0(call, mid, pos, ap, config) and
node.argumentOf(call, pos)
)
}
}
import FlowExploration
@@ -3378,6 +3688,14 @@ private predicate partialFlow(
PartialPathNode source, PartialPathNode node, Configuration configuration
) {
source.getConfiguration() = configuration and
configuration.isSource(source.getNode()) and
source.isFwdSource() and
node = source.getASuccessor+()
}
private predicate revPartialFlow(
PartialPathNode node, PartialPathNode sink, Configuration configuration
) {
sink.getConfiguration() = configuration and
sink.isRevSink() and
node.getASuccessor+() = sink
}

View File

@@ -112,8 +112,8 @@ abstract class Configuration extends string {
predicate hasFlowToExpr(DataFlowExpr sink) { hasFlowTo(exprNode(sink)) }
/**
* Gets the exploration limit for `hasPartialFlow` measured in approximate
* number of interprocedural steps.
* Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev`
* measured in approximate number of interprocedural steps.
*/
int explorationLimit() { none() }
@@ -123,7 +123,7 @@ abstract class Configuration extends string {
* is restricted to be less than or equal to `explorationLimit()`. This
* predicate completely disregards sink definitions.
*
* This predicate is intended for dataflow exploration and debugging and may
* This predicate is intended for data-flow exploration and debugging and may
* perform poorly if the number of sources is too big and/or the exploration
* limit is set too high without using barriers.
*
@@ -136,6 +136,29 @@ abstract class Configuration extends string {
partialFlow(source, node, this) and
dist = node.getSourceDistance()
}
/**
* Holds if there is a partial data flow path from `node` to `sink`. The
* approximate distance between `node` and the closest sink is `dist` and
* is restricted to be less than or equal to `explorationLimit()`. This
* predicate completely disregards source definitions.
*
* This predicate is intended for data-flow exploration and debugging and may
* perform poorly if the number of sinks is too big and/or the exploration
* limit is set too high without using barriers.
*
* This predicate is disabled (has no results) by default. Override
* `explorationLimit()` with a suitable number to enable this predicate.
*
* To use this in a `path-problem` query, import the module `PartialPathGraph`.
*
* Note that reverse flow has slightly lower precision than the corresponding
* forward flow, as reverse flow disregards type pruning among other features.
*/
final predicate hasPartialFlowRev(PartialPathNode node, PartialPathNode sink, int dist) {
revPartialFlow(node, sink, this) and
dist = node.getSinkDistance()
}
}
/**
@@ -2943,12 +2966,26 @@ private module FlowExploration {
)
}
private predicate interestingCallableSink(DataFlowCallable c, Configuration config) {
exists(Node n | config.isSink(n) and c = n.getEnclosingCallable())
or
exists(DataFlowCallable mid |
interestingCallableSink(mid, config) and callableStep(c, mid, config)
)
}
private newtype TCallableExt =
TCallable(DataFlowCallable c, Configuration config) { interestingCallableSrc(c, config) } or
TCallableSrc()
TCallable(DataFlowCallable c, Configuration config) {
interestingCallableSrc(c, config) or
interestingCallableSink(c, config)
} or
TCallableSrc() or
TCallableSink()
private predicate callableExtSrc(TCallableSrc src) { any() }
private predicate callableExtSink(TCallableSink sink) { any() }
private predicate callableExtStepFwd(TCallableExt ce1, TCallableExt ce2) {
exists(DataFlowCallable c1, DataFlowCallable c2, Configuration config |
callableStep(c1, c2, config) and
@@ -2961,15 +2998,32 @@ private module FlowExploration {
config.isSource(n) and
ce2 = TCallable(n.getEnclosingCallable(), config)
)
or
exists(Node n, Configuration config |
ce2 = TCallableSink() and
config.isSink(n) and
ce1 = TCallable(n.getEnclosingCallable(), config)
)
}
private predicate callableExtStepRev(TCallableExt ce1, TCallableExt ce2) {
callableExtStepFwd(ce2, ce1)
}
private int distSrcExt(TCallableExt c) =
shortestDistances(callableExtSrc/1, callableExtStepFwd/2)(_, c, result)
private int distSinkExt(TCallableExt c) =
shortestDistances(callableExtSink/1, callableExtStepRev/2)(_, c, result)
private int distSrc(DataFlowCallable c, Configuration config) {
result = distSrcExt(TCallable(c, config)) - 1
}
private int distSink(DataFlowCallable c, Configuration config) {
result = distSinkExt(TCallable(c, config)) - 1
}
private newtype TPartialAccessPath =
TPartialNil(DataFlowType t) or
TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] }
@@ -2997,18 +3051,12 @@ private module FlowExploration {
or
exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType())
}
abstract AccessPathFront getFront();
}
private class PartialAccessPathNil extends PartialAccessPath, TPartialNil {
override string toString() {
exists(DataFlowType t | this = TPartialNil(t) | result = concat(": " + ppReprType(t)))
}
override AccessPathFront getFront() {
exists(DataFlowType t | this = TPartialNil(t) | result = TFrontNil(t))
}
}
private class PartialAccessPathCons extends PartialAccessPath, TPartialCons {
@@ -3019,9 +3067,39 @@ private module FlowExploration {
else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]"
)
}
}
override AccessPathFront getFront() {
exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc))
private newtype TRevPartialAccessPath =
TRevPartialNil() or
TRevPartialCons(Content c, int len) { len in [1 .. accessPathLimit()] }
/**
* Conceptually a list of `Content`s, but only the first
* element of the list and its length are tracked.
*/
private class RevPartialAccessPath extends TRevPartialAccessPath {
abstract string toString();
Content getHead() { this = TRevPartialCons(result, _) }
int len() {
this = TRevPartialNil() and result = 0
or
this = TRevPartialCons(_, result)
}
}
private class RevPartialAccessPathNil extends RevPartialAccessPath, TRevPartialNil {
override string toString() { result = "" }
}
private class RevPartialAccessPathCons extends RevPartialAccessPath, TRevPartialCons {
override string toString() {
exists(Content c, int len | this = TRevPartialCons(c, len) |
if len = 1
then result = "[" + c.toString() + "]"
else result = "[" + c.toString() + ", ... (" + len.toString() + ")]"
)
}
}
@@ -3033,8 +3111,16 @@ private module FlowExploration {
TSummaryCtx2None() or
TSummaryCtx2Some(PartialAccessPath ap)
private newtype TRevSummaryCtx1 =
TRevSummaryCtx1None() or
TRevSummaryCtx1Some(ReturnPosition pos)
private newtype TRevSummaryCtx2 =
TRevSummaryCtx2None() or
TRevSummaryCtx2Some(RevPartialAccessPath ap)
private newtype TPartialPathNode =
TPartialPathNodeMk(
TPartialPathNodeFwd(
Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
Configuration config
) {
@@ -3048,6 +3134,23 @@ private module FlowExploration {
or
partialPathNodeMk0(node, cc, sc1, sc2, ap, config) and
distSrc(node.getEnclosingCallable(), config) <= config.explorationLimit()
} or
TPartialPathNodeRev(
Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, RevPartialAccessPath ap,
Configuration config
) {
config.isSink(node) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = TRevPartialNil() and
not fullBarrier(node, config) and
exists(config.explorationLimit())
or
exists(PartialPathNodeRev mid |
revPartialPathStep(mid, node, sc1, sc2, ap, config) and
not fullBarrier(node, config) and
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
)
}
pragma[nomagic]
@@ -3055,7 +3158,7 @@ private module FlowExploration {
Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
Configuration config
) {
exists(PartialPathNode mid |
exists(PartialPathNodeFwd mid |
partialPathStep(mid, node, cc, sc1, sc2, ap, config) and
not fullBarrier(node, config) and
if node instanceof CastingNode
@@ -3107,15 +3210,32 @@ private module FlowExploration {
result = distSrc(this.getNode().getEnclosingCallable(), this.getConfiguration())
}
/**
* Gets the approximate distance to the nearest sink measured in number
* of interprocedural steps.
*/
int getSinkDistance() {
result = distSink(this.getNode().getEnclosingCallable(), this.getConfiguration())
}
private string ppAp() {
exists(string s | s = this.(PartialPathNodePriv).getAp().toString() |
exists(string s |
s = this.(PartialPathNodeFwd).getAp().toString() or
s = this.(PartialPathNodeRev).getAp().toString()
|
if s = "" then result = "" else result = " " + s
)
}
private string ppCtx() {
result = " <" + this.(PartialPathNodePriv).getCallContext().toString() + ">"
result = " <" + this.(PartialPathNodeFwd).getCallContext().toString() + ">"
}
/** Holds if this is a source in a forward-flow path. */
predicate isFwdSource() { this.(PartialPathNodeFwd).isSource() }
/** Holds if this is a sink in a reverse-flow path. */
predicate isRevSink() { this.(PartialPathNodeRev).isSink() }
}
/**
@@ -3126,7 +3246,7 @@ private module FlowExploration {
query predicate edges(PartialPathNode a, PartialPathNode b) { a.getASuccessor() = b }
}
private class PartialPathNodePriv extends PartialPathNode {
private class PartialPathNodeFwd extends PartialPathNode, TPartialPathNodeFwd {
Node node;
CallContext cc;
TSummaryCtx1 sc1;
@@ -3134,7 +3254,7 @@ private module FlowExploration {
PartialAccessPath ap;
Configuration config;
PartialPathNodePriv() { this = TPartialPathNodeMk(node, cc, sc1, sc2, ap, config) }
PartialPathNodeFwd() { this = TPartialPathNodeFwd(node, cc, sc1, sc2, ap, config) }
override Node getNode() { result = node }
@@ -3148,14 +3268,54 @@ private module FlowExploration {
override Configuration getConfiguration() { result = config }
override PartialPathNodePriv getASuccessor() {
override PartialPathNodeFwd getASuccessor() {
partialPathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx1(),
result.getSummaryCtx2(), result.getAp(), result.getConfiguration())
}
predicate isSource() {
config.isSource(node) and
cc instanceof CallContextAny and
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
ap instanceof TPartialNil
}
}
private class PartialPathNodeRev extends PartialPathNode, TPartialPathNodeRev {
Node node;
TRevSummaryCtx1 sc1;
TRevSummaryCtx2 sc2;
RevPartialAccessPath ap;
Configuration config;
PartialPathNodeRev() { this = TPartialPathNodeRev(node, sc1, sc2, ap, config) }
override Node getNode() { result = node }
TRevSummaryCtx1 getSummaryCtx1() { result = sc1 }
TRevSummaryCtx2 getSummaryCtx2() { result = sc2 }
RevPartialAccessPath getAp() { result = ap }
override Configuration getConfiguration() { result = config }
override PartialPathNodeRev getASuccessor() {
revPartialPathStep(result, this.getNode(), this.getSummaryCtx1(), this.getSummaryCtx2(),
this.getAp(), this.getConfiguration())
}
predicate isSink() {
config.isSink(node) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = TRevPartialNil()
}
}
private predicate partialPathStep(
PartialPathNodePriv mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
PartialAccessPath ap, Configuration config
) {
not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and
@@ -3221,8 +3381,7 @@ private module FlowExploration {
pragma[inline]
private predicate partialPathStoreStep(
PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node,
PartialAccessPath ap2
PartialPathNodeFwd mid, PartialAccessPath ap1, TypedContent tc, Node node, PartialAccessPath ap2
) {
exists(Node midNode, DataFlowType contentType |
midNode = mid.getNode() and
@@ -3238,7 +3397,7 @@ private module FlowExploration {
private predicate apConsFwd(
PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config
) {
exists(PartialPathNodePriv mid |
exists(PartialPathNodeFwd mid |
partialPathStoreStep(mid, ap1, tc, _, ap2) and
config = mid.getConfiguration()
)
@@ -3246,7 +3405,7 @@ private module FlowExploration {
pragma[nomagic]
private predicate partialPathReadStep(
PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
PartialPathNodeFwd mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
Configuration config
) {
exists(Node midNode |
@@ -3260,7 +3419,7 @@ private module FlowExploration {
}
private predicate partialPathOutOfCallable0(
PartialPathNodePriv mid, ReturnPosition pos, CallContext innercc, PartialAccessPath ap,
PartialPathNodeFwd mid, ReturnPosition pos, CallContext innercc, PartialAccessPath ap,
Configuration config
) {
pos = getReturnPosition(mid.getNode()) and
@@ -3272,7 +3431,7 @@ private module FlowExploration {
pragma[nomagic]
private predicate partialPathOutOfCallable1(
PartialPathNodePriv mid, DataFlowCall call, ReturnKindExt kind, CallContext cc,
PartialPathNodeFwd mid, DataFlowCall call, ReturnKindExt kind, CallContext cc,
PartialAccessPath ap, Configuration config
) {
exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc |
@@ -3286,7 +3445,7 @@ private module FlowExploration {
}
private predicate partialPathOutOfCallable(
PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
PartialPathNodeFwd mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
) {
exists(ReturnKindExt kind, DataFlowCall call |
partialPathOutOfCallable1(mid, call, kind, cc, ap, config)
@@ -3297,7 +3456,7 @@ private module FlowExploration {
pragma[noinline]
private predicate partialPathIntoArg(
PartialPathNodePriv mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap,
PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap,
Configuration config
) {
exists(ArgumentNode arg |
@@ -3311,7 +3470,7 @@ private module FlowExploration {
pragma[nomagic]
private predicate partialPathIntoCallable0(
PartialPathNodePriv mid, DataFlowCallable callable, int i, CallContext outercc,
PartialPathNodeFwd mid, DataFlowCallable callable, int i, CallContext outercc,
DataFlowCall call, PartialAccessPath ap, Configuration config
) {
partialPathIntoArg(mid, i, outercc, call, ap, config) and
@@ -3319,7 +3478,7 @@ private module FlowExploration {
}
private predicate partialPathIntoCallable(
PartialPathNodePriv mid, ParameterNode p, CallContext outercc, CallContextCall innercc,
PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc,
TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap,
Configuration config
) {
@@ -3340,7 +3499,7 @@ private module FlowExploration {
ReturnKindExt kind, CallContextCall cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
PartialAccessPath ap, Configuration config
) {
exists(PartialPathNodePriv mid, ReturnNodeExt ret |
exists(PartialPathNodeFwd mid, ReturnNodeExt ret |
mid.getNode() = ret and
kind = ret.getKind() and
cc = mid.getCallContext() and
@@ -3353,7 +3512,7 @@ private module FlowExploration {
pragma[noinline]
private predicate partialPathThroughCallable0(
DataFlowCall call, PartialPathNodePriv mid, ReturnKindExt kind, CallContext cc,
DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc,
PartialAccessPath ap, Configuration config
) {
exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 |
@@ -3363,13 +3522,164 @@ private module FlowExploration {
}
private predicate partialPathThroughCallable(
PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
PartialPathNodeFwd mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
) {
exists(DataFlowCall call, ReturnKindExt kind |
partialPathThroughCallable0(call, mid, kind, cc, ap, config) and
out = kind.getAnOutNode(call)
)
}
private predicate revPartialPathStep(
PartialPathNodeRev mid, Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2,
RevPartialAccessPath ap, Configuration config
) {
localFlowStep(node, mid.getNode(), config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
ap = mid.getAp() and
config = mid.getConfiguration()
or
additionalLocalFlowStep(node, mid.getNode(), config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
mid.getAp() instanceof RevPartialAccessPathNil and
ap = TRevPartialNil() and
config = mid.getConfiguration()
or
jumpStep(node, mid.getNode(), config) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = mid.getAp() and
config = mid.getConfiguration()
or
additionalJumpStep(node, mid.getNode(), config) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
mid.getAp() instanceof RevPartialAccessPathNil and
ap = TRevPartialNil() and
config = mid.getConfiguration()
or
revPartialPathReadStep(mid, _, _, node, ap) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
config = mid.getConfiguration()
or
exists(RevPartialAccessPath ap0, Content c |
revPartialPathStoreStep(mid, ap0, c, node, config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
apConsRev(ap, c, ap0, config)
)
or
exists(ParameterNode p |
mid.getNode() = p and
viableParamArg(_, p, node) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = mid.getAp() and
config = mid.getConfiguration()
)
or
exists(ReturnPosition pos |
revPartialPathIntoReturn(mid, pos, sc1, sc2, _, ap, config) and
pos = getReturnPosition(node)
)
or
revPartialPathThroughCallable(mid, node, ap, config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2()
}
pragma[inline]
private predicate revPartialPathReadStep(
PartialPathNodeRev mid, RevPartialAccessPath ap1, Content c, Node node, RevPartialAccessPath ap2
) {
exists(Node midNode |
midNode = mid.getNode() and
ap1 = mid.getAp() and
read(node, c, midNode) and
ap2.getHead() = c and
ap2.len() = unbindInt(ap1.len() + 1)
)
}
pragma[nomagic]
private predicate apConsRev(
RevPartialAccessPath ap1, Content c, RevPartialAccessPath ap2, Configuration config
) {
exists(PartialPathNodeRev mid |
revPartialPathReadStep(mid, ap1, c, _, ap2) and
config = mid.getConfiguration()
)
}
pragma[nomagic]
private predicate revPartialPathStoreStep(
PartialPathNodeRev mid, RevPartialAccessPath ap, Content c, Node node, Configuration config
) {
exists(Node midNode, TypedContent tc |
midNode = mid.getNode() and
ap = mid.getAp() and
store(node, tc, midNode, _) and
ap.getHead() = c and
config = mid.getConfiguration() and
tc.getContent() = c
)
}
pragma[nomagic]
private predicate revPartialPathIntoReturn(
PartialPathNodeRev mid, ReturnPosition pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2,
DataFlowCall call, RevPartialAccessPath ap, Configuration config
) {
exists(Node out |
mid.getNode() = out and
viableReturnPosOut(call, pos, out) and
sc1 = TRevSummaryCtx1Some(pos) and
sc2 = TRevSummaryCtx2Some(ap) and
ap = mid.getAp() and
config = mid.getConfiguration()
)
}
pragma[nomagic]
private predicate revPartialPathFlowsThrough(
int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap,
Configuration config
) {
exists(PartialPathNodeRev mid, ParameterNode p |
mid.getNode() = p and
p.isParameterOf(_, pos) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
ap = mid.getAp() and
config = mid.getConfiguration()
)
}
pragma[nomagic]
private predicate revPartialPathThroughCallable0(
DataFlowCall call, PartialPathNodeRev mid, int pos, RevPartialAccessPath ap,
Configuration config
) {
exists(TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2 |
revPartialPathIntoReturn(mid, _, sc1, sc2, call, _, config) and
revPartialPathFlowsThrough(pos, sc1, sc2, ap, config)
)
}
pragma[nomagic]
private predicate revPartialPathThroughCallable(
PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config
) {
exists(DataFlowCall call, int pos |
revPartialPathThroughCallable0(call, mid, pos, ap, config) and
node.argumentOf(call, pos)
)
}
}
import FlowExploration
@@ -3378,6 +3688,14 @@ private predicate partialFlow(
PartialPathNode source, PartialPathNode node, Configuration configuration
) {
source.getConfiguration() = configuration and
configuration.isSource(source.getNode()) and
source.isFwdSource() and
node = source.getASuccessor+()
}
private predicate revPartialFlow(
PartialPathNode node, PartialPathNode sink, Configuration configuration
) {
sink.getConfiguration() = configuration and
sink.isRevSink() and
node.getASuccessor+() = sink
}

View File

@@ -112,8 +112,8 @@ abstract class Configuration extends string {
predicate hasFlowToExpr(DataFlowExpr sink) { hasFlowTo(exprNode(sink)) }
/**
* Gets the exploration limit for `hasPartialFlow` measured in approximate
* number of interprocedural steps.
* Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev`
* measured in approximate number of interprocedural steps.
*/
int explorationLimit() { none() }
@@ -123,7 +123,7 @@ abstract class Configuration extends string {
* is restricted to be less than or equal to `explorationLimit()`. This
* predicate completely disregards sink definitions.
*
* This predicate is intended for dataflow exploration and debugging and may
* This predicate is intended for data-flow exploration and debugging and may
* perform poorly if the number of sources is too big and/or the exploration
* limit is set too high without using barriers.
*
@@ -136,6 +136,29 @@ abstract class Configuration extends string {
partialFlow(source, node, this) and
dist = node.getSourceDistance()
}
/**
* Holds if there is a partial data flow path from `node` to `sink`. The
* approximate distance between `node` and the closest sink is `dist` and
* is restricted to be less than or equal to `explorationLimit()`. This
* predicate completely disregards source definitions.
*
* This predicate is intended for data-flow exploration and debugging and may
* perform poorly if the number of sinks is too big and/or the exploration
* limit is set too high without using barriers.
*
* This predicate is disabled (has no results) by default. Override
* `explorationLimit()` with a suitable number to enable this predicate.
*
* To use this in a `path-problem` query, import the module `PartialPathGraph`.
*
* Note that reverse flow has slightly lower precision than the corresponding
* forward flow, as reverse flow disregards type pruning among other features.
*/
final predicate hasPartialFlowRev(PartialPathNode node, PartialPathNode sink, int dist) {
revPartialFlow(node, sink, this) and
dist = node.getSinkDistance()
}
}
/**
@@ -2943,12 +2966,26 @@ private module FlowExploration {
)
}
private predicate interestingCallableSink(DataFlowCallable c, Configuration config) {
exists(Node n | config.isSink(n) and c = n.getEnclosingCallable())
or
exists(DataFlowCallable mid |
interestingCallableSink(mid, config) and callableStep(c, mid, config)
)
}
private newtype TCallableExt =
TCallable(DataFlowCallable c, Configuration config) { interestingCallableSrc(c, config) } or
TCallableSrc()
TCallable(DataFlowCallable c, Configuration config) {
interestingCallableSrc(c, config) or
interestingCallableSink(c, config)
} or
TCallableSrc() or
TCallableSink()
private predicate callableExtSrc(TCallableSrc src) { any() }
private predicate callableExtSink(TCallableSink sink) { any() }
private predicate callableExtStepFwd(TCallableExt ce1, TCallableExt ce2) {
exists(DataFlowCallable c1, DataFlowCallable c2, Configuration config |
callableStep(c1, c2, config) and
@@ -2961,15 +2998,32 @@ private module FlowExploration {
config.isSource(n) and
ce2 = TCallable(n.getEnclosingCallable(), config)
)
or
exists(Node n, Configuration config |
ce2 = TCallableSink() and
config.isSink(n) and
ce1 = TCallable(n.getEnclosingCallable(), config)
)
}
private predicate callableExtStepRev(TCallableExt ce1, TCallableExt ce2) {
callableExtStepFwd(ce2, ce1)
}
private int distSrcExt(TCallableExt c) =
shortestDistances(callableExtSrc/1, callableExtStepFwd/2)(_, c, result)
private int distSinkExt(TCallableExt c) =
shortestDistances(callableExtSink/1, callableExtStepRev/2)(_, c, result)
private int distSrc(DataFlowCallable c, Configuration config) {
result = distSrcExt(TCallable(c, config)) - 1
}
private int distSink(DataFlowCallable c, Configuration config) {
result = distSinkExt(TCallable(c, config)) - 1
}
private newtype TPartialAccessPath =
TPartialNil(DataFlowType t) or
TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] }
@@ -2997,18 +3051,12 @@ private module FlowExploration {
or
exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType())
}
abstract AccessPathFront getFront();
}
private class PartialAccessPathNil extends PartialAccessPath, TPartialNil {
override string toString() {
exists(DataFlowType t | this = TPartialNil(t) | result = concat(": " + ppReprType(t)))
}
override AccessPathFront getFront() {
exists(DataFlowType t | this = TPartialNil(t) | result = TFrontNil(t))
}
}
private class PartialAccessPathCons extends PartialAccessPath, TPartialCons {
@@ -3019,9 +3067,39 @@ private module FlowExploration {
else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]"
)
}
}
override AccessPathFront getFront() {
exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc))
private newtype TRevPartialAccessPath =
TRevPartialNil() or
TRevPartialCons(Content c, int len) { len in [1 .. accessPathLimit()] }
/**
* Conceptually a list of `Content`s, but only the first
* element of the list and its length are tracked.
*/
private class RevPartialAccessPath extends TRevPartialAccessPath {
abstract string toString();
Content getHead() { this = TRevPartialCons(result, _) }
int len() {
this = TRevPartialNil() and result = 0
or
this = TRevPartialCons(_, result)
}
}
private class RevPartialAccessPathNil extends RevPartialAccessPath, TRevPartialNil {
override string toString() { result = "" }
}
private class RevPartialAccessPathCons extends RevPartialAccessPath, TRevPartialCons {
override string toString() {
exists(Content c, int len | this = TRevPartialCons(c, len) |
if len = 1
then result = "[" + c.toString() + "]"
else result = "[" + c.toString() + ", ... (" + len.toString() + ")]"
)
}
}
@@ -3033,8 +3111,16 @@ private module FlowExploration {
TSummaryCtx2None() or
TSummaryCtx2Some(PartialAccessPath ap)
private newtype TRevSummaryCtx1 =
TRevSummaryCtx1None() or
TRevSummaryCtx1Some(ReturnPosition pos)
private newtype TRevSummaryCtx2 =
TRevSummaryCtx2None() or
TRevSummaryCtx2Some(RevPartialAccessPath ap)
private newtype TPartialPathNode =
TPartialPathNodeMk(
TPartialPathNodeFwd(
Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
Configuration config
) {
@@ -3048,6 +3134,23 @@ private module FlowExploration {
or
partialPathNodeMk0(node, cc, sc1, sc2, ap, config) and
distSrc(node.getEnclosingCallable(), config) <= config.explorationLimit()
} or
TPartialPathNodeRev(
Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, RevPartialAccessPath ap,
Configuration config
) {
config.isSink(node) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = TRevPartialNil() and
not fullBarrier(node, config) and
exists(config.explorationLimit())
or
exists(PartialPathNodeRev mid |
revPartialPathStep(mid, node, sc1, sc2, ap, config) and
not fullBarrier(node, config) and
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
)
}
pragma[nomagic]
@@ -3055,7 +3158,7 @@ private module FlowExploration {
Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
Configuration config
) {
exists(PartialPathNode mid |
exists(PartialPathNodeFwd mid |
partialPathStep(mid, node, cc, sc1, sc2, ap, config) and
not fullBarrier(node, config) and
if node instanceof CastingNode
@@ -3107,15 +3210,32 @@ private module FlowExploration {
result = distSrc(this.getNode().getEnclosingCallable(), this.getConfiguration())
}
/**
* Gets the approximate distance to the nearest sink measured in number
* of interprocedural steps.
*/
int getSinkDistance() {
result = distSink(this.getNode().getEnclosingCallable(), this.getConfiguration())
}
private string ppAp() {
exists(string s | s = this.(PartialPathNodePriv).getAp().toString() |
exists(string s |
s = this.(PartialPathNodeFwd).getAp().toString() or
s = this.(PartialPathNodeRev).getAp().toString()
|
if s = "" then result = "" else result = " " + s
)
}
private string ppCtx() {
result = " <" + this.(PartialPathNodePriv).getCallContext().toString() + ">"
result = " <" + this.(PartialPathNodeFwd).getCallContext().toString() + ">"
}
/** Holds if this is a source in a forward-flow path. */
predicate isFwdSource() { this.(PartialPathNodeFwd).isSource() }
/** Holds if this is a sink in a reverse-flow path. */
predicate isRevSink() { this.(PartialPathNodeRev).isSink() }
}
/**
@@ -3126,7 +3246,7 @@ private module FlowExploration {
query predicate edges(PartialPathNode a, PartialPathNode b) { a.getASuccessor() = b }
}
private class PartialPathNodePriv extends PartialPathNode {
private class PartialPathNodeFwd extends PartialPathNode, TPartialPathNodeFwd {
Node node;
CallContext cc;
TSummaryCtx1 sc1;
@@ -3134,7 +3254,7 @@ private module FlowExploration {
PartialAccessPath ap;
Configuration config;
PartialPathNodePriv() { this = TPartialPathNodeMk(node, cc, sc1, sc2, ap, config) }
PartialPathNodeFwd() { this = TPartialPathNodeFwd(node, cc, sc1, sc2, ap, config) }
override Node getNode() { result = node }
@@ -3148,14 +3268,54 @@ private module FlowExploration {
override Configuration getConfiguration() { result = config }
override PartialPathNodePriv getASuccessor() {
override PartialPathNodeFwd getASuccessor() {
partialPathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx1(),
result.getSummaryCtx2(), result.getAp(), result.getConfiguration())
}
predicate isSource() {
config.isSource(node) and
cc instanceof CallContextAny and
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
ap instanceof TPartialNil
}
}
private class PartialPathNodeRev extends PartialPathNode, TPartialPathNodeRev {
Node node;
TRevSummaryCtx1 sc1;
TRevSummaryCtx2 sc2;
RevPartialAccessPath ap;
Configuration config;
PartialPathNodeRev() { this = TPartialPathNodeRev(node, sc1, sc2, ap, config) }
override Node getNode() { result = node }
TRevSummaryCtx1 getSummaryCtx1() { result = sc1 }
TRevSummaryCtx2 getSummaryCtx2() { result = sc2 }
RevPartialAccessPath getAp() { result = ap }
override Configuration getConfiguration() { result = config }
override PartialPathNodeRev getASuccessor() {
revPartialPathStep(result, this.getNode(), this.getSummaryCtx1(), this.getSummaryCtx2(),
this.getAp(), this.getConfiguration())
}
predicate isSink() {
config.isSink(node) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = TRevPartialNil()
}
}
private predicate partialPathStep(
PartialPathNodePriv mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
PartialAccessPath ap, Configuration config
) {
not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and
@@ -3221,8 +3381,7 @@ private module FlowExploration {
pragma[inline]
private predicate partialPathStoreStep(
PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node,
PartialAccessPath ap2
PartialPathNodeFwd mid, PartialAccessPath ap1, TypedContent tc, Node node, PartialAccessPath ap2
) {
exists(Node midNode, DataFlowType contentType |
midNode = mid.getNode() and
@@ -3238,7 +3397,7 @@ private module FlowExploration {
private predicate apConsFwd(
PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config
) {
exists(PartialPathNodePriv mid |
exists(PartialPathNodeFwd mid |
partialPathStoreStep(mid, ap1, tc, _, ap2) and
config = mid.getConfiguration()
)
@@ -3246,7 +3405,7 @@ private module FlowExploration {
pragma[nomagic]
private predicate partialPathReadStep(
PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
PartialPathNodeFwd mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
Configuration config
) {
exists(Node midNode |
@@ -3260,7 +3419,7 @@ private module FlowExploration {
}
private predicate partialPathOutOfCallable0(
PartialPathNodePriv mid, ReturnPosition pos, CallContext innercc, PartialAccessPath ap,
PartialPathNodeFwd mid, ReturnPosition pos, CallContext innercc, PartialAccessPath ap,
Configuration config
) {
pos = getReturnPosition(mid.getNode()) and
@@ -3272,7 +3431,7 @@ private module FlowExploration {
pragma[nomagic]
private predicate partialPathOutOfCallable1(
PartialPathNodePriv mid, DataFlowCall call, ReturnKindExt kind, CallContext cc,
PartialPathNodeFwd mid, DataFlowCall call, ReturnKindExt kind, CallContext cc,
PartialAccessPath ap, Configuration config
) {
exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc |
@@ -3286,7 +3445,7 @@ private module FlowExploration {
}
private predicate partialPathOutOfCallable(
PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
PartialPathNodeFwd mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
) {
exists(ReturnKindExt kind, DataFlowCall call |
partialPathOutOfCallable1(mid, call, kind, cc, ap, config)
@@ -3297,7 +3456,7 @@ private module FlowExploration {
pragma[noinline]
private predicate partialPathIntoArg(
PartialPathNodePriv mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap,
PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap,
Configuration config
) {
exists(ArgumentNode arg |
@@ -3311,7 +3470,7 @@ private module FlowExploration {
pragma[nomagic]
private predicate partialPathIntoCallable0(
PartialPathNodePriv mid, DataFlowCallable callable, int i, CallContext outercc,
PartialPathNodeFwd mid, DataFlowCallable callable, int i, CallContext outercc,
DataFlowCall call, PartialAccessPath ap, Configuration config
) {
partialPathIntoArg(mid, i, outercc, call, ap, config) and
@@ -3319,7 +3478,7 @@ private module FlowExploration {
}
private predicate partialPathIntoCallable(
PartialPathNodePriv mid, ParameterNode p, CallContext outercc, CallContextCall innercc,
PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc,
TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap,
Configuration config
) {
@@ -3340,7 +3499,7 @@ private module FlowExploration {
ReturnKindExt kind, CallContextCall cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
PartialAccessPath ap, Configuration config
) {
exists(PartialPathNodePriv mid, ReturnNodeExt ret |
exists(PartialPathNodeFwd mid, ReturnNodeExt ret |
mid.getNode() = ret and
kind = ret.getKind() and
cc = mid.getCallContext() and
@@ -3353,7 +3512,7 @@ private module FlowExploration {
pragma[noinline]
private predicate partialPathThroughCallable0(
DataFlowCall call, PartialPathNodePriv mid, ReturnKindExt kind, CallContext cc,
DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc,
PartialAccessPath ap, Configuration config
) {
exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 |
@@ -3363,13 +3522,164 @@ private module FlowExploration {
}
private predicate partialPathThroughCallable(
PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
PartialPathNodeFwd mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
) {
exists(DataFlowCall call, ReturnKindExt kind |
partialPathThroughCallable0(call, mid, kind, cc, ap, config) and
out = kind.getAnOutNode(call)
)
}
private predicate revPartialPathStep(
PartialPathNodeRev mid, Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2,
RevPartialAccessPath ap, Configuration config
) {
localFlowStep(node, mid.getNode(), config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
ap = mid.getAp() and
config = mid.getConfiguration()
or
additionalLocalFlowStep(node, mid.getNode(), config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
mid.getAp() instanceof RevPartialAccessPathNil and
ap = TRevPartialNil() and
config = mid.getConfiguration()
or
jumpStep(node, mid.getNode(), config) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = mid.getAp() and
config = mid.getConfiguration()
or
additionalJumpStep(node, mid.getNode(), config) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
mid.getAp() instanceof RevPartialAccessPathNil and
ap = TRevPartialNil() and
config = mid.getConfiguration()
or
revPartialPathReadStep(mid, _, _, node, ap) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
config = mid.getConfiguration()
or
exists(RevPartialAccessPath ap0, Content c |
revPartialPathStoreStep(mid, ap0, c, node, config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
apConsRev(ap, c, ap0, config)
)
or
exists(ParameterNode p |
mid.getNode() = p and
viableParamArg(_, p, node) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = mid.getAp() and
config = mid.getConfiguration()
)
or
exists(ReturnPosition pos |
revPartialPathIntoReturn(mid, pos, sc1, sc2, _, ap, config) and
pos = getReturnPosition(node)
)
or
revPartialPathThroughCallable(mid, node, ap, config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2()
}
pragma[inline]
private predicate revPartialPathReadStep(
PartialPathNodeRev mid, RevPartialAccessPath ap1, Content c, Node node, RevPartialAccessPath ap2
) {
exists(Node midNode |
midNode = mid.getNode() and
ap1 = mid.getAp() and
read(node, c, midNode) and
ap2.getHead() = c and
ap2.len() = unbindInt(ap1.len() + 1)
)
}
pragma[nomagic]
private predicate apConsRev(
RevPartialAccessPath ap1, Content c, RevPartialAccessPath ap2, Configuration config
) {
exists(PartialPathNodeRev mid |
revPartialPathReadStep(mid, ap1, c, _, ap2) and
config = mid.getConfiguration()
)
}
pragma[nomagic]
private predicate revPartialPathStoreStep(
PartialPathNodeRev mid, RevPartialAccessPath ap, Content c, Node node, Configuration config
) {
exists(Node midNode, TypedContent tc |
midNode = mid.getNode() and
ap = mid.getAp() and
store(node, tc, midNode, _) and
ap.getHead() = c and
config = mid.getConfiguration() and
tc.getContent() = c
)
}
pragma[nomagic]
private predicate revPartialPathIntoReturn(
PartialPathNodeRev mid, ReturnPosition pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2,
DataFlowCall call, RevPartialAccessPath ap, Configuration config
) {
exists(Node out |
mid.getNode() = out and
viableReturnPosOut(call, pos, out) and
sc1 = TRevSummaryCtx1Some(pos) and
sc2 = TRevSummaryCtx2Some(ap) and
ap = mid.getAp() and
config = mid.getConfiguration()
)
}
pragma[nomagic]
private predicate revPartialPathFlowsThrough(
int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap,
Configuration config
) {
exists(PartialPathNodeRev mid, ParameterNode p |
mid.getNode() = p and
p.isParameterOf(_, pos) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
ap = mid.getAp() and
config = mid.getConfiguration()
)
}
pragma[nomagic]
private predicate revPartialPathThroughCallable0(
DataFlowCall call, PartialPathNodeRev mid, int pos, RevPartialAccessPath ap,
Configuration config
) {
exists(TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2 |
revPartialPathIntoReturn(mid, _, sc1, sc2, call, _, config) and
revPartialPathFlowsThrough(pos, sc1, sc2, ap, config)
)
}
pragma[nomagic]
private predicate revPartialPathThroughCallable(
PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config
) {
exists(DataFlowCall call, int pos |
revPartialPathThroughCallable0(call, mid, pos, ap, config) and
node.argumentOf(call, pos)
)
}
}
import FlowExploration
@@ -3378,6 +3688,14 @@ private predicate partialFlow(
PartialPathNode source, PartialPathNode node, Configuration configuration
) {
source.getConfiguration() = configuration and
configuration.isSource(source.getNode()) and
source.isFwdSource() and
node = source.getASuccessor+()
}
private predicate revPartialFlow(
PartialPathNode node, PartialPathNode sink, Configuration configuration
) {
sink.getConfiguration() = configuration and
sink.isRevSink() and
node.getASuccessor+() = sink
}

View File

@@ -112,8 +112,8 @@ abstract class Configuration extends string {
predicate hasFlowToExpr(DataFlowExpr sink) { hasFlowTo(exprNode(sink)) }
/**
* Gets the exploration limit for `hasPartialFlow` measured in approximate
* number of interprocedural steps.
* Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev`
* measured in approximate number of interprocedural steps.
*/
int explorationLimit() { none() }
@@ -123,7 +123,7 @@ abstract class Configuration extends string {
* is restricted to be less than or equal to `explorationLimit()`. This
* predicate completely disregards sink definitions.
*
* This predicate is intended for dataflow exploration and debugging and may
* This predicate is intended for data-flow exploration and debugging and may
* perform poorly if the number of sources is too big and/or the exploration
* limit is set too high without using barriers.
*
@@ -136,6 +136,29 @@ abstract class Configuration extends string {
partialFlow(source, node, this) and
dist = node.getSourceDistance()
}
/**
* Holds if there is a partial data flow path from `node` to `sink`. The
* approximate distance between `node` and the closest sink is `dist` and
* is restricted to be less than or equal to `explorationLimit()`. This
* predicate completely disregards source definitions.
*
* This predicate is intended for data-flow exploration and debugging and may
* perform poorly if the number of sinks is too big and/or the exploration
* limit is set too high without using barriers.
*
* This predicate is disabled (has no results) by default. Override
* `explorationLimit()` with a suitable number to enable this predicate.
*
* To use this in a `path-problem` query, import the module `PartialPathGraph`.
*
* Note that reverse flow has slightly lower precision than the corresponding
* forward flow, as reverse flow disregards type pruning among other features.
*/
final predicate hasPartialFlowRev(PartialPathNode node, PartialPathNode sink, int dist) {
revPartialFlow(node, sink, this) and
dist = node.getSinkDistance()
}
}
/**
@@ -2943,12 +2966,26 @@ private module FlowExploration {
)
}
private predicate interestingCallableSink(DataFlowCallable c, Configuration config) {
exists(Node n | config.isSink(n) and c = n.getEnclosingCallable())
or
exists(DataFlowCallable mid |
interestingCallableSink(mid, config) and callableStep(c, mid, config)
)
}
private newtype TCallableExt =
TCallable(DataFlowCallable c, Configuration config) { interestingCallableSrc(c, config) } or
TCallableSrc()
TCallable(DataFlowCallable c, Configuration config) {
interestingCallableSrc(c, config) or
interestingCallableSink(c, config)
} or
TCallableSrc() or
TCallableSink()
private predicate callableExtSrc(TCallableSrc src) { any() }
private predicate callableExtSink(TCallableSink sink) { any() }
private predicate callableExtStepFwd(TCallableExt ce1, TCallableExt ce2) {
exists(DataFlowCallable c1, DataFlowCallable c2, Configuration config |
callableStep(c1, c2, config) and
@@ -2961,15 +2998,32 @@ private module FlowExploration {
config.isSource(n) and
ce2 = TCallable(n.getEnclosingCallable(), config)
)
or
exists(Node n, Configuration config |
ce2 = TCallableSink() and
config.isSink(n) and
ce1 = TCallable(n.getEnclosingCallable(), config)
)
}
private predicate callableExtStepRev(TCallableExt ce1, TCallableExt ce2) {
callableExtStepFwd(ce2, ce1)
}
private int distSrcExt(TCallableExt c) =
shortestDistances(callableExtSrc/1, callableExtStepFwd/2)(_, c, result)
private int distSinkExt(TCallableExt c) =
shortestDistances(callableExtSink/1, callableExtStepRev/2)(_, c, result)
private int distSrc(DataFlowCallable c, Configuration config) {
result = distSrcExt(TCallable(c, config)) - 1
}
private int distSink(DataFlowCallable c, Configuration config) {
result = distSinkExt(TCallable(c, config)) - 1
}
private newtype TPartialAccessPath =
TPartialNil(DataFlowType t) or
TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] }
@@ -2997,18 +3051,12 @@ private module FlowExploration {
or
exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType())
}
abstract AccessPathFront getFront();
}
private class PartialAccessPathNil extends PartialAccessPath, TPartialNil {
override string toString() {
exists(DataFlowType t | this = TPartialNil(t) | result = concat(": " + ppReprType(t)))
}
override AccessPathFront getFront() {
exists(DataFlowType t | this = TPartialNil(t) | result = TFrontNil(t))
}
}
private class PartialAccessPathCons extends PartialAccessPath, TPartialCons {
@@ -3019,9 +3067,39 @@ private module FlowExploration {
else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]"
)
}
}
override AccessPathFront getFront() {
exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc))
private newtype TRevPartialAccessPath =
TRevPartialNil() or
TRevPartialCons(Content c, int len) { len in [1 .. accessPathLimit()] }
/**
* Conceptually a list of `Content`s, but only the first
* element of the list and its length are tracked.
*/
private class RevPartialAccessPath extends TRevPartialAccessPath {
abstract string toString();
Content getHead() { this = TRevPartialCons(result, _) }
int len() {
this = TRevPartialNil() and result = 0
or
this = TRevPartialCons(_, result)
}
}
private class RevPartialAccessPathNil extends RevPartialAccessPath, TRevPartialNil {
override string toString() { result = "" }
}
private class RevPartialAccessPathCons extends RevPartialAccessPath, TRevPartialCons {
override string toString() {
exists(Content c, int len | this = TRevPartialCons(c, len) |
if len = 1
then result = "[" + c.toString() + "]"
else result = "[" + c.toString() + ", ... (" + len.toString() + ")]"
)
}
}
@@ -3033,8 +3111,16 @@ private module FlowExploration {
TSummaryCtx2None() or
TSummaryCtx2Some(PartialAccessPath ap)
private newtype TRevSummaryCtx1 =
TRevSummaryCtx1None() or
TRevSummaryCtx1Some(ReturnPosition pos)
private newtype TRevSummaryCtx2 =
TRevSummaryCtx2None() or
TRevSummaryCtx2Some(RevPartialAccessPath ap)
private newtype TPartialPathNode =
TPartialPathNodeMk(
TPartialPathNodeFwd(
Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
Configuration config
) {
@@ -3048,6 +3134,23 @@ private module FlowExploration {
or
partialPathNodeMk0(node, cc, sc1, sc2, ap, config) and
distSrc(node.getEnclosingCallable(), config) <= config.explorationLimit()
} or
TPartialPathNodeRev(
Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, RevPartialAccessPath ap,
Configuration config
) {
config.isSink(node) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = TRevPartialNil() and
not fullBarrier(node, config) and
exists(config.explorationLimit())
or
exists(PartialPathNodeRev mid |
revPartialPathStep(mid, node, sc1, sc2, ap, config) and
not fullBarrier(node, config) and
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
)
}
pragma[nomagic]
@@ -3055,7 +3158,7 @@ private module FlowExploration {
Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
Configuration config
) {
exists(PartialPathNode mid |
exists(PartialPathNodeFwd mid |
partialPathStep(mid, node, cc, sc1, sc2, ap, config) and
not fullBarrier(node, config) and
if node instanceof CastingNode
@@ -3107,15 +3210,32 @@ private module FlowExploration {
result = distSrc(this.getNode().getEnclosingCallable(), this.getConfiguration())
}
/**
* Gets the approximate distance to the nearest sink measured in number
* of interprocedural steps.
*/
int getSinkDistance() {
result = distSink(this.getNode().getEnclosingCallable(), this.getConfiguration())
}
private string ppAp() {
exists(string s | s = this.(PartialPathNodePriv).getAp().toString() |
exists(string s |
s = this.(PartialPathNodeFwd).getAp().toString() or
s = this.(PartialPathNodeRev).getAp().toString()
|
if s = "" then result = "" else result = " " + s
)
}
private string ppCtx() {
result = " <" + this.(PartialPathNodePriv).getCallContext().toString() + ">"
result = " <" + this.(PartialPathNodeFwd).getCallContext().toString() + ">"
}
/** Holds if this is a source in a forward-flow path. */
predicate isFwdSource() { this.(PartialPathNodeFwd).isSource() }
/** Holds if this is a sink in a reverse-flow path. */
predicate isRevSink() { this.(PartialPathNodeRev).isSink() }
}
/**
@@ -3126,7 +3246,7 @@ private module FlowExploration {
query predicate edges(PartialPathNode a, PartialPathNode b) { a.getASuccessor() = b }
}
private class PartialPathNodePriv extends PartialPathNode {
private class PartialPathNodeFwd extends PartialPathNode, TPartialPathNodeFwd {
Node node;
CallContext cc;
TSummaryCtx1 sc1;
@@ -3134,7 +3254,7 @@ private module FlowExploration {
PartialAccessPath ap;
Configuration config;
PartialPathNodePriv() { this = TPartialPathNodeMk(node, cc, sc1, sc2, ap, config) }
PartialPathNodeFwd() { this = TPartialPathNodeFwd(node, cc, sc1, sc2, ap, config) }
override Node getNode() { result = node }
@@ -3148,14 +3268,54 @@ private module FlowExploration {
override Configuration getConfiguration() { result = config }
override PartialPathNodePriv getASuccessor() {
override PartialPathNodeFwd getASuccessor() {
partialPathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx1(),
result.getSummaryCtx2(), result.getAp(), result.getConfiguration())
}
predicate isSource() {
config.isSource(node) and
cc instanceof CallContextAny and
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
ap instanceof TPartialNil
}
}
private class PartialPathNodeRev extends PartialPathNode, TPartialPathNodeRev {
Node node;
TRevSummaryCtx1 sc1;
TRevSummaryCtx2 sc2;
RevPartialAccessPath ap;
Configuration config;
PartialPathNodeRev() { this = TPartialPathNodeRev(node, sc1, sc2, ap, config) }
override Node getNode() { result = node }
TRevSummaryCtx1 getSummaryCtx1() { result = sc1 }
TRevSummaryCtx2 getSummaryCtx2() { result = sc2 }
RevPartialAccessPath getAp() { result = ap }
override Configuration getConfiguration() { result = config }
override PartialPathNodeRev getASuccessor() {
revPartialPathStep(result, this.getNode(), this.getSummaryCtx1(), this.getSummaryCtx2(),
this.getAp(), this.getConfiguration())
}
predicate isSink() {
config.isSink(node) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = TRevPartialNil()
}
}
private predicate partialPathStep(
PartialPathNodePriv mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
PartialAccessPath ap, Configuration config
) {
not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and
@@ -3221,8 +3381,7 @@ private module FlowExploration {
pragma[inline]
private predicate partialPathStoreStep(
PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node,
PartialAccessPath ap2
PartialPathNodeFwd mid, PartialAccessPath ap1, TypedContent tc, Node node, PartialAccessPath ap2
) {
exists(Node midNode, DataFlowType contentType |
midNode = mid.getNode() and
@@ -3238,7 +3397,7 @@ private module FlowExploration {
private predicate apConsFwd(
PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config
) {
exists(PartialPathNodePriv mid |
exists(PartialPathNodeFwd mid |
partialPathStoreStep(mid, ap1, tc, _, ap2) and
config = mid.getConfiguration()
)
@@ -3246,7 +3405,7 @@ private module FlowExploration {
pragma[nomagic]
private predicate partialPathReadStep(
PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
PartialPathNodeFwd mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
Configuration config
) {
exists(Node midNode |
@@ -3260,7 +3419,7 @@ private module FlowExploration {
}
private predicate partialPathOutOfCallable0(
PartialPathNodePriv mid, ReturnPosition pos, CallContext innercc, PartialAccessPath ap,
PartialPathNodeFwd mid, ReturnPosition pos, CallContext innercc, PartialAccessPath ap,
Configuration config
) {
pos = getReturnPosition(mid.getNode()) and
@@ -3272,7 +3431,7 @@ private module FlowExploration {
pragma[nomagic]
private predicate partialPathOutOfCallable1(
PartialPathNodePriv mid, DataFlowCall call, ReturnKindExt kind, CallContext cc,
PartialPathNodeFwd mid, DataFlowCall call, ReturnKindExt kind, CallContext cc,
PartialAccessPath ap, Configuration config
) {
exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc |
@@ -3286,7 +3445,7 @@ private module FlowExploration {
}
private predicate partialPathOutOfCallable(
PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
PartialPathNodeFwd mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
) {
exists(ReturnKindExt kind, DataFlowCall call |
partialPathOutOfCallable1(mid, call, kind, cc, ap, config)
@@ -3297,7 +3456,7 @@ private module FlowExploration {
pragma[noinline]
private predicate partialPathIntoArg(
PartialPathNodePriv mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap,
PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap,
Configuration config
) {
exists(ArgumentNode arg |
@@ -3311,7 +3470,7 @@ private module FlowExploration {
pragma[nomagic]
private predicate partialPathIntoCallable0(
PartialPathNodePriv mid, DataFlowCallable callable, int i, CallContext outercc,
PartialPathNodeFwd mid, DataFlowCallable callable, int i, CallContext outercc,
DataFlowCall call, PartialAccessPath ap, Configuration config
) {
partialPathIntoArg(mid, i, outercc, call, ap, config) and
@@ -3319,7 +3478,7 @@ private module FlowExploration {
}
private predicate partialPathIntoCallable(
PartialPathNodePriv mid, ParameterNode p, CallContext outercc, CallContextCall innercc,
PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc,
TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap,
Configuration config
) {
@@ -3340,7 +3499,7 @@ private module FlowExploration {
ReturnKindExt kind, CallContextCall cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
PartialAccessPath ap, Configuration config
) {
exists(PartialPathNodePriv mid, ReturnNodeExt ret |
exists(PartialPathNodeFwd mid, ReturnNodeExt ret |
mid.getNode() = ret and
kind = ret.getKind() and
cc = mid.getCallContext() and
@@ -3353,7 +3512,7 @@ private module FlowExploration {
pragma[noinline]
private predicate partialPathThroughCallable0(
DataFlowCall call, PartialPathNodePriv mid, ReturnKindExt kind, CallContext cc,
DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc,
PartialAccessPath ap, Configuration config
) {
exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 |
@@ -3363,13 +3522,164 @@ private module FlowExploration {
}
private predicate partialPathThroughCallable(
PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
PartialPathNodeFwd mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
) {
exists(DataFlowCall call, ReturnKindExt kind |
partialPathThroughCallable0(call, mid, kind, cc, ap, config) and
out = kind.getAnOutNode(call)
)
}
private predicate revPartialPathStep(
PartialPathNodeRev mid, Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2,
RevPartialAccessPath ap, Configuration config
) {
localFlowStep(node, mid.getNode(), config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
ap = mid.getAp() and
config = mid.getConfiguration()
or
additionalLocalFlowStep(node, mid.getNode(), config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
mid.getAp() instanceof RevPartialAccessPathNil and
ap = TRevPartialNil() and
config = mid.getConfiguration()
or
jumpStep(node, mid.getNode(), config) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = mid.getAp() and
config = mid.getConfiguration()
or
additionalJumpStep(node, mid.getNode(), config) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
mid.getAp() instanceof RevPartialAccessPathNil and
ap = TRevPartialNil() and
config = mid.getConfiguration()
or
revPartialPathReadStep(mid, _, _, node, ap) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
config = mid.getConfiguration()
or
exists(RevPartialAccessPath ap0, Content c |
revPartialPathStoreStep(mid, ap0, c, node, config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
apConsRev(ap, c, ap0, config)
)
or
exists(ParameterNode p |
mid.getNode() = p and
viableParamArg(_, p, node) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = mid.getAp() and
config = mid.getConfiguration()
)
or
exists(ReturnPosition pos |
revPartialPathIntoReturn(mid, pos, sc1, sc2, _, ap, config) and
pos = getReturnPosition(node)
)
or
revPartialPathThroughCallable(mid, node, ap, config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2()
}
pragma[inline]
private predicate revPartialPathReadStep(
PartialPathNodeRev mid, RevPartialAccessPath ap1, Content c, Node node, RevPartialAccessPath ap2
) {
exists(Node midNode |
midNode = mid.getNode() and
ap1 = mid.getAp() and
read(node, c, midNode) and
ap2.getHead() = c and
ap2.len() = unbindInt(ap1.len() + 1)
)
}
pragma[nomagic]
private predicate apConsRev(
RevPartialAccessPath ap1, Content c, RevPartialAccessPath ap2, Configuration config
) {
exists(PartialPathNodeRev mid |
revPartialPathReadStep(mid, ap1, c, _, ap2) and
config = mid.getConfiguration()
)
}
pragma[nomagic]
private predicate revPartialPathStoreStep(
PartialPathNodeRev mid, RevPartialAccessPath ap, Content c, Node node, Configuration config
) {
exists(Node midNode, TypedContent tc |
midNode = mid.getNode() and
ap = mid.getAp() and
store(node, tc, midNode, _) and
ap.getHead() = c and
config = mid.getConfiguration() and
tc.getContent() = c
)
}
pragma[nomagic]
private predicate revPartialPathIntoReturn(
PartialPathNodeRev mid, ReturnPosition pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2,
DataFlowCall call, RevPartialAccessPath ap, Configuration config
) {
exists(Node out |
mid.getNode() = out and
viableReturnPosOut(call, pos, out) and
sc1 = TRevSummaryCtx1Some(pos) and
sc2 = TRevSummaryCtx2Some(ap) and
ap = mid.getAp() and
config = mid.getConfiguration()
)
}
pragma[nomagic]
private predicate revPartialPathFlowsThrough(
int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap,
Configuration config
) {
exists(PartialPathNodeRev mid, ParameterNode p |
mid.getNode() = p and
p.isParameterOf(_, pos) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
ap = mid.getAp() and
config = mid.getConfiguration()
)
}
pragma[nomagic]
private predicate revPartialPathThroughCallable0(
DataFlowCall call, PartialPathNodeRev mid, int pos, RevPartialAccessPath ap,
Configuration config
) {
exists(TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2 |
revPartialPathIntoReturn(mid, _, sc1, sc2, call, _, config) and
revPartialPathFlowsThrough(pos, sc1, sc2, ap, config)
)
}
pragma[nomagic]
private predicate revPartialPathThroughCallable(
PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config
) {
exists(DataFlowCall call, int pos |
revPartialPathThroughCallable0(call, mid, pos, ap, config) and
node.argumentOf(call, pos)
)
}
}
import FlowExploration
@@ -3378,6 +3688,14 @@ private predicate partialFlow(
PartialPathNode source, PartialPathNode node, Configuration configuration
) {
source.getConfiguration() = configuration and
configuration.isSource(source.getNode()) and
source.isFwdSource() and
node = source.getASuccessor+()
}
private predicate revPartialFlow(
PartialPathNode node, PartialPathNode sink, Configuration configuration
) {
sink.getConfiguration() = configuration and
sink.isRevSink() and
node.getASuccessor+() = sink
}

View File

@@ -112,8 +112,8 @@ abstract class Configuration extends string {
predicate hasFlowToExpr(DataFlowExpr sink) { hasFlowTo(exprNode(sink)) }
/**
* Gets the exploration limit for `hasPartialFlow` measured in approximate
* number of interprocedural steps.
* Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev`
* measured in approximate number of interprocedural steps.
*/
int explorationLimit() { none() }
@@ -123,7 +123,7 @@ abstract class Configuration extends string {
* is restricted to be less than or equal to `explorationLimit()`. This
* predicate completely disregards sink definitions.
*
* This predicate is intended for dataflow exploration and debugging and may
* This predicate is intended for data-flow exploration and debugging and may
* perform poorly if the number of sources is too big and/or the exploration
* limit is set too high without using barriers.
*
@@ -136,6 +136,29 @@ abstract class Configuration extends string {
partialFlow(source, node, this) and
dist = node.getSourceDistance()
}
/**
* Holds if there is a partial data flow path from `node` to `sink`. The
* approximate distance between `node` and the closest sink is `dist` and
* is restricted to be less than or equal to `explorationLimit()`. This
* predicate completely disregards source definitions.
*
* This predicate is intended for data-flow exploration and debugging and may
* perform poorly if the number of sinks is too big and/or the exploration
* limit is set too high without using barriers.
*
* This predicate is disabled (has no results) by default. Override
* `explorationLimit()` with a suitable number to enable this predicate.
*
* To use this in a `path-problem` query, import the module `PartialPathGraph`.
*
* Note that reverse flow has slightly lower precision than the corresponding
* forward flow, as reverse flow disregards type pruning among other features.
*/
final predicate hasPartialFlowRev(PartialPathNode node, PartialPathNode sink, int dist) {
revPartialFlow(node, sink, this) and
dist = node.getSinkDistance()
}
}
/**
@@ -2943,12 +2966,26 @@ private module FlowExploration {
)
}
private predicate interestingCallableSink(DataFlowCallable c, Configuration config) {
exists(Node n | config.isSink(n) and c = n.getEnclosingCallable())
or
exists(DataFlowCallable mid |
interestingCallableSink(mid, config) and callableStep(c, mid, config)
)
}
private newtype TCallableExt =
TCallable(DataFlowCallable c, Configuration config) { interestingCallableSrc(c, config) } or
TCallableSrc()
TCallable(DataFlowCallable c, Configuration config) {
interestingCallableSrc(c, config) or
interestingCallableSink(c, config)
} or
TCallableSrc() or
TCallableSink()
private predicate callableExtSrc(TCallableSrc src) { any() }
private predicate callableExtSink(TCallableSink sink) { any() }
private predicate callableExtStepFwd(TCallableExt ce1, TCallableExt ce2) {
exists(DataFlowCallable c1, DataFlowCallable c2, Configuration config |
callableStep(c1, c2, config) and
@@ -2961,15 +2998,32 @@ private module FlowExploration {
config.isSource(n) and
ce2 = TCallable(n.getEnclosingCallable(), config)
)
or
exists(Node n, Configuration config |
ce2 = TCallableSink() and
config.isSink(n) and
ce1 = TCallable(n.getEnclosingCallable(), config)
)
}
private predicate callableExtStepRev(TCallableExt ce1, TCallableExt ce2) {
callableExtStepFwd(ce2, ce1)
}
private int distSrcExt(TCallableExt c) =
shortestDistances(callableExtSrc/1, callableExtStepFwd/2)(_, c, result)
private int distSinkExt(TCallableExt c) =
shortestDistances(callableExtSink/1, callableExtStepRev/2)(_, c, result)
private int distSrc(DataFlowCallable c, Configuration config) {
result = distSrcExt(TCallable(c, config)) - 1
}
private int distSink(DataFlowCallable c, Configuration config) {
result = distSinkExt(TCallable(c, config)) - 1
}
private newtype TPartialAccessPath =
TPartialNil(DataFlowType t) or
TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] }
@@ -2997,18 +3051,12 @@ private module FlowExploration {
or
exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType())
}
abstract AccessPathFront getFront();
}
private class PartialAccessPathNil extends PartialAccessPath, TPartialNil {
override string toString() {
exists(DataFlowType t | this = TPartialNil(t) | result = concat(": " + ppReprType(t)))
}
override AccessPathFront getFront() {
exists(DataFlowType t | this = TPartialNil(t) | result = TFrontNil(t))
}
}
private class PartialAccessPathCons extends PartialAccessPath, TPartialCons {
@@ -3019,9 +3067,39 @@ private module FlowExploration {
else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]"
)
}
}
override AccessPathFront getFront() {
exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc))
private newtype TRevPartialAccessPath =
TRevPartialNil() or
TRevPartialCons(Content c, int len) { len in [1 .. accessPathLimit()] }
/**
* Conceptually a list of `Content`s, but only the first
* element of the list and its length are tracked.
*/
private class RevPartialAccessPath extends TRevPartialAccessPath {
abstract string toString();
Content getHead() { this = TRevPartialCons(result, _) }
int len() {
this = TRevPartialNil() and result = 0
or
this = TRevPartialCons(_, result)
}
}
private class RevPartialAccessPathNil extends RevPartialAccessPath, TRevPartialNil {
override string toString() { result = "" }
}
private class RevPartialAccessPathCons extends RevPartialAccessPath, TRevPartialCons {
override string toString() {
exists(Content c, int len | this = TRevPartialCons(c, len) |
if len = 1
then result = "[" + c.toString() + "]"
else result = "[" + c.toString() + ", ... (" + len.toString() + ")]"
)
}
}
@@ -3033,8 +3111,16 @@ private module FlowExploration {
TSummaryCtx2None() or
TSummaryCtx2Some(PartialAccessPath ap)
private newtype TRevSummaryCtx1 =
TRevSummaryCtx1None() or
TRevSummaryCtx1Some(ReturnPosition pos)
private newtype TRevSummaryCtx2 =
TRevSummaryCtx2None() or
TRevSummaryCtx2Some(RevPartialAccessPath ap)
private newtype TPartialPathNode =
TPartialPathNodeMk(
TPartialPathNodeFwd(
Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
Configuration config
) {
@@ -3048,6 +3134,23 @@ private module FlowExploration {
or
partialPathNodeMk0(node, cc, sc1, sc2, ap, config) and
distSrc(node.getEnclosingCallable(), config) <= config.explorationLimit()
} or
TPartialPathNodeRev(
Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, RevPartialAccessPath ap,
Configuration config
) {
config.isSink(node) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = TRevPartialNil() and
not fullBarrier(node, config) and
exists(config.explorationLimit())
or
exists(PartialPathNodeRev mid |
revPartialPathStep(mid, node, sc1, sc2, ap, config) and
not fullBarrier(node, config) and
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
)
}
pragma[nomagic]
@@ -3055,7 +3158,7 @@ private module FlowExploration {
Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
Configuration config
) {
exists(PartialPathNode mid |
exists(PartialPathNodeFwd mid |
partialPathStep(mid, node, cc, sc1, sc2, ap, config) and
not fullBarrier(node, config) and
if node instanceof CastingNode
@@ -3107,15 +3210,32 @@ private module FlowExploration {
result = distSrc(this.getNode().getEnclosingCallable(), this.getConfiguration())
}
/**
* Gets the approximate distance to the nearest sink measured in number
* of interprocedural steps.
*/
int getSinkDistance() {
result = distSink(this.getNode().getEnclosingCallable(), this.getConfiguration())
}
private string ppAp() {
exists(string s | s = this.(PartialPathNodePriv).getAp().toString() |
exists(string s |
s = this.(PartialPathNodeFwd).getAp().toString() or
s = this.(PartialPathNodeRev).getAp().toString()
|
if s = "" then result = "" else result = " " + s
)
}
private string ppCtx() {
result = " <" + this.(PartialPathNodePriv).getCallContext().toString() + ">"
result = " <" + this.(PartialPathNodeFwd).getCallContext().toString() + ">"
}
/** Holds if this is a source in a forward-flow path. */
predicate isFwdSource() { this.(PartialPathNodeFwd).isSource() }
/** Holds if this is a sink in a reverse-flow path. */
predicate isRevSink() { this.(PartialPathNodeRev).isSink() }
}
/**
@@ -3126,7 +3246,7 @@ private module FlowExploration {
query predicate edges(PartialPathNode a, PartialPathNode b) { a.getASuccessor() = b }
}
private class PartialPathNodePriv extends PartialPathNode {
private class PartialPathNodeFwd extends PartialPathNode, TPartialPathNodeFwd {
Node node;
CallContext cc;
TSummaryCtx1 sc1;
@@ -3134,7 +3254,7 @@ private module FlowExploration {
PartialAccessPath ap;
Configuration config;
PartialPathNodePriv() { this = TPartialPathNodeMk(node, cc, sc1, sc2, ap, config) }
PartialPathNodeFwd() { this = TPartialPathNodeFwd(node, cc, sc1, sc2, ap, config) }
override Node getNode() { result = node }
@@ -3148,14 +3268,54 @@ private module FlowExploration {
override Configuration getConfiguration() { result = config }
override PartialPathNodePriv getASuccessor() {
override PartialPathNodeFwd getASuccessor() {
partialPathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx1(),
result.getSummaryCtx2(), result.getAp(), result.getConfiguration())
}
predicate isSource() {
config.isSource(node) and
cc instanceof CallContextAny and
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
ap instanceof TPartialNil
}
}
private class PartialPathNodeRev extends PartialPathNode, TPartialPathNodeRev {
Node node;
TRevSummaryCtx1 sc1;
TRevSummaryCtx2 sc2;
RevPartialAccessPath ap;
Configuration config;
PartialPathNodeRev() { this = TPartialPathNodeRev(node, sc1, sc2, ap, config) }
override Node getNode() { result = node }
TRevSummaryCtx1 getSummaryCtx1() { result = sc1 }
TRevSummaryCtx2 getSummaryCtx2() { result = sc2 }
RevPartialAccessPath getAp() { result = ap }
override Configuration getConfiguration() { result = config }
override PartialPathNodeRev getASuccessor() {
revPartialPathStep(result, this.getNode(), this.getSummaryCtx1(), this.getSummaryCtx2(),
this.getAp(), this.getConfiguration())
}
predicate isSink() {
config.isSink(node) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = TRevPartialNil()
}
}
private predicate partialPathStep(
PartialPathNodePriv mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
PartialAccessPath ap, Configuration config
) {
not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and
@@ -3221,8 +3381,7 @@ private module FlowExploration {
pragma[inline]
private predicate partialPathStoreStep(
PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node,
PartialAccessPath ap2
PartialPathNodeFwd mid, PartialAccessPath ap1, TypedContent tc, Node node, PartialAccessPath ap2
) {
exists(Node midNode, DataFlowType contentType |
midNode = mid.getNode() and
@@ -3238,7 +3397,7 @@ private module FlowExploration {
private predicate apConsFwd(
PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config
) {
exists(PartialPathNodePriv mid |
exists(PartialPathNodeFwd mid |
partialPathStoreStep(mid, ap1, tc, _, ap2) and
config = mid.getConfiguration()
)
@@ -3246,7 +3405,7 @@ private module FlowExploration {
pragma[nomagic]
private predicate partialPathReadStep(
PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
PartialPathNodeFwd mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
Configuration config
) {
exists(Node midNode |
@@ -3260,7 +3419,7 @@ private module FlowExploration {
}
private predicate partialPathOutOfCallable0(
PartialPathNodePriv mid, ReturnPosition pos, CallContext innercc, PartialAccessPath ap,
PartialPathNodeFwd mid, ReturnPosition pos, CallContext innercc, PartialAccessPath ap,
Configuration config
) {
pos = getReturnPosition(mid.getNode()) and
@@ -3272,7 +3431,7 @@ private module FlowExploration {
pragma[nomagic]
private predicate partialPathOutOfCallable1(
PartialPathNodePriv mid, DataFlowCall call, ReturnKindExt kind, CallContext cc,
PartialPathNodeFwd mid, DataFlowCall call, ReturnKindExt kind, CallContext cc,
PartialAccessPath ap, Configuration config
) {
exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc |
@@ -3286,7 +3445,7 @@ private module FlowExploration {
}
private predicate partialPathOutOfCallable(
PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
PartialPathNodeFwd mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
) {
exists(ReturnKindExt kind, DataFlowCall call |
partialPathOutOfCallable1(mid, call, kind, cc, ap, config)
@@ -3297,7 +3456,7 @@ private module FlowExploration {
pragma[noinline]
private predicate partialPathIntoArg(
PartialPathNodePriv mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap,
PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap,
Configuration config
) {
exists(ArgumentNode arg |
@@ -3311,7 +3470,7 @@ private module FlowExploration {
pragma[nomagic]
private predicate partialPathIntoCallable0(
PartialPathNodePriv mid, DataFlowCallable callable, int i, CallContext outercc,
PartialPathNodeFwd mid, DataFlowCallable callable, int i, CallContext outercc,
DataFlowCall call, PartialAccessPath ap, Configuration config
) {
partialPathIntoArg(mid, i, outercc, call, ap, config) and
@@ -3319,7 +3478,7 @@ private module FlowExploration {
}
private predicate partialPathIntoCallable(
PartialPathNodePriv mid, ParameterNode p, CallContext outercc, CallContextCall innercc,
PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc,
TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap,
Configuration config
) {
@@ -3340,7 +3499,7 @@ private module FlowExploration {
ReturnKindExt kind, CallContextCall cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
PartialAccessPath ap, Configuration config
) {
exists(PartialPathNodePriv mid, ReturnNodeExt ret |
exists(PartialPathNodeFwd mid, ReturnNodeExt ret |
mid.getNode() = ret and
kind = ret.getKind() and
cc = mid.getCallContext() and
@@ -3353,7 +3512,7 @@ private module FlowExploration {
pragma[noinline]
private predicate partialPathThroughCallable0(
DataFlowCall call, PartialPathNodePriv mid, ReturnKindExt kind, CallContext cc,
DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc,
PartialAccessPath ap, Configuration config
) {
exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 |
@@ -3363,13 +3522,164 @@ private module FlowExploration {
}
private predicate partialPathThroughCallable(
PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
PartialPathNodeFwd mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
) {
exists(DataFlowCall call, ReturnKindExt kind |
partialPathThroughCallable0(call, mid, kind, cc, ap, config) and
out = kind.getAnOutNode(call)
)
}
private predicate revPartialPathStep(
PartialPathNodeRev mid, Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2,
RevPartialAccessPath ap, Configuration config
) {
localFlowStep(node, mid.getNode(), config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
ap = mid.getAp() and
config = mid.getConfiguration()
or
additionalLocalFlowStep(node, mid.getNode(), config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
mid.getAp() instanceof RevPartialAccessPathNil and
ap = TRevPartialNil() and
config = mid.getConfiguration()
or
jumpStep(node, mid.getNode(), config) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = mid.getAp() and
config = mid.getConfiguration()
or
additionalJumpStep(node, mid.getNode(), config) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
mid.getAp() instanceof RevPartialAccessPathNil and
ap = TRevPartialNil() and
config = mid.getConfiguration()
or
revPartialPathReadStep(mid, _, _, node, ap) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
config = mid.getConfiguration()
or
exists(RevPartialAccessPath ap0, Content c |
revPartialPathStoreStep(mid, ap0, c, node, config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
apConsRev(ap, c, ap0, config)
)
or
exists(ParameterNode p |
mid.getNode() = p and
viableParamArg(_, p, node) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = mid.getAp() and
config = mid.getConfiguration()
)
or
exists(ReturnPosition pos |
revPartialPathIntoReturn(mid, pos, sc1, sc2, _, ap, config) and
pos = getReturnPosition(node)
)
or
revPartialPathThroughCallable(mid, node, ap, config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2()
}
pragma[inline]
private predicate revPartialPathReadStep(
PartialPathNodeRev mid, RevPartialAccessPath ap1, Content c, Node node, RevPartialAccessPath ap2
) {
exists(Node midNode |
midNode = mid.getNode() and
ap1 = mid.getAp() and
read(node, c, midNode) and
ap2.getHead() = c and
ap2.len() = unbindInt(ap1.len() + 1)
)
}
pragma[nomagic]
private predicate apConsRev(
RevPartialAccessPath ap1, Content c, RevPartialAccessPath ap2, Configuration config
) {
exists(PartialPathNodeRev mid |
revPartialPathReadStep(mid, ap1, c, _, ap2) and
config = mid.getConfiguration()
)
}
pragma[nomagic]
private predicate revPartialPathStoreStep(
PartialPathNodeRev mid, RevPartialAccessPath ap, Content c, Node node, Configuration config
) {
exists(Node midNode, TypedContent tc |
midNode = mid.getNode() and
ap = mid.getAp() and
store(node, tc, midNode, _) and
ap.getHead() = c and
config = mid.getConfiguration() and
tc.getContent() = c
)
}
pragma[nomagic]
private predicate revPartialPathIntoReturn(
PartialPathNodeRev mid, ReturnPosition pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2,
DataFlowCall call, RevPartialAccessPath ap, Configuration config
) {
exists(Node out |
mid.getNode() = out and
viableReturnPosOut(call, pos, out) and
sc1 = TRevSummaryCtx1Some(pos) and
sc2 = TRevSummaryCtx2Some(ap) and
ap = mid.getAp() and
config = mid.getConfiguration()
)
}
pragma[nomagic]
private predicate revPartialPathFlowsThrough(
int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap,
Configuration config
) {
exists(PartialPathNodeRev mid, ParameterNode p |
mid.getNode() = p and
p.isParameterOf(_, pos) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
ap = mid.getAp() and
config = mid.getConfiguration()
)
}
pragma[nomagic]
private predicate revPartialPathThroughCallable0(
DataFlowCall call, PartialPathNodeRev mid, int pos, RevPartialAccessPath ap,
Configuration config
) {
exists(TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2 |
revPartialPathIntoReturn(mid, _, sc1, sc2, call, _, config) and
revPartialPathFlowsThrough(pos, sc1, sc2, ap, config)
)
}
pragma[nomagic]
private predicate revPartialPathThroughCallable(
PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config
) {
exists(DataFlowCall call, int pos |
revPartialPathThroughCallable0(call, mid, pos, ap, config) and
node.argumentOf(call, pos)
)
}
}
import FlowExploration
@@ -3378,6 +3688,14 @@ private predicate partialFlow(
PartialPathNode source, PartialPathNode node, Configuration configuration
) {
source.getConfiguration() = configuration and
configuration.isSource(source.getNode()) and
source.isFwdSource() and
node = source.getASuccessor+()
}
private predicate revPartialFlow(
PartialPathNode node, PartialPathNode sink, Configuration configuration
) {
sink.getConfiguration() = configuration and
sink.isRevSink() and
node.getASuccessor+() = sink
}

View File

@@ -677,6 +677,11 @@ private predicate exprToExprStep_nocfg(Expr fromExpr, Expr toExpr) {
exists(DataFlowFunction f, FunctionInput inModel, FunctionOutput outModel |
f.hasDataFlow(inModel, outModel) and
(
exists(int iIn |
inModel.isParameterDeref(iIn) and
call.passesByReference(iIn, fromExpr)
)
or
exists(int iIn |
inModel.isParameter(iIn) and
fromExpr = call.getArgument(iIn)

View File

@@ -620,8 +620,7 @@ module FlowVar_internal {
private predicate largeVariable(Variable v, int liveBlocks, int defs) {
liveBlocks = strictcount(SubBasicBlock sbb | variableLiveInSBB(sbb, v)) and
defs = strictcount(SubBasicBlock sbb | exists(TBlockVar(sbb, v))) and
// Convert to float to avoid int overflow (32-bit two's complement)
liveBlocks.(float) * defs.(float) > 100000.0
liveBlocks * defs > 1000000
}
/**

View File

@@ -441,40 +441,6 @@ class ConstructorCall extends FunctionCall {
override Constructor getTarget() { result = super.getTarget() }
}
/**
* A C++ `throw` expression.
* ```
* throw Exc(2);
* ```
*/
class ThrowExpr extends Expr, @throw_expr {
/**
* Gets the expression that will be thrown, if any. There is no result if
* `this` is a `ReThrowExpr`.
*/
Expr getExpr() { result = this.getChild(0) }
override string getAPrimaryQlClass() { result = "ThrowExpr" }
override string toString() { result = "throw ..." }
override int getPrecedence() { result = 1 }
}
/**
* A C++ `throw` expression with no argument (which causes the current exception to be re-thrown).
* ```
* throw;
* ```
*/
class ReThrowExpr extends ThrowExpr {
ReThrowExpr() { this.getType() instanceof VoidType }
override string getAPrimaryQlClass() { result = "ReThrowExpr" }
override string toString() { result = "re-throw exception " }
}
/**
* A call to a destructor.
* ```

View File

@@ -840,6 +840,28 @@ class ArrayToPointerConversion extends Conversion, @array_to_pointer {
override predicate mayBeGloballyImpure() { none() }
}
/**
* A node representing a temporary object created as part of an expression.
*
* This is most commonly seen in the following cases:
* ```c++
* // when binding a reference to a prvalue
* const std::string& r = std::string("text");
*
* // when performing member access on a class prvalue
* strlen(std::string("text").c_str());
*
* // when a prvalue of a type with a destructor is discarded
* s.substr(0, 5); // Return value is discarded but requires destruction
* ```
*/
class TemporaryObjectExpr extends Conversion, @temp_init {
/** Gets a textual representation of this conversion. */
override string toString() { result = "temporary object" }
override string getAPrimaryQlClass() { result = "TemporaryObjectExpr" }
}
/**
* A node representing the Cast sub-class of entity `cast`.
*/

View File

@@ -64,7 +64,7 @@ class RelationalOperation extends ComparisonOperation, @rel_op_expr {
* if the overall expression evaluates to `true`; for example on
* `x <= 20` this is the `20`, and on `y > 0` it is `y`.
*/
abstract Expr getGreaterOperand();
Expr getGreaterOperand() { none() } // overridden in subclasses
/**
* Gets the operand on the "lesser" (or "lesser-or-equal") side
@@ -72,7 +72,7 @@ class RelationalOperation extends ComparisonOperation, @rel_op_expr {
* if the overall expression evaluates to `true`; for example on
* `x <= 20` this is `x`, and on `y > 0` it is the `0`.
*/
abstract Expr getLesserOperand();
Expr getLesserOperand() { none() } // overridden in subclasses
}
/**

View File

@@ -6,7 +6,6 @@ import semmle.code.cpp.Element
private import semmle.code.cpp.Enclosing
private import semmle.code.cpp.internal.ResolveClass
private import semmle.code.cpp.internal.AddressConstantExpression
private import semmle.code.cpp.models.implementations.Allocation
/**
* A C/C++ expression.
@@ -839,7 +838,7 @@ class NewOrNewArrayExpr extends Expr, @any_new_expr {
* For example, for `new int` the result is `int`.
* For `new int[5]` the result is `int[5]`.
*/
abstract Type getAllocatedType();
Type getAllocatedType() { none() } // overridden in subclasses
/**
* Gets the pointer `p` if this expression is of the form `new(p) T...`.
@@ -1146,6 +1145,40 @@ class BlockExpr extends Literal {
Function getFunction() { code_block(underlyingElement(this), unresolveElement(result)) }
}
/**
* A C++ `throw` expression.
* ```
* throw Exc(2);
* ```
*/
class ThrowExpr extends Expr, @throw_expr {
/**
* Gets the expression that will be thrown, if any. There is no result if
* `this` is a `ReThrowExpr`.
*/
Expr getExpr() { result = this.getChild(0) }
override string getAPrimaryQlClass() { result = "ThrowExpr" }
override string toString() { result = "throw ..." }
override int getPrecedence() { result = 1 }
}
/**
* A C++ `throw` expression with no argument (which causes the current exception to be re-thrown).
* ```
* throw;
* ```
*/
class ReThrowExpr extends ThrowExpr {
ReThrowExpr() { this.getType() instanceof VoidType }
override string getAPrimaryQlClass() { result = "ReThrowExpr" }
override string toString() { result = "re-throw exception " }
}
/**
* A C++11 `noexcept` expression, returning `true` if its subexpression is guaranteed
* not to `throw` exceptions. For example:

View File

@@ -47,7 +47,17 @@ class LabelLiteral extends Literal {
}
/** A character literal or a string literal. */
abstract class TextLiteral extends Literal {
class TextLiteral extends Literal {
TextLiteral() {
// String Literal
// Note that `AggregateLiteral`s can also have an array type, but they derive from
// @aggregateliteral rather than @literal.
this.getType() instanceof ArrayType
or
// Char literal
this.getValueText().regexpMatch("(?s)\\s*L?'.*")
}
/** Gets a hex escape sequence that appears in the character or string literal (see [lex.ccon] in the C++ Standard). */
string getAHexEscapeSequence(int occurrence, int offset) {
result = getValueText().regexpFind("(?<!\\\\)\\\\x[0-9a-fA-F]+", occurrence, offset)

View File

@@ -39,7 +39,7 @@ class BinaryLogicalOperation extends BinaryOperation, @bin_log_op_expr {
* is true, `x` and `y` must also be true, so `impliesValue(x, true, true)` and
* `impliesValue(y, true, true)` hold.
*/
abstract predicate impliesValue(Expr part, boolean partIsTrue, boolean wholeIsTrue);
predicate impliesValue(Expr part, boolean partIsTrue, boolean wholeIsTrue) { none() } // overridden in subclasses
}
/**

View File

@@ -59,7 +59,7 @@ class Namespace extends @namespace {
}
}
abstract class Declaration extends @declaration {
class Declaration extends @declaration {
string toString() { result = "QualifiedName Declaration" }
/** Gets the name of this declaration. */

View File

@@ -83,7 +83,7 @@ private class DefaultTaintTrackingCfg extends DataFlow::Configuration {
override predicate isSink(DataFlow::Node sink) { exists(adjustedSink(sink)) }
override predicate isAdditionalFlowStep(DataFlow::Node n1, DataFlow::Node n2) {
instructionTaintStep(n1.asInstruction(), n2.asInstruction())
commonTaintStep(n1, n2)
}
override predicate isBarrier(DataFlow::Node node) { nodeIsBarrier(node) }
@@ -101,7 +101,7 @@ private class ToGlobalVarTaintTrackingCfg extends DataFlow::Configuration {
}
override predicate isAdditionalFlowStep(DataFlow::Node n1, DataFlow::Node n2) {
instructionTaintStep(n1.asInstruction(), n2.asInstruction())
commonTaintStep(n1, n2)
or
writesVariable(n1.asInstruction(), n2.asVariable().(GlobalOrNamespaceVariable))
or
@@ -125,7 +125,7 @@ private class FromGlobalVarTaintTrackingCfg extends DataFlow2::Configuration {
override predicate isSink(DataFlow::Node sink) { exists(adjustedSink(sink)) }
override predicate isAdditionalFlowStep(DataFlow::Node n1, DataFlow::Node n2) {
instructionTaintStep(n1.asInstruction(), n2.asInstruction())
commonTaintStep(n1, n2)
or
// Additional step for flow out of variables. There is no flow _into_
// variables in this configuration, so this step only serves to take flow
@@ -215,19 +215,62 @@ private predicate nodeIsBarrierIn(DataFlow::Node node) {
}
cached
private predicate instructionTaintStep(Instruction i1, Instruction i2) {
private predicate commonTaintStep(DataFlow::Node fromNode, DataFlow::Node toNode) {
instructionToInstructionTaintStep(fromNode.asInstruction(), toNode.asInstruction())
or
operandToInstructionTaintStep(fromNode.asOperand(), toNode.asInstruction())
or
operandToOperandTaintStep(fromNode.asOperand(), toNode.asOperand())
}
private predicate operandToOperandTaintStep(Operand fromOperand, Operand toOperand) {
exists(ReadSideEffectInstruction readInstr |
fromOperand = readInstr.getArgumentOperand() and
toOperand = readInstr.getSideEffectOperand()
)
}
private predicate operandToInstructionTaintStep(Operand fromOperand, Instruction toInstr) {
// Expressions computed from tainted data are also tainted
exists(CallInstruction call, int argIndex | call = i2 |
exists(CallInstruction call, int argIndex | call = toInstr |
isPureFunction(call.getStaticCallTarget().getName()) and
i1 = getACallArgumentOrIndirection(call, argIndex) and
forall(Instruction arg | arg = call.getAnArgument() |
arg = getACallArgumentOrIndirection(call, argIndex) or predictableInstruction(arg)
fromOperand = getACallArgumentOrIndirection(call, argIndex) and
forall(Operand argOperand | argOperand = call.getAnArgumentOperand() |
argOperand = getACallArgumentOrIndirection(call, argIndex) or
predictableInstruction(argOperand.getAnyDef())
) and
// flow through `strlen` tends to cause dubious results, if the length is
// bounded.
not call.getStaticCallTarget().getName() = "strlen"
)
or
// Flow from argument to return value
toInstr =
any(CallInstruction call |
exists(int indexIn |
modelTaintToReturnValue(call.getStaticCallTarget(), indexIn) and
fromOperand = getACallArgumentOrIndirection(call, indexIn) and
not predictableOnlyFlow(call.getStaticCallTarget().getName())
)
)
or
// Flow from input argument to output argument
// TODO: This won't work in practice as long as all aliased memory is tracked
// together in a single virtual variable.
// TODO: Will this work on the test for `TaintedPath.ql`, where the output arg
// is a pointer addition expression?
toInstr =
any(WriteSideEffectInstruction outInstr |
exists(CallInstruction call, int indexIn, int indexOut |
modelTaintToParameter(call.getStaticCallTarget(), indexIn, indexOut) and
fromOperand = getACallArgumentOrIndirection(call, indexIn) and
outInstr.getIndex() = indexOut and
outInstr.getPrimaryInstruction() = call
)
)
}
private predicate instructionToInstructionTaintStep(Instruction i1, Instruction i2) {
// Flow through pointer dereference
i2.(LoadInstruction).getSourceAddress() = i1
or
@@ -291,29 +334,6 @@ private predicate instructionTaintStep(Instruction i1, Instruction i2) {
read.getAnOperand().(SideEffectOperand).getAnyDef() = i1 and
read.getArgumentDef() = i2
)
or
// Flow from argument to return value
i2 =
any(CallInstruction call |
exists(int indexIn |
modelTaintToReturnValue(call.getStaticCallTarget(), indexIn) and
i1 = getACallArgumentOrIndirection(call, indexIn) and
not predictableOnlyFlow(call.getStaticCallTarget().getName())
)
)
or
// Flow from input argument to output argument
// TODO: Will this work on the test for `TaintedPath.ql`, where the output arg
// is a pointer addition expression?
i2 =
any(WriteSideEffectInstruction outNode |
exists(CallInstruction call, int indexIn, int indexOut |
modelTaintToParameter(call.getStaticCallTarget(), indexIn, indexOut) and
i1 = getACallArgumentOrIndirection(call, indexIn) and
outNode.getIndex() = indexOut and
outNode.getPrimaryInstruction() = call
)
)
}
pragma[noinline]
@@ -329,15 +349,25 @@ private InitializeParameterInstruction getInitializeParameter(IRFunction f, Para
}
/**
* Get an instruction that goes into argument `argumentIndex` of `call`. This
* Returns the index of the side effect instruction corresponding to the specified function output,
* if one exists.
*/
private int getWriteSideEffectIndex(FunctionOutput output) {
output.isParameterDeref(result)
or
output.isQualifierObject() and result = -1
}
/**
* Get an operand that goes into argument `argumentIndex` of `call`. This
* can be either directly or through one pointer indirection.
*/
private Instruction getACallArgumentOrIndirection(CallInstruction call, int argumentIndex) {
result = call.getPositionalArgument(argumentIndex)
private Operand getACallArgumentOrIndirection(CallInstruction call, int argumentIndex) {
result = call.getPositionalArgumentOperand(argumentIndex)
or
exists(ReadSideEffectInstruction readSE |
// TODO: why are read side effect operands imprecise?
result = readSE.getSideEffectOperand().getAnyDef() and
result = readSE.getSideEffectOperand() and
readSE.getPrimaryInstruction() = call and
readSE.getIndex() = argumentIndex
)
@@ -351,7 +381,7 @@ private predicate modelTaintToParameter(Function f, int parameterIn, int paramet
f.(TaintFunction).hasTaintFlow(modelIn, modelOut)
) and
(modelIn.isParameter(parameterIn) or modelIn.isParameterDeref(parameterIn)) and
modelOut.isParameterDeref(parameterOut)
parameterOut = getWriteSideEffectIndex(modelOut)
)
}
@@ -540,7 +570,7 @@ module TaintedWithPath {
}
override predicate isAdditionalFlowStep(DataFlow::Node n1, DataFlow::Node n2) {
instructionTaintStep(n1.asInstruction(), n2.asInstruction())
commonTaintStep(n1, n2)
or
exists(TaintTrackingConfiguration cfg | cfg.taintThroughGlobals() |
writesVariable(n1.asInstruction(), n2.asVariable().(GlobalOrNamespaceVariable))

View File

@@ -112,8 +112,8 @@ abstract class Configuration extends string {
predicate hasFlowToExpr(DataFlowExpr sink) { hasFlowTo(exprNode(sink)) }
/**
* Gets the exploration limit for `hasPartialFlow` measured in approximate
* number of interprocedural steps.
* Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev`
* measured in approximate number of interprocedural steps.
*/
int explorationLimit() { none() }
@@ -123,7 +123,7 @@ abstract class Configuration extends string {
* is restricted to be less than or equal to `explorationLimit()`. This
* predicate completely disregards sink definitions.
*
* This predicate is intended for dataflow exploration and debugging and may
* This predicate is intended for data-flow exploration and debugging and may
* perform poorly if the number of sources is too big and/or the exploration
* limit is set too high without using barriers.
*
@@ -136,6 +136,29 @@ abstract class Configuration extends string {
partialFlow(source, node, this) and
dist = node.getSourceDistance()
}
/**
* Holds if there is a partial data flow path from `node` to `sink`. The
* approximate distance between `node` and the closest sink is `dist` and
* is restricted to be less than or equal to `explorationLimit()`. This
* predicate completely disregards source definitions.
*
* This predicate is intended for data-flow exploration and debugging and may
* perform poorly if the number of sinks is too big and/or the exploration
* limit is set too high without using barriers.
*
* This predicate is disabled (has no results) by default. Override
* `explorationLimit()` with a suitable number to enable this predicate.
*
* To use this in a `path-problem` query, import the module `PartialPathGraph`.
*
* Note that reverse flow has slightly lower precision than the corresponding
* forward flow, as reverse flow disregards type pruning among other features.
*/
final predicate hasPartialFlowRev(PartialPathNode node, PartialPathNode sink, int dist) {
revPartialFlow(node, sink, this) and
dist = node.getSinkDistance()
}
}
/**
@@ -2943,12 +2966,26 @@ private module FlowExploration {
)
}
private predicate interestingCallableSink(DataFlowCallable c, Configuration config) {
exists(Node n | config.isSink(n) and c = n.getEnclosingCallable())
or
exists(DataFlowCallable mid |
interestingCallableSink(mid, config) and callableStep(c, mid, config)
)
}
private newtype TCallableExt =
TCallable(DataFlowCallable c, Configuration config) { interestingCallableSrc(c, config) } or
TCallableSrc()
TCallable(DataFlowCallable c, Configuration config) {
interestingCallableSrc(c, config) or
interestingCallableSink(c, config)
} or
TCallableSrc() or
TCallableSink()
private predicate callableExtSrc(TCallableSrc src) { any() }
private predicate callableExtSink(TCallableSink sink) { any() }
private predicate callableExtStepFwd(TCallableExt ce1, TCallableExt ce2) {
exists(DataFlowCallable c1, DataFlowCallable c2, Configuration config |
callableStep(c1, c2, config) and
@@ -2961,15 +2998,32 @@ private module FlowExploration {
config.isSource(n) and
ce2 = TCallable(n.getEnclosingCallable(), config)
)
or
exists(Node n, Configuration config |
ce2 = TCallableSink() and
config.isSink(n) and
ce1 = TCallable(n.getEnclosingCallable(), config)
)
}
private predicate callableExtStepRev(TCallableExt ce1, TCallableExt ce2) {
callableExtStepFwd(ce2, ce1)
}
private int distSrcExt(TCallableExt c) =
shortestDistances(callableExtSrc/1, callableExtStepFwd/2)(_, c, result)
private int distSinkExt(TCallableExt c) =
shortestDistances(callableExtSink/1, callableExtStepRev/2)(_, c, result)
private int distSrc(DataFlowCallable c, Configuration config) {
result = distSrcExt(TCallable(c, config)) - 1
}
private int distSink(DataFlowCallable c, Configuration config) {
result = distSinkExt(TCallable(c, config)) - 1
}
private newtype TPartialAccessPath =
TPartialNil(DataFlowType t) or
TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] }
@@ -2997,18 +3051,12 @@ private module FlowExploration {
or
exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType())
}
abstract AccessPathFront getFront();
}
private class PartialAccessPathNil extends PartialAccessPath, TPartialNil {
override string toString() {
exists(DataFlowType t | this = TPartialNil(t) | result = concat(": " + ppReprType(t)))
}
override AccessPathFront getFront() {
exists(DataFlowType t | this = TPartialNil(t) | result = TFrontNil(t))
}
}
private class PartialAccessPathCons extends PartialAccessPath, TPartialCons {
@@ -3019,9 +3067,39 @@ private module FlowExploration {
else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]"
)
}
}
override AccessPathFront getFront() {
exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc))
private newtype TRevPartialAccessPath =
TRevPartialNil() or
TRevPartialCons(Content c, int len) { len in [1 .. accessPathLimit()] }
/**
* Conceptually a list of `Content`s, but only the first
* element of the list and its length are tracked.
*/
private class RevPartialAccessPath extends TRevPartialAccessPath {
abstract string toString();
Content getHead() { this = TRevPartialCons(result, _) }
int len() {
this = TRevPartialNil() and result = 0
or
this = TRevPartialCons(_, result)
}
}
private class RevPartialAccessPathNil extends RevPartialAccessPath, TRevPartialNil {
override string toString() { result = "" }
}
private class RevPartialAccessPathCons extends RevPartialAccessPath, TRevPartialCons {
override string toString() {
exists(Content c, int len | this = TRevPartialCons(c, len) |
if len = 1
then result = "[" + c.toString() + "]"
else result = "[" + c.toString() + ", ... (" + len.toString() + ")]"
)
}
}
@@ -3033,8 +3111,16 @@ private module FlowExploration {
TSummaryCtx2None() or
TSummaryCtx2Some(PartialAccessPath ap)
private newtype TRevSummaryCtx1 =
TRevSummaryCtx1None() or
TRevSummaryCtx1Some(ReturnPosition pos)
private newtype TRevSummaryCtx2 =
TRevSummaryCtx2None() or
TRevSummaryCtx2Some(RevPartialAccessPath ap)
private newtype TPartialPathNode =
TPartialPathNodeMk(
TPartialPathNodeFwd(
Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
Configuration config
) {
@@ -3048,6 +3134,23 @@ private module FlowExploration {
or
partialPathNodeMk0(node, cc, sc1, sc2, ap, config) and
distSrc(node.getEnclosingCallable(), config) <= config.explorationLimit()
} or
TPartialPathNodeRev(
Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, RevPartialAccessPath ap,
Configuration config
) {
config.isSink(node) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = TRevPartialNil() and
not fullBarrier(node, config) and
exists(config.explorationLimit())
or
exists(PartialPathNodeRev mid |
revPartialPathStep(mid, node, sc1, sc2, ap, config) and
not fullBarrier(node, config) and
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
)
}
pragma[nomagic]
@@ -3055,7 +3158,7 @@ private module FlowExploration {
Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
Configuration config
) {
exists(PartialPathNode mid |
exists(PartialPathNodeFwd mid |
partialPathStep(mid, node, cc, sc1, sc2, ap, config) and
not fullBarrier(node, config) and
if node instanceof CastingNode
@@ -3107,15 +3210,32 @@ private module FlowExploration {
result = distSrc(this.getNode().getEnclosingCallable(), this.getConfiguration())
}
/**
* Gets the approximate distance to the nearest sink measured in number
* of interprocedural steps.
*/
int getSinkDistance() {
result = distSink(this.getNode().getEnclosingCallable(), this.getConfiguration())
}
private string ppAp() {
exists(string s | s = this.(PartialPathNodePriv).getAp().toString() |
exists(string s |
s = this.(PartialPathNodeFwd).getAp().toString() or
s = this.(PartialPathNodeRev).getAp().toString()
|
if s = "" then result = "" else result = " " + s
)
}
private string ppCtx() {
result = " <" + this.(PartialPathNodePriv).getCallContext().toString() + ">"
result = " <" + this.(PartialPathNodeFwd).getCallContext().toString() + ">"
}
/** Holds if this is a source in a forward-flow path. */
predicate isFwdSource() { this.(PartialPathNodeFwd).isSource() }
/** Holds if this is a sink in a reverse-flow path. */
predicate isRevSink() { this.(PartialPathNodeRev).isSink() }
}
/**
@@ -3126,7 +3246,7 @@ private module FlowExploration {
query predicate edges(PartialPathNode a, PartialPathNode b) { a.getASuccessor() = b }
}
private class PartialPathNodePriv extends PartialPathNode {
private class PartialPathNodeFwd extends PartialPathNode, TPartialPathNodeFwd {
Node node;
CallContext cc;
TSummaryCtx1 sc1;
@@ -3134,7 +3254,7 @@ private module FlowExploration {
PartialAccessPath ap;
Configuration config;
PartialPathNodePriv() { this = TPartialPathNodeMk(node, cc, sc1, sc2, ap, config) }
PartialPathNodeFwd() { this = TPartialPathNodeFwd(node, cc, sc1, sc2, ap, config) }
override Node getNode() { result = node }
@@ -3148,14 +3268,54 @@ private module FlowExploration {
override Configuration getConfiguration() { result = config }
override PartialPathNodePriv getASuccessor() {
override PartialPathNodeFwd getASuccessor() {
partialPathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx1(),
result.getSummaryCtx2(), result.getAp(), result.getConfiguration())
}
predicate isSource() {
config.isSource(node) and
cc instanceof CallContextAny and
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
ap instanceof TPartialNil
}
}
private class PartialPathNodeRev extends PartialPathNode, TPartialPathNodeRev {
Node node;
TRevSummaryCtx1 sc1;
TRevSummaryCtx2 sc2;
RevPartialAccessPath ap;
Configuration config;
PartialPathNodeRev() { this = TPartialPathNodeRev(node, sc1, sc2, ap, config) }
override Node getNode() { result = node }
TRevSummaryCtx1 getSummaryCtx1() { result = sc1 }
TRevSummaryCtx2 getSummaryCtx2() { result = sc2 }
RevPartialAccessPath getAp() { result = ap }
override Configuration getConfiguration() { result = config }
override PartialPathNodeRev getASuccessor() {
revPartialPathStep(result, this.getNode(), this.getSummaryCtx1(), this.getSummaryCtx2(),
this.getAp(), this.getConfiguration())
}
predicate isSink() {
config.isSink(node) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = TRevPartialNil()
}
}
private predicate partialPathStep(
PartialPathNodePriv mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
PartialAccessPath ap, Configuration config
) {
not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and
@@ -3221,8 +3381,7 @@ private module FlowExploration {
pragma[inline]
private predicate partialPathStoreStep(
PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node,
PartialAccessPath ap2
PartialPathNodeFwd mid, PartialAccessPath ap1, TypedContent tc, Node node, PartialAccessPath ap2
) {
exists(Node midNode, DataFlowType contentType |
midNode = mid.getNode() and
@@ -3238,7 +3397,7 @@ private module FlowExploration {
private predicate apConsFwd(
PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config
) {
exists(PartialPathNodePriv mid |
exists(PartialPathNodeFwd mid |
partialPathStoreStep(mid, ap1, tc, _, ap2) and
config = mid.getConfiguration()
)
@@ -3246,7 +3405,7 @@ private module FlowExploration {
pragma[nomagic]
private predicate partialPathReadStep(
PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
PartialPathNodeFwd mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
Configuration config
) {
exists(Node midNode |
@@ -3260,7 +3419,7 @@ private module FlowExploration {
}
private predicate partialPathOutOfCallable0(
PartialPathNodePriv mid, ReturnPosition pos, CallContext innercc, PartialAccessPath ap,
PartialPathNodeFwd mid, ReturnPosition pos, CallContext innercc, PartialAccessPath ap,
Configuration config
) {
pos = getReturnPosition(mid.getNode()) and
@@ -3272,7 +3431,7 @@ private module FlowExploration {
pragma[nomagic]
private predicate partialPathOutOfCallable1(
PartialPathNodePriv mid, DataFlowCall call, ReturnKindExt kind, CallContext cc,
PartialPathNodeFwd mid, DataFlowCall call, ReturnKindExt kind, CallContext cc,
PartialAccessPath ap, Configuration config
) {
exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc |
@@ -3286,7 +3445,7 @@ private module FlowExploration {
}
private predicate partialPathOutOfCallable(
PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
PartialPathNodeFwd mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
) {
exists(ReturnKindExt kind, DataFlowCall call |
partialPathOutOfCallable1(mid, call, kind, cc, ap, config)
@@ -3297,7 +3456,7 @@ private module FlowExploration {
pragma[noinline]
private predicate partialPathIntoArg(
PartialPathNodePriv mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap,
PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap,
Configuration config
) {
exists(ArgumentNode arg |
@@ -3311,7 +3470,7 @@ private module FlowExploration {
pragma[nomagic]
private predicate partialPathIntoCallable0(
PartialPathNodePriv mid, DataFlowCallable callable, int i, CallContext outercc,
PartialPathNodeFwd mid, DataFlowCallable callable, int i, CallContext outercc,
DataFlowCall call, PartialAccessPath ap, Configuration config
) {
partialPathIntoArg(mid, i, outercc, call, ap, config) and
@@ -3319,7 +3478,7 @@ private module FlowExploration {
}
private predicate partialPathIntoCallable(
PartialPathNodePriv mid, ParameterNode p, CallContext outercc, CallContextCall innercc,
PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc,
TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap,
Configuration config
) {
@@ -3340,7 +3499,7 @@ private module FlowExploration {
ReturnKindExt kind, CallContextCall cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
PartialAccessPath ap, Configuration config
) {
exists(PartialPathNodePriv mid, ReturnNodeExt ret |
exists(PartialPathNodeFwd mid, ReturnNodeExt ret |
mid.getNode() = ret and
kind = ret.getKind() and
cc = mid.getCallContext() and
@@ -3353,7 +3512,7 @@ private module FlowExploration {
pragma[noinline]
private predicate partialPathThroughCallable0(
DataFlowCall call, PartialPathNodePriv mid, ReturnKindExt kind, CallContext cc,
DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc,
PartialAccessPath ap, Configuration config
) {
exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 |
@@ -3363,13 +3522,164 @@ private module FlowExploration {
}
private predicate partialPathThroughCallable(
PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
PartialPathNodeFwd mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
) {
exists(DataFlowCall call, ReturnKindExt kind |
partialPathThroughCallable0(call, mid, kind, cc, ap, config) and
out = kind.getAnOutNode(call)
)
}
private predicate revPartialPathStep(
PartialPathNodeRev mid, Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2,
RevPartialAccessPath ap, Configuration config
) {
localFlowStep(node, mid.getNode(), config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
ap = mid.getAp() and
config = mid.getConfiguration()
or
additionalLocalFlowStep(node, mid.getNode(), config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
mid.getAp() instanceof RevPartialAccessPathNil and
ap = TRevPartialNil() and
config = mid.getConfiguration()
or
jumpStep(node, mid.getNode(), config) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = mid.getAp() and
config = mid.getConfiguration()
or
additionalJumpStep(node, mid.getNode(), config) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
mid.getAp() instanceof RevPartialAccessPathNil and
ap = TRevPartialNil() and
config = mid.getConfiguration()
or
revPartialPathReadStep(mid, _, _, node, ap) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
config = mid.getConfiguration()
or
exists(RevPartialAccessPath ap0, Content c |
revPartialPathStoreStep(mid, ap0, c, node, config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
apConsRev(ap, c, ap0, config)
)
or
exists(ParameterNode p |
mid.getNode() = p and
viableParamArg(_, p, node) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = mid.getAp() and
config = mid.getConfiguration()
)
or
exists(ReturnPosition pos |
revPartialPathIntoReturn(mid, pos, sc1, sc2, _, ap, config) and
pos = getReturnPosition(node)
)
or
revPartialPathThroughCallable(mid, node, ap, config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2()
}
pragma[inline]
private predicate revPartialPathReadStep(
PartialPathNodeRev mid, RevPartialAccessPath ap1, Content c, Node node, RevPartialAccessPath ap2
) {
exists(Node midNode |
midNode = mid.getNode() and
ap1 = mid.getAp() and
read(node, c, midNode) and
ap2.getHead() = c and
ap2.len() = unbindInt(ap1.len() + 1)
)
}
pragma[nomagic]
private predicate apConsRev(
RevPartialAccessPath ap1, Content c, RevPartialAccessPath ap2, Configuration config
) {
exists(PartialPathNodeRev mid |
revPartialPathReadStep(mid, ap1, c, _, ap2) and
config = mid.getConfiguration()
)
}
pragma[nomagic]
private predicate revPartialPathStoreStep(
PartialPathNodeRev mid, RevPartialAccessPath ap, Content c, Node node, Configuration config
) {
exists(Node midNode, TypedContent tc |
midNode = mid.getNode() and
ap = mid.getAp() and
store(node, tc, midNode, _) and
ap.getHead() = c and
config = mid.getConfiguration() and
tc.getContent() = c
)
}
pragma[nomagic]
private predicate revPartialPathIntoReturn(
PartialPathNodeRev mid, ReturnPosition pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2,
DataFlowCall call, RevPartialAccessPath ap, Configuration config
) {
exists(Node out |
mid.getNode() = out and
viableReturnPosOut(call, pos, out) and
sc1 = TRevSummaryCtx1Some(pos) and
sc2 = TRevSummaryCtx2Some(ap) and
ap = mid.getAp() and
config = mid.getConfiguration()
)
}
pragma[nomagic]
private predicate revPartialPathFlowsThrough(
int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap,
Configuration config
) {
exists(PartialPathNodeRev mid, ParameterNode p |
mid.getNode() = p and
p.isParameterOf(_, pos) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
ap = mid.getAp() and
config = mid.getConfiguration()
)
}
pragma[nomagic]
private predicate revPartialPathThroughCallable0(
DataFlowCall call, PartialPathNodeRev mid, int pos, RevPartialAccessPath ap,
Configuration config
) {
exists(TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2 |
revPartialPathIntoReturn(mid, _, sc1, sc2, call, _, config) and
revPartialPathFlowsThrough(pos, sc1, sc2, ap, config)
)
}
pragma[nomagic]
private predicate revPartialPathThroughCallable(
PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config
) {
exists(DataFlowCall call, int pos |
revPartialPathThroughCallable0(call, mid, pos, ap, config) and
node.argumentOf(call, pos)
)
}
}
import FlowExploration
@@ -3378,6 +3688,14 @@ private predicate partialFlow(
PartialPathNode source, PartialPathNode node, Configuration configuration
) {
source.getConfiguration() = configuration and
configuration.isSource(source.getNode()) and
source.isFwdSource() and
node = source.getASuccessor+()
}
private predicate revPartialFlow(
PartialPathNode node, PartialPathNode sink, Configuration configuration
) {
sink.getConfiguration() = configuration and
sink.isRevSink() and
node.getASuccessor+() = sink
}

View File

@@ -112,8 +112,8 @@ abstract class Configuration extends string {
predicate hasFlowToExpr(DataFlowExpr sink) { hasFlowTo(exprNode(sink)) }
/**
* Gets the exploration limit for `hasPartialFlow` measured in approximate
* number of interprocedural steps.
* Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev`
* measured in approximate number of interprocedural steps.
*/
int explorationLimit() { none() }
@@ -123,7 +123,7 @@ abstract class Configuration extends string {
* is restricted to be less than or equal to `explorationLimit()`. This
* predicate completely disregards sink definitions.
*
* This predicate is intended for dataflow exploration and debugging and may
* This predicate is intended for data-flow exploration and debugging and may
* perform poorly if the number of sources is too big and/or the exploration
* limit is set too high without using barriers.
*
@@ -136,6 +136,29 @@ abstract class Configuration extends string {
partialFlow(source, node, this) and
dist = node.getSourceDistance()
}
/**
* Holds if there is a partial data flow path from `node` to `sink`. The
* approximate distance between `node` and the closest sink is `dist` and
* is restricted to be less than or equal to `explorationLimit()`. This
* predicate completely disregards source definitions.
*
* This predicate is intended for data-flow exploration and debugging and may
* perform poorly if the number of sinks is too big and/or the exploration
* limit is set too high without using barriers.
*
* This predicate is disabled (has no results) by default. Override
* `explorationLimit()` with a suitable number to enable this predicate.
*
* To use this in a `path-problem` query, import the module `PartialPathGraph`.
*
* Note that reverse flow has slightly lower precision than the corresponding
* forward flow, as reverse flow disregards type pruning among other features.
*/
final predicate hasPartialFlowRev(PartialPathNode node, PartialPathNode sink, int dist) {
revPartialFlow(node, sink, this) and
dist = node.getSinkDistance()
}
}
/**
@@ -2943,12 +2966,26 @@ private module FlowExploration {
)
}
private predicate interestingCallableSink(DataFlowCallable c, Configuration config) {
exists(Node n | config.isSink(n) and c = n.getEnclosingCallable())
or
exists(DataFlowCallable mid |
interestingCallableSink(mid, config) and callableStep(c, mid, config)
)
}
private newtype TCallableExt =
TCallable(DataFlowCallable c, Configuration config) { interestingCallableSrc(c, config) } or
TCallableSrc()
TCallable(DataFlowCallable c, Configuration config) {
interestingCallableSrc(c, config) or
interestingCallableSink(c, config)
} or
TCallableSrc() or
TCallableSink()
private predicate callableExtSrc(TCallableSrc src) { any() }
private predicate callableExtSink(TCallableSink sink) { any() }
private predicate callableExtStepFwd(TCallableExt ce1, TCallableExt ce2) {
exists(DataFlowCallable c1, DataFlowCallable c2, Configuration config |
callableStep(c1, c2, config) and
@@ -2961,15 +2998,32 @@ private module FlowExploration {
config.isSource(n) and
ce2 = TCallable(n.getEnclosingCallable(), config)
)
or
exists(Node n, Configuration config |
ce2 = TCallableSink() and
config.isSink(n) and
ce1 = TCallable(n.getEnclosingCallable(), config)
)
}
private predicate callableExtStepRev(TCallableExt ce1, TCallableExt ce2) {
callableExtStepFwd(ce2, ce1)
}
private int distSrcExt(TCallableExt c) =
shortestDistances(callableExtSrc/1, callableExtStepFwd/2)(_, c, result)
private int distSinkExt(TCallableExt c) =
shortestDistances(callableExtSink/1, callableExtStepRev/2)(_, c, result)
private int distSrc(DataFlowCallable c, Configuration config) {
result = distSrcExt(TCallable(c, config)) - 1
}
private int distSink(DataFlowCallable c, Configuration config) {
result = distSinkExt(TCallable(c, config)) - 1
}
private newtype TPartialAccessPath =
TPartialNil(DataFlowType t) or
TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] }
@@ -2997,18 +3051,12 @@ private module FlowExploration {
or
exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType())
}
abstract AccessPathFront getFront();
}
private class PartialAccessPathNil extends PartialAccessPath, TPartialNil {
override string toString() {
exists(DataFlowType t | this = TPartialNil(t) | result = concat(": " + ppReprType(t)))
}
override AccessPathFront getFront() {
exists(DataFlowType t | this = TPartialNil(t) | result = TFrontNil(t))
}
}
private class PartialAccessPathCons extends PartialAccessPath, TPartialCons {
@@ -3019,9 +3067,39 @@ private module FlowExploration {
else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]"
)
}
}
override AccessPathFront getFront() {
exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc))
private newtype TRevPartialAccessPath =
TRevPartialNil() or
TRevPartialCons(Content c, int len) { len in [1 .. accessPathLimit()] }
/**
* Conceptually a list of `Content`s, but only the first
* element of the list and its length are tracked.
*/
private class RevPartialAccessPath extends TRevPartialAccessPath {
abstract string toString();
Content getHead() { this = TRevPartialCons(result, _) }
int len() {
this = TRevPartialNil() and result = 0
or
this = TRevPartialCons(_, result)
}
}
private class RevPartialAccessPathNil extends RevPartialAccessPath, TRevPartialNil {
override string toString() { result = "" }
}
private class RevPartialAccessPathCons extends RevPartialAccessPath, TRevPartialCons {
override string toString() {
exists(Content c, int len | this = TRevPartialCons(c, len) |
if len = 1
then result = "[" + c.toString() + "]"
else result = "[" + c.toString() + ", ... (" + len.toString() + ")]"
)
}
}
@@ -3033,8 +3111,16 @@ private module FlowExploration {
TSummaryCtx2None() or
TSummaryCtx2Some(PartialAccessPath ap)
private newtype TRevSummaryCtx1 =
TRevSummaryCtx1None() or
TRevSummaryCtx1Some(ReturnPosition pos)
private newtype TRevSummaryCtx2 =
TRevSummaryCtx2None() or
TRevSummaryCtx2Some(RevPartialAccessPath ap)
private newtype TPartialPathNode =
TPartialPathNodeMk(
TPartialPathNodeFwd(
Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
Configuration config
) {
@@ -3048,6 +3134,23 @@ private module FlowExploration {
or
partialPathNodeMk0(node, cc, sc1, sc2, ap, config) and
distSrc(node.getEnclosingCallable(), config) <= config.explorationLimit()
} or
TPartialPathNodeRev(
Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, RevPartialAccessPath ap,
Configuration config
) {
config.isSink(node) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = TRevPartialNil() and
not fullBarrier(node, config) and
exists(config.explorationLimit())
or
exists(PartialPathNodeRev mid |
revPartialPathStep(mid, node, sc1, sc2, ap, config) and
not fullBarrier(node, config) and
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
)
}
pragma[nomagic]
@@ -3055,7 +3158,7 @@ private module FlowExploration {
Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
Configuration config
) {
exists(PartialPathNode mid |
exists(PartialPathNodeFwd mid |
partialPathStep(mid, node, cc, sc1, sc2, ap, config) and
not fullBarrier(node, config) and
if node instanceof CastingNode
@@ -3107,15 +3210,32 @@ private module FlowExploration {
result = distSrc(this.getNode().getEnclosingCallable(), this.getConfiguration())
}
/**
* Gets the approximate distance to the nearest sink measured in number
* of interprocedural steps.
*/
int getSinkDistance() {
result = distSink(this.getNode().getEnclosingCallable(), this.getConfiguration())
}
private string ppAp() {
exists(string s | s = this.(PartialPathNodePriv).getAp().toString() |
exists(string s |
s = this.(PartialPathNodeFwd).getAp().toString() or
s = this.(PartialPathNodeRev).getAp().toString()
|
if s = "" then result = "" else result = " " + s
)
}
private string ppCtx() {
result = " <" + this.(PartialPathNodePriv).getCallContext().toString() + ">"
result = " <" + this.(PartialPathNodeFwd).getCallContext().toString() + ">"
}
/** Holds if this is a source in a forward-flow path. */
predicate isFwdSource() { this.(PartialPathNodeFwd).isSource() }
/** Holds if this is a sink in a reverse-flow path. */
predicate isRevSink() { this.(PartialPathNodeRev).isSink() }
}
/**
@@ -3126,7 +3246,7 @@ private module FlowExploration {
query predicate edges(PartialPathNode a, PartialPathNode b) { a.getASuccessor() = b }
}
private class PartialPathNodePriv extends PartialPathNode {
private class PartialPathNodeFwd extends PartialPathNode, TPartialPathNodeFwd {
Node node;
CallContext cc;
TSummaryCtx1 sc1;
@@ -3134,7 +3254,7 @@ private module FlowExploration {
PartialAccessPath ap;
Configuration config;
PartialPathNodePriv() { this = TPartialPathNodeMk(node, cc, sc1, sc2, ap, config) }
PartialPathNodeFwd() { this = TPartialPathNodeFwd(node, cc, sc1, sc2, ap, config) }
override Node getNode() { result = node }
@@ -3148,14 +3268,54 @@ private module FlowExploration {
override Configuration getConfiguration() { result = config }
override PartialPathNodePriv getASuccessor() {
override PartialPathNodeFwd getASuccessor() {
partialPathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx1(),
result.getSummaryCtx2(), result.getAp(), result.getConfiguration())
}
predicate isSource() {
config.isSource(node) and
cc instanceof CallContextAny and
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
ap instanceof TPartialNil
}
}
private class PartialPathNodeRev extends PartialPathNode, TPartialPathNodeRev {
Node node;
TRevSummaryCtx1 sc1;
TRevSummaryCtx2 sc2;
RevPartialAccessPath ap;
Configuration config;
PartialPathNodeRev() { this = TPartialPathNodeRev(node, sc1, sc2, ap, config) }
override Node getNode() { result = node }
TRevSummaryCtx1 getSummaryCtx1() { result = sc1 }
TRevSummaryCtx2 getSummaryCtx2() { result = sc2 }
RevPartialAccessPath getAp() { result = ap }
override Configuration getConfiguration() { result = config }
override PartialPathNodeRev getASuccessor() {
revPartialPathStep(result, this.getNode(), this.getSummaryCtx1(), this.getSummaryCtx2(),
this.getAp(), this.getConfiguration())
}
predicate isSink() {
config.isSink(node) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = TRevPartialNil()
}
}
private predicate partialPathStep(
PartialPathNodePriv mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
PartialAccessPath ap, Configuration config
) {
not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and
@@ -3221,8 +3381,7 @@ private module FlowExploration {
pragma[inline]
private predicate partialPathStoreStep(
PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node,
PartialAccessPath ap2
PartialPathNodeFwd mid, PartialAccessPath ap1, TypedContent tc, Node node, PartialAccessPath ap2
) {
exists(Node midNode, DataFlowType contentType |
midNode = mid.getNode() and
@@ -3238,7 +3397,7 @@ private module FlowExploration {
private predicate apConsFwd(
PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config
) {
exists(PartialPathNodePriv mid |
exists(PartialPathNodeFwd mid |
partialPathStoreStep(mid, ap1, tc, _, ap2) and
config = mid.getConfiguration()
)
@@ -3246,7 +3405,7 @@ private module FlowExploration {
pragma[nomagic]
private predicate partialPathReadStep(
PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
PartialPathNodeFwd mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
Configuration config
) {
exists(Node midNode |
@@ -3260,7 +3419,7 @@ private module FlowExploration {
}
private predicate partialPathOutOfCallable0(
PartialPathNodePriv mid, ReturnPosition pos, CallContext innercc, PartialAccessPath ap,
PartialPathNodeFwd mid, ReturnPosition pos, CallContext innercc, PartialAccessPath ap,
Configuration config
) {
pos = getReturnPosition(mid.getNode()) and
@@ -3272,7 +3431,7 @@ private module FlowExploration {
pragma[nomagic]
private predicate partialPathOutOfCallable1(
PartialPathNodePriv mid, DataFlowCall call, ReturnKindExt kind, CallContext cc,
PartialPathNodeFwd mid, DataFlowCall call, ReturnKindExt kind, CallContext cc,
PartialAccessPath ap, Configuration config
) {
exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc |
@@ -3286,7 +3445,7 @@ private module FlowExploration {
}
private predicate partialPathOutOfCallable(
PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
PartialPathNodeFwd mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
) {
exists(ReturnKindExt kind, DataFlowCall call |
partialPathOutOfCallable1(mid, call, kind, cc, ap, config)
@@ -3297,7 +3456,7 @@ private module FlowExploration {
pragma[noinline]
private predicate partialPathIntoArg(
PartialPathNodePriv mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap,
PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap,
Configuration config
) {
exists(ArgumentNode arg |
@@ -3311,7 +3470,7 @@ private module FlowExploration {
pragma[nomagic]
private predicate partialPathIntoCallable0(
PartialPathNodePriv mid, DataFlowCallable callable, int i, CallContext outercc,
PartialPathNodeFwd mid, DataFlowCallable callable, int i, CallContext outercc,
DataFlowCall call, PartialAccessPath ap, Configuration config
) {
partialPathIntoArg(mid, i, outercc, call, ap, config) and
@@ -3319,7 +3478,7 @@ private module FlowExploration {
}
private predicate partialPathIntoCallable(
PartialPathNodePriv mid, ParameterNode p, CallContext outercc, CallContextCall innercc,
PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc,
TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap,
Configuration config
) {
@@ -3340,7 +3499,7 @@ private module FlowExploration {
ReturnKindExt kind, CallContextCall cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
PartialAccessPath ap, Configuration config
) {
exists(PartialPathNodePriv mid, ReturnNodeExt ret |
exists(PartialPathNodeFwd mid, ReturnNodeExt ret |
mid.getNode() = ret and
kind = ret.getKind() and
cc = mid.getCallContext() and
@@ -3353,7 +3512,7 @@ private module FlowExploration {
pragma[noinline]
private predicate partialPathThroughCallable0(
DataFlowCall call, PartialPathNodePriv mid, ReturnKindExt kind, CallContext cc,
DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc,
PartialAccessPath ap, Configuration config
) {
exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 |
@@ -3363,13 +3522,164 @@ private module FlowExploration {
}
private predicate partialPathThroughCallable(
PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
PartialPathNodeFwd mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
) {
exists(DataFlowCall call, ReturnKindExt kind |
partialPathThroughCallable0(call, mid, kind, cc, ap, config) and
out = kind.getAnOutNode(call)
)
}
private predicate revPartialPathStep(
PartialPathNodeRev mid, Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2,
RevPartialAccessPath ap, Configuration config
) {
localFlowStep(node, mid.getNode(), config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
ap = mid.getAp() and
config = mid.getConfiguration()
or
additionalLocalFlowStep(node, mid.getNode(), config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
mid.getAp() instanceof RevPartialAccessPathNil and
ap = TRevPartialNil() and
config = mid.getConfiguration()
or
jumpStep(node, mid.getNode(), config) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = mid.getAp() and
config = mid.getConfiguration()
or
additionalJumpStep(node, mid.getNode(), config) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
mid.getAp() instanceof RevPartialAccessPathNil and
ap = TRevPartialNil() and
config = mid.getConfiguration()
or
revPartialPathReadStep(mid, _, _, node, ap) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
config = mid.getConfiguration()
or
exists(RevPartialAccessPath ap0, Content c |
revPartialPathStoreStep(mid, ap0, c, node, config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
apConsRev(ap, c, ap0, config)
)
or
exists(ParameterNode p |
mid.getNode() = p and
viableParamArg(_, p, node) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = mid.getAp() and
config = mid.getConfiguration()
)
or
exists(ReturnPosition pos |
revPartialPathIntoReturn(mid, pos, sc1, sc2, _, ap, config) and
pos = getReturnPosition(node)
)
or
revPartialPathThroughCallable(mid, node, ap, config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2()
}
pragma[inline]
private predicate revPartialPathReadStep(
PartialPathNodeRev mid, RevPartialAccessPath ap1, Content c, Node node, RevPartialAccessPath ap2
) {
exists(Node midNode |
midNode = mid.getNode() and
ap1 = mid.getAp() and
read(node, c, midNode) and
ap2.getHead() = c and
ap2.len() = unbindInt(ap1.len() + 1)
)
}
pragma[nomagic]
private predicate apConsRev(
RevPartialAccessPath ap1, Content c, RevPartialAccessPath ap2, Configuration config
) {
exists(PartialPathNodeRev mid |
revPartialPathReadStep(mid, ap1, c, _, ap2) and
config = mid.getConfiguration()
)
}
pragma[nomagic]
private predicate revPartialPathStoreStep(
PartialPathNodeRev mid, RevPartialAccessPath ap, Content c, Node node, Configuration config
) {
exists(Node midNode, TypedContent tc |
midNode = mid.getNode() and
ap = mid.getAp() and
store(node, tc, midNode, _) and
ap.getHead() = c and
config = mid.getConfiguration() and
tc.getContent() = c
)
}
pragma[nomagic]
private predicate revPartialPathIntoReturn(
PartialPathNodeRev mid, ReturnPosition pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2,
DataFlowCall call, RevPartialAccessPath ap, Configuration config
) {
exists(Node out |
mid.getNode() = out and
viableReturnPosOut(call, pos, out) and
sc1 = TRevSummaryCtx1Some(pos) and
sc2 = TRevSummaryCtx2Some(ap) and
ap = mid.getAp() and
config = mid.getConfiguration()
)
}
pragma[nomagic]
private predicate revPartialPathFlowsThrough(
int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap,
Configuration config
) {
exists(PartialPathNodeRev mid, ParameterNode p |
mid.getNode() = p and
p.isParameterOf(_, pos) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
ap = mid.getAp() and
config = mid.getConfiguration()
)
}
pragma[nomagic]
private predicate revPartialPathThroughCallable0(
DataFlowCall call, PartialPathNodeRev mid, int pos, RevPartialAccessPath ap,
Configuration config
) {
exists(TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2 |
revPartialPathIntoReturn(mid, _, sc1, sc2, call, _, config) and
revPartialPathFlowsThrough(pos, sc1, sc2, ap, config)
)
}
pragma[nomagic]
private predicate revPartialPathThroughCallable(
PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config
) {
exists(DataFlowCall call, int pos |
revPartialPathThroughCallable0(call, mid, pos, ap, config) and
node.argumentOf(call, pos)
)
}
}
import FlowExploration
@@ -3378,6 +3688,14 @@ private predicate partialFlow(
PartialPathNode source, PartialPathNode node, Configuration configuration
) {
source.getConfiguration() = configuration and
configuration.isSource(source.getNode()) and
source.isFwdSource() and
node = source.getASuccessor+()
}
private predicate revPartialFlow(
PartialPathNode node, PartialPathNode sink, Configuration configuration
) {
sink.getConfiguration() = configuration and
sink.isRevSink() and
node.getASuccessor+() = sink
}

View File

@@ -112,8 +112,8 @@ abstract class Configuration extends string {
predicate hasFlowToExpr(DataFlowExpr sink) { hasFlowTo(exprNode(sink)) }
/**
* Gets the exploration limit for `hasPartialFlow` measured in approximate
* number of interprocedural steps.
* Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev`
* measured in approximate number of interprocedural steps.
*/
int explorationLimit() { none() }
@@ -123,7 +123,7 @@ abstract class Configuration extends string {
* is restricted to be less than or equal to `explorationLimit()`. This
* predicate completely disregards sink definitions.
*
* This predicate is intended for dataflow exploration and debugging and may
* This predicate is intended for data-flow exploration and debugging and may
* perform poorly if the number of sources is too big and/or the exploration
* limit is set too high without using barriers.
*
@@ -136,6 +136,29 @@ abstract class Configuration extends string {
partialFlow(source, node, this) and
dist = node.getSourceDistance()
}
/**
* Holds if there is a partial data flow path from `node` to `sink`. The
* approximate distance between `node` and the closest sink is `dist` and
* is restricted to be less than or equal to `explorationLimit()`. This
* predicate completely disregards source definitions.
*
* This predicate is intended for data-flow exploration and debugging and may
* perform poorly if the number of sinks is too big and/or the exploration
* limit is set too high without using barriers.
*
* This predicate is disabled (has no results) by default. Override
* `explorationLimit()` with a suitable number to enable this predicate.
*
* To use this in a `path-problem` query, import the module `PartialPathGraph`.
*
* Note that reverse flow has slightly lower precision than the corresponding
* forward flow, as reverse flow disregards type pruning among other features.
*/
final predicate hasPartialFlowRev(PartialPathNode node, PartialPathNode sink, int dist) {
revPartialFlow(node, sink, this) and
dist = node.getSinkDistance()
}
}
/**
@@ -2943,12 +2966,26 @@ private module FlowExploration {
)
}
private predicate interestingCallableSink(DataFlowCallable c, Configuration config) {
exists(Node n | config.isSink(n) and c = n.getEnclosingCallable())
or
exists(DataFlowCallable mid |
interestingCallableSink(mid, config) and callableStep(c, mid, config)
)
}
private newtype TCallableExt =
TCallable(DataFlowCallable c, Configuration config) { interestingCallableSrc(c, config) } or
TCallableSrc()
TCallable(DataFlowCallable c, Configuration config) {
interestingCallableSrc(c, config) or
interestingCallableSink(c, config)
} or
TCallableSrc() or
TCallableSink()
private predicate callableExtSrc(TCallableSrc src) { any() }
private predicate callableExtSink(TCallableSink sink) { any() }
private predicate callableExtStepFwd(TCallableExt ce1, TCallableExt ce2) {
exists(DataFlowCallable c1, DataFlowCallable c2, Configuration config |
callableStep(c1, c2, config) and
@@ -2961,15 +2998,32 @@ private module FlowExploration {
config.isSource(n) and
ce2 = TCallable(n.getEnclosingCallable(), config)
)
or
exists(Node n, Configuration config |
ce2 = TCallableSink() and
config.isSink(n) and
ce1 = TCallable(n.getEnclosingCallable(), config)
)
}
private predicate callableExtStepRev(TCallableExt ce1, TCallableExt ce2) {
callableExtStepFwd(ce2, ce1)
}
private int distSrcExt(TCallableExt c) =
shortestDistances(callableExtSrc/1, callableExtStepFwd/2)(_, c, result)
private int distSinkExt(TCallableExt c) =
shortestDistances(callableExtSink/1, callableExtStepRev/2)(_, c, result)
private int distSrc(DataFlowCallable c, Configuration config) {
result = distSrcExt(TCallable(c, config)) - 1
}
private int distSink(DataFlowCallable c, Configuration config) {
result = distSinkExt(TCallable(c, config)) - 1
}
private newtype TPartialAccessPath =
TPartialNil(DataFlowType t) or
TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] }
@@ -2997,18 +3051,12 @@ private module FlowExploration {
or
exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType())
}
abstract AccessPathFront getFront();
}
private class PartialAccessPathNil extends PartialAccessPath, TPartialNil {
override string toString() {
exists(DataFlowType t | this = TPartialNil(t) | result = concat(": " + ppReprType(t)))
}
override AccessPathFront getFront() {
exists(DataFlowType t | this = TPartialNil(t) | result = TFrontNil(t))
}
}
private class PartialAccessPathCons extends PartialAccessPath, TPartialCons {
@@ -3019,9 +3067,39 @@ private module FlowExploration {
else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]"
)
}
}
override AccessPathFront getFront() {
exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc))
private newtype TRevPartialAccessPath =
TRevPartialNil() or
TRevPartialCons(Content c, int len) { len in [1 .. accessPathLimit()] }
/**
* Conceptually a list of `Content`s, but only the first
* element of the list and its length are tracked.
*/
private class RevPartialAccessPath extends TRevPartialAccessPath {
abstract string toString();
Content getHead() { this = TRevPartialCons(result, _) }
int len() {
this = TRevPartialNil() and result = 0
or
this = TRevPartialCons(_, result)
}
}
private class RevPartialAccessPathNil extends RevPartialAccessPath, TRevPartialNil {
override string toString() { result = "" }
}
private class RevPartialAccessPathCons extends RevPartialAccessPath, TRevPartialCons {
override string toString() {
exists(Content c, int len | this = TRevPartialCons(c, len) |
if len = 1
then result = "[" + c.toString() + "]"
else result = "[" + c.toString() + ", ... (" + len.toString() + ")]"
)
}
}
@@ -3033,8 +3111,16 @@ private module FlowExploration {
TSummaryCtx2None() or
TSummaryCtx2Some(PartialAccessPath ap)
private newtype TRevSummaryCtx1 =
TRevSummaryCtx1None() or
TRevSummaryCtx1Some(ReturnPosition pos)
private newtype TRevSummaryCtx2 =
TRevSummaryCtx2None() or
TRevSummaryCtx2Some(RevPartialAccessPath ap)
private newtype TPartialPathNode =
TPartialPathNodeMk(
TPartialPathNodeFwd(
Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
Configuration config
) {
@@ -3048,6 +3134,23 @@ private module FlowExploration {
or
partialPathNodeMk0(node, cc, sc1, sc2, ap, config) and
distSrc(node.getEnclosingCallable(), config) <= config.explorationLimit()
} or
TPartialPathNodeRev(
Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, RevPartialAccessPath ap,
Configuration config
) {
config.isSink(node) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = TRevPartialNil() and
not fullBarrier(node, config) and
exists(config.explorationLimit())
or
exists(PartialPathNodeRev mid |
revPartialPathStep(mid, node, sc1, sc2, ap, config) and
not fullBarrier(node, config) and
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
)
}
pragma[nomagic]
@@ -3055,7 +3158,7 @@ private module FlowExploration {
Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
Configuration config
) {
exists(PartialPathNode mid |
exists(PartialPathNodeFwd mid |
partialPathStep(mid, node, cc, sc1, sc2, ap, config) and
not fullBarrier(node, config) and
if node instanceof CastingNode
@@ -3107,15 +3210,32 @@ private module FlowExploration {
result = distSrc(this.getNode().getEnclosingCallable(), this.getConfiguration())
}
/**
* Gets the approximate distance to the nearest sink measured in number
* of interprocedural steps.
*/
int getSinkDistance() {
result = distSink(this.getNode().getEnclosingCallable(), this.getConfiguration())
}
private string ppAp() {
exists(string s | s = this.(PartialPathNodePriv).getAp().toString() |
exists(string s |
s = this.(PartialPathNodeFwd).getAp().toString() or
s = this.(PartialPathNodeRev).getAp().toString()
|
if s = "" then result = "" else result = " " + s
)
}
private string ppCtx() {
result = " <" + this.(PartialPathNodePriv).getCallContext().toString() + ">"
result = " <" + this.(PartialPathNodeFwd).getCallContext().toString() + ">"
}
/** Holds if this is a source in a forward-flow path. */
predicate isFwdSource() { this.(PartialPathNodeFwd).isSource() }
/** Holds if this is a sink in a reverse-flow path. */
predicate isRevSink() { this.(PartialPathNodeRev).isSink() }
}
/**
@@ -3126,7 +3246,7 @@ private module FlowExploration {
query predicate edges(PartialPathNode a, PartialPathNode b) { a.getASuccessor() = b }
}
private class PartialPathNodePriv extends PartialPathNode {
private class PartialPathNodeFwd extends PartialPathNode, TPartialPathNodeFwd {
Node node;
CallContext cc;
TSummaryCtx1 sc1;
@@ -3134,7 +3254,7 @@ private module FlowExploration {
PartialAccessPath ap;
Configuration config;
PartialPathNodePriv() { this = TPartialPathNodeMk(node, cc, sc1, sc2, ap, config) }
PartialPathNodeFwd() { this = TPartialPathNodeFwd(node, cc, sc1, sc2, ap, config) }
override Node getNode() { result = node }
@@ -3148,14 +3268,54 @@ private module FlowExploration {
override Configuration getConfiguration() { result = config }
override PartialPathNodePriv getASuccessor() {
override PartialPathNodeFwd getASuccessor() {
partialPathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx1(),
result.getSummaryCtx2(), result.getAp(), result.getConfiguration())
}
predicate isSource() {
config.isSource(node) and
cc instanceof CallContextAny and
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
ap instanceof TPartialNil
}
}
private class PartialPathNodeRev extends PartialPathNode, TPartialPathNodeRev {
Node node;
TRevSummaryCtx1 sc1;
TRevSummaryCtx2 sc2;
RevPartialAccessPath ap;
Configuration config;
PartialPathNodeRev() { this = TPartialPathNodeRev(node, sc1, sc2, ap, config) }
override Node getNode() { result = node }
TRevSummaryCtx1 getSummaryCtx1() { result = sc1 }
TRevSummaryCtx2 getSummaryCtx2() { result = sc2 }
RevPartialAccessPath getAp() { result = ap }
override Configuration getConfiguration() { result = config }
override PartialPathNodeRev getASuccessor() {
revPartialPathStep(result, this.getNode(), this.getSummaryCtx1(), this.getSummaryCtx2(),
this.getAp(), this.getConfiguration())
}
predicate isSink() {
config.isSink(node) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = TRevPartialNil()
}
}
private predicate partialPathStep(
PartialPathNodePriv mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
PartialAccessPath ap, Configuration config
) {
not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and
@@ -3221,8 +3381,7 @@ private module FlowExploration {
pragma[inline]
private predicate partialPathStoreStep(
PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node,
PartialAccessPath ap2
PartialPathNodeFwd mid, PartialAccessPath ap1, TypedContent tc, Node node, PartialAccessPath ap2
) {
exists(Node midNode, DataFlowType contentType |
midNode = mid.getNode() and
@@ -3238,7 +3397,7 @@ private module FlowExploration {
private predicate apConsFwd(
PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config
) {
exists(PartialPathNodePriv mid |
exists(PartialPathNodeFwd mid |
partialPathStoreStep(mid, ap1, tc, _, ap2) and
config = mid.getConfiguration()
)
@@ -3246,7 +3405,7 @@ private module FlowExploration {
pragma[nomagic]
private predicate partialPathReadStep(
PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
PartialPathNodeFwd mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
Configuration config
) {
exists(Node midNode |
@@ -3260,7 +3419,7 @@ private module FlowExploration {
}
private predicate partialPathOutOfCallable0(
PartialPathNodePriv mid, ReturnPosition pos, CallContext innercc, PartialAccessPath ap,
PartialPathNodeFwd mid, ReturnPosition pos, CallContext innercc, PartialAccessPath ap,
Configuration config
) {
pos = getReturnPosition(mid.getNode()) and
@@ -3272,7 +3431,7 @@ private module FlowExploration {
pragma[nomagic]
private predicate partialPathOutOfCallable1(
PartialPathNodePriv mid, DataFlowCall call, ReturnKindExt kind, CallContext cc,
PartialPathNodeFwd mid, DataFlowCall call, ReturnKindExt kind, CallContext cc,
PartialAccessPath ap, Configuration config
) {
exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc |
@@ -3286,7 +3445,7 @@ private module FlowExploration {
}
private predicate partialPathOutOfCallable(
PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
PartialPathNodeFwd mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
) {
exists(ReturnKindExt kind, DataFlowCall call |
partialPathOutOfCallable1(mid, call, kind, cc, ap, config)
@@ -3297,7 +3456,7 @@ private module FlowExploration {
pragma[noinline]
private predicate partialPathIntoArg(
PartialPathNodePriv mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap,
PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap,
Configuration config
) {
exists(ArgumentNode arg |
@@ -3311,7 +3470,7 @@ private module FlowExploration {
pragma[nomagic]
private predicate partialPathIntoCallable0(
PartialPathNodePriv mid, DataFlowCallable callable, int i, CallContext outercc,
PartialPathNodeFwd mid, DataFlowCallable callable, int i, CallContext outercc,
DataFlowCall call, PartialAccessPath ap, Configuration config
) {
partialPathIntoArg(mid, i, outercc, call, ap, config) and
@@ -3319,7 +3478,7 @@ private module FlowExploration {
}
private predicate partialPathIntoCallable(
PartialPathNodePriv mid, ParameterNode p, CallContext outercc, CallContextCall innercc,
PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc,
TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap,
Configuration config
) {
@@ -3340,7 +3499,7 @@ private module FlowExploration {
ReturnKindExt kind, CallContextCall cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
PartialAccessPath ap, Configuration config
) {
exists(PartialPathNodePriv mid, ReturnNodeExt ret |
exists(PartialPathNodeFwd mid, ReturnNodeExt ret |
mid.getNode() = ret and
kind = ret.getKind() and
cc = mid.getCallContext() and
@@ -3353,7 +3512,7 @@ private module FlowExploration {
pragma[noinline]
private predicate partialPathThroughCallable0(
DataFlowCall call, PartialPathNodePriv mid, ReturnKindExt kind, CallContext cc,
DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc,
PartialAccessPath ap, Configuration config
) {
exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 |
@@ -3363,13 +3522,164 @@ private module FlowExploration {
}
private predicate partialPathThroughCallable(
PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
PartialPathNodeFwd mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
) {
exists(DataFlowCall call, ReturnKindExt kind |
partialPathThroughCallable0(call, mid, kind, cc, ap, config) and
out = kind.getAnOutNode(call)
)
}
private predicate revPartialPathStep(
PartialPathNodeRev mid, Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2,
RevPartialAccessPath ap, Configuration config
) {
localFlowStep(node, mid.getNode(), config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
ap = mid.getAp() and
config = mid.getConfiguration()
or
additionalLocalFlowStep(node, mid.getNode(), config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
mid.getAp() instanceof RevPartialAccessPathNil and
ap = TRevPartialNil() and
config = mid.getConfiguration()
or
jumpStep(node, mid.getNode(), config) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = mid.getAp() and
config = mid.getConfiguration()
or
additionalJumpStep(node, mid.getNode(), config) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
mid.getAp() instanceof RevPartialAccessPathNil and
ap = TRevPartialNil() and
config = mid.getConfiguration()
or
revPartialPathReadStep(mid, _, _, node, ap) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
config = mid.getConfiguration()
or
exists(RevPartialAccessPath ap0, Content c |
revPartialPathStoreStep(mid, ap0, c, node, config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
apConsRev(ap, c, ap0, config)
)
or
exists(ParameterNode p |
mid.getNode() = p and
viableParamArg(_, p, node) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = mid.getAp() and
config = mid.getConfiguration()
)
or
exists(ReturnPosition pos |
revPartialPathIntoReturn(mid, pos, sc1, sc2, _, ap, config) and
pos = getReturnPosition(node)
)
or
revPartialPathThroughCallable(mid, node, ap, config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2()
}
pragma[inline]
private predicate revPartialPathReadStep(
PartialPathNodeRev mid, RevPartialAccessPath ap1, Content c, Node node, RevPartialAccessPath ap2
) {
exists(Node midNode |
midNode = mid.getNode() and
ap1 = mid.getAp() and
read(node, c, midNode) and
ap2.getHead() = c and
ap2.len() = unbindInt(ap1.len() + 1)
)
}
pragma[nomagic]
private predicate apConsRev(
RevPartialAccessPath ap1, Content c, RevPartialAccessPath ap2, Configuration config
) {
exists(PartialPathNodeRev mid |
revPartialPathReadStep(mid, ap1, c, _, ap2) and
config = mid.getConfiguration()
)
}
pragma[nomagic]
private predicate revPartialPathStoreStep(
PartialPathNodeRev mid, RevPartialAccessPath ap, Content c, Node node, Configuration config
) {
exists(Node midNode, TypedContent tc |
midNode = mid.getNode() and
ap = mid.getAp() and
store(node, tc, midNode, _) and
ap.getHead() = c and
config = mid.getConfiguration() and
tc.getContent() = c
)
}
pragma[nomagic]
private predicate revPartialPathIntoReturn(
PartialPathNodeRev mid, ReturnPosition pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2,
DataFlowCall call, RevPartialAccessPath ap, Configuration config
) {
exists(Node out |
mid.getNode() = out and
viableReturnPosOut(call, pos, out) and
sc1 = TRevSummaryCtx1Some(pos) and
sc2 = TRevSummaryCtx2Some(ap) and
ap = mid.getAp() and
config = mid.getConfiguration()
)
}
pragma[nomagic]
private predicate revPartialPathFlowsThrough(
int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap,
Configuration config
) {
exists(PartialPathNodeRev mid, ParameterNode p |
mid.getNode() = p and
p.isParameterOf(_, pos) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
ap = mid.getAp() and
config = mid.getConfiguration()
)
}
pragma[nomagic]
private predicate revPartialPathThroughCallable0(
DataFlowCall call, PartialPathNodeRev mid, int pos, RevPartialAccessPath ap,
Configuration config
) {
exists(TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2 |
revPartialPathIntoReturn(mid, _, sc1, sc2, call, _, config) and
revPartialPathFlowsThrough(pos, sc1, sc2, ap, config)
)
}
pragma[nomagic]
private predicate revPartialPathThroughCallable(
PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config
) {
exists(DataFlowCall call, int pos |
revPartialPathThroughCallable0(call, mid, pos, ap, config) and
node.argumentOf(call, pos)
)
}
}
import FlowExploration
@@ -3378,6 +3688,14 @@ private predicate partialFlow(
PartialPathNode source, PartialPathNode node, Configuration configuration
) {
source.getConfiguration() = configuration and
configuration.isSource(source.getNode()) and
source.isFwdSource() and
node = source.getASuccessor+()
}
private predicate revPartialFlow(
PartialPathNode node, PartialPathNode sink, Configuration configuration
) {
sink.getConfiguration() = configuration and
sink.isRevSink() and
node.getASuccessor+() = sink
}

View File

@@ -112,8 +112,8 @@ abstract class Configuration extends string {
predicate hasFlowToExpr(DataFlowExpr sink) { hasFlowTo(exprNode(sink)) }
/**
* Gets the exploration limit for `hasPartialFlow` measured in approximate
* number of interprocedural steps.
* Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev`
* measured in approximate number of interprocedural steps.
*/
int explorationLimit() { none() }
@@ -123,7 +123,7 @@ abstract class Configuration extends string {
* is restricted to be less than or equal to `explorationLimit()`. This
* predicate completely disregards sink definitions.
*
* This predicate is intended for dataflow exploration and debugging and may
* This predicate is intended for data-flow exploration and debugging and may
* perform poorly if the number of sources is too big and/or the exploration
* limit is set too high without using barriers.
*
@@ -136,6 +136,29 @@ abstract class Configuration extends string {
partialFlow(source, node, this) and
dist = node.getSourceDistance()
}
/**
* Holds if there is a partial data flow path from `node` to `sink`. The
* approximate distance between `node` and the closest sink is `dist` and
* is restricted to be less than or equal to `explorationLimit()`. This
* predicate completely disregards source definitions.
*
* This predicate is intended for data-flow exploration and debugging and may
* perform poorly if the number of sinks is too big and/or the exploration
* limit is set too high without using barriers.
*
* This predicate is disabled (has no results) by default. Override
* `explorationLimit()` with a suitable number to enable this predicate.
*
* To use this in a `path-problem` query, import the module `PartialPathGraph`.
*
* Note that reverse flow has slightly lower precision than the corresponding
* forward flow, as reverse flow disregards type pruning among other features.
*/
final predicate hasPartialFlowRev(PartialPathNode node, PartialPathNode sink, int dist) {
revPartialFlow(node, sink, this) and
dist = node.getSinkDistance()
}
}
/**
@@ -2943,12 +2966,26 @@ private module FlowExploration {
)
}
private predicate interestingCallableSink(DataFlowCallable c, Configuration config) {
exists(Node n | config.isSink(n) and c = n.getEnclosingCallable())
or
exists(DataFlowCallable mid |
interestingCallableSink(mid, config) and callableStep(c, mid, config)
)
}
private newtype TCallableExt =
TCallable(DataFlowCallable c, Configuration config) { interestingCallableSrc(c, config) } or
TCallableSrc()
TCallable(DataFlowCallable c, Configuration config) {
interestingCallableSrc(c, config) or
interestingCallableSink(c, config)
} or
TCallableSrc() or
TCallableSink()
private predicate callableExtSrc(TCallableSrc src) { any() }
private predicate callableExtSink(TCallableSink sink) { any() }
private predicate callableExtStepFwd(TCallableExt ce1, TCallableExt ce2) {
exists(DataFlowCallable c1, DataFlowCallable c2, Configuration config |
callableStep(c1, c2, config) and
@@ -2961,15 +2998,32 @@ private module FlowExploration {
config.isSource(n) and
ce2 = TCallable(n.getEnclosingCallable(), config)
)
or
exists(Node n, Configuration config |
ce2 = TCallableSink() and
config.isSink(n) and
ce1 = TCallable(n.getEnclosingCallable(), config)
)
}
private predicate callableExtStepRev(TCallableExt ce1, TCallableExt ce2) {
callableExtStepFwd(ce2, ce1)
}
private int distSrcExt(TCallableExt c) =
shortestDistances(callableExtSrc/1, callableExtStepFwd/2)(_, c, result)
private int distSinkExt(TCallableExt c) =
shortestDistances(callableExtSink/1, callableExtStepRev/2)(_, c, result)
private int distSrc(DataFlowCallable c, Configuration config) {
result = distSrcExt(TCallable(c, config)) - 1
}
private int distSink(DataFlowCallable c, Configuration config) {
result = distSinkExt(TCallable(c, config)) - 1
}
private newtype TPartialAccessPath =
TPartialNil(DataFlowType t) or
TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] }
@@ -2997,18 +3051,12 @@ private module FlowExploration {
or
exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType())
}
abstract AccessPathFront getFront();
}
private class PartialAccessPathNil extends PartialAccessPath, TPartialNil {
override string toString() {
exists(DataFlowType t | this = TPartialNil(t) | result = concat(": " + ppReprType(t)))
}
override AccessPathFront getFront() {
exists(DataFlowType t | this = TPartialNil(t) | result = TFrontNil(t))
}
}
private class PartialAccessPathCons extends PartialAccessPath, TPartialCons {
@@ -3019,9 +3067,39 @@ private module FlowExploration {
else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]"
)
}
}
override AccessPathFront getFront() {
exists(TypedContent tc | this = TPartialCons(tc, _) | result = TFrontHead(tc))
private newtype TRevPartialAccessPath =
TRevPartialNil() or
TRevPartialCons(Content c, int len) { len in [1 .. accessPathLimit()] }
/**
* Conceptually a list of `Content`s, but only the first
* element of the list and its length are tracked.
*/
private class RevPartialAccessPath extends TRevPartialAccessPath {
abstract string toString();
Content getHead() { this = TRevPartialCons(result, _) }
int len() {
this = TRevPartialNil() and result = 0
or
this = TRevPartialCons(_, result)
}
}
private class RevPartialAccessPathNil extends RevPartialAccessPath, TRevPartialNil {
override string toString() { result = "" }
}
private class RevPartialAccessPathCons extends RevPartialAccessPath, TRevPartialCons {
override string toString() {
exists(Content c, int len | this = TRevPartialCons(c, len) |
if len = 1
then result = "[" + c.toString() + "]"
else result = "[" + c.toString() + ", ... (" + len.toString() + ")]"
)
}
}
@@ -3033,8 +3111,16 @@ private module FlowExploration {
TSummaryCtx2None() or
TSummaryCtx2Some(PartialAccessPath ap)
private newtype TRevSummaryCtx1 =
TRevSummaryCtx1None() or
TRevSummaryCtx1Some(ReturnPosition pos)
private newtype TRevSummaryCtx2 =
TRevSummaryCtx2None() or
TRevSummaryCtx2Some(RevPartialAccessPath ap)
private newtype TPartialPathNode =
TPartialPathNodeMk(
TPartialPathNodeFwd(
Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
Configuration config
) {
@@ -3048,6 +3134,23 @@ private module FlowExploration {
or
partialPathNodeMk0(node, cc, sc1, sc2, ap, config) and
distSrc(node.getEnclosingCallable(), config) <= config.explorationLimit()
} or
TPartialPathNodeRev(
Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, RevPartialAccessPath ap,
Configuration config
) {
config.isSink(node) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = TRevPartialNil() and
not fullBarrier(node, config) and
exists(config.explorationLimit())
or
exists(PartialPathNodeRev mid |
revPartialPathStep(mid, node, sc1, sc2, ap, config) and
not fullBarrier(node, config) and
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
)
}
pragma[nomagic]
@@ -3055,7 +3158,7 @@ private module FlowExploration {
Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, PartialAccessPath ap,
Configuration config
) {
exists(PartialPathNode mid |
exists(PartialPathNodeFwd mid |
partialPathStep(mid, node, cc, sc1, sc2, ap, config) and
not fullBarrier(node, config) and
if node instanceof CastingNode
@@ -3107,15 +3210,32 @@ private module FlowExploration {
result = distSrc(this.getNode().getEnclosingCallable(), this.getConfiguration())
}
/**
* Gets the approximate distance to the nearest sink measured in number
* of interprocedural steps.
*/
int getSinkDistance() {
result = distSink(this.getNode().getEnclosingCallable(), this.getConfiguration())
}
private string ppAp() {
exists(string s | s = this.(PartialPathNodePriv).getAp().toString() |
exists(string s |
s = this.(PartialPathNodeFwd).getAp().toString() or
s = this.(PartialPathNodeRev).getAp().toString()
|
if s = "" then result = "" else result = " " + s
)
}
private string ppCtx() {
result = " <" + this.(PartialPathNodePriv).getCallContext().toString() + ">"
result = " <" + this.(PartialPathNodeFwd).getCallContext().toString() + ">"
}
/** Holds if this is a source in a forward-flow path. */
predicate isFwdSource() { this.(PartialPathNodeFwd).isSource() }
/** Holds if this is a sink in a reverse-flow path. */
predicate isRevSink() { this.(PartialPathNodeRev).isSink() }
}
/**
@@ -3126,7 +3246,7 @@ private module FlowExploration {
query predicate edges(PartialPathNode a, PartialPathNode b) { a.getASuccessor() = b }
}
private class PartialPathNodePriv extends PartialPathNode {
private class PartialPathNodeFwd extends PartialPathNode, TPartialPathNodeFwd {
Node node;
CallContext cc;
TSummaryCtx1 sc1;
@@ -3134,7 +3254,7 @@ private module FlowExploration {
PartialAccessPath ap;
Configuration config;
PartialPathNodePriv() { this = TPartialPathNodeMk(node, cc, sc1, sc2, ap, config) }
PartialPathNodeFwd() { this = TPartialPathNodeFwd(node, cc, sc1, sc2, ap, config) }
override Node getNode() { result = node }
@@ -3148,14 +3268,54 @@ private module FlowExploration {
override Configuration getConfiguration() { result = config }
override PartialPathNodePriv getASuccessor() {
override PartialPathNodeFwd getASuccessor() {
partialPathStep(this, result.getNode(), result.getCallContext(), result.getSummaryCtx1(),
result.getSummaryCtx2(), result.getAp(), result.getConfiguration())
}
predicate isSource() {
config.isSource(node) and
cc instanceof CallContextAny and
sc1 = TSummaryCtx1None() and
sc2 = TSummaryCtx2None() and
ap instanceof TPartialNil
}
}
private class PartialPathNodeRev extends PartialPathNode, TPartialPathNodeRev {
Node node;
TRevSummaryCtx1 sc1;
TRevSummaryCtx2 sc2;
RevPartialAccessPath ap;
Configuration config;
PartialPathNodeRev() { this = TPartialPathNodeRev(node, sc1, sc2, ap, config) }
override Node getNode() { result = node }
TRevSummaryCtx1 getSummaryCtx1() { result = sc1 }
TRevSummaryCtx2 getSummaryCtx2() { result = sc2 }
RevPartialAccessPath getAp() { result = ap }
override Configuration getConfiguration() { result = config }
override PartialPathNodeRev getASuccessor() {
revPartialPathStep(result, this.getNode(), this.getSummaryCtx1(), this.getSummaryCtx2(),
this.getAp(), this.getConfiguration())
}
predicate isSink() {
config.isSink(node) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = TRevPartialNil()
}
}
private predicate partialPathStep(
PartialPathNodePriv mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
PartialPathNodeFwd mid, Node node, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
PartialAccessPath ap, Configuration config
) {
not isUnreachableInCall(node, cc.(CallContextSpecificCall).getCall()) and
@@ -3221,8 +3381,7 @@ private module FlowExploration {
pragma[inline]
private predicate partialPathStoreStep(
PartialPathNodePriv mid, PartialAccessPath ap1, TypedContent tc, Node node,
PartialAccessPath ap2
PartialPathNodeFwd mid, PartialAccessPath ap1, TypedContent tc, Node node, PartialAccessPath ap2
) {
exists(Node midNode, DataFlowType contentType |
midNode = mid.getNode() and
@@ -3238,7 +3397,7 @@ private module FlowExploration {
private predicate apConsFwd(
PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config
) {
exists(PartialPathNodePriv mid |
exists(PartialPathNodeFwd mid |
partialPathStoreStep(mid, ap1, tc, _, ap2) and
config = mid.getConfiguration()
)
@@ -3246,7 +3405,7 @@ private module FlowExploration {
pragma[nomagic]
private predicate partialPathReadStep(
PartialPathNodePriv mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
PartialPathNodeFwd mid, PartialAccessPath ap, TypedContent tc, Node node, CallContext cc,
Configuration config
) {
exists(Node midNode |
@@ -3260,7 +3419,7 @@ private module FlowExploration {
}
private predicate partialPathOutOfCallable0(
PartialPathNodePriv mid, ReturnPosition pos, CallContext innercc, PartialAccessPath ap,
PartialPathNodeFwd mid, ReturnPosition pos, CallContext innercc, PartialAccessPath ap,
Configuration config
) {
pos = getReturnPosition(mid.getNode()) and
@@ -3272,7 +3431,7 @@ private module FlowExploration {
pragma[nomagic]
private predicate partialPathOutOfCallable1(
PartialPathNodePriv mid, DataFlowCall call, ReturnKindExt kind, CallContext cc,
PartialPathNodeFwd mid, DataFlowCall call, ReturnKindExt kind, CallContext cc,
PartialAccessPath ap, Configuration config
) {
exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc |
@@ -3286,7 +3445,7 @@ private module FlowExploration {
}
private predicate partialPathOutOfCallable(
PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
PartialPathNodeFwd mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
) {
exists(ReturnKindExt kind, DataFlowCall call |
partialPathOutOfCallable1(mid, call, kind, cc, ap, config)
@@ -3297,7 +3456,7 @@ private module FlowExploration {
pragma[noinline]
private predicate partialPathIntoArg(
PartialPathNodePriv mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap,
PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap,
Configuration config
) {
exists(ArgumentNode arg |
@@ -3311,7 +3470,7 @@ private module FlowExploration {
pragma[nomagic]
private predicate partialPathIntoCallable0(
PartialPathNodePriv mid, DataFlowCallable callable, int i, CallContext outercc,
PartialPathNodeFwd mid, DataFlowCallable callable, int i, CallContext outercc,
DataFlowCall call, PartialAccessPath ap, Configuration config
) {
partialPathIntoArg(mid, i, outercc, call, ap, config) and
@@ -3319,7 +3478,7 @@ private module FlowExploration {
}
private predicate partialPathIntoCallable(
PartialPathNodePriv mid, ParameterNode p, CallContext outercc, CallContextCall innercc,
PartialPathNodeFwd mid, ParameterNode p, CallContext outercc, CallContextCall innercc,
TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap,
Configuration config
) {
@@ -3340,7 +3499,7 @@ private module FlowExploration {
ReturnKindExt kind, CallContextCall cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
PartialAccessPath ap, Configuration config
) {
exists(PartialPathNodePriv mid, ReturnNodeExt ret |
exists(PartialPathNodeFwd mid, ReturnNodeExt ret |
mid.getNode() = ret and
kind = ret.getKind() and
cc = mid.getCallContext() and
@@ -3353,7 +3512,7 @@ private module FlowExploration {
pragma[noinline]
private predicate partialPathThroughCallable0(
DataFlowCall call, PartialPathNodePriv mid, ReturnKindExt kind, CallContext cc,
DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, CallContext cc,
PartialAccessPath ap, Configuration config
) {
exists(ParameterNode p, CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2 |
@@ -3363,13 +3522,164 @@ private module FlowExploration {
}
private predicate partialPathThroughCallable(
PartialPathNodePriv mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
PartialPathNodeFwd mid, Node out, CallContext cc, PartialAccessPath ap, Configuration config
) {
exists(DataFlowCall call, ReturnKindExt kind |
partialPathThroughCallable0(call, mid, kind, cc, ap, config) and
out = kind.getAnOutNode(call)
)
}
private predicate revPartialPathStep(
PartialPathNodeRev mid, Node node, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2,
RevPartialAccessPath ap, Configuration config
) {
localFlowStep(node, mid.getNode(), config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
ap = mid.getAp() and
config = mid.getConfiguration()
or
additionalLocalFlowStep(node, mid.getNode(), config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
mid.getAp() instanceof RevPartialAccessPathNil and
ap = TRevPartialNil() and
config = mid.getConfiguration()
or
jumpStep(node, mid.getNode(), config) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = mid.getAp() and
config = mid.getConfiguration()
or
additionalJumpStep(node, mid.getNode(), config) and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
mid.getAp() instanceof RevPartialAccessPathNil and
ap = TRevPartialNil() and
config = mid.getConfiguration()
or
revPartialPathReadStep(mid, _, _, node, ap) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
config = mid.getConfiguration()
or
exists(RevPartialAccessPath ap0, Content c |
revPartialPathStoreStep(mid, ap0, c, node, config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
apConsRev(ap, c, ap0, config)
)
or
exists(ParameterNode p |
mid.getNode() = p and
viableParamArg(_, p, node) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
sc1 = TRevSummaryCtx1None() and
sc2 = TRevSummaryCtx2None() and
ap = mid.getAp() and
config = mid.getConfiguration()
)
or
exists(ReturnPosition pos |
revPartialPathIntoReturn(mid, pos, sc1, sc2, _, ap, config) and
pos = getReturnPosition(node)
)
or
revPartialPathThroughCallable(mid, node, ap, config) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2()
}
pragma[inline]
private predicate revPartialPathReadStep(
PartialPathNodeRev mid, RevPartialAccessPath ap1, Content c, Node node, RevPartialAccessPath ap2
) {
exists(Node midNode |
midNode = mid.getNode() and
ap1 = mid.getAp() and
read(node, c, midNode) and
ap2.getHead() = c and
ap2.len() = unbindInt(ap1.len() + 1)
)
}
pragma[nomagic]
private predicate apConsRev(
RevPartialAccessPath ap1, Content c, RevPartialAccessPath ap2, Configuration config
) {
exists(PartialPathNodeRev mid |
revPartialPathReadStep(mid, ap1, c, _, ap2) and
config = mid.getConfiguration()
)
}
pragma[nomagic]
private predicate revPartialPathStoreStep(
PartialPathNodeRev mid, RevPartialAccessPath ap, Content c, Node node, Configuration config
) {
exists(Node midNode, TypedContent tc |
midNode = mid.getNode() and
ap = mid.getAp() and
store(node, tc, midNode, _) and
ap.getHead() = c and
config = mid.getConfiguration() and
tc.getContent() = c
)
}
pragma[nomagic]
private predicate revPartialPathIntoReturn(
PartialPathNodeRev mid, ReturnPosition pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2,
DataFlowCall call, RevPartialAccessPath ap, Configuration config
) {
exists(Node out |
mid.getNode() = out and
viableReturnPosOut(call, pos, out) and
sc1 = TRevSummaryCtx1Some(pos) and
sc2 = TRevSummaryCtx2Some(ap) and
ap = mid.getAp() and
config = mid.getConfiguration()
)
}
pragma[nomagic]
private predicate revPartialPathFlowsThrough(
int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap,
Configuration config
) {
exists(PartialPathNodeRev mid, ParameterNode p |
mid.getNode() = p and
p.isParameterOf(_, pos) and
sc1 = mid.getSummaryCtx1() and
sc2 = mid.getSummaryCtx2() and
ap = mid.getAp() and
config = mid.getConfiguration()
)
}
pragma[nomagic]
private predicate revPartialPathThroughCallable0(
DataFlowCall call, PartialPathNodeRev mid, int pos, RevPartialAccessPath ap,
Configuration config
) {
exists(TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2 |
revPartialPathIntoReturn(mid, _, sc1, sc2, call, _, config) and
revPartialPathFlowsThrough(pos, sc1, sc2, ap, config)
)
}
pragma[nomagic]
private predicate revPartialPathThroughCallable(
PartialPathNodeRev mid, ArgumentNode node, RevPartialAccessPath ap, Configuration config
) {
exists(DataFlowCall call, int pos |
revPartialPathThroughCallable0(call, mid, pos, ap, config) and
node.argumentOf(call, pos)
)
}
}
import FlowExploration
@@ -3378,6 +3688,14 @@ private predicate partialFlow(
PartialPathNode source, PartialPathNode node, Configuration configuration
) {
source.getConfiguration() = configuration and
configuration.isSource(source.getNode()) and
source.isFwdSource() and
node = source.getASuccessor+()
}
private predicate revPartialFlow(
PartialPathNode node, PartialPathNode sink, Configuration configuration
) {
sink.getConfiguration() = configuration and
sink.isRevSink() and
node.getASuccessor+() = sink
}

View File

@@ -28,11 +28,7 @@ private class PrimaryArgumentNode extends ArgumentNode {
PrimaryArgumentNode() { exists(CallInstruction call | op = call.getAnArgumentOperand()) }
override predicate argumentOf(DataFlowCall call, int pos) {
op = call.getPositionalArgumentOperand(pos)
or
op = call.getThisArgumentOperand() and pos = -1
}
override predicate argumentOf(DataFlowCall call, int pos) { op = call.getArgumentOperand(pos) }
override string toString() {
result = "Argument " + op.(PositionalArgumentOperand).getIndex()
@@ -110,10 +106,10 @@ class ReturnIndirectionNode extends ReturnNode {
override ReturnIndirectionInstruction primary;
override ReturnKind getKind() {
result = TIndirectReturnKind(-1) and
primary.isThisIndirection()
or
result = TIndirectReturnKind(primary.getParameter().getIndex())
exists(int index |
primary.hasIndex(index) and
result = TIndirectReturnKind(index)
)
}
}
@@ -228,7 +224,7 @@ private class ArrayContent extends Content, TArrayContent {
private predicate fieldStoreStepNoChi(Node node1, FieldContent f, PostUpdateNode node2) {
exists(StoreInstruction store, Class c |
store = node2.asInstruction() and
store.getSourceValue() = node1.asInstruction() and
store.getSourceValueOperand() = node1.asOperand() and
getWrittenField(store, f.(FieldContent).getAField(), c) and
f.hasOffset(c, _, _)
)
@@ -243,18 +239,20 @@ pragma[noinline]
private predicate getWrittenField(Instruction instr, Field f, Class c) {
exists(FieldAddressInstruction fa |
fa =
getFieldInstruction([instr.(StoreInstruction).getDestinationAddress(),
instr.(WriteSideEffectInstruction).getDestinationAddress()]) and
getFieldInstruction([
instr.(StoreInstruction).getDestinationAddress(),
instr.(WriteSideEffectInstruction).getDestinationAddress()
]) and
f = fa.getField() and
c = f.getDeclaringType()
)
}
private predicate fieldStoreStepChi(Node node1, FieldContent f, PostUpdateNode node2) {
exists(StoreInstruction store, ChiInstruction chi |
node1.asInstruction() = store and
exists(ChiPartialOperand operand, ChiInstruction chi |
chi.getPartialOperand() = operand and
node1.asOperand() = operand and
node2.asInstruction() = chi and
chi.getPartial() = store and
exists(Class c |
c = chi.getResultType() and
exists(int startBit, int endBit |
@@ -262,7 +260,7 @@ private predicate fieldStoreStepChi(Node node1, FieldContent f, PostUpdateNode n
f.hasOffset(c, startBit, endBit)
)
or
getWrittenField(store, f.getAField(), c) and
getWrittenField(operand.getDef(), f.getAField(), c) and
f.hasOffset(c, _, _)
)
)
@@ -270,8 +268,13 @@ private predicate fieldStoreStepChi(Node node1, FieldContent f, PostUpdateNode n
private predicate arrayStoreStepChi(Node node1, ArrayContent a, PostUpdateNode node2) {
a = TArrayContent() and
exists(StoreInstruction store |
node1.asInstruction() = store and
exists(ChiPartialOperand operand, ChiInstruction chi, StoreInstruction store |
chi.getPartialOperand() = operand and
store = operand.getDef() and
node1.asOperand() = operand and
// This `ChiInstruction` will always have a non-conflated result because both `ArrayStoreNode`
// and `PointerStoreNode` require it in their characteristic predicates.
node2.asInstruction() = chi and
(
// `x[i] = taint()`
// This matches the characteristic predicate in `ArrayStoreNode`.
@@ -280,10 +283,7 @@ private predicate arrayStoreStepChi(Node node1, ArrayContent a, PostUpdateNode n
// `*p = taint()`
// This matches the characteristic predicate in `PointerStoreNode`.
store.getDestinationAddress().(CopyValueInstruction).getUnary() instanceof LoadInstruction
) and
// This `ChiInstruction` will always have a non-conflated result because both `ArrayStoreNode`
// and `PointerStoreNode` require it in their characteristic predicates.
node2.asInstruction().(ChiInstruction).getPartial() = store
)
)
}
@@ -304,7 +304,7 @@ predicate storeStep(Node node1, Content f, PostUpdateNode node2) {
private predicate fieldStoreStepAfterArraySuppression(
Node node1, FieldContent f, PostUpdateNode node2
) {
exists(BufferMayWriteSideEffectInstruction write, ChiInstruction chi, Class c |
exists(WriteSideEffectInstruction write, ChiInstruction chi, Class c |
not chi.isResultConflated() and
node1.asInstruction() = chi and
node2.asInstruction() = chi and
@@ -332,17 +332,17 @@ private predicate getLoadedField(LoadInstruction load, Field f, Class c) {
* `node2`.
*/
private predicate fieldReadStep(Node node1, FieldContent f, Node node2) {
exists(LoadInstruction load |
node2.asInstruction() = load and
node1.asInstruction() = load.getSourceValueOperand().getAnyDef() and
exists(LoadOperand operand |
node2.asOperand() = operand and
node1.asInstruction() = operand.getAnyDef() and
exists(Class c |
c = load.getSourceValueOperand().getAnyDef().getResultType() and
c = operand.getAnyDef().getResultType() and
exists(int startBit, int endBit |
load.getSourceValueOperand().getUsedInterval(unbindInt(startBit), unbindInt(endBit)) and
operand.getUsedInterval(unbindInt(startBit), unbindInt(endBit)) and
f.hasOffset(c, startBit, endBit)
)
or
getLoadedField(load, f.getAField(), c) and
getLoadedField(operand.getUse(), f.getAField(), c) and
f.hasOffset(c, _, _)
)
)
@@ -363,7 +363,7 @@ private predicate fieldReadStep(Node node1, FieldContent f, Node node2) {
*/
predicate suppressArrayRead(Node node1, ArrayContent a, Node node2) {
a = TArrayContent() and
exists(BufferMayWriteSideEffectInstruction write, ChiInstruction chi |
exists(WriteSideEffectInstruction write, ChiInstruction chi |
node1.asInstruction() = write and
node2.asInstruction() = chi and
chi.getPartial() = write and
@@ -384,20 +384,20 @@ private Instruction skipOneCopyValueInstructionRec(CopyValueInstruction copy) {
result = skipOneCopyValueInstructionRec(copy.getUnary())
}
private Instruction skipCopyValueInstructions(Instruction instr) {
not result instanceof CopyValueInstruction and result = instr
private Instruction skipCopyValueInstructions(Operand op) {
not result instanceof CopyValueInstruction and result = op.getDef()
or
result = skipOneCopyValueInstructionRec(instr)
result = skipOneCopyValueInstructionRec(op.getDef())
}
private predicate arrayReadStep(Node node1, ArrayContent a, Node node2) {
a = TArrayContent() and
// Explicit dereferences such as `*p` or `p[i]` where `p` is a pointer or array.
exists(LoadInstruction load, Instruction address |
load.getSourceValueOperand().isDefinitionInexact() and
node1.asInstruction() = load.getSourceValueOperand().getAnyDef() and
load = node2.asInstruction() and
address = skipCopyValueInstructions(load.getSourceAddress()) and
exists(LoadOperand operand, Instruction address |
operand.isDefinitionInexact() and
node1.asInstruction() = operand.getAnyDef() and
operand = node2.asOperand() and
address = skipCopyValueInstructions(operand.getAddressOperand()) and
(
address instanceof LoadInstruction or
address instanceof ArrayToPointerConvertInstruction or
@@ -418,18 +418,18 @@ private predicate arrayReadStep(Node node1, ArrayContent a, Node node2) {
* use(x);
* ```
* the load on `x` in `use(x)` will exactly overlap with its definition (in this case the definition
* is a `BufferMayWriteSideEffect`). This predicate pops the `ArrayContent` (pushed by the store in `f`)
* is a `WriteSideEffect`). This predicate pops the `ArrayContent` (pushed by the store in `f`)
* from the access path.
*/
private predicate exactReadStep(Node node1, ArrayContent a, Node node2) {
a = TArrayContent() and
exists(BufferMayWriteSideEffectInstruction write, ChiInstruction chi |
exists(WriteSideEffectInstruction write, ChiInstruction chi |
not chi.isResultConflated() and
chi.getPartial() = write and
node1.asInstruction() = write and
node2.asInstruction() = chi and
// To distinquish this case from the `arrayReadStep` case we require that the entire variable was
// overwritten by the `BufferMayWriteSideEffectInstruction` (i.e., there is a load that reads the
// overwritten by the `WriteSideEffectInstruction` (i.e., there is a load that reads the
// entire variable).
exists(LoadInstruction load | load.getSourceValue() = chi)
)
@@ -496,13 +496,6 @@ class DataFlowType = IRType;
/** A function call relevant for data flow. */
class DataFlowCall extends CallInstruction {
/**
* Gets the nth argument for this call.
*
* The range of `n` is from `0` to `getNumberOfArguments() - 1`.
*/
Node getArgument(int n) { result.asInstruction() = this.getPositionalArgument(n) }
Function getEnclosingCallable() { result = this.getEnclosingFunction() }
}

View File

@@ -266,10 +266,8 @@ class ParameterIndirectionNode extends ParameterNode {
override predicate isParameterOf(Function f, int pos) {
exists(int index |
f.getParameter(index) = instr.getParameter()
or
index = -1 and
instr.getIRVariable().(IRThisVariable).getEnclosingFunction() = f
instr.getEnclosingFunction() = f and
instr.hasIndex(index)
|
pos = getArgumentPosOfSideEffect(index)
)
@@ -396,16 +394,16 @@ private FieldAddressInstruction getFieldInstruction(Instruction instr) {
/**
* The target of a `fieldStoreStepAfterArraySuppression` store step, which is used to convert
* an `ArrayContent` to a `FieldContent` when the `BufferMayWriteSideEffect` instruction stores
* an `ArrayContent` to a `FieldContent` when the `WriteSideEffect` instruction stores
* into a field. See the QLDoc for `suppressArrayRead` for an example of where such a conversion
* is inserted.
*/
private class BufferMayWriteSideEffectFieldStoreQualifierNode extends PartialDefinitionNode {
private class WriteSideEffectFieldStoreQualifierNode extends PartialDefinitionNode {
override ChiInstruction instr;
BufferMayWriteSideEffectInstruction write;
WriteSideEffectInstruction write;
FieldAddressInstruction field;
BufferMayWriteSideEffectFieldStoreQualifierNode() {
WriteSideEffectFieldStoreQualifierNode() {
not instr.isResultConflated() and
instr.getPartial() = write and
field = getFieldInstruction(write.getDestinationAddress())
@@ -476,16 +474,8 @@ class DefinitionByReferenceNode extends InstructionNode {
instr
.getPrimaryInstruction()
.(CallInstruction)
.getPositionalArgument(instr.getIndex())
.getArgument(instr.getIndex())
.getUnconvertedResultExpression()
or
result =
instr
.getPrimaryInstruction()
.(CallInstruction)
.getThisArgument()
.getUnconvertedResultExpression() and
instr.getIndex() = -1
}
/** Gets the parameter through which this value is assigned. */

View File

@@ -26,15 +26,6 @@ class IRBlockBase extends TIRBlock {
/** Gets the source location of the first non-`Phi` instruction in this block. */
final Language::Location getLocation() { result = getFirstInstruction().getLocation() }
/**
* INTERNAL: Do not use.
*
* Gets a string that uniquely identifies this block within its enclosing function.
*
* This predicate is used by debugging and printing code only.
*/
final string getUniqueId() { result = getFirstInstruction(this).getUniqueId() }
/**
* INTERNAL: Do not use.
*
@@ -47,14 +38,15 @@ class IRBlockBase extends TIRBlock {
config.shouldEvaluateDebugStringsForFunction(this.getEnclosingFunction())
) and
this =
rank[result + 1](IRBlock funcBlock, int sortOverride |
rank[result + 1](IRBlock funcBlock, int sortOverride, int sortKey1, int sortKey2 |
funcBlock.getEnclosingFunction() = getEnclosingFunction() and
funcBlock.getFirstInstruction().hasSortKeys(sortKey1, sortKey2) and
// Ensure that the block containing `EnterFunction` always comes first.
if funcBlock.getFirstInstruction() instanceof EnterFunctionInstruction
then sortOverride = 0
else sortOverride = 1
|
funcBlock order by sortOverride, funcBlock.getUniqueId()
funcBlock order by sortOverride, sortKey1, sortKey2
)
}
@@ -171,6 +163,46 @@ class IRBlock extends IRBlockBase {
not strictlyDominates(result)
}
/**
* Holds if this block immediately post-dominates `block`.
*
* Block `A` immediate post-dominates block `B` if block `A` strictly post-dominates block `B` and
* block `B` is a direct successor of block `A`.
*/
final predicate immediatelyPostDominates(IRBlock block) {
blockImmediatelyPostDominates(this, block)
}
/**
* Holds if this block strictly post-dominates `block`.
*
* Block `A` strictly post-dominates block `B` if block `A` post-dominates block `B` and blocks `A`
* and `B` are not the same block.
*/
final predicate strictlyPostDominates(IRBlock block) {
blockImmediatelyPostDominates+(this, block)
}
/**
* Holds if this block is a post-dominator of `block`.
*
* Block `A` post-dominates block `B` if any control flow path from `B` to the exit block of the
* function must pass through block `A`. A block always post-dominates itself.
*/
final predicate postDominates(IRBlock block) { strictlyPostDominates(block) or this = block }
/**
* Gets a block on the post-dominance frontier of this block.
*
* The post-dominance frontier of block `A` is the set of blocks `B` such that block `A` does not
* post-dominate block `B`, but block `A` does post-dominate an immediate successor of block `B`.
*/
pragma[noinline]
final IRBlock postPominanceFrontier() {
postDominates(result.getASuccessor()) and
not strictlyPostDominates(result)
}
/**
* Holds if this block is reachable from the entry block of its function.
*/
@@ -288,3 +320,12 @@ private module Cached {
}
private Instruction getFirstInstruction(TIRBlock block) { block = MkIRBlock(result) }
private predicate blockFunctionExit(IRBlock exit) {
exit.getLastInstruction() instanceof ExitFunctionInstruction
}
private predicate blockPredecessor(IRBlock src, IRBlock pred) { src.getAPredecessor() = pred }
private predicate blockImmediatelyPostDominates(IRBlock postDominator, IRBlock block) =
idominance(blockFunctionExit/1, blockPredecessor/2)(_, postDominator, block)

View File

@@ -494,4 +494,34 @@ module InstructionConsistency {
irFunc = getInstructionIRFunction(instr, irFuncText)
)
}
/**
* Holds if the object address operand for the given `FieldAddress` instruction does not have an
* address type.
*/
query predicate fieldAddressOnNonPointer(
FieldAddressInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
) {
not instr.getObjectAddressOperand().getIRType() instanceof IRAddressType and
message =
"FieldAddress instruction '" + instr.toString() +
"' has an object address operand that is not an address, in function '$@'." and
irFunc = getInstructionIRFunction(instr, irFuncText)
}
/**
* Holds if the `this` argument operand for the given `Call` instruction does not have an address
* type.
*/
query predicate thisArgumentIsNonPointer(
CallInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
) {
exists(ThisArgumentOperand thisOperand | thisOperand = instr.getThisArgumentOperand() |
not thisOperand.getIRType() instanceof IRAddressType
) and
message =
"Call instruction '" + instr.toString() +
"' has a `this` argument operand that is not an address, in function '$@'." and
irFunc = getInstructionIRFunction(instr, irFuncText)
}
}

View File

@@ -171,6 +171,16 @@ class Instruction extends Construction::TStageInstruction {
*/
final string getUniqueId() { result = Construction::getInstructionUniqueId(this) }
/**
* INTERNAL: Do not use.
*
* Gets two sort keys for this instruction - used to order instructions for printing
* in test outputs.
*/
final predicate hasSortKeys(int key1, int key2) {
Construction::instructionHasSortKeys(this, key1, key2)
}
/**
* Gets the basic block that contains this instruction.
*/
@@ -572,6 +582,17 @@ class InitializeParameterInstruction extends VariableInstruction {
* Gets the parameter initialized by this instruction.
*/
final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() }
/**
* Holds if this instruction initializes the parameter with index `index`, or
* if `index` is `-1` and this instruction initializes `this`.
*/
pragma[noinline]
final predicate hasIndex(int index) {
index >= 0 and index = this.getParameter().getIndex()
or
index = -1 and this.getIRVariable() instanceof IRThisVariable
}
}
/**
@@ -595,6 +616,18 @@ class InitializeIndirectionInstruction extends VariableInstruction {
* Gets the parameter initialized by this instruction.
*/
final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() }
/**
* Holds if this instruction initializes the memory pointed to by the parameter with
* index `index`, or if `index` is `-1` and this instruction initializes the memory
* pointed to by `this`.
*/
pragma[noinline]
final predicate hasIndex(int index) {
index >= 0 and index = this.getParameter().getIndex()
or
index = -1 and this.getIRVariable() instanceof IRThisVariable
}
}
/**
@@ -769,6 +802,17 @@ class ReturnIndirectionInstruction extends VariableInstruction {
* Holds if this instruction is the return indirection for `this`.
*/
final predicate isThisIndirection() { var instanceof IRThisVariable }
/**
* Holds if this instruction is the return indirection for the parameter with index `index`, or
* if this instruction is the return indirection for `this` and `index` is `-1`.
*/
pragma[noinline]
final predicate hasIndex(int index) {
index >= 0 and index = this.getParameter().getIndex()
or
index = -1 and this.isThisIndirection()
}
}
/**
@@ -1567,6 +1611,7 @@ class CallInstruction extends Instruction {
/**
* Gets the argument operand at the specified index.
*/
pragma[noinline]
final PositionalArgumentOperand getPositionalArgumentOperand(int index) {
result = getAnOperand() and
result.getIndex() = index
@@ -1575,10 +1620,27 @@ class CallInstruction extends Instruction {
/**
* Gets the argument at the specified index.
*/
pragma[noinline]
final Instruction getPositionalArgument(int index) {
result = getPositionalArgumentOperand(index).getDef()
}
/**
* Gets the argument operand at the specified index, or `this` if `index` is `-1`.
*/
pragma[noinline]
final ArgumentOperand getArgumentOperand(int index) {
index >= 0 and result = getPositionalArgumentOperand(index)
or
index = -1 and result = getThisArgumentOperand()
}
/**
* Gets the argument at the specified index, or `this` if `index` is `-1`.
*/
pragma[noinline]
final Instruction getArgument(int index) { result = getArgumentOperand(index).getDef() }
/**
* Gets the number of arguments of the call, including the `this` pointer, if any.
*/

View File

@@ -562,6 +562,9 @@ private Overlap getVariableMemoryLocationOverlap(
use.getEndBitOffset())
}
bindingset[result, b]
private boolean unbindBool(boolean b) { result != b.booleanNot() }
MemoryLocation getResultMemoryLocation(Instruction instr) {
exists(MemoryAccessKind kind, boolean isMayAccess |
kind = instr.getResultMemoryAccess() and
@@ -574,7 +577,8 @@ MemoryLocation getResultMemoryLocation(Instruction instr) {
exists(Allocation var, IRType type, IntValue startBitOffset, IntValue endBitOffset |
hasResultMemoryAccess(instr, var, type, _, startBitOffset, endBitOffset, isMayAccess) and
result =
TVariableMemoryLocation(var, type, _, startBitOffset, endBitOffset, isMayAccess)
TVariableMemoryLocation(var, type, _, startBitOffset, endBitOffset,
unbindBool(isMayAccess))
)
else result = TUnknownMemoryLocation(instr.getEnclosingIRFunction(), isMayAccess)
)
@@ -582,7 +586,7 @@ MemoryLocation getResultMemoryLocation(Instruction instr) {
kind instanceof EntireAllocationMemoryAccess and
result =
TEntireAllocationMemoryLocation(getAddressOperandAllocation(instr.getResultAddressOperand()),
isMayAccess)
unbindBool(isMayAccess))
or
kind instanceof EscapedMemoryAccess and
result = TAllAliasedMemory(instr.getEnclosingIRFunction(), isMayAccess)

View File

@@ -403,16 +403,27 @@ private import PhiInsertion
* instruction for the virtual variable as a whole.
*/
private module PhiInsertion {
/**
* Holds if `phiBlock` is a block in the dominance frontier of a block that has a definition of the
* memory location `defLocation`.
*/
pragma[noinline]
private predicate dominanceFrontierOfDefinition(
Alias::MemoryLocation defLocation, OldBlock phiBlock
) {
exists(OldBlock defBlock |
phiBlock = Dominance::getDominanceFrontier(defBlock) and
definitionHasDefinitionInBlock(defLocation, defBlock)
)
}
/**
* Holds if a `Phi` instruction needs to be inserted for location `defLocation` at the beginning of block `phiBlock`.
*/
predicate definitionHasPhiNode(Alias::MemoryLocation defLocation, OldBlock phiBlock) {
exists(OldBlock defBlock |
phiBlock = Dominance::getDominanceFrontier(defBlock) and
definitionHasDefinitionInBlock(defLocation, defBlock) and
/* We can also eliminate those nodes where the definition is not live on any incoming edge */
definitionLiveOnEntryToBlock(defLocation, phiBlock)
)
dominanceFrontierOfDefinition(defLocation, phiBlock) and
/* We can also eliminate those nodes where the definition is not live on any incoming edge */
definitionLiveOnEntryToBlock(defLocation, phiBlock)
}
/**
@@ -856,7 +867,8 @@ private module CachedForDebugging {
exists(Alias::MemoryLocation location, OldBlock phiBlock, string specificity |
instr = getPhi(phiBlock, location) and
result =
"Phi Block(" + phiBlock.getUniqueId() + ")[" + specificity + "]: " + location.getUniqueId() and
"Phi Block(" + phiBlock.getFirstInstruction().getUniqueId() + ")[" + specificity + "]: " +
location.getUniqueId() and
if location instanceof Alias::VirtualVariable
then
// Sort Phi nodes for virtual variables before Phi nodes for member locations.
@@ -873,6 +885,24 @@ private module CachedForDebugging {
result.getAST() = var.getAST() and
result.getTag() = var.getTag()
}
cached
predicate instructionHasSortKeys(Instruction instr, int key1, int key2) {
exists(OldInstruction oldInstr |
oldInstr = getOldInstruction(instr) and
oldInstr.hasSortKeys(key1, key2)
)
or
instr instanceof TUnreachedInstruction and
key1 = maxValue() and
key2 = maxValue()
}
/**
* Returns the value of the maximum representable integer.
*/
cached
int maxValue() { result = 2147483647 }
}
module SSAConsistency {

View File

@@ -26,15 +26,6 @@ class IRBlockBase extends TIRBlock {
/** Gets the source location of the first non-`Phi` instruction in this block. */
final Language::Location getLocation() { result = getFirstInstruction().getLocation() }
/**
* INTERNAL: Do not use.
*
* Gets a string that uniquely identifies this block within its enclosing function.
*
* This predicate is used by debugging and printing code only.
*/
final string getUniqueId() { result = getFirstInstruction(this).getUniqueId() }
/**
* INTERNAL: Do not use.
*
@@ -47,14 +38,15 @@ class IRBlockBase extends TIRBlock {
config.shouldEvaluateDebugStringsForFunction(this.getEnclosingFunction())
) and
this =
rank[result + 1](IRBlock funcBlock, int sortOverride |
rank[result + 1](IRBlock funcBlock, int sortOverride, int sortKey1, int sortKey2 |
funcBlock.getEnclosingFunction() = getEnclosingFunction() and
funcBlock.getFirstInstruction().hasSortKeys(sortKey1, sortKey2) and
// Ensure that the block containing `EnterFunction` always comes first.
if funcBlock.getFirstInstruction() instanceof EnterFunctionInstruction
then sortOverride = 0
else sortOverride = 1
|
funcBlock order by sortOverride, funcBlock.getUniqueId()
funcBlock order by sortOverride, sortKey1, sortKey2
)
}
@@ -171,6 +163,46 @@ class IRBlock extends IRBlockBase {
not strictlyDominates(result)
}
/**
* Holds if this block immediately post-dominates `block`.
*
* Block `A` immediate post-dominates block `B` if block `A` strictly post-dominates block `B` and
* block `B` is a direct successor of block `A`.
*/
final predicate immediatelyPostDominates(IRBlock block) {
blockImmediatelyPostDominates(this, block)
}
/**
* Holds if this block strictly post-dominates `block`.
*
* Block `A` strictly post-dominates block `B` if block `A` post-dominates block `B` and blocks `A`
* and `B` are not the same block.
*/
final predicate strictlyPostDominates(IRBlock block) {
blockImmediatelyPostDominates+(this, block)
}
/**
* Holds if this block is a post-dominator of `block`.
*
* Block `A` post-dominates block `B` if any control flow path from `B` to the exit block of the
* function must pass through block `A`. A block always post-dominates itself.
*/
final predicate postDominates(IRBlock block) { strictlyPostDominates(block) or this = block }
/**
* Gets a block on the post-dominance frontier of this block.
*
* The post-dominance frontier of block `A` is the set of blocks `B` such that block `A` does not
* post-dominate block `B`, but block `A` does post-dominate an immediate successor of block `B`.
*/
pragma[noinline]
final IRBlock postPominanceFrontier() {
postDominates(result.getASuccessor()) and
not strictlyPostDominates(result)
}
/**
* Holds if this block is reachable from the entry block of its function.
*/
@@ -288,3 +320,12 @@ private module Cached {
}
private Instruction getFirstInstruction(TIRBlock block) { block = MkIRBlock(result) }
private predicate blockFunctionExit(IRBlock exit) {
exit.getLastInstruction() instanceof ExitFunctionInstruction
}
private predicate blockPredecessor(IRBlock src, IRBlock pred) { src.getAPredecessor() = pred }
private predicate blockImmediatelyPostDominates(IRBlock postDominator, IRBlock block) =
idominance(blockFunctionExit/1, blockPredecessor/2)(_, postDominator, block)

View File

@@ -494,4 +494,34 @@ module InstructionConsistency {
irFunc = getInstructionIRFunction(instr, irFuncText)
)
}
/**
* Holds if the object address operand for the given `FieldAddress` instruction does not have an
* address type.
*/
query predicate fieldAddressOnNonPointer(
FieldAddressInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
) {
not instr.getObjectAddressOperand().getIRType() instanceof IRAddressType and
message =
"FieldAddress instruction '" + instr.toString() +
"' has an object address operand that is not an address, in function '$@'." and
irFunc = getInstructionIRFunction(instr, irFuncText)
}
/**
* Holds if the `this` argument operand for the given `Call` instruction does not have an address
* type.
*/
query predicate thisArgumentIsNonPointer(
CallInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
) {
exists(ThisArgumentOperand thisOperand | thisOperand = instr.getThisArgumentOperand() |
not thisOperand.getIRType() instanceof IRAddressType
) and
message =
"Call instruction '" + instr.toString() +
"' has a `this` argument operand that is not an address, in function '$@'." and
irFunc = getInstructionIRFunction(instr, irFuncText)
}
}

View File

@@ -171,6 +171,16 @@ class Instruction extends Construction::TStageInstruction {
*/
final string getUniqueId() { result = Construction::getInstructionUniqueId(this) }
/**
* INTERNAL: Do not use.
*
* Gets two sort keys for this instruction - used to order instructions for printing
* in test outputs.
*/
final predicate hasSortKeys(int key1, int key2) {
Construction::instructionHasSortKeys(this, key1, key2)
}
/**
* Gets the basic block that contains this instruction.
*/
@@ -572,6 +582,17 @@ class InitializeParameterInstruction extends VariableInstruction {
* Gets the parameter initialized by this instruction.
*/
final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() }
/**
* Holds if this instruction initializes the parameter with index `index`, or
* if `index` is `-1` and this instruction initializes `this`.
*/
pragma[noinline]
final predicate hasIndex(int index) {
index >= 0 and index = this.getParameter().getIndex()
or
index = -1 and this.getIRVariable() instanceof IRThisVariable
}
}
/**
@@ -595,6 +616,18 @@ class InitializeIndirectionInstruction extends VariableInstruction {
* Gets the parameter initialized by this instruction.
*/
final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() }
/**
* Holds if this instruction initializes the memory pointed to by the parameter with
* index `index`, or if `index` is `-1` and this instruction initializes the memory
* pointed to by `this`.
*/
pragma[noinline]
final predicate hasIndex(int index) {
index >= 0 and index = this.getParameter().getIndex()
or
index = -1 and this.getIRVariable() instanceof IRThisVariable
}
}
/**
@@ -769,6 +802,17 @@ class ReturnIndirectionInstruction extends VariableInstruction {
* Holds if this instruction is the return indirection for `this`.
*/
final predicate isThisIndirection() { var instanceof IRThisVariable }
/**
* Holds if this instruction is the return indirection for the parameter with index `index`, or
* if this instruction is the return indirection for `this` and `index` is `-1`.
*/
pragma[noinline]
final predicate hasIndex(int index) {
index >= 0 and index = this.getParameter().getIndex()
or
index = -1 and this.isThisIndirection()
}
}
/**
@@ -1567,6 +1611,7 @@ class CallInstruction extends Instruction {
/**
* Gets the argument operand at the specified index.
*/
pragma[noinline]
final PositionalArgumentOperand getPositionalArgumentOperand(int index) {
result = getAnOperand() and
result.getIndex() = index
@@ -1575,10 +1620,27 @@ class CallInstruction extends Instruction {
/**
* Gets the argument at the specified index.
*/
pragma[noinline]
final Instruction getPositionalArgument(int index) {
result = getPositionalArgumentOperand(index).getDef()
}
/**
* Gets the argument operand at the specified index, or `this` if `index` is `-1`.
*/
pragma[noinline]
final ArgumentOperand getArgumentOperand(int index) {
index >= 0 and result = getPositionalArgumentOperand(index)
or
index = -1 and result = getThisArgumentOperand()
}
/**
* Gets the argument at the specified index, or `this` if `index` is `-1`.
*/
pragma[noinline]
final Instruction getArgument(int index) { result = getArgumentOperand(index).getDef() }
/**
* Gets the number of arguments of the call, including the `this` pointer, if any.
*/

View File

@@ -380,10 +380,21 @@ private module CachedForDebugging {
string getTempVariableUniqueId(IRTempVariable var) {
exists(TranslatedElement element |
var = element.getTempVariable(_) and
result = element.getId() + ":" + getTempVariableTagId(var.getTag())
result = element.getId().toString() + ":" + getTempVariableTagId(var.getTag())
)
}
cached
predicate instructionHasSortKeys(Instruction instruction, int key1, int key2) {
key1 = getInstructionTranslatedElement(instruction).getId() and
getInstructionTag(instruction) =
rank[key2](InstructionTag tag, string tagId |
tagId = getInstructionTagId(tag)
|
tag order by tagId
)
}
cached
string getInstructionUniqueId(Instruction instruction) {
result =

View File

@@ -8,6 +8,16 @@ private import TranslatedElement
private import TranslatedExpr
private import TranslatedFunction
/**
* Gets the `CallInstruction` from the `TranslatedCallExpr` for the specified expression.
*/
private CallInstruction getTranslatedCallInstruction(Call call) {
exists(TranslatedCallExpr translatedCall |
translatedCall.getExpr() = call and
result = translatedCall.getInstruction(CallTag())
)
}
/**
* The IR translation of a call to a function. The call may be from an actual
* call in the source code, or could be a call that is part of the translation
@@ -388,7 +398,7 @@ class TranslatedAllocationSideEffects extends TranslatedSideEffects,
tag = OnlyInstructionTag() and
if expr instanceof NewOrNewArrayExpr
then result = getTranslatedAllocatorCall(expr).getInstruction(CallTag())
else result = getTranslatedExpr(expr).getInstruction(CallTag())
else result = getTranslatedCallInstruction(expr)
}
}
@@ -409,7 +419,7 @@ class TranslatedCallSideEffects extends TranslatedSideEffects, TTranslatedCallSi
override Instruction getPrimaryInstructionForSideEffect(InstructionTag tag) {
tag = OnlyInstructionTag() and
result = getTranslatedExpr(expr).getInstruction(CallTag())
result = getTranslatedCallInstruction(expr)
}
}
@@ -599,7 +609,7 @@ class TranslatedSideEffect extends TranslatedElement, TTranslatedArgumentSideEff
override Instruction getPrimaryInstructionForSideEffect(InstructionTag tag) {
tag = OnlyInstructionTag() and
result = getTranslatedExpr(call).getInstruction(CallTag())
result = getTranslatedCallInstruction(call)
}
final override int getInstructionIndex(InstructionTag tag) {

View File

@@ -227,6 +227,125 @@ private predicate usedAsCondition(Expr expr) {
)
}
/**
* Holds if `conv` is an `InheritanceConversion` that requires a `TranslatedLoad`, despite not being
* marked as having an lvalue-to-rvalue conversion.
*
* This is necessary for an `InheritanceConversion` that is originally modeled as a
* prvalue-to-prvalue conversion, since we transform it into a glvalue-to-glvalue conversion. If it
* is actually consumed as a prvalue, such as on the right hand side of an assignment, we need to
* load the resulting glvalue.
*/
private predicate isInheritanceConversionWithImplicitLoad(InheritanceConversion conv) {
// Must have originally been a prvalue-to-prvalue conversion.
isClassPRValue(conv.getExpr()) and
not conv.hasLValueToRValueConversion() and
// Exclude that case where this will be consumed as a glvalue, such as when used as the qualifier
// of a field access.
not isPRValueConversionOnGLValue(conv)
}
/**
* Holds if `expr` is the result of a field access whose qualifier was a prvalue and whose result is
* a prvalue. These accesses are not marked as having loads, but we do need a load in the IR.
*/
private predicate isPRValueFieldAccessWithImplicitLoad(Expr expr) {
expr instanceof ValueFieldAccess and
expr.isPRValueCategory() and
// No need to do a load if we're replacing the result with a constant anyway.
not isIRConstant(expr) and
// Model an array prvalue as the address of the array, just like an array glvalue.
not expr.getUnspecifiedType() instanceof ArrayType
}
/**
* Holds if `expr` is a prvalue of class type.
*
* This same test is used in several places.
*/
pragma[inline]
private predicate isClassPRValue(Expr expr) {
expr.isPRValueCategory() and
expr.getUnspecifiedType() instanceof Class
}
/**
* Holds if `expr` is consumed as a glvalue by its parent. If `expr` is actually a prvalue, it will
* have any lvalue-to-rvalue conversion ignored. If it does not have an lvalue-to-rvalue conversion,
* it will be materialized into a temporary object.
*/
private predicate consumedAsGLValue(Expr expr) {
isClassPRValue(expr) and
(
// Qualifier of a field access.
expr = any(FieldAccess a).getQualifier().getFullyConverted()
or
// Qualifier of a member function call.
expr = any(Call c).getQualifier().getFullyConverted()
or
// The operand of an inheritance conversion.
expr = any(InheritanceConversion c).getExpr()
)
}
/**
* Holds if `expr` is a conversion that is originally a prvalue-to-prvalue conversion, but which is
* applied to a prvalue that will actually be consumed as a glvalue.
*/
predicate isPRValueConversionOnGLValue(Conversion conv) {
exists(Expr consumed |
consumedAsGLValue(consumed) and
isClassPRValue(conv.getExpr()) and
(
// Example: The conversion of `std::string` to `const std::string` when evaluating
// `std::string("foo").c_str()`.
conv instanceof PrvalueAdjustmentConversion
or
// Parentheses are transparent.
conv instanceof ParenthesisExpr
or
// Example: The base class conversion in `f().m()`, when `m` is member function of a base
// class of the return type of `f()`.
conv instanceof InheritanceConversion
) and
(
// Base case: The conversion is consumed directly.
conv = consumed
or
// Recursive case: The conversion is the operand of another prvalue conversion.
isPRValueConversionOnGLValue(conv.getConversion())
)
)
}
/**
* Holds if `expr` is a prvalue of class type that is used in a context that requires a glvalue.
*
* Any conversions between `expr` and the ancestor that consumes the glvalue will also be treated
* as glvalues, but are not part of this relation.
*
* For example:
* ```c++
* std::string("s").c_str();
* ```
* The object for the qualifier is a prvalue(load) of type `std::string`, but the actual
* fully-converted qualifier of the call to `c_str()` is a prvalue adjustment conversion that
* converts the type to `const std::string` to match the type of the `this` pointer of the
* member function. In this case, `mustTransformToGLValue()` will hold for the temporary
* `std::string` object, but not the prvalue adjustment on top of it.
* `isPRValueConversionOnGLValue()` would hold for the prvalue adjustment.
*/
private predicate mustTransformToGLValue(Expr expr) {
not isPRValueConversionOnGLValue(expr) and
(
// The expression is the fully converted qualifier, with no prvalue adjustments on top.
consumedAsGLValue(expr)
or
// The expression has conversions on top, but they are all prvalue adjustments.
isPRValueConversionOnGLValue(expr.getConversion())
)
}
/**
* Holds if `expr` has an lvalue-to-rvalue conversion that should be ignored
* when generating IR. This occurs for conversion from an lvalue of function type
@@ -234,15 +353,24 @@ private predicate usedAsCondition(Expr expr) {
* AST as an lvalue-to-rvalue conversion, but the IR represents both a function
* lvalue and a function pointer prvalue the same.
*/
private predicate ignoreLoad(Expr expr) {
predicate ignoreLoad(Expr expr) {
expr.hasLValueToRValueConversion() and
(
expr instanceof ThisExpr or
expr instanceof FunctionAccess or
expr instanceof ThisExpr
or
expr instanceof FunctionAccess
or
expr.(PointerDereferenceExpr).getOperand().getFullyConverted().getType().getUnspecifiedType()
instanceof FunctionPointerType or
instanceof FunctionPointerType
or
expr.(ReferenceDereferenceExpr).getExpr().getType().getUnspecifiedType() instanceof
FunctionReferenceType
or
// The extractor represents the qualifier of a field access or member function call as a load of
// the temporary object if the original qualifier was a prvalue. For IR purposes, we always want
// to use the address of the temporary object as the qualifier of a field access or the `this`
// argument to a member function call.
mustTransformToGLValue(expr)
)
}
@@ -257,6 +385,17 @@ private predicate needsLoadForParentExpr(Expr expr) {
exists(CrementOperation crement | expr = crement.getOperand().getFullyConverted())
or
exists(AssignOperation ao | expr = ao.getLValue().getFullyConverted())
or
// For arguments that are passed by value but require a constructor call, the extractor emits a
// `TemporaryObjectExpr` as the argument, and marks it as a glvalue. This is roughly how a code-
// generating compiler would implement this, passing the address of the temporary so that the
// callee is using the exact same memory location allocated by the caller. We don't fully model
// this yet, though, so we'll synthesize a load so that we appear to be passing the temporary
// object via a bitwise copy.
exists(Call call |
expr = call.getAnArgument().getFullyConverted().(TemporaryObjectExpr) and
expr.isGLValueCategory()
)
}
/**
@@ -267,6 +406,10 @@ predicate hasTranslatedLoad(Expr expr) {
expr.hasLValueToRValueConversion()
or
needsLoadForParentExpr(expr)
or
isPRValueFieldAccessWithImplicitLoad(expr)
or
isInheritanceConversionWithImplicitLoad(expr)
) and
not ignoreExpr(expr) and
not isNativeCondition(expr) and
@@ -274,6 +417,16 @@ predicate hasTranslatedLoad(Expr expr) {
not ignoreLoad(expr)
}
/**
* Holds if `expr` should have a `TranslatedSyntheticTemporaryObject` on it.
*/
predicate hasTranslatedSyntheticTemporaryObject(Expr expr) {
not ignoreExpr(expr) and
mustTransformToGLValue(expr) and
// If it's a load, we'll just ignore the load in `ignoreLoad()`.
not expr.hasLValueToRValueConversion()
}
/**
* Holds if the specified `DeclarationEntry` needs an IR translation. An IR translation is only
* necessary for automatic local variables, or for static local variables with dynamic
@@ -304,6 +457,9 @@ newtype TTranslatedElement =
// A separate element to handle the lvalue-to-rvalue conversion step of an
// expression.
TTranslatedLoad(Expr expr) { hasTranslatedLoad(expr) } or
// A temporary object that we had to synthesize ourselves, so that we could do a field access or
// method call on a prvalue.
TTranslatedSyntheticTemporaryObject(Expr expr) { hasTranslatedSyntheticTemporaryObject(expr) } or
// For expressions that would not otherwise generate an instruction.
TTranslatedResultCopy(Expr expr) {
not ignoreExpr(expr) and
@@ -351,6 +507,7 @@ newtype TTranslatedElement =
exists(ConstructorFieldInit fieldInit | fieldInit.getExpr().getFullyConverted() = expr) or
exists(NewExpr newExpr | newExpr.getInitializer().getFullyConverted() = expr) or
exists(ThrowExpr throw | throw.getExpr().getFullyConverted() = expr) or
exists(TemporaryObjectExpr temp | temp.getExpr() = expr) or
exists(LambdaExpression lambda | lambda.getInitializer().getFullyConverted() = expr)
)
} or
@@ -595,10 +752,10 @@ abstract class TranslatedElement extends TTranslatedElement {
abstract TranslatedElement getChild(int id);
/**
* Gets the an identifier string for the element. This string is unique within
* Gets the an identifier string for the element. This id is unique within
* the scope of the element's function.
*/
final string getId() { result = getUniqueId().toString() }
final int getId() { result = getUniqueId() }
private TranslatedElement getChildByRank(int rankIndex) {
result =

View File

@@ -15,8 +15,9 @@ private import TranslatedStmt
import TranslatedCall
/**
* Gets the TranslatedExpr for the specified expression. If `expr` is a load,
* the result is the TranslatedExpr for the load portion.
* Gets the TranslatedExpr for the specified expression. If `expr` is a load or synthesized
* temporary object, the result is the TranslatedExpr for the load or synthetic temporary object
* portion.
*/
TranslatedExpr getTranslatedExpr(Expr expr) {
result.getExpr() = expr and
@@ -107,12 +108,17 @@ abstract class TranslatedCoreExpr extends TranslatedExpr {
// If this TranslatedExpr doesn't produce the result, then it must represent
// a glvalue that is then loaded by a TranslatedLoad.
hasTranslatedLoad(expr)
or
// The expression should be treated as a glvalue because its operand was forced to be a glvalue,
// such as for the qualifier of a member access.
isPRValueConversionOnGLValue(expr)
}
final override predicate producesExprResult() {
// If there's no load, then this is the only TranslatedExpr for this
// If there's no load or temp object, then this is the only TranslatedExpr for this
// expression.
not hasTranslatedLoad(expr) and
not hasTranslatedSyntheticTemporaryObject(expr) and
// If there's a result copy, then this expression's result is the copy.
not exprNeedsCopyIfNotLoaded(expr)
}
@@ -246,19 +252,35 @@ class TranslatedConditionValue extends TranslatedCoreExpr, ConditionContext,
private TranslatedCondition getCondition() { result = getTranslatedCondition(expr) }
}
/**
* The IR translation of a node synthesized to adjust the value category of its operand.
* One of:
* - `TranslatedLoad` - Convert from glvalue to prvalue by loading from the location.
* - `TranslatedSyntheticTemporaryObject` - Convert from prvalue to glvalue by storing to a
* temporary variable.
*/
abstract class TranslatedValueCategoryAdjustment extends TranslatedExpr {
final override Instruction getFirstInstruction() { result = getOperand().getFirstInstruction() }
final override TranslatedElement getChild(int id) { id = 0 and result = getOperand() }
final override predicate producesExprResult() {
// A temp object always produces the result of the expression.
any()
}
final TranslatedCoreExpr getOperand() { result.getExpr() = expr }
}
/**
* IR translation of an implicit lvalue-to-rvalue conversion on the result of
* an expression.
*/
class TranslatedLoad extends TranslatedExpr, TTranslatedLoad {
class TranslatedLoad extends TranslatedValueCategoryAdjustment, TTranslatedLoad {
TranslatedLoad() { this = TTranslatedLoad(expr) }
override string toString() { result = "Load of " + expr.toString() }
override Instruction getFirstInstruction() { result = getOperand().getFirstInstruction() }
override TranslatedElement getChild(int id) { id = 0 and result = getOperand() }
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = LoadTag() and
opcode instanceof Opcode::Load and
@@ -286,18 +308,75 @@ class TranslatedLoad extends TranslatedExpr, TTranslatedLoad {
result = getOperand().getResult()
)
}
final override predicate producesExprResult() {
// A load always produces the result of the expression.
any()
}
TranslatedCoreExpr getOperand() { result.getExpr() = expr }
}
/**
* IR translation of an implicit lvalue-to-rvalue conversion on the result of
* an expression.
* The IR translation of a temporary object synthesized by the IR to hold a class prvalue on which
* a member access is going to be performed. This differs from `TranslatedTemporaryObjectExpr` in
* that instances of `TranslatedSyntheticTemporaryObject` are synthesized during IR construction,
* whereas `TranslatedTemporaryObjectExpr` instances are created from `TemporaryObjectExpr` nodes
* from the AST.
*/
class TranslatedSyntheticTemporaryObject extends TranslatedValueCategoryAdjustment,
TTranslatedSyntheticTemporaryObject {
TranslatedSyntheticTemporaryObject() { this = TTranslatedSyntheticTemporaryObject(expr) }
override string toString() { result = "Temporary materialization of " + expr.toString() }
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = InitializerVariableAddressTag() and
opcode instanceof Opcode::VariableAddress and
resultType = getTypeForGLValue(expr.getType())
or
tag = InitializerStoreTag() and
opcode instanceof Opcode::Store and
resultType = getTypeForPRValue(expr.getType())
}
override predicate isResultGLValue() { any() }
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
tag = InitializerVariableAddressTag() and
result = getInstruction(InitializerStoreTag()) and
kind instanceof GotoEdge
or
tag = InitializerStoreTag() and
result = getParent().getChildSuccessor(this) and
kind instanceof GotoEdge
}
override Instruction getChildSuccessor(TranslatedElement child) {
child = getOperand() and result = getInstruction(InitializerVariableAddressTag())
}
override Instruction getResult() { result = getInstruction(InitializerVariableAddressTag()) }
override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
tag = InitializerStoreTag() and
(
operandTag instanceof AddressOperandTag and
result = getInstruction(InitializerVariableAddressTag())
or
operandTag instanceof StoreValueOperandTag and
result = getOperand().getResult()
)
}
final override predicate hasTempVariable(TempVariableTag tag, CppType type) {
tag = TempObjectTempVar() and
type = getTypeForPRValue(expr.getType())
}
final override IRVariable getInstructionVariable(InstructionTag tag) {
tag = InitializerVariableAddressTag() and
result = getIRTempVariable(expr, TempObjectTempVar())
}
}
/**
* IR translation of an expression that simply returns its result. We generate an otherwise useless
* `CopyValue` instruction for these expressions so that there is at least one instruction
* associated with the expression.
*/
class TranslatedResultCopy extends TranslatedExpr, TTranslatedResultCopy {
TranslatedResultCopy() { this = TTranslatedResultCopy(expr) }
@@ -2049,6 +2128,38 @@ class TranslatedBinaryConditionalExpr extends TranslatedConditionalExpr {
}
}
/**
* IR translation of the materialization of a temporary object.
*
* This translation allocates a temporary variable, and initializes it treating `expr.getExpr()` as
* its initializer.
*/
class TranslatedTemporaryObjectExpr extends TranslatedNonConstantExpr,
TranslatedVariableInitialization {
override TemporaryObjectExpr expr;
final override predicate hasTempVariable(TempVariableTag tag, CppType type) {
tag = TempObjectTempVar() and
type = getTypeForPRValue(expr.getType())
}
override Type getTargetType() { result = expr.getType() }
final override TranslatedInitialization getInitialization() {
result = getTranslatedInitialization(expr.getExpr())
}
final override IRVariable getIRVariable() {
result = getIRTempVariable(expr, TempObjectTempVar())
}
final override Instruction getInitializationSuccessor() {
result = getParent().getChildSuccessor(this)
}
final override Instruction getResult() { result = getTargetAddress() }
}
/**
* IR translation of a `throw` expression.
*/

View File

@@ -26,15 +26,6 @@ class IRBlockBase extends TIRBlock {
/** Gets the source location of the first non-`Phi` instruction in this block. */
final Language::Location getLocation() { result = getFirstInstruction().getLocation() }
/**
* INTERNAL: Do not use.
*
* Gets a string that uniquely identifies this block within its enclosing function.
*
* This predicate is used by debugging and printing code only.
*/
final string getUniqueId() { result = getFirstInstruction(this).getUniqueId() }
/**
* INTERNAL: Do not use.
*
@@ -47,14 +38,15 @@ class IRBlockBase extends TIRBlock {
config.shouldEvaluateDebugStringsForFunction(this.getEnclosingFunction())
) and
this =
rank[result + 1](IRBlock funcBlock, int sortOverride |
rank[result + 1](IRBlock funcBlock, int sortOverride, int sortKey1, int sortKey2 |
funcBlock.getEnclosingFunction() = getEnclosingFunction() and
funcBlock.getFirstInstruction().hasSortKeys(sortKey1, sortKey2) and
// Ensure that the block containing `EnterFunction` always comes first.
if funcBlock.getFirstInstruction() instanceof EnterFunctionInstruction
then sortOverride = 0
else sortOverride = 1
|
funcBlock order by sortOverride, funcBlock.getUniqueId()
funcBlock order by sortOverride, sortKey1, sortKey2
)
}
@@ -171,6 +163,46 @@ class IRBlock extends IRBlockBase {
not strictlyDominates(result)
}
/**
* Holds if this block immediately post-dominates `block`.
*
* Block `A` immediate post-dominates block `B` if block `A` strictly post-dominates block `B` and
* block `B` is a direct successor of block `A`.
*/
final predicate immediatelyPostDominates(IRBlock block) {
blockImmediatelyPostDominates(this, block)
}
/**
* Holds if this block strictly post-dominates `block`.
*
* Block `A` strictly post-dominates block `B` if block `A` post-dominates block `B` and blocks `A`
* and `B` are not the same block.
*/
final predicate strictlyPostDominates(IRBlock block) {
blockImmediatelyPostDominates+(this, block)
}
/**
* Holds if this block is a post-dominator of `block`.
*
* Block `A` post-dominates block `B` if any control flow path from `B` to the exit block of the
* function must pass through block `A`. A block always post-dominates itself.
*/
final predicate postDominates(IRBlock block) { strictlyPostDominates(block) or this = block }
/**
* Gets a block on the post-dominance frontier of this block.
*
* The post-dominance frontier of block `A` is the set of blocks `B` such that block `A` does not
* post-dominate block `B`, but block `A` does post-dominate an immediate successor of block `B`.
*/
pragma[noinline]
final IRBlock postPominanceFrontier() {
postDominates(result.getASuccessor()) and
not strictlyPostDominates(result)
}
/**
* Holds if this block is reachable from the entry block of its function.
*/
@@ -288,3 +320,12 @@ private module Cached {
}
private Instruction getFirstInstruction(TIRBlock block) { block = MkIRBlock(result) }
private predicate blockFunctionExit(IRBlock exit) {
exit.getLastInstruction() instanceof ExitFunctionInstruction
}
private predicate blockPredecessor(IRBlock src, IRBlock pred) { src.getAPredecessor() = pred }
private predicate blockImmediatelyPostDominates(IRBlock postDominator, IRBlock block) =
idominance(blockFunctionExit/1, blockPredecessor/2)(_, postDominator, block)

View File

@@ -494,4 +494,34 @@ module InstructionConsistency {
irFunc = getInstructionIRFunction(instr, irFuncText)
)
}
/**
* Holds if the object address operand for the given `FieldAddress` instruction does not have an
* address type.
*/
query predicate fieldAddressOnNonPointer(
FieldAddressInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
) {
not instr.getObjectAddressOperand().getIRType() instanceof IRAddressType and
message =
"FieldAddress instruction '" + instr.toString() +
"' has an object address operand that is not an address, in function '$@'." and
irFunc = getInstructionIRFunction(instr, irFuncText)
}
/**
* Holds if the `this` argument operand for the given `Call` instruction does not have an address
* type.
*/
query predicate thisArgumentIsNonPointer(
CallInstruction instr, string message, OptionalIRFunction irFunc, string irFuncText
) {
exists(ThisArgumentOperand thisOperand | thisOperand = instr.getThisArgumentOperand() |
not thisOperand.getIRType() instanceof IRAddressType
) and
message =
"Call instruction '" + instr.toString() +
"' has a `this` argument operand that is not an address, in function '$@'." and
irFunc = getInstructionIRFunction(instr, irFuncText)
}
}

View File

@@ -171,6 +171,16 @@ class Instruction extends Construction::TStageInstruction {
*/
final string getUniqueId() { result = Construction::getInstructionUniqueId(this) }
/**
* INTERNAL: Do not use.
*
* Gets two sort keys for this instruction - used to order instructions for printing
* in test outputs.
*/
final predicate hasSortKeys(int key1, int key2) {
Construction::instructionHasSortKeys(this, key1, key2)
}
/**
* Gets the basic block that contains this instruction.
*/
@@ -572,6 +582,17 @@ class InitializeParameterInstruction extends VariableInstruction {
* Gets the parameter initialized by this instruction.
*/
final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() }
/**
* Holds if this instruction initializes the parameter with index `index`, or
* if `index` is `-1` and this instruction initializes `this`.
*/
pragma[noinline]
final predicate hasIndex(int index) {
index >= 0 and index = this.getParameter().getIndex()
or
index = -1 and this.getIRVariable() instanceof IRThisVariable
}
}
/**
@@ -595,6 +616,18 @@ class InitializeIndirectionInstruction extends VariableInstruction {
* Gets the parameter initialized by this instruction.
*/
final Language::Parameter getParameter() { result = var.(IRUserVariable).getVariable() }
/**
* Holds if this instruction initializes the memory pointed to by the parameter with
* index `index`, or if `index` is `-1` and this instruction initializes the memory
* pointed to by `this`.
*/
pragma[noinline]
final predicate hasIndex(int index) {
index >= 0 and index = this.getParameter().getIndex()
or
index = -1 and this.getIRVariable() instanceof IRThisVariable
}
}
/**
@@ -769,6 +802,17 @@ class ReturnIndirectionInstruction extends VariableInstruction {
* Holds if this instruction is the return indirection for `this`.
*/
final predicate isThisIndirection() { var instanceof IRThisVariable }
/**
* Holds if this instruction is the return indirection for the parameter with index `index`, or
* if this instruction is the return indirection for `this` and `index` is `-1`.
*/
pragma[noinline]
final predicate hasIndex(int index) {
index >= 0 and index = this.getParameter().getIndex()
or
index = -1 and this.isThisIndirection()
}
}
/**
@@ -1567,6 +1611,7 @@ class CallInstruction extends Instruction {
/**
* Gets the argument operand at the specified index.
*/
pragma[noinline]
final PositionalArgumentOperand getPositionalArgumentOperand(int index) {
result = getAnOperand() and
result.getIndex() = index
@@ -1575,10 +1620,27 @@ class CallInstruction extends Instruction {
/**
* Gets the argument at the specified index.
*/
pragma[noinline]
final Instruction getPositionalArgument(int index) {
result = getPositionalArgumentOperand(index).getDef()
}
/**
* Gets the argument operand at the specified index, or `this` if `index` is `-1`.
*/
pragma[noinline]
final ArgumentOperand getArgumentOperand(int index) {
index >= 0 and result = getPositionalArgumentOperand(index)
or
index = -1 and result = getThisArgumentOperand()
}
/**
* Gets the argument at the specified index, or `this` if `index` is `-1`.
*/
pragma[noinline]
final Instruction getArgument(int index) { result = getArgumentOperand(index).getDef() }
/**
* Gets the number of arguments of the call, including the `this` pointer, if any.
*/

View File

@@ -403,16 +403,27 @@ private import PhiInsertion
* instruction for the virtual variable as a whole.
*/
private module PhiInsertion {
/**
* Holds if `phiBlock` is a block in the dominance frontier of a block that has a definition of the
* memory location `defLocation`.
*/
pragma[noinline]
private predicate dominanceFrontierOfDefinition(
Alias::MemoryLocation defLocation, OldBlock phiBlock
) {
exists(OldBlock defBlock |
phiBlock = Dominance::getDominanceFrontier(defBlock) and
definitionHasDefinitionInBlock(defLocation, defBlock)
)
}
/**
* Holds if a `Phi` instruction needs to be inserted for location `defLocation` at the beginning of block `phiBlock`.
*/
predicate definitionHasPhiNode(Alias::MemoryLocation defLocation, OldBlock phiBlock) {
exists(OldBlock defBlock |
phiBlock = Dominance::getDominanceFrontier(defBlock) and
definitionHasDefinitionInBlock(defLocation, defBlock) and
/* We can also eliminate those nodes where the definition is not live on any incoming edge */
definitionLiveOnEntryToBlock(defLocation, phiBlock)
)
dominanceFrontierOfDefinition(defLocation, phiBlock) and
/* We can also eliminate those nodes where the definition is not live on any incoming edge */
definitionLiveOnEntryToBlock(defLocation, phiBlock)
}
/**
@@ -856,7 +867,8 @@ private module CachedForDebugging {
exists(Alias::MemoryLocation location, OldBlock phiBlock, string specificity |
instr = getPhi(phiBlock, location) and
result =
"Phi Block(" + phiBlock.getUniqueId() + ")[" + specificity + "]: " + location.getUniqueId() and
"Phi Block(" + phiBlock.getFirstInstruction().getUniqueId() + ")[" + specificity + "]: " +
location.getUniqueId() and
if location instanceof Alias::VirtualVariable
then
// Sort Phi nodes for virtual variables before Phi nodes for member locations.
@@ -873,6 +885,24 @@ private module CachedForDebugging {
result.getAST() = var.getAST() and
result.getTag() = var.getTag()
}
cached
predicate instructionHasSortKeys(Instruction instr, int key1, int key2) {
exists(OldInstruction oldInstr |
oldInstr = getOldInstruction(instr) and
oldInstr.hasSortKeys(key1, key2)
)
or
instr instanceof TUnreachedInstruction and
key1 = maxValue() and
key2 = maxValue()
}
/**
* Returns the value of the maximum representable integer.
*/
cached
int maxValue() { result = 2147483647 }
}
module SSAConsistency {

View File

@@ -4,7 +4,8 @@ newtype TTempVariableTag =
ThrowTempVar() or
LambdaTempVar() or
EllipsisTempVar() or
ThisTempVar()
ThisTempVar() or
TempObjectTempVar()
string getTempVariableTagId(TTempVariableTag tag) {
tag = ConditionValueTempVar() and result = "CondVal"
@@ -18,4 +19,6 @@ string getTempVariableTagId(TTempVariableTag tag) {
tag = EllipsisTempVar() and result = "Ellipsis"
or
tag = ThisTempVar() and result = "This"
or
tag = TempObjectTempVar() and result = "Temp"
}

View File

@@ -1,6 +1,7 @@
private import implementations.Allocation
private import implementations.Deallocation
private import implementations.Fread
private import implementations.Getenv
private import implementations.Gets
private import implementations.IdentityFunction
private import implementations.Inet

View File

@@ -10,7 +10,7 @@ import semmle.code.cpp.models.interfaces.Allocation
* An allocation function (such as `malloc`) that has an argument for the size
* in bytes.
*/
class MallocAllocationFunction extends AllocationFunction {
private class MallocAllocationFunction extends AllocationFunction {
int sizeArg;
MallocAllocationFunction() {
@@ -112,7 +112,7 @@ class MallocAllocationFunction extends AllocationFunction {
* An allocation function (such as `alloca`) that does not require a
* corresponding free (and has an argument for the size in bytes).
*/
class AllocaAllocationFunction extends AllocationFunction {
private class AllocaAllocationFunction extends AllocationFunction {
int sizeArg;
AllocaAllocationFunction() {
@@ -137,7 +137,7 @@ class AllocaAllocationFunction extends AllocationFunction {
* An allocation function (such as `calloc`) that has an argument for the size
* and another argument for the size of those units (in bytes).
*/
class CallocAllocationFunction extends AllocationFunction {
private class CallocAllocationFunction extends AllocationFunction {
int sizeArg;
int multArg;
@@ -158,7 +158,7 @@ class CallocAllocationFunction extends AllocationFunction {
* An allocation function (such as `realloc`) that has an argument for the size
* in bytes, and an argument for an existing pointer that is to be reallocated.
*/
class ReallocAllocationFunction extends AllocationFunction {
private class ReallocAllocationFunction extends AllocationFunction {
int sizeArg;
int reallocArg;
@@ -197,7 +197,7 @@ class ReallocAllocationFunction extends AllocationFunction {
* A miscellaneous allocation function that has no explicit argument for
* the size of the allocation.
*/
class SizelessAllocationFunction extends AllocationFunction {
private class SizelessAllocationFunction extends AllocationFunction {
SizelessAllocationFunction() {
exists(string name |
hasGlobalName(name) and
@@ -236,40 +236,6 @@ class SizelessAllocationFunction extends AllocationFunction {
}
}
/**
* An `operator new` or `operator new[]` function that may be associated with `new` or
* `new[]` expressions. Note that `new` and `new[]` are not function calls, but these
* functions may also be called directly.
*/
class OperatorNewAllocationFunction extends AllocationFunction {
OperatorNewAllocationFunction() {
exists(string name |
hasGlobalName(name) and
(
// operator new(bytes, ...)
name = "operator new"
or
// operator new[](bytes, ...)
name = "operator new[]"
)
)
}
override int getSizeArg() { result = 0 }
override predicate requiresDealloc() { not exists(getPlacementArgument()) }
/**
* Gets the position of the placement pointer if this is a placement
* `operator new` function.
*/
int getPlacementArgument() {
getNumberOfParameters() = 2 and
getParameter(1).getType() instanceof VoidPointerType and
result = 1
}
}
/**
* Holds if `sizeExpr` is an expression consisting of a subexpression
* `lengthExpr` multiplied by a constant `sizeof` that is the result of a
@@ -302,7 +268,7 @@ private predicate deconstructSizeExpr(Expr sizeExpr, Expr lengthExpr, int sizeof
/**
* An allocation expression that is a function call, such as call to `malloc`.
*/
class CallAllocationExpr extends AllocationExpr, FunctionCall {
private class CallAllocationExpr extends AllocationExpr, FunctionCall {
AllocationFunction target;
CallAllocationExpr() {
@@ -353,7 +319,7 @@ class CallAllocationExpr extends AllocationExpr, FunctionCall {
/**
* An allocation expression that is a `new` expression.
*/
class NewAllocationExpr extends AllocationExpr, NewExpr {
private class NewAllocationExpr extends AllocationExpr, NewExpr {
NewAllocationExpr() { this instanceof NewExpr }
override int getSizeBytes() { result = getAllocatedType().getSize() }
@@ -366,7 +332,7 @@ class NewAllocationExpr extends AllocationExpr, NewExpr {
/**
* An allocation expression that is a `new []` expression.
*/
class NewArrayAllocationExpr extends AllocationExpr, NewArrayExpr {
private class NewArrayAllocationExpr extends AllocationExpr, NewArrayExpr {
NewArrayAllocationExpr() { this instanceof NewArrayExpr }
override Expr getSizeExpr() {

View File

@@ -9,7 +9,7 @@ import semmle.code.cpp.models.interfaces.Deallocation
/**
* A deallocation function such as `free`.
*/
class StandardDeallocationFunction extends DeallocationFunction {
private class StandardDeallocationFunction extends DeallocationFunction {
int freedArg;
StandardDeallocationFunction() {
@@ -89,32 +89,10 @@ class StandardDeallocationFunction extends DeallocationFunction {
override int getFreedArg() { result = freedArg }
}
/**
* An `operator delete` or `operator delete[]` function that may be associated
* with `delete` or `delete[]` expressions. Note that `delete` and `delete[]`
* are not function calls, but these functions may also be called directly.
*/
class OperatorDeleteDeallocationFunction extends DeallocationFunction {
OperatorDeleteDeallocationFunction() {
exists(string name |
hasGlobalName(name) and
(
// operator delete(pointer, ...)
name = "operator delete"
or
// operator delete[](pointer, ...)
name = "operator delete[]"
)
)
}
override int getFreedArg() { result = 0 }
}
/**
* An deallocation expression that is a function call, such as call to `free`.
*/
class CallDeallocationExpr extends DeallocationExpr, FunctionCall {
private class CallDeallocationExpr extends DeallocationExpr, FunctionCall {
DeallocationFunction target;
CallDeallocationExpr() { target = getTarget() }
@@ -125,7 +103,7 @@ class CallDeallocationExpr extends DeallocationExpr, FunctionCall {
/**
* An deallocation expression that is a `delete` expression.
*/
class DeleteDeallocationExpr extends DeallocationExpr, DeleteExpr {
private class DeleteDeallocationExpr extends DeallocationExpr, DeleteExpr {
DeleteDeallocationExpr() { this instanceof DeleteExpr }
override Expr getFreedExpr() { result = getExpr() }
@@ -134,7 +112,7 @@ class DeleteDeallocationExpr extends DeallocationExpr, DeleteExpr {
/**
* An deallocation expression that is a `delete []` expression.
*/
class DeleteArrayDeallocationExpr extends DeallocationExpr, DeleteArrayExpr {
private class DeleteArrayDeallocationExpr extends DeallocationExpr, DeleteArrayExpr {
DeleteArrayDeallocationExpr() { this instanceof DeleteArrayExpr }
override Expr getFreedExpr() { result = getExpr() }

View File

@@ -1,7 +1,7 @@
import semmle.code.cpp.models.interfaces.Alias
import semmle.code.cpp.models.interfaces.FlowSource
class Fread extends AliasFunction, RemoteFlowFunction {
private class Fread extends AliasFunction, RemoteFlowFunction {
Fread() { this.hasGlobalName("fread") }
override predicate parameterNeverEscapes(int n) {

View File

@@ -6,7 +6,8 @@ import semmle.code.cpp.models.interfaces.FlowSource
/**
* The standard functions `getdelim`, `getwdelim` and the glibc variant `__getdelim`.
*/
class GetDelimFunction extends TaintFunction, AliasFunction, SideEffectFunction, RemoteFlowFunction {
private class GetDelimFunction extends TaintFunction, AliasFunction, SideEffectFunction,
RemoteFlowFunction {
GetDelimFunction() { hasGlobalName(["getdelim", "getwdelim", "__getdelim"]) }
override predicate hasTaintFlow(FunctionInput i, FunctionOutput o) {

View File

@@ -0,0 +1,21 @@
/**
* Provides an implementation class modeling the POSIX function `getenv`.
*/
import cpp
import semmle.code.cpp.models.interfaces.FlowSource
/**
* The POSIX function `getenv`.
*/
class Getenv extends LocalFlowFunction {
Getenv() { this.hasGlobalName("getenv") }
override predicate hasLocalFlowSource(FunctionOutput output, string description) {
(
output.isReturnValueDeref() or
output.isReturnValue()
) and
description = "an environment variable"
}
}

View File

@@ -13,7 +13,7 @@ import semmle.code.cpp.models.interfaces.FlowSource
/**
* The standard functions `gets` and `fgets`.
*/
class GetsFunction extends DataFlowFunction, TaintFunction, ArrayFunction, AliasFunction,
private class GetsFunction extends DataFlowFunction, TaintFunction, ArrayFunction, AliasFunction,
SideEffectFunction, RemoteFlowFunction {
GetsFunction() {
// gets(str)

View File

@@ -6,7 +6,7 @@ import semmle.code.cpp.models.interfaces.SideEffect
/**
* The standard function templates `std::move` and `std::forward`.
*/
class IdentityFunction extends DataFlowFunction, SideEffectFunction, AliasFunction {
private class IdentityFunction extends DataFlowFunction, SideEffectFunction, AliasFunction {
IdentityFunction() {
this.getNamespace().getParentNamespace() instanceof GlobalNamespace and
this.getNamespace().getName() = "std" and

View File

@@ -1,7 +1,8 @@
import semmle.code.cpp.models.interfaces.Taint
import semmle.code.cpp.models.interfaces.Alias
import semmle.code.cpp.models.interfaces.ArrayFunction
class InetNtoa extends TaintFunction {
private class InetNtoa extends TaintFunction {
InetNtoa() { hasGlobalName("inet_ntoa") }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
@@ -10,7 +11,7 @@ class InetNtoa extends TaintFunction {
}
}
class InetAton extends TaintFunction, ArrayFunction {
private class InetAton extends TaintFunction, ArrayFunction {
InetAton() { hasGlobalName("inet_aton") }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
@@ -30,7 +31,7 @@ class InetAton extends TaintFunction, ArrayFunction {
}
}
class InetAddr extends TaintFunction, ArrayFunction, AliasFunction {
private class InetAddr extends TaintFunction, ArrayFunction, AliasFunction {
InetAddr() { hasGlobalName("inet_addr") }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
@@ -49,7 +50,7 @@ class InetAddr extends TaintFunction, ArrayFunction, AliasFunction {
override predicate parameterIsAlwaysReturned(int index) { none() }
}
class InetNetwork extends TaintFunction, ArrayFunction {
private class InetNetwork extends TaintFunction, ArrayFunction {
InetNetwork() { hasGlobalName("inet_network") }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
@@ -62,7 +63,7 @@ class InetNetwork extends TaintFunction, ArrayFunction {
override predicate hasArrayWithNullTerminator(int bufParam) { bufParam = 0 }
}
class InetMakeaddr extends TaintFunction {
private class InetMakeaddr extends TaintFunction {
InetMakeaddr() { hasGlobalName("inet_makeaddr") }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
@@ -74,7 +75,7 @@ class InetMakeaddr extends TaintFunction {
}
}
class InetLnaof extends TaintFunction {
private class InetLnaof extends TaintFunction {
InetLnaof() { hasGlobalName("inet_lnaof") }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
@@ -83,7 +84,7 @@ class InetLnaof extends TaintFunction {
}
}
class InetNetof extends TaintFunction {
private class InetNetof extends TaintFunction {
InetNetof() { hasGlobalName("inet_netof") }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
@@ -92,7 +93,7 @@ class InetNetof extends TaintFunction {
}
}
class InetPton extends TaintFunction, ArrayFunction {
private class InetPton extends TaintFunction, ArrayFunction {
InetPton() { hasGlobalName("inet_pton") }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
@@ -112,7 +113,7 @@ class InetPton extends TaintFunction, ArrayFunction {
override predicate hasArrayWithUnknownSize(int bufParam) { bufParam = 2 }
}
class Gethostbyname extends TaintFunction, ArrayFunction {
private class Gethostbyname extends TaintFunction, ArrayFunction {
Gethostbyname() { hasGlobalName("gethostbyname") }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
@@ -125,7 +126,7 @@ class Gethostbyname extends TaintFunction, ArrayFunction {
override predicate hasArrayWithNullTerminator(int bufParam) { bufParam = 0 }
}
class Gethostbyaddr extends TaintFunction, ArrayFunction {
private class Gethostbyaddr extends TaintFunction, ArrayFunction {
Gethostbyaddr() { hasGlobalName("gethostbyaddr") }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {

View File

@@ -13,7 +13,7 @@ import semmle.code.cpp.models.interfaces.Iterator
/**
* An instantiation of the `std::iterator_traits` template.
*/
class IteratorTraits extends Class {
private class IteratorTraits extends Class {
IteratorTraits() {
this.hasQualifiedName("std", "iterator_traits") and
not this instanceof TemplateClass and
@@ -29,7 +29,7 @@ class IteratorTraits extends Class {
/**
* A type which has the typedefs expected for an iterator.
*/
class IteratorByTypedefs extends Class {
private class IteratorByTypedefs extends Iterator, Class {
IteratorByTypedefs() {
this.getAMember().(TypedefType).hasName("difference_type") and
this.getAMember().(TypedefType).hasName("value_type") and
@@ -43,19 +43,16 @@ class IteratorByTypedefs extends Class {
/**
* The `std::iterator` class.
*/
class StdIterator extends Class {
private class StdIterator extends Iterator, Class {
StdIterator() { this.hasQualifiedName("std", "iterator") }
}
/**
* A type which can be used as an iterator
* A type that is deduced to be an iterator because there is a corresponding
* `std::iterator_traits` instantiation for it.
*/
class Iterator extends Type {
Iterator() {
this instanceof IteratorByTypedefs or
exists(IteratorTraits it | it.getIteratorType() = this) or
this instanceof StdIterator
}
private class IteratorByTraits extends Iterator {
IteratorByTraits() { exists(IteratorTraits it | it.getIteratorType() = this) }
}
private FunctionInput getIteratorArgumentInput(Operator op, int index) {
@@ -81,7 +78,8 @@ private FunctionInput getIteratorArgumentInput(Operator op, int index) {
/**
* A non-member prefix `operator*` function for an iterator type.
*/
class IteratorPointerDereferenceOperator extends Operator, TaintFunction, IteratorReferenceFunction {
private class IteratorPointerDereferenceOperator extends Operator, TaintFunction,
IteratorReferenceFunction {
FunctionInput iteratorInput;
IteratorPointerDereferenceOperator() {
@@ -101,7 +99,7 @@ class IteratorPointerDereferenceOperator extends Operator, TaintFunction, Iterat
/**
* A non-member `operator++` or `operator--` function for an iterator type.
*/
class IteratorCrementOperator extends Operator, DataFlowFunction {
private class IteratorCrementOperator extends Operator, DataFlowFunction {
FunctionInput iteratorInput;
IteratorCrementOperator() {
@@ -118,7 +116,7 @@ class IteratorCrementOperator extends Operator, DataFlowFunction {
/**
* A non-member `operator+` function for an iterator type.
*/
class IteratorAddOperator extends Operator, TaintFunction {
private class IteratorAddOperator extends Operator, TaintFunction {
FunctionInput iteratorInput;
IteratorAddOperator() {
@@ -135,7 +133,7 @@ class IteratorAddOperator extends Operator, TaintFunction {
/**
* A non-member `operator-` function that takes a pointer difference type as its second argument.
*/
class IteratorSubOperator extends Operator, TaintFunction {
private class IteratorSubOperator extends Operator, TaintFunction {
FunctionInput iteratorInput;
IteratorSubOperator() {
@@ -153,7 +151,7 @@ class IteratorSubOperator extends Operator, TaintFunction {
/**
* A non-member `operator+=` or `operator-=` function for an iterator type.
*/
class IteratorAssignArithmeticOperator extends Operator, DataFlowFunction, TaintFunction {
private class IteratorAssignArithmeticOperator extends Operator, DataFlowFunction, TaintFunction {
IteratorAssignArithmeticOperator() {
this.hasName(["operator+=", "operator-="]) and
this.getDeclaringType() instanceof Iterator
@@ -192,7 +190,7 @@ class IteratorPointerDereferenceMemberOperator extends MemberFunction, TaintFunc
/**
* An `operator++` or `operator--` member function for an iterator type.
*/
class IteratorCrementMemberOperator extends MemberFunction, DataFlowFunction, TaintFunction {
private class IteratorCrementMemberOperator extends MemberFunction, DataFlowFunction, TaintFunction {
IteratorCrementMemberOperator() {
this.hasName(["operator++", "operator--"]) and
this.getDeclaringType() instanceof Iterator
@@ -215,7 +213,7 @@ class IteratorCrementMemberOperator extends MemberFunction, DataFlowFunction, Ta
/**
* A member `operator->` function for an iterator type.
*/
class IteratorFieldMemberOperator extends Operator, TaintFunction {
private class IteratorFieldMemberOperator extends Operator, TaintFunction {
IteratorFieldMemberOperator() {
this.hasName("operator->") and
this.getDeclaringType() instanceof Iterator
@@ -230,7 +228,7 @@ class IteratorFieldMemberOperator extends Operator, TaintFunction {
/**
* An `operator+` or `operator-` member function of an iterator class.
*/
class IteratorBinaryArithmeticMemberOperator extends MemberFunction, TaintFunction {
private class IteratorBinaryArithmeticMemberOperator extends MemberFunction, TaintFunction {
IteratorBinaryArithmeticMemberOperator() {
this.hasName(["operator+", "operator-"]) and
this.getDeclaringType() instanceof Iterator
@@ -245,7 +243,8 @@ class IteratorBinaryArithmeticMemberOperator extends MemberFunction, TaintFuncti
/**
* An `operator+=` or `operator-=` member function of an iterator class.
*/
class IteratorAssignArithmeticMemberOperator extends MemberFunction, DataFlowFunction, TaintFunction {
private class IteratorAssignArithmeticMemberOperator extends MemberFunction, DataFlowFunction,
TaintFunction {
IteratorAssignArithmeticMemberOperator() {
this.hasName(["operator+=", "operator-="]) and
this.getDeclaringType() instanceof Iterator
@@ -268,7 +267,8 @@ class IteratorAssignArithmeticMemberOperator extends MemberFunction, DataFlowFun
/**
* An `operator[]` member function of an iterator class.
*/
class IteratorArrayMemberOperator extends MemberFunction, TaintFunction, IteratorReferenceFunction {
private class IteratorArrayMemberOperator extends MemberFunction, TaintFunction,
IteratorReferenceFunction {
IteratorArrayMemberOperator() {
this.hasName("operator[]") and
this.getDeclaringType() instanceof Iterator
@@ -287,7 +287,7 @@ class IteratorArrayMemberOperator extends MemberFunction, TaintFunction, Iterato
* The `hasTaintFlow` override provides flow through output iterators that return themselves with
* `operator*` and use their own `operator=` to assign to the container.
*/
class IteratorAssignmentMemberOperator extends MemberFunction, TaintFunction {
private class IteratorAssignmentMemberOperator extends MemberFunction, TaintFunction {
IteratorAssignmentMemberOperator() {
this.hasName("operator=") and
this.getDeclaringType() instanceof Iterator and
@@ -305,11 +305,13 @@ class IteratorAssignmentMemberOperator extends MemberFunction, TaintFunction {
* A `begin` or `end` member function, or a related member function, that
* returns an iterator.
*/
class BeginOrEndFunction extends MemberFunction, TaintFunction, GetIteratorFunction {
private class BeginOrEndFunction extends MemberFunction, TaintFunction, GetIteratorFunction {
BeginOrEndFunction() {
this
.hasName(["begin", "cbegin", "rbegin", "crbegin", "end", "cend", "rend", "crend",
"before_begin", "cbefore_begin"]) and
.hasName([
"begin", "cbegin", "rbegin", "crbegin", "end", "cend", "rend", "crend", "before_begin",
"cbefore_begin"
]) and
this.getType().getUnspecifiedType() instanceof Iterator
}
@@ -328,7 +330,7 @@ class BeginOrEndFunction extends MemberFunction, TaintFunction, GetIteratorFunct
* The `std::front_inserter`, `std::inserter`, and `std::back_inserter`
* functions.
*/
class InserterIteratorFunction extends GetIteratorFunction {
private class InserterIteratorFunction extends GetIteratorFunction {
InserterIteratorFunction() {
this.hasQualifiedName("std", ["front_inserter", "inserter", "back_inserter"])
}

View File

@@ -12,7 +12,7 @@ import semmle.code.cpp.models.interfaces.Taint
* it does correspond with the constructors we are confident taint should flow
* through.
*/
class ConversionConstructorModel extends Constructor, TaintFunction {
private class ConversionConstructorModel extends Constructor, TaintFunction {
ConversionConstructorModel() {
strictcount(Parameter p | p = getAParameter() and not p.hasInitializer()) = 1 and
not hasSpecifier("explicit")
@@ -32,10 +32,10 @@ class ConversionConstructorModel extends Constructor, TaintFunction {
/**
* Model for C++ copy constructors.
*/
class CopyConstructorModel extends CopyConstructor, DataFlowFunction {
private class CopyConstructorModel extends CopyConstructor, DataFlowFunction {
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
// data flow from the first constructor argument to the returned object
input.isParameter(0) and
input.isParameterDeref(0) and
(
output.isReturnValue()
or
@@ -47,10 +47,10 @@ class CopyConstructorModel extends CopyConstructor, DataFlowFunction {
/**
* Model for C++ move constructors.
*/
class MoveConstructorModel extends MoveConstructor, DataFlowFunction {
private class MoveConstructorModel extends MoveConstructor, DataFlowFunction {
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
// data flow from the first constructor argument to the returned object
input.isParameter(0) and
input.isParameterDeref(0) and
(
output.isReturnValue()
or
@@ -62,7 +62,7 @@ class MoveConstructorModel extends MoveConstructor, DataFlowFunction {
/**
* Model for C++ copy assignment operators.
*/
class CopyAssignmentOperatorModel extends CopyAssignmentOperator, TaintFunction {
private class CopyAssignmentOperatorModel extends CopyAssignmentOperator, TaintFunction {
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
// taint flow from argument to self
input.isParameterDeref(0) and
@@ -78,7 +78,7 @@ class CopyAssignmentOperatorModel extends CopyAssignmentOperator, TaintFunction
/**
* Model for C++ move assignment operators.
*/
class MoveAssignmentOperatorModel extends MoveAssignmentOperator, TaintFunction {
private class MoveAssignmentOperatorModel extends MoveAssignmentOperator, TaintFunction {
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
// taint flow from argument to self
input.isParameterDeref(0) and

View File

@@ -13,7 +13,7 @@ import semmle.code.cpp.models.interfaces.Taint
* The standard functions `memcpy`, `memmove` and `bcopy`; and the gcc variant
* `__builtin___memcpy_chk`.
*/
class MemcpyFunction extends ArrayFunction, DataFlowFunction, SideEffectFunction {
private class MemcpyFunction extends ArrayFunction, DataFlowFunction, SideEffectFunction {
MemcpyFunction() {
// memcpy(dest, src, num)
// memmove(dest, src, num)

View File

@@ -12,7 +12,8 @@ import semmle.code.cpp.models.interfaces.SideEffect
/**
* The standard function `memset` and its assorted variants
*/
class MemsetFunction extends ArrayFunction, DataFlowFunction, AliasFunction, SideEffectFunction {
private class MemsetFunction extends ArrayFunction, DataFlowFunction, AliasFunction,
SideEffectFunction {
MemsetFunction() {
hasGlobalName("memset") or
hasGlobalName("wmemset") or

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