Compare commits

...

359 Commits

Author SHA1 Message Date
Erik Krogh Kristensen
73e6550fd0 update externs from closure-compiler 2021-01-13 13:54:22 +01:00
Geoffrey White
69664535b0 Merge pull request #4881 from ihsinme/main
CPP: Add query for CWE-401 memory leak on unsuccessful call to realloc function
2021-01-13 10:58:09 +00:00
Anders Schack-Mulligen
29935e1388 Merge pull request #4771 from intrigus-lgtm/split-cwe-295
Java: Add unsafe hostname verification query and remove existing overlapping query
2021-01-13 11:31:38 +01:00
intrigus
2931e1f3fb Java: Add change note for #4771 2021-01-12 15:37:45 +01:00
intrigus
1901f6bf55 Java: Make @id @name of query more similar. 2021-01-12 15:36:55 +01:00
intrigus
4fa8f5eab2 Java: Accept test changes 2021-01-12 15:29:03 +01:00
CodeQL CI
1c8547c897 Merge pull request #4774 from erik-krogh/forms
Approved by asgerf
2021-01-12 02:01:38 -08:00
ihsinme
bbd3f7631e Delete test.c
sorry i was in a hurry
2021-01-11 23:52:26 +03:00
ihsinme
b92d63d5df Delete CompilerRemovalOfCodeToClearBuffers.qlref
sorry i was in a hurry
2021-01-11 23:51:37 +03:00
ihsinme
05f866e912 Delete CompilerRemovalOfCodeToClearBuffers.expected
sorry i was in a hurry
2021-01-11 23:51:18 +03:00
ihsinme
d7a5e61f8e Delete CompilerRemovalOfCodeToClearBuffers.qhelp
sorry i was in a hurry
2021-01-11 23:50:47 +03:00
ihsinme
c38cfcb735 Delete CompilerRemovalOfCodeToClearBuffers.ql
sorry i was in a hurry
2021-01-11 23:50:19 +03:00
ihsinme
65ff526eef Delete CompilerRemovalOfCodeToClearBuffers.c
sorry i was in a hurry
2021-01-11 23:49:53 +03:00
ihsinme
ed6d8e3d18 Add files via upload 2021-01-11 23:40:38 +03:00
ihsinme
b185a33157 Add files via upload 2021-01-11 23:39:02 +03:00
ihsinme
b28444b55c Update MemoryLeakOnFailedCallToRealloc.ql
I thought since there is no work on this PR, I will delete the residual import.
2021-01-11 21:17:49 +03:00
intrigus
85286f362c Java: Replace global flow by local flow 2021-01-11 19:02:07 +01:00
intrigus-lgtm
722bd4dafa Java: Revise qhelp 2021-01-11 18:57:24 +01:00
intrigus-lgtm
4cfdb10ddc Java: Improve QLDoc & simplify code
Co-authored-by: Anders Schack-Mulligen <aschackmull@users.noreply.github.com>
2021-01-11 18:50:43 +01:00
CodeQL CI
4bc287e89b Merge pull request #4933 from madneal/fix-for-predicates
Approved by shati-patel
2021-01-11 06:01:33 -08:00
madneal
ee3ffa0700 add extra clarifications in the comments 2021-01-11 21:43:24 +08:00
Mathias Vorreiter Pedersen
59abcd6dae Merge pull request #4938 from geoffw0/cpp302
C++: Tidy up old QL headers
2021-01-11 14:12:16 +01:00
intrigus
5c1e746c96 Java: Rename to EnvReadMethod 2021-01-11 13:42:08 +01:00
intrigus
1eb2b75389 Java: Further reduce FPs, simply Flag2Guard flow 2021-01-11 13:42:08 +01:00
intrigus
b4692734b2 Java: Add QLDoc improve query message 2021-01-11 13:42:08 +01:00
intrigus-lgtm
f4b912cd8a Apply suggestions from doc review
Co-authored-by: Felicity Chapman <felicitymay@github.com>
2021-01-11 13:42:08 +01:00
intrigus
e11304a1ca Java: Autoformat 2021-01-11 13:42:08 +01:00
intrigus-lgtm
b8f3e64a0f Apply suggestions from code review
Co-authored-by: Felicity Chapman <felicitymay@github.com>
2021-01-11 13:42:08 +01:00
intrigus
502e4c39f5 Java: Fix Qhelp 2021-01-11 13:42:08 +01:00
intrigus-lgtm
355cb6eeec Fix Qhelp format
Co-authored-by: Felicity Chapman <felicitymay@github.com>
2021-01-11 13:42:07 +01:00
intrigus-lgtm
10fc2cf9f8 Apply suggestions from code review
Co-authored-by: Chris Smowton <smowton@github.com>
2021-01-11 13:42:07 +01:00
intrigus
c88f07dde4 Java: Accept test output 2021-01-11 13:42:07 +01:00
intrigus
33b0ff28d8 Java: Update test 2021-01-11 13:42:07 +01:00
intrigus
9e2ef9bd74 Java: Filter results by feature flags.
This ignores results that are guarded by a feature flag
that suggests an intentionally insecure feature.
Inspired by Go's `InsecureFeatureFlag.qll` and
`DisabledCertificateCheck.ql`.
2021-01-11 13:42:07 +01:00
intrigus
a62a2e58dd Java: Improve QL-Doc 2021-01-11 13:42:07 +01:00
intrigus
d98b171998 Java: Make EnvTaintedMethod public + QL-Doc 2021-01-11 13:42:07 +01:00
intrigus
e021158b5f Java: Tighter model of HostnameVerifier#verify
This more tightly models `HostnameVerifier#verify` previously it
was possible to accidentally match other methods called `verify`.
2021-01-11 13:42:07 +01:00
intrigus
0a9df07df7 Apply suggestions from review. 2021-01-11 13:42:07 +01:00
intrigus
70b0703952 Java: Remove overlapping code 2021-01-11 13:42:07 +01:00
intrigus
3da1cb0879 Java: Add unsafe hostname verification query 2021-01-11 13:42:07 +01:00
intrigus
8df5d77398 Java: Model HostnameVerifier method
Model `HostnameVerifier#setDefaultHostnameVerifier`
2021-01-11 13:42:06 +01:00
Anders Schack-Mulligen
3a2dd8f1ed Merge pull request #4867 from RasmusWL/java-externalapis-taint-step
Java: Fix taint-step handling for untrusted-data-external-api
2021-01-11 13:36:59 +01:00
madneal
4e373aaf29 replace error with errors 2021-01-11 19:38:27 +08:00
madneal
e0fc9bac08 add error for shotString 2021-01-11 19:15:22 +08:00
Geoffrey White
cf1d1dc5c0 C++: Remove old tags. 2021-01-11 09:31:06 +00:00
madneal
1e2487320c address #4932,fix for errors of Binding behavior 2021-01-09 21:38:25 +08:00
Geoffrey White
70ce5fde75 C++: Improve metadata for GlobalNamespaceClasses.ql. 2021-01-08 18:27:06 +00:00
Shati Patel
b794fcb841 Merge pull request #4925 from shati-patel/fix-links
Fix broken links in CodeQL documentation
2021-01-08 16:35:15 +00:00
Shati Patel
53c46edc1c Address review comments 2021-01-08 15:20:40 +00:00
Rasmus Wriedt Larsen
00c253a710 Java: Don't ignore local taint steps (fixup) 2021-01-08 15:29:01 +01:00
Anders Schack-Mulligen
e5b4975450 Merge pull request #4675 from luchua-bc/cleartext-storage-shared-prefs
Java: Query to detect cleartext storage of sensitive information using Android SharedPreferences
2021-01-08 12:41:34 +01:00
Tamás Vajk
136e5c93d1 Merge pull request #4672 from tamasvajk/feature/extract-anon-types
C#: Extract anonymous types explicitly
2021-01-08 11:54:37 +01:00
CodeQL CI
807fc94627 Merge pull request #4921 from erik-krogh/moreShellSan
Approved by esbena
2021-01-08 00:58:26 -08:00
Tamas Vajk
800fd94572 Add DB upgrade folder 2021-01-08 08:20:49 +01:00
Erik Krogh Kristensen
6423c32990 Update javascript/ql/src/semmle/javascript/security/dataflow/UnsafeShellCommandConstructionCustomizations.qll
Co-authored-by: Esben Sparre Andreasen <esbena@github.com>
2021-01-07 22:02:39 +01:00
Shati Patel
cdcb4a9599 Fix redirects from Sphinx linkcheck 2021-01-07 15:45:40 +00:00
Shati Patel
3da66b7fd9 Fix broken links from Sphinx linkcheck 2021-01-07 15:45:28 +00:00
Tamas Vajk
f971f42bb1 Add new stats file 2021-01-07 15:24:10 +01:00
Tamas Vajk
fdf5cf9dd0 C#: Extract anonymous types explicitly 2021-01-07 15:24:10 +01:00
luchua-bc
606d0946fc Update qldoc 2021-01-07 14:05:12 +00:00
Tamás Vajk
3b16d2689d Merge pull request #4821 from tamasvajk/feature/csharp9-cil-init-prop
C#: Extract init only accessors from CIL
2021-01-07 15:04:40 +01:00
CodeQL CI
c193d9f375 Merge pull request #4823 from erik-krogh/furtherReDoS
Approved by esbena
2021-01-07 05:24:07 -08:00
Erik Krogh Kristensen
7eab08511b add source code examples to blocksCharInAccess 2021-01-07 13:58:26 +01:00
Erik Krogh Kristensen
8b03ab0c01 update docstring for getAShellChar
Co-authored-by: Esben Sparre Andreasen <esbena@github.com>
2021-01-07 13:58:26 +01:00
Erik Krogh Kristensen
2aa59a3f8b support sanitizers that sanitize individual chars in js/shell-command-constructed-from-input 2021-01-07 13:58:25 +01:00
Mathias Vorreiter Pedersen
13a67c906e Merge pull request #4810 from geoffw0/multtoalloc
C++: Query for multiplications used in allocations.
2021-01-07 13:48:58 +01:00
luchua-bc
b54e5b1c49 Revamp the library module 2021-01-07 12:44:59 +00:00
ihsinme
2d6dafc6be Update MemoryLeakOnFailedCallToRealloc.ql 2021-01-07 15:44:50 +03:00
ihsinme
f378c14659 Update MemoryLeakOnFailedCallToRealloc.expected 2021-01-07 15:43:58 +03:00
ihsinme
592cd284e8 Update test.c 2021-01-07 15:41:31 +03:00
CodeQL CI
7db5a999e9 Merge pull request #4919 from erik-krogh/revertSum
Approved by esbena
2021-01-07 03:55:14 -08:00
Tamás Vajk
6cbff13778 Merge pull request #4905 from tamasvajk/fix/attribute-argument-extraction
C#: Fix attribute argument extraction
2021-01-07 12:28:43 +01:00
Erik Krogh Kristensen
7e21081b70 add comment about regexp detected by js/polynomial-redos 2021-01-07 12:06:12 +01:00
Alexander Eyers-Taylor
4100973d17 Merge pull request #4914 from alexet/fix-spec-bugs
QL Language specification. Fix multiple spec bugs.
2021-01-07 10:56:53 +00:00
Tamas Vajk
e00db46d60 Minor code quality improvements 2021-01-07 09:19:13 +01:00
Tom Hvitved
2c09f9a8f2 Merge pull request #4903 from hvitved/csharp/ssa-fast-tc
C#: Port SSA performance improvements from Java
2021-01-07 09:17:21 +01:00
Erik Krogh Kristensen
bfd8d1b1e9 Merge branch 'main' into revertSum 2021-01-06 23:04:08 +01:00
ihsinme
abdeaabd77 Update MemoryLeakOnFailedCallToRealloc.ql 2021-01-06 22:46:03 +03:00
ihsinme
2b8227e04d Update cpp/ql/src/experimental/Security/CWE/CWE-401/MemoryLeakOnFailedCallToRealloc.ql
Co-authored-by: Geoffrey White <40627776+geoffw0@users.noreply.github.com>
2021-01-06 22:23:46 +03:00
ihsinme
f7eb328f76 Update cpp/ql/src/experimental/Security/CWE/CWE-401/MemoryLeakOnFailedCallToRealloc.qhelp
Co-authored-by: Geoffrey White <40627776+geoffw0@users.noreply.github.com>
2021-01-06 22:18:14 +03:00
ihsinme
d7f31ca1a0 Update cpp/ql/src/experimental/Security/CWE/CWE-401/MemoryLeakOnFailedCallToRealloc.qhelp
Co-authored-by: Geoffrey White <40627776+geoffw0@users.noreply.github.com>
2021-01-06 22:17:26 +03:00
CodeQL CI
9d4cd0aa85 Merge pull request #4862 from erik-krogh/shellSanitizer
Approved by esbena
2021-01-06 11:16:12 -08:00
Geoffrey White
f69ceb3dbb Merge pull request #4904 from MathiasVP/conflated-dataflow-testcases
C++: Add dataflow testcases that need flow through conflated memory
2021-01-06 17:48:18 +00:00
luchua-bc
f13b8814f5 Update class/method names in the module 2021-01-06 16:49:35 +00:00
luchua-bc
5690bf49f4 Optimize the query 2021-01-06 16:21:26 +00:00
Alexander Eyers-Taylor
2686335531 Merge pull request #1 from shati-patel/fix-spec-bugs-edits
Editorial review for QL language updates
2021-01-06 14:48:26 +00:00
Erik Krogh Kristensen
f1cee70e82 add class-field flowstep to js/shell-command-constructed-from-input 2021-01-06 14:37:00 +01:00
Tamas Vajk
04074c425b C#: Fix named attribute argument extraction 2021-01-06 14:27:36 +01:00
Tamas Vajk
44372f4db7 C#: Fix attribute argument extraction when default argument value is present 2021-01-06 14:27:36 +01:00
Tamas Vajk
6d95ad3282 C#: Add file instead of generated location for extraction errors when possible 2021-01-06 14:27:31 +01:00
Shati Patel
bc6b1e8ed7 Fix typos and small formatting bugs 2021-01-06 12:11:16 +00:00
Tom Hvitved
74622cf6f3 C#: Fix join-order following stats update 2021-01-06 12:16:19 +01:00
Shati Patel
203d74f255 Remove links to QLDoc spec 2021-01-06 11:04:58 +00:00
Shati Patel
b230868893 Merge pull request #4874 from shati-patel/docs-highlighting
Docs: Tweak syntax highlighting
2021-01-06 10:51:01 +00:00
Jonas Jensen
2483b09e44 Merge pull request #4913 from MathiasVP/pre-hook-autoformat-check
Add pre-commit hook to scripts folder and document it
2021-01-06 11:26:39 +01:00
Erik Krogh Kristensen
28cffa1e07 add comment in isFork about /(a*)*/ regular expressions 2021-01-06 10:44:13 +01:00
Erik Krogh Kristensen
c58f67b189 reintroduce performance improvement - but sound this time 2021-01-06 10:44:13 +01:00
Erik Krogh Kristensen
4392f0270c autoformat 2021-01-06 10:37:36 +01:00
Erik Krogh Kristensen
3d98732136 support nested stars in js/ReDoS 2021-01-06 10:37:35 +01:00
Erik Krogh Kristensen
77967c3e63 undo unsound optimization in js/ReDoS 2021-01-06 10:36:21 +01:00
Erik Krogh Kristensen
b42aac17d5 add more tests for js/ReDoS 2021-01-06 10:34:06 +01:00
Shati Patel
ad07072478 clarify highlight_language conf option 2021-01-05 19:13:28 +00:00
Shati Patel
5a9e098479 Merge pull request #4875 from madneal/htmlComment
Html comment
2021-01-05 16:15:57 +00:00
CodeQL CI
60bba5ea42 Merge pull request #4886 from madneal/test-custom-quries
Approved by shati-patel
2021-01-05 08:07:12 -08:00
Mathias Vorreiter Pedersen
f18486aa60 Update docs/pre-commit-hook-setup.md
Co-authored-by: Jonas Jensen <jbj@github.com>
2021-01-05 17:00:23 +01:00
Shati Patel
1c0e94984c Update docs/codeql/codeql-cli/query-reference-files.rst 2021-01-05 15:50:54 +00:00
Mathias Vorreiter Pedersen
ae388ec796 Update docs/pre-commit-hook-setup.md
Co-authored-by: Cornelius Riemenschneider <criemen@github.com>
2021-01-05 16:27:53 +01:00
Mathias Vorreiter Pedersen
11e2bc3b78 Respond to review comments. 2021-01-05 16:21:24 +01:00
alexet
0bd8c55510 Docs: Remove qldoc from the TOC as it no longer exists 2021-01-05 15:10:59 +00:00
Shati Patel
edcd2dd294 Merge pull request #4878 from shati-patel/docs-pullquotes
Docs: Change remaining notes to "pull-quote" directives
2021-01-05 14:51:01 +00:00
Shati Patel
2702b65651 Merge pull request #4873 from shati-patel/docs-formatting
Docs: Fix CSS for "pull-quotes" and expandable sections
2021-01-05 14:50:45 +00:00
alexet
5d84ecc7f3 QLSpecification: Fix handling of fields to handle overriding properly. 2021-01-05 14:49:02 +00:00
Tamas Vajk
12c28547fc Fix code review findings 2021-01-05 15:15:13 +01:00
Taus
75cfec863f Merge pull request #4828 from yoff/yoff-python-add-source-nodes
Python: add source nodes
2021-01-05 15:07:51 +01:00
alexet
ebb253e409 QLSpec: Fix typo 2021-01-05 14:01:36 +00:00
alexet
67c2006eb0 QLSpec: Adjust wierd wording 2021-01-05 14:01:36 +00:00
alexet
3db9ad3a97 QLSpec: Prevent int-float transitive closures 2021-01-05 14:01:36 +00:00
alexet
fa8a2c0cce QLSpec: Fix predicate resolution 2021-01-05 14:01:35 +00:00
alexet
ce905c0d34 QLSpec: Finish specification for fields. 2021-01-05 14:01:31 +00:00
Shati Patel
929c007e5d Update docs/codeql/ql-language-reference/expressions.rst
Co-authored-by: hubwriter <hubwriter@github.com>
2021-01-05 13:53:19 +00:00
alexet
82187cb1f6 QLSpec:Link to common mark spec 2021-01-05 12:52:48 +00:00
alexet
2bda26b3df QLSpec: Make qldoc part of the language.
We have treated it this way for a while internally and it corrects for some minor deviations from the spec.
2021-01-05 12:52:42 +00:00
Mathias Vorreiter Pedersen
229ab7623e - Add pre-commit hook script to misc/scripts
- Refer to it in CONTRIBUTING.md
- Add setup note in docs folder
2021-01-05 13:47:30 +01:00
Chris Smowton
e87fd86e63 Merge pull request #4814 from luchua-bc/java/password-in-configuration
Java: Password in Java EE configuration files
2021-01-05 11:42:27 +00:00
CodeQL CI
a5e28ac6d6 Merge pull request #4847 from erik-krogh/afterReDoS
Approved by esbena
2021-01-05 01:51:27 -08:00
Anders Schack-Mulligen
26a9ba4aa0 Merge pull request #4898 from JLLeitschuh/feat/JLL/system_get_property
Add MethodAccessSystemGetProperty predicate
2021-01-05 10:46:22 +01:00
Jonathan Leitschuh
ba4a562c9a Update PrintAst.actual with new test output 2021-01-04 23:37:58 -05:00
Rasmus Lerchedahl Petersen
8ceb33d3f7 Python: Also restrict StepSumary::step 2021-01-04 16:42:11 +01:00
Jonathan Leitschuh
028e4756bb Apply suggestions from code review
Co-authored-by: Anders Schack-Mulligen <aschackmull@users.noreply.github.com>
2021-01-04 10:13:52 -05:00
Erik Krogh Kristensen
368603eefa add change note 2021-01-04 15:23:52 +01:00
Tom Hvitved
7f25efd43f Merge pull request #4858 from hvitved/csharp/merge-format-queries
C#: Merge queries `FormatInvalid.ql`, `FormatMissingArgument.ql`, and `FormatUnusedArgument.ql`
2021-01-04 14:53:34 +01:00
Tom Hvitved
1237e566d0 C#: Fix typo 2021-01-04 12:59:45 +01:00
Erik Krogh Kristensen
ce8cc2368b improve precision of intersect 2021-01-04 11:55:51 +01:00
Mathias Vorreiter Pedersen
bb158f1857 C++: Add dataflow testcases that need flow through conflated memory. 2021-01-04 11:43:23 +01:00
Tom Hvitved
c1f822c83f C#: Port SSA performance improvements from Java 2021-01-04 10:18:17 +01:00
Jonas Jensen
86194226e2 Merge pull request #4891 from MathiasVP/get-an-overload-perf-fix
C++: Fix join order in getAnOverload
2021-01-04 10:02:59 +01:00
Tom Hvitved
6d973d0103 Merge pull request #4857 from hvitved/csharp/expr-has-value
C#: Move `Expr::hasValue()` to `DotNet::Expr`
2021-01-04 10:02:45 +01:00
Mathias Vorreiter Pedersen
134982c5a9 C++: Respond to review comments. 2021-01-04 09:06:58 +01:00
Jonathan Leitschuh
54950c2f42 Add MethodAccessSystemGetProperty predicate 2021-01-01 20:07:45 -05:00
Mathias Vorreiter Pedersen
454605b7b1 C++: Fix join order in getAnOverload. 2020-12-30 10:34:26 +01:00
neal1991
380d15eabe fix for the dead link, #4885 2020-12-28 10:28:50 +08:00
ihsinme
0c7381a3b0 Add files via upload 2020-12-26 20:45:11 +03:00
ihsinme
cd7c47ea39 Add files via upload 2020-12-26 20:43:25 +03:00
Shati Patel
8c7245113d Change ordering of sidebar TOC to match index file 2020-12-23 17:16:56 +00:00
Shati Patel
050b15103e Convert remaining notes to pull-quote directives 2020-12-23 17:13:24 +00:00
Shati Patel
dc528767f6 Don't turn arrow into ▶ emoji 2020-12-23 16:47:37 +00:00
Shati Patel
ff8e9e6adf Fix code block in other CodeQL docs 2020-12-23 12:41:46 +00:00
madneal
583395d862 fix LineComment and BlockComment level 2020-12-23 19:49:30 +08:00
neal1991
623de3df41 the level of HTMLHtmlCommentStart and HtmlCommentEnd should be same 2020-12-23 19:18:13 +08:00
Shati Patel
f1d8d9414f Fix code blocks in QL language reference 2020-12-23 10:49:23 +00:00
Shati Patel
a14f53c02f Set default highlighting language to "none" globally
Otherwise Python is the default
2020-12-23 10:29:10 +00:00
Shati Patel
c2fdb47abe Docs: Fix CSS for "pull-quotes" 2020-12-23 07:30:11 +00:00
Erik Krogh Kristensen
44571ffeea use the full ascii set instead of a few chosen chars 2020-12-22 16:00:23 +01:00
Erik Krogh Kristensen
303408b774 remove duplicate char 2020-12-22 15:48:24 +01:00
Erik Krogh Kristensen
354954c80c changes based on review 2020-12-22 15:41:06 +01:00
Erik Krogh Kristensen
530a4aea35 Merge branch 'main' into shellSanitizer 2020-12-22 13:57:15 +01:00
Erik Krogh Kristensen
f7f88689c4 use strings in isTypeofGard 2020-12-22 13:55:32 +01:00
CodeQL CI
2bb96369f1 Merge pull request #4868 from erik-krogh/boundShell
Approved by esbena
2020-12-22 03:35:42 -08:00
CodeQL CI
7c6b4d7324 Merge pull request #4865 from esbena/js/fix-execa-model
Approved by erik-krogh
2020-12-22 03:32:26 -08:00
Erik Krogh Kristensen
da9a4e5267 add test 2020-12-22 11:22:25 +01:00
Erik Krogh Kristensen
b8b5aef5f4 recognize Object.defineProperty(obj, prop, {get: func}) as a property-write 2020-12-22 11:21:41 +01:00
Erik Krogh Kristensen
6a9089b15e recognize bound functions in js/shell-command-constructed-from-input 2020-12-22 11:20:34 +01:00
CodeQL CI
67d0f4d938 Merge pull request #4866 from esbena/js/add-tests-for-examples
Approved by erik-krogh
2020-12-22 02:04:47 -08:00
Rasmus Wriedt Larsen
874af7637f Java: Fix taint-step handling for untrusted-data-external-api
The previous implementation would not handle any `AdditionalTaintStep`
subclasses.
2020-12-22 11:02:50 +01:00
CodeQL CI
e2bba97794 Merge pull request #4860 from erik-krogh/functionExports
Approved by esbena
2020-12-22 01:05:37 -08:00
Erik Krogh Kristensen
df95562f8f remove TTUndefined from TypeOfSanitizer in js/shell-command-constructed-from-input 2020-12-22 09:43:50 +01:00
CodeQL CI
b35edc9de6 Merge pull request #4732 from github/esbena-patch-4
Approved by erik-krogh
2020-12-22 00:42:25 -08:00
Erik Krogh Kristensen
6eb88b9e41 introduce and use TaintTracking::isTypeofGuard 2020-12-22 09:42:12 +01:00
Esben Sparre Andreasen
34a09ff522 JS: add js/conditional-bypass example as a test case 2020-12-22 09:34:25 +01:00
Esben Sparre Andreasen
009527c69c JS: add change note 2020-12-22 09:26:35 +01:00
Esben Sparre Andreasen
ab4f3ea259 JS: fixup for execa.shell and execa.shellSync models 2020-12-22 09:06:18 +01:00
Esben Sparre Andreasen
ba714a1214 JS: add execa.shell tests 2020-12-22 09:01:43 +01:00
Erik Krogh Kristensen
34a6e15426 make TypeOfSanitizer slightly more robost 2020-12-22 08:53:14 +01:00
Erik Krogh Kristensen
18d26cabe5 Update javascript/ql/src/semmle/javascript/security/dataflow/UnsafeShellCommandConstructionCustomizations.qll
Co-authored-by: Esben Sparre Andreasen <esbena@github.com>
2020-12-22 08:37:24 +01:00
Jonas Jensen
430194bb66 Merge pull request #4863 from MathiasVP/is-source-on-default-taint-tracking
C++: Overridable isSource on DefaultTaintTracking
2020-12-22 08:32:07 +01:00
Mathias Vorreiter Pedersen
4f07474b62 C++: Also allow custom sources in taintedWithoutGlobals 2020-12-21 19:55:47 +01:00
Mathias Vorreiter Pedersen
f4f96fe257 C++: Use isSource in queries. These were the only queries that restrict the source after dataflow terminates. 2020-12-21 16:35:35 +01:00
Mathias Vorreiter Pedersen
0e84c638b6 C++: Add isSource to AdjustedConfiguration 2020-12-21 16:34:22 +01:00
Erik Krogh Kristensen
876ba7ef2d add typeof sanitizer to js/shell-command-constructed-from-input 2020-12-21 14:16:55 +01:00
Tom Hvitved
0c78fb2933 Merge pull request #4855 from madneal/fix-for-csharp-docs
Fix for csharp docs
2020-12-21 14:11:36 +01:00
Erik Krogh Kristensen
4ef569fbbe recognize more exported functions in js/shell-command-constructed-from-input 2020-12-21 13:50:22 +01:00
Shati Patel
0a0137bb5e Merge pull request #4859 from github/shati-patel-patch-1
Fix typo in docs title
2020-12-21 12:07:32 +00:00
Erik Krogh Kristensen
e3ec67d5e3 avoid materializing isFeasibleTuple 2020-12-21 12:53:41 +01:00
Jonas Jensen
4308381057 Merge pull request #4846 from MathiasVP/default-taint-tracking-operand-instruction-interleaving
C++: Instruction -> Operand interleaving for DefaultTaintTracking
2020-12-21 12:44:06 +01:00
Shati Patel
66b85f1e5e Fix typo 2020-12-21 11:29:02 +00:00
Neal Caffery
ee0257836f removed, as it fixed by #4848 2020-12-21 19:05:37 +08:00
Erik Krogh Kristensen
cbad705029 general performance improvements in the ReDoS utility library 2020-12-21 11:49:21 +01:00
Tom Hvitved
591f90f98e C#: Add change note 2020-12-21 10:26:49 +01:00
Tom Hvitved
b5a1e039a4 C#: Merge queries FormatInvalid.ql, FormatMissingArgument.ql, and FormatUnusedArgument.ql 2020-12-21 10:13:56 +01:00
Tom Hvitved
8d6c69bf74 C#: Move Expr::hasValue() to DotNet::Expr 2020-12-21 09:46:45 +01:00
Mathias Vorreiter Pedersen
06366fa320 Merge pull request #4856 from jbj/gvn-wrapper-test
C++: Test the AST wrapper for IR GVN
2020-12-21 09:31:10 +01:00
Tom Hvitved
16aee6e71e Merge pull request #4842 from hvitved/csharp/format-method-no-insertion-param
C#: Recognize format methods without insertion parameters
2020-12-21 09:25:18 +01:00
Jonas Jensen
3236cbd83e C++: Test the AST wrapper for IR GVN
Out of our 3 GVN libraries, the one we actually use in production didn't
have tests -- except indirectly through `diff_ir_expr.ql`.
2020-12-21 08:21:02 +01:00
neal1991
b9d24b8255 fix for issue #4849 2020-12-21 08:54:15 +08:00
neal1991
eac83df40b fix for issue #4848 2020-12-21 08:52:42 +08:00
Erik Krogh Kristensen
3a43421193 add missing qhelp 2020-12-19 00:02:42 +01:00
Erik Krogh Kristensen
05569187b4 improve performance of suffix checking 2020-12-18 17:21:15 +01:00
Erik Krogh Kristensen
6369374224 implement new algorithm for detecting superlinear backtracking in regular expressions 2020-12-18 17:21:15 +01:00
Erik Krogh Kristensen
7ce91e9146 introduce cannonical representatives of RegExpTerms to decrease the number of InputSymbols in the NFA 2020-12-18 17:21:11 +01:00
Erik Krogh Kristensen
34dda6d38b refactor to share predicates between regular expression queries 2020-12-18 16:15:56 +01:00
Rasmus Wriedt Larsen
49f902d28b Merge pull request #4757 from yoff/python-dataflow-synthetic-callables
Python: Enclosing callable for synthetic arguments
2020-12-18 16:06:26 +01:00
yoff
a08eb99778 Merge pull request #4779 from RasmusWL/django-class-based-handlers
Python: Add modeling of django class based view handlers
2020-12-18 15:58:51 +01:00
Anders Schack-Mulligen
5106d5df53 Merge pull request #4833 from luchua-bc/java-broken-crypto-algorithms
Java: Add missing broken crypto algorithms
2020-12-18 15:12:29 +01:00
Rasmus Wriedt Larsen
3e6296c7b8 Python: Fix grammar in QLDoc 2020-12-18 14:54:14 +01:00
Rasmus Wriedt Larsen
ed11e8f916 Python: Simplify predicate implementation
Co-authored-by: yoff <lerchedahl@gmail.com>
2020-12-18 14:52:20 +01:00
Mathias Vorreiter Pedersen
b5102043b1 Fix comments. 2020-12-18 14:19:02 +01:00
Chris Smowton
de4cdda839 Merge pull request #4841 from smowton/smowton/admin/mergeback-126-2020-12-16
Mergeback rc/1.26
2020-12-18 12:59:06 +00:00
Mathias Vorreiter Pedersen
f5e4725642 C++: Propagate flow from instruction's to non-exact operands for arrays and unions, and accept test changes. 2020-12-18 13:54:34 +01:00
Rasmus Lerchedahl Petersen
e6e1cc2398 Python: Remember to accept failing tests 2020-12-18 13:38:14 +01:00
Rasmus Lerchedahl Petersen
712765c185 Python: Use ImportExp instead of SSA nodes
This also reverts the previous commit.
It should be squashed with that one, but for now we keep the history,
so we can track the performance tests.
2020-12-18 13:30:24 +01:00
Erik Krogh Kristensen
b2116dc5b4 add more tests for polynomial/exponential redos 2020-12-18 13:19:17 +01:00
Mathias Vorreiter Pedersen
2bf8e47932 Merge branch 'main' into default-taint-tracking-operand-instruction-interleaving 2020-12-18 11:59:10 +01:00
Mathias Vorreiter Pedersen
7ccd48e53c Make DefaultTaintTracking do operand->instruction->operand interleaving like DataFlowUtil. 2020-12-18 11:57:16 +01:00
Geoffrey White
dc4ca9b1b9 C++: Add qhelp and example. 2020-12-18 10:10:05 +00:00
Tom Hvitved
d53faa86dc C#: Restrict FormatInvalid.ql and UncontrolledFormatString.ql to calls with insertions 2020-12-18 10:53:11 +01:00
Rasmus Lerchedahl Petersen
0629d3e6e7 Python: Enclosing callable for synthetic arguments 2020-12-18 10:45:24 +01:00
Rasmus Lerchedahl Petersen
a16d58dfc0 Python: Add tests cases with synthetic arguments 2020-12-18 10:41:42 +01:00
Jonas Jensen
fd7dec7f20 Merge pull request #4824 from geoffw0/modelchanges5
C++: Add cases in the Allocation model.
2020-12-18 09:16:01 +01:00
Tamas Vajk
8e8c3a9ded Add change note 2020-12-18 09:15:33 +01:00
Tamas Vajk
6fd1f0049d Add DB upgrade folder 2020-12-18 09:10:55 +01:00
yoff
39acc9a40b Merge pull request #4735 from RasmusWL/python-untrusted-flow
Python: Untrusted data used in external APIs
2020-12-18 00:15:08 +01:00
yoff
9dd6439e3c Merge pull request #4749 from RasmusWL/command-injection-tests
Python: Add some command injection tests
2020-12-17 23:36:06 +01:00
yoff
8a44405365 Merge pull request #4827 from RasmusWL/reword-qldoc-for-type-tracking-classes
Python: Reword QLDoc for class modeling with type-tracking
2020-12-17 23:28:19 +01:00
Tamas Vajk
8eeab8fdd0 Add new stats file 2020-12-17 21:22:58 +01:00
Tamas Vajk
f3a0d1d138 Add test to list all custom modifiers extracted from IL 2020-12-17 15:43:41 +01:00
Tamas Vajk
7662b55ecc C#: Extract init only accessors and custom modifiers 2020-12-17 15:43:41 +01:00
luchua-bc
bfb138d415 Update qldoc 2020-12-17 14:42:14 +00:00
Tom Hvitved
7a132149a2 C#: Add change note 2020-12-17 15:39:01 +01:00
Tom Hvitved
fe559c190e C#: Recognize format methods without insertion parameters 2020-12-17 15:39:01 +01:00
Geoffrey White
fda531da49 C++: Add query precision. 2020-12-17 14:31:43 +00:00
luchua-bc
7b44ee50ea Revamp the functions to have a string parameter 2020-12-17 14:26:13 +00:00
Tamas Vajk
57c163f314 C#: Add test for CIL setter extraction 2020-12-17 15:23:33 +01:00
Tamás Vajk
45893ab084 Merge pull request #4775 from tamasvajk/feature/cil-attribute-decoding2
C#: Improve CIL attribute decoding
2020-12-17 15:20:44 +01:00
Tamás Vajk
65c58edbed Merge pull request #4617 from tamasvajk/feature/csharp9-implicit-obj-creation
C#: Extract 'ImplicitObjectCreationExpressionSyntax'
2020-12-17 15:20:13 +01:00
Tamas Vajk
f12befdcd0 Add extra test for collection initialization 2020-12-17 13:49:02 +01:00
luchua-bc
b44f01a87b Enhance the check for embedded passwords 2020-12-17 03:47:38 +00:00
luchua-bc
bed8a68d28 Exclude broken algorithms from the list of secure algorithms 2020-12-17 00:41:23 +00:00
Aditya Sharad
a79f1e145b Merge pull request #4832 from github/docs/add-favicon
[docs] Add GitHub favicon to CodeQL docs
2020-12-16 13:29:32 -08:00
Chris Smowton
faa08c10e0 Merge branch 'rc/1.26' of https://github.com/github/codeql into smowton/admin/mergeback-126-2020-12-16 2020-12-16 21:08:20 +00:00
James Fletcher
7bfc2853cb Merge pull request #4839 from github/docs/css-fixes-126
[CodeQL docs] Fix two CSS bugs (rc/1.26)
2020-12-16 18:10:18 +00:00
James Fletcher
58f17d79c2 Merge pull request #4838 from github/docs/css-fixes
[CodeQL docs] Fix two CSS bugs
2020-12-16 16:44:59 +00:00
james
6c430ce0c7 align list items correctly 2020-12-16 16:41:27 +00:00
james
686eca9adf fix footnote spacing 2020-12-16 16:41:27 +00:00
Geoffrey White
136fa01b87 C++: Query headers (apart from precison - needs more research). 2020-12-16 16:24:10 +00:00
Geoffrey White
1e3535754b C++: Violation message. 2020-12-16 16:13:02 +00:00
james
ff123f8e78 align list items correctly 2020-12-16 15:58:12 +00:00
Geoffrey White
2210344f4d C++: Add a test. 2020-12-16 15:44:53 +00:00
james
256460dddc fix footnote spacing 2020-12-16 15:31:25 +00:00
Tamas Vajk
1bc65a68df Address PR review comments 2020-12-16 16:12:11 +01:00
Geoffrey White
c89f7d824b C++: Back out support for SysAllocString. It turns out supporting all of the SysAlloc stuff is beyond our current models, supporting just SysFreeString as we do is OK. 2020-12-16 15:08:53 +00:00
CodeQL CI
41ef7a3fce Merge pull request #4733 from erik-krogh/args
Approved by esbena
2020-12-16 06:51:26 -08:00
CodeQL CI
287954e0d8 Merge pull request #4686 from erik-krogh/buildFp
Approved by esbena
2020-12-16 06:42:41 -08:00
Erik Krogh Kristensen
94e07bb91c add change note 2020-12-16 15:10:03 +01:00
Erik Krogh Kristensen
99af484042 move the "commander" source 2020-12-16 15:05:59 +01:00
luchua-bc
6b77922a25 Fix typo and update qldoc 2020-12-16 14:04:45 +00:00
Erik Krogh Kristensen
2ae0400922 update docstring for dashdash 2020-12-16 15:00:44 +01:00
Erik Krogh Kristensen
3d03e7192c Update javascript/ql/src/semmle/javascript/security/dataflow/IndirectCommandInjectionCustomizations.qll
Co-authored-by: Esben Sparre Andreasen <esbena@github.com>
2020-12-16 14:57:20 +01:00
Erik Krogh Kristensen
d377a02825 add change note 2020-12-16 14:53:23 +01:00
James Fletcher
8b6c53cbb5 Merge pull request #4830 from owen-mc/update-go-supported-frameworks
Update supported Go frameworks
2020-12-16 10:07:48 +00:00
Jonas Jensen
406cc64dcc Merge pull request #4831 from geoffw0/printfunknown
C++: Fix Printf.qll specsAreKnown
2020-12-16 10:56:56 +01:00
james
1a7ca1d3d2 add favicon to query help and support projects 2020-12-16 09:29:26 +00:00
CodeQL CI
9ae8880bd0 Merge pull request #4802 from max-schaefer/js/external-remote-flow-sources
Approved by asgerf, jf205
2020-12-16 00:34:40 -08:00
luchua-bc
d7facb42d6 Add missing broken crypto algorithms 2020-12-16 04:32:11 +00:00
james
c26ae246b3 correct path to favicon on docs landing page 2020-12-15 20:24:29 +00:00
james
f8d8082cf3 add github favicon 2020-12-15 19:34:56 +00:00
Geoffrey White
93dfeac3c8 C++: Make specsAreKnown more accurate. 2020-12-15 17:43:28 +00:00
Geoffrey White
676e85a155 C++: Re-enable the check. 2020-12-15 17:42:10 +00:00
Geoffrey White
94dea9f71d C++: Add a test of unknown format specifiers (with specsAreKnown check disabled). 2020-12-15 17:40:32 +00:00
Tamas Vajk
8fd409898a Add new stats file 2020-12-15 18:34:47 +01:00
Owen Mansel-Chan
1d3d4ed4bf Update supported Go frameworks 2020-12-15 17:04:32 +00:00
Max Schaefer
cf5891487d Apply suggestions from code review
Co-authored-by: James Fletcher <42464962+jf205@users.noreply.github.com>
2020-12-15 15:56:05 +00:00
Max Schaefer
f8d8a9b356 Apply suggestions from code review
Co-authored-by: James Fletcher <42464962+jf205@users.noreply.github.com>
2020-12-15 15:44:19 +00:00
james
7e3feb4f52 add beta note and tweak intro text 2020-12-15 15:35:28 +00:00
james
4720e6cd3b typo 2020-12-15 15:26:07 +00:00
Rasmus Lerchedahl Petersen
638fcab12d Python: Allow path from non-sourceNodes
This is against the philosophy, but we
have still restricted attributes.
We use this PR to test performance.
2020-12-15 15:35:16 +01:00
Rasmus Wriedt Larsen
8df186167e Python: Reword QLDoc for class modeling with type-tracking
As discussed in https://github.com/github/codeql/pull/4797#discussion_r542423387
2020-12-15 15:15:03 +01:00
yoff
be5dbf2ccf Merge pull request #4797 from RasmusWL/stdlib-http-source-modeling
Python: Model sources from stdlib HTTP servers
2020-12-15 14:49:32 +01:00
Tamas Vajk
3cf967458f Fix failing test 2020-12-15 14:28:51 +01:00
james
99a634d2c2 add introduction to article 2020-12-15 12:11:29 +00:00
Tamas Vajk
adba961634 Add DB upgrade folder 2020-12-15 13:10:53 +01:00
Tamas Vajk
6cf3ca49e4 C#: Extract 'ImplicitObjectCreationExpressionSyntax' 2020-12-15 13:10:53 +01:00
Tom Hvitved
8c235323e7 Merge pull request #4796 from hvitved/csharp/cfg/simplify
C#: Various simplifications to CFG logic
2020-12-15 13:07:13 +01:00
Rasmus Wriedt Larsen
050e720770 Python: Minor rewrite
Co-authored-by: yoff <lerchedahl@gmail.com>
2020-12-15 12:02:56 +01:00
Rasmus Wriedt Larsen
272feedb69 Merge branch 'main' into stdlib-http-source-modeling 2020-12-15 11:59:23 +01:00
Rasmus Wriedt Larsen
ed53742f03 Python: Fix additional taint-steps for cgi
So there isn't flow from *any* instance to *any* access of the methods,
but only from the _actual_ instance where the method is accessed.
2020-12-15 11:41:00 +01:00
Max Schaefer
00f244c1d4 JavaScript: Add documentation about new way of adding remote flow sources. 2020-12-15 10:16:20 +00:00
Geoffrey White
4bf2f3af50 C++: Fix comment. 2020-12-15 10:15:41 +00:00
Rasmus Lerchedahl Petersen
e64af59667 Merge branch 'main' of github.com:github/codeql into tausbn-python-add-source-nodes 2020-12-15 11:13:35 +01:00
Rasmus Lerchedahl Petersen
a152833a51 Merge branch 'python-add-source-nodes' of https://github.com/tausbn/codeql into tausbn-python-add-source-nodes 2020-12-15 11:13:02 +01:00
luchua-bc
523f0fb247 Enhance the query and update qldoc 2020-12-14 17:01:30 +00:00
Geoffrey White
e02ebfb9bd C++: Add extra cases to Allocation / Deallocation. 2020-12-14 14:00:36 +00:00
Geoffrey White
899d1ab6d8 C++: Add tests of strdup (and variants) as allocators. 2020-12-14 13:50:11 +00:00
Tom Hvitved
bb637f666c C#: Introduce CfgScope class and generalize ControlFlowTree to include callables 2020-12-14 10:38:39 +01:00
Tom Hvitved
a92404a6cd C#: Add LabeledStmtTree for goto CFG edges 2020-12-14 09:58:54 +01:00
Tom Hvitved
06d42dac3e C#: Use set literals in Splitting.qll 2020-12-14 09:58:54 +01:00
Tom Hvitved
0b2233155c C#: Simplify CFG logic for finally blocks 2020-12-14 09:58:53 +01:00
luchua-bc
d469e9b24e Format the code and minor text change 2020-12-13 21:15:18 +00:00
luchua-bc
e27ccd0a81 Format the code and update qldoc 2020-12-13 02:33:03 +00:00
Max Schaefer
be35e85639 JavaScript: Add change note. 2020-12-12 11:42:55 +00:00
Max Schaefer
9f8508fdc7 JavaScript: Allow specifying additional remote flow sources through JSON. 2020-12-12 11:42:55 +00:00
luchua-bc
7ba237120b Password in Java EE configuration files 2020-12-12 05:15:04 +00:00
Geoffrey White
c5592a1ed7 C++: New experimental query. 2020-12-10 16:46:09 +00:00
Tamas Vajk
a7451a12fc Fix attribute test after rebase 2020-12-09 21:24:21 +01:00
Tamas Vajk
f50cdf5ac7 Add logging to missing underlying enum type 2020-12-09 21:04:12 +01:00
Tamas Vajk
8e6e4189b3 Add logging to attribute decoding failures 2020-12-09 21:04:12 +01:00
Tamas Vajk
db426c1ffe C#: Extract generic types in CIL attribute extraction 2020-12-09 21:04:12 +01:00
Tamas Vajk
56eb04fe6d C#: Improve attribute argument (type, enum) decoding in CIL extraction 2020-12-09 21:04:12 +01:00
Tamas Vajk
0c0ef772c1 Add method to get qualified name of CIL extraction types 2020-12-09 21:00:39 +01:00
Tamas Vajk
d270aa2281 C#: Extract ID writing logic to separate class 2020-12-09 21:00:39 +01:00
Tamas Vajk
fc5f6c5203 C#: Fix ID of TypeReferenceType for top level classes 2020-12-09 21:00:39 +01:00
Tamas Vajk
332a64a6ca Fix erroneous refactorings 2020-12-09 21:00:39 +01:00
Tamas Vajk
151379edd8 C#: Cleanup CIL extraction 'Type' classes 2020-12-09 21:00:39 +01:00
Max Schaefer
0ccfe4f135 JavaScript: Teach autobuilder to include codeql-javascript-*.json files. 2020-12-09 11:35:51 +00:00
Rasmus Wriedt Larsen
fabc6fb7d9 Python: Add change-note 2020-12-08 14:04:46 +01:00
Rasmus Wriedt Larsen
ba1ca70858 Python: Add source modeling of stdlib HTTPRequestHandlers 2020-12-08 14:04:15 +01:00
Rasmus Wriedt Larsen
34863721f0 Python: Model cgi.FieldStorage 2020-12-08 14:03:13 +01:00
Rasmus Wriedt Larsen
43688715f5 Python: Add test of stdlib HTTP server facilities
Just a port of the old tests, except for the fact that I learned
`cgi.FieldStorage()` _should_ be tainted when not specifying any arguments. (and
moved taint-test to own function)

Also clarified how imports of all the .*HTTPRequestHandler works in Python2
2020-12-08 14:01:55 +01:00
Rasmus Wriedt Larsen
e5e8ec6ecc Python: Add a few test-cases for barrier guards and references
I'm not sure references is the best name, but it's the best I could come up with
jsut now
2020-12-07 15:27:20 +01:00
Rasmus Wriedt Larsen
5aa2c2f9d4 Python: Add command injection regex restricted FP 2020-12-07 15:26:56 +01:00
Rasmus Wriedt Larsen
32b547b3f2 Python: Add example of bad command injection sanitizer 2020-12-07 15:26:55 +01:00
Rasmus Wriedt Larsen
8444654117 Python: Adjust whitespace in command injection test 2020-12-07 15:26:54 +01:00
Rasmus Wriedt Larsen
608ce50399 Python: Expose HTTP verbs in HTTP concept
Let's discuss whether doing it this way is reasonable, since I'm not 100% sure
whether this fits into "concepts" or not.
2020-12-04 14:04:56 +01:00
Rasmus Wriedt Larsen
c7ab78f8c2 Python: Add modeling of django class based view handlers
BUT, since MyCustomViewBaseClass.post (django-v2-v3/testapp/views.py) and
Foo.post (django-v2-v3/routing_test.py) aren't handled, this raises important
question about how to do MRO without points-to :S
2020-12-04 14:03:59 +01:00
Rasmus Wriedt Larsen
4ead118a31 Python: Add class based route handler in django tests
Disabled CSRF middleware for now, since it blocked my debugging curl POST requests :(
2020-12-04 13:27:01 +01:00
Rasmus Wriedt Larsen
ffdbecfbb7 Python: Simplify getARouteHandler for Django 2020-12-04 11:29:52 +01:00
Erik Krogh Kristensen
47488f86b5 update test 2020-12-03 16:58:08 +01:00
Erik Krogh Kristensen
3bad75dae5 better support for forms in js/xss-through-dom 2020-12-03 16:57:41 +01:00
Rasmus Wriedt Larsen
a08e1db601 Python: Remove leftover note to self in qhelp file 2020-11-30 17:44:18 +01:00
Rasmus Wriedt Larsen
94e90aac39 Python: Only one Unit implementation
Conflict arose since the Unit in DataFlowPrivate was added in a merged PR.

The behavior from this PR will make it match what java does (931322e4c5/java/ql/src/semmle/code/Unit.qll)
2020-11-30 14:41:47 +01:00
Rasmus Wriedt Larsen
1eac1995a9 Merge branch 'main' into python-untrusted-flow 2020-11-30 14:38:52 +01:00
Rasmus Wriedt Larsen
4ab3fff973 Python: Fix untrusted data to external API example
The hmac.digest function was only added in python 3.7, so obviously doesn't work
on Python 2
2020-11-30 10:42:30 +01:00
Rasmus Wriedt Larsen
cbfcfdf883 Python: Fix UntrustedDataToExternalAPI.qhelp 2020-11-27 17:54:22 +01:00
luchua-bc
ad0ac5b874 Change kind to problem 2020-11-27 16:43:57 +00:00
Erik Krogh Kristensen
fd0d5c9e46 add command parsing model for "commander" 2020-11-27 09:58:00 +00:00
Erik Krogh Kristensen
653ebf7668 add command parsing model for "dashdash" 2020-11-27 09:57:05 +00:00
Erik Krogh Kristensen
269de49196 add model for "meow" 2020-11-27 09:57:05 +00:00
Erik Krogh Kristensen
c5ac98d2e8 add command parsing model for command-line-args 2020-11-27 09:57:05 +00:00
Erik Krogh Kristensen
f33cd8bc8e add command parsing model for argparse 2020-11-27 09:57:05 +00:00
Erik Krogh Kristensen
45067ee651 add command parsing model for "arg" 2020-11-27 09:57:05 +00:00
Erik Krogh Kristensen
821b4be522 more accurately model command parsers that take process.argv as an argument 2020-11-27 09:56:50 +00:00
luchua-bc
a83ddd66eb Add comments about how the future promotion should go 2020-11-26 17:41:46 +00:00
Rasmus Wriedt Larsen
9e4910f863 Python: Untrusted data used in external APIs
A port of the one for Java that was added in https://github.com/github/codeql/pull/3938
2020-11-26 18:19:35 +01:00
Rasmus Wriedt Larsen
7e3dbb0149 Python: Add Unit helper library
Like Java did in https://github.com/github/codeql/pull/4184
2020-11-26 18:17:14 +01:00
luchua-bc
7ad031ca70 Move to experimental and update qldoc 2020-11-26 17:09:53 +00:00
Esben Sparre Andreasen
82e8114c0f Add security tag to js/angular/double-compilation 2020-11-26 10:39:19 +01:00
luchua-bc
a49160423b Enhance the query and add more test cases 2020-11-25 04:33:26 +00:00
luchua-bc
a311462791 Move to query-test folder and update qldoc 2020-11-19 13:12:42 +00:00
Erik Krogh Kristensen
64828713d6 remove FPs in js/build-artifact-leak where the "leaked" properties are constrained to a safe subset 2020-11-18 10:35:02 +01:00
Erik Krogh Kristensen
06733eadea remove two unused imports 2020-11-18 10:34:15 +01:00
luchua-bc
85434ca410 Format the source code and update qldoc 2020-11-17 21:20:53 +00:00
luchua-bc
0bd6255c41 Query for cleartext storage using Android SharedPreferences 2020-11-16 17:23:01 +00:00
Taus
a9149b7e47 Python: Update python/ql/src/semmle/python/dataflow/new/internal/DataFlowPrivate.qll
Co-authored-by: yoff <lerchedahl@gmail.com>
2020-11-06 17:15:58 +01:00
Taus Brock-Nannestad
5a9cc0861c Merge branch 'main' into python-add-source-nodes 2020-11-06 17:12:41 +01:00
Taus Brock-Nannestad
7c58b28e36 Python: Write DataFlow::update more succinctly
This has no impact on performance, but it cleans up the code a bit,
and (hopefully) makes it more readable.
2020-11-05 16:47:41 +01:00
Taus Brock-Nannestad
bae4acabb1 Python: Fix bad join in StrConst::isUnicode
Also fixes a bug ("`B`" was not recognised as a bytestring prefix).

The basic idea behind this fix is that the set of possible prefixes is
fairly small, so it's easier just to precompute them, and then join
them with the entire prefix of the string in question (rather than
look at each string in isolation, get its prefix, and _then_ check
whether it looks like it's a unicode string prefix, which essentially
is what the code did before).
2020-11-05 16:45:27 +01:00
Taus Brock-Nannestad
1251bc57f5 Python: Fix bad join in TObject::literal_instantiation
Here, `context.appliesTo(n)` was being distributed across all of the
disjuncts, which caused poor performance.

The new helper predicate, `literal_node_class` should be fairly small,
since it only applies to a subset of `ControlFlowNode`s, and only
assigns a limited set of `ClassObjectInternal`s to these nodes.
2020-11-05 16:40:29 +01:00
Taus Brock-Nannestad
35a63e2411 Python: Fix bad join in regex::used_as_regex
Since the number of relevant attributes in the `re` module is fairly
small, it made sense to factor this out in a separate predicate, and
the join order also became more sensible.
2020-11-05 16:33:59 +01:00
Taus Brock-Nannestad
035e747ad5 Python: Fix slow use of regexCapture in Builtin::strValue
This is only _really_ expensive when there are a _lot_ of strings in
the database, but for this case, where we're always extracting the
same substring of the string, it's easier -- and faster -- to just
make a substring operation directly.
2020-11-05 16:33:33 +01:00
Taus Brock-Nannestad
83ba8c9bf5 Python: Add LocalSourceNode and flowsTo
This fixes the major performance problem with type tracking on
some (pathological) databases.

The interface could probably be improved a bit. In particular, I'm
thinking that we might want to have `DataFlow::exprNode` return a
`LocalSourceNode` so that a cast isn't necessary in order to use
`flowsTo`.

I have added two `cached` annotations. The one on `flowsTo` is
crucial, as performance regresses without it. The one on
`simpleLocalFlowStep` may not be needed, but Java has a similar
annotation, and to me it makes sense to have this relation cached.
2020-11-05 16:26:03 +01:00
488 changed files with 49668 additions and 24072 deletions

View File

@@ -38,6 +38,8 @@ If you have an idea for a query that you would like to share with other CodeQL u
- The queries and libraries must be autoformatted, for example using the "Format Document" command in [CodeQL for Visual Studio Code](https://help.semmle.com/codeql/codeql-for-vscode/procedures/about-codeql-for-vscode.html).
If you prefer, you can use this [pre-commit hook](misc/scripts/pre-commit) that automatically checks whether your files are correctly formatted. See the [pre-commit hook installation guide](docs/install-pre-commit-hook.md) for instructions on how to install the hook.
4. **Compilation**
- Compilation of the query and any associated libraries and tests must be resilient to future development of the [supported](docs/supported-queries.md) libraries. This means that the functionality cannot use internal libraries, cannot depend on the output of `getAQlClass`, and cannot make use of regexp matching on `toString`.

View File

@@ -4,9 +4,6 @@
* @kind graph
* @id cpp/architecture/class-hierarchies
* @graph.layout organic
* @workingset jhotdraw
* @result succeed 48
* @result_ondemand succeed 48
* @tags maintainability
*/

View File

@@ -4,9 +4,6 @@
* @kind chart
* @id cpp/architecture/inheritance-depth-distribution
* @chart.type line
* @workingset jhotdraw
* @result succeed 48
* @result_ondemand succeed 48
* @tags maintainability
*/

View File

@@ -1,7 +1,8 @@
/**
* @name Global namespace classes
* @description Finds classes that belong to no namespace.
* @kind table
* @kind problem
* @problem.severity recommendation
* @id cpp/architecture/global-namespace-classes
* @tags maintainability
* modularity

View File

@@ -4,9 +4,6 @@
* @kind problem
* @id cpp/architecture/classes-with-many-dependencies
* @problem.severity recommendation
* @workingset jhotdraw
* @result succeed 20
* @result_ondemand succeed 20
* @tags maintainability
* statistical
* non-attributable

View File

@@ -29,6 +29,8 @@ class QueryString extends EnvironmentRead {
}
class Configuration extends TaintTrackingConfiguration {
override predicate isSource(Expr source) { source instanceof QueryString }
override predicate isSink(Element tainted) {
exists(PrintStdoutCall call | call.getAnArgument() = tainted)
}

View File

@@ -34,6 +34,10 @@ predicate sqlite_encryption_used() {
}
class Configuration extends TaintTrackingConfiguration {
override predicate isSource(Expr source) {
super.isSource(source) and source instanceof SensitiveExpr
}
override predicate isSink(Element taintedArg) {
exists(SqliteFunctionCall sqliteCall |
taintedArg = sqliteCall.getASource() and

View File

@@ -0,0 +1,19 @@
image::image(int width, int height)
{
int x, y;
// allocate width * height pixels
pixels = new uint32_t[width * height];
// fill width * height pixels
for (y = 0; y < height; y++)
{
for (x = 0; x < width; x++)
{
pixels[(y * width) + height] = 0;
}
}
// ...
}

View File

@@ -0,0 +1,25 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>The result of a multiplication is used in the size of an allocation. If the multiplication can be made to overflow, a much smaller amount of memory may be allocated than the rest of the code expects. This may lead to overflowing writes when the buffer is accessed later.</p>
</overview>
<recommendation>
<p>To fix this issue, ensure that the arithmetic used in the size of an allocation cannot overflow before memory is allocated.</p>
</recommendation>
<example>
<p>In the following example, an array of size <code>width * height</code> is allocated and stored as <code>pixels</code>. If <code>width</code> and <code>height</code> are set such that the multiplication overflows and wraps to a small value (say, 4) then the initialization code that follows the allocation will write beyond the end of the array.</p>
<sample src="AllocMultiplicationOverflow.cpp"/>
</example>
<references>
<li>
Cplusplus.com: <a href="http://www.cplusplus.com/articles/DE18T05o/">Integer overflow</a>.
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,40 @@
/**
* @name Multiplication result may overflow and be used in allocation
* @description Using a multiplication result that may overflow in the size of an allocation may lead to buffer overflows when the allocated memory is used.
* @kind path-problem
* @problem.severity warning
* @precision low
* @tags security
* correctness
* external/cwe/cwe-190
* external/cwe/cwe-128
* @id cpp/multiplication-overflow-in-alloc
*/
import cpp
import semmle.code.cpp.models.interfaces.Allocation
import semmle.code.cpp.dataflow.DataFlow
import DataFlow::PathGraph
class MultToAllocConfig extends DataFlow::Configuration {
MultToAllocConfig() { this = "MultToAllocConfig" }
override predicate isSource(DataFlow::Node node) {
// a multiplication of two non-constant expressions
exists(MulExpr me |
me = node.asExpr() and
forall(Expr e | e = me.getAnOperand() | not exists(e.getValue()))
)
}
override predicate isSink(DataFlow::Node node) {
// something that affects an allocation size
node.asExpr() = any(AllocationExpr ae).getSizeExpr().getAChild*()
}
}
from MultToAllocConfig config, DataFlow::PathNode source, DataFlow::PathNode sink
where config.hasFlowPath(source, sink)
select sink, source, sink,
"Potentially overflowing value from $@ is used in the size of this allocation.", source,
"multiplication"

View File

@@ -0,0 +1,20 @@
// BAD: on unsuccessful call to realloc, we will lose a pointer to a valid memory block
if (currentSize < newSize)
{
buffer = (unsigned char *)realloc(buffer, newSize);
}
// GOOD: this way we will exclude possible memory leak
unsigned char * tmp;
if (currentSize < newSize)
{
tmp = (unsigned char *)realloc(buffer, newSize);
}
if (tmp == NULL)
{
free(buffer);
}
else
buffer = tmp;

View File

@@ -0,0 +1,38 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>Memory leak on failed call to realloc.
The expression <code>mem = realloc (mem, size)</code> is potentially dangerous, if the call fails, we will lose the pointer to the memory block.
An unsuccessful call is possible not only when trying to allocate a large amount of memory, but also when the process memory is strongly segmented.</p>
<p>False positives include code in which immediately after calling the realloc function, the pointer is manipulated without first checking for validity.
In this case, an exception will occur in the program and it will terminate.
But from the point of view of safe coding, these places require the attention of developers.
At this stage, false positives are also possible in situations where the exception handling is quite complicated and occurs outside the base block in which memory is redistributed.</p>
</overview>
<recommendation>
<p>We recommend storing the result in a temporary variable and eliminating memory leak.</p>
</recommendation>
<example>
<p>The following example demonstrates an erroneous and corrected use of the <code>realloc</code> function.</p>
<sample src="MemoryLeakOnFailedCallToRealloc.c" />
</example>
<references>
<li>
CERT C++ Coding Standard:
<a href="https://wiki.sei.cmu.edu/confluence/display/cplusplus/MEM51-CPP.+Properly+deallocate+dynamically+allocated+resources">MEM51-CPP. Properly deallocate dynamically allocated resources</a>.
</li>
<li>
CERT C Coding Standard:
<a href="https://wiki.sei.cmu.edu/confluence/display/c/WIN30-C.+Properly+pair+allocation+and+deallocation+functions">WIN30-C. Properly pair allocation and deallocation functions</a>.
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,69 @@
/**
* @name Memory leak on failed call to realloc
* @description The expression mem = realloc (mem, size) is potentially dangerous, if the call fails, we will lose the pointer to the memory block.
* We recommend storing the result in a temporary variable and eliminating memory leak.
* @kind problem
* @id cpp/memory-leak-on-failed-call-to-realloc
* @problem.severity warning
* @precision medium
* @tags correctness
* security
* external/cwe/cwe-401
*/
import cpp
/**
* A call to `realloc` of the form `v = realloc(v, size)`, for some variable `v`.
*/
class ReallocCallLeak extends FunctionCall {
Variable v;
ReallocCallLeak() {
exists(AssignExpr ex, VariableAccess va1, VariableAccess va2 |
this.getTarget().hasName("realloc") and
this = ex.getRValue() and
va1 = ex.getLValue() and
va2 = this.getArgument(0) and
va1 = v.getAnAccess() and
va2 = v.getAnAccess()
)
}
predicate isExistsIfWithExitCall() {
exists(IfStmt ifc |
this.getArgument(0) = v.getAnAccess() and
ifc.getCondition().getAChild*() = v.getAnAccess() and
ifc.getEnclosingFunction() = this.getEnclosingFunction() and
ifc.getLocation().getStartLine() >= this.getArgument(0).getLocation().getStartLine() and
exists(FunctionCall fc |
fc.getTarget().hasName("exit") and
fc.getEnclosingFunction() = this.getEnclosingFunction() and
(ifc.getThen().getAChild*() = fc or ifc.getElse().getAChild*() = fc)
)
or
exists(FunctionCall fc, FunctionCall ftmp1, FunctionCall ftmp2 |
ftmp1.getTarget().hasName("exit") and
ftmp2.(ControlFlowNode).getASuccessor*() = ftmp1 and
fc = ftmp2.getEnclosingFunction().getACallToThisFunction() and
fc.getEnclosingFunction() = this.getEnclosingFunction() and
(ifc.getThen().getAChild*() = fc or ifc.getElse().getAChild*() = fc)
)
)
}
predicate isExistsAssertWithArgumentCall() {
exists(FunctionCall fc |
fc.getTarget().hasName("__assert_fail") and
this.getEnclosingFunction() = fc.getEnclosingFunction() and
fc.getLocation().getStartLine() > this.getArgument(0).getLocation().getEndLine() and
fc.getArgument(0).toString().matches("%" + this.getArgument(0).toString() + "%")
)
}
}
from ReallocCallLeak rcl
where
not rcl.isExistsIfWithExitCall() and
not rcl.isExistsAssertWithArgumentCall()
select rcl, "possible loss of original pointer on unsuccessful call realloc"

View File

@@ -391,20 +391,30 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
/** Holds if this function has a `noexcept` exception specification. */
predicate isNoExcept() { getADeclarationEntry().isNoExcept() }
/** Gets a function that overloads this one. */
/**
* Gets a function that overloads this one.
*
* Note: if _overrides_ are wanted rather than _overloads_ then
* `MemberFunction::getAnOverridingFunction` should be used instead.
*/
Function getAnOverload() {
result.getName() = getName() and
result.getNamespace() = getNamespace() and
result != this and
// If this function is declared in a class, only consider other
// functions from the same class. Conversely, if this function is not
// declared in a class, only consider other functions not declared in a
// class.
(
if exists(getDeclaringType())
then result.getDeclaringType() = getDeclaringType()
else not exists(result.getDeclaringType())
// If this function is declared in a class, only consider other
// functions from the same class.
exists(string name, Class declaringType |
candGetAnOverloadMember(name, declaringType, this) and
candGetAnOverloadMember(name, declaringType, result)
)
or
// Conversely, if this function is not
// declared in a class, only consider other functions not declared in a
// class.
exists(string name, Namespace namespace |
candGetAnOverloadNonMember(name, namespace, this) and
candGetAnOverloadNonMember(name, namespace, result)
)
) and
result != this and
// Instantiations and specializations don't participate in overload
// resolution.
not (
@@ -462,6 +472,19 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
override AccessHolder getEnclosingAccessHolder() { result = this.getDeclaringType() }
}
pragma[noinline]
private predicate candGetAnOverloadMember(string name, Class declaringType, Function f) {
f.getName() = name and
f.getDeclaringType() = declaringType
}
pragma[noinline]
private predicate candGetAnOverloadNonMember(string name, Namespace namespace, Function f) {
f.getName() = name and
f.getNamespace() = namespace and
not exists(f.getDeclaringType())
}
/**
* A particular declaration or definition of a C/C++ function. For example the
* declaration and definition of `MyFunction` in the following code are each a

View File

@@ -900,6 +900,7 @@ class FormatLiteral extends Literal {
*/
int getNumArgNeeded(int n) {
exists(this.getConvSpecOffset(n)) and
exists(this.getConversionChar(n)) and
result = count(int mode | hasFormatArgumentIndexFor(n, mode))
}

View File

@@ -46,21 +46,23 @@ predicate predictableOnlyFlow(string name) {
private DataFlow::Node getNodeForSource(Expr source) {
isUserInput(source, _) and
(
result = DataFlow::exprNode(source)
or
// Some of the sources in `isUserInput` are intended to match the value of
// an expression, while others (those modeled below) are intended to match
// the taint that propagates out of an argument, like the `char *` argument
// to `gets`. It's impossible here to tell which is which, but the "access
// to argv" source is definitely not intended to match an output argument,
// and it causes false positives if we let it.
//
// This case goes together with the similar (but not identical) rule in
// `nodeIsBarrierIn`.
result = DataFlow::definitionByReferenceNodeFromArgument(source) and
not argv(source.(VariableAccess).getTarget())
)
result = getNodeForExpr(source)
}
private DataFlow::Node getNodeForExpr(Expr node) {
result = DataFlow::exprNode(node)
or
// Some of the sources in `isUserInput` are intended to match the value of
// an expression, while others (those modeled below) are intended to match
// the taint that propagates out of an argument, like the `char *` argument
// to `gets`. It's impossible here to tell which is which, but the "access
// to argv" source is definitely not intended to match an output argument,
// and it causes false positives if we let it.
//
// This case goes together with the similar (but not identical) rule in
// `nodeIsBarrierIn`.
result = DataFlow::definitionByReferenceNodeFromArgument(node) and
not argv(node.(VariableAccess).getTarget())
}
private class DefaultTaintTrackingCfg extends DataFlow::Configuration {
@@ -204,16 +206,27 @@ private predicate nodeIsBarrierIn(DataFlow::Node node) {
cached
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())
instructionToOperandTaintStep(fromNode.asInstruction(), toNode.asOperand())
}
private predicate operandToOperandTaintStep(Operand fromOperand, Operand toOperand) {
private predicate instructionToOperandTaintStep(Instruction fromInstr, Operand toOperand) {
// Propagate flow from the definition of an operand to the operand, even when the overlap is inexact.
// We only do this in certain cases:
// 1. The instruction's result must not be conflated, and
// 2. The instruction's result type is one the types where we expect element-to-object flow. Currently
// this is array types and union types. This matches the other two cases of element-to-object flow in
// `DefaultTaintTracking`.
toOperand.getAnyDef() = fromInstr and
not fromInstr.isResultConflated() and
(
fromInstr.getResultType() instanceof ArrayType or
fromInstr.getResultType() instanceof Union
)
or
exists(ReadSideEffectInstruction readInstr |
fromOperand = readInstr.getArgumentOperand() and
fromInstr = readInstr.getArgumentDef() and
toOperand = readInstr.getSideEffectOperand()
)
}
@@ -256,18 +269,18 @@ private predicate operandToInstructionTaintStep(Operand fromOperand, Instruction
outInstr.getPrimaryInstruction() = call
)
)
}
private predicate instructionToInstructionTaintStep(Instruction i1, Instruction i2) {
or
// Flow through pointer dereference
i2.(LoadInstruction).getSourceAddress() = i1
toInstr.(LoadInstruction).getSourceAddressOperand() = fromOperand
or
// Flow through partial reads of arrays and unions
i2.(LoadInstruction).getSourceValueOperand().getAnyDef() = i1 and
not i1.isResultConflated() and
(
i1.getResultType() instanceof ArrayType or
i1.getResultType() instanceof Union
toInstr.(LoadInstruction).getSourceValueOperand() = fromOperand and
exists(Instruction fromInstr | fromInstr = fromOperand.getAnyDef() |
not fromInstr.isResultConflated() and
(
fromInstr.getResultType() instanceof ArrayType or
fromInstr.getResultType() instanceof Union
)
)
or
// Unary instructions tend to preserve enough information in practice that we
@@ -277,63 +290,54 @@ private predicate instructionToInstructionTaintStep(Instruction i1, Instruction
// `FieldAddressInstruction` could cause flow into one field to come out an
// unrelated field. This would happen across function boundaries, where the IR
// would not be able to match loads to stores.
i2.(UnaryInstruction).getUnary() = i1 and
toInstr.(UnaryInstruction).getUnaryOperand() = fromOperand and
(
not i2 instanceof FieldAddressInstruction
not toInstr instanceof FieldAddressInstruction
or
i2.(FieldAddressInstruction).getField().getDeclaringType() instanceof Union
toInstr.(FieldAddressInstruction).getField().getDeclaringType() instanceof Union
)
or
// Flow out of definition-by-reference
i2.(ChiInstruction).getPartial() = i1.(WriteSideEffectInstruction) and
not i2.isResultConflated()
or
// Flow from an element to an array or union that contains it.
i2.(ChiInstruction).getPartial() = i1 and
not i2.isResultConflated() and
exists(Type t | i2.getResultLanguageType().hasType(t, false) |
toInstr.(ChiInstruction).getPartialOperand() = fromOperand and
not toInstr.isResultConflated() and
exists(Type t | toInstr.getResultLanguageType().hasType(t, false) |
t instanceof Union
or
t instanceof ArrayType
)
or
exists(BinaryInstruction bin |
bin = i2 and
predictableInstruction(i2.getAnOperand().getDef()) and
i1 = i2.getAnOperand().getDef()
bin = toInstr and
predictableInstruction(toInstr.getAnOperand().getDef()) and
fromOperand = toInstr.getAnOperand()
)
or
// This is part of the translation of `a[i]`, where we want taint to flow
// from `a`.
i2.(PointerAddInstruction).getLeft() = i1
or
// Until we have from through indirections across calls, we'll take flow out
// of the parameter and into its indirection.
exists(IRFunction f, Parameter parameter |
i1 = getInitializeParameter(f, parameter) and
i2 = getInitializeIndirection(f, parameter)
)
toInstr.(PointerAddInstruction).getLeftOperand() = fromOperand
or
// Until we have flow through indirections across calls, we'll take flow out
// of the indirection and into the argument.
// When we get proper flow through indirections across calls, this code can be
// moved to `adjusedSink` or possibly into the `DataFlow::ExprNode` class.
exists(ReadSideEffectInstruction read |
read.getAnOperand().(SideEffectOperand).getAnyDef() = i1 and
read.getArgumentDef() = i2
read.getSideEffectOperand() = fromOperand and
read.getArgumentDef() = toInstr
)
}
pragma[noinline]
private InitializeIndirectionInstruction getInitializeIndirection(IRFunction f, Parameter p) {
result.getParameter() = p and
result.getEnclosingIRFunction() = f
}
pragma[noinline]
private InitializeParameterInstruction getInitializeParameter(IRFunction f, Parameter p) {
result.getParameter() = p and
result.getEnclosingIRFunction() = f
or
// Until we have from through indirections across calls, we'll take flow out
// of the parameter and into its indirection.
// `InitializeIndirectionInstruction` only has a single operand: the address of the
// value whose indirection we are initializing. When initializing an indirection of a parameter `p`,
// the IR looks like this:
// ```
// m1 = InitializeParameter[p] : &r1
// r2 = Load[p] : r2, m1
// m3 = InitializeIndirection[p] : &r2
// ```
// So by having flow from `r2` to `m3` we're enabling flow from `m1` to `m3`. This relies on the
// `LoadOperand`'s overlap being exact.
toInstr.(InitializeIndirectionInstruction).getAnOperand() = fromOperand
}
/**
@@ -535,6 +539,9 @@ module TaintedWithPath {
* a characteristic predicate.
*/
class TaintTrackingConfiguration extends TSingleton {
/** Override this to specify which elements are sources in this configuration. */
predicate isSource(Expr source) { exists(getNodeForSource(source)) }
/** Override this to specify which elements are sinks in this configuration. */
abstract predicate isSink(Element e);
@@ -551,7 +558,11 @@ module TaintedWithPath {
private class AdjustedConfiguration extends DataFlow3::Configuration {
AdjustedConfiguration() { this = "AdjustedConfiguration" }
override predicate isSource(DataFlow::Node source) { source = getNodeForSource(_) }
override predicate isSource(DataFlow::Node source) {
exists(TaintTrackingConfiguration cfg, Expr e |
cfg.isSource(e) and source = getNodeForExpr(e)
)
}
override predicate isSink(DataFlow::Node sink) {
exists(TaintTrackingConfiguration cfg | cfg.isSink(adjustedSink(sink)))
@@ -594,7 +605,8 @@ module TaintedWithPath {
exists(AdjustedConfiguration cfg, DataFlow3::Node sourceNode, DataFlow3::Node sinkNode |
cfg.hasFlow(sourceNode, sinkNode)
|
sourceNode = getNodeForSource(e)
sourceNode = getNodeForExpr(e) and
exists(TaintTrackingConfiguration ttCfg | ttCfg.isSource(e))
or
e = adjustedSink(sinkNode) and
exists(TaintTrackingConfiguration ttCfg | ttCfg.isSink(e))
@@ -648,7 +660,7 @@ module TaintedWithPath {
/** A PathNode whose `Element` is a source. It may also be a sink. */
private class InitialPathNode extends EndpointPathNode {
InitialPathNode() { exists(getNodeForSource(this.inner())) }
InitialPathNode() { exists(TaintTrackingConfiguration cfg | cfg.isSource(this.inner())) }
}
/** A PathNode whose `Element` is a sink. It may also be a source. */
@@ -670,14 +682,14 @@ module TaintedWithPath {
// Same for the first node
exists(WrapPathNode sourceNode |
DataFlow3::PathGraph::edges(sourceNode.inner(), b.(WrapPathNode).inner()) and
sourceNode.inner().getNode() = getNodeForSource(a.(InitialPathNode).inner())
sourceNode.inner().getNode() = getNodeForExpr(a.(InitialPathNode).inner())
)
or
// Finally, handle the case where the path goes directly from a source to a
// sink, meaning that they both need to be translated.
exists(WrapPathNode sinkNode, WrapPathNode sourceNode |
DataFlow3::PathGraph::edges(sourceNode.inner(), sinkNode.inner()) and
sourceNode.inner().getNode() = getNodeForSource(a.(InitialPathNode).inner()) and
sourceNode.inner().getNode() = getNodeForExpr(a.(InitialPathNode).inner()) and
b.(FinalPathNode).inner() = adjustedSink(sinkNode.inner().getNode())
)
}
@@ -700,7 +712,7 @@ module TaintedWithPath {
predicate taintedWithPath(Expr source, Element tainted, PathNode sourceNode, PathNode sinkNode) {
exists(AdjustedConfiguration cfg, DataFlow3::Node flowSource, DataFlow3::Node flowSink |
source = sourceNode.(InitialPathNode).inner() and
flowSource = getNodeForSource(source) and
flowSource = getNodeForExpr(source) and
cfg.hasFlow(flowSource, flowSink) and
tainted = adjustedSink(flowSink) and
tainted = sinkNode.(FinalPathNode).inner()
@@ -722,8 +734,8 @@ module TaintedWithPath {
* through a global variable.
*/
predicate taintedWithoutGlobals(Element tainted) {
exists(PathNode sourceNode, FinalPathNode sinkNode |
sourceNode.(WrapPathNode).inner().getNode() = getNodeForSource(_) and
exists(AdjustedConfiguration cfg, PathNode sourceNode, FinalPathNode sinkNode |
cfg.isSource(sourceNode.(WrapPathNode).inner().getNode()) and
edgesWithoutGlobals+(sourceNode, sinkNode) and
tainted = sinkNode.inner()
)

View File

@@ -82,7 +82,9 @@ private class AllocaAllocationFunction extends AllocationFunction {
hasGlobalName([
// --- stack allocation
"alloca", // // alloca(size)
"__builtin_alloca" // __builtin_alloca(size)
"__builtin_alloca", // __builtin_alloca(size)
"_alloca", // _alloca(size)
"_malloca" // _malloca(size)
]) and
sizeArg = 0
}

View File

@@ -14,6 +14,7 @@ import semmle.code.cpp.models.interfaces.Taint
private class StrdupFunction extends AllocationFunction, ArrayFunction, DataFlowFunction {
StrdupFunction() {
hasGlobalName([
// --- C library allocation
"strdup", // strdup(str)
"wcsdup", // wcsdup(str)
"_strdup", // _strdup(str)
@@ -39,8 +40,8 @@ private class StrndupFunction extends AllocationFunction, ArrayFunction, DataFlo
StrndupFunction() {
exists(string name |
hasGlobalName(name) and
// strndup(str, maxlen)
name = "strndup"
// --- C library allocation
name = "strndup" // strndup(str, maxlen)
)
}

View File

@@ -0,0 +1,17 @@
edges
| test.cpp:22:17:22:21 | ... * ... | test.cpp:23:33:23:37 | size1 |
nodes
| test.cpp:13:33:13:37 | ... * ... | semmle.label | ... * ... |
| test.cpp:15:31:15:35 | ... * ... | semmle.label | ... * ... |
| test.cpp:19:34:19:38 | ... * ... | semmle.label | ... * ... |
| test.cpp:22:17:22:21 | ... * ... | semmle.label | ... * ... |
| test.cpp:23:33:23:37 | size1 | semmle.label | size1 |
| test.cpp:30:27:30:31 | ... * ... | semmle.label | ... * ... |
| test.cpp:31:27:31:31 | ... * ... | semmle.label | ... * ... |
#select
| test.cpp:13:33:13:37 | ... * ... | test.cpp:13:33:13:37 | ... * ... | test.cpp:13:33:13:37 | ... * ... | Potentially overflowing value from $@ is used in the size of this allocation. | test.cpp:13:33:13:37 | ... * ... | multiplication |
| test.cpp:15:31:15:35 | ... * ... | test.cpp:15:31:15:35 | ... * ... | test.cpp:15:31:15:35 | ... * ... | Potentially overflowing value from $@ is used in the size of this allocation. | test.cpp:15:31:15:35 | ... * ... | multiplication |
| test.cpp:19:34:19:38 | ... * ... | test.cpp:19:34:19:38 | ... * ... | test.cpp:19:34:19:38 | ... * ... | Potentially overflowing value from $@ is used in the size of this allocation. | test.cpp:19:34:19:38 | ... * ... | multiplication |
| test.cpp:23:33:23:37 | size1 | test.cpp:22:17:22:21 | ... * ... | test.cpp:23:33:23:37 | size1 | Potentially overflowing value from $@ is used in the size of this allocation. | test.cpp:22:17:22:21 | ... * ... | multiplication |
| test.cpp:30:27:30:31 | ... * ... | test.cpp:30:27:30:31 | ... * ... | test.cpp:30:27:30:31 | ... * ... | Potentially overflowing value from $@ is used in the size of this allocation. | test.cpp:30:27:30:31 | ... * ... | multiplication |
| test.cpp:31:27:31:31 | ... * ... | test.cpp:31:27:31:31 | ... * ... | test.cpp:31:27:31:31 | ... * ... | Potentially overflowing value from $@ is used in the size of this allocation. | test.cpp:31:27:31:31 | ... * ... | multiplication |

View File

@@ -0,0 +1 @@
experimental/Security/CWE/CWE-190/AllocMultiplicationOverflow.ql

View File

@@ -0,0 +1,32 @@
typedef unsigned long size_t;
void *malloc(size_t size);
int getAnInt();
void test()
{
int x = getAnInt();
int y = getAnInt();
char *buffer1 = (char *)malloc(x + y); // GOOD
char *buffer2 = (char *)malloc(x * y); // BAD
int *buffer3 = (int *)malloc(x * sizeof(int)); // GOOD
int *buffer4 = (int *)malloc(x * y * sizeof(int)); // BAD
if ((x <= 1000) && (y <= 1000))
{
char *buffer5 = (char *)malloc(x * y); // GOOD [FALSE POSITIVE]
}
size_t size1 = x * y;
char *buffer5 = (char *)malloc(size1); // BAD
size_t size2 = x;
size2 *= y;
char *buffer6 = (char *)malloc(size2); // BAD [NOT DETECTED]
char *buffer7 = new char[x * 10]; // GOOD
char *buffer8 = new char[x * y]; // BAD
char *buffer9 = new char[x * x]; // BAD
}

View File

@@ -0,0 +1,4 @@
| test.c:34:29:34:35 | call to realloc | possible loss of original pointer on unsuccessful call realloc |
| test.c:63:29:63:35 | call to realloc | possible loss of original pointer on unsuccessful call realloc |
| test.c:139:29:139:35 | call to realloc | possible loss of original pointer on unsuccessful call realloc |
| test.c:186:29:186:35 | call to realloc | possible loss of original pointer on unsuccessful call realloc |

View File

@@ -0,0 +1 @@
experimental/Security/CWE/CWE-401/MemoryLeakOnFailedCallToRealloc.ql

View File

@@ -0,0 +1,274 @@
#define size_t int
#define NULL ((void*)0)
#define assert(x) if (!(x)) __assert_fail(#x,__FILE__,__LINE__)
void __assert_fail(const char *assertion, const char *file, int line) { }
void aFakeFailed_1(int file, int line)
{
}
void aFailed_1(int file, int line)
{
exit(0);
}
void aFailed_2(int file, int line, int ex)
{
if(ex == 1)
exit(0);
else
return;
}
#define F_NUM 1
#define myASSERT_1(expr) \
if (!(expr)) \
aFailed_1(F_NUM, __LINE__)
#define myASSERT_2(expr) \
if (!(expr)) \
aFailed_2(F_NUM, __LINE__, 1)
unsigned char * badResize_0(unsigned char * buffer,size_t currentSize,size_t newSize)
{
// BAD: on unsuccessful call to realloc, we will lose a pointer to a valid memory block
if (currentSize < newSize)
{
buffer = (unsigned char *)realloc(buffer, newSize);
}
return buffer;
}
unsigned char * goodResize_0(unsigned char * buffer,size_t currentSize,size_t newSize)
{
// GOOD: this way we will exclude possible memory leak
unsigned char * tmp;
if (currentSize < newSize)
{
tmp = (unsigned char *)realloc(buffer, newSize);
}
if (tmp == NULL)
{
free(buffer);
return NULL;
}
else
buffer = tmp;
return buffer;
}
unsigned char * badResize_1_0(unsigned char * buffer,size_t currentSize,size_t newSize)
{
if(!buffer)
exit(0);
// BAD: on unsuccessful call to realloc, we will lose a pointer to a valid memory block
if (currentSize < newSize)
{
buffer = (unsigned char *)realloc(buffer, newSize);
}
return buffer;
}
unsigned char * noBadResize_1_0(unsigned char * buffer,size_t currentSize,size_t newSize)
{
// GOOD: program to end
if (currentSize < newSize)
{
buffer = (unsigned char *)realloc(buffer, newSize);
}
if(!buffer)
exit(0);
return buffer;
}
unsigned char * noBadResize_1_1(unsigned char * buffer,size_t currentSize,size_t newSize)
{
// GOOD: program to end
if (currentSize < newSize)
{
buffer = (unsigned char *)realloc(buffer, newSize);
}
if(buffer)
return buffer;
else
exit(0);
}
unsigned char * noBadResize_1_2(unsigned char * buffer,size_t currentSize,size_t newSize)
{
// GOOD: program to end
if (currentSize < newSize)
{
if(buffer = (unsigned char *)realloc(buffer, newSize))
exit(0);
}
return buffer;
}
unsigned char * noBadResize_1_3(unsigned char * buffer,size_t currentSize,size_t newSize)
{
// GOOD: program to end
if (currentSize < newSize)
{
buffer = (unsigned char *)realloc(buffer, newSize);
}
if(!buffer)
aFailed_1(1, 1);
return buffer;
}
unsigned char * noBadResize_1_4(unsigned char * buffer,size_t currentSize,size_t newSize)
{
// GOOD: program to end
if (currentSize < newSize)
{
buffer = (unsigned char *)realloc(buffer, newSize);
}
if(buffer)
return buffer;
else
aFailed_1(1, 1);
}
unsigned char * noBadResize_1_5(unsigned char * buffer,size_t currentSize,size_t newSize)
{
// GOOD: program to end
if (currentSize < newSize)
{
if(buffer = (unsigned char *)realloc(buffer, newSize))
aFailed_1(1, 1);
}
return buffer;
}
unsigned char * badResize_1_1(unsigned char * buffer,size_t currentSize,size_t newSize)
{
// BAD: on unsuccessful call to realloc, we will lose a pointer to a valid memory block
if (currentSize < newSize)
{
buffer = (unsigned char *)realloc(buffer, newSize);
}
if(!buffer)
aFakeFailed_1(1, 1);
return buffer;
}
unsigned char * noBadResize_1_6(unsigned char * buffer,size_t currentSize,size_t newSize)
{
// GOOD: program to end
if (currentSize < newSize)
{
buffer = (unsigned char *)realloc(buffer, newSize);
}
if(!buffer)
aFailed_2(1, 1, 1);
return buffer;
}
unsigned char * noBadResize_1_7(unsigned char * buffer,size_t currentSize,size_t newSize)
{
// GOOD: program to end
if (currentSize < newSize)
{
buffer = (unsigned char *)realloc(buffer, newSize);
}
if(buffer)
return buffer;
else
aFailed_2(1, 1, 1);
}
unsigned char * noBadResize_1_8(unsigned char * buffer,size_t currentSize,size_t newSize)
{
// GOOD: program to end
if (currentSize < newSize)
{
if(buffer = (unsigned char *)realloc(buffer, newSize))
aFailed_2(1, 1, 1);
}
return buffer;
}
unsigned char * badResize_2_0(unsigned char * buffer,size_t currentSize,size_t newSize)
{
// BAD: on unsuccessful call to realloc, we will lose a pointer to a valid memory block
assert(buffer!=0);
if (currentSize < newSize)
{
buffer = (unsigned char *)realloc(buffer, newSize);
}
return buffer;
}
unsigned char * noBadResize_2_0(unsigned char * buffer,size_t currentSize,size_t newSize)
{
// GOOD: program to end
if (currentSize < newSize)
{
buffer = (unsigned char *)realloc(buffer, newSize);
assert(buffer!=0);
}
return buffer;
}
unsigned char * noBadResize2e_2_1(unsigned char * buffer,size_t currentSize,size_t newSize)
{
// GOOD: program to end
if (currentSize < newSize)
{
buffer = (unsigned char *)realloc(buffer, newSize);
}
assert(buffer!=0);
return buffer;
}
unsigned char * noBadResize_2_2(unsigned char * buffer,size_t currentSize,size_t newSize)
{
// GOOD: program to end
if (currentSize < newSize)
{
buffer = (unsigned char *)realloc(buffer, newSize);
assert(buffer);
}
return buffer;
}
unsigned char * noBadResize_2_3(unsigned char * buffer,size_t currentSize,size_t newSize)
{
// GOOD: program to end
if (currentSize < newSize)
{
buffer = (unsigned char *)realloc(buffer, newSize);
}
assert(buffer);
return buffer;
}
unsigned char * noBadResize_2_4(unsigned char * buffer,size_t currentSize,size_t newSize)
{
// GOOD: program to end
if (currentSize < newSize)
{
buffer = (unsigned char *)realloc(buffer, newSize);
myASSERT_1(buffer);
}
return buffer;
}
unsigned char * noBadResize_2_5(unsigned char * buffer,size_t currentSize,size_t newSize)
{
// GOOD: program to end
if (currentSize < newSize)
{
buffer = (unsigned char *)realloc(buffer, newSize);
}
myASSERT_1(buffer);
return buffer;
}
unsigned char * noBadResize_2_6(unsigned char * buffer,size_t currentSize,size_t newSize)
{
// GOOD: program to end
if (currentSize < newSize)
{
buffer = (unsigned char *)realloc(buffer, newSize);
myASSERT_2(buffer);
}
return buffer;
}
unsigned char * noBadResize_2_7(unsigned char * buffer,size_t currentSize,size_t newSize)
{
// GOOD: program to end
if (currentSize < newSize)
{
buffer = (unsigned char *)realloc(buffer, newSize);
}
myASSERT_2(buffer);
return buffer;
}

View File

@@ -0,0 +1,62 @@
int user_input();
void sink(int);
struct A {
int* p;
int x;
};
void pointer_without_allocation(const A& ra) {
*ra.p = user_input();
sink(*ra.p); // $ MISSING: ast,ir
}
void argument_source(void*);
void sink(void*);
void pointer_without_allocation_2() {
char *raw;
argument_source(raw);
sink(raw); // $ ast MISSING: ir
}
A* makeA() {
return new A;
}
void no_InitializeDynamicAllocation_instruction() {
A* pa = makeA();
pa->x = user_input();
sink(pa->x); // $ ast MISSING: ir
}
void fresh_or_arg(A* arg, bool unknown) {
A* pa;
pa = unknown ? arg : new A;
pa->x = user_input();
sink(pa->x); // $ ast MISSING: ir
}
struct LinkedList {
LinkedList* next;
int y;
LinkedList() = default;
LinkedList(LinkedList* next) : next(next) {}
};
// Note: This example also suffers from #113: there is no ChiInstruction that merges the result of the
// InitializeDynamicAllocation instruction into {AllAliasedMemory}. But even when that's fixed there's
// still no dataflow because `ll->next->y = user_input()` writes to {AllAliasedMemory}.
void too_many_indirections() {
LinkedList* ll = new LinkedList;
ll->next = new LinkedList;
ll->next->y = user_input();
sink(ll->next->y); // $ ast MISSING: ir
}
void too_many_indirections_2(LinkedList* next) {
LinkedList* ll = new LinkedList(next);
ll->next->y = user_input();
sink(ll->next->y); // $ ast MISSING: ir
}

View File

@@ -121,6 +121,13 @@ postWithInFlow
| by_reference.cpp:127:30:127:38 | inner_ptr [inner post update] | PostUpdateNode should not be the target of local flow. |
| complex.cpp:11:22:11:23 | a_ [post update] | PostUpdateNode should not be the target of local flow. |
| complex.cpp:12:22:12:23 | b_ [post update] | PostUpdateNode should not be the target of local flow. |
| conflated.cpp:10:3:10:7 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
| conflated.cpp:10:7:10:7 | p [inner post update] | PostUpdateNode should not be the target of local flow. |
| conflated.cpp:29:7:29:7 | x [post update] | PostUpdateNode should not be the target of local flow. |
| conflated.cpp:36:7:36:7 | x [post update] | PostUpdateNode should not be the target of local flow. |
| conflated.cpp:53:7:53:10 | next [post update] | PostUpdateNode should not be the target of local flow. |
| conflated.cpp:54:13:54:13 | y [post update] | PostUpdateNode should not be the target of local flow. |
| conflated.cpp:60:13:60:13 | y [post update] | PostUpdateNode should not be the target of local flow. |
| constructors.cpp:20:24:20:25 | a_ [post update] | PostUpdateNode should not be the target of local flow. |
| constructors.cpp:21:24:21:25 | b_ [post update] | PostUpdateNode should not be the target of local flow. |
| qualifiers.cpp:9:36:9:36 | a [post update] | PostUpdateNode should not be the target of local flow. |

View File

@@ -1,6 +1,10 @@
uniqueEnclosingCallable
uniqueType
uniqueNodeLocation
| E.cpp:15:31:15:33 | buf | Node should have one location but has 2. |
| aliasing.cpp:2:11:2:13 | (unnamed parameter 0) | Node should have one location but has 2. |
| conflated.cpp:2:11:2:13 | (unnamed parameter 0) | Node should have one location but has 2. |
| conflated.cpp:14:22:14:25 | buf | Node should have one location but has 2. |
| file://:0:0:0:0 | (unnamed parameter 0) | Node should have one location but has 0. |
| file://:0:0:0:0 | (unnamed parameter 0) | Node should have one location but has 0. |
| file://:0:0:0:0 | (unnamed parameter 0) | Node should have one location but has 0. |
@@ -129,6 +133,8 @@ postWithInFlow
| complex.cpp:54:12:54:12 | Chi | PostUpdateNode should not be the target of local flow. |
| complex.cpp:55:12:55:12 | Chi | PostUpdateNode should not be the target of local flow. |
| complex.cpp:56:12:56:12 | Chi | PostUpdateNode should not be the target of local flow. |
| conflated.cpp:45:39:45:42 | Chi | PostUpdateNode should not be the target of local flow. |
| conflated.cpp:53:3:53:27 | Chi | PostUpdateNode should not be the target of local flow. |
| constructors.cpp:20:24:20:29 | Chi | PostUpdateNode should not be the target of local flow. |
| constructors.cpp:21:24:21:29 | Chi | PostUpdateNode should not be the target of local flow. |
| constructors.cpp:23:28:23:28 | Chi | PostUpdateNode should not be the target of local flow. |

View File

@@ -309,6 +309,22 @@
| complex.cpp:62:7:62:8 | b2 | AST only |
| complex.cpp:65:7:65:8 | b3 | AST only |
| complex.cpp:68:7:68:8 | b4 | AST only |
| conflated.cpp:10:3:10:7 | * ... | AST only |
| conflated.cpp:10:4:10:5 | ra | AST only |
| conflated.cpp:19:19:19:21 | raw | AST only |
| conflated.cpp:20:8:20:10 | raw | AST only |
| conflated.cpp:29:3:29:4 | pa | AST only |
| conflated.cpp:29:7:29:7 | x | AST only |
| conflated.cpp:36:3:36:4 | pa | AST only |
| conflated.cpp:36:7:36:7 | x | AST only |
| conflated.cpp:53:7:53:10 | next | AST only |
| conflated.cpp:54:3:54:4 | ll | AST only |
| conflated.cpp:54:7:54:10 | next | AST only |
| conflated.cpp:54:13:54:13 | y | AST only |
| conflated.cpp:59:35:59:38 | next | AST only |
| conflated.cpp:60:3:60:4 | ll | AST only |
| conflated.cpp:60:7:60:10 | next | AST only |
| conflated.cpp:60:13:60:13 | y | AST only |
| constructors.cpp:20:24:20:25 | a_ | AST only |
| constructors.cpp:21:24:21:25 | b_ | AST only |
| constructors.cpp:28:10:28:10 | f | AST only |

View File

@@ -57,6 +57,7 @@
| complex.cpp:54:6:54:10 | inner |
| complex.cpp:55:6:55:10 | inner |
| complex.cpp:56:6:56:10 | inner |
| conflated.cpp:53:3:53:4 | ll |
| constructors.cpp:20:24:20:25 | this |
| constructors.cpp:21:24:21:25 | this |
| qualifiers.cpp:9:30:9:33 | this |

View File

@@ -366,6 +366,23 @@
| complex.cpp:62:7:62:8 | b2 |
| complex.cpp:65:7:65:8 | b3 |
| complex.cpp:68:7:68:8 | b4 |
| conflated.cpp:10:3:10:7 | * ... |
| conflated.cpp:10:4:10:5 | ra |
| conflated.cpp:19:19:19:21 | raw |
| conflated.cpp:20:8:20:10 | raw |
| conflated.cpp:29:3:29:4 | pa |
| conflated.cpp:29:7:29:7 | x |
| conflated.cpp:36:3:36:4 | pa |
| conflated.cpp:36:7:36:7 | x |
| conflated.cpp:53:3:53:4 | ll |
| conflated.cpp:53:7:53:10 | next |
| conflated.cpp:54:3:54:4 | ll |
| conflated.cpp:54:7:54:10 | next |
| conflated.cpp:54:13:54:13 | y |
| conflated.cpp:59:35:59:38 | next |
| conflated.cpp:60:3:60:4 | ll |
| conflated.cpp:60:7:60:10 | next |
| conflated.cpp:60:13:60:13 | y |
| constructors.cpp:20:24:20:25 | a_ |
| constructors.cpp:20:24:20:25 | this |
| constructors.cpp:21:24:21:25 | b_ |

View File

@@ -336,6 +336,27 @@ edges
| complex.cpp:62:7:62:8 | b2 [inner, f, b_] | complex.cpp:40:17:40:17 | b [inner, f, b_] |
| complex.cpp:65:7:65:8 | b3 [inner, f, a_] | complex.cpp:40:17:40:17 | b [inner, f, a_] |
| complex.cpp:65:7:65:8 | b3 [inner, f, b_] | complex.cpp:40:17:40:17 | b [inner, f, b_] |
| conflated.cpp:19:19:19:21 | ref arg raw | conflated.cpp:20:8:20:10 | raw |
| conflated.cpp:29:3:29:4 | pa [post update] [x] | conflated.cpp:30:8:30:9 | pa [x] |
| conflated.cpp:29:3:29:22 | ... = ... | conflated.cpp:29:3:29:4 | pa [post update] [x] |
| conflated.cpp:29:11:29:20 | call to user_input | conflated.cpp:29:3:29:22 | ... = ... |
| conflated.cpp:30:8:30:9 | pa [x] | conflated.cpp:30:12:30:12 | x |
| conflated.cpp:36:3:36:4 | pa [post update] [x] | conflated.cpp:37:8:37:9 | pa [x] |
| conflated.cpp:36:3:36:22 | ... = ... | conflated.cpp:36:3:36:4 | pa [post update] [x] |
| conflated.cpp:36:11:36:20 | call to user_input | conflated.cpp:36:3:36:22 | ... = ... |
| conflated.cpp:37:8:37:9 | pa [x] | conflated.cpp:37:12:37:12 | x |
| conflated.cpp:54:3:54:4 | ll [post update] [next, y] | conflated.cpp:55:8:55:9 | ll [next, y] |
| conflated.cpp:54:3:54:28 | ... = ... | conflated.cpp:54:7:54:10 | next [post update] [y] |
| conflated.cpp:54:7:54:10 | next [post update] [y] | conflated.cpp:54:3:54:4 | ll [post update] [next, y] |
| conflated.cpp:54:17:54:26 | call to user_input | conflated.cpp:54:3:54:28 | ... = ... |
| conflated.cpp:55:8:55:9 | ll [next, y] | conflated.cpp:55:12:55:15 | next [y] |
| conflated.cpp:55:12:55:15 | next [y] | conflated.cpp:55:18:55:18 | y |
| conflated.cpp:60:3:60:4 | ll [post update] [next, y] | conflated.cpp:61:8:61:9 | ll [next, y] |
| conflated.cpp:60:3:60:28 | ... = ... | conflated.cpp:60:7:60:10 | next [post update] [y] |
| conflated.cpp:60:7:60:10 | next [post update] [y] | conflated.cpp:60:3:60:4 | ll [post update] [next, y] |
| conflated.cpp:60:17:60:26 | call to user_input | conflated.cpp:60:3:60:28 | ... = ... |
| conflated.cpp:61:8:61:9 | ll [next, y] | conflated.cpp:61:12:61:15 | next [y] |
| conflated.cpp:61:12:61:15 | next [y] | conflated.cpp:61:18:61:18 | y |
| constructors.cpp:26:15:26:15 | f [a_] | constructors.cpp:28:10:28:10 | f [a_] |
| constructors.cpp:26:15:26:15 | f [b_] | constructors.cpp:29:10:29:10 | f [b_] |
| constructors.cpp:28:10:28:10 | f [a_] | constructors.cpp:28:12:28:12 | call to a |
@@ -827,6 +848,32 @@ nodes
| complex.cpp:62:7:62:8 | b2 [inner, f, b_] | semmle.label | b2 [inner, f, b_] |
| complex.cpp:65:7:65:8 | b3 [inner, f, a_] | semmle.label | b3 [inner, f, a_] |
| complex.cpp:65:7:65:8 | b3 [inner, f, b_] | semmle.label | b3 [inner, f, b_] |
| conflated.cpp:19:19:19:21 | ref arg raw | semmle.label | ref arg raw |
| conflated.cpp:20:8:20:10 | raw | semmle.label | raw |
| conflated.cpp:29:3:29:4 | pa [post update] [x] | semmle.label | pa [post update] [x] |
| conflated.cpp:29:3:29:22 | ... = ... | semmle.label | ... = ... |
| conflated.cpp:29:11:29:20 | call to user_input | semmle.label | call to user_input |
| conflated.cpp:30:8:30:9 | pa [x] | semmle.label | pa [x] |
| conflated.cpp:30:12:30:12 | x | semmle.label | x |
| conflated.cpp:36:3:36:4 | pa [post update] [x] | semmle.label | pa [post update] [x] |
| conflated.cpp:36:3:36:22 | ... = ... | semmle.label | ... = ... |
| conflated.cpp:36:11:36:20 | call to user_input | semmle.label | call to user_input |
| conflated.cpp:37:8:37:9 | pa [x] | semmle.label | pa [x] |
| conflated.cpp:37:12:37:12 | x | semmle.label | x |
| conflated.cpp:54:3:54:4 | ll [post update] [next, y] | semmle.label | ll [post update] [next, y] |
| conflated.cpp:54:3:54:28 | ... = ... | semmle.label | ... = ... |
| conflated.cpp:54:7:54:10 | next [post update] [y] | semmle.label | next [post update] [y] |
| conflated.cpp:54:17:54:26 | call to user_input | semmle.label | call to user_input |
| conflated.cpp:55:8:55:9 | ll [next, y] | semmle.label | ll [next, y] |
| conflated.cpp:55:12:55:15 | next [y] | semmle.label | next [y] |
| conflated.cpp:55:18:55:18 | y | semmle.label | y |
| conflated.cpp:60:3:60:4 | ll [post update] [next, y] | semmle.label | ll [post update] [next, y] |
| conflated.cpp:60:3:60:28 | ... = ... | semmle.label | ... = ... |
| conflated.cpp:60:7:60:10 | next [post update] [y] | semmle.label | next [post update] [y] |
| conflated.cpp:60:17:60:26 | call to user_input | semmle.label | call to user_input |
| conflated.cpp:61:8:61:9 | ll [next, y] | semmle.label | ll [next, y] |
| conflated.cpp:61:12:61:15 | next [y] | semmle.label | next [y] |
| conflated.cpp:61:18:61:18 | y | semmle.label | y |
| constructors.cpp:26:15:26:15 | f [a_] | semmle.label | f [a_] |
| constructors.cpp:26:15:26:15 | f [b_] | semmle.label | f [b_] |
| constructors.cpp:28:10:28:10 | f [a_] | semmle.label | f [a_] |
@@ -1028,6 +1075,11 @@ nodes
| complex.cpp:42:18:42:18 | call to a | complex.cpp:55:19:55:28 | call to user_input | complex.cpp:42:18:42:18 | call to a | call to a flows from $@ | complex.cpp:55:19:55:28 | call to user_input | call to user_input |
| complex.cpp:43:18:43:18 | call to b | complex.cpp:54:19:54:28 | call to user_input | complex.cpp:43:18:43:18 | call to b | call to b flows from $@ | complex.cpp:54:19:54:28 | call to user_input | call to user_input |
| complex.cpp:43:18:43:18 | call to b | complex.cpp:56:19:56:28 | call to user_input | complex.cpp:43:18:43:18 | call to b | call to b flows from $@ | complex.cpp:56:19:56:28 | call to user_input | call to user_input |
| conflated.cpp:20:8:20:10 | raw | conflated.cpp:19:19:19:21 | ref arg raw | conflated.cpp:20:8:20:10 | raw | raw flows from $@ | conflated.cpp:19:19:19:21 | ref arg raw | ref arg raw |
| conflated.cpp:30:12:30:12 | x | conflated.cpp:29:11:29:20 | call to user_input | conflated.cpp:30:12:30:12 | x | x flows from $@ | conflated.cpp:29:11:29:20 | call to user_input | call to user_input |
| conflated.cpp:37:12:37:12 | x | conflated.cpp:36:11:36:20 | call to user_input | conflated.cpp:37:12:37:12 | x | x flows from $@ | conflated.cpp:36:11:36:20 | call to user_input | call to user_input |
| conflated.cpp:55:18:55:18 | y | conflated.cpp:54:17:54:26 | call to user_input | conflated.cpp:55:18:55:18 | y | y flows from $@ | conflated.cpp:54:17:54:26 | call to user_input | call to user_input |
| conflated.cpp:61:18:61:18 | y | conflated.cpp:60:17:60:26 | call to user_input | conflated.cpp:61:18:61:18 | y | y flows from $@ | conflated.cpp:60:17:60:26 | call to user_input | call to user_input |
| constructors.cpp:28:12:28:12 | call to a | constructors.cpp:34:11:34:20 | call to user_input | constructors.cpp:28:12:28:12 | call to a | call to a flows from $@ | constructors.cpp:34:11:34:20 | call to user_input | call to user_input |
| constructors.cpp:28:12:28:12 | call to a | constructors.cpp:36:11:36:20 | call to user_input | constructors.cpp:28:12:28:12 | call to a | call to a flows from $@ | constructors.cpp:36:11:36:20 | call to user_input | call to user_input |
| constructors.cpp:29:12:29:12 | call to b | constructors.cpp:35:14:35:23 | call to user_input | constructors.cpp:29:12:29:12 | call to b | call to b flows from $@ | constructors.cpp:35:14:35:23 | call to user_input | call to user_input |

View File

@@ -0,0 +1,46 @@
| test.cpp:5:3:5:3 | GVN | 5:c3-c3 6:c3-c3 |
| test.cpp:5:7:5:8 | GVN | 5:c7-c8 6:c7-c8 |
| test.cpp:5:7:5:13 | GVN | 5:c7-c13 6:c7-c13 7:c7-c7 |
| test.cpp:5:12:5:13 | GVN | 5:c12-c13 6:c12-c13 |
| test.cpp:16:3:16:3 | GVN | 16:c3-c3 17:c3-c3 |
| test.cpp:16:7:16:8 | GVN | 16:c7-c8 17:c7-c8 |
| test.cpp:16:7:16:13 | GVN | 16:c7-c13 17:c7-c13 |
| test.cpp:16:7:16:24 | GVN | 16:c7-c24 17:c7-c24 18:c7-c7 |
| test.cpp:16:12:16:13 | GVN | 16:c12-c13 17:c12-c13 |
| test.cpp:16:17:16:24 | GVN | 16:c17-c24 17:c17-c24 |
| test.cpp:29:3:29:3 | GVN | 29:c3-c3 31:c3-c3 |
| test.cpp:29:7:29:8 | GVN | 29:c7-c8 31:c7-c8 |
| test.cpp:29:7:29:13 | GVN | 29:c7-c13 31:c7-c13 |
| test.cpp:29:12:29:13 | GVN | 29:c12-c13 31:c12-c13 |
| test.cpp:31:7:31:24 | GVN | 31:c7-c24 32:c7-c7 |
| test.cpp:43:3:43:3 | GVN | 43:c3-c3 45:c3-c3 |
| test.cpp:43:7:43:8 | GVN | 43:c7-c8 45:c7-c8 |
| test.cpp:43:7:43:13 | GVN | 43:c7-c13 45:c7-c13 |
| test.cpp:43:7:43:24 | GVN | 43:c7-c24 45:c7-c24 46:c7-c7 |
| test.cpp:43:12:43:13 | GVN | 43:c12-c13 45:c12-c13 |
| test.cpp:43:17:43:24 | GVN | 43:c17-c24 45:c17-c24 |
| test.cpp:44:3:44:5 | GVN | 44:c3-c5 44:c4-c5 |
| test.cpp:53:10:53:13 | GVN | 53:c10-c13 56:c21-c24 |
| test.cpp:53:10:53:13 | GVN | 53:c10-c13 56:c21-c24 |
| test.cpp:53:11:53:13 | GVN | 53:c11-c13 56:c22-c24 |
| test.cpp:53:18:53:21 | GVN | 53:c18-c21 56:c39-c42 59:c17-c20 |
| test.cpp:56:13:56:16 | GVN | 56:c13-c16 56:c31-c34 59:c9-c12 |
| test.cpp:56:13:56:16 | GVN | 56:c13-c16 56:c31-c34 59:c9-c12 |
| test.cpp:56:14:56:16 | GVN | 56:c14-c16 56:c32-c34 56:c47-c49 59:c10-c12 |
| test.cpp:62:5:62:10 | GVN | 62:c5-c10 65:c10-c15 |
| test.cpp:77:20:77:28 | GVN | 77:c20-c28 79:c7-c7 |
| test.cpp:79:11:79:14 | GVN | 79:c11-c14 79:c24-c27 |
| test.cpp:92:11:92:16 | GVN | 92:c11-c16 92:c15-c16 93:c10-c10 |
| test.cpp:105:11:105:12 | GVN | 105:c11-c12 106:c33-c34 |
| test.cpp:105:11:105:12 | GVN | 105:c11-c12 106:c33-c34 107:c11-c12 |
| test.cpp:105:15:105:15 | GVN | 105:c15-c15 107:c15-c15 109:c10-c10 |
| test.cpp:113:3:113:5 | GVN | 113:c3-c5 115:c3-c5 |
| test.cpp:125:11:125:12 | GVN | 125:c11-c12 126:c11-c12 128:c3-c4 129:c11-c12 |
| test.cpp:125:15:125:15 | GVN | 125:c15-c15 126:c15-c15 |
| test.cpp:128:11:128:11 | GVN | 128:c11-c11 129:c15-c15 |
| test.cpp:136:11:136:18 | GVN | 136:c11-c18 137:c11-c18 139:c3-c10 |
| test.cpp:136:21:136:21 | GVN | 136:c21-c21 137:c21-c21 |
| test.cpp:144:11:144:12 | GVN | 144:c11-c12 145:c11-c12 147:c3-c4 149:c11-c12 |
| test.cpp:144:15:144:15 | GVN | 144:c15-c15 149:c15-c15 |
| test.cpp:153:11:153:18 | GVN | 153:c11-c18 154:c11-c18 156:c3-c10 |
| test.cpp:153:21:153:21 | GVN | 153:c21-c21 154:c21-c21 |

View File

@@ -0,0 +1,11 @@
import cpp
import semmle.code.cpp.ir.internal.ASTValueNumbering
from GVN g
where strictcount(g.getAnExpr()) > 1
select g,
strictconcat(Location loc |
loc = g.getAnExpr().getLocation()
|
loc.getStartLine() + ":c" + loc.getStartColumn() + "-c" + loc.getEndColumn(), " "
)

View File

@@ -18,3 +18,6 @@
| test.cpp:235:2:235:5 | call to free | There is a new/free mismatch between this free and the corresponding $@. | test.cpp:227:7:227:13 | new | new |
| test.cpp:239:2:239:5 | call to free | There is a new/free mismatch between this free and the corresponding $@. | test.cpp:228:7:228:17 | new[] | new[] |
| test.cpp:272:3:272:6 | call to free | There is a new/free mismatch between this free and the corresponding $@. | test.cpp:265:7:265:13 | new | new |
| test.cpp:441:2:441:10 | delete | There is a malloc/delete mismatch between this delete and the corresponding $@. | test.cpp:434:13:434:18 | call to strdup | malloc |
| test.cpp:443:2:443:10 | delete | There is a malloc/delete mismatch between this delete and the corresponding $@. | test.cpp:436:13:436:19 | call to strndup | malloc |
| test.cpp:445:2:445:10 | delete | There is a malloc/delete mismatch between this delete and the corresponding $@. | test.cpp:438:16:438:21 | call to wcsdup | malloc |

View File

@@ -424,3 +424,24 @@ void test13()
delete myPointer3.getPointer(); // GOOD
}
char *strdup(const char *s1);
char *strndup(const char *s1, size_t n);
wchar_t* wcsdup(const wchar_t* s1);
void test14()
{
char *s1 = strdup("string");
char *s2 = strdup("string");
char *s3 = strndup("string", 3);
char *s4 = strndup("string", 3);
wchar_t *s5 = wcsdup(L"string");
wchar_t *s6 = wcsdup(L"string");
delete s1; // BAD: strdup -> delete
free(s2); // GOOD
delete s3; // BAD: strndup -> delete
free(s4); // GOOD
delete s5; // BAD: wcsdup -> delete
free(s6); // GOOD
}

View File

@@ -42,4 +42,8 @@ void test(int i, const char *str)
}
printf("%@ %i %i", 1, 2); // GOOD
printf("%Y", 1, 2); // GOOD (unknown format character, this might be correct)
printf("%1.1Y", 1, 2); // GOOD (unknown format character, this might be correct)
printf("%*.*Y", 1, 2); // GOOD (unknown format character, this might be correct)
}

View File

@@ -4,12 +4,14 @@ edges
| globalVars.c:8:7:8:10 | copy | globalVars.c:27:9:27:12 | copy |
| globalVars.c:8:7:8:10 | copy | globalVars.c:30:15:30:18 | copy |
| globalVars.c:8:7:8:10 | copy | globalVars.c:30:15:30:18 | copy |
| globalVars.c:8:7:8:10 | copy | globalVars.c:30:15:30:18 | copy |
| globalVars.c:8:7:8:10 | copy | globalVars.c:35:11:35:14 | copy |
| globalVars.c:9:7:9:11 | copy2 | globalVars.c:38:9:38:13 | copy2 |
| globalVars.c:9:7:9:11 | copy2 | globalVars.c:38:9:38:13 | copy2 |
| globalVars.c:9:7:9:11 | copy2 | globalVars.c:38:9:38:13 | copy2 |
| globalVars.c:9:7:9:11 | copy2 | globalVars.c:41:15:41:19 | copy2 |
| globalVars.c:9:7:9:11 | copy2 | globalVars.c:41:15:41:19 | copy2 |
| globalVars.c:9:7:9:11 | copy2 | globalVars.c:41:15:41:19 | copy2 |
| globalVars.c:9:7:9:11 | copy2 | globalVars.c:50:9:50:13 | copy2 |
| globalVars.c:9:7:9:11 | copy2 | globalVars.c:50:9:50:13 | copy2 |
| globalVars.c:9:7:9:11 | copy2 | globalVars.c:50:9:50:13 | copy2 |
@@ -25,9 +27,15 @@ edges
| globalVars.c:24:11:24:14 | argv | globalVars.c:11:22:11:25 | argv |
| globalVars.c:27:9:27:12 | copy | globalVars.c:27:9:27:12 | (const char *)... |
| globalVars.c:27:9:27:12 | copy | globalVars.c:27:9:27:12 | copy |
| globalVars.c:30:15:30:18 | copy | globalVars.c:30:15:30:18 | copy |
| globalVars.c:30:15:30:18 | copy | globalVars.c:30:15:30:18 | copy |
| globalVars.c:30:15:30:18 | copy | globalVars.c:30:15:30:18 | copy |
| globalVars.c:35:11:35:14 | copy | globalVars.c:15:21:15:23 | val |
| globalVars.c:38:9:38:13 | copy2 | globalVars.c:38:9:38:13 | (const char *)... |
| globalVars.c:38:9:38:13 | copy2 | globalVars.c:38:9:38:13 | copy2 |
| globalVars.c:41:15:41:19 | copy2 | globalVars.c:41:15:41:19 | copy2 |
| globalVars.c:41:15:41:19 | copy2 | globalVars.c:41:15:41:19 | copy2 |
| globalVars.c:41:15:41:19 | copy2 | globalVars.c:41:15:41:19 | copy2 |
| globalVars.c:50:9:50:13 | copy2 | globalVars.c:50:9:50:13 | (const char *)... |
| globalVars.c:50:9:50:13 | copy2 | globalVars.c:50:9:50:13 | copy2 |
nodes

View File

@@ -0,0 +1,2 @@
lgtm,codescanning
* For string formatting methods, such as `System.Console.WriteLine(string format, params object[] arg)`, we now also recognize overloads without insertion parameters as string formatting methods. For example, `System.Console.WriteLine(string value)` is now also a member of the class `FormatMethod` in `frameworks/Format.qll`.

View File

@@ -0,0 +1,4 @@
lgtm,codescanning
* CIL extraction has been improved to store `modreq` and `modopt` custom modifiers.
The extracted information is surfaced through the `CustomModifierReceiver` class. Additionally,
the information is also used to evaluate the new `Setter::isInitOnly` predicate.

View File

@@ -0,0 +1,2 @@
lgtm,codescanning
* The queries `FormatInvalid.ql`, `FormatMissingArgument.ql`, and `FormatUnusedArgument.ql` have been merged into a single `FormatInvalid.ql` query.

View File

@@ -1,6 +1,5 @@
using System;
using System.IO;
using System.Linq;
using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;
@@ -83,7 +82,7 @@ namespace Semmle.Extraction.CIL
trapFile.Write(GetString(def.Name));
trapFile.Write('_');
trapFile.Write(def.Version.ToString());
trapFile.Write("::");
trapFile.Write(Entities.Type.AssemblyTypeNameSeparator);
}
public Entities.TypeSignatureDecoder TypeSignatureDecoder { get; }

View File

@@ -27,27 +27,26 @@ namespace Semmle.Extraction.CIL.Entities
return obj is ArrayType array && elementType.Equals(array.elementType) && rank == array.rank;
}
public override int GetHashCode()
{
return elementType.GetHashCode() * 5 + rank;
}
public override int GetHashCode() => HashCode.Combine(elementType, rank);
public override void WriteId(TextWriter trapFile, bool inContext)
{
elementType.GetId(trapFile, inContext);
elementType.WriteId(trapFile, inContext);
trapFile.Write('[');
for (var i = 1; i < rank; ++i)
{
trapFile.Write(',');
}
trapFile.Write(']');
}
public override string Name => elementType.Name + "[]";
public override Namespace Namespace => Cx.SystemNamespace;
public override Namespace ContainingNamespace => Cx.SystemNamespace;
public override Type? ContainingType => null;
public override int ThisTypeParameters => elementType.ThisTypeParameters;
public override int ThisTypeParameterCount => elementType.ThisTypeParameterCount;
public override CilTypeKind Kind => CilTypeKind.Array;
@@ -71,7 +70,5 @@ namespace Semmle.Extraction.CIL.Entities
public override IEnumerable<Type> GenericArguments => elementType.GenericArguments;
public override IEnumerable<Type> TypeParameters => elementType.TypeParameters;
public override IEnumerable<Type> MethodParameters => throw new NotImplementedException();
}
}

View File

@@ -43,33 +43,38 @@ namespace Semmle.Extraction.CIL.Entities
{
decoded = attrib.DecodeValue(new CustomAttributeDecoder(Cx));
}
catch (NotImplementedException)
catch
{
// Attribute decoding is only partial at this stage.
Cx.Cx.Extractor.Logger.Log(Util.Logging.Severity.Info,
$"Attribute decoding is partial. Decoding attribute {constructor.DeclaringType.GetQualifiedName()} failed on {@object}.");
yield break;
}
for (var index = 0; index < decoded.FixedArguments.Length; ++index)
{
var value = decoded.FixedArguments[index].Value;
var stringValue = GetStringValue(value);
var stringValue = GetStringValue(decoded.FixedArguments[index].Type, decoded.FixedArguments[index].Value);
yield return Tuples.cil_attribute_positional_argument(this, index, stringValue);
}
foreach (var p in decoded.NamedArguments)
{
var value = p.Value;
var stringValue = GetStringValue(value);
yield return Tuples.cil_attribute_named_argument(this, p.Name, stringValue);
var stringValue = GetStringValue(p.Type, p.Value);
yield return Tuples.cil_attribute_named_argument(this, p.Name!, stringValue);
}
}
}
private static string GetStringValue(object? value)
private static string GetStringValue(Type type, object? value)
{
if (value is System.Collections.Immutable.ImmutableArray<CustomAttributeTypedArgument<Type>> values)
{
return "[" + string.Join(",", values.Select(v => GetStringValue(v.Value))) + "]";
return "[" + string.Join(",", values.Select(v => GetStringValue(v.Type, v.Value))) + "]";
}
if (type.GetQualifiedName() == "System.Type" &&
value is Type t)
{
return t.GetQualifiedName();
}
return value?.ToString() ?? "null";

View File

@@ -17,36 +17,18 @@ namespace Semmle.Extraction.CIL.Entities
// Either null or notEmpty
private readonly Type[]? thisTypeArguments;
public override IEnumerable<Type> ThisTypeArguments => thisTypeArguments.EnumerateNull();
public override IEnumerable<Type> ThisGenericArguments => thisTypeArguments.EnumerateNull();
public override IEnumerable<IExtractionProduct> Contents
{
get
{
foreach (var c in base.Contents)
yield return c;
var i = 0;
foreach (var type in ThisGenericArguments)
{
yield return type;
yield return Tuples.cil_type_argument(this, i++, type);
}
}
}
public override Type SourceDeclaration => unboundGenericType;
private readonly Type? containingType;
private readonly NamedTypeIdWriter idWriter;
public ConstructedType(Context cx, Type unboundType, IEnumerable<Type> typeArguments) : base(cx)
{
idWriter = new NamedTypeIdWriter(this);
var suppliedArgs = typeArguments.Count();
if (suppliedArgs != unboundType.TotalTypeParametersCheck)
if (suppliedArgs != unboundType.TotalTypeParametersCount)
throw new InternalError("Unexpected number of type arguments in ConstructedType");
unboundGenericType = unboundType;
var thisParams = unboundType.ThisTypeParameters;
var thisParams = unboundType.ThisTypeParameterCount;
if (typeArguments.Count() == thisParams)
{
@@ -88,14 +70,29 @@ namespace Semmle.Extraction.CIL.Entities
return h;
}
private readonly Type? containingType;
public override IEnumerable<IExtractionProduct> Contents
{
get
{
foreach (var c in base.Contents)
yield return c;
var i = 0;
foreach (var type in ThisTypeArguments)
{
yield return type;
yield return Tuples.cil_type_argument(this, i++, type);
}
}
}
public override Type SourceDeclaration => unboundGenericType;
public override Type? ContainingType => containingType;
public override string Name => unboundGenericType.Name;
public override Namespace Namespace => unboundGenericType.Namespace!;
public override int ThisTypeParameters => thisTypeArguments == null ? 0 : thisTypeArguments.Length;
public override Namespace ContainingNamespace => unboundGenericType.ContainingNamespace!;
public override CilTypeKind Kind => unboundGenericType.Kind;
@@ -106,40 +103,17 @@ namespace Semmle.Extraction.CIL.Entities
public override void WriteId(TextWriter trapFile, bool inContext)
{
if (ContainingType != null)
{
ContainingType.GetId(trapFile, inContext);
trapFile.Write('.');
}
else
{
WriteAssemblyPrefix(trapFile);
if (!Namespace.IsGlobalNamespace)
{
Namespace.WriteId(trapFile);
trapFile.Write('.');
}
}
trapFile.Write(unboundGenericType.Name);
if (thisTypeArguments != null && thisTypeArguments.Any())
{
trapFile.Write('<');
var index = 0;
foreach (var t in thisTypeArguments)
{
trapFile.WriteSeparator(",", ref index);
t.WriteId(trapFile);
}
trapFile.Write('>');
}
idWriter.WriteId(trapFile, inContext);
}
public override void WriteAssemblyPrefix(TextWriter trapFile) => unboundGenericType.WriteAssemblyPrefix(trapFile);
public override int ThisTypeParameterCount => thisTypeArguments?.Length ?? 0;
public override IEnumerable<Type> TypeParameters => GenericArguments;
public override IEnumerable<Type> MethodParameters => throw new NotImplementedException();
public override IEnumerable<Type> ThisTypeArguments => thisTypeArguments.EnumerateNull();
public override IEnumerable<Type> ThisGenericArguments => thisTypeArguments.EnumerateNull();
}
}

View File

@@ -14,7 +14,7 @@ namespace Semmle.Extraction.CIL.Entities
public Type GetPrimitiveType(PrimitiveTypeCode typeCode) => cx.Create(typeCode);
public Type GetSystemType() => throw new NotImplementedException();
public Type GetSystemType() => new NoMetadataHandleType(cx, "System.Type");
public Type GetSZArrayType(Type elementType) =>
cx.Populate(new ArrayType(cx, elementType));
@@ -25,10 +25,23 @@ namespace Semmle.Extraction.CIL.Entities
public Type GetTypeFromReference(MetadataReader reader, TypeReferenceHandle handle, byte rawTypeKind) =>
(Type)cx.Create(handle);
public Type GetTypeFromSerializedName(string name) => throw new NotImplementedException();
public Type GetTypeFromSerializedName(string name) => new NoMetadataHandleType(cx, name);
public PrimitiveTypeCode GetUnderlyingEnumType(Type type) => throw new NotImplementedException();
public PrimitiveTypeCode GetUnderlyingEnumType(Type type)
{
if (type is TypeDefinitionType tdt &&
tdt.GetUnderlyingEnumType() is PrimitiveTypeCode underlying)
{
return underlying;
}
public bool IsSystemType(Type type) => type is PrimitiveType; // ??
var name = type.GetQualifiedName();
cx.Cx.Extractor.Logger.Log(Util.Logging.Severity.Info, $"Couldn't get underlying enum type for {name}");
// We can't fall back to Int32, because the type returned here defines how many bytes are read from the
// stream and how those bytes are interpreted.
throw new NotImplementedException();
}
public bool IsSystemType(Type type) => type.GetQualifiedName() == "System.Type";
}
}

View File

@@ -76,9 +76,10 @@ namespace Semmle.Extraction.CIL.Entities
var typeSignature = md.DecodeSignature(Cx.TypeSignatureDecoder, this);
Parameters = MakeParameters(typeSignature.ParameterTypes).ToArray();
var parameters = GetParameterExtractionProducts(typeSignature.ParameterTypes).ToArray();
Parameters = parameters.OfType<Parameter>().ToArray();
foreach (var c in Parameters)
foreach (var c in parameters)
yield return c;
foreach (var c in PopulateFlags)
@@ -95,7 +96,12 @@ namespace Semmle.Extraction.CIL.Entities
}
yield return Tuples.metadata_handle(this, Cx.Assembly, MetadataTokens.GetToken(handle));
yield return Tuples.cil_method(this, Name, declaringType, typeSignature.ReturnType);
foreach (var m in GetMethodExtractionProducts(Name, declaringType, typeSignature.ReturnType))
{
yield return m;
}
yield return Tuples.cil_method_source_declaration(this, this);
yield return Tuples.cil_method_location(this, Cx.Assembly);
@@ -178,7 +184,7 @@ namespace Semmle.Extraction.CIL.Entities
}
}
private IEnumerable<IExtractionProduct> Decode(byte[] ilbytes, Dictionary<int, Instruction> jump_table)
private IEnumerable<IExtractionProduct> Decode(byte[]? ilbytes, Dictionary<int, Instruction> jump_table)
{
// Sequence points are stored in order of offset.
// We use an enumerator to locate the correct sequence point for each instruction.
@@ -203,9 +209,9 @@ namespace Semmle.Extraction.CIL.Entities
}
var child = 0;
for (var offset = 0; offset < ilbytes.Length;)
for (var offset = 0; offset < (ilbytes?.Length ?? 0);)
{
var instruction = new Instruction(Cx, this, ilbytes, offset, child++);
var instruction = new Instruction(Cx, this, ilbytes!, offset, child++);
yield return instruction;
if (nextSequencePoint != null && offset >= nextSequencePoint.Current.Offset)
@@ -245,12 +251,12 @@ namespace Semmle.Extraction.CIL.Entities
var ilbytes = body.GetILBytes();
var child = 0;
for (var offset = 0; offset < ilbytes.Length;)
for (var offset = 0; offset < (ilbytes?.Length ?? 0);)
{
Instruction decoded;
try
{
decoded = new Instruction(Cx, this, ilbytes, offset, child++);
decoded = new Instruction(Cx, this, ilbytes!, offset, child++);
offset += decoded.Width;
}
catch // lgtm[cs/catch-of-all-exceptions]

View File

@@ -16,18 +16,16 @@ namespace Semmle.Extraction.CIL.Entities
public override string Name => "!error";
public override Namespace Namespace => Cx.GlobalNamespace;
public override Namespace ContainingNamespace => Cx.GlobalNamespace;
public override Type? ContainingType => null;
public override int ThisTypeParameters => 0;
public override int ThisTypeParameterCount => 0;
public override void WriteAssemblyPrefix(TextWriter trapFile) => throw new NotImplementedException();
public override IEnumerable<Type> TypeParameters => throw new NotImplementedException();
public override IEnumerable<Type> MethodParameters => throw new NotImplementedException();
public override Type Construct(IEnumerable<Type> typeArguments) => throw new NotImplementedException();
}
}

View File

@@ -8,7 +8,7 @@ namespace Semmle.Extraction.CIL.Entities
/// <summary>
/// An entity representing a field.
/// </summary>
internal abstract class Field : GenericContext, IMember
internal abstract class Field : GenericContext, IMember, ICustomModifierReceiver
{
protected Field(Context cx) : base(cx)
{
@@ -45,7 +45,13 @@ namespace Semmle.Extraction.CIL.Entities
{
get
{
yield return Tuples.cil_field(this, DeclaringType, Name, Type);
var t = Type;
if (t is ModifiedType mt)
{
t = mt.Unmodified;
yield return Tuples.cil_custom_modifiers(this, mt.Modifier, mt.IsRequired);
}
yield return Tuples.cil_field(this, DeclaringType, Name, t);
}
}

View File

@@ -0,0 +1,59 @@
using System.Collections.Generic;
using System.Reflection.Metadata;
namespace Semmle.Extraction.CIL.Entities
{
internal static class GenericsHelper
{
public static TypeTypeParameter[] MakeTypeParameters(Type container, int count)
{
var newTypeParams = new TypeTypeParameter[count];
for (var i = 0; i < newTypeParams.Length; ++i)
{
newTypeParams[i] = new TypeTypeParameter(container, i);
}
return newTypeParams;
}
public static string GetNonGenericName(StringHandle name, MetadataReader reader)
{
var n = reader.GetString(name);
return GetNonGenericName(n);
}
public static string GetNonGenericName(string name)
{
var tick = name.LastIndexOf('`');
return tick == -1
? name
: name.Substring(0, tick);
}
public static int GetGenericTypeParameterCount(StringHandle name, MetadataReader reader)
{
var n = reader.GetString(name);
return GetGenericTypeParameterCount(n);
}
public static int GetGenericTypeParameterCount(string name)
{
var tick = name.LastIndexOf('`');
return tick == -1
? 0
: int.Parse(name.Substring(tick + 1));
}
public static IEnumerable<Type> GetAllTypeParameters(Type? container, IEnumerable<TypeTypeParameter> thisTypeParameters)
{
if (container != null)
{
foreach (var t in container.TypeParameters)
yield return t;
}
foreach (var t in thisTypeParameters)
yield return t;
}
}
}

View File

@@ -76,12 +76,16 @@ namespace Semmle.Extraction.CIL.Entities
var typeSignature = mr.DecodeMethodSignature(Cx.TypeSignatureDecoder, this);
Parameters = MakeParameters(typeSignature.ParameterTypes).ToArray();
foreach (var p in Parameters) yield return p;
var parameters = GetParameterExtractionProducts(typeSignature.ParameterTypes).ToArray();
Parameters = parameters.OfType<Parameter>().ToArray();
foreach (var p in parameters) yield return p;
foreach (var f in PopulateFlags) yield return f;
yield return Tuples.cil_method(this, Name, DeclaringType, typeSignature.ReturnType);
foreach (var m in GetMethodExtractionProducts(Name, DeclaringType, typeSignature.ReturnType))
{
yield return m;
}
if (SourceDeclaration != null)
yield return Tuples.cil_method_source_declaration(this, SourceDeclaration);

View File

@@ -3,14 +3,13 @@ using System.Reflection.Metadata;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using Semmle.Util;
namespace Semmle.Extraction.CIL.Entities
{
/// <summary>
/// A method entity.
/// </summary>
internal abstract class Method : TypeContainer, IMember
internal abstract class Method : TypeContainer, IMember, ICustomModifierReceiver
{
protected MethodTypeParameter[]? genericParams;
protected GenericContext gc;
@@ -21,6 +20,8 @@ namespace Semmle.Extraction.CIL.Entities
this.gc = gc;
}
public ITypeSignature ReturnType => signature.ReturnType;
public override IEnumerable<Type> TypeParameters => gc.TypeParameters.Concat(DeclaringType.TypeParameters);
public override IEnumerable<Type> MethodParameters =>
@@ -76,7 +77,7 @@ namespace Semmle.Extraction.CIL.Entities
public abstract bool IsStatic { get; }
protected IEnumerable<Parameter> MakeParameters(IEnumerable<Type> parameterTypes)
protected IEnumerable<IExtractionProduct> GetParameterExtractionProducts(IEnumerable<Type> parameterTypes)
{
var i = 0;
@@ -86,7 +87,26 @@ namespace Semmle.Extraction.CIL.Entities
}
foreach (var p in parameterTypes)
yield return Cx.Populate(new Parameter(Cx, this, i++, p));
{
var t = p;
if (t is ModifiedType mt)
{
t = mt.Unmodified;
yield return Tuples.cil_custom_modifiers(this, mt.Modifier, mt.IsRequired);
}
yield return Cx.Populate(new Parameter(Cx, this, i++, t));
}
}
protected IEnumerable<IExtractionProduct> GetMethodExtractionProducts(string name, Type declaringType, Type returnType)
{
var t = returnType;
if (t is ModifiedType mt)
{
t = mt.Unmodified;
yield return Tuples.cil_custom_modifiers(this, mt.Modifier, mt.IsRequired);
}
yield return Tuples.cil_method(this, name, declaringType, t);
}
}
}

View File

@@ -77,14 +77,19 @@ namespace Semmle.Extraction.CIL.Entities
throw new InternalError($"Unexpected constructed method handle kind {ms.Method.Kind}");
}
Parameters = MakeParameters(constructedTypeSignature.ParameterTypes).ToArray();
foreach (var p in Parameters)
var parameters = GetParameterExtractionProducts(constructedTypeSignature.ParameterTypes).ToArray();
Parameters = parameters.OfType<Parameter>().ToArray();
foreach (var p in parameters)
yield return p;
foreach (var f in PopulateFlags)
yield return f;
yield return Tuples.cil_method(this, Name, DeclaringType, constructedTypeSignature.ReturnType);
foreach (var m in GetMethodExtractionProducts(Name, DeclaringType, constructedTypeSignature.ReturnType))
{
yield return m;
}
yield return Tuples.cil_method_source_declaration(this, SourceDeclaration);
if (typeParams.Length != unboundMethod.GenericParameterCount)

View File

@@ -41,8 +41,6 @@ namespace Semmle.Extraction.CIL.Entities
public override IEnumerable<Type> TypeParameters => throw new NotImplementedException();
public override IEnumerable<Type> MethodParameters => throw new NotImplementedException();
public override IEnumerable<IExtractionProduct> Contents
{
get

View File

@@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using System.IO;
namespace Semmle.Extraction.CIL.Entities
{
/// <summary>
/// Modified types are not written directly to trap files. Instead, the modifiers are stored
/// on the modifiable entity (field type, property/method/function pointer parameter or return types).
/// </summary>
internal sealed class ModifiedType : Type
{
public ModifiedType(Context cx, Type unmodified, Type modifier, bool isRequired) : base(cx)
{
Unmodified = unmodified;
Modifier = modifier;
IsRequired = isRequired;
}
public Type Unmodified { get; }
public Type Modifier { get; }
public bool IsRequired { get; }
public override CilTypeKind Kind => throw new NotImplementedException();
public override Namespace? ContainingNamespace => throw new NotImplementedException();
public override Type? ContainingType => throw new NotImplementedException();
public override IEnumerable<Type> TypeParameters => throw new NotImplementedException();
public override int ThisTypeParameterCount => throw new NotImplementedException();
public override Type Construct(IEnumerable<Type> typeArguments) => throw new NotImplementedException();
public override string Name => $"{Unmodified.Name} {(IsRequired ? "modreq" : "modopt")}({Modifier.Name})";
public override void WriteAssemblyPrefix(TextWriter trapFile) => throw new NotImplementedException();
public override void WriteId(TextWriter trapFile, bool inContext) => throw new NotImplementedException();
}
}

View File

@@ -0,0 +1,63 @@
using Microsoft.CodeAnalysis;
using System.Linq;
using System.IO;
namespace Semmle.Extraction.CIL.Entities
{
internal class NamedTypeIdWriter
{
private readonly Type type;
public NamedTypeIdWriter(Type type)
{
this.type = type;
}
public void WriteId(TextWriter trapFile, bool inContext)
{
if (type.IsPrimitiveType)
{
Type.WritePrimitiveTypeId(trapFile, type.Name);
return;
}
var ct = type.ContainingType;
if (ct != null)
{
ct.WriteId(trapFile, inContext);
trapFile.Write('.');
}
else
{
type.WriteAssemblyPrefix(trapFile);
var ns = type.ContainingNamespace!;
if (!ns.IsGlobalNamespace)
{
ns.WriteId(trapFile);
trapFile.Write('.');
}
}
trapFile.Write(type.Name);
var thisTypeArguments = type.ThisTypeArguments;
if (thisTypeArguments != null && thisTypeArguments.Any())
{
trapFile.Write('<');
var index = 0;
foreach (var t in thisTypeArguments)
{
trapFile.WriteSeparator(",", ref index);
t.WriteId(trapFile);
}
trapFile.Write('>');
}
else if (type.ThisTypeParameterCount > 0)
{
trapFile.Write('`');
trapFile.Write(type.ThisTypeParameterCount);
}
}
}
}

View File

@@ -0,0 +1,163 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Semmle.Util;
namespace Semmle.Extraction.CIL.Entities
{
internal sealed partial class NoMetadataHandleType
{
/// <summary>
/// Parser to split a fully qualified name into short name, namespace or declaring type name, assembly name, and
/// type argument names. Names are in the following format:
/// <c>N1.N2.T1`2+T2`2[T3,[T4, A1, Version=...],T5,T6], A2, Version=...</c>
/// </summary>
/// <example>
/// <code>typeof(System.Collections.Generic.List<int>.Enumerator)
/// -> System.Collections.Generic.List`1+Enumerator[[System.Int32, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]], System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e
/// typeof(System.Collections.Generic.List<>.Enumerator)
/// -> System.Collections.Generic.List`1+Enumerator, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e
/// </code>
/// </example>
private class FullyQualifiedNameParser
{
public string ShortName { get; internal set; }
public string? AssemblyName { get; internal set; }
public IEnumerable<string>? TypeArguments { get; internal set; }
public string? UnboundGenericTypeName { get; internal set; }
public string ContainerName { get; internal set; }
public bool IsContainerNamespace { get; internal set; }
private string AssemblySuffix => string.IsNullOrWhiteSpace(AssemblyName) ? "" : $", {AssemblyName}";
public FullyQualifiedNameParser(string name)
{
ExtractAssemblyName(ref name, out var lastBracketIndex);
ExtractTypeArguments(ref name, lastBracketIndex, out var containerTypeArguments);
ContainerName = ExtractContainer(ref name, containerTypeArguments);
ShortName = name;
}
private void ExtractTypeArguments(ref string name, int lastBracketIndex, out string containerTypeArguments)
{
var firstBracketIndex = name.IndexOf('[');
if (firstBracketIndex < 0)
{
// not generic or non-constructed generic
TypeArguments = null;
containerTypeArguments = "";
UnboundGenericTypeName = null;
return;
}
// "T3,[T4, Assembly1, Version=...],T5,T6"
string typeArgs;
(name, _, typeArgs, _) = name.Split(firstBracketIndex, firstBracketIndex + 1, lastBracketIndex - firstBracketIndex - 1);
var thisTypeArgCount = GenericsHelper.GetGenericTypeParameterCount(name);
if (thisTypeArgCount == 0)
{
// not generic or non-constructed generic; container is constructed
TypeArguments = null;
containerTypeArguments = $"[{typeArgs}]";
UnboundGenericTypeName = null;
return;
}
// constructed generic
// "T3,[T4, Assembly1, Version=...]", ["T5", "T6"]
var (containerTypeArgs, thisTypeArgs) = ParseTypeArgumentStrings(typeArgs, thisTypeArgCount);
TypeArguments = thisTypeArgs;
containerTypeArguments = string.IsNullOrWhiteSpace(containerTypeArgs)
? "" // containing type is not constructed generics
: $"[{containerTypeArgs}]"; // "T3,[T4, Assembly1, Version=...],,]"
UnboundGenericTypeName = $"{name}{AssemblySuffix}";
}
private string ExtractContainer(ref string name, string containerTypeArguments)
{
var lastPlusIndex = name.LastIndexOf('+');
IsContainerNamespace = lastPlusIndex < 0;
if (IsContainerNamespace)
{
return ExtractContainerNamespace(ref name);
}
return ExtractContainerType(ref name, containerTypeArguments, lastPlusIndex);
}
private static string ExtractContainerNamespace(ref string name)
{
var lastDotIndex = name.LastIndexOf('.');
if (lastDotIndex >= 0)
{
string containerName;
(containerName, _, name) = name.Split(lastDotIndex, lastDotIndex + 1);
return containerName;
}
return ""; // global namespace name
}
private string ExtractContainerType(ref string name, string containerTypeArguments, int lastPlusIndex)
{
string containerName;
(containerName, _, name) = name.Split(lastPlusIndex, lastPlusIndex + 1);
return $"{containerName}{containerTypeArguments}{AssemblySuffix}";
}
private void ExtractAssemblyName(ref string name, out int lastBracketIndex)
{
lastBracketIndex = name.LastIndexOf(']');
var assemblyCommaIndex = name.IndexOf(',', lastBracketIndex < 0 ? 0 : lastBracketIndex);
if (assemblyCommaIndex >= 0)
{
// "Assembly2, Version=..."
(name, _, AssemblyName) = name.Split(assemblyCommaIndex, assemblyCommaIndex + 2);
}
}
private static (string, IEnumerable<string>) ParseTypeArgumentStrings(string typeArgs, int thisTypeArgCount)
{
var thisTypeArgs = new Stack<string>(thisTypeArgCount);
while (typeArgs.Length > 0 && thisTypeArgCount > 0)
{
int startCurrentType;
if (typeArgs[^1] != ']')
{
startCurrentType = typeArgs.LastIndexOf(',') + 1;
thisTypeArgs.Push(typeArgs.Substring(startCurrentType));
}
else
{
var bracketCount = 1;
for (startCurrentType = typeArgs.Length - 2; startCurrentType >= 0 && bracketCount > 0; startCurrentType--)
{
if (typeArgs[startCurrentType] == ']')
{
bracketCount++;
}
else if (typeArgs[startCurrentType] == '[')
{
bracketCount--;
}
}
startCurrentType++;
thisTypeArgs.Push(typeArgs[(startCurrentType + 1)..^1]);
}
typeArgs = startCurrentType != 0
? typeArgs.Substring(0, startCurrentType - 1)
: "";
thisTypeArgCount--;
}
return (typeArgs, thisTypeArgs.ToList());
}
}
}
}

View File

@@ -0,0 +1,158 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using Semmle.Util;
namespace Semmle.Extraction.CIL.Entities
{
internal sealed partial class NoMetadataHandleType : Type
{
private readonly string originalName;
private readonly string name;
private readonly string? assemblyName;
private readonly string containerName;
private readonly bool isContainerNamespace;
private readonly Lazy<TypeTypeParameter[]>? typeParams;
// Either null or notEmpty
private readonly Type[]? thisTypeArguments;
private readonly Type unboundGenericType;
private readonly Type? containingType;
private readonly NamedTypeIdWriter idWriter;
public NoMetadataHandleType(Context cx, string originalName) : base(cx)
{
this.originalName = originalName;
this.idWriter = new NamedTypeIdWriter(this);
var nameParser = new FullyQualifiedNameParser(originalName);
name = nameParser.ShortName;
assemblyName = nameParser.AssemblyName;
isContainerNamespace = nameParser.IsContainerNamespace;
containerName = nameParser.ContainerName;
unboundGenericType = nameParser.UnboundGenericTypeName == null
? this
: new NoMetadataHandleType(Cx, nameParser.UnboundGenericTypeName);
if (nameParser.TypeArguments != null)
{
thisTypeArguments = nameParser.TypeArguments.Select(t => new NoMetadataHandleType(Cx, t)).ToArray();
}
else
{
typeParams = new Lazy<TypeTypeParameter[]>(GenericsHelper.MakeTypeParameters(this, ThisTypeParameterCount));
}
containingType = isContainerNamespace
? null
: new NoMetadataHandleType(Cx, containerName);
Populate();
}
private void Populate()
{
if (isContainerNamespace &&
!ContainingNamespace!.IsGlobalNamespace)
{
Cx.Populate(ContainingNamespace);
}
Cx.Populate(this);
}
public override bool Equals(object? obj)
{
return obj is NoMetadataHandleType t && originalName.Equals(t.originalName, StringComparison.Ordinal);
}
public override int GetHashCode()
{
return originalName.GetHashCode(StringComparison.Ordinal);
}
public override IEnumerable<IExtractionProduct> Contents
{
get
{
foreach (var tp in typeParams?.Value ?? Array.Empty<TypeTypeParameter>())
yield return tp;
foreach (var c in base.Contents)
yield return c;
var i = 0;
foreach (var type in ThisTypeArguments)
{
yield return type;
yield return Tuples.cil_type_argument(this, i++, type);
}
}
}
public override CilTypeKind Kind => CilTypeKind.ValueOrRefType;
public override string Name => GenericsHelper.GetNonGenericName(name);
public override Namespace? ContainingNamespace => isContainerNamespace
? containerName == Cx.GlobalNamespace.Name
? Cx.GlobalNamespace
: new Namespace(Cx, containerName)
: null;
public override Type? ContainingType => containingType;
public override Type SourceDeclaration => unboundGenericType;
public override Type Construct(IEnumerable<Type> typeArguments)
{
if (TotalTypeParametersCount != typeArguments.Count())
throw new InternalError("Mismatched type arguments");
return Cx.Populate(new ConstructedType(Cx, this, typeArguments));
}
public override void WriteAssemblyPrefix(TextWriter trapFile)
{
if (!string.IsNullOrWhiteSpace(assemblyName))
{
var an = new AssemblyName(assemblyName);
trapFile.Write(an.Name);
trapFile.Write('_');
trapFile.Write((an.Version ?? new Version(0, 0, 0, 0)).ToString());
trapFile.Write(Type.AssemblyTypeNameSeparator);
}
else
{
Cx.WriteAssemblyPrefix(trapFile);
}
}
public override void WriteId(TextWriter trapFile, bool inContext)
{
idWriter.WriteId(trapFile, inContext);
}
public override int ThisTypeParameterCount => unboundGenericType == this
? GenericsHelper.GetGenericTypeParameterCount(name)
: thisTypeArguments!.Length;
public override IEnumerable<Type> TypeParameters => unboundGenericType == this
? GenericsHelper.GetAllTypeParameters(containingType, typeParams!.Value)
: GenericArguments;
public override IEnumerable<Type> ThisTypeArguments => unboundGenericType == this
? base.ThisTypeArguments
: thisTypeArguments!;
public override IEnumerable<Type> ThisGenericArguments => unboundGenericType == this
? typeParams!.Value
: thisTypeArguments!;
}
}

View File

@@ -18,11 +18,7 @@ namespace Semmle.Extraction.CIL.Entities
return obj is PointerType pt && pointee.Equals(pt.pointee);
}
public override int GetHashCode()
{
return pointee.GetHashCode() * 29;
}
public override int GetHashCode() => HashCode.Combine(pointee, nameof(PointerType));
public override void WriteId(TextWriter trapFile, bool inContext)
{
@@ -32,13 +28,13 @@ namespace Semmle.Extraction.CIL.Entities
public override string Name => pointee.Name + "*";
public override Namespace? Namespace => pointee.Namespace;
public override Namespace? ContainingNamespace => pointee.ContainingNamespace;
public override Type? ContainingType => pointee.ContainingType;
public override TypeContainer Parent => pointee.Parent;
public override int ThisTypeParameters => 0;
public override int ThisTypeParameterCount => 0;
public override CilTypeKind Kind => CilTypeKind.Pointer;
@@ -46,8 +42,6 @@ namespace Semmle.Extraction.CIL.Entities
public override IEnumerable<Type> TypeParameters => throw new NotImplementedException();
public override IEnumerable<Type> MethodParameters => throw new NotImplementedException();
public override Type Construct(IEnumerable<Type> typeArguments) => throw new NotImplementedException();
public override IEnumerable<IExtractionProduct> Contents

View File

@@ -18,24 +18,20 @@ namespace Semmle.Extraction.CIL.Entities
return obj is PrimitiveType pt && typeCode == pt.typeCode;
}
public override int GetHashCode()
{
return 1337 * (int)typeCode;
}
public override int GetHashCode() => typeCode.GetHashCode();
public override void WriteId(TextWriter trapFile, bool inContext)
{
trapFile.Write("builtin:");
trapFile.Write(Name);
Type.WritePrimitiveTypeId(trapFile, Name);
}
public override string Name => typeCode.Id();
public override Namespace Namespace => Cx.SystemNamespace;
public override Namespace ContainingNamespace => Cx.SystemNamespace;
public override Type? ContainingType => null;
public override int ThisTypeParameters => 0;
public override int ThisTypeParameterCount => 0;
public override CilTypeKind Kind => CilTypeKind.ValueOrRefType;
@@ -43,8 +39,6 @@ namespace Semmle.Extraction.CIL.Entities
public override IEnumerable<Type> TypeParameters => throw new NotImplementedException();
public override IEnumerable<Type> MethodParameters => throw new NotImplementedException();
public override Type Construct(IEnumerable<Type> typeArguments) => throw new NotImplementedException();
}
}

View File

@@ -8,7 +8,7 @@ namespace Semmle.Extraction.CIL.Entities
/// <summary>
/// A property.
/// </summary>
internal sealed class Property : LabelledEntity
internal sealed class Property : LabelledEntity, ICustomModifierReceiver
{
private readonly Handle handle;
private readonly Type type;
@@ -54,7 +54,15 @@ namespace Semmle.Extraction.CIL.Entities
yield return Tuples.metadata_handle(this, Cx.Assembly, MetadataTokens.GetToken(handle));
var sig = pd.DecodeSignature(Cx.TypeSignatureDecoder, type);
yield return Tuples.cil_property(this, type, Cx.ShortName(pd.Name), sig.ReturnType);
var name = Cx.ShortName(pd.Name);
var t = sig.ReturnType;
if (t is ModifiedType mt)
{
t = mt.Unmodified;
yield return Tuples.cil_custom_modifiers(this, mt.Modifier, mt.IsRequired);
}
yield return Tuples.cil_property(this, type, name, t);
var accessors = pd.GetAccessors();
if (!accessors.Getter.IsNil)

View File

@@ -1,6 +1,7 @@
using System.Reflection.Metadata;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
namespace Semmle.Extraction.CIL.Entities
{
@@ -22,7 +23,9 @@ namespace Semmle.Extraction.CIL.Entities
elementType.WriteId(trapFile, gc);
trapFile.Write('[');
for (var i = 1; i < shape.Rank; ++i)
{
trapFile.Write(',');
}
trapFile.Write(']');
}
}
@@ -136,21 +139,28 @@ namespace Semmle.Extraction.CIL.Entities
private class Modified : ITypeSignature
{
private readonly ITypeSignature unmodifiedType;
private readonly ITypeSignature modifier;
private readonly bool isRequired;
public Modified(ITypeSignature unmodifiedType)
public Modified(ITypeSignature unmodifiedType, ITypeSignature modifier, bool isRequired)
{
this.unmodifiedType = unmodifiedType;
this.modifier = modifier;
this.isRequired = isRequired;
}
public void WriteId(TextWriter trapFile, GenericContext gc)
{
unmodifiedType.WriteId(trapFile, gc);
trapFile.Write(isRequired ? " modreq(" : " modopt(");
modifier.WriteId(trapFile, gc);
trapFile.Write(")");
}
}
ITypeSignature ISignatureTypeProvider<ITypeSignature, object>.GetModifiedType(ITypeSignature modifier, ITypeSignature unmodifiedType, bool isRequired)
{
return new Modified(unmodifiedType);
return new Modified(unmodifiedType, modifier, isRequired);
}
private class Pinned : ITypeSignature

View File

@@ -1,8 +1,8 @@
using System.Reflection.Metadata;
using System.Collections.Generic;
using Semmle.Util;
using System.IO;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
namespace Semmle.Extraction.CIL.Entities
{
@@ -12,8 +12,10 @@ namespace Semmle.Extraction.CIL.Entities
public abstract class Type : TypeContainer, IMember
{
public override string IdSuffix => ";cil-type";
internal const string AssemblyTypeNameSeparator = "::";
internal const string PrimitiveTypePrefix = "builtin" + AssemblyTypeNameSeparator + "System.";
public sealed override void WriteId(TextWriter trapFile) => WriteId(trapFile, false);
protected Type(Context cx) : base(cx) { }
/// <summary>
/// Find the method in this type matching the name and signature.
@@ -29,29 +31,6 @@ namespace Semmle.Extraction.CIL.Entities
return null;
}
public IEnumerable<Type> TypeArguments
{
get
{
if (ContainingType != null)
{
foreach (var t in ContainingType.TypeArguments)
yield return t;
}
foreach (var t in ThisTypeArguments)
yield return t;
}
}
public virtual IEnumerable<Type> ThisTypeArguments
{
get
{
yield break;
}
}
/// <summary>
/// Writes the assembly identifier of this type.
/// </summary>
@@ -67,22 +46,24 @@ namespace Semmle.Extraction.CIL.Entities
/// </param>
public abstract void WriteId(TextWriter trapFile, bool inContext);
public void GetId(TextWriter trapFile, bool inContext)
public sealed override void WriteId(TextWriter trapFile) => WriteId(trapFile, false);
/// <summary>
/// Returns the friendly qualified name of types, such as
/// ``"System.Collection.Generic.List`1"`` or
/// ``"System.Collection.Generic.List<System.Int32>"``.
///
/// Note that method/type generic type parameters never show up in the returned name.
/// </summary>
public string GetQualifiedName()
{
if (inContext)
WriteId(trapFile, true);
else
WriteId(trapFile);
using var writer = new StringWriter();
WriteId(writer, false);
var name = writer.ToString();
return name.Substring(name.IndexOf(AssemblyTypeNameSeparator) + 2);
}
protected Type(Context cx) : base(cx) { }
public abstract CilTypeKind Kind
{
get;
}
public virtual TypeContainer Parent => (TypeContainer?)ContainingType ?? Namespace!;
public abstract CilTypeKind Kind { get; }
public override IEnumerable<IExtractionProduct> Contents
{
@@ -92,35 +73,43 @@ namespace Semmle.Extraction.CIL.Entities
}
}
/// <summary>
/// Whether this type is visible outside the current assembly.
/// </summary>
public virtual bool IsVisible => true;
public abstract string Name { get; }
public abstract Namespace? Namespace { get; }
public abstract Namespace? ContainingNamespace { get; }
public abstract Type? ContainingType { get; }
public virtual TypeContainer Parent => (TypeContainer?)ContainingType ?? ContainingNamespace!;
public abstract Type Construct(IEnumerable<Type> typeArguments);
/// <summary>
/// The number of type arguments, or 0 if this isn't generic.
/// The containing type may also have type arguments.
/// Returns the type arguments of constructed types. For non-constructed types it returns an
/// empty collection.
/// </summary>
public abstract int ThisTypeParameters { get; }
public virtual IEnumerable<Type> ThisTypeArguments
{
get
{
yield break;
}
}
/// <summary>
/// The total number of type parameters (including parent types).
/// The number of type parameters for non-constructed generic types, the number of type arguments
/// for constructed types, or 0.
/// </summary>
public abstract int ThisTypeParameterCount { get; }
/// <summary>
/// The total number of type parameters/type arguments (including parent types).
/// This is used for internal consistency checking only.
/// </summary>
public int TotalTypeParametersCheck =>
ContainingType == null ? ThisTypeParameters : ThisTypeParameters + ContainingType.TotalTypeParametersCheck;
public int TotalTypeParametersCount =>
ThisTypeParameterCount + (ContainingType?.TotalTypeParametersCount ?? 0);
/// <summary>
/// Returns all bound/unbound generic arguments
/// of a constructed/unbound generic type.
/// Returns all bound/unbound generic arguments of a constructed/unbound generic type.
/// </summary>
public virtual IEnumerable<Type> ThisGenericArguments
{
@@ -139,6 +128,7 @@ namespace Semmle.Extraction.CIL.Entities
foreach (var t in ContainingType.GenericArguments)
yield return t;
}
foreach (var t in ThisGenericArguments)
yield return t;
}
@@ -146,12 +136,34 @@ namespace Semmle.Extraction.CIL.Entities
public virtual Type SourceDeclaration => this;
public void PrimitiveTypeId(TextWriter trapFile)
public static void WritePrimitiveTypeId(TextWriter trapFile, string name)
{
trapFile.Write("builtin:");
trapFile.Write(Name);
trapFile.Write(PrimitiveTypePrefix);
trapFile.Write(name);
}
private static readonly Dictionary<string, PrimitiveTypeCode> primitiveTypeCodeMapping = new Dictionary<string, PrimitiveTypeCode>
{
{"Boolean", PrimitiveTypeCode.Boolean},
{"Object", PrimitiveTypeCode.Object},
{"Byte", PrimitiveTypeCode.Byte},
{"SByte", PrimitiveTypeCode.SByte},
{"Int16", PrimitiveTypeCode.Int16},
{"UInt16", PrimitiveTypeCode.UInt16},
{"Int32", PrimitiveTypeCode.Int32},
{"UInt32", PrimitiveTypeCode.UInt32},
{"Int64", PrimitiveTypeCode.Int64},
{"UInt64", PrimitiveTypeCode.UInt64},
{"Single", PrimitiveTypeCode.Single},
{"Double", PrimitiveTypeCode.Double},
{"String", PrimitiveTypeCode.String},
{"Void", PrimitiveTypeCode.Void},
{"IntPtr", PrimitiveTypeCode.IntPtr},
{"UIntPtr", PrimitiveTypeCode.UIntPtr},
{"Char", PrimitiveTypeCode.Char},
{"TypedReference", PrimitiveTypeCode.TypedReference}
};
/// <summary>
/// Gets the primitive type corresponding to this type, if possible.
/// </summary>
@@ -171,72 +183,20 @@ namespace Semmle.Extraction.CIL.Entities
private bool TryGetPrimitiveTypeCode(out PrimitiveTypeCode code)
{
if (ContainingType == null && Namespace?.Name == Cx.SystemNamespace.Name)
if (ContainingType == null &&
ContainingNamespace?.Name == Cx.SystemNamespace.Name &&
primitiveTypeCodeMapping.TryGetValue(Name, out code))
{
switch (Name)
{
case "Boolean":
code = PrimitiveTypeCode.Boolean;
return true;
case "Object":
code = PrimitiveTypeCode.Object;
return true;
case "Byte":
code = PrimitiveTypeCode.Byte;
return true;
case "SByte":
code = PrimitiveTypeCode.SByte;
return true;
case "Int16":
code = PrimitiveTypeCode.Int16;
return true;
case "UInt16":
code = PrimitiveTypeCode.UInt16;
return true;
case "Int32":
code = PrimitiveTypeCode.Int32;
return true;
case "UInt32":
code = PrimitiveTypeCode.UInt32;
return true;
case "Int64":
code = PrimitiveTypeCode.Int64;
return true;
case "UInt64":
code = PrimitiveTypeCode.UInt64;
return true;
case "Single":
code = PrimitiveTypeCode.Single;
return true;
case "Double":
code = PrimitiveTypeCode.Double;
return true;
case "String":
code = PrimitiveTypeCode.String;
return true;
case "Void":
code = PrimitiveTypeCode.Void;
return true;
case "IntPtr":
code = PrimitiveTypeCode.IntPtr;
return true;
case "UIntPtr":
code = PrimitiveTypeCode.UIntPtr;
return true;
case "Char":
code = PrimitiveTypeCode.Char;
return true;
case "TypedReference":
code = PrimitiveTypeCode.TypedReference;
return true;
}
return true;
}
code = default(PrimitiveTypeCode);
code = default;
return false;
}
protected bool IsPrimitiveType => TryGetPrimitiveTypeCode(out _);
protected internal bool IsPrimitiveType => TryGetPrimitiveTypeCode(out _);
public sealed override IEnumerable<Type> MethodParameters => Enumerable.Empty<Type>();
public static Type DecodeType(GenericContext gc, TypeSpecificationHandle handle) =>
gc.Cx.MdReader.GetTypeSpecification(handle).DecodeSignature(gc.Cx.TypeSignatureDecoder, gc);

View File

@@ -6,6 +6,7 @@ using System.Linq;
using System.Collections.Generic;
using System.Reflection;
using System.IO;
using System.Reflection.Metadata.Ecma335;
namespace Semmle.Extraction.CIL.Entities
{
@@ -14,11 +15,15 @@ namespace Semmle.Extraction.CIL.Entities
/// </summary>
public sealed class TypeDefinitionType : Type
{
private readonly Handle handle;
private readonly TypeDefinitionHandle handle;
private readonly TypeDefinition td;
private readonly Lazy<IEnumerable<TypeTypeParameter>> typeParams;
private readonly Type? declType;
private readonly NamedTypeIdWriter idWriter;
public TypeDefinitionType(Context cx, TypeDefinitionHandle handle) : base(cx)
{
idWriter = new NamedTypeIdWriter(this);
td = cx.MdReader.GetTypeDefinition(handle);
this.handle = handle;
@@ -39,66 +44,22 @@ namespace Semmle.Extraction.CIL.Entities
public override void WriteId(TextWriter trapFile, bool inContext)
{
if (IsPrimitiveType)
{
PrimitiveTypeId(trapFile);
return;
}
var name = Cx.GetString(td.Name);
if (ContainingType != null)
{
ContainingType.GetId(trapFile, inContext);
trapFile.Write('.');
}
else
{
WriteAssemblyPrefix(trapFile);
var ns = Namespace;
if (!ns.IsGlobalNamespace)
{
ns.WriteId(trapFile);
trapFile.Write('.');
}
}
trapFile.Write(name);
idWriter.WriteId(trapFile, inContext);
}
public override string Name
{
get
{
var name = Cx.GetString(td.Name);
var tick = name.IndexOf('`');
return tick == -1 ? name : name.Substring(0, tick);
}
}
public override string Name => GenericsHelper.GetNonGenericName(td.Name, Cx.MdReader);
public override Namespace Namespace => Cx.Create(td.NamespaceDefinition);
private readonly Type? declType;
public override Namespace ContainingNamespace => Cx.Create(td.NamespaceDefinition);
public override Type? ContainingType => declType;
public override int ThisTypeParameters
{
get
{
var containingType = td.GetDeclaringType();
var parentTypeParameters = containingType.IsNil ? 0 :
Cx.MdReader.GetTypeDefinition(containingType).GetGenericParameters().Count;
return td.GetGenericParameters().Count - parentTypeParameters;
}
}
public override CilTypeKind Kind => CilTypeKind.ValueOrRefType;
public override Type Construct(IEnumerable<Type> typeArguments)
{
if (TotalTypeParametersCount != typeArguments.Count())
throw new InternalError("Mismatched type arguments");
return Cx.Populate(new ConstructedType(Cx, this, typeArguments));
}
@@ -108,52 +69,50 @@ namespace Semmle.Extraction.CIL.Entities
if (ct is null)
Cx.WriteAssemblyPrefix(trapFile);
else if (IsPrimitiveType)
trapFile.Write("builtin:");
trapFile.Write(Type.PrimitiveTypePrefix);
else
ct.WriteAssemblyPrefix(trapFile);
}
private IEnumerable<TypeTypeParameter> MakeTypeParameters()
{
if (ThisTypeParameters == 0)
if (ThisTypeParameterCount == 0)
return Enumerable.Empty<TypeTypeParameter>();
var newTypeParams = new TypeTypeParameter[ThisTypeParameters];
var newTypeParams = new TypeTypeParameter[ThisTypeParameterCount];
var genericParams = td.GetGenericParameters();
var toSkip = genericParams.Count - newTypeParams.Length;
// Two-phase population because type parameters can be mutually dependent
for (var i = 0; i < newTypeParams.Length; ++i)
newTypeParams[i] = Cx.Populate(new TypeTypeParameter(this, this, i));
newTypeParams[i] = Cx.Populate(new TypeTypeParameter(this, i));
for (var i = 0; i < newTypeParams.Length; ++i)
newTypeParams[i].PopulateHandle(genericParams[i + toSkip]);
return newTypeParams;
}
private readonly Lazy<IEnumerable<TypeTypeParameter>> typeParams;
public override IEnumerable<Type> MethodParameters => Enumerable.Empty<Type>();
public override IEnumerable<Type> TypeParameters
public override int ThisTypeParameterCount
{
get
{
if (declType != null)
{
foreach (var t in declType.TypeParameters)
yield return t;
}
var containingType = td.GetDeclaringType();
var parentTypeParameters = containingType.IsNil
? 0
: Cx.MdReader.GetTypeDefinition(containingType).GetGenericParameters().Count;
foreach (var t in typeParams.Value)
yield return t;
return td.GetGenericParameters().Count - parentTypeParameters;
}
}
public override IEnumerable<Type> TypeParameters => GenericsHelper.GetAllTypeParameters(declType, typeParams!.Value);
public override IEnumerable<Type> ThisGenericArguments => typeParams.Value;
public override IEnumerable<IExtractionProduct> Contents
{
get
{
yield return Tuples.metadata_handle(this, Cx.Assembly, handle.GetHashCode());
yield return Tuples.metadata_handle(this, Cx.Assembly, MetadataTokens.GetToken(handle));
foreach (var c in base.Contents) yield return c;

View File

@@ -16,11 +16,11 @@ namespace Semmle.Extraction.CIL.Entities
this.gc = gc;
}
public override Namespace? Namespace => null;
public override Namespace? ContainingNamespace => null;
public override Type? ContainingType => null;
public override int ThisTypeParameters => 0;
public override int ThisTypeParameterCount => 0;
public override CilTypeKind Kind => CilTypeKind.TypeParameter;

View File

@@ -16,12 +16,14 @@ namespace Semmle.Extraction.CIL.Entities
private readonly TypeReferenceHandle handle;
private readonly TypeReference tr;
private readonly Lazy<TypeTypeParameter[]> typeParams;
private readonly NamedTypeIdWriter idWriter;
public TypeReferenceType(Context cx, TypeReferenceHandle handle) : base(cx)
{
this.typeParams = new Lazy<TypeTypeParameter[]>(MakeTypeParameters);
this.idWriter = new NamedTypeIdWriter(this);
this.handle = handle;
this.tr = cx.MdReader.GetTypeReference(handle);
this.typeParams = new Lazy<TypeTypeParameter[]>(GenericsHelper.MakeTypeParameters(this, ThisTypeParameterCount));
}
public override bool Equals(object? obj)
@@ -34,16 +36,6 @@ namespace Semmle.Extraction.CIL.Entities
return handle.GetHashCode();
}
private TypeTypeParameter[] MakeTypeParameters()
{
var newTypeParams = new TypeTypeParameter[ThisTypeParameters];
for (var i = 0; i < newTypeParams.Length; ++i)
{
newTypeParams[i] = new TypeTypeParameter(this, this, i);
}
return newTypeParams;
}
public override IEnumerable<IExtractionProduct> Contents
{
get
@@ -56,45 +48,17 @@ namespace Semmle.Extraction.CIL.Entities
}
}
public override string Name
{
get
{
var name = Cx.GetString(tr.Name);
var tick = name.IndexOf('`');
return tick == -1 ? name : name.Substring(0, tick);
}
}
public override string Name => GenericsHelper.GetNonGenericName(tr.Name, Cx.MdReader);
public override Namespace Namespace => Cx.CreateNamespace(tr.Namespace);
public override int ThisTypeParameters
{
get
{
// Parse the name
var name = Cx.GetString(tr.Name);
var tick = name.IndexOf('`');
return tick == -1 ? 0 : int.Parse(name.Substring(tick + 1));
}
}
public override IEnumerable<Type> ThisGenericArguments
{
get
{
foreach (var t in typeParams.Value)
yield return t;
}
}
public override Namespace ContainingNamespace => Cx.CreateNamespace(tr.Namespace);
public override Type? ContainingType
{
get
{
if (tr.ResolutionScope.Kind == HandleKind.TypeReference)
return (Type)Cx.Create((TypeReferenceHandle)tr.ResolutionScope);
return null;
return tr.ResolutionScope.Kind == HandleKind.TypeReference
? (Type)Cx.Create((TypeReferenceHandle)tr.ResolutionScope)
: null;
}
}
@@ -112,7 +76,7 @@ namespace Semmle.Extraction.CIL.Entities
trapFile.Write(Cx.GetString(assemblyDef.Name));
trapFile.Write('_');
trapFile.Write(assemblyDef.Version.ToString());
trapFile.Write("::");
trapFile.Write(Entities.Type.AssemblyTypeNameSeparator);
break;
default:
Cx.WriteAssemblyPrefix(trapFile);
@@ -120,43 +84,20 @@ namespace Semmle.Extraction.CIL.Entities
}
}
public override IEnumerable<Type> TypeParameters => typeParams.Value;
public override int ThisTypeParameterCount => GenericsHelper.GetGenericTypeParameterCount(tr.Name, Cx.MdReader);
public override IEnumerable<Type> MethodParameters => throw new InternalError("This type does not have method parameters");
public override IEnumerable<Type> TypeParameters => GenericsHelper.GetAllTypeParameters(ContainingType, typeParams!.Value);
public override IEnumerable<Type> ThisGenericArguments => typeParams.Value;
public override void WriteId(TextWriter trapFile, bool inContext)
{
if (IsPrimitiveType)
{
PrimitiveTypeId(trapFile);
return;
}
var ct = ContainingType;
if (ct != null)
{
ct.GetId(trapFile, inContext);
}
else
{
if (tr.ResolutionScope.Kind == HandleKind.AssemblyReference)
{
WriteAssemblyPrefix(trapFile);
}
if (!Namespace.IsGlobalNamespace)
{
Namespace.WriteId(trapFile);
}
}
trapFile.Write('.');
trapFile.Write(Cx.GetString(tr.Name));
idWriter.WriteId(trapFile, inContext);
}
public override Type Construct(IEnumerable<Type> typeArguments)
{
if (TotalTypeParametersCheck != typeArguments.Count())
if (TotalTypeParametersCount != typeArguments.Count())
throw new InternalError("Mismatched type arguments");
return Cx.Populate(new ConstructedType(Cx, this, typeArguments));

View File

@@ -35,7 +35,7 @@ namespace Semmle.Extraction.CIL.Entities
genericContext.GetGenericTypeParameter(index);
Type ISignatureTypeProvider<Type, GenericContext>.GetModifiedType(Type modifier, Type unmodifiedType, bool isRequired) =>
unmodifiedType; // !! Not implemented properly
new ModifiedType(cx, unmodifiedType, modifier, isRequired);
Type ISignatureTypeProvider<Type, GenericContext>.GetPinnedType(Type elementType) =>
cx.Populate(new PointerType(cx, elementType));

View File

@@ -9,7 +9,7 @@ namespace Semmle.Extraction.CIL.Entities
private readonly Type type;
private readonly int index;
public TypeTypeParameter(GenericContext cx, Type t, int i) : base(cx)
public TypeTypeParameter(Type t, int i) : base(t)
{
index = i;
type = t;
@@ -37,8 +37,6 @@ namespace Semmle.Extraction.CIL.Entities
public override IEnumerable<Type> TypeParameters => Enumerable.Empty<Type>();
public override IEnumerable<Type> MethodParameters => Enumerable.Empty<Type>();
public override IEnumerable<IExtractionProduct> Contents
{
get

View File

@@ -17,7 +17,7 @@ namespace Semmle.Extraction.CIL
}
/// <summary>
/// The list of generic type parameters, including type parameters of
/// The list of generic type parameters/arguments, including type parameters/arguments of
/// containing types.
/// </summary>
public abstract IEnumerable<Entities.Type> TypeParameters { get; }

View File

@@ -0,0 +1,6 @@
namespace Semmle.Extraction.CIL
{
internal interface ICustomModifierReceiver
{
}
}

View File

@@ -188,6 +188,9 @@ namespace Semmle.Extraction.CIL
internal static Tuple cil_virtual(Method method) =>
new Tuple("cil_virtual", method);
internal static Tuple cil_custom_modifiers(ICustomModifierReceiver receiver, Type modifier, bool isRequired) =>
new Tuple("cil_custom_modifiers", receiver, modifier, isRequired ? 1 : 0);
internal static Tuple containerparent(Folder parent, IFileOrFolder child) =>
new Tuple("containerparent", parent, child);

View File

@@ -66,12 +66,25 @@ namespace Semmle.Extraction.CSharp.Entities
private void ExtractArguments(TextWriter trapFile)
{
var ctorArguments = attributeSyntax?.ArgumentList?.Arguments.Where(a => a.NameEquals == null).ToList();
var childIndex = 0;
foreach (var constructorArgument in symbol.ConstructorArguments)
for (var i = 0; i < symbol.ConstructorArguments.Length; i++)
{
var constructorArgument = symbol.ConstructorArguments[i];
var paramName = symbol.AttributeConstructor?.Parameters[i].Name;
var argSyntax = ctorArguments?.SingleOrDefault(a => a.NameColon != null && a.NameColon.Name.Identifier.Text == paramName);
if (argSyntax == null && // couldn't find named argument
ctorArguments?.Count > childIndex && // there're more arguments
ctorArguments[childIndex].NameColon == null) // the argument is positional
{
argSyntax = ctorArguments[childIndex];
}
CreateExpressionFromArgument(
constructorArgument,
attributeSyntax?.ArgumentList.Arguments[childIndex].Expression,
argSyntax?.Expression,
this,
childIndex++);
}

View File

@@ -84,6 +84,9 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
case SyntaxKind.ObjectCreationExpression:
return ExplicitObjectCreation.Create(info);
case SyntaxKind.ImplicitObjectCreationExpression:
return ImplicitObjectCreation.Create(info);
case SyntaxKind.ArrayCreationExpression:
return NormalArrayCreation.Create(info);
@@ -179,7 +182,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
return ImplicitArrayCreation.Create(info);
case SyntaxKind.AnonymousObjectCreationExpression:
return ImplicitObjectCreation.Create(info);
return AnonymousObjectCreation.Create(info);
case SyntaxKind.ComplexElementInitializerExpression:
return CollectionInitializer.Create(info);

View File

@@ -1,137 +0,0 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Semmle.Extraction.Entities;
using Semmle.Extraction.Kinds;
using System.IO;
using System.Linq;
namespace Semmle.Extraction.CSharp.Entities.Expressions
{
internal abstract class ObjectCreation<TExpressionSyntax> : Expression<TExpressionSyntax>
where TExpressionSyntax : ExpressionSyntax
{
protected ObjectCreation(ExpressionNodeInfo info)
: base(info) { }
}
// new Foo(...) { ... }.
internal class ExplicitObjectCreation : ObjectCreation<ObjectCreationExpressionSyntax>
{
private static bool IsDynamicObjectCreation(Context cx, ObjectCreationExpressionSyntax node)
{
return node.ArgumentList != null && node.ArgumentList.Arguments.Any(arg => IsDynamic(cx, arg.Expression));
}
private static ExprKind GetKind(Context cx, ObjectCreationExpressionSyntax node)
{
var si = cx.GetModel(node).GetSymbolInfo(node.Type);
return Entities.Type.IsDelegate(si.Symbol as INamedTypeSymbol) ? ExprKind.EXPLICIT_DELEGATE_CREATION : ExprKind.OBJECT_CREATION;
}
private ExplicitObjectCreation(ExpressionNodeInfo info)
: base(info.SetKind(GetKind(info.Context, (ObjectCreationExpressionSyntax)info.Node))) { }
public static Expression Create(ExpressionNodeInfo info) => new ExplicitObjectCreation(info).TryPopulate();
protected override void PopulateExpression(TextWriter trapFile)
{
if (Syntax.ArgumentList != null)
{
PopulateArguments(trapFile, Syntax.ArgumentList, 0);
}
var target = cx.GetModel(Syntax).GetSymbolInfo(Syntax);
var method = (IMethodSymbol)target.Symbol;
if (method != null)
{
trapFile.expr_call(this, Method.Create(cx, method));
}
if (IsDynamicObjectCreation(cx, Syntax))
{
var name = GetDynamicName(Syntax.Type);
if (name.HasValue)
trapFile.dynamic_member_name(this, name.Value.Text);
else
cx.ModelError(Syntax, "Unable to get name for dynamic object creation.");
}
if (Syntax.Initializer != null)
{
switch (Syntax.Initializer.Kind())
{
case SyntaxKind.CollectionInitializerExpression:
CollectionInitializer.Create(new ExpressionNodeInfo(cx, Syntax.Initializer, this, -1) { Type = Type });
break;
case SyntaxKind.ObjectInitializerExpression:
ObjectInitializer.Create(new ExpressionNodeInfo(cx, Syntax.Initializer, this, -1) { Type = Type });
break;
default:
cx.ModelError("Unhandled initializer in object creation");
break;
}
}
TypeMention.Create(cx, Syntax.Type, this, Type);
}
private static SyntaxToken? GetDynamicName(CSharpSyntaxNode name)
{
switch (name.Kind())
{
case SyntaxKind.IdentifierName:
return ((IdentifierNameSyntax)name).Identifier;
case SyntaxKind.GenericName:
return ((GenericNameSyntax)name).Identifier;
case SyntaxKind.QualifiedName:
// We ignore any qualifiers, for now
return GetDynamicName(((QualifiedNameSyntax)name).Right);
default:
return null;
}
}
}
internal class ImplicitObjectCreation : ObjectCreation<AnonymousObjectCreationExpressionSyntax>
{
public ImplicitObjectCreation(ExpressionNodeInfo info)
: base(info.SetKind(ExprKind.OBJECT_CREATION)) { }
public static Expression Create(ExpressionNodeInfo info) =>
new ImplicitObjectCreation(info).TryPopulate();
protected override void PopulateExpression(TextWriter trapFile)
{
var target = cx.GetSymbolInfo(Syntax);
var method = (IMethodSymbol)target.Symbol;
if (method != null)
{
trapFile.expr_call(this, Method.Create(cx, method));
}
var child = 0;
var objectInitializer = Syntax.Initializers.Any() ?
new Expression(new ExpressionInfo(cx, Type, Location, ExprKind.OBJECT_INIT, this, -1, false, null)) :
null;
foreach (var init in Syntax.Initializers)
{
// Create an "assignment"
var property = cx.GetModel(init).GetDeclaredSymbol(init);
var propEntity = Property.Create(cx, property);
var type = Entities.Type.Create(cx, property.GetAnnotatedType());
var loc = cx.Create(init.GetLocation());
var assignment = new Expression(new ExpressionInfo(cx, type, loc, ExprKind.SIMPLE_ASSIGN, objectInitializer, child++, false, null));
Create(cx, init.Expression, assignment, 0);
Property.Create(cx, property);
var access = new Expression(new ExpressionInfo(cx, type, loc, ExprKind.PROPERTY_ACCESS, assignment, 1, false, null));
trapFile.expr_access(access, propEntity);
}
}
}
}

View File

@@ -0,0 +1,50 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Semmle.Extraction.Entities;
using Semmle.Extraction.Kinds;
using System.IO;
namespace Semmle.Extraction.CSharp.Entities.Expressions
{
internal class AnonymousObjectCreation : Expression<AnonymousObjectCreationExpressionSyntax>
{
public AnonymousObjectCreation(ExpressionNodeInfo info)
: base(info.SetKind(ExprKind.OBJECT_CREATION)) { }
public static Expression Create(ExpressionNodeInfo info) =>
new AnonymousObjectCreation(info).TryPopulate();
protected override void PopulateExpression(TextWriter trapFile)
{
var target = cx.GetSymbolInfo(Syntax);
var method = (IMethodSymbol)target.Symbol;
if (method != null)
{
trapFile.expr_call(this, Method.Create(cx, method));
}
var child = 0;
var objectInitializer = Syntax.Initializers.Any() ?
new Expression(new ExpressionInfo(cx, Type, Location, ExprKind.OBJECT_INIT, this, -1, false, null)) :
null;
foreach (var init in Syntax.Initializers)
{
// Create an "assignment"
var property = cx.GetModel(init).GetDeclaredSymbol(init);
var propEntity = Property.Create(cx, property);
var type = Entities.Type.Create(cx, property.GetAnnotatedType());
var loc = cx.Create(init.GetLocation());
var assignment = new Expression(new ExpressionInfo(cx, type, loc, ExprKind.SIMPLE_ASSIGN, objectInitializer, child++, false, null));
Create(cx, init.Expression, assignment, 0);
Property.Create(cx, property);
var access = new Expression(new ExpressionInfo(cx, type, loc, ExprKind.PROPERTY_ACCESS, assignment, 1, false, null));
trapFile.expr_access(access, propEntity);
}
}
}
}

View File

@@ -0,0 +1,75 @@
using System.IO;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Semmle.Extraction.Kinds;
namespace Semmle.Extraction.CSharp.Entities.Expressions
{
internal abstract class BaseObjectCreation<TExpressionSyntax> : Expression<TExpressionSyntax>
where TExpressionSyntax : BaseObjectCreationExpressionSyntax
{
protected BaseObjectCreation(ExpressionNodeInfo info)
: base(info.SetKind(GetKind(info.Context, (BaseObjectCreationExpressionSyntax)info.Node)))
{
}
protected override void PopulateExpression(TextWriter trapFile)
{
if (Syntax.ArgumentList != null)
{
PopulateArguments(trapFile, Syntax.ArgumentList, 0);
}
var target = cx.GetModel(Syntax).GetSymbolInfo(Syntax);
if (target.Symbol is IMethodSymbol method)
{
trapFile.expr_call(this, Method.Create(cx, method));
}
if (IsDynamicObjectCreation(cx, Syntax))
{
if (cx.GetModel(Syntax).GetTypeInfo(Syntax).Type is INamedTypeSymbol type &&
!string.IsNullOrEmpty(type.Name))
{
trapFile.dynamic_member_name(this, type.Name);
}
else
{
cx.ModelError(Syntax, "Unable to get name for dynamic object creation.");
}
}
if (Syntax.Initializer != null)
{
switch (Syntax.Initializer.Kind())
{
case SyntaxKind.CollectionInitializerExpression:
CollectionInitializer.Create(new ExpressionNodeInfo(cx, Syntax.Initializer, this, -1) { Type = Type });
break;
case SyntaxKind.ObjectInitializerExpression:
ObjectInitializer.Create(new ExpressionNodeInfo(cx, Syntax.Initializer, this, -1) { Type = Type });
break;
default:
cx.ModelError("Unhandled initializer in object creation");
break;
}
}
}
private static ExprKind GetKind(Context cx, BaseObjectCreationExpressionSyntax node)
{
var type = cx.GetModel(node).GetTypeInfo(node).Type;
return Entities.Type.IsDelegate(type as INamedTypeSymbol)
? ExprKind.EXPLICIT_DELEGATE_CREATION
: ExprKind.OBJECT_CREATION;
}
private static bool IsDynamicObjectCreation(Context cx, BaseObjectCreationExpressionSyntax node)
{
return node.ArgumentList != null &&
node.ArgumentList.Arguments.Any(arg => IsDynamic(cx, arg.Expression));
}
}
}

View File

@@ -0,0 +1,20 @@
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System.IO;
namespace Semmle.Extraction.CSharp.Entities.Expressions
{
// new Foo(...) { ... }.
internal class ExplicitObjectCreation : BaseObjectCreation<ObjectCreationExpressionSyntax>
{
private ExplicitObjectCreation(ExpressionNodeInfo info) : base(info) { }
public static Expression Create(ExpressionNodeInfo info) => new ExplicitObjectCreation(info).TryPopulate();
protected override void PopulateExpression(TextWriter trapFile)
{
base.PopulateExpression(trapFile);
TypeMention.Create(cx, Syntax.Type, this, Type);
}
}
}

View File

@@ -0,0 +1,19 @@
using System.IO;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace Semmle.Extraction.CSharp.Entities.Expressions
{
internal class ImplicitObjectCreation : BaseObjectCreation<ImplicitObjectCreationExpressionSyntax>
{
private ImplicitObjectCreation(ExpressionNodeInfo info) : base(info) { }
public static Expression Create(ExpressionNodeInfo info) => new ImplicitObjectCreation(info).TryPopulate();
protected override void PopulateExpression(TextWriter trapFile)
{
base.PopulateExpression(trapFile);
trapFile.implicitly_typed_object_creation(this);
}
}
}

View File

@@ -87,6 +87,11 @@ namespace Semmle.Extraction.CSharp.Entities
foreach (var l in Locations)
trapFile.type_location(this, l);
}
if (symbol.IsAnonymousType)
{
trapFile.anonymous_types(this);
}
}
private readonly Lazy<Type[]> typeArgumentsLazy;

View File

@@ -271,6 +271,11 @@ namespace Semmle.Extraction.CSharp
trapFile.WriteTuple("extend", type, super);
}
internal static void anonymous_types(this TextWriter trapFile, Type type)
{
trapFile.WriteTuple("anonymous_types", type);
}
internal static void field_location(this TextWriter trapFile, Field field, Location location)
{
trapFile.WriteTuple("field_location", field, location);
@@ -301,6 +306,11 @@ namespace Semmle.Extraction.CSharp
trapFile.WriteTuple("implicitly_typed_array_creation", array);
}
internal static void implicitly_typed_object_creation(this TextWriter trapFile, Expression expression)
{
trapFile.WriteTuple("implicitly_typed_object_creation", expression);
}
internal static void indexer_location(this TextWriter trapFile, Indexer indexer, Location location)
{
trapFile.WriteTuple("indexer_location", indexer, location);

View File

@@ -0,0 +1,30 @@
using Microsoft.CodeAnalysis;
namespace Semmle.Extraction
{
/// <summary>
/// The scope of symbols in an assembly.
/// </summary>
public class AssemblyScope : IExtractionScope
{
private readonly IAssemblySymbol assembly;
private readonly string filepath;
public AssemblyScope(IAssemblySymbol symbol, string path, bool isOutput)
{
assembly = symbol;
filepath = path;
IsGlobalScope = isOutput;
}
public bool IsGlobalScope { get; }
public bool InFileScope(string path) => path == filepath;
public bool InScope(ISymbol symbol) =>
SymbolEqualityComparer.Default.Equals(symbol.ContainingAssembly, assembly) ||
SymbolEqualityComparer.Default.Equals(symbol, assembly);
public bool FromSource => false;
}
}

View File

@@ -215,7 +215,7 @@ namespace Semmle.Extraction
}
catch (Exception ex) // lgtm[cs/catch-of-all-exceptions]
{
ExtractionError("Uncaught exception", ex.Message, GeneratedLocation.Create(this), ex.StackTrace);
ExtractionError("Uncaught exception", ex.Message, Entities.Location.Create(this), ex.StackTrace);
}
}
}
@@ -250,6 +250,8 @@ namespace Semmle.Extraction
private IExtractionScope scope { get; }
public SyntaxTree? SourceTree => scope is SourceScope sc ? sc.SourceTree : null;
/// <summary>
/// Whether the given symbol needs to be defined in this context.
/// This is the case if the symbol is contained in the source/assembly, or
@@ -451,7 +453,7 @@ namespace Semmle.Extraction
}
else
{
ExtractionError(message, "", GeneratedLocation.Create(this));
ExtractionError(message, "", Entities.Location.Create(this));
}
}
@@ -522,13 +524,21 @@ namespace Semmle.Extraction
Message message;
if (node != null)
{
message = Message.Create(context, ex.Message, node, ex.StackTrace);
}
else if (symbol != null)
{
message = Message.Create(context, ex.Message, symbol, ex.StackTrace);
}
else if (ex is InternalError ie)
{
message = new Message(ie.Text, ie.EntityText, Entities.Location.Create(context, ie.Location), ex.StackTrace);
}
else
message = new Message("Uncaught exception", ex.Message, GeneratedLocation.Create(context), ex.StackTrace);
{
message = new Message("Uncaught exception", ex.Message, Entities.Location.Create(context), ex.StackTrace);
}
context.ExtractionError(message);
}

View File

@@ -14,7 +14,7 @@ namespace Semmle.Extraction.Entities
protected override void Populate(TextWriter trapFile)
{
trapFile.extractor_messages(this, msg.Severity, "C# extractor", msg.Text, msg.EntityText, msg.Location ?? GeneratedLocation.Create(cx), msg.StackTrace);
trapFile.extractor_messages(this, msg.Severity, "C# extractor", msg.Text, msg.EntityText, msg.Location ?? Location.Create(cx), msg.StackTrace);
}
public override TrapStackBehaviour TrapStackBehaviour => TrapStackBehaviour.NoLabel;

View File

@@ -28,7 +28,7 @@ namespace Semmle.Extraction.Entities
public override bool Equals(object? obj) => obj != null && obj.GetType() == typeof(GeneratedLocation);
public static GeneratedLocation Create(Context cx) => GeneratedLocationFactory.Instance.CreateEntity(cx, typeof(GeneratedLocation), null);
public static new GeneratedLocation Create(Context cx) => GeneratedLocationFactory.Instance.CreateEntity(cx, typeof(GeneratedLocation), null);
private class GeneratedLocationFactory : ICachedEntityFactory<string?, GeneratedLocation>
{

View File

@@ -1,4 +1,6 @@
using Microsoft.CodeAnalysis.Text;
namespace Semmle.Extraction.Entities
{
public abstract class Location : CachedEntity<Microsoft.CodeAnalysis.Location?>
@@ -13,6 +15,13 @@ namespace Semmle.Extraction.Entities
? NonGeneratedSourceLocation.Create(cx, loc)
: Assembly.Create(cx, loc);
public static Location Create(Context cx)
{
return cx.SourceTree == null
? GeneratedLocation.Create(cx)
: Create(cx, Microsoft.CodeAnalysis.Location.Create(cx.SourceTree, TextSpan.FromBounds(0, 0)));
}
public override Microsoft.CodeAnalysis.Location? ReportingLocation => symbol;
public override TrapStackBehaviour TrapStackBehaviour => TrapStackBehaviour.OptionalLabel;

View File

@@ -1,78 +0,0 @@
using Microsoft.CodeAnalysis;
using System.Linq;
namespace Semmle.Extraction
{
/// <summary>
/// Defines which entities belong in the trap file
/// for the currently extracted entity. This is used to ensure that
/// trap files do not contain redundant information. Generally a symbol
/// should have an affinity with exactly one trap file, except for constructed
/// symbols.
/// </summary>
public interface IExtractionScope
{
/// <summary>
/// Whether the given symbol belongs in the trap file.
/// </summary>
/// <param name="symbol">The symbol to populate.</param>
bool InScope(ISymbol symbol);
/// <summary>
/// Whether the given file belongs in the trap file.
/// </summary>
/// <param name="path">The path to populate.</param>
bool InFileScope(string path);
bool IsGlobalScope { get; }
bool FromSource { get; }
}
/// <summary>
/// The scope of symbols in an assembly.
/// </summary>
public class AssemblyScope : IExtractionScope
{
private readonly IAssemblySymbol assembly;
private readonly string filepath;
public AssemblyScope(IAssemblySymbol symbol, string path, bool isOutput)
{
assembly = symbol;
filepath = path;
IsGlobalScope = isOutput;
}
public bool IsGlobalScope { get; }
public bool InFileScope(string path) => path == filepath;
public bool InScope(ISymbol symbol) =>
SymbolEqualityComparer.Default.Equals(symbol.ContainingAssembly, assembly) ||
SymbolEqualityComparer.Default.Equals(symbol, assembly);
public bool FromSource => false;
}
/// <summary>
/// The scope of symbols in a source file.
/// </summary>
public class SourceScope : IExtractionScope
{
private readonly SyntaxTree sourceTree;
public SourceScope(SyntaxTree tree)
{
sourceTree = tree;
}
public bool IsGlobalScope => false;
public bool InFileScope(string path) => path == sourceTree.FilePath;
public bool InScope(ISymbol symbol) => symbol.Locations.Any(loc => loc.SourceTree == sourceTree);
public bool FromSource => true;
}
}

View File

@@ -0,0 +1,30 @@
using Microsoft.CodeAnalysis;
namespace Semmle.Extraction
{
/// <summary>
/// Defines which entities belong in the trap file
/// for the currently extracted entity. This is used to ensure that
/// trap files do not contain redundant information. Generally a symbol
/// should have an affinity with exactly one trap file, except for constructed
/// symbols.
/// </summary>
public interface IExtractionScope
{
/// <summary>
/// Whether the given symbol belongs in the trap file.
/// </summary>
/// <param name="symbol">The symbol to populate.</param>
bool InScope(ISymbol symbol);
/// <summary>
/// Whether the given file belongs in the trap file.
/// </summary>
/// <param name="path">The path to populate.</param>
bool InFileScope(string path);
bool IsGlobalScope { get; }
bool FromSource { get; }
}
}

View File

@@ -0,0 +1,27 @@
using Microsoft.CodeAnalysis;
using System.Linq;
namespace Semmle.Extraction
{
/// <summary>
/// The scope of symbols in a source file.
/// </summary>
public class SourceScope : IExtractionScope
{
public SyntaxTree SourceTree { get; }
public SourceScope(SyntaxTree tree)
{
SourceTree = tree;
}
public bool IsGlobalScope => false;
public bool InFileScope(string path) => path == SourceTree.FilePath;
public bool InScope(ISymbol symbol) => symbol.Locations.Any(loc => loc.SourceTree == SourceTree);
public bool FromSource => true;
}
}

View File

@@ -0,0 +1,41 @@
using System.Collections.Generic;
using System.Linq;
namespace Semmle.Util
{
public static class StringExtensions
{
public static (string, string) Split(this string self, int index0)
{
var split = self.Split(new[] { index0 });
return (split[0], split[1]);
}
public static (string, string, string) Split(this string self, int index0, int index1)
{
var split = self.Split(new[] { index0, index1 });
return (split[0], split[1], split[2]);
}
public static (string, string, string, string) Split(this string self, int index0, int index1, int index2)
{
var split = self.Split(new[] { index0, index1, index2 });
return (split[0], split[1], split[2], split[4]);
}
private static List<string> Split(this string self, params int[] indices)
{
var ret = new List<string>();
var previousIndex = 0;
foreach (var index in indices.OrderBy(i => i))
{
ret.Add(self.Substring(previousIndex, index - previousIndex));
previousIndex = index;
}
ret.Add(self.Substring(previousIndex));
return ret;
}
}
}

View File

@@ -4,28 +4,46 @@
<qhelp>
<overview>
<p>
The format string supplied to formatting methods (such as <code>string.Format()</code>)
must be formatted correctly, otherwise the exception <code>System.FormatException</code>
will be thrown.
When using string formatting methods (such as <code>string.Format()</code>), the following
should be taken into account:
</p>
<ol>
<li>
The formatting string must be formatted correctly, otherwise the exception
<code>System.FormatException</code> will be thrown.
</li>
<li>
All passed arguments should be used by the formatting string, otherwise such
arguments will be ignored.
</li>
<li>
Missing arguments will result in a <code>System.FormatException</code> exception
being thrown.
</li>
</ol>
</overview>
<recommendation>
<p>
Change the format string so that it is correctly formatted. Ensure that each
format item adheres to the syntax:
</p>
<blockquote>
<p><b>{</b><i>index</i>[<b>,</b><i>alignment</i>][<b>:</b><i>formatString</i>]<b>}</b></p>
</blockquote>
<p>
When literals <code>{</code> or <code>}</code> are required, replace them with <code>{{</code> and
<code>}}</code>, respectively, or supply them as arguments.
</p>
<ol>
<li>
Change the format string so that it is correctly formatted. Ensure that each
format item adheres to the syntax:
<blockquote>
<p><b>{</b><i>index</i>[<b>,</b><i>alignment</i>][<b>:</b><i>formatString</i>]<b>}</b></p>
</blockquote>
When literals <code>{</code> or <code>}</code> are required, replace them with <code>{{</code> and
<code>}}</code>, respectively, or supply them as arguments.
</li>
<li>
Change the format string to use the highlighted argument, or remove the unnecessary argument.
</li>
<li>
Supply the correct number of arguments to the format method, or change the format string
to use the correct arguments.
</li>
</ol>
</recommendation>
@@ -40,8 +58,39 @@ literals are not properly escaped.
In the revised example, the literals are properly escaped.
</p>
<sample src="FormatInvalidGood.cs" />
</example>
<example>
<p>
Here are three examples where the format string does not use all the arguments.
</p>
<sample src="FormatUnusedArgumentBad.cs"/>
<ul>
<li>On line 7, the second argument (<code>ex.HResult</code>) is not logged.</li>
<li>On line 8, the first argument (<code>ex</code>) is not logged but the second
argument (<code>ex.HResult</code>) is logged twice.</li>
<li>On line 9, a C-style format string is used, which is incorrect, and neither
argument will be logged.</li>
</ul>
</example>
<example>
<p>
Here are two examples where the call to <code>String.Format()</code> is missing arguments.
</p>
<sample src="FormatMissingArgumentBad.cs"/>
<ul>
<li>On line 7, the second argument (<code>last</code>) is not supplied.</li>
<li>On line 8, the format items are numbered <code>{1}</code> and <code>{2}</code>,
instead of <code>{0}</code> and <code>{1}</code> as they should be.</li>
</ul>
<p>
In the revised example, both arguments are supplied.
</p>
<sample src="FormatMissingArgumentGood.cs"/>
</example>
<references>
<li>MSDN: <a href="https://msdn.microsoft.com/en-us/library/system.string.format.aspx">String.Format Method</a>.</li>
<li>Microsoft: <a href="https://docs.microsoft.com/en-us/dotnet/standard/base-types/composite-formatting">Composite Formatting</a>.</li>

View File

@@ -1,18 +1,86 @@
/**
* @name Invalid format string
* @description Using a format string with an incorrect format causes a 'System.FormatException'.
* @name Invalid string formatting
* @description Calling 'string.Format()' with either an invalid format string or incorrect
* number of arguments may result in dropped arguments or a 'System.FormatException'.
* @kind path-problem
* @problem.severity error
* @precision high
* @id cs/invalid-format-string
* @id cs/invalid-string-formatting
* @tags reliability
* maintainability
*/
import csharp
import semmle.code.csharp.frameworks.Format
import FormatFlow
import DataFlow::PathGraph
from FormatCall s, InvalidFormatString src, PathNode source, PathNode sink
where hasFlowPath(src, source, s, sink)
select src, source, sink, "Invalid format string used in $@ formatting call.", s, "this"
private class FormatConfiguration extends DataFlow::Configuration {
FormatConfiguration() { this = "format" }
override predicate isSource(DataFlow::Node n) { n.asExpr() instanceof StringLiteral }
override predicate isSink(DataFlow::Node n) {
exists(FormatCall c | n.asExpr() = c.getFormatExpr())
}
}
private predicate invalidFormatString(
InvalidFormatString src, DataFlow::PathNode source, DataFlow::PathNode sink, string msg,
FormatCall call, string callString
) {
source.getNode().asExpr() = src and
sink.getNode().asExpr() = call.getFormatExpr() and
any(FormatConfiguration conf).hasFlowPath(source, sink) and
call.hasInsertions() and
msg = "Invalid format string used in $@ formatting call." and
callString = "this"
}
private predicate unusedArgument(
FormatCall call, DataFlow::PathNode source, DataFlow::PathNode sink, string msg,
ValidFormatString src, string srcString, Expr unusedExpr, string unusedString
) {
exists(int unused |
source.getNode().asExpr() = src and
sink.getNode().asExpr() = call.getFormatExpr() and
any(FormatConfiguration conf).hasFlowPath(source, sink) and
unused = call.getASuppliedArgument() and
not unused = src.getAnInsert() and
not src.getValue() = "" and
msg = "The $@ ignores $@." and
srcString = "format string" and
unusedExpr = call.getSuppliedExpr(unused) and
unusedString = "this supplied value"
)
}
private predicate missingArgument(
FormatCall call, DataFlow::PathNode source, DataFlow::PathNode sink, string msg,
ValidFormatString src, string srcString
) {
exists(int used, int supplied |
source.getNode().asExpr() = src and
sink.getNode().asExpr() = call.getFormatExpr() and
any(FormatConfiguration conf).hasFlowPath(source, sink) and
used = src.getAnInsert() and
supplied = call.getSuppliedArguments() and
used >= supplied and
msg = "Argument '{" + used + "}' has not been supplied to $@ format string." and
srcString = "this"
)
}
from
Element alert, DataFlow::PathNode source, DataFlow::PathNode sink, string msg, Element extra1,
string extra1String, Element extra2, string extra2String
where
invalidFormatString(alert, source, sink, msg, extra1, extra1String) and
extra2 = extra1 and
extra2String = extra1String
or
unusedArgument(alert, source, sink, msg, extra1, extra1String, extra2, extra2String)
or
missingArgument(alert, source, sink, msg, extra1, extra1String) and
extra2 = extra1 and
extra2String = extra1String
select alert, source, sink, msg, extra1, extra1String, extra2, extra2String

View File

@@ -1,9 +1,9 @@
using System;
class Bad
class Bad1
{
string GenerateEmptyClass(string c)
{
return string.Format("class {0} { }");
return string.Format("class {0} { }", "C");
}
}

View File

@@ -1,9 +1,9 @@
using System;
class Good
class Good1
{
string GenerateEmptyClass(string c)
{
return string.Format("class {0} {{ }}");
return string.Format("class {0} {{ }}", "C");
}
}

View File

@@ -1,41 +0,0 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
Formatting methods (such as <code>String.Format()</code>) that are missing arguments will
throw the exception <code>System.FormatException</code>. This is caused by the format
string not matching the actual arguments supplied or an incorrect format string.
</p>
</overview>
<recommendation>
<p>
Supply the correct number of arguments to the format method, or change the format string
to use the correct arguments.
</p>
</recommendation>
<example>
<p>
Here are two examples where the call to <code>String.Format()</code> is missing arguments.
</p>
<sample src="FormatMissingArgumentBad.cs"/>
<ul>
<li>On line 5, the second argument (<code>last</code>) is not supplied.</li>
<li>On line 6, the format items are numbered <code>{1}</code> and <code>{2}</code>,
instead of <code>{0}</code> and <code>{1}</code> as they should be.</li>
</ul>
<p>
In the revised example, both arguments are supplied.
</p>
<sample src="FormatMissingArgumentGood.cs"/>
</example>
<references>
<li>MSDN: <a href="https://msdn.microsoft.com/en-us/library/system.string.format.aspx">String.Format Method</a>.</li>
<li>Microsoft: <a href="https://docs.microsoft.com/en-us/dotnet/standard/base-types/composite-formatting">Composite Formatting</a>.</li>
</references>
</qhelp>

View File

@@ -1,24 +0,0 @@
/**
* @name Missing format argument
* @description Supplying too few arguments to a format string causes a 'System.FormatException'.
* @kind path-problem
* @problem.severity error
* @precision high
* @id cs/format-argument-missing
* @tags reliability
* maintainability
*/
import csharp
import semmle.code.csharp.frameworks.Format
import FormatFlow
from
FormatCall format, ValidFormatString src, int used, int supplied, PathNode source, PathNode sink
where
hasFlowPath(src, source, format, sink) and
used = src.getAnInsert() and
supplied = format.getSuppliedArguments() and
used >= supplied
select format, source, sink, "Argument '{" + used + "}' has not been supplied to $@ format string.",
src, "this"

View File

@@ -1,6 +1,6 @@
using System;
class Bad
class Bad3
{
void Hello(string first, string last)
{

View File

@@ -1,6 +1,6 @@
using System;
class Good
class Good3
{
void Hello(string first, string last)
{

View File

@@ -1,37 +0,0 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
Arguments which are passed to formatting methods (such as <code>String.Format()</code>)
but are not used, are either unnecessary or mean that the format string is incorrect. The result
is that the argument will be ignored, which may not be the intended behavior.
</p>
</overview>
<recommendation>
<p>
Change the format string to use the highlighted argument, or remove the unnecessary argument.
</p>
</recommendation>
<example>
<p>
Here are three examples where the format string does not use all the arguments.
</p>
<sample src="FormatUnusedArgumentBad.cs"/>
<ul>
<li>On line 5, the second argument (<code>ex.HResult</code>) is not logged.</li>
<li>On line 6, the first argument (<code>ex</code>) is not logged but the second
argument (<code>ex.HResult</code>) is logged twice.</li>
<li>On line 4, a C-style format string is used, which is incorrect, and neither
argument will be logged.</li>
</ul>
</example>
<references>
<li>MSDN: <a href="https://msdn.microsoft.com/en-us/library/system.string.format.aspx">String.Format Method</a>.</li>
<li>Microsoft: <a href="https://docs.microsoft.com/en-us/dotnet/standard/base-types/composite-formatting">Composite Formatting</a>.</li>
</references>
</qhelp>

View File

@@ -1,22 +0,0 @@
/**
* @name Unused format argument
* @description Supplying more arguments than are required for a format string may indicate an error in the format string.
* @kind path-problem
* @problem.severity warning
* @precision high
* @id cs/format-argument-unused
* @tags reliability
* maintainability
*/
import csharp
import semmle.code.csharp.frameworks.Format
import FormatFlow
from FormatCall format, int unused, ValidFormatString src, PathNode source, PathNode sink
where
hasFlowPath(src, source, format, sink) and
unused = format.getAnUnusedArgument(src) and
not src.getValue() = ""
select format, source, sink, "The $@ ignores $@.", src, "format string",
format.getSuppliedExpr(unused), "this supplied value"

View File

@@ -1,6 +1,6 @@
using System;
class Bad
class Bad2
{
void M(Exception ex)
{

View File

@@ -27,7 +27,7 @@ class FormatStringConfiguration extends TaintTracking::Configuration {
}
override predicate isSink(DataFlow::Node sink) {
sink.asExpr() = any(FormatCall call).getFormatExpr()
sink.asExpr() = any(FormatCall call | call.hasInsertions()).getFormatExpr()
}
}

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