mirror of
https://github.com/github/codeql.git
synced 2026-05-18 21:27:08 +02:00
Compare commits
1127 Commits
codeql-cli
...
criemen/pu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
22aea47604 | ||
|
|
bc1283c715 | ||
|
|
158dafa7d0 | ||
|
|
80364e9570 | ||
|
|
bf2ae9890f | ||
|
|
9d21e2cda3 | ||
|
|
13a7d9acb6 | ||
|
|
c483a4bf04 | ||
|
|
0ba3cd96f5 | ||
|
|
2f7766a557 | ||
|
|
df24e5982a | ||
|
|
e8b9d7e6fa | ||
|
|
15667dcf1e | ||
|
|
1d8d45b3aa | ||
|
|
bfc95c6f13 | ||
|
|
d01d657f89 | ||
|
|
170e2231d4 | ||
|
|
b639f60fa6 | ||
|
|
1a4c07a1ac | ||
|
|
f1047606ad | ||
|
|
a7a12f17b5 | ||
|
|
daf19a2468 | ||
|
|
ad1188be1f | ||
|
|
a6ce24dc39 | ||
|
|
cc4f8e038d | ||
|
|
03cf9b702c | ||
|
|
a568873a8e | ||
|
|
e66cce7fe1 | ||
|
|
e483948c19 | ||
|
|
384649b336 | ||
|
|
2848ccf0e2 | ||
|
|
08c0d8ec60 | ||
|
|
9aeebc6f39 | ||
|
|
a8201a19ae | ||
|
|
3a224871ee | ||
|
|
19e2af8a1d | ||
|
|
92699d18ac | ||
|
|
dcadda23cd | ||
|
|
f06c3fddd9 | ||
|
|
18fc92247b | ||
|
|
763d53afed | ||
|
|
78dc6502f5 | ||
|
|
b1329fd806 | ||
|
|
42a9962519 | ||
|
|
4378924785 | ||
|
|
20ea9255a1 | ||
|
|
ea3cc51286 | ||
|
|
47f679dc34 | ||
|
|
8dc9c95be4 | ||
|
|
2dd42f7aa5 | ||
|
|
2b7394cd42 | ||
|
|
499c4df79b | ||
|
|
a2994c073a | ||
|
|
8cc118f781 | ||
|
|
b008f98782 | ||
|
|
451f601a65 | ||
|
|
98d2c848bb | ||
|
|
74e446ea3b | ||
|
|
56dff8540f | ||
|
|
066f3b61a2 | ||
|
|
410543f26b | ||
|
|
e71cf0ff1d | ||
|
|
5dbb91f508 | ||
|
|
c4d33fbede | ||
|
|
e8d064e291 | ||
|
|
ea2b73bda2 | ||
|
|
62faab320b | ||
|
|
5076b1a214 | ||
|
|
b9a7f6a8f7 | ||
|
|
761f9cac97 | ||
|
|
e2a4c2aa1b | ||
|
|
6ffa821aa3 | ||
|
|
21ff705b73 | ||
|
|
1af3374322 | ||
|
|
1b22e0879a | ||
|
|
4617c055e2 | ||
|
|
a50584c665 | ||
|
|
44edff7661 | ||
|
|
9c823d708d | ||
|
|
c328957bf3 | ||
|
|
7e13c88f1d | ||
|
|
72493a6bd1 | ||
|
|
950e8c85e7 | ||
|
|
a158e40cc2 | ||
|
|
90bb0ba33f | ||
|
|
8f207e7a06 | ||
|
|
3c47c1137d | ||
|
|
d406646414 | ||
|
|
b32fb1d961 | ||
|
|
3f020a96e4 | ||
|
|
25b7214064 | ||
|
|
723850394d | ||
|
|
f1616d3c26 | ||
|
|
45167cf9f1 | ||
|
|
04c26056a1 | ||
|
|
4a85cc98e5 | ||
|
|
24da489bab | ||
|
|
c6ee25497e | ||
|
|
eb9c734120 | ||
|
|
332a3dbc61 | ||
|
|
253965096b | ||
|
|
964b3f2339 | ||
|
|
586e90043a | ||
|
|
ab7d30a464 | ||
|
|
38e1065b24 | ||
|
|
64b8f97d39 | ||
|
|
28c8611f93 | ||
|
|
b44ad2d39f | ||
|
|
8ea1d1376f | ||
|
|
7e10f212cd | ||
|
|
a1cb419902 | ||
|
|
7cf61ffb18 | ||
|
|
eb2e587637 | ||
|
|
c3caafcca1 | ||
|
|
b49a61705c | ||
|
|
32e8b5c667 | ||
|
|
145873f562 | ||
|
|
b7f0b7afb5 | ||
|
|
538d556465 | ||
|
|
04c0475251 | ||
|
|
100166fa53 | ||
|
|
d76182b945 | ||
|
|
829707528b | ||
|
|
cf134331a9 | ||
|
|
61cd93fcf3 | ||
|
|
e699b18928 | ||
|
|
1b1367eec0 | ||
|
|
58ef9f4e3e | ||
|
|
8814e6e2bd | ||
|
|
827d15aaa0 | ||
|
|
ab5e1f8a69 | ||
|
|
99335e5f29 | ||
|
|
49ffa8bf07 | ||
|
|
027e5e7291 | ||
|
|
904799b674 | ||
|
|
34c447939e | ||
|
|
869bf8afc6 | ||
|
|
79c6834aa3 | ||
|
|
f72afdc7cb | ||
|
|
e65a62c727 | ||
|
|
9105faa3fd | ||
|
|
faa2dcee24 | ||
|
|
a62ce4c51d | ||
|
|
5b572a2c2a | ||
|
|
ed0717af96 | ||
|
|
52717f8500 | ||
|
|
513a319149 | ||
|
|
b34fb7c26b | ||
|
|
1758a1e04b | ||
|
|
9456eaf5d3 | ||
|
|
372746ffd4 | ||
|
|
34f91f8129 | ||
|
|
65c654e946 | ||
|
|
10e1bfcee5 | ||
|
|
4d5f05a8dd | ||
|
|
1b9e8ae86f | ||
|
|
bbc359ebde | ||
|
|
76e740bc1d | ||
|
|
5eaaf02bf5 | ||
|
|
f125e732b0 | ||
|
|
c30ca4d9f6 | ||
|
|
0d814e0e20 | ||
|
|
f7e6bf7ef5 | ||
|
|
f8e17fe8cc | ||
|
|
76946184de | ||
|
|
e1c85e3581 | ||
|
|
8cc9838628 | ||
|
|
d5aa08dbf3 | ||
|
|
86e2b845f3 | ||
|
|
eab1569342 | ||
|
|
c816412795 | ||
|
|
84b7319688 | ||
|
|
f95ccd242f | ||
|
|
fcdac0aeb0 | ||
|
|
1322ce35d6 | ||
|
|
e857eedd4a | ||
|
|
462e564c19 | ||
|
|
e7092b403b | ||
|
|
d6acea1d0c | ||
|
|
52ceb7fb89 | ||
|
|
bcddef8f32 | ||
|
|
e721399eec | ||
|
|
674e65e134 | ||
|
|
4d54c09a6f | ||
|
|
b5d6bb9097 | ||
|
|
cfc419b639 | ||
|
|
304ad33837 | ||
|
|
231918f612 | ||
|
|
526204dc43 | ||
|
|
20a1e22c44 | ||
|
|
4dfcdbccd7 | ||
|
|
aab5da0435 | ||
|
|
9e09c5a6cf | ||
|
|
279b2c7a95 | ||
|
|
811c175556 | ||
|
|
a51d24cbab | ||
|
|
d68d2cca41 | ||
|
|
0cbedfb28c | ||
|
|
49aba25852 | ||
|
|
53d4a10108 | ||
|
|
59fb9cc862 | ||
|
|
4f10cb5fa0 | ||
|
|
57ff30c5f3 | ||
|
|
1ccea884ff | ||
|
|
f6f594e4b5 | ||
|
|
fb74a2a170 | ||
|
|
4d42a88c3c | ||
|
|
9842445b52 | ||
|
|
694b3b0fce | ||
|
|
8cba06a25c | ||
|
|
088f8297b6 | ||
|
|
ab23d0ad23 | ||
|
|
1a53b923a6 | ||
|
|
944f8842b7 | ||
|
|
64145cf181 | ||
|
|
f00ad7c0e7 | ||
|
|
5e2e224c9c | ||
|
|
e8120afef9 | ||
|
|
8bb4193704 | ||
|
|
e359744b8b | ||
|
|
896fb87d1d | ||
|
|
724d026238 | ||
|
|
eaba798e34 | ||
|
|
52db1c1253 | ||
|
|
fa06d88642 | ||
|
|
24f8b5f203 | ||
|
|
7b89c6c7f7 | ||
|
|
9bc04e7591 | ||
|
|
2a40a2a57f | ||
|
|
c5403f4249 | ||
|
|
3fd6bc9b5c | ||
|
|
486226814a | ||
|
|
12c28f3f68 | ||
|
|
871fd9aba3 | ||
|
|
5b1b30a171 | ||
|
|
880d56c576 | ||
|
|
9c8945f626 | ||
|
|
6575927630 | ||
|
|
2dcb55cc42 | ||
|
|
65645821be | ||
|
|
0338ffd125 | ||
|
|
61580da14d | ||
|
|
3989717878 | ||
|
|
336c7de643 | ||
|
|
8b91914826 | ||
|
|
575b66a054 | ||
|
|
dd95a2abab | ||
|
|
aab43afd81 | ||
|
|
99940a6084 | ||
|
|
c11fac81fd | ||
|
|
85e71c30dc | ||
|
|
c529988b43 | ||
|
|
8e95395382 | ||
|
|
61fb89721a | ||
|
|
8198b1a6ef | ||
|
|
ab3d21ce7e | ||
|
|
9f27eb3eda | ||
|
|
efda63d37a | ||
|
|
4eea214cb4 | ||
|
|
b8f62ae4d5 | ||
|
|
0f7325ec68 | ||
|
|
5fe3ab7890 | ||
|
|
53c2d2f1e7 | ||
|
|
07d51a55fd | ||
|
|
41b95a1938 | ||
|
|
cfb0a862c1 | ||
|
|
9b23635d0a | ||
|
|
bbdc2752ac | ||
|
|
5c74bebe6c | ||
|
|
757cf8d43a | ||
|
|
b53fa0f7f3 | ||
|
|
d85f81d699 | ||
|
|
7c7bdb2242 | ||
|
|
92b3eda12d | ||
|
|
a8549d2e23 | ||
|
|
54c9aea251 | ||
|
|
e71d038d7f | ||
|
|
c123513311 | ||
|
|
bab2dd4427 | ||
|
|
73df4fa920 | ||
|
|
95ddd6ec74 | ||
|
|
51e7f3be1a | ||
|
|
5b184c179a | ||
|
|
cba4ba042c | ||
|
|
a33393d452 | ||
|
|
7cb8a6c52f | ||
|
|
6815bcaa80 | ||
|
|
105984f7de | ||
|
|
6233da3e40 | ||
|
|
e68165fcb7 | ||
|
|
535e6db40f | ||
|
|
9b51e0e0ee | ||
|
|
30b5d5092d | ||
|
|
1dd3a0fd76 | ||
|
|
077a2e3479 | ||
|
|
ca9567debc | ||
|
|
5d5e31378b | ||
|
|
5cde3fa697 | ||
|
|
39a8b49222 | ||
|
|
b209fc67cb | ||
|
|
77128de105 | ||
|
|
7a1b85aa56 | ||
|
|
eb0621ab7a | ||
|
|
c0cf1c7c8c | ||
|
|
17990da205 | ||
|
|
d5475c4a89 | ||
|
|
471303bd7c | ||
|
|
2132c7bf96 | ||
|
|
7ca54a6f94 | ||
|
|
e8cb8b4f81 | ||
|
|
ba64cf3016 | ||
|
|
3c91333d0b | ||
|
|
d9e8e0e00a | ||
|
|
ff85db36e2 | ||
|
|
8f0b88497a | ||
|
|
31c427e64c | ||
|
|
e64a2d6c9c | ||
|
|
807e6795a7 | ||
|
|
880262d462 | ||
|
|
95ff5bae65 | ||
|
|
6cbe16e0c2 | ||
|
|
75615f2817 | ||
|
|
c07bf65eb6 | ||
|
|
8def1c2c13 | ||
|
|
1b90f22e84 | ||
|
|
ecdf62376d | ||
|
|
f5431abb10 | ||
|
|
73cc211779 | ||
|
|
08e08a2b3a | ||
|
|
657402b42f | ||
|
|
ecbf7aef18 | ||
|
|
669fc925e0 | ||
|
|
8a04840f93 | ||
|
|
f4e4e238ba | ||
|
|
8f682ef4e4 | ||
|
|
96d69ca49c | ||
|
|
8a261b7e7a | ||
|
|
daea674095 | ||
|
|
315f439135 | ||
|
|
06d8892e03 | ||
|
|
e22159ab5d | ||
|
|
355c7d9b41 | ||
|
|
00baccbc15 | ||
|
|
81dea9f89a | ||
|
|
76067cb12d | ||
|
|
e4cf7df38f | ||
|
|
b8b3689251 | ||
|
|
0693bf9e75 | ||
|
|
1aafc377ad | ||
|
|
abcd9165b4 | ||
|
|
0bc6934bfc | ||
|
|
4ae82ac215 | ||
|
|
ccad70897d | ||
|
|
318d954536 | ||
|
|
3239af9973 | ||
|
|
f7113e0105 | ||
|
|
ca2d94b297 | ||
|
|
2cff081f2b | ||
|
|
9055d9567a | ||
|
|
f99cb3f649 | ||
|
|
dfdd79d8cf | ||
|
|
8ec4f0b5bd | ||
|
|
b912918d8b | ||
|
|
ef88f3ed09 | ||
|
|
f5d4b2e6cd | ||
|
|
8b0f3af5b1 | ||
|
|
54793cfa01 | ||
|
|
d909f2bc4f | ||
|
|
7f27127301 | ||
|
|
520a2c96ff | ||
|
|
12b9b805e2 | ||
|
|
6eb07a7a7e | ||
|
|
9bfb189fa7 | ||
|
|
322fa36359 | ||
|
|
4a47e11a16 | ||
|
|
83249cd9c2 | ||
|
|
c6185b30ba | ||
|
|
9ce08c586c | ||
|
|
a8d3226e99 | ||
|
|
94212d103e | ||
|
|
608791fd7f | ||
|
|
7d92ec5ddf | ||
|
|
9157dee0db | ||
|
|
677520aa8e | ||
|
|
16dcc0969b | ||
|
|
09e59ccf44 | ||
|
|
a8f2cbc2b1 | ||
|
|
c00d0d302d | ||
|
|
5a7a1dc92e | ||
|
|
c480431ec0 | ||
|
|
f7fc2e0b00 | ||
|
|
397e641f2f | ||
|
|
00cbfaf40e | ||
|
|
42653b5fec | ||
|
|
58bbfe694f | ||
|
|
d9c7401ea2 | ||
|
|
ed7538d0b9 | ||
|
|
5b89bd23c7 | ||
|
|
b68abab12a | ||
|
|
d05c5e3d94 | ||
|
|
301a6cc191 | ||
|
|
acd0fa4b7b | ||
|
|
85a4dd0325 | ||
|
|
dc4604f5a5 | ||
|
|
40b6e1624f | ||
|
|
22e843abc6 | ||
|
|
3c70a2d7df | ||
|
|
541effb8cb | ||
|
|
248ffa15a2 | ||
|
|
072f19008a | ||
|
|
93988e5834 | ||
|
|
e0c2a43780 | ||
|
|
5843326b5c | ||
|
|
3a2b0a2feb | ||
|
|
fd55713006 | ||
|
|
c6372d5822 | ||
|
|
07dd6d5c8d | ||
|
|
708d12624f | ||
|
|
3eddd3114f | ||
|
|
61ce7252e6 | ||
|
|
b86aeb68ae | ||
|
|
cf025e1924 | ||
|
|
a200ced2d6 | ||
|
|
c04e59611b | ||
|
|
c5a87c95d8 | ||
|
|
4f22b91e73 | ||
|
|
797f675285 | ||
|
|
c32c810ae7 | ||
|
|
32fe084630 | ||
|
|
ba054bd428 | ||
|
|
0715c4ac5a | ||
|
|
99928b82ed | ||
|
|
377c6b4cc8 | ||
|
|
2939c89f7a | ||
|
|
bb6cc92728 | ||
|
|
d736426529 | ||
|
|
806d42852c | ||
|
|
8e251ee54f | ||
|
|
5228d94d42 | ||
|
|
7d9f78b2de | ||
|
|
2590d8a27f | ||
|
|
7df8b1ba51 | ||
|
|
7e839792da | ||
|
|
98106f31c1 | ||
|
|
676bcf39a5 | ||
|
|
a28f87fff0 | ||
|
|
94364f724e | ||
|
|
607ed2efb4 | ||
|
|
a304e2d8d6 | ||
|
|
cb85a756a0 | ||
|
|
e7886d0e57 | ||
|
|
1c611fecde | ||
|
|
51bc8e917e | ||
|
|
0fa5a1f274 | ||
|
|
15bb846a5f | ||
|
|
5c454bdd8c | ||
|
|
f194c70e8a | ||
|
|
179270ffc1 | ||
|
|
1f78882cdc | ||
|
|
f0f6c229f6 | ||
|
|
2f6dd2ab81 | ||
|
|
6ec223c515 | ||
|
|
8ccedd658a | ||
|
|
401717d739 | ||
|
|
527409d05f | ||
|
|
d3d2e2188d | ||
|
|
141af7cc87 | ||
|
|
6e3dddede0 | ||
|
|
683fe26034 | ||
|
|
ea1b8a3999 | ||
|
|
5f0efc19fa | ||
|
|
0dfd336729 | ||
|
|
b0758fd109 | ||
|
|
d62e888b86 | ||
|
|
50775d0c53 | ||
|
|
9874d40d29 | ||
|
|
044ee9b08a | ||
|
|
8b23f6db10 | ||
|
|
3c2206728d | ||
|
|
8c87cb83bb | ||
|
|
c9ce642e7a | ||
|
|
d18cdee0bc | ||
|
|
b78537dd74 | ||
|
|
e78091e9d0 | ||
|
|
c10e00d389 | ||
|
|
bf61114284 | ||
|
|
800d7546fa | ||
|
|
d6c57de650 | ||
|
|
d66494dcb0 | ||
|
|
86d6b8ef21 | ||
|
|
6147a38bea | ||
|
|
67fb866efa | ||
|
|
0f387eeac2 | ||
|
|
4b0a217420 | ||
|
|
3195f0c828 | ||
|
|
d98ccdfa06 | ||
|
|
c8b02241af | ||
|
|
a707e14495 | ||
|
|
146d84bbf8 | ||
|
|
1d45e3a558 | ||
|
|
70e9c48a47 | ||
|
|
dcc4ad2550 | ||
|
|
54cadcfe9b | ||
|
|
3716b8c6a0 | ||
|
|
9f7edf378e | ||
|
|
4f46ce1133 | ||
|
|
862942f3d8 | ||
|
|
adc82b553b | ||
|
|
c2252e12ac | ||
|
|
bab6ecf3bb | ||
|
|
f95b33049e | ||
|
|
2482519cd3 | ||
|
|
e44d4c45aa | ||
|
|
bfa189e2ac | ||
|
|
19b2e56d02 | ||
|
|
14d88eb3ce | ||
|
|
b754706e44 | ||
|
|
9db32f4d26 | ||
|
|
acb2bbb2a3 | ||
|
|
06f987ad58 | ||
|
|
925a2cca7e | ||
|
|
3ad9c026a5 | ||
|
|
7d9a68bf17 | ||
|
|
0468c5d0bf | ||
|
|
e55f2c5309 | ||
|
|
d7c784ef2f | ||
|
|
fb376a1cfd | ||
|
|
baa31e1469 | ||
|
|
8d962a5c28 | ||
|
|
389df35fb7 | ||
|
|
aa80dd41da | ||
|
|
553871678a | ||
|
|
15123a7b40 | ||
|
|
bbd80ec7a4 | ||
|
|
1d421b3b28 | ||
|
|
2a0459838b | ||
|
|
a87b991d78 | ||
|
|
0fb71e24cb | ||
|
|
14d04903dc | ||
|
|
181a063bb9 | ||
|
|
131d0b911f | ||
|
|
0124b0749f | ||
|
|
6daf80cdd0 | ||
|
|
9c139b591f | ||
|
|
b8cfff6d19 | ||
|
|
f33d7ee80d | ||
|
|
15a6308c72 | ||
|
|
86d1e5b646 | ||
|
|
dd9183c345 | ||
|
|
d0c9e3f7ad | ||
|
|
9082972842 | ||
|
|
05819a52ef | ||
|
|
116873c9b0 | ||
|
|
290b0fc4ab | ||
|
|
332d118d93 | ||
|
|
82bbecc9c4 | ||
|
|
15c1fd9425 | ||
|
|
be9009d653 | ||
|
|
037211c4a4 | ||
|
|
13ff9412a4 | ||
|
|
9d24b5afa6 | ||
|
|
8cba276b87 | ||
|
|
4ca8faa9c9 | ||
|
|
393f6b7666 | ||
|
|
95ec4e8d26 | ||
|
|
17e0cc5648 | ||
|
|
196b6d7a1d | ||
|
|
9def57250d | ||
|
|
9af9873e04 | ||
|
|
9f5782b67b | ||
|
|
c61177cf42 | ||
|
|
4140942479 | ||
|
|
fd306ed79b | ||
|
|
8962307291 | ||
|
|
0000c72329 | ||
|
|
3ef7a0932a | ||
|
|
f29d2c21bd | ||
|
|
53f69d9966 | ||
|
|
8fb2faa89b | ||
|
|
2b935e575a | ||
|
|
ec4c820391 | ||
|
|
1dce2eb325 | ||
|
|
49e5f8a1a5 | ||
|
|
f3b27d611a | ||
|
|
f57ba3e642 | ||
|
|
d4a072818f | ||
|
|
eeef062f7c | ||
|
|
9d56f3eb68 | ||
|
|
cf8db4e425 | ||
|
|
daa31b5bb7 | ||
|
|
8636a50190 | ||
|
|
fa28d94363 | ||
|
|
dbbc944f32 | ||
|
|
a88ad62c00 | ||
|
|
3e9341ff8a | ||
|
|
b9984beb16 | ||
|
|
68d90918cf | ||
|
|
25ffcb2fde | ||
|
|
6021d9238c | ||
|
|
3b44b131b9 | ||
|
|
4a97f95890 | ||
|
|
84ea3a9a2c | ||
|
|
8f2e51faa6 | ||
|
|
f828f8ea65 | ||
|
|
95d579d9de | ||
|
|
f3daba510b | ||
|
|
88e67715a1 | ||
|
|
53eb753346 | ||
|
|
037114b336 | ||
|
|
af72c0848e | ||
|
|
de58ee5a22 | ||
|
|
a1a93c7331 | ||
|
|
db07c162e4 | ||
|
|
9d00f660f1 | ||
|
|
0311888fd4 | ||
|
|
e4f23b31c6 | ||
|
|
57a53891e9 | ||
|
|
b6703bc25c | ||
|
|
003d208574 | ||
|
|
d8d7688f88 | ||
|
|
830b83f653 | ||
|
|
3592e76269 | ||
|
|
6aa4c5c187 | ||
|
|
fb8ee07b43 | ||
|
|
4ccff1a630 | ||
|
|
4b7160d4b2 | ||
|
|
5cce5008a3 | ||
|
|
5b6ce56ca2 | ||
|
|
2b81b6c323 | ||
|
|
a39d8b7c7c | ||
|
|
553c09ada3 | ||
|
|
b2f09949df | ||
|
|
1bc085c8f7 | ||
|
|
bb00d6919a | ||
|
|
e0e405bb31 | ||
|
|
ce711f7d2f | ||
|
|
19974f04c9 | ||
|
|
c1755603a8 | ||
|
|
24cf75a37c | ||
|
|
8169e7ce68 | ||
|
|
8383bc1629 | ||
|
|
35d1a9202b | ||
|
|
f85ee38e04 | ||
|
|
799c380e5f | ||
|
|
f20812d8ad | ||
|
|
05f3c64172 | ||
|
|
88e3227ed0 | ||
|
|
81246cd41a | ||
|
|
bab461ffd1 | ||
|
|
58eaddf627 | ||
|
|
d51fcd4f2a | ||
|
|
b484aee39e | ||
|
|
1c68c987b0 | ||
|
|
f6487d7b13 | ||
|
|
76637b0d03 | ||
|
|
77a7e007e3 | ||
|
|
bea7b94537 | ||
|
|
19d0d7bbc0 | ||
|
|
c5bdd5bbd7 | ||
|
|
bcde7151e8 | ||
|
|
2c76b13451 | ||
|
|
874d9d14bc | ||
|
|
bcedf687ff | ||
|
|
57796d6d02 | ||
|
|
c45fd4080d | ||
|
|
9d83c91deb | ||
|
|
3c22bf4a25 | ||
|
|
3c0bb6f4e5 | ||
|
|
1648b427c2 | ||
|
|
e443c65c75 | ||
|
|
220abc4d36 | ||
|
|
7d4ce0a79a | ||
|
|
ac9b77a3ac | ||
|
|
abfbb47390 | ||
|
|
e7e7e4197e | ||
|
|
e9a9f56ea5 | ||
|
|
03d7f5ec73 | ||
|
|
ba99d49b61 | ||
|
|
ea2cf27432 | ||
|
|
bd1731b420 | ||
|
|
31bea7e0ca | ||
|
|
e01e8a0842 | ||
|
|
6c057ddc81 | ||
|
|
3c401d3a4f | ||
|
|
cf1cddc8ca | ||
|
|
2e50d61446 | ||
|
|
dae187eb0b | ||
|
|
7f195d0257 | ||
|
|
a8162baada | ||
|
|
6e50357224 | ||
|
|
ef53184c10 | ||
|
|
9d38c255f5 | ||
|
|
4de4525528 | ||
|
|
b9940f260f | ||
|
|
58b0bed7f3 | ||
|
|
02af978e8d | ||
|
|
9609dc4030 | ||
|
|
4aee6d506d | ||
|
|
ac34b922ec | ||
|
|
18acad516b | ||
|
|
cedc84df8f | ||
|
|
2b9c96d7cc | ||
|
|
595014966a | ||
|
|
339c40c2b7 | ||
|
|
a108fcd2b4 | ||
|
|
79b4890794 | ||
|
|
bcda4a1377 | ||
|
|
dc985c2c98 | ||
|
|
45b1a5e29b | ||
|
|
58832a546a | ||
|
|
9f8c9615fc | ||
|
|
b6f6bdc6f4 | ||
|
|
b43aae18e0 | ||
|
|
06f52c2854 | ||
|
|
98a3f2da7d | ||
|
|
111ad8b525 | ||
|
|
a49b43fdf6 | ||
|
|
317c335269 | ||
|
|
db06c08141 | ||
|
|
f39301f533 | ||
|
|
a6646021d0 | ||
|
|
d967b2baa3 | ||
|
|
decd576a6b | ||
|
|
622f69e1a1 | ||
|
|
3c885f3969 | ||
|
|
3c83262d58 | ||
|
|
77d0df4fb6 | ||
|
|
c11adc8c28 | ||
|
|
43df0cd5f4 | ||
|
|
a1267d732e | ||
|
|
a4df20da85 | ||
|
|
81eaa6e327 | ||
|
|
5fba9895c6 | ||
|
|
64321b314f | ||
|
|
da33c220a6 | ||
|
|
212a0f27ff | ||
|
|
2f56ec7fe0 | ||
|
|
80c3993ddc | ||
|
|
3ad2d90014 | ||
|
|
611f98bca4 | ||
|
|
544660322f | ||
|
|
f08a598821 | ||
|
|
4d3d07a964 | ||
|
|
7b5f2c7d94 | ||
|
|
dc4cc74cbb | ||
|
|
eab96988bf | ||
|
|
2dcb5fbd16 | ||
|
|
3eb4e39251 | ||
|
|
6f782f3f62 | ||
|
|
55b9724f59 | ||
|
|
0a7af90454 | ||
|
|
41e666c724 | ||
|
|
902f0f91b0 | ||
|
|
88f6e04339 | ||
|
|
b560ab1a73 | ||
|
|
3626c814ac | ||
|
|
5e7026c6c6 | ||
|
|
da3fa22cbd | ||
|
|
bef556e208 | ||
|
|
5a5fc79b3b | ||
|
|
9926c817de | ||
|
|
53902c824d | ||
|
|
3c5675b3fb | ||
|
|
592ca06159 | ||
|
|
96ba3ec88e | ||
|
|
eb2790ae63 | ||
|
|
d22e2bae8e | ||
|
|
b2974ba1c6 | ||
|
|
3154a11b43 | ||
|
|
b201fd0bdd | ||
|
|
1ff4c0daf3 | ||
|
|
bc0e580683 | ||
|
|
f78ea26c3b | ||
|
|
93a9c62e29 | ||
|
|
7434a58afc | ||
|
|
ed80e4e284 | ||
|
|
c4eeda1560 | ||
|
|
7faf3a8b8e | ||
|
|
dd97e70ffc | ||
|
|
1847a6deb0 | ||
|
|
fdddec74d3 | ||
|
|
f77f91ef49 | ||
|
|
bf8307f78d | ||
|
|
3335d48154 | ||
|
|
c0db40d11a | ||
|
|
be64daf265 | ||
|
|
ee5cb6f3d8 | ||
|
|
844b29b637 | ||
|
|
5c50ed036c | ||
|
|
62299dc95a | ||
|
|
48e367cb11 | ||
|
|
8f82db07d3 | ||
|
|
ba9267df5b | ||
|
|
4cb0695646 | ||
|
|
c3cf425079 | ||
|
|
dd656d34be | ||
|
|
c647f30576 | ||
|
|
7155af50be | ||
|
|
6ba27dc863 | ||
|
|
622e176a16 | ||
|
|
4a4f9b3942 | ||
|
|
e7bfd7df62 | ||
|
|
543032a3de | ||
|
|
f69737b407 | ||
|
|
c720fb2c34 | ||
|
|
4886bb1116 | ||
|
|
8888ee9fa8 | ||
|
|
69d8fa6e4a | ||
|
|
ae0e25e5b1 | ||
|
|
2627a3dcb7 | ||
|
|
e1884c193b | ||
|
|
6cb5db2387 | ||
|
|
439afd97ba | ||
|
|
132bb9f1d6 | ||
|
|
58635bdbd6 | ||
|
|
79fe5f851b | ||
|
|
e99c78e027 | ||
|
|
75b1e14098 | ||
|
|
6bf05eb29a | ||
|
|
fb9fc14f3d | ||
|
|
8630630c45 | ||
|
|
54957d6ea4 | ||
|
|
f57e0cb71e | ||
|
|
ec862ebc72 | ||
|
|
9671e05148 | ||
|
|
3567c30020 | ||
|
|
407837afc4 | ||
|
|
ff55ed84f4 | ||
|
|
13a71a4f6d | ||
|
|
3105697c7f | ||
|
|
8747438613 | ||
|
|
82057e2e46 | ||
|
|
25cb52aa52 | ||
|
|
3c69f8f607 | ||
|
|
2f0987e980 | ||
|
|
db6d27bd2b | ||
|
|
9e39be5aea | ||
|
|
b87b8329a0 | ||
|
|
f945687a93 | ||
|
|
82afbbc17b | ||
|
|
8ec414d454 | ||
|
|
b22b4c5b85 | ||
|
|
e45f6e9b24 | ||
|
|
8915241166 | ||
|
|
336a18a1d9 | ||
|
|
ddfed6ea65 | ||
|
|
72ffcf5f9c | ||
|
|
f7e5fe7040 | ||
|
|
1c66e26e92 | ||
|
|
6e130d24cd | ||
|
|
5125468307 | ||
|
|
f3a5ce5efc | ||
|
|
6f08790065 | ||
|
|
2cbc59b7a1 | ||
|
|
9302b167d8 | ||
|
|
e13b3ed5e7 | ||
|
|
e29ce5f5d5 | ||
|
|
1df1f0dbc2 | ||
|
|
0235e9962e | ||
|
|
78ddb998a2 | ||
|
|
3949ae4123 | ||
|
|
330229c463 | ||
|
|
52be6579b7 | ||
|
|
1c8298ae08 | ||
|
|
d88a813038 | ||
|
|
254c6c3344 | ||
|
|
e7dc120456 | ||
|
|
ceb5b4c56e | ||
|
|
9d8b93ed45 | ||
|
|
fdb77457b3 | ||
|
|
04de315e0e | ||
|
|
dc34cb0aae | ||
|
|
bdd6fba06a | ||
|
|
15eabb42ef | ||
|
|
f799962146 | ||
|
|
cb3a59446c | ||
|
|
e70a49a771 | ||
|
|
db48461ebc | ||
|
|
ac2769c142 | ||
|
|
829557fdda | ||
|
|
c12c2a534c | ||
|
|
5323af33d2 | ||
|
|
96ef6809a7 | ||
|
|
312b96aa8b | ||
|
|
8cc4f86e7d | ||
|
|
32595b92a2 | ||
|
|
13100b19bc | ||
|
|
66f88d9c71 | ||
|
|
477322d21d | ||
|
|
846eac8be9 | ||
|
|
8842b974f0 | ||
|
|
65b69fe353 | ||
|
|
2a914770ea | ||
|
|
d030f0b5d2 | ||
|
|
33364a8ab6 | ||
|
|
d8bd18f369 | ||
|
|
3e1359b92e | ||
|
|
4c4d2415e9 | ||
|
|
34a0c02f70 | ||
|
|
5c4e02a1da | ||
|
|
21bc8c4324 | ||
|
|
19b14e166d | ||
|
|
3d96206783 | ||
|
|
959300f963 | ||
|
|
41ce4ffe4b | ||
|
|
eca112c3bc | ||
|
|
250cb30c05 | ||
|
|
dd96095a30 | ||
|
|
d5160927f5 | ||
|
|
925ee825ff | ||
|
|
36db493723 | ||
|
|
cbab24ba45 | ||
|
|
e0f866f19d | ||
|
|
138975e4a8 | ||
|
|
783ce48538 | ||
|
|
8a92a4250f | ||
|
|
1264e6e292 | ||
|
|
e9cd2dc9e1 | ||
|
|
16e2ac898f | ||
|
|
4d5f158652 | ||
|
|
f5355cfa98 | ||
|
|
1985dd629d | ||
|
|
82101434fd | ||
|
|
8cb80d6014 | ||
|
|
81b96a8041 | ||
|
|
29a61458e0 | ||
|
|
56ebe6c727 | ||
|
|
f2ea88aa4c | ||
|
|
9313564e64 | ||
|
|
ef7767b6cd | ||
|
|
3022c59654 | ||
|
|
ab3c03d2d6 | ||
|
|
f4e05cc621 | ||
|
|
946f0b4dc4 | ||
|
|
348c95ebe1 | ||
|
|
c55e03c588 | ||
|
|
acef9b7111 | ||
|
|
13734d4e62 | ||
|
|
70bb29581d | ||
|
|
448a9015c0 | ||
|
|
21592563cb | ||
|
|
ac7a25734b | ||
|
|
334c0d0449 | ||
|
|
fc26e148fd | ||
|
|
e3ce7c608b | ||
|
|
087d689997 | ||
|
|
af2a7eadc3 | ||
|
|
fb15a4ea11 | ||
|
|
c0c1ab7006 | ||
|
|
31a246b554 | ||
|
|
06d369ba11 | ||
|
|
b2b2c2016c | ||
|
|
5d946586b8 | ||
|
|
ec515f967d | ||
|
|
9917c3c5ba | ||
|
|
fae002fbb7 | ||
|
|
4831ee65b6 | ||
|
|
112992585a | ||
|
|
84d69566c9 | ||
|
|
ca7789d73c | ||
|
|
bed0d5678d | ||
|
|
6d3244d01c | ||
|
|
3ab73c8552 | ||
|
|
bdc3d10cdb | ||
|
|
49ef677b1d | ||
|
|
144c19db74 | ||
|
|
a8742ea111 | ||
|
|
db60360293 | ||
|
|
9c4567bd6d | ||
|
|
f2ef4ad6ac | ||
|
|
57beb73bf7 | ||
|
|
f1ead2554f | ||
|
|
33164c8a43 | ||
|
|
f33222c83b | ||
|
|
fa26b55452 | ||
|
|
16f9ad06ef | ||
|
|
891f05c5ae | ||
|
|
5618001c3f | ||
|
|
03d0d984e3 | ||
|
|
a55f07ac68 | ||
|
|
103932fec8 | ||
|
|
507ada1951 | ||
|
|
df463e51c1 | ||
|
|
60944a9bcb | ||
|
|
1d51d182ec | ||
|
|
04a0740ccb | ||
|
|
cd84500c56 | ||
|
|
8fbbc2b6d8 | ||
|
|
ce52cbb428 | ||
|
|
76780d74d9 | ||
|
|
95db7aa776 | ||
|
|
58737b1d7e | ||
|
|
0a33a6e79b | ||
|
|
cf35fd4dee | ||
|
|
cdafb26423 | ||
|
|
88ea9197d9 | ||
|
|
46b8e3be66 | ||
|
|
dec5fc0f48 | ||
|
|
40270e1f70 | ||
|
|
b598b4ac45 | ||
|
|
73e95d67b9 | ||
|
|
393bd7277c | ||
|
|
af4320df50 | ||
|
|
14deb06e80 | ||
|
|
638bfff09d | ||
|
|
0515b12305 | ||
|
|
19cc620f18 | ||
|
|
f208594067 | ||
|
|
8d3fc735ea | ||
|
|
f52b6e0449 | ||
|
|
6019a38266 | ||
|
|
23da0c16c7 | ||
|
|
7c4927c2e3 | ||
|
|
fcda0c9819 | ||
|
|
c17a36ec07 | ||
|
|
92d57ab504 | ||
|
|
9aad43f649 | ||
|
|
cbacd51337 | ||
|
|
22d5e9bbfb | ||
|
|
900c8b71d8 | ||
|
|
a27949cffa | ||
|
|
026a13b4db | ||
|
|
4bc272da73 | ||
|
|
e8d0f1fa3e | ||
|
|
23fd3f62ac | ||
|
|
26ef1bc249 | ||
|
|
18d9573b6a | ||
|
|
8c0f02ac4b | ||
|
|
3657269d3e | ||
|
|
d1fc700735 | ||
|
|
7675f153ce | ||
|
|
d1a2048134 | ||
|
|
340a17dd74 | ||
|
|
dbf0b98791 | ||
|
|
f649939d78 | ||
|
|
2e5f7dc8fc | ||
|
|
8faad92cfd | ||
|
|
0df10bda0c | ||
|
|
1da611cc02 | ||
|
|
bc42e2bc35 | ||
|
|
5787dcc82d | ||
|
|
356214c11b | ||
|
|
5683092d6a | ||
|
|
1ace9ee50f | ||
|
|
e1a5a84253 | ||
|
|
6e13b877bb | ||
|
|
e187a4a7d6 | ||
|
|
0c3aa7b7f2 | ||
|
|
8bdb67cc15 | ||
|
|
4eae191430 | ||
|
|
4728cf5a12 | ||
|
|
cd41a1ca8c | ||
|
|
3719e10ce8 | ||
|
|
52a1d905f1 | ||
|
|
a354ab84e9 | ||
|
|
e98485df7f | ||
|
|
f6b412e1c9 | ||
|
|
d1e040e27c | ||
|
|
a9b2876b71 | ||
|
|
66d8943c08 | ||
|
|
25200b47ea | ||
|
|
a5527903e9 | ||
|
|
7514037464 | ||
|
|
649af822fd | ||
|
|
5caa49aeab | ||
|
|
2bea0adb92 | ||
|
|
cfc1a3db22 | ||
|
|
df10a7e7f0 | ||
|
|
e0d70e3008 | ||
|
|
a727255a82 | ||
|
|
c10f41b168 | ||
|
|
4c1f433073 | ||
|
|
963e0a730d | ||
|
|
42571069bf | ||
|
|
017369c6f2 | ||
|
|
cc2b61186e | ||
|
|
dd27ef7d3c | ||
|
|
833165f86c | ||
|
|
c85262cfd0 | ||
|
|
a8863e44db | ||
|
|
3aacc5ffde | ||
|
|
fbf9545338 | ||
|
|
303882350b | ||
|
|
9611e4ce19 | ||
|
|
b12f4d97f8 | ||
|
|
ee3085e15e | ||
|
|
2097a001b9 | ||
|
|
53e96e5adf | ||
|
|
2e4e5ef480 | ||
|
|
8768eb64e6 | ||
|
|
609bb762fe | ||
|
|
90017712a6 | ||
|
|
32859eb057 | ||
|
|
4198f61c16 | ||
|
|
6f73e9c3ba | ||
|
|
9053ceb3b7 | ||
|
|
e13050b64e | ||
|
|
6789273ab1 | ||
|
|
aff6f00450 | ||
|
|
5a49f6bb9b | ||
|
|
f5efddc011 | ||
|
|
e45268cd4d | ||
|
|
eef8137166 | ||
|
|
faaddd4dfe | ||
|
|
e81a4fc330 | ||
|
|
f1a7f0a7e8 | ||
|
|
d44c9d3e74 | ||
|
|
905fa10c88 | ||
|
|
4191b07b1f | ||
|
|
77dcd68a86 | ||
|
|
d06444e639 | ||
|
|
369bc50709 | ||
|
|
6f60eb9e1a | ||
|
|
845f384df6 | ||
|
|
516fdf627a | ||
|
|
c7a7594821 | ||
|
|
37af588492 | ||
|
|
9540c58c4a | ||
|
|
8a80a734d8 | ||
|
|
9e33b47cbd | ||
|
|
e2fe0e11f0 | ||
|
|
3bd45a8536 | ||
|
|
effb8024a4 | ||
|
|
c16a2827d7 | ||
|
|
796075f9dc | ||
|
|
307187f6c1 | ||
|
|
39c52c9ecf |
7
.bazelrc
7
.bazelrc
@@ -14,4 +14,11 @@ build:linux --cxxopt=-std=c++20
|
||||
build:macos --cxxopt=-std=c++20 --cpu=darwin_x86_64
|
||||
build:windows --cxxopt=/std:c++20 --cxxopt=/Zc:preprocessor
|
||||
|
||||
# this requires developer mode, but is required to have pack installer functioning
|
||||
startup --windows_enable_symlinks
|
||||
common --enable_runfiles
|
||||
|
||||
common --registry=file:///%workspace%/misc/bazel/registry
|
||||
common --registry=https://bcr.bazel.build
|
||||
|
||||
try-import %workspace%/local.bazelrc
|
||||
|
||||
4
.bazelrc.internal
Normal file
4
.bazelrc.internal
Normal file
@@ -0,0 +1,4 @@
|
||||
# this file should contain bazel settings required to build things from `semmle-code`
|
||||
|
||||
common --registry=file:///%workspace%/ql/misc/bazel/registry
|
||||
common --registry=https://bcr.bazel.build
|
||||
@@ -1 +1 @@
|
||||
7.1.0
|
||||
7.1.2
|
||||
|
||||
10
.gitattributes
vendored
10
.gitattributes
vendored
@@ -67,14 +67,14 @@ go/extractor/opencsv/CSVReader.java -text
|
||||
# for those testing dbscheme files.
|
||||
*/ql/lib/upgrades/initial/*.dbscheme -text
|
||||
|
||||
# Generated test files - these are synced from the standard JavaScript libraries using
|
||||
# `javascript/ql/experimental/adaptivethreatmodeling/test/update_endpoint_test_files.py`.
|
||||
javascript/ql/experimental/adaptivethreatmodeling/test/endpoint_large_scale/autogenerated/**/*.js linguist-generated=true -merge
|
||||
javascript/ql/experimental/adaptivethreatmodeling/test/endpoint_large_scale/autogenerated/**/*.ts linguist-generated=true -merge
|
||||
|
||||
# Auto-generated modeling for Python
|
||||
python/ql/lib/semmle/python/frameworks/data/internal/subclass-capture/*.yml linguist-generated=true
|
||||
|
||||
# auto-generated bazel lock file
|
||||
ruby/extractor/cargo-bazel-lock.json linguist-generated=true
|
||||
ruby/extractor/cargo-bazel-lock.json -merge
|
||||
|
||||
# auto-generated files for the C# build
|
||||
csharp/paket.lock linguist-generated=true
|
||||
# needs eol=crlf, as `paket` touches this file and saves it als crlf
|
||||
csharp/.paket/Paket.Restore.targets linguist-generated=true eol=crlf
|
||||
|
||||
5
.github/labeler.yml
vendored
5
.github/labeler.yml
vendored
@@ -15,7 +15,7 @@ Java:
|
||||
- change-notes/**/*java.*
|
||||
|
||||
JS:
|
||||
- any: [ 'javascript/**/*', '!javascript/ql/experimental/adaptivethreatmodeling/**/*' ]
|
||||
- any: [ 'javascript/**/*' ]
|
||||
- change-notes/**/*javascript*
|
||||
|
||||
Kotlin:
|
||||
@@ -46,6 +46,3 @@ documentation:
|
||||
# Since these are all shared files that need to be synced, just pick _one_ copy of each.
|
||||
"DataFlow Library":
|
||||
- "shared/dataflow/**/*"
|
||||
|
||||
"ATM":
|
||||
- javascript/ql/experimental/adaptivethreatmodeling/**/*
|
||||
|
||||
28
.github/workflows/buildifier.yml
vendored
Normal file
28
.github/workflows/buildifier.yml
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
name: Check bazel formatting
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- "**.bazel"
|
||||
- "**.bzl"
|
||||
branches:
|
||||
- main
|
||||
- "rc/*"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
check:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Check bazel formatting
|
||||
uses: pre-commit/action@646c83fcd040023954eafda54b4db0192ce70507
|
||||
with:
|
||||
extra_args: >
|
||||
buildifier --all-files 2>&1 ||
|
||||
(
|
||||
echo -e "In order to format all bazel files, please run:\n bazel run //misc/bazel:buildifier"; exit 1
|
||||
)
|
||||
4
.github/workflows/codeql-analysis.yml
vendored
4
.github/workflows/codeql-analysis.yml
vendored
@@ -56,7 +56,9 @@ jobs:
|
||||
# uses a compiled language
|
||||
|
||||
- run: |
|
||||
dotnet build csharp
|
||||
cd csharp
|
||||
dotnet tool restore
|
||||
dotnet build .
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@main
|
||||
|
||||
3
.github/workflows/csharp-qltest.yml
vendored
3
.github/workflows/csharp-qltest.yml
vendored
@@ -81,10 +81,11 @@ jobs:
|
||||
dotnet-version: 8.0.101
|
||||
- name: Extractor unit tests
|
||||
run: |
|
||||
dotnet tool restore
|
||||
dotnet test -p:RuntimeFrameworkVersion=8.0.1 extractor/Semmle.Util.Tests
|
||||
dotnet test -p:RuntimeFrameworkVersion=8.0.1 extractor/Semmle.Extraction.Tests
|
||||
dotnet test -p:RuntimeFrameworkVersion=8.0.1 autobuilder/Semmle.Autobuild.CSharp.Tests
|
||||
dotnet test -p:RuntimeFrameworkVersion=8.0.1 "${{ github.workspace }}/cpp/autobuilder/Semmle.Autobuild.Cpp.Tests"
|
||||
dotnet test -p:RuntimeFrameworkVersion=8.0.1 autobuilder/Semmle.Autobuild.Cpp.Tests
|
||||
shell: bash
|
||||
stubgentest:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
65
.github/workflows/go-tests-other-os.yml
vendored
65
.github/workflows/go-tests-other-os.yml
vendored
@@ -7,8 +7,6 @@ on:
|
||||
- .github/workflows/go-tests-other-os.yml
|
||||
- .github/actions/**
|
||||
- codeql-workspace.yml
|
||||
env:
|
||||
GO_VERSION: '~1.22.0'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -18,72 +16,17 @@ jobs:
|
||||
name: Test MacOS
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- name: Set up Go ${{ env.GO_VERSION }}
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
cache: false
|
||||
id: go
|
||||
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up CodeQL CLI
|
||||
uses: ./.github/actions/fetch-codeql
|
||||
|
||||
- name: Enable problem matchers in repository
|
||||
shell: bash
|
||||
run: 'find .github/problem-matchers -name \*.json -exec echo "::add-matcher::{}" \;'
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
cd go
|
||||
make
|
||||
|
||||
- name: Cache compilation cache
|
||||
id: query-cache
|
||||
uses: ./.github/actions/cache-query-compilation
|
||||
with:
|
||||
key: go-qltest
|
||||
- name: Test
|
||||
run: |
|
||||
cd go
|
||||
make test cache="${{ steps.query-cache.outputs.cache-dir }}"
|
||||
- name: Run tests
|
||||
uses: ./go/actions/test
|
||||
|
||||
test-win:
|
||||
if: github.repository_owner == 'github'
|
||||
name: Test Windows
|
||||
runs-on: windows-latest-xl
|
||||
steps:
|
||||
- name: Set up Go ${{ env.GO_VERSION }}
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
cache: false
|
||||
id: go
|
||||
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up CodeQL CLI
|
||||
uses: ./.github/actions/fetch-codeql
|
||||
|
||||
- name: Enable problem matchers in repository
|
||||
shell: bash
|
||||
run: 'find .github/problem-matchers -name \*.json -exec echo "::add-matcher::{}" \;'
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
cd go
|
||||
make
|
||||
|
||||
- name: Cache compilation cache
|
||||
id: query-cache
|
||||
uses: ./.github/actions/cache-query-compilation
|
||||
with:
|
||||
key: go-qltest
|
||||
|
||||
- name: Test
|
||||
run: |
|
||||
cd go
|
||||
make test cache="${{ steps.query-cache.outputs.cache-dir }}"
|
||||
- name: Run tests
|
||||
uses: ./go/actions/test
|
||||
|
||||
51
.github/workflows/go-tests.yml
vendored
51
.github/workflows/go-tests.yml
vendored
@@ -16,9 +16,6 @@ on:
|
||||
- .github/actions/**
|
||||
- codeql-workspace.yml
|
||||
|
||||
env:
|
||||
GO_VERSION: '~1.22.0'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
@@ -28,51 +25,9 @@ jobs:
|
||||
name: Test Linux (Ubuntu)
|
||||
runs-on: ubuntu-latest-xl
|
||||
steps:
|
||||
- name: Set up Go ${{ env.GO_VERSION }}
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
cache: false
|
||||
id: go
|
||||
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up CodeQL CLI
|
||||
uses: ./.github/actions/fetch-codeql
|
||||
|
||||
- name: Enable problem matchers in repository
|
||||
shell: bash
|
||||
run: 'find .github/problem-matchers -name \*.json -exec echo "::add-matcher::{}" \;'
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
cd go
|
||||
make
|
||||
|
||||
- name: Check that all Go code is autoformatted
|
||||
run: |
|
||||
cd go
|
||||
make check-formatting
|
||||
|
||||
- name: Compile qhelp files to markdown
|
||||
run: |
|
||||
cd go
|
||||
env QHELP_OUT_DIR=qhelp-out make qhelp-to-markdown
|
||||
|
||||
- name: Upload qhelp markdown
|
||||
uses: actions/upload-artifact@v3
|
||||
- name: Run tests
|
||||
uses: ./go/actions/test
|
||||
with:
|
||||
name: qhelp-markdown
|
||||
path: go/qhelp-out/**/*.md
|
||||
|
||||
- name: Cache compilation cache
|
||||
id: query-cache
|
||||
uses: ./.github/actions/cache-query-compilation
|
||||
with:
|
||||
key: go-qltest
|
||||
|
||||
- name: Test
|
||||
run: |
|
||||
cd go
|
||||
make test cache="${{ steps.query-cache.outputs.cache-dir }}"
|
||||
run-code-checks: true
|
||||
|
||||
5
.lfsconfig
Normal file
5
.lfsconfig
Normal file
@@ -0,0 +1,5 @@
|
||||
[lfs]
|
||||
# codeql is publicly forked by many users, and we don't want any LFS file polluting their working
|
||||
# copies. We therefore exclude everything by default.
|
||||
# For files required by bazel builds, use rules in `misc/bazel/lfs.bzl` to download them on demand.
|
||||
fetchinclude = /nothing
|
||||
@@ -20,13 +20,23 @@ repos:
|
||||
- id: autopep8
|
||||
files: ^misc/codegen/.*\.py
|
||||
|
||||
- repo: https://github.com/warchant/pre-commit-buildifier
|
||||
rev: 0.0.2
|
||||
hooks:
|
||||
- id: buildifier
|
||||
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: buildifier
|
||||
name: Format bazel files
|
||||
files: \.(bazel|bzl)
|
||||
language: system
|
||||
entry: bazel run //misc/bazel:buildifier
|
||||
pass_filenames: false
|
||||
|
||||
# DISABLED: can be enabled by copying this config and installing `pre-commit` with `--config` on the copy
|
||||
# - id: go-gen
|
||||
# name: Check checked in generated files in go
|
||||
# files: ^go/.*
|
||||
# language: system
|
||||
# entry: bazel run //go:gen
|
||||
# pass_filenames: false
|
||||
|
||||
- id: codeql-format
|
||||
name: Fix QL file formatting
|
||||
files: \.qll?$
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/cpp/ @github/codeql-c-analysis
|
||||
/cpp/autobuilder/ @github/codeql-c-extractor
|
||||
/csharp/ @github/codeql-csharp
|
||||
/csharp/autobuilder/Semmle.Autobuild.Cpp @github/codeql-c-extractor
|
||||
/csharp/autobuilder/Semmle.Autobuild.Cpp.Tests @github/codeql-c-extractor
|
||||
/go/ @github/codeql-go
|
||||
/java/ @github/codeql-java
|
||||
/javascript/ @github/codeql-javascript
|
||||
@@ -12,9 +13,6 @@
|
||||
/java/ql/test-kotlin1/ @github/codeql-kotlin
|
||||
/java/ql/test-kotlin2/ @github/codeql-kotlin
|
||||
|
||||
# ML-powered queries
|
||||
/javascript/ql/experimental/adaptivethreatmodeling/ @github/codeql-ml-powered-queries-reviewers
|
||||
|
||||
# CodeQL tools and associated docs
|
||||
/docs/codeql/codeql-cli/ @github/codeql-cli-reviewers
|
||||
/docs/codeql/codeql-for-visual-studio-code/ @github/codeql-vscode-reviewers
|
||||
@@ -37,9 +35,7 @@ MODULE.bazel @github/codeql-ci-reviewers
|
||||
|
||||
# Workflows
|
||||
/.github/workflows/ @github/codeql-ci-reviewers
|
||||
/.github/workflows/atm-* @github/codeql-ml-powered-queries-reviewers
|
||||
/.github/workflows/go-* @github/codeql-go
|
||||
/.github/workflows/js-ml-tests.yml @github/codeql-ml-powered-queries-reviewers
|
||||
/.github/workflows/ql-for-ql-* @github/codeql-ql-for-ql-reviewers
|
||||
/.github/workflows/ruby-* @github/codeql-ruby
|
||||
/.github/workflows/swift.yml @github/codeql-swift
|
||||
|
||||
@@ -4,6 +4,8 @@ We welcome contributions to our CodeQL libraries and queries. Got an idea for a
|
||||
|
||||
There is lots of useful documentation to help you write queries, ranging from information about query file structure to tutorials for specific target languages. For more information on the documentation available, see [CodeQL queries](https://codeql.github.com/docs/writing-codeql-queries/codeql-queries) on [codeql.github.com](https://codeql.github.com).
|
||||
|
||||
Note that the CodeQL for Visual Studio Code documentation has been migrated to https://docs.github.com/en/code-security/codeql-for-vs-code/, but you can still contribute to it via a different repository. For more information, see [Contributing to GitHub Docs documentation](https://docs.github.com/en/contributing)."
|
||||
|
||||
## Change notes
|
||||
|
||||
Any nontrivial user-visible change to a query pack or library pack should have a change note. For details on how to add a change note for your change, see [this guide](docs/change-notes.md).
|
||||
@@ -43,7 +45,7 @@ If you have an idea for a query that you would like to share with other CodeQL u
|
||||
|
||||
3. **Formatting**
|
||||
|
||||
- The queries and libraries must be autoformatted, for example using the "Format Document" command in [CodeQL for Visual Studio Code](https://codeql.github.com/docs/codeql-for-visual-studio-code/about-codeql-for-visual-studio-code).
|
||||
- The queries and libraries must be autoformatted, for example using the "Format Document" command in [CodeQL for Visual Studio Code](https://docs.github.com/en/code-security/codeql-for-vs-code/).
|
||||
|
||||
If you prefer, you can either:
|
||||
1. install the [pre-commit framework](https://pre-commit.com/) and install the configured hooks on this repo via `pre-commit install`, or
|
||||
|
||||
11
MODULE.bazel
11
MODULE.bazel
@@ -13,14 +13,18 @@ local_path_override(
|
||||
|
||||
# see https://registry.bazel.build/ for a list of available packages
|
||||
|
||||
bazel_dep(name = "platforms", version = "0.0.8")
|
||||
bazel_dep(name = "rules_pkg", version = "0.9.1")
|
||||
bazel_dep(name = "platforms", version = "0.0.9")
|
||||
bazel_dep(name = "rules_go", version = "0.47.0")
|
||||
bazel_dep(name = "rules_pkg", version = "0.10.1")
|
||||
bazel_dep(name = "rules_nodejs", version = "6.0.3")
|
||||
bazel_dep(name = "rules_python", version = "0.31.0")
|
||||
bazel_dep(name = "bazel_skylib", version = "1.5.0")
|
||||
bazel_dep(name = "abseil-cpp", version = "20240116.0", repo_name = "absl")
|
||||
bazel_dep(name = "nlohmann_json", version = "3.11.3", repo_name = "json")
|
||||
bazel_dep(name = "fmt", version = "10.0.0")
|
||||
bazel_dep(name = "gazelle", version = "0.36.0")
|
||||
|
||||
bazel_dep(name = "buildifier_prebuilt", version = "6.4.0", dev_dependency = True)
|
||||
|
||||
pip = use_extension("@rules_python//python/extensions:pip.bzl", "pip")
|
||||
pip.parse(
|
||||
@@ -50,6 +54,9 @@ node.toolchain(
|
||||
)
|
||||
use_repo(node, "nodejs", "nodejs_toolchains")
|
||||
|
||||
go_sdk = use_extension("@rules_go//go:extensions.bzl", "go_sdk")
|
||||
go_sdk.download(version = "1.22.2")
|
||||
|
||||
register_toolchains(
|
||||
"@nodejs_toolchains//:all",
|
||||
)
|
||||
|
||||
@@ -4,7 +4,7 @@ This open source repository contains the standard CodeQL libraries and queries t
|
||||
|
||||
## How do I learn CodeQL and run queries?
|
||||
|
||||
There is [extensive documentation](https://codeql.github.com/docs/) on getting started with writing CodeQL using the [CodeQL extension for Visual Studio Code](https://codeql.github.com/docs/codeql-for-visual-studio-code/) and the [CodeQL CLI](https://codeql.github.com/docs/codeql-cli/).
|
||||
There is extensive documentation about the [CodeQL language](https://codeql.github.com/docs/), writing CodeQL using the [CodeQL extension for Visual Studio Code](https://docs.github.com/en/code-security/codeql-for-vs-code/) and using the [CodeQL CLI](https://docs.github.com/en/code-security/codeql-cli).
|
||||
|
||||
## Contributing
|
||||
|
||||
|
||||
@@ -11,16 +11,6 @@ provide:
|
||||
- "cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/qlpack.yml"
|
||||
- "go/ql/config/legacy-support/qlpack.yml"
|
||||
- "go/build/codeql-extractor-go/codeql-extractor.yml"
|
||||
- "javascript/ql/experimental/adaptivethreatmodeling/lib/qlpack.yml"
|
||||
# This pack is explicitly excluded from the workspace since most users
|
||||
# will want to use a version of this pack from the package cache. Internal
|
||||
# users can uncomment the following line and place a custom ML model
|
||||
# in the corresponding pack to test a custom ML model within their local
|
||||
# checkout.
|
||||
# - "javascript/ql/experimental/adaptivethreatmodeling/model/qlpack.yml"
|
||||
- "javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/qlpack.yml"
|
||||
- "javascript/ql/experimental/adaptivethreatmodeling/src/qlpack.yml"
|
||||
- "javascript/ql/experimental/adaptivethreatmodeling/test/qlpack.yml"
|
||||
- "csharp/ql/campaigns/Solorigate/lib/qlpack.yml"
|
||||
- "csharp/ql/campaigns/Solorigate/src/qlpack.yml"
|
||||
- "csharp/ql/campaigns/Solorigate/test/qlpack.yml"
|
||||
|
||||
@@ -362,7 +362,7 @@
|
||||
"java/ql/lib/semmle/code/java/security/internal/EncryptionKeySizes.qll"
|
||||
],
|
||||
"Python model summaries test extension": [
|
||||
"python/ql/test/experimental/dataflow/model-summaries/InlineTaintTest.ext.yml",
|
||||
"python/ql/test/experimental/dataflow/model-summaries/NormalDataflowTest.ext.yml"
|
||||
"python/ql/test/library-tests/dataflow/model-summaries/InlineTaintTest.ext.yml",
|
||||
"python/ql/test/library-tests/dataflow/model-summaries/NormalDataflowTest.ext.yml"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
load("@rules_pkg//:mappings.bzl", "pkg_filegroup")
|
||||
load("@rules_pkg//pkg:mappings.bzl", "pkg_filegroup")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
|
||||
13
cpp/autobuilder/.gitignore
vendored
13
cpp/autobuilder/.gitignore
vendored
@@ -1,13 +0,0 @@
|
||||
obj/
|
||||
TestResults/
|
||||
*.manifest
|
||||
*.pdb
|
||||
*.suo
|
||||
*.mdb
|
||||
*.vsmdi
|
||||
csharp.log
|
||||
**/bin/Debug
|
||||
**/bin/Release
|
||||
*.tlog
|
||||
.vs
|
||||
*.user
|
||||
1
cpp/autobuilder/README.md
Normal file
1
cpp/autobuilder/README.md
Normal file
@@ -0,0 +1 @@
|
||||
The Windows autobuilder that used to live in this directory moved to `csharp/autobuilder/Semmle.Autobuild.Cpp`.
|
||||
@@ -1,26 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<RuntimeIdentifiers>win-x64;linux-x64;osx-x64</RuntimeIdentifiers>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.IO.FileSystem" Version="4.3.0" />
|
||||
<PackageReference Include="System.IO.FileSystem.Primitives" Version="4.3.0" />
|
||||
<PackageReference Include="xunit" Version="2.6.2" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.4">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Semmle.Autobuild.Cpp\Semmle.Autobuild.Cpp.csproj" />
|
||||
<ProjectReference Include="..\..\..\csharp\autobuilder\Semmle.Autobuild.Shared\Semmle.Autobuild.Shared.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,4 +1,4 @@
|
||||
description: Revert support for repeated initializers, which are allowed in C with designated initializers.
|
||||
compatibility: full
|
||||
aggregate_field_init.rel: reorder aggregate_field_init.rel (int aggregate, int initializer, int field, int position) aggregate initializer field
|
||||
aggregate_array_init.rel: reorder aggregate_array_init.rel (int aggregate, int initializer, int element_index, int position) aggregate initializer element_index
|
||||
aggregate_field_init.rel: reorder aggregate_field_init.rel (@aggregateliteral aggregate, @expr initializer, @membervariable field, int position) aggregate initializer field
|
||||
aggregate_array_init.rel: reorder aggregate_array_init.rel (@aggregateliteral aggregate, @expr initializer, int element_index, int position) aggregate initializer element_index
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
load("@rules_pkg//:mappings.bzl", "pkg_files", "strip_prefix")
|
||||
load("@rules_pkg//pkg:mappings.bzl", "pkg_files", "strip_prefix")
|
||||
|
||||
pkg_files(
|
||||
name = "downgrades",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
load("@rules_pkg//:mappings.bzl", "pkg_files")
|
||||
load("@rules_pkg//pkg:mappings.bzl", "pkg_files")
|
||||
|
||||
package(default_visibility = ["//cpp:__pkg__"])
|
||||
|
||||
|
||||
@@ -1,3 +1,24 @@
|
||||
## 0.13.1
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.13.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* Deleted the deprecated `GlobalValueNumberingImpl.qll` implementation.
|
||||
|
||||
### New Features
|
||||
|
||||
* Models-as-Data support has been added for C/C++. This feature allows flow sources, sinks and summaries to be expressed in compact strings as an alternative to modelling each source / sink / summary with explicit QL. See `dataflow/ExternalFlow.qll` for documentation and specification of the model format, and `models/implementations/ZMQ.qll` for a simple example of models. Importing models from `.yml` is not yet supported.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Source models have been added for the standard library function `getc` (and variations).
|
||||
* Source, sink and flow models for the ZeroMQ (ZMQ) networking library have been added.
|
||||
* Parameters of functions without definitions now have `ParameterNode`s.
|
||||
* The alias analysis used internally by various libraries has been improved to answer alias questions more conservatively. As a result, some queries may report fewer false positives.
|
||||
|
||||
## 0.12.11
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
16
cpp/ql/lib/change-notes/released/0.13.0.md
Normal file
16
cpp/ql/lib/change-notes/released/0.13.0.md
Normal file
@@ -0,0 +1,16 @@
|
||||
## 0.13.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* Deleted the deprecated `GlobalValueNumberingImpl.qll` implementation.
|
||||
|
||||
### New Features
|
||||
|
||||
* Models-as-Data support has been added for C/C++. This feature allows flow sources, sinks and summaries to be expressed in compact strings as an alternative to modelling each source / sink / summary with explicit QL. See `dataflow/ExternalFlow.qll` for documentation and specification of the model format, and `models/implementations/ZMQ.qll` for a simple example of models. Importing models from `.yml` is not yet supported.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Source models have been added for the standard library function `getc` (and variations).
|
||||
* Source, sink and flow models for the ZeroMQ (ZMQ) networking library have been added.
|
||||
* Parameters of functions without definitions now have `ParameterNode`s.
|
||||
* The alias analysis used internally by various libraries has been improved to answer alias questions more conservatively. As a result, some queries may report fewer false positives.
|
||||
3
cpp/ql/lib/change-notes/released/0.13.1.md
Normal file
3
cpp/ql/lib/change-notes/released/0.13.1.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.13.1
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.12.11
|
||||
lastReleaseVersion: 0.13.1
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/cpp-all
|
||||
version: 0.12.11
|
||||
version: 0.13.2-dev
|
||||
groups: cpp
|
||||
dbscheme: semmlecode.cpp.dbscheme
|
||||
extractor: cpp
|
||||
@@ -7,6 +7,7 @@ library: true
|
||||
upgrades: upgrades
|
||||
dependencies:
|
||||
codeql/dataflow: ${workspace}
|
||||
codeql/mad: ${workspace}
|
||||
codeql/rangeanalysis: ${workspace}
|
||||
codeql/ssa: ${workspace}
|
||||
codeql/typeflow: ${workspace}
|
||||
|
||||
@@ -463,6 +463,25 @@ class StmtNode extends AstNode {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A node representing a child of a `Stmt` that is itself a `Stmt`.
|
||||
*/
|
||||
class ChildStmtNode extends StmtNode {
|
||||
Stmt childStmt;
|
||||
|
||||
ChildStmtNode() { exists(Stmt parent | parent.getAChild() = childStmt and childStmt = ast) }
|
||||
|
||||
override BaseAstNode getChildInternal(int childIndex) {
|
||||
result = super.getChildInternal(childIndex)
|
||||
or
|
||||
exists(int destructorIndex |
|
||||
result.getAst() = childStmt.getImplicitDestructorCall(destructorIndex) and
|
||||
childIndex =
|
||||
destructorIndex + max(int index | exists(childStmt.getChild(index)) or index = 0) + 1
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A node representing a `DeclStmt`.
|
||||
*/
|
||||
@@ -674,6 +693,13 @@ class FunctionNode extends FunctionOrGlobalOrNamespaceVariableNode {
|
||||
private string getChildAccessorWithoutConversions(Locatable parent, Element child) {
|
||||
shouldPrintDeclaration(getAnEnclosingDeclaration(parent)) and
|
||||
(
|
||||
exists(Stmt s, int i | s.getChild(i) = parent |
|
||||
exists(int n |
|
||||
s.getChild(i).(Stmt).getImplicitDestructorCall(n) = child and
|
||||
result = "getImplicitDestructorCall(" + n + ")"
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(Stmt s | s = parent |
|
||||
namedStmtChildPredicates(s, child, result)
|
||||
or
|
||||
|
||||
@@ -790,6 +790,27 @@ private predicate simple_comparison_eq(Instruction test, Operand op, int k, Abst
|
||||
exists(switch.getSuccessor(case)) and
|
||||
case.getValue().toInt() = k
|
||||
)
|
||||
or
|
||||
// There's no implicit CompareInstruction in files compiled as C since C
|
||||
// doesn't have implicit boolean conversions. So instead we check whether
|
||||
// there's a branch on a value of pointer or integer type.
|
||||
exists(ConditionalBranchInstruction branch, IRType type |
|
||||
not test instanceof CompareInstruction and
|
||||
type = test.getResultIRType() and
|
||||
(type instanceof IRAddressType or type instanceof IRIntegerType) and
|
||||
test = branch.getCondition() and
|
||||
op.getDef() = test
|
||||
|
|
||||
// We'd like to also include a case such as:
|
||||
// ```
|
||||
// k = 1 and
|
||||
// value.(BooleanValue).getValue() = true
|
||||
// ```
|
||||
// but all we know is that the value is non-zero in the true branch.
|
||||
// So we can only conclude something in the false branch.
|
||||
k = 0 and
|
||||
value.(BooleanValue).getValue() = false
|
||||
)
|
||||
}
|
||||
|
||||
private predicate complex_eq(
|
||||
@@ -1156,5 +1177,14 @@ private predicate add_eq(
|
||||
)
|
||||
}
|
||||
|
||||
private class IntegerOrPointerConstantInstruction extends ConstantInstruction {
|
||||
IntegerOrPointerConstantInstruction() {
|
||||
this instanceof IntegerConstantInstruction or
|
||||
this instanceof PointerConstantInstruction
|
||||
}
|
||||
}
|
||||
|
||||
/** The int value of integer constant expression. */
|
||||
private int int_value(Instruction i) { result = i.(IntegerConstantInstruction).getValue().toInt() }
|
||||
private int int_value(Instruction i) {
|
||||
result = i.(IntegerOrPointerConstantInstruction).getValue().toInt()
|
||||
}
|
||||
|
||||
556
cpp/ql/lib/semmle/code/cpp/dataflow/ExternalFlow.qll
Normal file
556
cpp/ql/lib/semmle/code/cpp/dataflow/ExternalFlow.qll
Normal file
@@ -0,0 +1,556 @@
|
||||
/**
|
||||
* INTERNAL use only. This is an experimental API subject to change without notice.
|
||||
*
|
||||
* Provides classes and predicates for dealing with flow models specified in CSV format.
|
||||
*
|
||||
* The CSV specification has the following columns:
|
||||
* - Sources:
|
||||
* `namespace; type; subtypes; name; signature; ext; output; kind`
|
||||
* - Sinks:
|
||||
* `namespace; type; subtypes; name; signature; ext; input; kind`
|
||||
* - Summaries:
|
||||
* `namespace; type; subtypes; name; signature; ext; input; output; kind`
|
||||
*
|
||||
* The interpretation of a row is similar to API-graphs with a left-to-right
|
||||
* reading.
|
||||
* 1. The `namespace` column selects a namespace.
|
||||
* 2. The `type` column selects a type within that namespace.
|
||||
* 3. The `subtypes` is a boolean that indicates whether to jump to an
|
||||
* arbitrary subtype of that type. Set this to `false` if leaving the `type`
|
||||
* blank (for example, a free function).
|
||||
* 4. The `name` column optionally selects a specific named member of the type.
|
||||
* 5. The `signature` column optionally restricts the named member. If
|
||||
* `signature` is blank then no such filtering is done. The format of the
|
||||
* signature is a comma-separated list of types enclosed in parentheses. The
|
||||
* types can be short names or fully qualified names (mixing these two options
|
||||
* is not allowed within a single signature).
|
||||
* 6. The `ext` column specifies additional API-graph-like edges. Currently
|
||||
* there is only one valid value: "".
|
||||
* 7. The `input` column specifies how data enters the element selected by the
|
||||
* first 6 columns, and the `output` column specifies how data leaves the
|
||||
* element selected by the first 6 columns. An `input` can be either:
|
||||
* - "": Selects a write to the selected element in case this is a field.
|
||||
* - "Argument[n]": Selects an argument in a call to the selected element.
|
||||
* The arguments are zero-indexed, and `-1` specifies the qualifier object,
|
||||
* that is, `*this`.
|
||||
* - one or more "*" can be added in front of the argument index to indicate
|
||||
* indirection, for example, `Argument[*0]` indicates the first indirection
|
||||
* of the 0th argument.
|
||||
* - `n1..n2` syntax can be used to indicate a range of arguments, inclusive
|
||||
* at both ends. One or more "*"s can be added in front of the whole range
|
||||
* to indicate that every argument in the range is indirect, for example
|
||||
* `*0..1` is the first indirection of both arguments 0 and 1.
|
||||
* - "ReturnValue": Selects a value being returned by the selected element.
|
||||
* One or more "*" can be added as an argument to indicate indirection, for
|
||||
* example, "ReturnValue[*]" indicates the first indirection of the return
|
||||
* value.
|
||||
*
|
||||
* An `output` can be either:
|
||||
* - "": Selects a read of a selected field.
|
||||
* - "Argument[n]": Selects the post-update value of an argument in a call to
|
||||
* the selected element. That is, the value of the argument after the call
|
||||
* returns. The arguments are zero-indexed, and `-1` specifies the qualifier
|
||||
* object, that is, `*this`.
|
||||
* - one or more "*" can be added in front of the argument index to indicate
|
||||
* indirection, for example, `Argument[*0]` indicates the first indirection
|
||||
* of the 0th argument.
|
||||
* - `n1..n2` syntax can be used to indicate a range of arguments, inclusive
|
||||
* at both ends. One or more "*"s can be added in front of the whole range
|
||||
* to indicate that every argument in the range is indirect, for example
|
||||
* `*0..1` is the first indirection of both arguments 0 and 1.
|
||||
* - "Parameter[n]": Selects the value of a parameter of the selected element.
|
||||
* The syntax is the same as for "Argument", for example "Parameter[0]",
|
||||
* "Parameter[*0]", "Parameter[0..2]" etc.
|
||||
* - "ReturnValue": Selects a value being returned by the selected element.
|
||||
* One or more "*" can be added as an argument to indicate indirection, for
|
||||
* example, "ReturnValue[*]" indicates the first indirection of the return
|
||||
* value.
|
||||
* 8. The `kind` column is a tag that can be referenced from QL to determine to
|
||||
* which classes the interpreted elements should be added. For example, for
|
||||
* sources "remote" indicates a default remote flow source, and for summaries
|
||||
* "taint" indicates a default additional taint step and "value" indicates a
|
||||
* globally applicable value-preserving step.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
private import new.DataFlow
|
||||
private import internal.FlowSummaryImpl
|
||||
private import internal.FlowSummaryImpl::Public
|
||||
private import internal.FlowSummaryImpl::Private
|
||||
private import internal.FlowSummaryImpl::Private::External
|
||||
private import codeql.mad.ModelValidation as SharedModelVal
|
||||
private import codeql.util.Unit
|
||||
|
||||
/**
|
||||
* A unit class for adding additional source model rows.
|
||||
*
|
||||
* Extend this class to add additional source definitions.
|
||||
*/
|
||||
class SourceModelCsv extends Unit {
|
||||
/** Holds if `row` specifies a source definition. */
|
||||
abstract predicate row(string row);
|
||||
}
|
||||
|
||||
/**
|
||||
* A unit class for adding additional sink model rows.
|
||||
*
|
||||
* Extend this class to add additional sink definitions.
|
||||
*/
|
||||
class SinkModelCsv extends Unit {
|
||||
/** Holds if `row` specifies a sink definition. */
|
||||
abstract predicate row(string row);
|
||||
}
|
||||
|
||||
/**
|
||||
* A unit class for adding additional summary model rows.
|
||||
*
|
||||
* Extend this class to add additional flow summary definitions.
|
||||
*/
|
||||
class SummaryModelCsv extends Unit {
|
||||
/** Holds if `row` specifies a summary definition. */
|
||||
abstract predicate row(string row);
|
||||
}
|
||||
|
||||
/** Holds if `row` is a source model. */
|
||||
predicate sourceModel(string row) { any(SourceModelCsv s).row(row) }
|
||||
|
||||
/** Holds if `row` is a sink model. */
|
||||
predicate sinkModel(string row) { any(SinkModelCsv s).row(row) }
|
||||
|
||||
/** Holds if `row` is a summary model. */
|
||||
predicate summaryModel(string row) { any(SummaryModelCsv s).row(row) }
|
||||
|
||||
/** Holds if a source model exists for the given parameters. */
|
||||
predicate sourceModel(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext,
|
||||
string output, string kind, string provenance
|
||||
) {
|
||||
exists(string row |
|
||||
sourceModel(row) and
|
||||
row.splitAt(";", 0) = namespace and
|
||||
row.splitAt(";", 1) = type and
|
||||
row.splitAt(";", 2) = subtypes.toString() and
|
||||
subtypes = [true, false] and
|
||||
row.splitAt(";", 3) = name and
|
||||
row.splitAt(";", 4) = signature and
|
||||
row.splitAt(";", 5) = ext and
|
||||
row.splitAt(";", 6) = output and
|
||||
row.splitAt(";", 7) = kind
|
||||
) and
|
||||
provenance = "manual"
|
||||
}
|
||||
|
||||
/** Holds if a sink model exists for the given parameters. */
|
||||
predicate sinkModel(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext,
|
||||
string input, string kind, string provenance
|
||||
) {
|
||||
exists(string row |
|
||||
sinkModel(row) and
|
||||
row.splitAt(";", 0) = namespace and
|
||||
row.splitAt(";", 1) = type and
|
||||
row.splitAt(";", 2) = subtypes.toString() and
|
||||
subtypes = [true, false] and
|
||||
row.splitAt(";", 3) = name and
|
||||
row.splitAt(";", 4) = signature and
|
||||
row.splitAt(";", 5) = ext and
|
||||
row.splitAt(";", 6) = input and
|
||||
row.splitAt(";", 7) = kind
|
||||
) and
|
||||
provenance = "manual"
|
||||
}
|
||||
|
||||
/** Holds if a summary model exists for the given parameters. */
|
||||
predicate summaryModel(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext,
|
||||
string input, string output, string kind, string provenance
|
||||
) {
|
||||
exists(string row |
|
||||
summaryModel(row) and
|
||||
row.splitAt(";", 0) = namespace and
|
||||
row.splitAt(";", 1) = type and
|
||||
row.splitAt(";", 2) = subtypes.toString() and
|
||||
subtypes = [true, false] and
|
||||
row.splitAt(";", 3) = name and
|
||||
row.splitAt(";", 4) = signature and
|
||||
row.splitAt(";", 5) = ext and
|
||||
row.splitAt(";", 6) = input and
|
||||
row.splitAt(";", 7) = output and
|
||||
row.splitAt(";", 8) = kind
|
||||
) and
|
||||
provenance = "manual"
|
||||
}
|
||||
|
||||
private predicate relevantNamespace(string namespace) {
|
||||
sourceModel(namespace, _, _, _, _, _, _, _, _) or
|
||||
sinkModel(namespace, _, _, _, _, _, _, _, _) or
|
||||
summaryModel(namespace, _, _, _, _, _, _, _, _, _)
|
||||
}
|
||||
|
||||
private predicate namespaceLink(string shortns, string longns) {
|
||||
relevantNamespace(shortns) and
|
||||
relevantNamespace(longns) and
|
||||
longns.prefix(longns.indexOf("::")) = shortns
|
||||
}
|
||||
|
||||
private predicate canonicalNamespace(string namespace) {
|
||||
relevantNamespace(namespace) and not namespaceLink(_, namespace)
|
||||
}
|
||||
|
||||
private predicate canonicalNamespaceLink(string namespace, string subns) {
|
||||
canonicalNamespace(namespace) and
|
||||
(subns = namespace or namespaceLink(namespace, subns))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if CSV framework coverage of `namespace` is `n` api endpoints of the
|
||||
* kind `(kind, part)`.
|
||||
*/
|
||||
predicate modelCoverage(string namespace, int namespaces, string kind, string part, int n) {
|
||||
namespaces = strictcount(string subns | canonicalNamespaceLink(namespace, subns)) and
|
||||
(
|
||||
part = "source" and
|
||||
n =
|
||||
strictcount(string subns, string type, boolean subtypes, string name, string signature,
|
||||
string ext, string output, string provenance |
|
||||
canonicalNamespaceLink(namespace, subns) and
|
||||
sourceModel(subns, type, subtypes, name, signature, ext, output, kind, provenance)
|
||||
)
|
||||
or
|
||||
part = "sink" and
|
||||
n =
|
||||
strictcount(string subns, string type, boolean subtypes, string name, string signature,
|
||||
string ext, string input, string provenance |
|
||||
canonicalNamespaceLink(namespace, subns) and
|
||||
sinkModel(subns, type, subtypes, name, signature, ext, input, kind, provenance)
|
||||
)
|
||||
or
|
||||
part = "summary" and
|
||||
n =
|
||||
strictcount(string subns, string type, boolean subtypes, string name, string signature,
|
||||
string ext, string input, string output, string provenance |
|
||||
canonicalNamespaceLink(namespace, subns) and
|
||||
summaryModel(subns, type, subtypes, name, signature, ext, input, output, kind, provenance)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** Provides a query predicate to check the CSV data for validation errors. */
|
||||
module CsvValidation {
|
||||
private string getInvalidModelInput() {
|
||||
exists(string pred, AccessPath input, string part |
|
||||
sinkModel(_, _, _, _, _, _, input, _, _) and pred = "sink"
|
||||
or
|
||||
summaryModel(_, _, _, _, _, _, input, _, _, _) and pred = "summary"
|
||||
|
|
||||
(
|
||||
invalidSpecComponent(input, part) and
|
||||
not part = "" and
|
||||
not (part = "Argument" and pred = "sink") and
|
||||
not parseArg(part, _)
|
||||
or
|
||||
part = input.getToken(_) and
|
||||
parseParam(part, _)
|
||||
) and
|
||||
result = "Unrecognized input specification \"" + part + "\" in " + pred + " model."
|
||||
)
|
||||
}
|
||||
|
||||
private string getInvalidModelOutput() {
|
||||
exists(string pred, string output, string part |
|
||||
sourceModel(_, _, _, _, _, _, output, _, _) and pred = "source"
|
||||
or
|
||||
summaryModel(_, _, _, _, _, _, _, output, _, _) and pred = "summary"
|
||||
|
|
||||
invalidSpecComponent(output, part) and
|
||||
not part = "" and
|
||||
not (part = ["Argument", "Parameter"] and pred = "source") and
|
||||
result = "Unrecognized output specification \"" + part + "\" in " + pred + " model."
|
||||
)
|
||||
}
|
||||
|
||||
private module KindValConfig implements SharedModelVal::KindValidationConfigSig {
|
||||
predicate summaryKind(string kind) { summaryModel(_, _, _, _, _, _, _, _, kind, _) }
|
||||
|
||||
predicate sinkKind(string kind) { sinkModel(_, _, _, _, _, _, _, kind, _) }
|
||||
|
||||
predicate sourceKind(string kind) { sourceModel(_, _, _, _, _, _, _, kind, _) }
|
||||
}
|
||||
|
||||
private module KindVal = SharedModelVal::KindValidation<KindValConfig>;
|
||||
|
||||
private string getInvalidModelSubtype() {
|
||||
exists(string pred, string row |
|
||||
sourceModel(row) and pred = "source"
|
||||
or
|
||||
sinkModel(row) and pred = "sink"
|
||||
or
|
||||
summaryModel(row) and pred = "summary"
|
||||
|
|
||||
exists(string b |
|
||||
b = row.splitAt(";", 2) and
|
||||
not b = ["true", "false"] and
|
||||
result = "Invalid boolean \"" + b + "\" in " + pred + " model."
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private string getInvalidModelColumnCount() {
|
||||
exists(string pred, string row, int expect |
|
||||
sourceModel(row) and expect = 8 and pred = "source"
|
||||
or
|
||||
sinkModel(row) and expect = 8 and pred = "sink"
|
||||
or
|
||||
summaryModel(row) and expect = 9 and pred = "summary"
|
||||
|
|
||||
exists(int cols |
|
||||
cols = 1 + max(int n | exists(row.splitAt(";", n))) and
|
||||
cols != expect and
|
||||
result =
|
||||
"Wrong number of columns in " + pred + " model row, expected " + expect + ", got " + cols +
|
||||
"."
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private string getInvalidModelSignature() {
|
||||
exists(string pred, string namespace, string type, string name, string signature, string ext |
|
||||
sourceModel(namespace, type, _, name, signature, ext, _, _, _) and pred = "source"
|
||||
or
|
||||
sinkModel(namespace, type, _, name, signature, ext, _, _, _) and pred = "sink"
|
||||
or
|
||||
summaryModel(namespace, type, _, name, signature, ext, _, _, _, _) and pred = "summary"
|
||||
|
|
||||
not namespace.regexpMatch("[a-zA-Z0-9_\\.]+") and
|
||||
result = "Dubious namespace \"" + namespace + "\" in " + pred + " model."
|
||||
or
|
||||
not type.regexpMatch("[a-zA-Z0-9_<>,\\+]+") and
|
||||
result = "Dubious type \"" + type + "\" in " + pred + " model."
|
||||
or
|
||||
not name.regexpMatch("[a-zA-Z0-9_<>,]*") and
|
||||
result = "Dubious member name \"" + name + "\" in " + pred + " model."
|
||||
or
|
||||
not signature.regexpMatch("|\\([a-zA-Z0-9_<>\\.\\+\\*,\\[\\]]*\\)") and
|
||||
result = "Dubious signature \"" + signature + "\" in " + pred + " model."
|
||||
or
|
||||
not ext.regexpMatch("|Attribute") and
|
||||
result = "Unrecognized extra API graph element \"" + ext + "\" in " + pred + " model."
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if some row in a CSV-based flow model appears to contain typos. */
|
||||
query predicate invalidModelRow(string msg) {
|
||||
msg =
|
||||
[
|
||||
getInvalidModelSignature(), getInvalidModelInput(), getInvalidModelOutput(),
|
||||
getInvalidModelSubtype(), getInvalidModelColumnCount(), KindVal::getInvalidModelKind()
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
private predicate elementSpec(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
||||
) {
|
||||
sourceModel(namespace, type, subtypes, name, signature, ext, _, _, _) or
|
||||
sinkModel(namespace, type, subtypes, name, signature, ext, _, _, _) or
|
||||
summaryModel(namespace, type, subtypes, name, signature, ext, _, _, _, _)
|
||||
}
|
||||
|
||||
private string paramsStringPart(Function c, int i) {
|
||||
i = -1 and result = "(" and exists(c)
|
||||
or
|
||||
exists(int n, string p | c.getParameter(n).getType().toString() = p |
|
||||
i = 2 * n and result = p
|
||||
or
|
||||
i = 2 * n - 1 and result = "," and n != 0
|
||||
)
|
||||
or
|
||||
i = 2 * c.getNumberOfParameters() and result = ")"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a parenthesized string containing all parameter types of this callable, separated by a comma.
|
||||
*
|
||||
* Returns the empty string if the callable has no parameters.
|
||||
* Parameter types are represented by their type erasure.
|
||||
*/
|
||||
cached
|
||||
private string paramsString(Function c) {
|
||||
result = concat(int i | | paramsStringPart(c, i) order by i)
|
||||
}
|
||||
|
||||
bindingset[func]
|
||||
private predicate matchesSignature(Function func, string signature) {
|
||||
signature = "" or
|
||||
paramsString(func) = signature
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the element in module `namespace` that satisfies the following properties:
|
||||
* 1. If the element is a member of a class-like type, then the class-like type has name `type`
|
||||
* 2. If `subtypes = true` and the element is a member of a class-like type, then overrides of the element
|
||||
* are also returned.
|
||||
* 3. The element has name `name`
|
||||
* 4. If `signature` is non-empty, then the element has a list of parameter types described by `signature`.
|
||||
*
|
||||
* NOTE: `namespace` is currently not used (since we don't properly extract modules yet).
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private Element interpretElement0(
|
||||
string namespace, string type, boolean subtypes, string name, string signature
|
||||
) {
|
||||
elementSpec(namespace, type, subtypes, name, signature, _) and
|
||||
(
|
||||
// Non-member functions
|
||||
exists(Function func |
|
||||
func.hasQualifiedName(namespace, name) and
|
||||
type = "" and
|
||||
matchesSignature(func, signature) and
|
||||
subtypes = false and
|
||||
not exists(func.getDeclaringType()) and
|
||||
result = func
|
||||
)
|
||||
or
|
||||
// Member functions
|
||||
exists(Class namedClass, Class classWithMethod, Function method |
|
||||
classWithMethod = method.getClassAndName(name) and
|
||||
namedClass.hasQualifiedName(namespace, type) and
|
||||
matchesSignature(method, signature) and
|
||||
result = method
|
||||
|
|
||||
// member declared in the named type or a subtype of it
|
||||
subtypes = true and
|
||||
classWithMethod = namedClass.getADerivedClass*()
|
||||
or
|
||||
// member declared directly in the named type
|
||||
subtypes = false and
|
||||
classWithMethod = namedClass
|
||||
)
|
||||
or
|
||||
// Member variables
|
||||
signature = "" and
|
||||
exists(Class namedClass, Class classWithMember, MemberVariable member |
|
||||
member.getName() = name and
|
||||
member = classWithMember.getAMember() and
|
||||
namedClass.hasQualifiedName(namespace, type) and
|
||||
result = member
|
||||
|
|
||||
// field declared in the named type or a subtype of it (or an extension of any)
|
||||
subtypes = true and
|
||||
classWithMember = namedClass.getADerivedClass*()
|
||||
or
|
||||
// field declared directly in the named type (or an extension of it)
|
||||
subtypes = false and
|
||||
classWithMember = namedClass
|
||||
)
|
||||
or
|
||||
// Global or namespace variables
|
||||
signature = "" and
|
||||
type = "" and
|
||||
subtypes = false and
|
||||
result = any(GlobalOrNamespaceVariable v | v.hasQualifiedName(namespace, name))
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the source/sink/summary element corresponding to the supplied parameters. */
|
||||
Element interpretElement(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
||||
) {
|
||||
elementSpec(namespace, type, subtypes, name, signature, ext) and
|
||||
exists(Element e | e = interpretElement0(namespace, type, subtypes, name, signature) |
|
||||
ext = "" and result = e
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
private module Cached {
|
||||
/**
|
||||
* Holds if `node` is specified as a source with the given kind in a CSV flow
|
||||
* model.
|
||||
*/
|
||||
cached
|
||||
predicate sourceNode(DataFlow::Node node, string kind) {
|
||||
exists(SourceSinkInterpretationInput::InterpretNode n |
|
||||
isSourceNode(n, kind, _) and n.asNode() = node // TODO
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is specified as a sink with the given kind in a CSV flow
|
||||
* model.
|
||||
*/
|
||||
cached
|
||||
predicate sinkNode(DataFlow::Node node, string kind) {
|
||||
exists(SourceSinkInterpretationInput::InterpretNode n |
|
||||
isSinkNode(n, kind, _) and n.asNode() = node // TODO
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
import Cached
|
||||
|
||||
private predicate interpretSummary(
|
||||
Function f, string input, string output, string kind, string provenance
|
||||
) {
|
||||
exists(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
||||
|
|
||||
summaryModel(namespace, type, subtypes, name, signature, ext, input, output, kind, provenance) and
|
||||
f = interpretElement(namespace, type, subtypes, name, signature, ext)
|
||||
)
|
||||
}
|
||||
|
||||
// adapter class for converting Mad summaries to `SummarizedCallable`s
|
||||
private class SummarizedCallableAdapter extends SummarizedCallable {
|
||||
SummarizedCallableAdapter() { interpretSummary(this, _, _, _, _) }
|
||||
|
||||
private predicate relevantSummaryElementManual(string input, string output, string kind) {
|
||||
exists(Provenance provenance |
|
||||
interpretSummary(this, input, output, kind, provenance) and
|
||||
provenance.isManual()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate relevantSummaryElementGenerated(string input, string output, string kind) {
|
||||
exists(Provenance provenance |
|
||||
interpretSummary(this, input, output, kind, provenance) and
|
||||
provenance.isGenerated()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate propagatesFlow(
|
||||
string input, string output, boolean preservesValue, string model
|
||||
) {
|
||||
exists(string kind |
|
||||
this.relevantSummaryElementManual(input, output, kind)
|
||||
or
|
||||
not this.relevantSummaryElementManual(_, _, _) and
|
||||
this.relevantSummaryElementGenerated(input, output, kind)
|
||||
|
|
||||
if kind = "value" then preservesValue = true else preservesValue = false
|
||||
) and
|
||||
model = "" // TODO
|
||||
}
|
||||
|
||||
override predicate hasProvenance(Provenance provenance) {
|
||||
interpretSummary(this, _, _, _, provenance)
|
||||
}
|
||||
}
|
||||
|
||||
// adapter class for converting Mad neutrals to `NeutralCallable`s
|
||||
private class NeutralCallableAdapter extends NeutralCallable {
|
||||
string kind;
|
||||
string provenance_;
|
||||
|
||||
NeutralCallableAdapter() {
|
||||
// Neutral models have not been implemented for CPP.
|
||||
none() and
|
||||
exists(this) and
|
||||
exists(kind) and
|
||||
exists(provenance_)
|
||||
}
|
||||
|
||||
override string getKind() { result = kind }
|
||||
|
||||
override predicate hasProvenance(Provenance provenance) { provenance = provenance_ }
|
||||
}
|
||||
@@ -290,6 +290,8 @@ predicate knownSourceModel(Node source, string model) { none() }
|
||||
|
||||
predicate knownSinkModel(Node sink, string model) { none() }
|
||||
|
||||
class DataFlowSecondLevelScope = Unit;
|
||||
|
||||
/**
|
||||
* Holds if flow is allowed to pass from parameter `p` and back to itself as a
|
||||
* side-effect, resulting in a summary from `p` to itself.
|
||||
|
||||
271
cpp/ql/lib/semmle/code/cpp/dataflow/internal/FlowSummaryImpl.qll
Normal file
271
cpp/ql/lib/semmle/code/cpp/dataflow/internal/FlowSummaryImpl.qll
Normal file
@@ -0,0 +1,271 @@
|
||||
/**
|
||||
* Provides classes and predicates for defining flow summaries.
|
||||
*/
|
||||
|
||||
private import cpp as Cpp
|
||||
private import codeql.dataflow.internal.FlowSummaryImpl
|
||||
private import codeql.dataflow.internal.AccessPathSyntax as AccessPath
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowImplSpecific as DataFlowImplSpecific
|
||||
private import semmle.code.cpp.dataflow.ExternalFlow
|
||||
private import semmle.code.cpp.ir.IR
|
||||
|
||||
module Input implements InputSig<Location, DataFlowImplSpecific::CppDataFlow> {
|
||||
class SummarizedCallableBase = Function;
|
||||
|
||||
ArgumentPosition callbackSelfParameterPosition() { result = TDirectPosition(-1) }
|
||||
|
||||
ReturnKind getStandardReturnValueKind() { result.(NormalReturnKind).getIndirectionIndex() = 0 }
|
||||
|
||||
string encodeParameterPosition(ParameterPosition pos) { result = pos.toString() }
|
||||
|
||||
string encodeArgumentPosition(ArgumentPosition pos) { result = pos.toString() }
|
||||
|
||||
string encodeReturn(ReturnKind rk, string arg) {
|
||||
rk != getStandardReturnValueKind() and
|
||||
result = "ReturnValue" and
|
||||
arg = repeatStars(rk.(NormalReturnKind).getIndirectionIndex())
|
||||
}
|
||||
|
||||
string encodeContent(ContentSet cs, string arg) {
|
||||
exists(FieldContent c |
|
||||
cs.isSingleton(c) and
|
||||
// FieldContent indices have 0 for the address, 1 for content, so we need to subtract one.
|
||||
result = "Field" and
|
||||
arg = repeatStars(c.getIndirectionIndex() - 1) + c.getField().getName()
|
||||
)
|
||||
}
|
||||
|
||||
string encodeWithoutContent(ContentSet c, string arg) {
|
||||
// used for type tracking, not currently used in C/C++.
|
||||
result = "WithoutContent" + c and arg = ""
|
||||
}
|
||||
|
||||
string encodeWithContent(ContentSet c, string arg) {
|
||||
// used for type tracking, not currently used in C/C++.
|
||||
result = "WithContent" + c and arg = ""
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes an argument / parameter position string, for example the `0` in `Argument[0]`.
|
||||
* Supports ranges (`Argument[x..y]`), qualifiers (`Argument[-1]`), indirections
|
||||
* (`Argument[*x]`) and combinations (such as `Argument[**0..1]`).
|
||||
*/
|
||||
bindingset[argString]
|
||||
private TPosition decodePosition(string argString) {
|
||||
exists(int indirection, string posString, int pos |
|
||||
argString = repeatStars(indirection) + posString and
|
||||
pos = AccessPath::parseInt(posString) and
|
||||
(
|
||||
pos >= 0 and indirection = 0 and result = TDirectPosition(pos)
|
||||
or
|
||||
pos >= 0 and indirection > 0 and result = TIndirectionPosition(pos, indirection)
|
||||
or
|
||||
// `Argument[-1]` / `Parameter[-1]` is the qualifier object `*this`, not the `this` pointer itself.
|
||||
pos = -1 and result = TIndirectionPosition(pos, indirection + 1)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[token]
|
||||
ParameterPosition decodeUnknownParameterPosition(AccessPath::AccessPathTokenBase token) {
|
||||
token.getName() = "Argument" and
|
||||
result = decodePosition(token.getAnArgument())
|
||||
}
|
||||
|
||||
bindingset[token]
|
||||
ArgumentPosition decodeUnknownArgumentPosition(AccessPath::AccessPathTokenBase token) {
|
||||
token.getName() = "Parameter" and
|
||||
result = decodePosition(token.getAnArgument())
|
||||
}
|
||||
|
||||
bindingset[token]
|
||||
ContentSet decodeUnknownContent(AccessPath::AccessPathTokenBase token) {
|
||||
// field content (no indirection support)
|
||||
exists(FieldContent c |
|
||||
result.isSingleton(c) and
|
||||
token.getName() = c.getField().getName() and
|
||||
not exists(token.getArgumentList()) and
|
||||
c.getIndirectionIndex() = 1
|
||||
)
|
||||
or
|
||||
// field content (with indirection support)
|
||||
exists(FieldContent c |
|
||||
result.isSingleton(c) and
|
||||
token.getName() = c.getField().getName() and
|
||||
// FieldContent indices have 0 for the address, 1 for content, so we need to subtract one.
|
||||
token.getAnArgument() = repeatStars(c.getIndirectionIndex() - 1)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private import Make<Location, DataFlowImplSpecific::CppDataFlow, Input> as Impl
|
||||
|
||||
private module StepsInput implements Impl::Private::StepsInputSig {
|
||||
DataFlowCall getACall(Public::SummarizedCallable sc) {
|
||||
result.getStaticCallTarget().getUnderlyingCallable() = sc
|
||||
}
|
||||
}
|
||||
|
||||
module SourceSinkInterpretationInput implements
|
||||
Impl::Private::External::SourceSinkInterpretationInputSig
|
||||
{
|
||||
class Element = Cpp::Element;
|
||||
|
||||
class SourceOrSinkElement = Element;
|
||||
|
||||
/**
|
||||
* Holds if an external source specification exists for `e` with output specification
|
||||
* `output`, kind `kind`, and provenance `provenance`.
|
||||
*/
|
||||
predicate sourceElement(
|
||||
SourceOrSinkElement e, string output, string kind, Public::Provenance provenance, string model
|
||||
) {
|
||||
exists(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
||||
|
|
||||
sourceModel(namespace, type, subtypes, name, signature, ext, output, kind, provenance) and
|
||||
e = interpretElement(namespace, type, subtypes, name, signature, ext) and
|
||||
model = "" // TODO
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an external sink specification exists for `e` with input specification
|
||||
* `input`, kind `kind` and provenance `provenance`.
|
||||
*/
|
||||
predicate sinkElement(
|
||||
SourceOrSinkElement e, string input, string kind, Public::Provenance provenance, string model
|
||||
) {
|
||||
exists(
|
||||
string package, string type, boolean subtypes, string name, string signature, string ext
|
||||
|
|
||||
sinkModel(package, type, subtypes, name, signature, ext, input, kind, provenance) and
|
||||
e = interpretElement(package, type, subtypes, name, signature, ext) and
|
||||
model = "" // TODO
|
||||
)
|
||||
}
|
||||
|
||||
private newtype TInterpretNode =
|
||||
TElement_(Element n) or
|
||||
TNode_(Node n)
|
||||
|
||||
/** An entity used to interpret a source/sink specification. */
|
||||
class InterpretNode extends TInterpretNode {
|
||||
/** Gets the element that this node corresponds to, if any. */
|
||||
SourceOrSinkElement asElement() { this = TElement_(result) }
|
||||
|
||||
/** Gets the data-flow node that this node corresponds to, if any. */
|
||||
Node asNode() { this = TNode_(result) }
|
||||
|
||||
/** Gets the call that this node corresponds to, if any. */
|
||||
DataFlowCall asCall() {
|
||||
this.asElement() = result.asCallInstruction().getUnconvertedResultExpression()
|
||||
}
|
||||
|
||||
/** Gets the callable that this node corresponds to, if any. */
|
||||
DataFlowCallable asCallable() { result.getUnderlyingCallable() = this.asElement() }
|
||||
|
||||
/** Gets the target of this call, if any. */
|
||||
Element getCallTarget() { result = this.asCall().getStaticCallTarget().getUnderlyingCallable() }
|
||||
|
||||
/** Gets a textual representation of this node. */
|
||||
string toString() {
|
||||
result = this.asElement().toString()
|
||||
or
|
||||
result = this.asNode().toString()
|
||||
or
|
||||
result = this.asCall().toString()
|
||||
}
|
||||
|
||||
/** Gets the location of this node. */
|
||||
Location getLocation() {
|
||||
result = this.asElement().getLocation()
|
||||
or
|
||||
result = this.asNode().getLocation()
|
||||
or
|
||||
result = this.asCall().getLocation()
|
||||
}
|
||||
}
|
||||
|
||||
/** Provides additional sink specification logic. */
|
||||
bindingset[c]
|
||||
predicate interpretOutput(string c, InterpretNode mid, InterpretNode node) {
|
||||
// Allow variables to be picked as output nodes.
|
||||
exists(Node n, Element ast |
|
||||
n = node.asNode() and
|
||||
ast = mid.asElement()
|
||||
|
|
||||
c = "" and
|
||||
n.asExpr().(VariableAccess).getTarget() = ast
|
||||
)
|
||||
}
|
||||
|
||||
/** Provides additional source specification logic. */
|
||||
bindingset[c]
|
||||
predicate interpretInput(string c, InterpretNode mid, InterpretNode node) {
|
||||
exists(Node n, Element ast, VariableAccess e |
|
||||
n = node.asNode() and
|
||||
ast = mid.asElement() and
|
||||
e.getTarget() = ast
|
||||
|
|
||||
// Allow variables to be picked as input nodes.
|
||||
// We could simply do this as `e = n.asExpr()`, but that would not allow
|
||||
// us to pick `x` as a sink in an example such as `x = source()` (but
|
||||
// only subsequent uses of `x`) since the variable access on `x` doesn't
|
||||
// actually load the value of `x`. So instead, we pick the instruction
|
||||
// node corresponding to the generated `StoreInstruction` and use the
|
||||
// expression associated with the destination instruction. This means
|
||||
// that the `x` in `x = source()` can be marked as an input.
|
||||
c = "" and
|
||||
exists(StoreInstruction store |
|
||||
store.getDestinationAddress().getUnconvertedResultExpression() = e and
|
||||
n.asInstruction() = store
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module Private {
|
||||
import Impl::Private
|
||||
|
||||
module Steps = Impl::Private::Steps<StepsInput>;
|
||||
|
||||
module External {
|
||||
import Impl::Private::External
|
||||
import Impl::Private::External::SourceSinkInterpretation<SourceSinkInterpretationInput>
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides predicates for constructing summary components.
|
||||
*/
|
||||
module SummaryComponent {
|
||||
private import Impl::Private::SummaryComponent as SC
|
||||
|
||||
predicate parameter = SC::parameter/1;
|
||||
|
||||
predicate argument = SC::argument/1;
|
||||
|
||||
predicate content = SC::content/1;
|
||||
|
||||
predicate withoutContent = SC::withoutContent/1;
|
||||
|
||||
predicate withContent = SC::withContent/1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides predicates for constructing stacks of summary components.
|
||||
*/
|
||||
module SummaryComponentStack {
|
||||
private import Impl::Private::SummaryComponentStack as SCS
|
||||
|
||||
predicate singleton = SCS::singleton/1;
|
||||
|
||||
predicate push = SCS::push/2;
|
||||
|
||||
predicate argument = SCS::argument/1;
|
||||
}
|
||||
}
|
||||
|
||||
module Public = Impl::Public;
|
||||
@@ -1338,6 +1338,24 @@ class CoAwaitExpr extends UnaryOperation, @co_await {
|
||||
override string getOperator() { result = "co_await" }
|
||||
|
||||
override int getPrecedence() { result = 16 }
|
||||
|
||||
/**
|
||||
* Gets the Boolean expression that is used to decide if the enclosing
|
||||
* coroutine should be suspended.
|
||||
*/
|
||||
Expr getAwaitReady() { result = this.getChild(1) }
|
||||
|
||||
/**
|
||||
* Gets the expression that represents the resume point if the enclosing
|
||||
* coroutine was suspended.
|
||||
*/
|
||||
Expr getAwaitResume() { result = this.getChild(2) }
|
||||
|
||||
/**
|
||||
* Gets the expression that is evaluated when the enclosing coroutine is
|
||||
* suspended.
|
||||
*/
|
||||
Expr getAwaitSuspend() { result = this.getChild(3) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1352,6 +1370,24 @@ class CoYieldExpr extends UnaryOperation, @co_yield {
|
||||
override string getOperator() { result = "co_yield" }
|
||||
|
||||
override int getPrecedence() { result = 2 }
|
||||
|
||||
/**
|
||||
* Gets the Boolean expression that is used to decide if the enclosing
|
||||
* coroutine should be suspended.
|
||||
*/
|
||||
Expr getAwaitReady() { result = this.getChild(1) }
|
||||
|
||||
/**
|
||||
* Gets the expression that represents the resume point if the enclosing
|
||||
* coroutine was suspended.
|
||||
*/
|
||||
Expr getAwaitResume() { result = this.getChild(2) }
|
||||
|
||||
/**
|
||||
* Gets the expression that is evaluated when the enclosing coroutine is
|
||||
* suspended.
|
||||
*/
|
||||
Expr getAwaitSuspend() { result = this.getChild(3) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1371,17 +1407,7 @@ class ReuseExpr extends Expr, @reuseexpr {
|
||||
/**
|
||||
* Gets the expression that is being re-used.
|
||||
*/
|
||||
Expr getReusedExpr() {
|
||||
// In the case of a prvalue, the extractor outputs the expression
|
||||
// before conversion, but the converted expression is intended.
|
||||
if this.isPRValueCategory()
|
||||
then result = this.getBaseReusedExpr().getFullyConverted()
|
||||
else result = this.getBaseReusedExpr()
|
||||
}
|
||||
|
||||
private Expr getBaseReusedExpr() {
|
||||
expr_reuse(underlyingElement(this), unresolveElement(result), _)
|
||||
}
|
||||
Expr getReusedExpr() { expr_reuse(underlyingElement(this), unresolveElement(result), _) }
|
||||
|
||||
override Type getType() { result = this.getReusedExpr().getType() }
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import cpp
|
||||
private import semmle.code.cpp.ir.ValueNumbering
|
||||
private import internal.DataFlowDispatch
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate
|
||||
|
||||
/**
|
||||
* Resolve potential target function(s) for `call`.
|
||||
@@ -16,8 +17,9 @@ private import semmle.code.cpp.ir.IR
|
||||
* to identify the possible target(s).
|
||||
*/
|
||||
Function resolveCall(Call call) {
|
||||
exists(CallInstruction callInstruction |
|
||||
exists(DataFlowCall dataFlowCall, CallInstruction callInstruction |
|
||||
callInstruction.getAst() = call and
|
||||
result = viableCallable(callInstruction)
|
||||
callInstruction = dataFlowCall.asCallInstruction() and
|
||||
result = viableCallable(dataFlowCall).getUnderlyingCallable()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -23,13 +23,13 @@ DataFlowCallable defaultViableCallable(DataFlowCall call) {
|
||||
// function with the right signature is present in the database, we return
|
||||
// that as a potential callee.
|
||||
exists(string qualifiedName, int nparams |
|
||||
callSignatureWithoutBody(qualifiedName, nparams, call) and
|
||||
functionSignatureWithBody(qualifiedName, nparams, result) and
|
||||
callSignatureWithoutBody(qualifiedName, nparams, call.asCallInstruction()) and
|
||||
functionSignatureWithBody(qualifiedName, nparams, result.getUnderlyingCallable()) and
|
||||
strictcount(Function other | functionSignatureWithBody(qualifiedName, nparams, other)) = 1
|
||||
)
|
||||
or
|
||||
// Virtual dispatch
|
||||
result = call.(VirtualDispatch::DataSensitiveCall).resolve()
|
||||
result.asSourceCallable() = call.(VirtualDispatch::DataSensitiveCall).resolve()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -40,7 +40,9 @@ DataFlowCallable viableCallable(DataFlowCall call) {
|
||||
result = defaultViableCallable(call)
|
||||
or
|
||||
// Additional call targets
|
||||
result = any(AdditionalCallTarget additional).viableTarget(call.getUnconvertedResultExpression())
|
||||
result.getUnderlyingCallable() =
|
||||
any(AdditionalCallTarget additional)
|
||||
.viableTarget(call.asCallInstruction().getUnconvertedResultExpression())
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -150,7 +152,7 @@ private module VirtualDispatch {
|
||||
ReturnNode node, ReturnKind kind, DataFlowCallable callable
|
||||
) {
|
||||
node.getKind() = kind and
|
||||
node.getEnclosingCallable() = callable
|
||||
node.getEnclosingCallable() = callable.getUnderlyingCallable()
|
||||
}
|
||||
|
||||
/** Call through a function pointer. */
|
||||
@@ -176,10 +178,15 @@ private module VirtualDispatch {
|
||||
/** Call to a virtual function. */
|
||||
private class DataSensitiveOverriddenFunctionCall extends DataSensitiveCall {
|
||||
DataSensitiveOverriddenFunctionCall() {
|
||||
exists(this.getStaticCallTarget().(VirtualFunction).getAnOverridingFunction())
|
||||
exists(
|
||||
this.getStaticCallTarget()
|
||||
.getUnderlyingCallable()
|
||||
.(VirtualFunction)
|
||||
.getAnOverridingFunction()
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getDispatchValue() { result.asInstruction() = this.getThisArgument() }
|
||||
override DataFlow::Node getDispatchValue() { result.asInstruction() = this.getArgument(-1) }
|
||||
|
||||
override MemberFunction resolve() {
|
||||
exists(Class overridingClass |
|
||||
@@ -194,7 +201,8 @@ private module VirtualDispatch {
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate overrideMayAffectCall(Class overridingClass, MemberFunction overridingFunction) {
|
||||
overridingFunction.getAnOverriddenFunction+() = this.getStaticCallTarget().(VirtualFunction) and
|
||||
overridingFunction.getAnOverriddenFunction+() =
|
||||
this.getStaticCallTarget().getUnderlyingCallable().(VirtualFunction) and
|
||||
overridingFunction.getDeclaringType() = overridingClass
|
||||
}
|
||||
|
||||
@@ -256,12 +264,12 @@ predicate mayBenefitFromCallContext(DataFlowCall call) { mayBenefitFromCallConte
|
||||
* value is given as the `arg`'th argument to `f`.
|
||||
*/
|
||||
private predicate mayBenefitFromCallContext(
|
||||
VirtualDispatch::DataSensitiveCall call, Function f, int arg
|
||||
VirtualDispatch::DataSensitiveCall call, DataFlowCallable f, int arg
|
||||
) {
|
||||
f = pragma[only_bind_out](call).getEnclosingCallable() and
|
||||
exists(InitializeParameterInstruction init |
|
||||
not exists(call.getStaticCallTarget()) and
|
||||
init.getEnclosingFunction() = f and
|
||||
init.getEnclosingFunction() = f.getUnderlyingCallable() and
|
||||
call.flowsFrom(DataFlow::instructionNode(init), _) and
|
||||
init.getParameter().getIndex() = arg
|
||||
)
|
||||
@@ -273,10 +281,11 @@ private predicate mayBenefitFromCallContext(
|
||||
*/
|
||||
DataFlowCallable viableImplInCallContext(DataFlowCall call, DataFlowCall ctx) {
|
||||
result = viableCallable(call) and
|
||||
exists(int i, Function f |
|
||||
exists(int i, DataFlowCallable f |
|
||||
mayBenefitFromCallContext(pragma[only_bind_into](call), f, i) and
|
||||
f = ctx.getStaticCallTarget() and
|
||||
result = ctx.getArgument(i).getUnconvertedResultExpression().(FunctionAccess).getTarget()
|
||||
result.asSourceCallable() =
|
||||
ctx.getArgument(i).getUnconvertedResultExpression().(FunctionAccess).getTarget()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -22,9 +22,13 @@ module CppDataFlow implements InputSig<Location> {
|
||||
|
||||
predicate getAdditionalFlowIntoCallNodeTerm = Private::getAdditionalFlowIntoCallNodeTerm/2;
|
||||
|
||||
predicate getSecondLevelScope = Private::getSecondLevelScope/1;
|
||||
|
||||
predicate validParameterAliasStep = Private::validParameterAliasStep/2;
|
||||
|
||||
predicate mayBenefitFromCallContext = Private::mayBenefitFromCallContext/1;
|
||||
|
||||
predicate viableImplInCallContext = Private::viableImplInCallContext/2;
|
||||
|
||||
predicate neverSkipInPathGraph = Private::neverSkipInPathGraph/1;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -10,6 +10,7 @@ private import semmle.code.cpp.ir.ValueNumbering
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import semmle.code.cpp.controlflow.IRGuards
|
||||
private import semmle.code.cpp.models.interfaces.DataFlow
|
||||
private import semmle.code.cpp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
private import DataFlowPrivate
|
||||
private import ModelUtil
|
||||
private import SsaInternals as Ssa
|
||||
@@ -45,6 +46,7 @@ private newtype TIRDataFlowNode =
|
||||
Ssa::isModifiableByCall(operand, indirectionIndex)
|
||||
} or
|
||||
TSsaPhiNode(Ssa::PhiNode phi) or
|
||||
TSsaIteratorNode(IteratorFlow::IteratorFlowNode n) or
|
||||
TRawIndirectOperand0(Node0Impl node, int indirectionIndex) {
|
||||
Ssa::hasRawIndirectOperand(node.asOperand(), indirectionIndex)
|
||||
} or
|
||||
@@ -59,7 +61,17 @@ private newtype TIRDataFlowNode =
|
||||
)
|
||||
} or
|
||||
TFinalGlobalValue(Ssa::GlobalUse globalUse) or
|
||||
TInitialGlobalValue(Ssa::GlobalDef globalUse)
|
||||
TInitialGlobalValue(Ssa::GlobalDef globalUse) or
|
||||
TBodyLessParameterNodeImpl(Parameter p, int indirectionIndex) {
|
||||
// Rule out parameters of catch blocks.
|
||||
not exists(p.getCatchBlock()) and
|
||||
// We subtract one because `getMaxIndirectionsForType` returns the maximum
|
||||
// indirection for a glvalue of a given type, and this doesn't apply to
|
||||
// parameters.
|
||||
indirectionIndex = [0 .. Ssa::getMaxIndirectionsForType(p.getUnspecifiedType()) - 1] and
|
||||
not any(InitializeParameterInstruction init).getParameter() = p
|
||||
} or
|
||||
TFlowSummaryNode(FlowSummaryImpl::Private::SummaryNode sn)
|
||||
|
||||
/**
|
||||
* An operand that is defined by a `FieldAddressInstruction`.
|
||||
@@ -387,7 +399,7 @@ class Node extends TIRDataFlowNode {
|
||||
index = 0 and
|
||||
result = this.(ExplicitParameterNode).getParameter()
|
||||
or
|
||||
this.(IndirectParameterNode).hasInstructionAndIndirectionIndex(_, index) and
|
||||
this.(IndirectParameterNode).getIndirectionIndex() = index and
|
||||
result = this.(IndirectParameterNode).getParameter()
|
||||
}
|
||||
|
||||
@@ -642,6 +654,30 @@ class SsaPhiNode extends Node, TSsaPhiNode {
|
||||
predicate isPhiRead() { phi.isPhiRead() }
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL: do not use.
|
||||
*
|
||||
* Dataflow nodes necessary for iterator flow
|
||||
*/
|
||||
class SsaIteratorNode extends Node, TSsaIteratorNode {
|
||||
IteratorFlow::IteratorFlowNode node;
|
||||
|
||||
SsaIteratorNode() { this = TSsaIteratorNode(node) }
|
||||
|
||||
/** Gets the phi node associated with this node. */
|
||||
IteratorFlow::IteratorFlowNode getIteratorFlowNode() { result = node }
|
||||
|
||||
override Declaration getEnclosingCallable() { result = this.getFunction() }
|
||||
|
||||
override Declaration getFunction() { result = node.getFunction() }
|
||||
|
||||
override DataFlowType getType() { result = node.getType() }
|
||||
|
||||
final override Location getLocationImpl() { result = node.getLocation() }
|
||||
|
||||
override string toStringImpl() { result = node.toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL: do not use.
|
||||
*
|
||||
@@ -738,37 +774,65 @@ class InitialGlobalValue extends Node, TInitialGlobalValue {
|
||||
/**
|
||||
* INTERNAL: do not use.
|
||||
*
|
||||
* A node representing an indirection of a parameter.
|
||||
* A node representing a parameter for a function with no body.
|
||||
*/
|
||||
class IndirectParameterNode extends Node instanceof IndirectInstruction {
|
||||
InitializeParameterInstruction init;
|
||||
class BodyLessParameterNodeImpl extends Node, TBodyLessParameterNodeImpl {
|
||||
Parameter p;
|
||||
int indirectionIndex;
|
||||
|
||||
IndirectParameterNode() { IndirectInstruction.super.hasInstructionAndIndirectionIndex(init, _) }
|
||||
|
||||
int getArgumentIndex() { init.hasIndex(result) }
|
||||
|
||||
/** Gets the parameter whose indirection is initialized. */
|
||||
Parameter getParameter() { result = init.getParameter() }
|
||||
BodyLessParameterNodeImpl() { this = TBodyLessParameterNodeImpl(p, indirectionIndex) }
|
||||
|
||||
override Declaration getEnclosingCallable() { result = this.getFunction() }
|
||||
|
||||
override Declaration getFunction() { result = init.getEnclosingFunction() }
|
||||
override Declaration getFunction() { result = p.getFunction() }
|
||||
|
||||
/** Gets the underlying operand and the underlying indirection index. */
|
||||
predicate hasInstructionAndIndirectionIndex(Instruction instr, int index) {
|
||||
IndirectInstruction.super.hasInstructionAndIndirectionIndex(instr, index)
|
||||
/** Gets the indirection index of this node. */
|
||||
int getIndirectionIndex() { result = indirectionIndex }
|
||||
|
||||
override DataFlowType getType() {
|
||||
result = getTypeImpl(p.getUnderlyingType(), this.getIndirectionIndex())
|
||||
}
|
||||
|
||||
override Location getLocationImpl() { result = this.getParameter().getLocation() }
|
||||
|
||||
override string toStringImpl() {
|
||||
exists(string prefix | prefix = stars(this) |
|
||||
result = prefix + this.getParameter().toString()
|
||||
or
|
||||
not exists(this.getParameter()) and
|
||||
result = prefix + "this"
|
||||
)
|
||||
final override Location getLocationImpl() {
|
||||
result = unique( | | p.getLocation())
|
||||
or
|
||||
count(p.getLocation()) != 1 and
|
||||
result instanceof UnknownDefaultLocation
|
||||
}
|
||||
|
||||
final override string toStringImpl() {
|
||||
exists(string prefix | prefix = stars(this) | result = prefix + p.toString())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A data-flow node used to model flow summaries. That is, a dataflow node
|
||||
* that is synthesized to represent a parameter, return value, or other part
|
||||
* of a models-as-data modeled function.
|
||||
*/
|
||||
class FlowSummaryNode extends Node, TFlowSummaryNode {
|
||||
/**
|
||||
* Gets the models-as-data `SummaryNode` associated with this dataflow
|
||||
* `FlowSummaryNode`.
|
||||
*/
|
||||
FlowSummaryImpl::Private::SummaryNode getSummaryNode() { this = TFlowSummaryNode(result) }
|
||||
|
||||
/**
|
||||
* Gets the summarized callable that this node belongs to.
|
||||
*/
|
||||
FlowSummaryImpl::Public::SummarizedCallable getSummarizedCallable() {
|
||||
result = this.getSummaryNode().getSummarizedCallable()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the enclosing callable. For a `FlowSummaryNode` this is always the
|
||||
* summarized function this node is part of.
|
||||
*/
|
||||
override Declaration getEnclosingCallable() { result = this.getSummarizedCallable() }
|
||||
|
||||
override Location getLocationImpl() { result = this.getSummarizedCallable().getLocation() }
|
||||
|
||||
override string toStringImpl() { result = this.getSummaryNode().toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -826,6 +890,9 @@ class IndirectArgumentOutNode extends PostUpdateNodeImpl {
|
||||
|
||||
CallInstruction getCallInstruction() { result.getAnArgumentOperand() = operand }
|
||||
|
||||
/**
|
||||
* Gets the `Function` that the call targets, if this is statically known.
|
||||
*/
|
||||
Function getStaticCallTarget() { result = this.getCallInstruction().getStaticCallTarget() }
|
||||
|
||||
override string toStringImpl() {
|
||||
@@ -1148,11 +1215,11 @@ class UninitializedNode extends Node {
|
||||
LocalVariable v;
|
||||
|
||||
UninitializedNode() {
|
||||
exists(Ssa::Def def |
|
||||
exists(Ssa::Def def, Ssa::SourceVariable sv |
|
||||
def.getIndirectionIndex() = 0 and
|
||||
def.getValue().asInstruction() instanceof UninitializedInstruction and
|
||||
Ssa::nodeToDefOrUse(this, def, _) and
|
||||
v = def.getSourceVariable().getBaseVariable().(Ssa::BaseIRVariable).getIRVariable().getAst()
|
||||
Ssa::defToNode(this, def, sv, _, _, _) and
|
||||
v = sv.getBaseVariable().(Ssa::BaseIRVariable).getIRVariable().getAst()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1620,68 +1687,36 @@ class IndirectExprNode extends Node instanceof IndirectExprNodeBase {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The value of a parameter at function entry, viewed as a node in a data
|
||||
* flow graph. This includes both explicit parameters such as `x` in `f(x)`
|
||||
* and implicit parameters such as `this` in `x.f()`.
|
||||
*
|
||||
* To match a specific kind of parameter, consider using one of the subclasses
|
||||
* `ExplicitParameterNode`, `ThisParameterNode`, or
|
||||
* `ParameterIndirectionNode`.
|
||||
*/
|
||||
class ParameterNode extends Node {
|
||||
ParameterNode() {
|
||||
// To avoid making this class abstract, we enumerate its values here
|
||||
this.asInstruction() instanceof InitializeParameterInstruction
|
||||
or
|
||||
this instanceof IndirectParameterNode
|
||||
}
|
||||
|
||||
abstract private class AbstractParameterNode extends Node {
|
||||
/**
|
||||
* Holds if this node is the parameter of `f` at the specified position. The
|
||||
* implicit `this` parameter is considered to have position `-1`, and
|
||||
* pointer-indirection parameters are at further negative positions.
|
||||
*/
|
||||
predicate isParameterOf(Function f, ParameterPosition pos) { none() } // overridden by subclasses
|
||||
abstract predicate isParameterOf(DataFlowCallable f, ParameterPosition pos);
|
||||
|
||||
/** Gets the `Parameter` associated with this node, if it exists. */
|
||||
Parameter getParameter() { none() } // overridden by subclasses
|
||||
}
|
||||
|
||||
/** An explicit positional parameter, including `this`, but not `...`. */
|
||||
class DirectParameterNode extends InstructionNode {
|
||||
override InitializeParameterInstruction instr;
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* Gets the `IRVariable` that this parameter references.
|
||||
*/
|
||||
IRVariable getIRVariable() { result = instr.getIRVariable() }
|
||||
abstract private class AbstractIndirectParameterNode extends AbstractParameterNode {
|
||||
/** Gets the indirection index of this parameter node. */
|
||||
abstract int getIndirectionIndex();
|
||||
}
|
||||
|
||||
/** An explicit positional parameter, not including `this` or `...`. */
|
||||
private class ExplicitParameterNode extends ParameterNode, DirectParameterNode {
|
||||
ExplicitParameterNode() { exists(instr.getParameter()) }
|
||||
/**
|
||||
* INTERNAL: do not use.
|
||||
*
|
||||
* A node representing an indirection of a parameter.
|
||||
*/
|
||||
final class IndirectParameterNode = AbstractIndirectParameterNode;
|
||||
|
||||
override predicate isParameterOf(Function f, ParameterPosition pos) {
|
||||
f.getParameter(pos.(DirectPosition).getIndex()) = instr.getParameter()
|
||||
}
|
||||
|
||||
override string toStringImpl() { result = instr.getParameter().toString() }
|
||||
|
||||
override Parameter getParameter() { result = instr.getParameter() }
|
||||
}
|
||||
|
||||
/** An implicit `this` parameter. */
|
||||
class ThisParameterNode extends ParameterNode, DirectParameterNode {
|
||||
ThisParameterNode() { instr.getIRVariable() instanceof IRThisVariable }
|
||||
|
||||
override predicate isParameterOf(Function f, ParameterPosition pos) {
|
||||
pos.(DirectPosition).getIndex() = -1 and instr.getEnclosingFunction() = f
|
||||
}
|
||||
|
||||
override string toStringImpl() { result = "this" }
|
||||
pragma[noinline]
|
||||
private predicate indirectParameterNodeHasArgumentIndexAndIndex(
|
||||
IndirectInstructionParameterNode node, int argumentIndex, int indirectionIndex
|
||||
) {
|
||||
node.hasInstructionAndIndirectionIndex(_, indirectionIndex) and
|
||||
node.getArgumentIndex() = argumentIndex
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
@@ -1692,23 +1727,167 @@ private predicate indirectPositionHasArgumentIndexAndIndex(
|
||||
pos.getIndirectionIndex() = indirectionIndex
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate indirectParameterNodeHasArgumentIndexAndIndex(
|
||||
IndirectParameterNode node, int argumentIndex, int indirectionIndex
|
||||
) {
|
||||
node.hasInstructionAndIndirectionIndex(_, indirectionIndex) and
|
||||
node.getArgumentIndex() = argumentIndex
|
||||
}
|
||||
private class IndirectInstructionParameterNode extends AbstractIndirectParameterNode instanceof IndirectInstruction
|
||||
{
|
||||
InitializeParameterInstruction init;
|
||||
|
||||
/** A synthetic parameter to model the pointed-to object of a pointer parameter. */
|
||||
class ParameterIndirectionNode extends ParameterNode instanceof IndirectParameterNode {
|
||||
override predicate isParameterOf(Function f, ParameterPosition pos) {
|
||||
IndirectParameterNode.super.getEnclosingCallable() = f and
|
||||
IndirectInstructionParameterNode() {
|
||||
IndirectInstruction.super.hasInstructionAndIndirectionIndex(init, _)
|
||||
}
|
||||
|
||||
int getArgumentIndex() { init.hasIndex(result) }
|
||||
|
||||
override string toStringImpl() {
|
||||
exists(string prefix | prefix = stars(this) |
|
||||
result = prefix + this.getParameter().toString()
|
||||
or
|
||||
not exists(this.getParameter()) and
|
||||
result = prefix + "this"
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the parameter whose indirection is initialized. */
|
||||
override Parameter getParameter() { result = init.getParameter() }
|
||||
|
||||
override Declaration getEnclosingCallable() { result = this.getFunction() }
|
||||
|
||||
override Declaration getFunction() { result = init.getEnclosingFunction() }
|
||||
|
||||
override predicate isParameterOf(DataFlowCallable f, ParameterPosition pos) {
|
||||
this.getEnclosingCallable() = f.getUnderlyingCallable() and
|
||||
exists(int argumentIndex, int indirectionIndex |
|
||||
indirectPositionHasArgumentIndexAndIndex(pos, argumentIndex, indirectionIndex) and
|
||||
indirectParameterNodeHasArgumentIndexAndIndex(this, argumentIndex, indirectionIndex)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the underlying operand and the underlying indirection index. */
|
||||
predicate hasInstructionAndIndirectionIndex(Instruction instr, int index) {
|
||||
IndirectInstruction.super.hasInstructionAndIndirectionIndex(instr, index)
|
||||
}
|
||||
|
||||
final override int getIndirectionIndex() { this.hasInstructionAndIndirectionIndex(init, result) }
|
||||
}
|
||||
|
||||
/**
|
||||
* The value of a parameter at function entry, viewed as a node in a data
|
||||
* flow graph. This includes both explicit parameters such as `x` in `f(x)`
|
||||
* and implicit parameters such as `this` in `x.f()`.
|
||||
*
|
||||
* To match a specific kind of parameter, consider using one of the subclasses
|
||||
* `ExplicitParameterNode`, `ThisParameterNode`, or
|
||||
* `ParameterIndirectionNode`.
|
||||
*/
|
||||
final class ParameterNode = AbstractParameterNode;
|
||||
|
||||
abstract private class AbstractDirectParameterNode extends AbstractParameterNode { }
|
||||
|
||||
/** An explicit positional parameter, including `this`, but not `...`. */
|
||||
final class DirectParameterNode = AbstractDirectParameterNode;
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* A non-indirect parameter node that is represented as an `Instruction`.
|
||||
*/
|
||||
abstract class InstructionDirectParameterNode extends InstructionNode, AbstractDirectParameterNode {
|
||||
final override InitializeParameterInstruction instr;
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* Gets the `IRVariable` that this parameter references.
|
||||
*/
|
||||
final IRVariable getIRVariable() { result = instr.getIRVariable() }
|
||||
}
|
||||
|
||||
abstract private class AbstractExplicitParameterNode extends AbstractDirectParameterNode { }
|
||||
|
||||
final class ExplicitParameterNode = AbstractExplicitParameterNode;
|
||||
|
||||
/** An explicit positional parameter, not including `this` or `...`. */
|
||||
private class ExplicitParameterInstructionNode extends AbstractExplicitParameterNode,
|
||||
InstructionDirectParameterNode
|
||||
{
|
||||
ExplicitParameterInstructionNode() { exists(instr.getParameter()) }
|
||||
|
||||
override predicate isParameterOf(DataFlowCallable f, ParameterPosition pos) {
|
||||
f.getUnderlyingCallable().(Function).getParameter(pos.(DirectPosition).getIndex()) =
|
||||
instr.getParameter()
|
||||
}
|
||||
|
||||
override string toStringImpl() { result = instr.getParameter().toString() }
|
||||
|
||||
override Parameter getParameter() { result = instr.getParameter() }
|
||||
}
|
||||
|
||||
/** An implicit `this` parameter. */
|
||||
class ThisParameterInstructionNode extends AbstractExplicitParameterNode,
|
||||
InstructionDirectParameterNode
|
||||
{
|
||||
ThisParameterInstructionNode() { instr.getIRVariable() instanceof IRThisVariable }
|
||||
|
||||
override predicate isParameterOf(DataFlowCallable f, ParameterPosition pos) {
|
||||
pos.(DirectPosition).getIndex() = -1 and
|
||||
instr.getEnclosingFunction() = f.getUnderlyingCallable()
|
||||
}
|
||||
|
||||
override string toStringImpl() { result = "this" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A parameter node that is part of a summary.
|
||||
*/
|
||||
class SummaryParameterNode extends AbstractParameterNode, FlowSummaryNode {
|
||||
SummaryParameterNode() {
|
||||
FlowSummaryImpl::Private::summaryParameterNode(this.getSummaryNode(), _)
|
||||
}
|
||||
|
||||
private ParameterPosition getPosition() {
|
||||
FlowSummaryImpl::Private::summaryParameterNode(this.getSummaryNode(), result)
|
||||
}
|
||||
|
||||
override predicate isParameterOf(DataFlowCallable c, ParameterPosition p) {
|
||||
c.getUnderlyingCallable() = this.getSummarizedCallable() and
|
||||
p = this.getPosition()
|
||||
}
|
||||
}
|
||||
|
||||
private class DirectBodyLessParameterNode extends AbstractExplicitParameterNode,
|
||||
BodyLessParameterNodeImpl
|
||||
{
|
||||
DirectBodyLessParameterNode() { indirectionIndex = 0 }
|
||||
|
||||
override predicate isParameterOf(DataFlowCallable f, ParameterPosition pos) {
|
||||
exists(Function func |
|
||||
this.getFunction() = func and
|
||||
f.asSourceCallable() = func and
|
||||
func.getParameter(pos.(DirectPosition).getIndex()) = p
|
||||
)
|
||||
}
|
||||
|
||||
override Parameter getParameter() { result = p }
|
||||
}
|
||||
|
||||
private class IndirectBodyLessParameterNode extends AbstractIndirectParameterNode,
|
||||
BodyLessParameterNodeImpl
|
||||
{
|
||||
IndirectBodyLessParameterNode() { not this instanceof DirectBodyLessParameterNode }
|
||||
|
||||
override predicate isParameterOf(DataFlowCallable f, ParameterPosition pos) {
|
||||
exists(Function func, int argumentPosition |
|
||||
this.getFunction() = func and
|
||||
f.asSourceCallable() = func and
|
||||
indirectPositionHasArgumentIndexAndIndex(pos, argumentPosition, indirectionIndex) and
|
||||
func.getParameter(argumentPosition) = p
|
||||
)
|
||||
}
|
||||
|
||||
override int getIndirectionIndex() {
|
||||
result = BodyLessParameterNodeImpl.super.getIndirectionIndex()
|
||||
}
|
||||
|
||||
override Parameter getParameter() { result = p }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1753,6 +1932,22 @@ abstract private class PartialDefinitionNode extends PostUpdateNode {
|
||||
abstract Expr getDefinedExpr();
|
||||
}
|
||||
|
||||
/**
|
||||
* A `PostUpdateNode` that is part of a flow summary. These are synthesized,
|
||||
* for example, when a models-as-data summary models a write to a field since
|
||||
* the write needs to target a `PostUpdateNode`.
|
||||
*/
|
||||
class SummaryPostUpdateNode extends FlowSummaryNode, PostUpdateNode {
|
||||
SummaryPostUpdateNode() {
|
||||
FlowSummaryImpl::Private::summaryPostUpdateNode(this.getSummaryNode(), _)
|
||||
}
|
||||
|
||||
override Node getPreUpdateNode() {
|
||||
FlowSummaryImpl::Private::summaryPostUpdateNode(this.getSummaryNode(),
|
||||
result.(FlowSummaryNode).getSummaryNode())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A node that represents the value of a variable after a function call that
|
||||
* may have changed the variable because it's passed by reference.
|
||||
@@ -1889,10 +2084,20 @@ cached
|
||||
private module Cached {
|
||||
/**
|
||||
* Holds if data flows from `nodeFrom` to `nodeTo` in exactly one local
|
||||
* (intra-procedural) step.
|
||||
* (intra-procedural) step. This relation is only used for local dataflow
|
||||
* (for example `DataFlow::localFlow(source, sink)`) so it contains
|
||||
* special cases that should only apply to local dataflow.
|
||||
*/
|
||||
cached
|
||||
predicate localFlowStep(Node nodeFrom, Node nodeTo) { simpleLocalFlowStep(nodeFrom, nodeTo, _) }
|
||||
predicate localFlowStep(Node nodeFrom, Node nodeTo) {
|
||||
// common dataflow steps
|
||||
simpleLocalFlowStep(nodeFrom, nodeTo, _)
|
||||
or
|
||||
// models-as-data summarized flow for local data flow (i.e. special case for flow
|
||||
// through calls to modeled functions, without relying on global dataflow to join
|
||||
// the dots).
|
||||
FlowSummaryImpl::Private::Steps::summaryThroughStepValue(nodeFrom, nodeTo, _)
|
||||
}
|
||||
|
||||
private predicate indirectionOperandFlow(RawIndirectOperand nodeFrom, Node nodeTo) {
|
||||
nodeFrom != nodeTo and
|
||||
@@ -1958,8 +2163,9 @@ private module Cached {
|
||||
/**
|
||||
* INTERNAL: do not use.
|
||||
*
|
||||
* This is the local flow predicate that's used as a building block in global
|
||||
* data flow. It may have less flow than the `localFlowStep` predicate.
|
||||
* This is the local flow predicate that's used as a building block in both
|
||||
* local and global data flow. It may have less flow than the `localFlowStep`
|
||||
* predicate.
|
||||
*/
|
||||
cached
|
||||
predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo, string model) {
|
||||
@@ -1970,6 +2176,8 @@ private module Cached {
|
||||
// Def-use/Use-use flow
|
||||
Ssa::ssaFlow(nodeFrom, nodeTo)
|
||||
or
|
||||
IteratorFlow::localFlowStep(nodeFrom, nodeTo)
|
||||
or
|
||||
// Operand -> Instruction flow
|
||||
simpleInstructionLocalFlowStep(nodeFrom.asOperand(), nodeTo.asInstruction())
|
||||
or
|
||||
@@ -2001,6 +2209,10 @@ private module Cached {
|
||||
// function such as `operator[]`.
|
||||
reverseFlow(nodeFrom, nodeTo) and
|
||||
model = ""
|
||||
or
|
||||
// models-as-data summarized flow
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom.(FlowSummaryNode).getSummaryNode(),
|
||||
nodeTo.(FlowSummaryNode).getSummaryNode(), true, model)
|
||||
}
|
||||
|
||||
private predicate simpleInstructionLocalFlowStep(Operand opFrom, Instruction iTo) {
|
||||
@@ -2242,6 +2454,8 @@ private Field getAFieldWithSize(Union u, int bytes) {
|
||||
cached
|
||||
private newtype TContent =
|
||||
TFieldContent(Field f, int indirectionIndex) {
|
||||
// the indirection index for field content starts at 1 (because `TFieldContent` is thought of as
|
||||
// the address of the field, `FieldAddress` in the IR).
|
||||
indirectionIndex = [1 .. Ssa::getMaxIndirectionsForType(f.getUnspecifiedType())] and
|
||||
// Reads and writes of union fields are tracked using `UnionContent`.
|
||||
not f.getDeclaringType() instanceof Union
|
||||
@@ -2251,7 +2465,8 @@ private newtype TContent =
|
||||
f = u.getAField() and
|
||||
bytes = getFieldSize(f) and
|
||||
// We key `UnionContent` by the union instead of its fields since a write to one
|
||||
// field can be read by any read of the union's fields.
|
||||
// field can be read by any read of the union's fields. Again, the indirection index
|
||||
// is 1-based (because 0 is considered the address).
|
||||
indirectionIndex =
|
||||
[1 .. max(Ssa::getMaxIndirectionsForType(getAFieldWithSize(u, bytes).getUnspecifiedType()))]
|
||||
)
|
||||
@@ -2285,16 +2500,14 @@ class Content extends TContent {
|
||||
abstract predicate impliesClearOf(Content c);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a string consisting of `n` star characters ("*"), where n >= 0. This is
|
||||
* used to represent indirection.
|
||||
*/
|
||||
bindingset[n]
|
||||
string repeatStars(int n) { result = concat(int i | i in [1 .. n] | "*") }
|
||||
|
||||
private module ContentStars {
|
||||
private int maxNumberOfIndirections() { result = max(any(Content c).getIndirectionIndex()) }
|
||||
|
||||
private string repeatStars(int n) {
|
||||
n = 0 and result = ""
|
||||
or
|
||||
n = [1 .. maxNumberOfIndirections()] and
|
||||
result = "*" + repeatStars(n - 1)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of stars (i.e., `*`s) needed to produce the `toString`
|
||||
* output for `c`.
|
||||
@@ -2374,6 +2587,12 @@ class UnionContent extends Content, TUnionContent {
|
||||
* stored into (`getAStoreContent`) or read from (`getAReadContent`).
|
||||
*/
|
||||
class ContentSet instanceof Content {
|
||||
/**
|
||||
* Holds if this content set is the singleton `{c}`. At present, this is
|
||||
* the only kind of content set supported in C/C++.
|
||||
*/
|
||||
predicate isSingleton(Content c) { this = c }
|
||||
|
||||
/** Gets a content that may be stored into when storing into this set. */
|
||||
Content getAStoreContent() { result = this }
|
||||
|
||||
@@ -2591,5 +2810,5 @@ class AdditionalCallTarget extends Unit {
|
||||
/**
|
||||
* Gets a viable target for `call`.
|
||||
*/
|
||||
abstract DataFlowCallable viableTarget(Call call);
|
||||
abstract Declaration viableTarget(Call call);
|
||||
}
|
||||
|
||||
@@ -3,12 +3,26 @@
|
||||
* `toString` for `Instruction` and `Operand` dataflow nodes.
|
||||
*/
|
||||
|
||||
private import cpp
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import codeql.util.Unit
|
||||
private import Node0ToString
|
||||
private import DataFlowUtil
|
||||
private import DataFlowPrivate
|
||||
|
||||
/**
|
||||
* Gets the string representation of the unconverted expression `loc` if
|
||||
* `loc` is an `Expression`.
|
||||
*
|
||||
* Otherwise, this gets the string representation of `loc`.
|
||||
*/
|
||||
private string unconvertedAstToString(Locatable loc) {
|
||||
result = loc.(Expr).getUnconverted().toString()
|
||||
or
|
||||
not loc instanceof Expr and
|
||||
result = loc.toString()
|
||||
}
|
||||
|
||||
private class NormalNode0ToString extends Node0ToString {
|
||||
NormalNode0ToString() {
|
||||
// Silence warning about `this` not being bound.
|
||||
@@ -18,14 +32,10 @@ private class NormalNode0ToString extends Node0ToString {
|
||||
override string instructionToString(Instruction i) {
|
||||
if i.(InitializeParameterInstruction).getIRVariable() instanceof IRThisVariable
|
||||
then result = "this"
|
||||
else result = i.getAst().toString()
|
||||
else result = unconvertedAstToString(i.getAst())
|
||||
}
|
||||
|
||||
override string operandToString(Operand op) {
|
||||
if op.getDef().(InitializeParameterInstruction).getIRVariable() instanceof IRThisVariable
|
||||
then result = "this"
|
||||
else result = op.getDef().getAst().toString()
|
||||
}
|
||||
override string operandToString(Operand op) { result = this.instructionToString(op.getDef()) }
|
||||
|
||||
override string toExprString(Node n) {
|
||||
result = n.asExpr(0).toString()
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -246,14 +246,6 @@ private module IteratorIndirections {
|
||||
baseType = super.getValueType()
|
||||
}
|
||||
|
||||
override predicate isAdditionalDereference(Instruction deref, Operand address) {
|
||||
exists(CallInstruction call |
|
||||
operandForFullyConvertedCall(getAUse(deref), call) and
|
||||
this = call.getStaticCallTarget().getClassAndName("operator*") and
|
||||
address = call.getThisArgumentOperand()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isAdditionalWrite(Node0Impl value, Operand address, boolean certain) {
|
||||
exists(CallInstruction call | call.getArgumentOperand(0) = value.asOperand() |
|
||||
this = call.getStaticCallTarget().getClassAndName("operator=") and
|
||||
@@ -262,16 +254,6 @@ private module IteratorIndirections {
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isAdditionalTaintStep(Node node1, Node node2) {
|
||||
exists(CallInstruction call |
|
||||
// Taint through `operator+=` and `operator-=` on iterators.
|
||||
call.getStaticCallTarget() instanceof Iterator::IteratorAssignArithmeticOperator and
|
||||
node2.(IndirectArgumentOutNode).getPreUpdateNode() = node1 and
|
||||
node1.(IndirectOperand).hasOperandAndIndirectionIndex(call.getArgumentOperand(0), _) and
|
||||
node1.getType().getUnspecifiedType() = this
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isAdditionalConversionFlow(Operand opFrom, Instruction instrTo) {
|
||||
// This is a bit annoying: Consider the following snippet:
|
||||
// ```
|
||||
@@ -589,230 +571,6 @@ private class BaseCallInstruction extends BaseSourceVariableInstruction, CallIns
|
||||
|
||||
cached
|
||||
private module Cached {
|
||||
private import semmle.code.cpp.models.interfaces.Iterator as Interfaces
|
||||
private import semmle.code.cpp.models.implementations.Iterator as Iterator
|
||||
private import semmle.code.cpp.models.interfaces.FunctionInputsAndOutputs as IO
|
||||
|
||||
/**
|
||||
* Holds if `next` is a instruction with a memory result that potentially
|
||||
* updates the memory produced by `prev`.
|
||||
*/
|
||||
private predicate memorySucc(Instruction prev, Instruction next) {
|
||||
prev = next.(ChiInstruction).getTotal()
|
||||
or
|
||||
// Phi inputs can be inexact.
|
||||
prev = next.(PhiInstruction).getAnInputOperand().getAnyDef()
|
||||
or
|
||||
prev = next.(CopyInstruction).getSourceValue()
|
||||
or
|
||||
exists(ReadSideEffectInstruction read |
|
||||
next = read.getPrimaryInstruction() and
|
||||
isAdditionalConversionFlow(_, next) and
|
||||
prev = read.getSideEffectOperand().getAnyDef()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `iteratorDerefAddress` is an address of an iterator dereference (i.e., `*it`)
|
||||
* that is used for a write operation that writes the value `value`. The `memory` instruction
|
||||
* represents the memory that the IR's SSA analysis determined was read by the call to `operator*`.
|
||||
*
|
||||
* The `numberOfLoads` integer represents the number of dereferences this write corresponds to
|
||||
* on the underlying container that produced the iterator.
|
||||
*/
|
||||
private predicate isChiAfterIteratorDef(
|
||||
Instruction memory, Operand iteratorDerefAddress, Node0Impl value, int numberOfLoads
|
||||
) {
|
||||
exists(
|
||||
BaseSourceVariableInstruction iteratorBase, ReadSideEffectInstruction read,
|
||||
Operand iteratorAddress
|
||||
|
|
||||
numberOfLoads >= 0 and
|
||||
isDef(_, value, iteratorDerefAddress, iteratorBase, numberOfLoads + 2, 0) and
|
||||
isUse(_, iteratorAddress, iteratorBase, numberOfLoads + 1, 0) and
|
||||
iteratorBase.getResultType() instanceof Interfaces::Iterator and
|
||||
isDereference(iteratorAddress.getDef(), read.getArgumentDef().getAUse(), _) and
|
||||
memory = read.getSideEffectOperand().getAnyDef()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate isSource(Instruction instr, Operand iteratorAddress, int numberOfLoads) {
|
||||
getAUse(instr) = iteratorAddress and
|
||||
exists(BaseSourceVariableInstruction iteratorBase |
|
||||
iteratorBase.getResultType() instanceof Interfaces::Iterator and
|
||||
not iteratorBase.getResultType() instanceof Cpp::PointerType and
|
||||
isUse(_, iteratorAddress, iteratorBase, numberOfLoads - 1, 0)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate isSink(Instruction instr, CallInstruction call) {
|
||||
getAUse(instr).(ArgumentOperand).getCall() = call and
|
||||
// Only include operations that may modify the object that the iterator points to.
|
||||
// The following is a non-exhaustive list of things that may modify the value of the
|
||||
// iterator, but never the value of what the iterator points to.
|
||||
// The more things we can exclude here, the faster the small dataflow-like analysis
|
||||
// done by `convertsIntoArgument` will converge.
|
||||
not exists(Function f | f = call.getStaticCallTarget() |
|
||||
f instanceof Iterator::IteratorCrementOperator or
|
||||
f instanceof Iterator::IteratorBinaryArithmeticOperator or
|
||||
f instanceof Iterator::IteratorAssignArithmeticOperator or
|
||||
f instanceof Iterator::IteratorCrementMemberOperator or
|
||||
f instanceof Iterator::IteratorBinaryArithmeticMemberOperator or
|
||||
f instanceof Iterator::IteratorAssignArithmeticMemberOperator or
|
||||
f instanceof Iterator::IteratorAssignmentMemberOperator
|
||||
)
|
||||
}
|
||||
|
||||
private predicate convertsIntoArgumentFwd(Instruction instr) {
|
||||
isSource(instr, _, _)
|
||||
or
|
||||
exists(Instruction prev | convertsIntoArgumentFwd(prev) |
|
||||
conversionFlow(unique( | | getAUse(prev)), instr, false, _)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate convertsIntoArgumentRev(Instruction instr) {
|
||||
convertsIntoArgumentFwd(instr) and
|
||||
(
|
||||
isSink(instr, _)
|
||||
or
|
||||
exists(Instruction next | convertsIntoArgumentRev(next) |
|
||||
conversionFlow(unique( | | getAUse(instr)), next, false, _)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate convertsIntoArgument(
|
||||
Operand iteratorAddress, CallInstruction call, int numberOfLoads
|
||||
) {
|
||||
exists(Instruction iteratorAddressDef |
|
||||
isSource(iteratorAddressDef, iteratorAddress, numberOfLoads) and
|
||||
isSink(iteratorAddressDef, call) and
|
||||
convertsIntoArgumentRev(pragma[only_bind_into](iteratorAddressDef))
|
||||
)
|
||||
}
|
||||
|
||||
private predicate isChiAfterIteratorArgument(
|
||||
Instruction memory, Operand iteratorAddress, int numberOfLoads
|
||||
) {
|
||||
// Ideally, `iteratorAddress` would be an `ArgumentOperand`, but there might be
|
||||
// various conversions applied to it before it becomes an argument.
|
||||
// So we do a small amount of flow to find the call that the iterator is passed to.
|
||||
exists(CallInstruction call | convertsIntoArgument(iteratorAddress, call, numberOfLoads) |
|
||||
exists(ReadSideEffectInstruction read |
|
||||
read.getPrimaryInstruction() = call and
|
||||
read.getSideEffectOperand().getAnyDef() = memory
|
||||
)
|
||||
or
|
||||
exists(LoadInstruction load |
|
||||
iteratorAddress.getDef() = load and
|
||||
memory = load.getSourceValueOperand().getAnyDef()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `iterator` is a `StoreInstruction` that stores the result of some function
|
||||
* returning an iterator into an address computed started at `containerBase`.
|
||||
*
|
||||
* For example, given a declaration like `std::vector<int>::iterator it = v.begin()`,
|
||||
* the `iterator` will be the `StoreInstruction` generated by the write to `it`, and
|
||||
* `containerBase` will be the address of `v`.
|
||||
*/
|
||||
private predicate isChiAfterBegin(
|
||||
BaseSourceVariableInstruction containerBase, StoreInstruction iterator
|
||||
) {
|
||||
exists(
|
||||
CallInstruction getIterator, Iterator::GetIteratorFunction getIteratorFunction,
|
||||
IO::FunctionInput input, int i
|
||||
|
|
||||
getIterator = iterator.getSourceValue() and
|
||||
getIteratorFunction = getIterator.getStaticCallTarget() and
|
||||
getIteratorFunction.getsIterator(input, _) and
|
||||
isDef(_, any(Node0Impl n | n.asInstruction() = iterator), _, _, 1, 0) and
|
||||
input.isParameterDerefOrQualifierObject(i) and
|
||||
isUse(_, getIterator.getArgumentOperand(i), containerBase, 0, 0)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `iteratorAddress` is an address of an iterator that is used for
|
||||
* a read operation. The `memory` instruction represents the memory that
|
||||
* the IR's SSA analysis determined was read by the call to `operator*`.
|
||||
*
|
||||
* Finally, the `numberOfLoads` integer represents the number of dereferences
|
||||
* this read corresponds to on the underlying container that produced the iterator.
|
||||
*/
|
||||
private predicate isChiBeforeIteratorUse(
|
||||
Operand iteratorAddress, Instruction memory, int numberOfLoads
|
||||
) {
|
||||
exists(
|
||||
BaseSourceVariableInstruction iteratorBase, LoadInstruction load,
|
||||
ReadSideEffectInstruction read, Operand iteratorDerefAddress
|
||||
|
|
||||
numberOfLoads >= 0 and
|
||||
isUse(_, iteratorAddress, iteratorBase, numberOfLoads + 1, 0) and
|
||||
isUse(_, iteratorDerefAddress, iteratorBase, numberOfLoads + 2, 0) and
|
||||
iteratorBase.getResultType() instanceof Interfaces::Iterator and
|
||||
load.getSourceAddressOperand() = iteratorDerefAddress and
|
||||
read.getPrimaryInstruction() = load.getSourceAddress() and
|
||||
memory = read.getSideEffectOperand().getAnyDef()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `iteratorDerefAddress` is an address of an iterator dereference (i.e., `*it`)
|
||||
* that is used for a write operation that writes the value `value` to a container that
|
||||
* created the iterator. `container` represents the base of the address of the container
|
||||
* that was used to create the iterator.
|
||||
*/
|
||||
cached
|
||||
predicate isIteratorDef(
|
||||
BaseSourceVariableInstruction container, Operand iteratorDerefAddress, Node0Impl value,
|
||||
int numberOfLoads, int indirectionIndex
|
||||
) {
|
||||
exists(Instruction memory, Instruction begin, int upper, int ind |
|
||||
isChiAfterIteratorDef(memory, iteratorDerefAddress, value, numberOfLoads) and
|
||||
memorySucc*(begin, memory) and
|
||||
isChiAfterBegin(container, begin) and
|
||||
upper = countIndirectionsForCppType(getResultLanguageType(container)) and
|
||||
ind = numberOfLoads + [1 .. upper] and
|
||||
indirectionIndex = ind - (numberOfLoads + 1)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `iteratorAddress` is an address of an iterator that is used for a
|
||||
* read operation to read a value from a container that created the iterator.
|
||||
* `container` represents the base of the address of the container that was used
|
||||
* to create the iterator.
|
||||
*/
|
||||
cached
|
||||
predicate isIteratorUse(
|
||||
BaseSourceVariableInstruction container, Operand iteratorAddress, int numberOfLoads,
|
||||
int indirectionIndex
|
||||
) {
|
||||
// Direct use
|
||||
exists(Instruction begin, Instruction memory, int upper, int ind |
|
||||
isChiBeforeIteratorUse(iteratorAddress, memory, numberOfLoads) and
|
||||
memorySucc*(begin, memory) and
|
||||
isChiAfterBegin(container, begin) and
|
||||
upper = countIndirectionsForCppType(getResultLanguageType(container)) and
|
||||
ind = numberOfLoads + [1 .. upper] and
|
||||
indirectionIndex = ind - (numberOfLoads + 1)
|
||||
)
|
||||
or
|
||||
// Use through function output
|
||||
exists(Instruction memory, Instruction begin, int upper, int ind |
|
||||
isChiAfterIteratorArgument(memory, iteratorAddress, numberOfLoads) and
|
||||
memorySucc*(begin, memory) and
|
||||
isChiAfterBegin(container, begin) and
|
||||
upper = countIndirectionsForCppType(getResultLanguageType(container)) and
|
||||
ind = numberOfLoads + [1 .. upper] and
|
||||
indirectionIndex = ind - (numberOfLoads - 1)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `op` is the only use of its defining instruction, and that op is used in a conversation */
|
||||
private predicate isConversion(Operand op) {
|
||||
exists(Instruction def, Operand use |
|
||||
|
||||
@@ -6,16 +6,26 @@ private import semmle.code.cpp.models.interfaces.SideEffect
|
||||
private import DataFlowUtil
|
||||
private import DataFlowPrivate
|
||||
private import SsaInternals as Ssa
|
||||
private import semmle.code.cpp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
private import semmle.code.cpp.ir.dataflow.FlowSteps
|
||||
|
||||
/**
|
||||
* Holds if taint propagates from `nodeFrom` to `nodeTo` in exactly one local
|
||||
* (intra-procedural) step.
|
||||
* (intra-procedural) step. This relation is only used for local taint flow
|
||||
* (for example `TaintTracking::localTaint(source, sink)`) so it may contain
|
||||
* special cases that should only apply to local taint flow.
|
||||
*/
|
||||
predicate localTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
// dataflow step
|
||||
DataFlow::localFlowStep(nodeFrom, nodeTo)
|
||||
or
|
||||
// taint flow step
|
||||
localAdditionalTaintStep(nodeFrom, nodeTo, _)
|
||||
or
|
||||
// models-as-data summarized flow for local data flow (i.e. special case for flow
|
||||
// through calls to modeled functions, without relying on global dataflow to join
|
||||
// the dots).
|
||||
FlowSummaryImpl::Private::Steps::summaryThroughStepTaint(nodeFrom, nodeTo, _)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -42,6 +52,10 @@ predicate localAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeT
|
||||
any(Ssa::Indirection ind).isAdditionalTaintStep(nodeFrom, nodeTo) and
|
||||
model = ""
|
||||
or
|
||||
// models-as-data summarized flow
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom.(FlowSummaryNode).getSummaryNode(),
|
||||
nodeTo.(FlowSummaryNode).getSummaryNode(), false, model)
|
||||
or
|
||||
// object->field conflation for content that is a `TaintInheritingContent`.
|
||||
exists(DataFlow::ContentSet f |
|
||||
readStep(nodeFrom, f, nodeTo) and
|
||||
|
||||
@@ -41,5 +41,5 @@ class IREscapeAnalysisConfiguration extends TIREscapeAnalysisConfiguration {
|
||||
* Holds if the escape analysis done by SSA construction should be sound. By default, the SSA is
|
||||
* built assuming that no variable's address ever escapes.
|
||||
*/
|
||||
predicate useSoundEscapeAnalysis() { none() }
|
||||
predicate useSoundEscapeAnalysis() { any() }
|
||||
}
|
||||
|
||||
@@ -17,18 +17,11 @@ private import Imports::IRType
|
||||
* The variable may be a user-declared variable (`IRUserVariable`) or a temporary variable generated
|
||||
* by the AST-to-IR translation (`IRTempVariable`).
|
||||
*/
|
||||
class IRVariable extends TIRVariable {
|
||||
abstract private class AbstractIRVariable extends TIRVariable {
|
||||
Language::Declaration func;
|
||||
|
||||
IRVariable() {
|
||||
this = TIRUserVariable(_, _, func) or
|
||||
this = TIRTempVariable(func, _, _, _) or
|
||||
this = TIRStringLiteral(func, _, _, _) or
|
||||
this = TIRDynamicInitializationFlag(func, _, _)
|
||||
}
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { none() }
|
||||
abstract string toString();
|
||||
|
||||
/**
|
||||
* Holds if this variable's value cannot be changed within a function. Currently used for string
|
||||
@@ -49,13 +42,13 @@ class IRVariable extends TIRVariable {
|
||||
/**
|
||||
* Gets the type of the variable.
|
||||
*/
|
||||
Language::LanguageType getLanguageType() { none() }
|
||||
abstract Language::LanguageType getLanguageType();
|
||||
|
||||
/**
|
||||
* Gets the AST node that declared this variable, or that introduced this
|
||||
* variable as part of the AST-to-IR translation.
|
||||
*/
|
||||
Language::AST getAst() { none() }
|
||||
abstract Language::AST getAst();
|
||||
|
||||
/** DEPRECATED: Alias for getAst */
|
||||
deprecated Language::AST getAST() { result = this.getAst() }
|
||||
@@ -64,7 +57,7 @@ class IRVariable extends TIRVariable {
|
||||
* Gets an identifier string for the variable. This identifier is unique
|
||||
* within the function.
|
||||
*/
|
||||
string getUniqueId() { none() }
|
||||
abstract string getUniqueId();
|
||||
|
||||
/**
|
||||
* Gets the source location of this variable.
|
||||
@@ -74,7 +67,7 @@ class IRVariable extends TIRVariable {
|
||||
/**
|
||||
* Gets the IR for the function that references this variable.
|
||||
*/
|
||||
final IRFunction getEnclosingIRFunction() { result.getFunction() = func }
|
||||
final IRFunction getEnclosingIRFunction() { result.getFunction() = this.getEnclosingFunction() }
|
||||
|
||||
/**
|
||||
* Gets the function that references this variable.
|
||||
@@ -82,10 +75,18 @@ class IRVariable extends TIRVariable {
|
||||
final Language::Declaration getEnclosingFunction() { result = func }
|
||||
}
|
||||
|
||||
/**
|
||||
* A variable referenced by the IR for a function.
|
||||
*
|
||||
* The variable may be a user-declared variable (`IRUserVariable`) or a temporary variable generated
|
||||
* by the AST-to-IR translation (`IRTempVariable`).
|
||||
*/
|
||||
final class IRVariable = AbstractIRVariable;
|
||||
|
||||
/**
|
||||
* A user-declared variable referenced by the IR for a function.
|
||||
*/
|
||||
class IRUserVariable extends IRVariable, TIRUserVariable {
|
||||
class IRUserVariable extends AbstractIRVariable, TIRUserVariable {
|
||||
Language::Variable var;
|
||||
Language::LanguageType type;
|
||||
|
||||
@@ -114,26 +115,29 @@ class IRUserVariable extends IRVariable, TIRUserVariable {
|
||||
* A variable (user-declared or temporary) that is allocated on the stack. This includes all
|
||||
* parameters, non-static local variables, and temporary variables.
|
||||
*/
|
||||
class IRAutomaticVariable extends IRVariable {
|
||||
IRAutomaticVariable() {
|
||||
exists(Language::Variable var |
|
||||
this = TIRUserVariable(var, _, func) and
|
||||
Language::isVariableAutomatic(var)
|
||||
)
|
||||
or
|
||||
this = TIRTempVariable(func, _, _, _)
|
||||
}
|
||||
abstract private class AbstractIRAutomaticVariable extends AbstractIRVariable { }
|
||||
|
||||
/**
|
||||
* A variable (user-declared or temporary) that is allocated on the stack. This includes all
|
||||
* parameters, non-static local variables, and temporary variables.
|
||||
*/
|
||||
final class IRAutomaticVariable = AbstractIRAutomaticVariable;
|
||||
|
||||
/**
|
||||
* A user-declared variable that is allocated on the stack. This includes all parameters and
|
||||
* non-static local variables.
|
||||
*/
|
||||
private class AbstractIRAutomaticUserVariable extends IRUserVariable, AbstractIRAutomaticVariable {
|
||||
override Language::AutomaticVariable var;
|
||||
|
||||
final override Language::AutomaticVariable getVariable() { result = var }
|
||||
}
|
||||
|
||||
/**
|
||||
* A user-declared variable that is allocated on the stack. This includes all parameters and
|
||||
* non-static local variables.
|
||||
*/
|
||||
class IRAutomaticUserVariable extends IRUserVariable, IRAutomaticVariable {
|
||||
override Language::AutomaticVariable var;
|
||||
|
||||
final override Language::AutomaticVariable getVariable() { result = var }
|
||||
}
|
||||
final class IRAutomaticUserVariable = AbstractIRAutomaticUserVariable;
|
||||
|
||||
/**
|
||||
* A user-declared variable that is not allocated on the stack. This includes all global variables,
|
||||
@@ -151,16 +155,10 @@ class IRStaticUserVariable extends IRUserVariable {
|
||||
* A variable that is not user-declared. This includes temporary variables generated as part of IR
|
||||
* construction, as well as string literals.
|
||||
*/
|
||||
class IRGeneratedVariable extends IRVariable {
|
||||
abstract private class AbstractIRGeneratedVariable extends AbstractIRVariable {
|
||||
Language::AST ast;
|
||||
Language::LanguageType type;
|
||||
|
||||
IRGeneratedVariable() {
|
||||
this = TIRTempVariable(func, ast, _, type) or
|
||||
this = TIRStringLiteral(func, ast, type, _) or
|
||||
this = TIRDynamicInitializationFlag(func, ast, type)
|
||||
}
|
||||
|
||||
final override Language::LanguageType getLanguageType() { result = type }
|
||||
|
||||
final override Language::AST getAst() { result = ast }
|
||||
@@ -196,12 +194,20 @@ class IRGeneratedVariable extends IRVariable {
|
||||
string getBaseString() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A variable that is not user-declared. This includes temporary variables generated as part of IR
|
||||
* construction, as well as string literals.
|
||||
*/
|
||||
final class IRGeneratedVariable = AbstractIRGeneratedVariable;
|
||||
|
||||
/**
|
||||
* A temporary variable introduced by IR construction. The most common examples are the variable
|
||||
* generated to hold the return value of a function, or the variable generated to hold the result of
|
||||
* a condition operator (`a ? b : c`).
|
||||
*/
|
||||
class IRTempVariable extends IRGeneratedVariable, IRAutomaticVariable, TIRTempVariable {
|
||||
class IRTempVariable extends AbstractIRGeneratedVariable, AbstractIRAutomaticVariable,
|
||||
TIRTempVariable
|
||||
{
|
||||
TempVariableTag tag;
|
||||
|
||||
IRTempVariable() { this = TIRTempVariable(func, ast, tag, type) }
|
||||
@@ -241,7 +247,7 @@ class IRThrowVariable extends IRTempVariable {
|
||||
* A temporary variable generated to hold the contents of all arguments passed to the `...` of a
|
||||
* function that accepts a variable number of arguments.
|
||||
*/
|
||||
class IREllipsisVariable extends IRTempVariable, IRParameter {
|
||||
class IREllipsisVariable extends IRTempVariable, AbstractIRParameter {
|
||||
IREllipsisVariable() { tag = EllipsisTempVar() }
|
||||
|
||||
final override string toString() { result = "#ellipsis" }
|
||||
@@ -252,7 +258,7 @@ class IREllipsisVariable extends IRTempVariable, IRParameter {
|
||||
/**
|
||||
* A temporary variable generated to hold the `this` pointer.
|
||||
*/
|
||||
class IRThisVariable extends IRTempVariable, IRParameter {
|
||||
class IRThisVariable extends IRTempVariable, AbstractIRParameter {
|
||||
IRThisVariable() { tag = ThisTempVar() }
|
||||
|
||||
final override string toString() { result = "#this" }
|
||||
@@ -264,7 +270,7 @@ class IRThisVariable extends IRTempVariable, IRParameter {
|
||||
* A variable generated to represent the contents of a string literal. This variable acts much like
|
||||
* a read-only global variable.
|
||||
*/
|
||||
class IRStringLiteral extends IRGeneratedVariable, TIRStringLiteral {
|
||||
class IRStringLiteral extends AbstractIRGeneratedVariable, TIRStringLiteral {
|
||||
Language::StringLiteral literal;
|
||||
|
||||
IRStringLiteral() { this = TIRStringLiteral(func, ast, type, literal) }
|
||||
@@ -288,7 +294,7 @@ class IRStringLiteral extends IRGeneratedVariable, TIRStringLiteral {
|
||||
* used to model the runtime initialization of static local variables in C++, as well as static
|
||||
* fields in C#.
|
||||
*/
|
||||
class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitializationFlag {
|
||||
class IRDynamicInitializationFlag extends AbstractIRGeneratedVariable, TIRDynamicInitializationFlag {
|
||||
Language::Variable var;
|
||||
|
||||
IRDynamicInitializationFlag() {
|
||||
@@ -314,24 +320,24 @@ class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitial
|
||||
* An IR variable which acts like a function parameter, including positional parameters and the
|
||||
* temporary variables generated for `this` and ellipsis parameters.
|
||||
*/
|
||||
class IRParameter extends IRAutomaticVariable {
|
||||
IRParameter() {
|
||||
this.(IRAutomaticUserVariable).getVariable() instanceof Language::Parameter
|
||||
or
|
||||
this = TIRTempVariable(_, _, ThisTempVar(), _)
|
||||
or
|
||||
this = TIRTempVariable(_, _, EllipsisTempVar(), _)
|
||||
}
|
||||
|
||||
abstract private class AbstractIRParameter extends AbstractIRAutomaticVariable {
|
||||
/**
|
||||
* Gets the zero-based index of this parameter. The `this` parameter has index -1.
|
||||
*/
|
||||
int getIndex() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An IR variable which acts like a function parameter, including positional parameters and the
|
||||
* temporary variables generated for `this` and ellipsis parameters.
|
||||
*/
|
||||
final class IRParameter = AbstractIRParameter;
|
||||
|
||||
/**
|
||||
* An IR variable representing a positional parameter.
|
||||
*/
|
||||
class IRPositionalParameter extends IRParameter, IRAutomaticUserVariable {
|
||||
class IRPositionalParameter extends AbstractIRParameter, AbstractIRAutomaticUserVariable {
|
||||
IRPositionalParameter() { this.getVariable() instanceof Language::Parameter }
|
||||
|
||||
final override int getIndex() { result = this.getVariable().(Language::Parameter).getIndex() }
|
||||
}
|
||||
|
||||
@@ -247,8 +247,7 @@ class Instruction extends Construction::TStageInstruction {
|
||||
* Gets the type of the result produced by this instruction. If the instruction does not produce
|
||||
* a result, its result type will be `IRVoidType`.
|
||||
*/
|
||||
cached
|
||||
final IRType getResultIRType() { result = this.getResultLanguageType().getIRType() }
|
||||
final IRType getResultIRType() { result = Construction::getInstructionResultIRType(this) }
|
||||
|
||||
/**
|
||||
* Gets the type of the result produced by this instruction. If the
|
||||
@@ -995,9 +994,8 @@ class ConstantInstruction extends ConstantValueInstruction {
|
||||
*/
|
||||
class IntegerConstantInstruction extends ConstantInstruction {
|
||||
IntegerConstantInstruction() {
|
||||
exists(IRType resultType |
|
||||
resultType = this.getResultIRType() and
|
||||
(resultType instanceof IRIntegerType or resultType instanceof IRBooleanType)
|
||||
exists(IRType resultType | resultType = this.getResultIRType() |
|
||||
resultType instanceof IRIntegerType or resultType instanceof IRBooleanType
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1009,6 +1007,17 @@ class FloatConstantInstruction extends ConstantInstruction {
|
||||
FloatConstantInstruction() { this.getResultIRType() instanceof IRFloatingPointType }
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction whose result is a constant value of a pointer type.
|
||||
*/
|
||||
class PointerConstantInstruction extends ConstantInstruction {
|
||||
PointerConstantInstruction() {
|
||||
exists(IRType resultType | resultType = this.getResultIRType() |
|
||||
resultType instanceof IRAddressType or resultType instanceof IRFunctionAddressType
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction whose result is the address of a string literal.
|
||||
*/
|
||||
|
||||
@@ -429,6 +429,11 @@ private module Cached {
|
||||
instr = unreachedInstruction(_) and result = Language::getVoidType()
|
||||
}
|
||||
|
||||
cached
|
||||
IRType getInstructionResultIRType(Instruction instr) {
|
||||
result = instr.getResultLanguageType().getIRType()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `opcode` is the opcode that specifies the operation performed by `instr`.
|
||||
*
|
||||
|
||||
@@ -17,18 +17,11 @@ private import Imports::IRType
|
||||
* The variable may be a user-declared variable (`IRUserVariable`) or a temporary variable generated
|
||||
* by the AST-to-IR translation (`IRTempVariable`).
|
||||
*/
|
||||
class IRVariable extends TIRVariable {
|
||||
abstract private class AbstractIRVariable extends TIRVariable {
|
||||
Language::Declaration func;
|
||||
|
||||
IRVariable() {
|
||||
this = TIRUserVariable(_, _, func) or
|
||||
this = TIRTempVariable(func, _, _, _) or
|
||||
this = TIRStringLiteral(func, _, _, _) or
|
||||
this = TIRDynamicInitializationFlag(func, _, _)
|
||||
}
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { none() }
|
||||
abstract string toString();
|
||||
|
||||
/**
|
||||
* Holds if this variable's value cannot be changed within a function. Currently used for string
|
||||
@@ -49,13 +42,13 @@ class IRVariable extends TIRVariable {
|
||||
/**
|
||||
* Gets the type of the variable.
|
||||
*/
|
||||
Language::LanguageType getLanguageType() { none() }
|
||||
abstract Language::LanguageType getLanguageType();
|
||||
|
||||
/**
|
||||
* Gets the AST node that declared this variable, or that introduced this
|
||||
* variable as part of the AST-to-IR translation.
|
||||
*/
|
||||
Language::AST getAst() { none() }
|
||||
abstract Language::AST getAst();
|
||||
|
||||
/** DEPRECATED: Alias for getAst */
|
||||
deprecated Language::AST getAST() { result = this.getAst() }
|
||||
@@ -64,7 +57,7 @@ class IRVariable extends TIRVariable {
|
||||
* Gets an identifier string for the variable. This identifier is unique
|
||||
* within the function.
|
||||
*/
|
||||
string getUniqueId() { none() }
|
||||
abstract string getUniqueId();
|
||||
|
||||
/**
|
||||
* Gets the source location of this variable.
|
||||
@@ -74,7 +67,7 @@ class IRVariable extends TIRVariable {
|
||||
/**
|
||||
* Gets the IR for the function that references this variable.
|
||||
*/
|
||||
final IRFunction getEnclosingIRFunction() { result.getFunction() = func }
|
||||
final IRFunction getEnclosingIRFunction() { result.getFunction() = this.getEnclosingFunction() }
|
||||
|
||||
/**
|
||||
* Gets the function that references this variable.
|
||||
@@ -82,10 +75,18 @@ class IRVariable extends TIRVariable {
|
||||
final Language::Declaration getEnclosingFunction() { result = func }
|
||||
}
|
||||
|
||||
/**
|
||||
* A variable referenced by the IR for a function.
|
||||
*
|
||||
* The variable may be a user-declared variable (`IRUserVariable`) or a temporary variable generated
|
||||
* by the AST-to-IR translation (`IRTempVariable`).
|
||||
*/
|
||||
final class IRVariable = AbstractIRVariable;
|
||||
|
||||
/**
|
||||
* A user-declared variable referenced by the IR for a function.
|
||||
*/
|
||||
class IRUserVariable extends IRVariable, TIRUserVariable {
|
||||
class IRUserVariable extends AbstractIRVariable, TIRUserVariable {
|
||||
Language::Variable var;
|
||||
Language::LanguageType type;
|
||||
|
||||
@@ -114,26 +115,29 @@ class IRUserVariable extends IRVariable, TIRUserVariable {
|
||||
* A variable (user-declared or temporary) that is allocated on the stack. This includes all
|
||||
* parameters, non-static local variables, and temporary variables.
|
||||
*/
|
||||
class IRAutomaticVariable extends IRVariable {
|
||||
IRAutomaticVariable() {
|
||||
exists(Language::Variable var |
|
||||
this = TIRUserVariable(var, _, func) and
|
||||
Language::isVariableAutomatic(var)
|
||||
)
|
||||
or
|
||||
this = TIRTempVariable(func, _, _, _)
|
||||
}
|
||||
abstract private class AbstractIRAutomaticVariable extends AbstractIRVariable { }
|
||||
|
||||
/**
|
||||
* A variable (user-declared or temporary) that is allocated on the stack. This includes all
|
||||
* parameters, non-static local variables, and temporary variables.
|
||||
*/
|
||||
final class IRAutomaticVariable = AbstractIRAutomaticVariable;
|
||||
|
||||
/**
|
||||
* A user-declared variable that is allocated on the stack. This includes all parameters and
|
||||
* non-static local variables.
|
||||
*/
|
||||
private class AbstractIRAutomaticUserVariable extends IRUserVariable, AbstractIRAutomaticVariable {
|
||||
override Language::AutomaticVariable var;
|
||||
|
||||
final override Language::AutomaticVariable getVariable() { result = var }
|
||||
}
|
||||
|
||||
/**
|
||||
* A user-declared variable that is allocated on the stack. This includes all parameters and
|
||||
* non-static local variables.
|
||||
*/
|
||||
class IRAutomaticUserVariable extends IRUserVariable, IRAutomaticVariable {
|
||||
override Language::AutomaticVariable var;
|
||||
|
||||
final override Language::AutomaticVariable getVariable() { result = var }
|
||||
}
|
||||
final class IRAutomaticUserVariable = AbstractIRAutomaticUserVariable;
|
||||
|
||||
/**
|
||||
* A user-declared variable that is not allocated on the stack. This includes all global variables,
|
||||
@@ -151,16 +155,10 @@ class IRStaticUserVariable extends IRUserVariable {
|
||||
* A variable that is not user-declared. This includes temporary variables generated as part of IR
|
||||
* construction, as well as string literals.
|
||||
*/
|
||||
class IRGeneratedVariable extends IRVariable {
|
||||
abstract private class AbstractIRGeneratedVariable extends AbstractIRVariable {
|
||||
Language::AST ast;
|
||||
Language::LanguageType type;
|
||||
|
||||
IRGeneratedVariable() {
|
||||
this = TIRTempVariable(func, ast, _, type) or
|
||||
this = TIRStringLiteral(func, ast, type, _) or
|
||||
this = TIRDynamicInitializationFlag(func, ast, type)
|
||||
}
|
||||
|
||||
final override Language::LanguageType getLanguageType() { result = type }
|
||||
|
||||
final override Language::AST getAst() { result = ast }
|
||||
@@ -196,12 +194,20 @@ class IRGeneratedVariable extends IRVariable {
|
||||
string getBaseString() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A variable that is not user-declared. This includes temporary variables generated as part of IR
|
||||
* construction, as well as string literals.
|
||||
*/
|
||||
final class IRGeneratedVariable = AbstractIRGeneratedVariable;
|
||||
|
||||
/**
|
||||
* A temporary variable introduced by IR construction. The most common examples are the variable
|
||||
* generated to hold the return value of a function, or the variable generated to hold the result of
|
||||
* a condition operator (`a ? b : c`).
|
||||
*/
|
||||
class IRTempVariable extends IRGeneratedVariable, IRAutomaticVariable, TIRTempVariable {
|
||||
class IRTempVariable extends AbstractIRGeneratedVariable, AbstractIRAutomaticVariable,
|
||||
TIRTempVariable
|
||||
{
|
||||
TempVariableTag tag;
|
||||
|
||||
IRTempVariable() { this = TIRTempVariable(func, ast, tag, type) }
|
||||
@@ -241,7 +247,7 @@ class IRThrowVariable extends IRTempVariable {
|
||||
* A temporary variable generated to hold the contents of all arguments passed to the `...` of a
|
||||
* function that accepts a variable number of arguments.
|
||||
*/
|
||||
class IREllipsisVariable extends IRTempVariable, IRParameter {
|
||||
class IREllipsisVariable extends IRTempVariable, AbstractIRParameter {
|
||||
IREllipsisVariable() { tag = EllipsisTempVar() }
|
||||
|
||||
final override string toString() { result = "#ellipsis" }
|
||||
@@ -252,7 +258,7 @@ class IREllipsisVariable extends IRTempVariable, IRParameter {
|
||||
/**
|
||||
* A temporary variable generated to hold the `this` pointer.
|
||||
*/
|
||||
class IRThisVariable extends IRTempVariable, IRParameter {
|
||||
class IRThisVariable extends IRTempVariable, AbstractIRParameter {
|
||||
IRThisVariable() { tag = ThisTempVar() }
|
||||
|
||||
final override string toString() { result = "#this" }
|
||||
@@ -264,7 +270,7 @@ class IRThisVariable extends IRTempVariable, IRParameter {
|
||||
* A variable generated to represent the contents of a string literal. This variable acts much like
|
||||
* a read-only global variable.
|
||||
*/
|
||||
class IRStringLiteral extends IRGeneratedVariable, TIRStringLiteral {
|
||||
class IRStringLiteral extends AbstractIRGeneratedVariable, TIRStringLiteral {
|
||||
Language::StringLiteral literal;
|
||||
|
||||
IRStringLiteral() { this = TIRStringLiteral(func, ast, type, literal) }
|
||||
@@ -288,7 +294,7 @@ class IRStringLiteral extends IRGeneratedVariable, TIRStringLiteral {
|
||||
* used to model the runtime initialization of static local variables in C++, as well as static
|
||||
* fields in C#.
|
||||
*/
|
||||
class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitializationFlag {
|
||||
class IRDynamicInitializationFlag extends AbstractIRGeneratedVariable, TIRDynamicInitializationFlag {
|
||||
Language::Variable var;
|
||||
|
||||
IRDynamicInitializationFlag() {
|
||||
@@ -314,24 +320,24 @@ class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitial
|
||||
* An IR variable which acts like a function parameter, including positional parameters and the
|
||||
* temporary variables generated for `this` and ellipsis parameters.
|
||||
*/
|
||||
class IRParameter extends IRAutomaticVariable {
|
||||
IRParameter() {
|
||||
this.(IRAutomaticUserVariable).getVariable() instanceof Language::Parameter
|
||||
or
|
||||
this = TIRTempVariable(_, _, ThisTempVar(), _)
|
||||
or
|
||||
this = TIRTempVariable(_, _, EllipsisTempVar(), _)
|
||||
}
|
||||
|
||||
abstract private class AbstractIRParameter extends AbstractIRAutomaticVariable {
|
||||
/**
|
||||
* Gets the zero-based index of this parameter. The `this` parameter has index -1.
|
||||
*/
|
||||
int getIndex() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An IR variable which acts like a function parameter, including positional parameters and the
|
||||
* temporary variables generated for `this` and ellipsis parameters.
|
||||
*/
|
||||
final class IRParameter = AbstractIRParameter;
|
||||
|
||||
/**
|
||||
* An IR variable representing a positional parameter.
|
||||
*/
|
||||
class IRPositionalParameter extends IRParameter, IRAutomaticUserVariable {
|
||||
class IRPositionalParameter extends AbstractIRParameter, AbstractIRAutomaticUserVariable {
|
||||
IRPositionalParameter() { this.getVariable() instanceof Language::Parameter }
|
||||
|
||||
final override int getIndex() { result = this.getVariable().(Language::Parameter).getIndex() }
|
||||
}
|
||||
|
||||
@@ -247,8 +247,7 @@ class Instruction extends Construction::TStageInstruction {
|
||||
* Gets the type of the result produced by this instruction. If the instruction does not produce
|
||||
* a result, its result type will be `IRVoidType`.
|
||||
*/
|
||||
cached
|
||||
final IRType getResultIRType() { result = this.getResultLanguageType().getIRType() }
|
||||
final IRType getResultIRType() { result = Construction::getInstructionResultIRType(this) }
|
||||
|
||||
/**
|
||||
* Gets the type of the result produced by this instruction. If the
|
||||
@@ -995,9 +994,8 @@ class ConstantInstruction extends ConstantValueInstruction {
|
||||
*/
|
||||
class IntegerConstantInstruction extends ConstantInstruction {
|
||||
IntegerConstantInstruction() {
|
||||
exists(IRType resultType |
|
||||
resultType = this.getResultIRType() and
|
||||
(resultType instanceof IRIntegerType or resultType instanceof IRBooleanType)
|
||||
exists(IRType resultType | resultType = this.getResultIRType() |
|
||||
resultType instanceof IRIntegerType or resultType instanceof IRBooleanType
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1009,6 +1007,17 @@ class FloatConstantInstruction extends ConstantInstruction {
|
||||
FloatConstantInstruction() { this.getResultIRType() instanceof IRFloatingPointType }
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction whose result is a constant value of a pointer type.
|
||||
*/
|
||||
class PointerConstantInstruction extends ConstantInstruction {
|
||||
PointerConstantInstruction() {
|
||||
exists(IRType resultType | resultType = this.getResultIRType() |
|
||||
resultType instanceof IRAddressType or resultType instanceof IRFunctionAddressType
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction whose result is the address of a string literal.
|
||||
*/
|
||||
|
||||
@@ -377,6 +377,10 @@ CppType getInstructionResultType(TStageInstruction instr) {
|
||||
result = getVoidType()
|
||||
}
|
||||
|
||||
IRType getInstructionResultIRType(Instruction instr) {
|
||||
result = instr.getResultLanguageType().getIRType()
|
||||
}
|
||||
|
||||
predicate getInstructionOpcode(Opcode opcode, TStageInstruction instr) {
|
||||
getInstructionTranslatedElement(instr).hasInstruction(opcode, getInstructionTag(instr), _)
|
||||
or
|
||||
|
||||
@@ -89,7 +89,8 @@ newtype TInstructionTag =
|
||||
ImplicitDestructorTag(int index) {
|
||||
exists(Expr e | exists(e.getImplicitDestructorCall(index))) or
|
||||
exists(Stmt s | exists(s.getImplicitDestructorCall(index)))
|
||||
}
|
||||
} or
|
||||
CoAwaitBranchTag()
|
||||
|
||||
class InstructionTag extends TInstructionTag {
|
||||
final string toString() { result = getInstructionTagId(this) }
|
||||
@@ -186,6 +187,8 @@ string getInstructionTagId(TInstructionTag tag) {
|
||||
or
|
||||
tag = BoolConversionCompareTag() and result = "BoolConvComp"
|
||||
or
|
||||
tag = ResultCopyTag() and result = "ResultCopy"
|
||||
or
|
||||
tag = LoadTag() and result = "Load" // Implicit load due to lvalue-to-rvalue conversion
|
||||
or
|
||||
tag = CatchTag() and result = "Catch"
|
||||
@@ -263,4 +266,6 @@ string getInstructionTagId(TInstructionTag tag) {
|
||||
exists(int index |
|
||||
tag = ImplicitDestructorTag(index) and result = "ImplicitDestructor(" + index + ")"
|
||||
)
|
||||
or
|
||||
tag = CoAwaitBranchTag() and result = "CoAwaitBranch"
|
||||
}
|
||||
|
||||
@@ -830,6 +830,12 @@ newtype TTranslatedElement =
|
||||
not ignoreExpr(dc)
|
||||
)
|
||||
} or
|
||||
// The set of destructors to invoke after a handler for a `try` statement. These
|
||||
// need to be special cased because the destructors need to run following an
|
||||
// `ExceptionEdge`, but not following a `GotoEdge` edge.
|
||||
TTranslatedDestructorsAfterHandler(Handler handler) {
|
||||
exists(handler.getAnImplicitDestructorCall())
|
||||
} or
|
||||
// A precise side effect of an argument to a `Call`
|
||||
TTranslatedArgumentExprSideEffect(Call call, Expr expr, int n, SideEffectOpcode opcode) {
|
||||
not ignoreExpr(expr) and
|
||||
|
||||
@@ -538,6 +538,11 @@ class TranslatedResultCopy extends TranslatedExpr, TTranslatedResultCopy {
|
||||
final override predicate producesExprResult() { any() }
|
||||
|
||||
private TranslatedCoreExpr getOperand() { result.getExpr() = expr }
|
||||
|
||||
override predicate handlesDestructorsExplicitly() {
|
||||
// The destructor calls will already have been generated by the translation of `expr`.
|
||||
any()
|
||||
}
|
||||
}
|
||||
|
||||
class TranslatedCommaExpr extends TranslatedNonConstantExpr {
|
||||
@@ -1306,6 +1311,146 @@ class TranslatedUnaryExpr extends TranslatedSingleInstructionExpr {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* IR translation of a `co_await` or `co_yield` expression.
|
||||
*
|
||||
* The translation of `x = co_await ...` is essentially:
|
||||
* ```cpp
|
||||
* if (!awaiter.await_ready()) {
|
||||
* awaiter.await_suspend();
|
||||
* }
|
||||
* x = awaiter.await_resume();
|
||||
* ```
|
||||
* where `awaiter` is an object constructed from programmer-supplied
|
||||
* input, and for IR construction purposes these are resolved by the C/C++
|
||||
* front-end.
|
||||
*
|
||||
* See https://en.cppreference.com/w/cpp/language/coroutines#co_await for the
|
||||
* specification on how `awaiter` is obtained.
|
||||
*/
|
||||
abstract private class TranslatedCoExpr extends TranslatedNonConstantExpr {
|
||||
/** Gets the operand of this operation. */
|
||||
abstract Expr getOperand();
|
||||
|
||||
/**
|
||||
* Gets the expression that decides if the enclosing coroutine should be
|
||||
* suspended.
|
||||
*/
|
||||
abstract Expr getAwaitReady();
|
||||
|
||||
/**
|
||||
* Gets the expression that is evaluated when the enclosing coroutine is
|
||||
* suspended.
|
||||
*/
|
||||
abstract Expr getAwaitSuspend();
|
||||
|
||||
/**
|
||||
* Gets the expression that represents the resume point if the enclosing
|
||||
* coroutine was suspended.
|
||||
*/
|
||||
abstract Expr getAwaitResume();
|
||||
|
||||
final override Instruction getFirstInstruction(EdgeKind kind) {
|
||||
result = this.getTranslatedOperand().getFirstInstruction(kind)
|
||||
}
|
||||
|
||||
override Instruction getALastInstructionInternal() {
|
||||
result = this.getTranslatedAwaitResume().getALastInstruction()
|
||||
}
|
||||
|
||||
final override TranslatedElement getChildInternal(int id) {
|
||||
id = 0 and result = this.getTranslatedOperand()
|
||||
or
|
||||
id = 1 and result = this.getTranslatedAwaitReady()
|
||||
or
|
||||
id = 2 and result = this.getTranslatedAwaitResume()
|
||||
or
|
||||
id = 3 and result = this.getTranslatedAwaitSuspend()
|
||||
}
|
||||
|
||||
final override Instruction getInstructionSuccessorInternal(InstructionTag tag, EdgeKind kind) {
|
||||
tag = CoAwaitBranchTag() and
|
||||
(
|
||||
kind instanceof TrueEdge and
|
||||
result = this.getTranslatedAwaitResume().getFirstInstruction(any(GotoEdge goto))
|
||||
or
|
||||
kind instanceof FalseEdge and
|
||||
result = this.getTranslatedAwaitSuspend().getFirstInstruction(any(GotoEdge goto))
|
||||
)
|
||||
}
|
||||
|
||||
override Instruction getResult() { result = this.getTranslatedAwaitResume().getResult() }
|
||||
|
||||
final override Instruction getChildSuccessorInternal(TranslatedElement child, EdgeKind kind) {
|
||||
child = this.getTranslatedOperand() and
|
||||
result = this.getTranslatedAwaitReady().getFirstInstruction(kind)
|
||||
or
|
||||
child = this.getTranslatedAwaitReady() and
|
||||
kind instanceof GotoEdge and
|
||||
result = this.getInstruction(CoAwaitBranchTag())
|
||||
or
|
||||
child = this.getTranslatedAwaitSuspend() and
|
||||
result = this.getTranslatedAwaitResume().getFirstInstruction(kind)
|
||||
or
|
||||
child = this.getTranslatedAwaitResume() and
|
||||
result = this.getParent().getChildSuccessor(this, kind)
|
||||
}
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
tag = CoAwaitBranchTag() and
|
||||
opcode instanceof Opcode::ConditionalBranch and
|
||||
resultType = getVoidType()
|
||||
}
|
||||
|
||||
override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
|
||||
tag = CoAwaitBranchTag() and
|
||||
operandTag instanceof ConditionOperandTag and
|
||||
result = this.getTranslatedAwaitReady().getResult()
|
||||
}
|
||||
|
||||
private TranslatedExpr getTranslatedOperand() {
|
||||
result = getTranslatedExpr(this.getOperand().getFullyConverted())
|
||||
}
|
||||
|
||||
private TranslatedExpr getTranslatedAwaitReady() {
|
||||
result = getTranslatedExpr(this.getAwaitReady().getFullyConverted())
|
||||
}
|
||||
|
||||
private TranslatedExpr getTranslatedAwaitResume() {
|
||||
result = getTranslatedExpr(this.getAwaitResume().getFullyConverted())
|
||||
}
|
||||
|
||||
private TranslatedExpr getTranslatedAwaitSuspend() {
|
||||
result = getTranslatedExpr(this.getAwaitSuspend().getFullyConverted())
|
||||
}
|
||||
}
|
||||
|
||||
/** IR translation of `co_await`. */
|
||||
class TranslatedCoAwaitExpr extends TranslatedCoExpr {
|
||||
override CoAwaitExpr expr;
|
||||
|
||||
final override Expr getOperand() { result = expr.getOperand() }
|
||||
|
||||
final override Expr getAwaitReady() { result = expr.getAwaitReady() }
|
||||
|
||||
final override Expr getAwaitSuspend() { result = expr.getAwaitSuspend() }
|
||||
|
||||
final override Expr getAwaitResume() { result = expr.getAwaitResume() }
|
||||
}
|
||||
|
||||
/** IR translation of `co_yield`. */
|
||||
class TranslatedCoYieldxpr extends TranslatedCoExpr {
|
||||
override CoYieldExpr expr;
|
||||
|
||||
final override Expr getOperand() { result = expr.getOperand() }
|
||||
|
||||
final override Expr getAwaitReady() { result = expr.getAwaitReady() }
|
||||
|
||||
final override Expr getAwaitSuspend() { result = expr.getAwaitSuspend() }
|
||||
|
||||
final override Expr getAwaitResume() { result = expr.getAwaitResume() }
|
||||
}
|
||||
|
||||
abstract class TranslatedConversion extends TranslatedNonConstantExpr {
|
||||
override Conversion expr;
|
||||
|
||||
@@ -1699,9 +1844,6 @@ class TranslatedAssignExpr extends TranslatedNonConstantExpr {
|
||||
child = this.getRightOperand() and
|
||||
result = this.getLeftOperand().getFirstInstruction(kind)
|
||||
or
|
||||
child = this.getRightOperand() and
|
||||
result = this.getLeftOperand().getFirstInstruction(kind)
|
||||
or
|
||||
kind instanceof GotoEdge and
|
||||
child = this.getLeftOperand() and
|
||||
result = this.getInstruction(AssignmentStoreTag())
|
||||
|
||||
@@ -777,6 +777,72 @@ abstract class TranslatedHandler extends TranslatedStmt {
|
||||
TranslatedStmt getBlock() { result = getTranslatedStmt(stmt.getBlock()) }
|
||||
}
|
||||
|
||||
/**
|
||||
* The IR translation of the destructor calls of the parent `TranslatedCatchByTypeHandler`.
|
||||
*
|
||||
* This object does not itself generate the destructor calls. Instead, its
|
||||
* children provide the actual calls.
|
||||
*/
|
||||
class TranslatedDestructorsAfterHandler extends TranslatedElement,
|
||||
TTranslatedDestructorsAfterHandler
|
||||
{
|
||||
Handler handler;
|
||||
|
||||
TranslatedDestructorsAfterHandler() { this = TTranslatedDestructorsAfterHandler(handler) }
|
||||
|
||||
override string toString() { result = "Destructor calls after handler: " + handler }
|
||||
|
||||
private TranslatedCall getTranslatedImplicitDestructorCall(int id) {
|
||||
result.getExpr() = handler.getImplicitDestructorCall(id)
|
||||
}
|
||||
|
||||
override Instruction getFirstInstruction(EdgeKind kind) {
|
||||
result = this.getChild(0).getFirstInstruction(kind)
|
||||
}
|
||||
|
||||
override Handler getAst() { result = handler }
|
||||
|
||||
override Instruction getInstructionSuccessorInternal(InstructionTag tag, EdgeKind kind) { none() }
|
||||
|
||||
override TranslatedElement getChild(int id) {
|
||||
result = this.getTranslatedImplicitDestructorCall(id)
|
||||
}
|
||||
|
||||
override predicate handlesDestructorsExplicitly() { any() }
|
||||
|
||||
override Declaration getFunction() { result = handler.getEnclosingFunction() }
|
||||
|
||||
override Instruction getChildSuccessorInternal(TranslatedElement child, EdgeKind kind) {
|
||||
exists(int id | child = this.getChild(id) |
|
||||
// Transition to the next child, if any.
|
||||
result = this.getChild(id + 1).getFirstInstruction(kind)
|
||||
or
|
||||
// And otherwise go to the next handler, if any.
|
||||
not exists(this.getChild(id + 1)) and
|
||||
result =
|
||||
getTranslatedStmt(handler)
|
||||
.getParent()
|
||||
.(TranslatedTryStmt)
|
||||
.getNextHandler(getTranslatedStmt(handler), kind)
|
||||
)
|
||||
}
|
||||
|
||||
override TranslatedElement getLastChild() {
|
||||
result =
|
||||
this.getTranslatedImplicitDestructorCall(max(int id |
|
||||
exists(handler.getImplicitDestructorCall(id))
|
||||
))
|
||||
}
|
||||
|
||||
override Instruction getALastInstructionInternal() {
|
||||
result = this.getLastChild().getALastInstruction()
|
||||
}
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The IR translation of a C++ `catch` block that catches an exception with a
|
||||
* specific type (e.g. `catch (const std::exception&)`).
|
||||
@@ -790,10 +856,14 @@ class TranslatedCatchByTypeHandler extends TranslatedHandler {
|
||||
resultType = getVoidType()
|
||||
}
|
||||
|
||||
override predicate handlesDestructorsExplicitly() { any() }
|
||||
|
||||
override TranslatedElement getChildInternal(int id) {
|
||||
result = super.getChildInternal(id)
|
||||
or
|
||||
id = 0 and result = this.getParameter()
|
||||
or
|
||||
id = 1 and result = this.getDestructors()
|
||||
}
|
||||
|
||||
override Instruction getChildSuccessorInternal(TranslatedElement child, EdgeKind kind) {
|
||||
@@ -810,7 +880,9 @@ class TranslatedCatchByTypeHandler extends TranslatedHandler {
|
||||
result = this.getParameter().getFirstInstruction(kind)
|
||||
or
|
||||
kind instanceof ExceptionEdge and
|
||||
result = this.getParent().(TranslatedTryStmt).getNextHandler(this, any(GotoEdge edge))
|
||||
if exists(this.getDestructors())
|
||||
then result = this.getDestructors().getFirstInstruction(any(GotoEdge edge))
|
||||
else result = this.getParent().(TranslatedTryStmt).getNextHandler(this, any(GotoEdge edge))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -822,6 +894,8 @@ class TranslatedCatchByTypeHandler extends TranslatedHandler {
|
||||
private TranslatedParameter getParameter() {
|
||||
result = getTranslatedParameter(stmt.getParameter())
|
||||
}
|
||||
|
||||
private TranslatedDestructorsAfterHandler getDestructors() { result.getAst() = stmt }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -842,9 +916,7 @@ class TranslatedCatchAnyHandler extends TranslatedHandler {
|
||||
}
|
||||
}
|
||||
|
||||
class TranslatedIfStmt extends TranslatedStmt, ConditionContext {
|
||||
override IfStmt stmt;
|
||||
|
||||
abstract class TranslatedIfLikeStmt extends TranslatedStmt, ConditionContext {
|
||||
override Instruction getFirstInstruction(EdgeKind kind) {
|
||||
if this.hasInitialization()
|
||||
then result = this.getInitialization().getFirstInstruction(kind)
|
||||
@@ -857,6 +929,8 @@ class TranslatedIfStmt extends TranslatedStmt, ConditionContext {
|
||||
|
||||
override TranslatedElement getLastChild() { result = this.getElse() or result = this.getThen() }
|
||||
|
||||
override predicate handlesDestructorsExplicitly() { any() }
|
||||
|
||||
override TranslatedElement getChildInternal(int id) {
|
||||
id = 0 and result = this.getInitialization()
|
||||
or
|
||||
@@ -867,25 +941,21 @@ class TranslatedIfStmt extends TranslatedStmt, ConditionContext {
|
||||
id = 3 and result = this.getElse()
|
||||
}
|
||||
|
||||
private predicate hasInitialization() { exists(stmt.getInitialization()) }
|
||||
abstract predicate hasInitialization();
|
||||
|
||||
private TranslatedStmt getInitialization() {
|
||||
result = getTranslatedStmt(stmt.getInitialization())
|
||||
}
|
||||
abstract TranslatedStmt getInitialization();
|
||||
|
||||
private TranslatedCondition getCondition() {
|
||||
result = getTranslatedCondition(stmt.getCondition().getFullyConverted())
|
||||
}
|
||||
abstract TranslatedCondition getCondition();
|
||||
|
||||
private Instruction getFirstConditionInstruction(EdgeKind kind) {
|
||||
result = this.getCondition().getFirstInstruction(kind)
|
||||
}
|
||||
|
||||
private TranslatedStmt getThen() { result = getTranslatedStmt(stmt.getThen()) }
|
||||
abstract TranslatedStmt getThen();
|
||||
|
||||
private TranslatedStmt getElse() { result = getTranslatedStmt(stmt.getElse()) }
|
||||
abstract TranslatedStmt getElse();
|
||||
|
||||
private predicate hasElse() { exists(stmt.getElse()) }
|
||||
abstract predicate hasElse();
|
||||
|
||||
override Instruction getInstructionSuccessorInternal(InstructionTag tag, EdgeKind kind) { none() }
|
||||
|
||||
@@ -898,7 +968,11 @@ class TranslatedIfStmt extends TranslatedStmt, ConditionContext {
|
||||
child = this.getCondition() and
|
||||
if this.hasElse()
|
||||
then result = this.getElse().getFirstInstruction(kind)
|
||||
else result = this.getParent().getChildSuccessor(this, kind)
|
||||
else (
|
||||
if this.hasAnImplicitDestructorCall()
|
||||
then result = this.getChild(this.getFirstDestructorCallIndex()).getFirstInstruction(kind)
|
||||
else result = this.getParent().getChildSuccessor(this, kind)
|
||||
)
|
||||
}
|
||||
|
||||
override Instruction getChildSuccessorInternal(TranslatedElement child, EdgeKind kind) {
|
||||
@@ -906,7 +980,24 @@ class TranslatedIfStmt extends TranslatedStmt, ConditionContext {
|
||||
result = this.getFirstConditionInstruction(kind)
|
||||
or
|
||||
(child = this.getThen() or child = this.getElse()) and
|
||||
result = this.getParent().getChildSuccessor(this, kind)
|
||||
(
|
||||
if this.hasAnImplicitDestructorCall()
|
||||
then result = this.getChild(this.getFirstDestructorCallIndex()).getFirstInstruction(kind)
|
||||
else result = this.getParent().getChildSuccessor(this, kind)
|
||||
)
|
||||
or
|
||||
exists(int destructorId |
|
||||
destructorId >= this.getFirstDestructorCallIndex() and
|
||||
child = this.getChild(destructorId) and
|
||||
result = this.getChild(destructorId + 1).getFirstInstruction(kind)
|
||||
)
|
||||
or
|
||||
exists(int lastDestructorIndex |
|
||||
lastDestructorIndex =
|
||||
max(int n | exists(this.getChild(n)) and n >= this.getFirstDestructorCallIndex()) and
|
||||
child = this.getChild(lastDestructorIndex) and
|
||||
result = this.getParent().getChildSuccessor(this, kind)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
@@ -914,76 +1005,44 @@ class TranslatedIfStmt extends TranslatedStmt, ConditionContext {
|
||||
}
|
||||
}
|
||||
|
||||
class TranslatedConstExprIfStmt extends TranslatedStmt, ConditionContext {
|
||||
override ConstexprIfStmt stmt;
|
||||
class TranslatedIfStmt extends TranslatedIfLikeStmt {
|
||||
override IfStmt stmt;
|
||||
|
||||
override Instruction getFirstInstruction(EdgeKind kind) {
|
||||
if this.hasInitialization()
|
||||
then result = this.getInitialization().getFirstInstruction(kind)
|
||||
else result = this.getFirstConditionInstruction(kind)
|
||||
}
|
||||
override predicate hasInitialization() { exists(stmt.getInitialization()) }
|
||||
|
||||
override TranslatedElement getChildInternal(int id) {
|
||||
id = 0 and result = this.getInitialization()
|
||||
or
|
||||
id = 1 and result = this.getCondition()
|
||||
or
|
||||
id = 2 and result = this.getThen()
|
||||
or
|
||||
id = 3 and result = this.getElse()
|
||||
}
|
||||
|
||||
private predicate hasInitialization() { exists(stmt.getInitialization()) }
|
||||
|
||||
private TranslatedStmt getInitialization() {
|
||||
override TranslatedStmt getInitialization() {
|
||||
result = getTranslatedStmt(stmt.getInitialization())
|
||||
}
|
||||
|
||||
private TranslatedCondition getCondition() {
|
||||
override TranslatedCondition getCondition() {
|
||||
result = getTranslatedCondition(stmt.getCondition().getFullyConverted())
|
||||
}
|
||||
|
||||
private Instruction getFirstConditionInstruction(EdgeKind kind) {
|
||||
result = this.getCondition().getFirstInstruction(kind)
|
||||
override TranslatedStmt getThen() { result = getTranslatedStmt(stmt.getThen()) }
|
||||
|
||||
override TranslatedStmt getElse() { result = getTranslatedStmt(stmt.getElse()) }
|
||||
|
||||
override predicate hasElse() { exists(stmt.getElse()) }
|
||||
}
|
||||
|
||||
class TranslatedConstExprIfStmt extends TranslatedIfLikeStmt {
|
||||
override ConstexprIfStmt stmt;
|
||||
|
||||
override predicate hasInitialization() { exists(stmt.getInitialization()) }
|
||||
|
||||
override TranslatedStmt getInitialization() {
|
||||
result = getTranslatedStmt(stmt.getInitialization())
|
||||
}
|
||||
|
||||
private TranslatedStmt getThen() { result = getTranslatedStmt(stmt.getThen()) }
|
||||
|
||||
private TranslatedStmt getElse() { result = getTranslatedStmt(stmt.getElse()) }
|
||||
|
||||
private predicate hasElse() { exists(stmt.getElse()) }
|
||||
|
||||
override Instruction getInstructionSuccessorInternal(InstructionTag tag, EdgeKind kind) { none() }
|
||||
|
||||
override Instruction getChildTrueSuccessor(TranslatedCondition child, EdgeKind kind) {
|
||||
child = this.getCondition() and
|
||||
result = this.getThen().getFirstInstruction(kind)
|
||||
override TranslatedCondition getCondition() {
|
||||
result = getTranslatedCondition(stmt.getCondition().getFullyConverted())
|
||||
}
|
||||
|
||||
override Instruction getChildFalseSuccessor(TranslatedCondition child, EdgeKind kind) {
|
||||
child = this.getCondition() and
|
||||
if this.hasElse()
|
||||
then result = this.getElse().getFirstInstruction(kind)
|
||||
else result = this.getParent().getChildSuccessor(this, kind)
|
||||
}
|
||||
override TranslatedStmt getThen() { result = getTranslatedStmt(stmt.getThen()) }
|
||||
|
||||
override Instruction getChildSuccessorInternal(TranslatedElement child, EdgeKind kind) {
|
||||
child = this.getInitialization() and
|
||||
result = this.getFirstConditionInstruction(kind)
|
||||
or
|
||||
(child = this.getThen() or child = this.getElse()) and
|
||||
result = this.getParent().getChildSuccessor(this, kind)
|
||||
}
|
||||
override TranslatedStmt getElse() { result = getTranslatedStmt(stmt.getElse()) }
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
none()
|
||||
}
|
||||
|
||||
override Instruction getALastInstructionInternal() {
|
||||
result = this.getThen().getALastInstruction()
|
||||
or
|
||||
result = this.getElse().getALastInstruction()
|
||||
}
|
||||
override predicate hasElse() { exists(stmt.getElse()) }
|
||||
}
|
||||
|
||||
abstract class TranslatedLoop extends TranslatedStmt, ConditionContext {
|
||||
@@ -1163,6 +1222,8 @@ class TranslatedForStmt extends TranslatedLoop {
|
||||
class TranslatedRangeBasedForStmt extends TranslatedStmt, ConditionContext {
|
||||
override RangeBasedForStmt stmt;
|
||||
|
||||
override predicate handlesDestructorsExplicitly() { any() }
|
||||
|
||||
override TranslatedElement getChildInternal(int id) {
|
||||
id = 0 and result = this.getInitialization()
|
||||
or
|
||||
@@ -1216,6 +1277,19 @@ class TranslatedRangeBasedForStmt extends TranslatedStmt, ConditionContext {
|
||||
or
|
||||
child = this.getUpdate() and
|
||||
result = this.getCondition().getFirstInstruction(kind)
|
||||
or
|
||||
exists(int destructorId |
|
||||
destructorId >= this.getFirstDestructorCallIndex() and
|
||||
child = this.getChild(destructorId) and
|
||||
result = this.getChild(destructorId + 1).getFirstInstruction(kind)
|
||||
)
|
||||
or
|
||||
exists(int lastDestructorIndex |
|
||||
lastDestructorIndex =
|
||||
max(int n | exists(this.getChild(n)) and n >= this.getFirstDestructorCallIndex()) and
|
||||
child = this.getChild(lastDestructorIndex) and
|
||||
result = this.getParent().getChildSuccessor(this, kind)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
@@ -1231,7 +1305,9 @@ class TranslatedRangeBasedForStmt extends TranslatedStmt, ConditionContext {
|
||||
|
||||
override Instruction getChildFalseSuccessor(TranslatedCondition child, EdgeKind kind) {
|
||||
child = this.getCondition() and
|
||||
result = this.getParent().getChildSuccessor(this, kind)
|
||||
if this.hasAnImplicitDestructorCall()
|
||||
then result = this.getChild(this.getFirstDestructorCallIndex()).getFirstInstruction(kind)
|
||||
else result = this.getParent().getChildSuccessor(this, kind)
|
||||
}
|
||||
|
||||
private TranslatedDeclStmt getRangeVariableDeclStmt() {
|
||||
@@ -1276,6 +1352,11 @@ class TranslatedJumpStmt extends TranslatedStmt {
|
||||
override JumpStmt stmt;
|
||||
|
||||
override Instruction getFirstInstruction(EdgeKind kind) {
|
||||
// The first instruction is a destructor call, if any.
|
||||
result = this.getChildInternal(0).getFirstInstruction(kind)
|
||||
or
|
||||
// Otherwise, the first (and only) instruction is a `NoOp`
|
||||
not exists(this.getChildInternal(0)) and
|
||||
result = this.getInstruction(OnlyInstructionTag()) and
|
||||
kind instanceof GotoEdge
|
||||
}
|
||||
@@ -1284,7 +1365,20 @@ class TranslatedJumpStmt extends TranslatedStmt {
|
||||
result = this.getInstruction(OnlyInstructionTag())
|
||||
}
|
||||
|
||||
override TranslatedElement getChildInternal(int id) { none() }
|
||||
private TranslatedCall getTranslatedImplicitDestructorCall(int id) {
|
||||
result.getExpr() = stmt.getImplicitDestructorCall(id)
|
||||
}
|
||||
|
||||
override TranslatedElement getLastChild() {
|
||||
result =
|
||||
this.getTranslatedImplicitDestructorCall(max(int id |
|
||||
exists(stmt.getImplicitDestructorCall(id))
|
||||
))
|
||||
}
|
||||
|
||||
override TranslatedElement getChildInternal(int id) {
|
||||
result = this.getTranslatedImplicitDestructorCall(id)
|
||||
}
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
tag = OnlyInstructionTag() and
|
||||
@@ -1297,7 +1391,19 @@ class TranslatedJumpStmt extends TranslatedStmt {
|
||||
result = getTranslatedStmt(stmt.getTarget()).getFirstInstruction(kind)
|
||||
}
|
||||
|
||||
override Instruction getChildSuccessorInternal(TranslatedElement child, EdgeKind kind) { none() }
|
||||
final override predicate handlesDestructorsExplicitly() { any() }
|
||||
|
||||
override Instruction getChildSuccessorInternal(TranslatedElement child, EdgeKind kind) {
|
||||
exists(int id | child = this.getChildInternal(id) |
|
||||
// Transition to the next destructor call, if any.
|
||||
result = this.getChildInternal(id + 1).getFirstInstruction(kind)
|
||||
or
|
||||
// And otherwise, exit this element by flowing to the target of the jump.
|
||||
not exists(this.getChildInternal(id + 1)) and
|
||||
kind instanceof GotoEdge and
|
||||
result = this.getInstruction(OnlyInstructionTag())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private EdgeKind getCaseEdge(SwitchCase switchCase) {
|
||||
@@ -1501,3 +1607,41 @@ class TranslatedVlaDeclarationStmt extends TranslatedStmt {
|
||||
|
||||
override Instruction getChildSuccessorInternal(TranslatedElement child, EdgeKind kind) { none() }
|
||||
}
|
||||
|
||||
class TranslatedCoReturnStmt extends TranslatedStmt {
|
||||
override CoReturnStmt stmt;
|
||||
|
||||
private TranslatedExpr getTranslatedOperand() {
|
||||
result = getTranslatedExpr(stmt.getOperand().getFullyConverted())
|
||||
}
|
||||
|
||||
override TranslatedExpr getChildInternal(int id) {
|
||||
id = 0 and
|
||||
result = this.getTranslatedOperand()
|
||||
}
|
||||
|
||||
override Instruction getFirstInstruction(EdgeKind kind) {
|
||||
result = this.getTranslatedOperand().getFirstInstruction(kind)
|
||||
}
|
||||
|
||||
override Instruction getALastInstructionInternal() {
|
||||
result = this.getInstruction(OnlyInstructionTag())
|
||||
}
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
tag = OnlyInstructionTag() and
|
||||
opcode instanceof Opcode::NoOp and
|
||||
resultType = getVoidType()
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessorInternal(InstructionTag tag, EdgeKind kind) {
|
||||
tag = OnlyInstructionTag() and
|
||||
result = this.getParent().getChildSuccessor(this, kind)
|
||||
}
|
||||
|
||||
override Instruction getChildSuccessorInternal(TranslatedElement child, EdgeKind kind) {
|
||||
child = this.getTranslatedOperand() and
|
||||
kind instanceof GotoEdge and
|
||||
result = this.getInstruction(OnlyInstructionTag())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,18 +17,11 @@ private import Imports::IRType
|
||||
* The variable may be a user-declared variable (`IRUserVariable`) or a temporary variable generated
|
||||
* by the AST-to-IR translation (`IRTempVariable`).
|
||||
*/
|
||||
class IRVariable extends TIRVariable {
|
||||
abstract private class AbstractIRVariable extends TIRVariable {
|
||||
Language::Declaration func;
|
||||
|
||||
IRVariable() {
|
||||
this = TIRUserVariable(_, _, func) or
|
||||
this = TIRTempVariable(func, _, _, _) or
|
||||
this = TIRStringLiteral(func, _, _, _) or
|
||||
this = TIRDynamicInitializationFlag(func, _, _)
|
||||
}
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { none() }
|
||||
abstract string toString();
|
||||
|
||||
/**
|
||||
* Holds if this variable's value cannot be changed within a function. Currently used for string
|
||||
@@ -49,13 +42,13 @@ class IRVariable extends TIRVariable {
|
||||
/**
|
||||
* Gets the type of the variable.
|
||||
*/
|
||||
Language::LanguageType getLanguageType() { none() }
|
||||
abstract Language::LanguageType getLanguageType();
|
||||
|
||||
/**
|
||||
* Gets the AST node that declared this variable, or that introduced this
|
||||
* variable as part of the AST-to-IR translation.
|
||||
*/
|
||||
Language::AST getAst() { none() }
|
||||
abstract Language::AST getAst();
|
||||
|
||||
/** DEPRECATED: Alias for getAst */
|
||||
deprecated Language::AST getAST() { result = this.getAst() }
|
||||
@@ -64,7 +57,7 @@ class IRVariable extends TIRVariable {
|
||||
* Gets an identifier string for the variable. This identifier is unique
|
||||
* within the function.
|
||||
*/
|
||||
string getUniqueId() { none() }
|
||||
abstract string getUniqueId();
|
||||
|
||||
/**
|
||||
* Gets the source location of this variable.
|
||||
@@ -74,7 +67,7 @@ class IRVariable extends TIRVariable {
|
||||
/**
|
||||
* Gets the IR for the function that references this variable.
|
||||
*/
|
||||
final IRFunction getEnclosingIRFunction() { result.getFunction() = func }
|
||||
final IRFunction getEnclosingIRFunction() { result.getFunction() = this.getEnclosingFunction() }
|
||||
|
||||
/**
|
||||
* Gets the function that references this variable.
|
||||
@@ -82,10 +75,18 @@ class IRVariable extends TIRVariable {
|
||||
final Language::Declaration getEnclosingFunction() { result = func }
|
||||
}
|
||||
|
||||
/**
|
||||
* A variable referenced by the IR for a function.
|
||||
*
|
||||
* The variable may be a user-declared variable (`IRUserVariable`) or a temporary variable generated
|
||||
* by the AST-to-IR translation (`IRTempVariable`).
|
||||
*/
|
||||
final class IRVariable = AbstractIRVariable;
|
||||
|
||||
/**
|
||||
* A user-declared variable referenced by the IR for a function.
|
||||
*/
|
||||
class IRUserVariable extends IRVariable, TIRUserVariable {
|
||||
class IRUserVariable extends AbstractIRVariable, TIRUserVariable {
|
||||
Language::Variable var;
|
||||
Language::LanguageType type;
|
||||
|
||||
@@ -114,26 +115,29 @@ class IRUserVariable extends IRVariable, TIRUserVariable {
|
||||
* A variable (user-declared or temporary) that is allocated on the stack. This includes all
|
||||
* parameters, non-static local variables, and temporary variables.
|
||||
*/
|
||||
class IRAutomaticVariable extends IRVariable {
|
||||
IRAutomaticVariable() {
|
||||
exists(Language::Variable var |
|
||||
this = TIRUserVariable(var, _, func) and
|
||||
Language::isVariableAutomatic(var)
|
||||
)
|
||||
or
|
||||
this = TIRTempVariable(func, _, _, _)
|
||||
}
|
||||
abstract private class AbstractIRAutomaticVariable extends AbstractIRVariable { }
|
||||
|
||||
/**
|
||||
* A variable (user-declared or temporary) that is allocated on the stack. This includes all
|
||||
* parameters, non-static local variables, and temporary variables.
|
||||
*/
|
||||
final class IRAutomaticVariable = AbstractIRAutomaticVariable;
|
||||
|
||||
/**
|
||||
* A user-declared variable that is allocated on the stack. This includes all parameters and
|
||||
* non-static local variables.
|
||||
*/
|
||||
private class AbstractIRAutomaticUserVariable extends IRUserVariable, AbstractIRAutomaticVariable {
|
||||
override Language::AutomaticVariable var;
|
||||
|
||||
final override Language::AutomaticVariable getVariable() { result = var }
|
||||
}
|
||||
|
||||
/**
|
||||
* A user-declared variable that is allocated on the stack. This includes all parameters and
|
||||
* non-static local variables.
|
||||
*/
|
||||
class IRAutomaticUserVariable extends IRUserVariable, IRAutomaticVariable {
|
||||
override Language::AutomaticVariable var;
|
||||
|
||||
final override Language::AutomaticVariable getVariable() { result = var }
|
||||
}
|
||||
final class IRAutomaticUserVariable = AbstractIRAutomaticUserVariable;
|
||||
|
||||
/**
|
||||
* A user-declared variable that is not allocated on the stack. This includes all global variables,
|
||||
@@ -151,16 +155,10 @@ class IRStaticUserVariable extends IRUserVariable {
|
||||
* A variable that is not user-declared. This includes temporary variables generated as part of IR
|
||||
* construction, as well as string literals.
|
||||
*/
|
||||
class IRGeneratedVariable extends IRVariable {
|
||||
abstract private class AbstractIRGeneratedVariable extends AbstractIRVariable {
|
||||
Language::AST ast;
|
||||
Language::LanguageType type;
|
||||
|
||||
IRGeneratedVariable() {
|
||||
this = TIRTempVariable(func, ast, _, type) or
|
||||
this = TIRStringLiteral(func, ast, type, _) or
|
||||
this = TIRDynamicInitializationFlag(func, ast, type)
|
||||
}
|
||||
|
||||
final override Language::LanguageType getLanguageType() { result = type }
|
||||
|
||||
final override Language::AST getAst() { result = ast }
|
||||
@@ -196,12 +194,20 @@ class IRGeneratedVariable extends IRVariable {
|
||||
string getBaseString() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A variable that is not user-declared. This includes temporary variables generated as part of IR
|
||||
* construction, as well as string literals.
|
||||
*/
|
||||
final class IRGeneratedVariable = AbstractIRGeneratedVariable;
|
||||
|
||||
/**
|
||||
* A temporary variable introduced by IR construction. The most common examples are the variable
|
||||
* generated to hold the return value of a function, or the variable generated to hold the result of
|
||||
* a condition operator (`a ? b : c`).
|
||||
*/
|
||||
class IRTempVariable extends IRGeneratedVariable, IRAutomaticVariable, TIRTempVariable {
|
||||
class IRTempVariable extends AbstractIRGeneratedVariable, AbstractIRAutomaticVariable,
|
||||
TIRTempVariable
|
||||
{
|
||||
TempVariableTag tag;
|
||||
|
||||
IRTempVariable() { this = TIRTempVariable(func, ast, tag, type) }
|
||||
@@ -241,7 +247,7 @@ class IRThrowVariable extends IRTempVariable {
|
||||
* A temporary variable generated to hold the contents of all arguments passed to the `...` of a
|
||||
* function that accepts a variable number of arguments.
|
||||
*/
|
||||
class IREllipsisVariable extends IRTempVariable, IRParameter {
|
||||
class IREllipsisVariable extends IRTempVariable, AbstractIRParameter {
|
||||
IREllipsisVariable() { tag = EllipsisTempVar() }
|
||||
|
||||
final override string toString() { result = "#ellipsis" }
|
||||
@@ -252,7 +258,7 @@ class IREllipsisVariable extends IRTempVariable, IRParameter {
|
||||
/**
|
||||
* A temporary variable generated to hold the `this` pointer.
|
||||
*/
|
||||
class IRThisVariable extends IRTempVariable, IRParameter {
|
||||
class IRThisVariable extends IRTempVariable, AbstractIRParameter {
|
||||
IRThisVariable() { tag = ThisTempVar() }
|
||||
|
||||
final override string toString() { result = "#this" }
|
||||
@@ -264,7 +270,7 @@ class IRThisVariable extends IRTempVariable, IRParameter {
|
||||
* A variable generated to represent the contents of a string literal. This variable acts much like
|
||||
* a read-only global variable.
|
||||
*/
|
||||
class IRStringLiteral extends IRGeneratedVariable, TIRStringLiteral {
|
||||
class IRStringLiteral extends AbstractIRGeneratedVariable, TIRStringLiteral {
|
||||
Language::StringLiteral literal;
|
||||
|
||||
IRStringLiteral() { this = TIRStringLiteral(func, ast, type, literal) }
|
||||
@@ -288,7 +294,7 @@ class IRStringLiteral extends IRGeneratedVariable, TIRStringLiteral {
|
||||
* used to model the runtime initialization of static local variables in C++, as well as static
|
||||
* fields in C#.
|
||||
*/
|
||||
class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitializationFlag {
|
||||
class IRDynamicInitializationFlag extends AbstractIRGeneratedVariable, TIRDynamicInitializationFlag {
|
||||
Language::Variable var;
|
||||
|
||||
IRDynamicInitializationFlag() {
|
||||
@@ -314,24 +320,24 @@ class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitial
|
||||
* An IR variable which acts like a function parameter, including positional parameters and the
|
||||
* temporary variables generated for `this` and ellipsis parameters.
|
||||
*/
|
||||
class IRParameter extends IRAutomaticVariable {
|
||||
IRParameter() {
|
||||
this.(IRAutomaticUserVariable).getVariable() instanceof Language::Parameter
|
||||
or
|
||||
this = TIRTempVariable(_, _, ThisTempVar(), _)
|
||||
or
|
||||
this = TIRTempVariable(_, _, EllipsisTempVar(), _)
|
||||
}
|
||||
|
||||
abstract private class AbstractIRParameter extends AbstractIRAutomaticVariable {
|
||||
/**
|
||||
* Gets the zero-based index of this parameter. The `this` parameter has index -1.
|
||||
*/
|
||||
int getIndex() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An IR variable which acts like a function parameter, including positional parameters and the
|
||||
* temporary variables generated for `this` and ellipsis parameters.
|
||||
*/
|
||||
final class IRParameter = AbstractIRParameter;
|
||||
|
||||
/**
|
||||
* An IR variable representing a positional parameter.
|
||||
*/
|
||||
class IRPositionalParameter extends IRParameter, IRAutomaticUserVariable {
|
||||
class IRPositionalParameter extends AbstractIRParameter, AbstractIRAutomaticUserVariable {
|
||||
IRPositionalParameter() { this.getVariable() instanceof Language::Parameter }
|
||||
|
||||
final override int getIndex() { result = this.getVariable().(Language::Parameter).getIndex() }
|
||||
}
|
||||
|
||||
@@ -247,8 +247,7 @@ class Instruction extends Construction::TStageInstruction {
|
||||
* Gets the type of the result produced by this instruction. If the instruction does not produce
|
||||
* a result, its result type will be `IRVoidType`.
|
||||
*/
|
||||
cached
|
||||
final IRType getResultIRType() { result = this.getResultLanguageType().getIRType() }
|
||||
final IRType getResultIRType() { result = Construction::getInstructionResultIRType(this) }
|
||||
|
||||
/**
|
||||
* Gets the type of the result produced by this instruction. If the
|
||||
@@ -995,9 +994,8 @@ class ConstantInstruction extends ConstantValueInstruction {
|
||||
*/
|
||||
class IntegerConstantInstruction extends ConstantInstruction {
|
||||
IntegerConstantInstruction() {
|
||||
exists(IRType resultType |
|
||||
resultType = this.getResultIRType() and
|
||||
(resultType instanceof IRIntegerType or resultType instanceof IRBooleanType)
|
||||
exists(IRType resultType | resultType = this.getResultIRType() |
|
||||
resultType instanceof IRIntegerType or resultType instanceof IRBooleanType
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1009,6 +1007,17 @@ class FloatConstantInstruction extends ConstantInstruction {
|
||||
FloatConstantInstruction() { this.getResultIRType() instanceof IRFloatingPointType }
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction whose result is a constant value of a pointer type.
|
||||
*/
|
||||
class PointerConstantInstruction extends ConstantInstruction {
|
||||
PointerConstantInstruction() {
|
||||
exists(IRType resultType | resultType = this.getResultIRType() |
|
||||
resultType instanceof IRAddressType or resultType instanceof IRFunctionAddressType
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction whose result is the address of a string literal.
|
||||
*/
|
||||
|
||||
@@ -429,6 +429,11 @@ private module Cached {
|
||||
instr = unreachedInstruction(_) and result = Language::getVoidType()
|
||||
}
|
||||
|
||||
cached
|
||||
IRType getInstructionResultIRType(Instruction instr) {
|
||||
result = instr.getResultLanguageType().getIRType()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `opcode` is the opcode that specifies the operation performed by `instr`.
|
||||
*
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
private import implementations.Allocation
|
||||
private import implementations.Deallocation
|
||||
private import implementations.Fopen
|
||||
private import implementations.Fread
|
||||
private import implementations.Getenv
|
||||
private import implementations.Gets
|
||||
@@ -41,4 +42,4 @@ private import implementations.SqLite3
|
||||
private import implementations.PostgreSql
|
||||
private import implementations.System
|
||||
private import implementations.StructuredExceptionHandling
|
||||
private import implementations.Fopen
|
||||
private import implementations.ZMQ
|
||||
|
||||
@@ -112,3 +112,21 @@ private class GetsFunction extends DataFlowFunction, ArrayFunction, AliasFunctio
|
||||
|
||||
override predicate hasArrayOutput(int bufParam) { bufParam = 0 }
|
||||
}
|
||||
|
||||
/**
|
||||
* A model for `getc` and similar functions that are flow sources.
|
||||
*/
|
||||
private class GetcSource extends SourceModelCsv {
|
||||
override predicate row(string row) {
|
||||
row =
|
||||
[
|
||||
";;false;getc;;;ReturnValue;remote", ";;false;getwc;;;ReturnValue;remote",
|
||||
";;false;_getc_nolock;;;ReturnValue;remote", ";;false;_getwc_nolock;;;ReturnValue;remote",
|
||||
";;false;getch;;;ReturnValue;local", ";;false;_getch;;;ReturnValue;local",
|
||||
";;false;_getwch;;;ReturnValue;local", ";;false;_getch_nolock;;;ReturnValue;local",
|
||||
";;false;_getwch_nolock;;;ReturnValue;local", ";;false;getchar;;;ReturnValue;local",
|
||||
";;false;getwchar;;;ReturnValue;local", ";;false;_getchar_nolock;;;ReturnValue;local",
|
||||
";;false;_getwchar_nolock;;;ReturnValue;local",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -560,7 +560,7 @@ private class IteratorAssignmentMemberOperatorModel extends IteratorAssignmentMe
|
||||
TaintFunction, SideEffectFunction, AliasFunction
|
||||
{
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isParameterDeref(0) and
|
||||
(input.isParameterDeref(0) or input.isParameter(0)) and
|
||||
output.isQualifierObject()
|
||||
}
|
||||
|
||||
@@ -579,17 +579,34 @@ private class IteratorAssignmentMemberOperatorModel extends IteratorAssignmentMe
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { index = -1 }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `begin` member function, or a related function, that returns an iterator.
|
||||
*/
|
||||
class BeginFunction extends MemberFunction {
|
||||
BeginFunction() {
|
||||
this.hasName(["begin", "cbegin", "rbegin", "crbegin", "before_begin", "cbefore_begin"]) and
|
||||
this.getType().getUnspecifiedType() instanceof Iterator
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An `end` member function, or a related function, that returns an iterator.
|
||||
*/
|
||||
class EndFunction extends MemberFunction {
|
||||
EndFunction() {
|
||||
this.hasName(["end", "cend", "rend", "crend"]) and
|
||||
this.getType().getUnspecifiedType() instanceof Iterator
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `begin` or `end` member function, or a related member function, that
|
||||
* returns an iterator.
|
||||
*/
|
||||
class BeginOrEndFunction extends MemberFunction {
|
||||
BeginOrEndFunction() {
|
||||
this.hasName([
|
||||
"begin", "cbegin", "rbegin", "crbegin", "end", "cend", "rend", "crend", "before_begin",
|
||||
"cbefore_begin"
|
||||
]) and
|
||||
this.getType().getUnspecifiedType() instanceof Iterator
|
||||
this instanceof BeginFunction or
|
||||
this instanceof EndFunction
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
45
cpp/ql/lib/semmle/code/cpp/models/implementations/ZMQ.qll
Normal file
45
cpp/ql/lib/semmle/code/cpp/models/implementations/ZMQ.qll
Normal file
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* Provides implementation classes modeling the ZeroMQ networking library.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.models.interfaces.FlowSource
|
||||
|
||||
/**
|
||||
* Remote flow sources.
|
||||
*/
|
||||
private class ZmqSource extends SourceModelCsv {
|
||||
override predicate row(string row) {
|
||||
row =
|
||||
[
|
||||
";;false;zmq_recv;;;Argument[*1];remote", ";;false;zmq_recvmsg;;;Argument[*1];remote",
|
||||
";;false;zmq_msg_recv;;;Argument[*0];remote",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remote flow sinks.
|
||||
*/
|
||||
private class ZmqSinks extends SinkModelCsv {
|
||||
override predicate row(string row) {
|
||||
row =
|
||||
[
|
||||
";;false;zmq_send;;;Argument[*1];remote-sink",
|
||||
";;false;zmq_sendmsg;;;Argument[*1];remote-sink",
|
||||
";;false;zmq_msg_send;;;Argument[*0];remote-sink",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Flow steps.
|
||||
*/
|
||||
private class ZmqSummaries extends SummaryModelCsv {
|
||||
override predicate row(string row) {
|
||||
row =
|
||||
[
|
||||
";;false;zmq_msg_init_data;;;Argument[*1];Argument[*0];taint",
|
||||
";;false;zmq_msg_data;;;Argument[*0];ReturnValue[*];taint",
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@
|
||||
import cpp
|
||||
import FunctionInputsAndOutputs
|
||||
import semmle.code.cpp.models.Models
|
||||
import semmle.code.cpp.dataflow.ExternalFlow
|
||||
|
||||
/**
|
||||
* A library function that returns data that may be read from a network connection.
|
||||
|
||||
@@ -20,6 +20,9 @@ abstract class RemoteFlowSource extends FlowSource { }
|
||||
/** A data flow source of local user input. */
|
||||
abstract class LocalFlowSource extends FlowSource { }
|
||||
|
||||
/**
|
||||
* A remote data flow source that is defined through a `RemoteFlowSourceFunction` model.
|
||||
*/
|
||||
private class RemoteModelSource extends RemoteFlowSource {
|
||||
string sourceType;
|
||||
|
||||
@@ -34,6 +37,9 @@ private class RemoteModelSource extends RemoteFlowSource {
|
||||
override string getSourceType() { result = sourceType }
|
||||
}
|
||||
|
||||
/**
|
||||
* A local data flow source that is defined through a `LocalFlowSourceFunction` model.
|
||||
*/
|
||||
private class LocalModelSource extends LocalFlowSource {
|
||||
string sourceType;
|
||||
|
||||
@@ -48,6 +54,9 @@ private class LocalModelSource extends LocalFlowSource {
|
||||
override string getSourceType() { result = sourceType }
|
||||
}
|
||||
|
||||
/**
|
||||
* A local data flow source that the `argv` parameter to `main`.
|
||||
*/
|
||||
private class ArgvSource extends LocalFlowSource {
|
||||
ArgvSource() {
|
||||
exists(Function main, Parameter argv |
|
||||
@@ -60,12 +69,33 @@ private class ArgvSource extends LocalFlowSource {
|
||||
override string getSourceType() { result = "a command-line argument" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A remote data flow source that is defined through 'models as data'.
|
||||
*/
|
||||
private class ExternalRemoteFlowSource extends RemoteFlowSource {
|
||||
ExternalRemoteFlowSource() { sourceNode(this, "remote") }
|
||||
|
||||
override string getSourceType() { result = "external" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A local data flow source that is defined through 'models as data'.
|
||||
*/
|
||||
private class ExternalLocalFlowSource extends LocalFlowSource {
|
||||
ExternalLocalFlowSource() { sourceNode(this, "local") }
|
||||
|
||||
override string getSourceType() { result = "external" }
|
||||
}
|
||||
|
||||
/** A remote data flow sink. */
|
||||
abstract class RemoteFlowSink extends DataFlow::Node {
|
||||
/** Gets a string that describes the type of this flow sink. */
|
||||
abstract string getSinkType();
|
||||
}
|
||||
|
||||
/**
|
||||
* A remote flow sink derived from the `RemoteFlowSinkFunction` model.
|
||||
*/
|
||||
private class RemoteParameterSink extends RemoteFlowSink {
|
||||
string sourceType;
|
||||
|
||||
@@ -79,3 +109,12 @@ private class RemoteParameterSink extends RemoteFlowSink {
|
||||
|
||||
override string getSinkType() { result = sourceType }
|
||||
}
|
||||
|
||||
/**
|
||||
* A remote flow sink defined in a CSV model.
|
||||
*/
|
||||
private class RemoteFlowFromCsvSink extends RemoteFlowSink {
|
||||
RemoteFlowFromCsvSink() { sinkNode(this, "remote-sink") }
|
||||
|
||||
override string getSinkType() { result = "remote flow sink" }
|
||||
}
|
||||
|
||||
@@ -139,6 +139,7 @@ private module ParameterSinks {
|
||||
}
|
||||
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowImplCommon
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate
|
||||
|
||||
/**
|
||||
* Holds if `n` represents the expression `e`, and `e` is a pointer that is
|
||||
@@ -149,11 +150,11 @@ private import semmle.code.cpp.ir.dataflow.internal.DataFlowImplCommon
|
||||
predicate isUse(DataFlow::Node n, Expr e) {
|
||||
isUse0(e) and n.asExpr() = e
|
||||
or
|
||||
exists(CallInstruction call, InitializeParameterInstruction init |
|
||||
exists(DataFlowCall call, InitializeParameterInstruction init |
|
||||
n.asOperand().getDef().getUnconvertedResultExpression() = e and
|
||||
pragma[only_bind_into](init) = ParameterSinks::getAnAlwaysDereferencedParameter() and
|
||||
viableParamArg(call, DataFlow::instructionNode(init), n) and
|
||||
pragma[only_bind_out](init.getEnclosingFunction()) =
|
||||
pragma[only_bind_out](call.getStaticCallTarget())
|
||||
pragma[only_bind_out](call.asCallInstruction().getStaticCallTarget())
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,616 +0,0 @@
|
||||
/**
|
||||
* DEPRECATED: This library has been replaced with a newer version which
|
||||
* provides better performance and precision. Use
|
||||
* `semmle.code.cpp.valuenumbering.GlobalValueNumbering` instead.
|
||||
*
|
||||
* Provides an implementation of Global Value Numbering.
|
||||
* See https://en.wikipedia.org/wiki/Global_value_numbering
|
||||
*
|
||||
* The predicate `globalValueNumber` converts an expression into a `GVN`,
|
||||
* which is an abstract type representing the value of the expression. If
|
||||
* two expressions have the same `GVN` then they compute the same value.
|
||||
* For example:
|
||||
*
|
||||
* ```
|
||||
* void f(int x, int y) {
|
||||
* g(x+y, x+y);
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* In this example, both arguments in the call to `g` compute the same value,
|
||||
* so both arguments have the same `GVN`. In other words, we can find
|
||||
* this call with the following query:
|
||||
*
|
||||
* ```
|
||||
* from FunctionCall call, GVN v
|
||||
* where v = globalValueNumber(call.getArgument(0))
|
||||
* and v = globalValueNumber(call.getArgument(1))
|
||||
* select call
|
||||
* ```
|
||||
*
|
||||
* The analysis is conservative, so two expressions might have different
|
||||
* `GVN`s even though the actually always compute the same value. The most
|
||||
* common reason for this is that the analysis cannot prove that there
|
||||
* are no side-effects that might cause the computed value to change.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Note to developers: the correctness of this module depends on the
|
||||
* definitions of GVN, globalValueNumber, and analyzableExpr being kept in
|
||||
* sync with each other. If you change this module then make sure that the
|
||||
* change is symmetric across all three.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
private import semmle.code.cpp.controlflow.SSA
|
||||
|
||||
/**
|
||||
* Holds if the result is a control flow node that might change the
|
||||
* value of any global variable. This is used in the implementation
|
||||
* of `GVN_OtherVariable`, because we need to be quite conservative when
|
||||
* we assign a value number to a global variable. For example:
|
||||
*
|
||||
* ```
|
||||
* x = g+1;
|
||||
* dosomething();
|
||||
* y = g+1;
|
||||
* ```
|
||||
*
|
||||
* It is not safe to assign the same value number to both instances
|
||||
* of `g+1` in this example, because the call to `dosomething` might
|
||||
* change the value of `g`.
|
||||
*/
|
||||
private ControlFlowNode nodeWithPossibleSideEffect() {
|
||||
result instanceof Call
|
||||
or
|
||||
// If the lhs of an assignment is not analyzable by SSA, then
|
||||
// we need to treat the assignment as having a possible side-effect.
|
||||
result instanceof Assignment and not result instanceof SsaDefinition
|
||||
or
|
||||
result instanceof CrementOperation and not result instanceof SsaDefinition
|
||||
or
|
||||
exists(LocalVariable v |
|
||||
result = v.getInitializer().getExpr() and not result instanceof SsaDefinition
|
||||
)
|
||||
or
|
||||
result instanceof AsmStmt
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the entry node of the control flow graph of which `node` is a
|
||||
* member.
|
||||
*/
|
||||
cached
|
||||
private ControlFlowNode getControlFlowEntry(ControlFlowNode node) {
|
||||
result = node.getControlFlowScope().getEntryPoint() and
|
||||
result.getASuccessor*() = node
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there is a control flow edge from `src` to `dst` or
|
||||
* if `dst` is an expression with a possible side-effect. The idea
|
||||
* is to treat side effects as entry points in the control flow
|
||||
* graph so that we can use the dominator tree to find the most recent
|
||||
* side-effect.
|
||||
*/
|
||||
private predicate sideEffectCfg(ControlFlowNode src, ControlFlowNode dst) {
|
||||
src.getASuccessor() = dst
|
||||
or
|
||||
// Add an edge from the entry point to any node that might have a side
|
||||
// effect.
|
||||
dst = nodeWithPossibleSideEffect() and
|
||||
src = getControlFlowEntry(dst)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `dominator` is the immediate dominator of `node` in
|
||||
* the side-effect CFG.
|
||||
*/
|
||||
private predicate iDomEffect(ControlFlowNode dominator, ControlFlowNode node) =
|
||||
idominance(functionEntry/1, sideEffectCfg/2)(_, dominator, node)
|
||||
|
||||
/**
|
||||
* Gets the most recent side effect. To be more precise, `result` is a
|
||||
* dominator of `node` and no side-effects can occur between `result` and
|
||||
* `node`.
|
||||
*
|
||||
* `sideEffectCFG` has an edge from the function entry to every node with a
|
||||
* side-effect. This means that every node with a side-effect has the
|
||||
* function entry as its immediate dominator. So if node `x` dominates node
|
||||
* `y` then there can be no side effects between `x` and `y` unless `x` is
|
||||
* the function entry. So the optimal choice for `result` has the function
|
||||
* entry as its immediate dominator.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* 000: int f(int a, int b, int *p) {
|
||||
* 001: int r = 0;
|
||||
* 002: if (a) {
|
||||
* 003: if (b) {
|
||||
* 004: sideEffect1();
|
||||
* 005: }
|
||||
* 006: } else {
|
||||
* 007: sideEffect2();
|
||||
* 008: }
|
||||
* 009: if (a) {
|
||||
* 010: r++; // Not a side-effect, because r is an SSA variable.
|
||||
* 011: }
|
||||
* 012: if (b) {
|
||||
* 013: r++; // Not a side-effect, because r is an SSA variable.
|
||||
* 014: }
|
||||
* 015: return *p;
|
||||
* 016: }
|
||||
* ```
|
||||
*
|
||||
* Suppose we want to find the most recent side-effect for the dereference
|
||||
* of `p` on line 015. The `sideEffectCFG` has an edge from the function
|
||||
* entry (line 000) to the side effects at lines 004 and 007. Therefore,
|
||||
* the immediate dominator tree looks like this:
|
||||
*
|
||||
* 000 - 001 - 002 - 003
|
||||
* - 004
|
||||
* - 007
|
||||
* - 009 - 010
|
||||
* - 012 - 013
|
||||
* - 015
|
||||
*
|
||||
* The immediate dominator path to line 015 is 000 - 009 - 012 - 015.
|
||||
* Therefore, the most recent side effect for line 015 is line 009.
|
||||
*/
|
||||
cached
|
||||
private ControlFlowNode mostRecentSideEffect(ControlFlowNode node) {
|
||||
exists(ControlFlowNode entry |
|
||||
functionEntry(entry) and
|
||||
iDomEffect(entry, result) and
|
||||
iDomEffect*(result, node)
|
||||
)
|
||||
}
|
||||
|
||||
/** Used to represent the "global value number" of an expression. */
|
||||
cached
|
||||
private newtype GvnBase =
|
||||
GVN_IntConst(int val, Type t) { mk_IntConst(val, t, _) } or
|
||||
GVN_FloatConst(float val, Type t) { mk_FloatConst(val, t, _) } or
|
||||
// If the local variable does not have a defining value, then
|
||||
// we use the SsaDefinition as its global value number.
|
||||
GVN_UndefinedStackVariable(StackVariable x, SsaDefinition def) {
|
||||
mk_UndefinedStackVariable(x, def, _)
|
||||
} or
|
||||
// Variables with no SSA information. As a crude (but safe)
|
||||
// approximation, we use `mostRecentSideEffect` to compute a definition
|
||||
// location for the variable. This ensures that two instances of the same
|
||||
// global variable will only get the same value number if they are
|
||||
// guaranteed to have the same value.
|
||||
GVN_OtherVariable(Variable x, ControlFlowNode dominator) { mk_OtherVariable(x, dominator, _) } or
|
||||
deprecated GVN_FieldAccess(GVN s, Field f) {
|
||||
mk_DotFieldAccess(s, f, _) or
|
||||
mk_PointerFieldAccess_with_deref(s, f, _) or
|
||||
mk_ImplicitThisFieldAccess_with_deref(s, f, _)
|
||||
} or
|
||||
// Dereference a pointer. The value might have changed since the last
|
||||
// time the pointer was dereferenced, so we need to include a definition
|
||||
// location. As a crude (but safe) approximation, we use
|
||||
// `mostRecentSideEffect` to compute a definition location.
|
||||
deprecated GVN_Deref(GVN p, ControlFlowNode dominator) {
|
||||
mk_Deref(p, dominator, _) or
|
||||
mk_PointerFieldAccess(p, _, dominator, _) or
|
||||
mk_ImplicitThisFieldAccess_with_qualifier(p, _, dominator, _)
|
||||
} or
|
||||
GVN_ThisExpr(Function fcn) {
|
||||
mk_ThisExpr(fcn, _) or
|
||||
mk_ImplicitThisFieldAccess(fcn, _, _, _)
|
||||
} or
|
||||
deprecated GVN_Conversion(Type t, GVN child) { mk_Conversion(t, child, _) } or
|
||||
deprecated GVN_BinaryOp(GVN lhs, GVN rhs, string opname) { mk_BinaryOp(lhs, rhs, opname, _) } or
|
||||
deprecated GVN_UnaryOp(GVN child, string opname) { mk_UnaryOp(child, opname, _) } or
|
||||
deprecated GVN_ArrayAccess(GVN x, GVN i, ControlFlowNode dominator) {
|
||||
mk_ArrayAccess(x, i, dominator, _)
|
||||
} or
|
||||
// Any expression that is not handled by the cases above is
|
||||
// given a unique number based on the expression itself.
|
||||
GVN_Unanalyzable(Expr e) { not analyzableExpr(e) }
|
||||
|
||||
/**
|
||||
* A Global Value Number. A GVN is an abstract representation of the value
|
||||
* computed by an expression. The relationship between `Expr` and `GVN` is
|
||||
* many-to-one: every `Expr` has exactly one `GVN`, but multiple
|
||||
* expressions can have the same `GVN`. If two expressions have the same
|
||||
* `GVN`, it means that they compute the same value at run time. The `GVN`
|
||||
* is an opaque value, so you cannot deduce what the run-time value of an
|
||||
* expression will be from its `GVN`. The only use for the `GVN` of an
|
||||
* expression is to find other expressions that compute the same value.
|
||||
* Use the predicate `globalValueNumber` to get the `GVN` for an `Expr`.
|
||||
*
|
||||
* Note: `GVN` has `toString` and `getLocation` methods, so that it can be
|
||||
* displayed in a results list. These work by picking an arbitrary
|
||||
* expression with this `GVN` and using its `toString` and `getLocation`
|
||||
* methods.
|
||||
*/
|
||||
deprecated class GVN extends GvnBase {
|
||||
GVN() { this instanceof GvnBase }
|
||||
|
||||
/** Gets an expression that has this GVN. */
|
||||
Expr getAnExpr() { this = globalValueNumber(result) }
|
||||
|
||||
/** Gets the kind of the GVN. This can be useful for debugging. */
|
||||
string getKind() {
|
||||
if this instanceof GVN_IntConst
|
||||
then result = "IntConst"
|
||||
else
|
||||
if this instanceof GVN_FloatConst
|
||||
then result = "FloatConst"
|
||||
else
|
||||
if this instanceof GVN_UndefinedStackVariable
|
||||
then result = "UndefinedStackVariable"
|
||||
else
|
||||
if this instanceof GVN_OtherVariable
|
||||
then result = "OtherVariable"
|
||||
else
|
||||
if this instanceof GVN_FieldAccess
|
||||
then result = "FieldAccess"
|
||||
else
|
||||
if this instanceof GVN_Deref
|
||||
then result = "Deref"
|
||||
else
|
||||
if this instanceof GVN_ThisExpr
|
||||
then result = "ThisExpr"
|
||||
else
|
||||
if this instanceof GVN_Conversion
|
||||
then result = "Conversion"
|
||||
else
|
||||
if this instanceof GVN_BinaryOp
|
||||
then result = "BinaryOp"
|
||||
else
|
||||
if this instanceof GVN_UnaryOp
|
||||
then result = "UnaryOp"
|
||||
else
|
||||
if this instanceof GVN_ArrayAccess
|
||||
then result = "ArrayAccess"
|
||||
else
|
||||
if this instanceof GVN_Unanalyzable
|
||||
then result = "Unanalyzable"
|
||||
else result = "error"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an example of an expression with this GVN.
|
||||
* This is useful for things like implementing toString().
|
||||
*/
|
||||
private Expr exampleExpr() {
|
||||
// Pick the expression with the minimum source location string. This is
|
||||
// just an arbitrary way to pick an expression with this `GVN`.
|
||||
result = min(Expr e | this = globalValueNumber(e) | e order by e.getLocation().toString())
|
||||
}
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = this.exampleExpr().toString() }
|
||||
|
||||
/** Gets the primary location of this element. */
|
||||
Location getLocation() { result = this.exampleExpr().getLocation() }
|
||||
}
|
||||
|
||||
private predicate analyzableIntConst(Expr e) {
|
||||
strictcount(e.getValue().toInt()) = 1 and
|
||||
strictcount(e.getUnspecifiedType()) = 1
|
||||
}
|
||||
|
||||
private predicate mk_IntConst(int val, Type t, Expr e) {
|
||||
analyzableIntConst(e) and
|
||||
val = e.getValue().toInt() and
|
||||
t = e.getUnspecifiedType()
|
||||
}
|
||||
|
||||
private predicate analyzableFloatConst(Expr e) {
|
||||
strictcount(e.getValue().toFloat()) = 1 and
|
||||
strictcount(e.getUnspecifiedType()) = 1 and
|
||||
not analyzableIntConst(e)
|
||||
}
|
||||
|
||||
private predicate mk_FloatConst(float val, Type t, Expr e) {
|
||||
analyzableFloatConst(e) and
|
||||
val = e.getValue().toFloat() and
|
||||
t = e.getUnspecifiedType()
|
||||
}
|
||||
|
||||
private predicate analyzableStackVariable(VariableAccess access) {
|
||||
strictcount(SsaDefinition def | def.getAUse(_) = access | def) = 1 and
|
||||
strictcount(SsaDefinition def, Variable v | def.getAUse(v) = access | v) = 1 and
|
||||
count(SsaDefinition def, Variable v |
|
||||
def.getAUse(v) = access
|
||||
|
|
||||
def.getDefiningValue(v).getFullyConverted()
|
||||
) <= 1 and
|
||||
not analyzableConst(access)
|
||||
}
|
||||
|
||||
// Note: this predicate only has a result if the access has no
|
||||
// defining value. If there is a defining value, then there is no
|
||||
// need to generate a fresh `GVN` for the access because `globalValueNumber`
|
||||
// will follow the chain and use the GVN of the defining value.
|
||||
private predicate mk_UndefinedStackVariable(
|
||||
StackVariable x, SsaDefinition def, VariableAccess access
|
||||
) {
|
||||
analyzableStackVariable(access) and
|
||||
access = def.getAUse(x) and
|
||||
not exists(def.getDefiningValue(x))
|
||||
}
|
||||
|
||||
private predicate analyzableDotFieldAccess(DotFieldAccess access) {
|
||||
strictcount(access.getTarget()) = 1 and
|
||||
strictcount(access.getQualifier().getFullyConverted()) = 1 and
|
||||
not analyzableConst(access)
|
||||
}
|
||||
|
||||
deprecated private predicate mk_DotFieldAccess(GVN qualifier, Field target, DotFieldAccess access) {
|
||||
analyzableDotFieldAccess(access) and
|
||||
target = access.getTarget() and
|
||||
qualifier = globalValueNumber(access.getQualifier().getFullyConverted())
|
||||
}
|
||||
|
||||
private predicate analyzablePointerFieldAccess(PointerFieldAccess access) {
|
||||
strictcount(mostRecentSideEffect(access)) = 1 and
|
||||
strictcount(access.getTarget()) = 1 and
|
||||
strictcount(access.getQualifier().getFullyConverted()) = 1 and
|
||||
not analyzableConst(access)
|
||||
}
|
||||
|
||||
deprecated private predicate mk_PointerFieldAccess(
|
||||
GVN qualifier, Field target, ControlFlowNode dominator, PointerFieldAccess access
|
||||
) {
|
||||
analyzablePointerFieldAccess(access) and
|
||||
dominator = mostRecentSideEffect(access) and
|
||||
target = access.getTarget() and
|
||||
qualifier = globalValueNumber(access.getQualifier().getFullyConverted())
|
||||
}
|
||||
|
||||
/**
|
||||
* `obj->field` is equivalent to `(*obj).field`, so we need to wrap an
|
||||
* extra `GVN_Deref` around the qualifier.
|
||||
*/
|
||||
deprecated private predicate mk_PointerFieldAccess_with_deref(
|
||||
GVN new_qualifier, Field target, PointerFieldAccess access
|
||||
) {
|
||||
exists(GVN qualifier, ControlFlowNode dominator |
|
||||
mk_PointerFieldAccess(qualifier, target, dominator, access) and
|
||||
new_qualifier = GVN_Deref(qualifier, dominator)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate analyzableImplicitThisFieldAccess(ImplicitThisFieldAccess access) {
|
||||
strictcount(mostRecentSideEffect(access)) = 1 and
|
||||
strictcount(access.getTarget()) = 1 and
|
||||
strictcount(access.getEnclosingFunction()) = 1 and
|
||||
not analyzableConst(access)
|
||||
}
|
||||
|
||||
private predicate mk_ImplicitThisFieldAccess(
|
||||
Function fcn, Field target, ControlFlowNode dominator, ImplicitThisFieldAccess access
|
||||
) {
|
||||
analyzableImplicitThisFieldAccess(access) and
|
||||
dominator = mostRecentSideEffect(access) and
|
||||
target = access.getTarget() and
|
||||
fcn = access.getEnclosingFunction()
|
||||
}
|
||||
|
||||
deprecated private predicate mk_ImplicitThisFieldAccess_with_qualifier(
|
||||
GVN qualifier, Field target, ControlFlowNode dominator, ImplicitThisFieldAccess access
|
||||
) {
|
||||
exists(Function fcn |
|
||||
mk_ImplicitThisFieldAccess(fcn, target, dominator, access) and
|
||||
qualifier = GVN_ThisExpr(fcn)
|
||||
)
|
||||
}
|
||||
|
||||
deprecated private predicate mk_ImplicitThisFieldAccess_with_deref(
|
||||
GVN new_qualifier, Field target, ImplicitThisFieldAccess access
|
||||
) {
|
||||
exists(GVN qualifier, ControlFlowNode dominator |
|
||||
mk_ImplicitThisFieldAccess_with_qualifier(qualifier, target, dominator, access) and
|
||||
new_qualifier = GVN_Deref(qualifier, dominator)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `access` is an access of a variable that does
|
||||
* not have SSA information. (For example, because the variable
|
||||
* is global.)
|
||||
*/
|
||||
private predicate analyzableOtherVariable(VariableAccess access) {
|
||||
not access instanceof FieldAccess and
|
||||
not exists(SsaDefinition def | access = def.getAUse(_)) and
|
||||
strictcount(access.getTarget()) = 1 and
|
||||
strictcount(mostRecentSideEffect(access)) = 1 and
|
||||
not analyzableConst(access)
|
||||
}
|
||||
|
||||
private predicate mk_OtherVariable(Variable x, ControlFlowNode dominator, VariableAccess access) {
|
||||
analyzableOtherVariable(access) and
|
||||
x = access.getTarget() and
|
||||
dominator = mostRecentSideEffect(access)
|
||||
}
|
||||
|
||||
private predicate analyzableConversion(Conversion conv) {
|
||||
strictcount(conv.getUnspecifiedType()) = 1 and
|
||||
strictcount(conv.getExpr()) = 1 and
|
||||
not analyzableConst(conv)
|
||||
}
|
||||
|
||||
deprecated private predicate mk_Conversion(Type t, GVN child, Conversion conv) {
|
||||
analyzableConversion(conv) and
|
||||
t = conv.getUnspecifiedType() and
|
||||
child = globalValueNumber(conv.getExpr())
|
||||
}
|
||||
|
||||
private predicate analyzableBinaryOp(BinaryOperation op) {
|
||||
op.isPure() and
|
||||
strictcount(op.getLeftOperand().getFullyConverted()) = 1 and
|
||||
strictcount(op.getRightOperand().getFullyConverted()) = 1 and
|
||||
strictcount(op.getOperator()) = 1 and
|
||||
not analyzableConst(op)
|
||||
}
|
||||
|
||||
deprecated private predicate mk_BinaryOp(GVN lhs, GVN rhs, string opname, BinaryOperation op) {
|
||||
analyzableBinaryOp(op) and
|
||||
lhs = globalValueNumber(op.getLeftOperand().getFullyConverted()) and
|
||||
rhs = globalValueNumber(op.getRightOperand().getFullyConverted()) and
|
||||
opname = op.getOperator()
|
||||
}
|
||||
|
||||
private predicate analyzableUnaryOp(UnaryOperation op) {
|
||||
not op instanceof PointerDereferenceExpr and
|
||||
op.isPure() and
|
||||
strictcount(op.getOperand().getFullyConverted()) = 1 and
|
||||
strictcount(op.getOperator()) = 1 and
|
||||
not analyzableConst(op)
|
||||
}
|
||||
|
||||
deprecated private predicate mk_UnaryOp(GVN child, string opname, UnaryOperation op) {
|
||||
analyzableUnaryOp(op) and
|
||||
child = globalValueNumber(op.getOperand().getFullyConverted()) and
|
||||
opname = op.getOperator()
|
||||
}
|
||||
|
||||
private predicate analyzableThisExpr(ThisExpr thisExpr) {
|
||||
strictcount(thisExpr.getEnclosingFunction()) = 1 and
|
||||
not analyzableConst(thisExpr)
|
||||
}
|
||||
|
||||
private predicate mk_ThisExpr(Function fcn, ThisExpr thisExpr) {
|
||||
analyzableThisExpr(thisExpr) and
|
||||
fcn = thisExpr.getEnclosingFunction()
|
||||
}
|
||||
|
||||
private predicate analyzableArrayAccess(ArrayExpr ae) {
|
||||
strictcount(ae.getArrayBase().getFullyConverted()) = 1 and
|
||||
strictcount(ae.getArrayOffset().getFullyConverted()) = 1 and
|
||||
strictcount(mostRecentSideEffect(ae)) = 1 and
|
||||
not analyzableConst(ae)
|
||||
}
|
||||
|
||||
deprecated private predicate mk_ArrayAccess(
|
||||
GVN base, GVN offset, ControlFlowNode dominator, ArrayExpr ae
|
||||
) {
|
||||
analyzableArrayAccess(ae) and
|
||||
base = globalValueNumber(ae.getArrayBase().getFullyConverted()) and
|
||||
offset = globalValueNumber(ae.getArrayOffset().getFullyConverted()) and
|
||||
dominator = mostRecentSideEffect(ae)
|
||||
}
|
||||
|
||||
private predicate analyzablePointerDereferenceExpr(PointerDereferenceExpr deref) {
|
||||
strictcount(deref.getOperand().getFullyConverted()) = 1 and
|
||||
strictcount(mostRecentSideEffect(deref)) = 1 and
|
||||
not analyzableConst(deref)
|
||||
}
|
||||
|
||||
deprecated private predicate mk_Deref(GVN p, ControlFlowNode dominator, PointerDereferenceExpr deref) {
|
||||
analyzablePointerDereferenceExpr(deref) and
|
||||
p = globalValueNumber(deref.getOperand().getFullyConverted()) and
|
||||
dominator = mostRecentSideEffect(deref)
|
||||
}
|
||||
|
||||
/** Gets the global value number of expression `e`. */
|
||||
cached
|
||||
deprecated GVN globalValueNumber(Expr e) {
|
||||
exists(int val, Type t |
|
||||
mk_IntConst(val, t, e) and
|
||||
result = GVN_IntConst(val, t)
|
||||
)
|
||||
or
|
||||
exists(float val, Type t |
|
||||
mk_FloatConst(val, t, e) and
|
||||
result = GVN_FloatConst(val, t)
|
||||
)
|
||||
or
|
||||
// Local variable with a defining value.
|
||||
exists(StackVariable x, SsaDefinition def |
|
||||
analyzableStackVariable(e) and
|
||||
e = def.getAUse(x) and
|
||||
result = globalValueNumber(def.getDefiningValue(x).getFullyConverted())
|
||||
)
|
||||
or
|
||||
// Local variable without a defining value.
|
||||
exists(StackVariable x, SsaDefinition def |
|
||||
mk_UndefinedStackVariable(x, def, e) and
|
||||
result = GVN_UndefinedStackVariable(x, def)
|
||||
)
|
||||
or
|
||||
// Variable with no SSA information.
|
||||
exists(Variable x, ControlFlowNode dominator |
|
||||
mk_OtherVariable(x, dominator, e) and
|
||||
result = GVN_OtherVariable(x, dominator)
|
||||
)
|
||||
or
|
||||
exists(GVN qualifier, Field target |
|
||||
mk_DotFieldAccess(qualifier, target, e) and
|
||||
result = GVN_FieldAccess(qualifier, target)
|
||||
)
|
||||
or
|
||||
exists(GVN qualifier, Field target |
|
||||
mk_PointerFieldAccess_with_deref(qualifier, target, e) and
|
||||
result = GVN_FieldAccess(qualifier, target)
|
||||
)
|
||||
or
|
||||
exists(GVN qualifier, Field target |
|
||||
mk_ImplicitThisFieldAccess_with_deref(qualifier, target, e) and
|
||||
result = GVN_FieldAccess(qualifier, target)
|
||||
)
|
||||
or
|
||||
exists(Function fcn |
|
||||
mk_ThisExpr(fcn, e) and
|
||||
result = GVN_ThisExpr(fcn)
|
||||
)
|
||||
or
|
||||
exists(Type t, GVN child |
|
||||
mk_Conversion(t, child, e) and
|
||||
result = GVN_Conversion(t, child)
|
||||
)
|
||||
or
|
||||
exists(GVN lhs, GVN rhs, string opname |
|
||||
mk_BinaryOp(lhs, rhs, opname, e) and
|
||||
result = GVN_BinaryOp(lhs, rhs, opname)
|
||||
)
|
||||
or
|
||||
exists(GVN child, string opname |
|
||||
mk_UnaryOp(child, opname, e) and
|
||||
result = GVN_UnaryOp(child, opname)
|
||||
)
|
||||
or
|
||||
exists(GVN x, GVN i, ControlFlowNode dominator |
|
||||
mk_ArrayAccess(x, i, dominator, e) and
|
||||
result = GVN_ArrayAccess(x, i, dominator)
|
||||
)
|
||||
or
|
||||
exists(GVN p, ControlFlowNode dominator |
|
||||
mk_Deref(p, dominator, e) and
|
||||
result = GVN_Deref(p, dominator)
|
||||
)
|
||||
or
|
||||
not analyzableExpr(e) and result = GVN_Unanalyzable(e)
|
||||
}
|
||||
|
||||
private predicate analyzableConst(Expr e) {
|
||||
analyzableIntConst(e) or
|
||||
analyzableFloatConst(e)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the expression is explicitly handled by `globalValueNumber`.
|
||||
* Unanalyzable expressions still need to be given a global value number,
|
||||
* but it will be a unique number that is not shared with any other
|
||||
* expression.
|
||||
*/
|
||||
private predicate analyzableExpr(Expr e) {
|
||||
analyzableConst(e) or
|
||||
analyzableStackVariable(e) or
|
||||
analyzableDotFieldAccess(e) or
|
||||
analyzablePointerFieldAccess(e) or
|
||||
analyzableImplicitThisFieldAccess(e) or
|
||||
analyzableOtherVariable(e) or
|
||||
analyzableConversion(e) or
|
||||
analyzableBinaryOp(e) or
|
||||
analyzableUnaryOp(e) or
|
||||
analyzableThisExpr(e) or
|
||||
analyzableArrayAccess(e) or
|
||||
analyzablePointerDereferenceExpr(e)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
description: Removed unused column from the `folders` and `files` relations
|
||||
compatibility: full
|
||||
files.rel: reorder files.rel (int id, string name, string simple, string ext, int fromSource) id name
|
||||
folders.rel: reorder folders.rel (int id, string name, string simple) id name
|
||||
files.rel: reorder files.rel (@file id, string name, string simple, string ext, int fromSource) id name
|
||||
folders.rel: reorder folders.rel (@folder id, string name, string simple) id name
|
||||
@@ -1,3 +1,17 @@
|
||||
## 0.9.12
|
||||
|
||||
### New Queries
|
||||
|
||||
* Added a new query, `cpp/iterator-to-expired-container`, to detect the creation of iterators owned by a temporary objects that are about to be destroyed.
|
||||
|
||||
## 0.9.11
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* The "Uncontrolled data used in path expression" query (`cpp/path-injection`) query produces fewer near-duplicate results.
|
||||
* The "Global variable may be used before initialization" query (`cpp/global-use-before-init`) no longer raises an alert on global variables that are initialized when they are declared.
|
||||
* The "Inconsistent null check of pointer" query (`cpp/inconsistent-nullness-testing`) query no longer raises an alert when the guarded check is in a macro expansion.
|
||||
|
||||
## 0.9.10
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
@@ -14,13 +14,32 @@ the program, or security vulnerabilities, by allowing an attacker to overwrite a
|
||||
</overview>
|
||||
<recommendation>
|
||||
<p>
|
||||
Ensure that all execution paths deallocate the allocated memory at most once. If possible, reassign
|
||||
the pointer to a null value after deallocating it. This will prevent double-free vulnerabilities since
|
||||
most deallocation functions will perform a null-pointer check before attempting to deallocate the memory.
|
||||
Ensure that all execution paths deallocate the allocated memory at most once. In complex cases it may
|
||||
help to reassign a pointer to a null value after deallocating it. This will prevent double-free vulnerabilities
|
||||
since most deallocation functions will perform a null-pointer check before attempting to deallocate memory.
|
||||
</p>
|
||||
|
||||
</recommendation>
|
||||
<example><sample src="DoubleFree.cpp" />
|
||||
<example>
|
||||
<p>
|
||||
In the following example, <code>buff</code> is allocated and then freed twice:
|
||||
</p>
|
||||
<sample src="DoubleFreeBad.cpp" />
|
||||
<p>
|
||||
Reviewing the code above, the issue can be fixed by simply deleting the additional call to
|
||||
<code>free(buff)</code>.
|
||||
</p>
|
||||
<sample src="DoubleFreeGood.cpp" />
|
||||
<p>
|
||||
In the next example, <code>task</code> may be deleted twice, if an exception occurs inside the <code>try</code>
|
||||
block after the first <code>delete</code>:
|
||||
</p>
|
||||
<sample src="DoubleFreeBad2.cpp" />
|
||||
<p>
|
||||
The problem can be solved by assigning a null value to the pointer after the first <code>delete</code>, as
|
||||
calling <code>delete</code> a second time on the null pointer is harmless.
|
||||
</p>
|
||||
<sample src="DoubleFreeGood2.cpp" />
|
||||
</example>
|
||||
<references>
|
||||
|
||||
|
||||
16
cpp/ql/src/Critical/DoubleFreeBad2.cpp
Normal file
16
cpp/ql/src/Critical/DoubleFreeBad2.cpp
Normal file
@@ -0,0 +1,16 @@
|
||||
void g() {
|
||||
MyTask *task = nullptr;
|
||||
|
||||
try
|
||||
{
|
||||
task = new MyTask;
|
||||
|
||||
...
|
||||
|
||||
delete task;
|
||||
|
||||
...
|
||||
} catch (...) {
|
||||
delete task; // BAD: potential double-free
|
||||
}
|
||||
}
|
||||
7
cpp/ql/src/Critical/DoubleFreeGood.cpp
Normal file
7
cpp/ql/src/Critical/DoubleFreeGood.cpp
Normal file
@@ -0,0 +1,7 @@
|
||||
int* f() {
|
||||
int *buff = malloc(SIZE*sizeof(int));
|
||||
do_stuff(buff);
|
||||
free(buff); // GOOD: buff is only freed once.
|
||||
int *new_buffer = malloc(SIZE*sizeof(int));
|
||||
return new_buffer;
|
||||
}
|
||||
17
cpp/ql/src/Critical/DoubleFreeGood2.cpp
Normal file
17
cpp/ql/src/Critical/DoubleFreeGood2.cpp
Normal file
@@ -0,0 +1,17 @@
|
||||
void g() {
|
||||
MyTask *task = nullptr;
|
||||
|
||||
try
|
||||
{
|
||||
task = new MyTask;
|
||||
|
||||
...
|
||||
|
||||
delete task;
|
||||
task = nullptr;
|
||||
|
||||
...
|
||||
} catch (...) {
|
||||
delete task; // GOOD: harmless if task is NULL
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,9 @@ predicate useFunc(GlobalVariable v, Function f) {
|
||||
}
|
||||
|
||||
predicate uninitialisedBefore(GlobalVariable v, Function f) {
|
||||
f.hasGlobalName("main")
|
||||
f.hasGlobalName("main") and
|
||||
not initialisedAtDeclaration(v) and
|
||||
not isStdlibVariable(v)
|
||||
or
|
||||
exists(Call call, Function g |
|
||||
uninitialisedBefore(v, g) and
|
||||
@@ -98,10 +100,15 @@ predicate callReaches(Call call, ControlFlowNode successor) {
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `v` has an initializer. */
|
||||
predicate initialisedAtDeclaration(GlobalVariable v) { exists(v.getInitializer()) }
|
||||
|
||||
/** Holds if `v` is a global variable that does not need to be initialized. */
|
||||
predicate isStdlibVariable(GlobalVariable v) { v.hasGlobalName(["stdin", "stdout", "stderr"]) }
|
||||
|
||||
from GlobalVariable v, Function f
|
||||
where
|
||||
uninitialisedBefore(v, f) and
|
||||
useFunc(v, f)
|
||||
select f,
|
||||
"The variable '" + v.getName() +
|
||||
" is used in this function but may not be initialized when it is called."
|
||||
select f, "The variable $@ is used in this function but may not be initialized when it is called.",
|
||||
v, v.getName()
|
||||
|
||||
@@ -15,6 +15,8 @@ import cpp
|
||||
from StackVariable v, ControlFlowNode def, VariableAccess checked, VariableAccess unchecked
|
||||
where
|
||||
checked = v.getAnAccess() and
|
||||
// The check can often be in a macro for handling exception
|
||||
not checked.isInMacroExpansion() and
|
||||
dereferenced(checked) and
|
||||
unchecked = v.getAnAccess() and
|
||||
dereferenced(unchecked) and
|
||||
|
||||
@@ -54,6 +54,7 @@ predicate undefinedLocalUse(VariableAccess va) {
|
||||
// it is hard to tell when a struct or array has been initialized, so we
|
||||
// ignore them
|
||||
not isAggregateType(lv.getUnderlyingType()) and
|
||||
not lv.isStatic() and // static variables are initialized to zero or null by default
|
||||
not lv.getType().hasName("va_list") and
|
||||
va = lv.getAnAccess() and
|
||||
noDefPath(lv, va) and
|
||||
@@ -70,7 +71,8 @@ predicate uninitialisedGlobal(GlobalVariable gv) {
|
||||
va = gv.getAnAccess() and
|
||||
va.isRValue() and
|
||||
not gv.hasInitializer() and
|
||||
not gv.hasSpecifier("extern")
|
||||
not gv.hasSpecifier("extern") and
|
||||
not gv.isStatic() // static variables are initialized to zero or null by default
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ in the previous example, one solution is to make the log message a trailing argu
|
||||
<p>An alternative solution is to allow <code>log_with_timestamp</code> to accept format arguments:</p>
|
||||
<sample src="NonConstantFormat-2-good.c" />
|
||||
<p>In this formulation, the non-constant format string to <code>printf</code> has been replaced with
|
||||
a non-constant format string to <code>vprintf</code>. Semmle will no longer consider the body of
|
||||
a non-constant format string to <code>vprintf</code>. The analysis will no longer consider the body of
|
||||
<code>log_with_timestamp</code> to be a problem, and will instead check that every call to
|
||||
<code>log_with_timestamp</code> passes a constant format string.</p>
|
||||
|
||||
|
||||
@@ -22,10 +22,8 @@ function.
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>cplusplus.com: <a href="http://www.tutorialspoint.com/cplusplus/cpp_functions.htm">C++ Functions</a>.</li>
|
||||
<li>CERT C Coding Standard: <a href="https://wiki.sei.cmu.edu/confluence/display/c/FIO47-C.+Use+valid+format+strings">FIO47-C. Use valid format strings</a>.</li>
|
||||
<li>Microsoft C Runtime Library Reference: <a href="https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/printf-printf-l-wprintf-wprintf-l">printf, wprintf</a>.</li>
|
||||
|
||||
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
|
||||
@@ -19,8 +19,8 @@ contents.
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
<p>Review the format and arguments expected by the highlighted function calls. Update either
|
||||
the format or the arguments so that the expected number of arguments are passed to the
|
||||
<p>Review the format and arguments expected by the highlighted function calls. Update either
|
||||
the format or the arguments so that the expected number of arguments are passed to the
|
||||
function.
|
||||
</p>
|
||||
|
||||
@@ -30,11 +30,8 @@ function.
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>CERT C Coding
|
||||
Standard: <a href="https://www.securecoding.cert.org/confluence/display/c/FIO30-C.+Exclude+user+input+from+format+strings">FIO30-C. Exclude user input from format strings</a>.</li>
|
||||
<li>cplusplus.com: <a href="http://www.tutorialspoint.com/cplusplus/cpp_functions.htm">C++ Functions</a>.</li>
|
||||
<li>CERT C Coding Standard: <a href="https://wiki.sei.cmu.edu/confluence/display/c/FIO47-C.+Use+valid+format+strings">FIO47-C. Use valid format strings</a>.</li>
|
||||
<li>Microsoft C Runtime Library Reference: <a href="https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/printf-printf-l-wprintf-wprintf-l">printf, wprintf</a>.</li>
|
||||
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
int main() {
|
||||
printf("%s\n", 42); //printf will treat 42 as a char*, will most likely segfault
|
||||
return 0;
|
||||
}
|
||||
@@ -4,29 +4,33 @@
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>Each call to the <code>printf</code> function or a related function should include
|
||||
the type and sequence of arguments defined by the format. If the function is passed arguments
|
||||
the type and sequence of arguments defined by the format. If the function is passed arguments
|
||||
of a different type or in a different sequence then the arguments are reinterpreted to fit the type and sequence expected, resulting in unpredictable behavior.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
<p>Review the format and arguments expected by the highlighted function calls. Update either
|
||||
the format or the arguments so that the expected type and sequence of arguments are passed to
|
||||
<p>Review the format and arguments expected by the highlighted function calls. Update either
|
||||
the format or the arguments so that the expected type and sequence of arguments are passed to
|
||||
the function.
|
||||
</p>
|
||||
|
||||
</recommendation>
|
||||
<example><sample src="WrongTypeFormatArguments.cpp" />
|
||||
<example>
|
||||
|
||||
<p>In the following example, the wrong format specifier is given for an integer format argument:</p>
|
||||
|
||||
<sample src="WrongTypeFormatArgumentsBad.cpp" />
|
||||
|
||||
<p>The corrected version uses <code>%i</code> as the format specifier for the integer format argument:</p>
|
||||
|
||||
<sample src="WrongTypeFormatArgumentsGood.cpp" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>CERT C Coding
|
||||
Standard: <a href="https://www.securecoding.cert.org/confluence/display/c/FIO30-C.+Exclude+user+input+from+format+strings">FIO30-C. Exclude user input from format strings</a>.</li>
|
||||
<li>cplusplus.com: <a href="http://www.tutorialspoint.com/cplusplus/cpp_functions.htm">C++ Functions</a>.</li>
|
||||
<li>CRT Alphabetical Function Reference: <a href="https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/printf-printf-l-wprintf-wprintf-l">printf, _printf_l, wprintf, _wprintf_l</a>.</li>
|
||||
|
||||
|
||||
|
||||
<li>Microsoft Learn: <a href="https://learn.microsoft.com/en-us/cpp/c-runtime-library/format-specification-syntax-printf-and-wprintf-functions?view=msvc-170">Format specification syntax: printf and wprintf functions</a>.</li>
|
||||
<li>cplusplus.com:<a href="https://cplusplus.com/reference/cstdio/printf/"></a>printf</li>
|
||||
<li>CERT C Coding Standard: <a href="https://wiki.sei.cmu.edu/confluence/display/c/FIO47-C.+Use+valid+format+strings">FIO47-C. Use valid format strings</a>.</li>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
int main() {
|
||||
printf("%s\n", 42); // BAD: printf will treat 42 as a char*, will most likely segfault
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
int main() {
|
||||
printf("%i\n", 42); // GOOD: printf will treat 42 as an int
|
||||
return 0;
|
||||
}
|
||||
@@ -2,19 +2,18 @@
|
||||
|
||||
void f_warning(int i)
|
||||
{
|
||||
// The usage of the logical not operator in this case is unlikely to be correct
|
||||
// BAD: the usage of the logical not operator in this case is unlikely to be correct
|
||||
// as the output is being used as an operator for a bit-wise and operation
|
||||
if (i & !FLAGS)
|
||||
if (i & !FLAGS)
|
||||
{
|
||||
// code
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void f_fixed(int i)
|
||||
{
|
||||
if (i & ~FLAGS) // Changing the logical not operator for the bit-wise not operator would fix this logic
|
||||
if (i & ~FLAGS) // GOOD: Changing the logical not operator for the bit-wise not operator would fix this logic
|
||||
{
|
||||
// code
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,13 @@
|
||||
<p>Carefully inspect the flagged expressions. Consider the intent in the code logic, and decide whether it is necessary to change the not operator.</p>
|
||||
</recommendation>
|
||||
|
||||
<example><sample src="IncorrectNotOperatorUsage.cpp" /></example>
|
||||
<example>
|
||||
<p>Here is an example of this issue and how it can be fixed:</p>
|
||||
|
||||
<sample src="IncorrectNotOperatorUsage.cpp" />
|
||||
|
||||
<p>In other cases, particularly when the expressions have <code>bool</code> type, the fix may instead be of the form <code>a && !b</code>.</p>
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
strncpy(dest, src, sizeof(src)); //wrong: size of dest should be used
|
||||
strncpy(dest, src, strlen(src)); //wrong: size of dest should be used
|
||||
@@ -3,7 +3,7 @@
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>The standard library function <code>strncpy</code> copies a source string to a destination buffer. The third argument defines the maximum number of characters to copy and should be less than
|
||||
<p>The standard library function <code>strncpy</code> copies a source string to a destination buffer. The third argument defines the maximum number of characters to copy and should be less than
|
||||
or equal to the size of the destination buffer. Calls of the form <code>strncpy(dest, src, strlen(src))</code> or <code>strncpy(dest, src, sizeof(src))</code> incorrectly set the third argument to the size of the source buffer. Executing a call of this type may cause a buffer overflow. Buffer overflows can lead to anything from a segmentation fault to a security vulnerability.</p>
|
||||
|
||||
</overview>
|
||||
@@ -12,14 +12,20 @@ or equal to the size of the destination buffer. Calls of the form <code>strncpy(
|
||||
not the source buffer.</p>
|
||||
|
||||
</recommendation>
|
||||
<example><sample src="StrncpyFlippedArgs.cpp" />
|
||||
|
||||
<example>
|
||||
<p>In the following examples, the size of the source buffer is incorrectly used as a parameter to <code>strncpy</code>:</p>
|
||||
|
||||
<sample src="StrncpyFlippedArgsBad.cpp" />
|
||||
|
||||
<p>The corrected version uses the size of the destination buffer, or a variable containing the size of the destination buffer as the size parameter to <code>strncpy</code>:</p>
|
||||
|
||||
<sample src="StrncpyFlippedArgsGood.cpp" />
|
||||
</example>
|
||||
|
||||
<references>
|
||||
|
||||
<li>cplusplus.com: <a href="http://www.cplusplus.com/reference/clibrary/cstring/strncpy/">strncpy</a>.</li>
|
||||
<li>cplusplus.com: <a href="https://cplusplus.com/reference/cstring/strncpy/">strncpy</a>.</li>
|
||||
<li>
|
||||
I. Gerg. <em>An Overview and Example of the Buffer-Overflow Exploit</em>. IANewsletter vol 7 no 4. 2005.
|
||||
</li>
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
char src[256];
|
||||
char dest1[128];
|
||||
|
||||
...
|
||||
|
||||
strncpy(dest1, src, sizeof(src)); // wrong: size of dest should be used
|
||||
|
||||
char *dest2 = (char *)malloc(sz1 + sz2 + sz3);
|
||||
strncpy(dest2, src, strlen(src)); // wrong: size of dest should be used
|
||||
@@ -0,0 +1,10 @@
|
||||
char src[256];
|
||||
char dest1[128];
|
||||
|
||||
...
|
||||
|
||||
strncpy(dest1, src, sizeof(dest1)); // correct
|
||||
|
||||
size_t destSize = sz1 + sz2 + sz3;
|
||||
char *dest2 = (char *)malloc(destSize);
|
||||
strncpy(dest2, src, destSize); // correct
|
||||
@@ -1,22 +0,0 @@
|
||||
int main(int argc, char** argv) {
|
||||
char *userAndFile = argv[2];
|
||||
|
||||
{
|
||||
char fileBuffer[FILENAME_MAX] = "/home/";
|
||||
char *fileName = fileBuffer;
|
||||
size_t len = strlen(fileName);
|
||||
strncat(fileName+len, userAndFile, FILENAME_MAX-len-1);
|
||||
// BAD: a string from the user is used in a filename
|
||||
fopen(fileName, "wb+");
|
||||
}
|
||||
|
||||
{
|
||||
char fileBuffer[FILENAME_MAX] = "/home/";
|
||||
char *fileName = fileBuffer;
|
||||
size_t len = strlen(fileName);
|
||||
// GOOD: use a fixed file
|
||||
char* fixed = "jim/file.txt";
|
||||
strncat(fileName+len, fixed, FILENAME_MAX-len-1);
|
||||
fopen(fileName, "wb+");
|
||||
}
|
||||
}
|
||||
@@ -3,36 +3,57 @@
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>Accessing paths controlled by users can allow an attacker to access unexpected resources. This
|
||||
<p>Accessing paths controlled by users can allow an attacker to access unexpected resources. This
|
||||
can result in sensitive information being revealed or deleted, or an attacker being able to influence
|
||||
behavior by modifying unexpected files.</p>
|
||||
|
||||
<p>Paths that are naively constructed from data controlled by a user may contain unexpected special characters,
|
||||
such as "..". Such a path may potentially point to any directory on the filesystem.</p>
|
||||
<p>Paths that are naively constructed from data controlled by a user may be absolute paths, or may contain
|
||||
unexpected special characters such as "..". Such a path could point anywhere on the file system.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>Validate user input before using it to construct a filepath. Ideally, follow these rules:</p>
|
||||
<p>Validate user input before using it to construct a file path.</p>
|
||||
|
||||
<ul>
|
||||
<li>Do not allow more than a single "." character.</li>
|
||||
<li>Do not allow directory separators such as "/" or "\" (depending on the filesystem).</li>
|
||||
<li>Do not rely on simply replacing problematic sequences such as "../". For example, after applying this filter to
|
||||
".../...//" the resulting string would still be "../".</li>
|
||||
<li>Ideally use a whitelist of known good patterns.</li>
|
||||
</ul>
|
||||
<p>Common validation methods include checking that the normalized path is relative and does not contain
|
||||
any ".." components, or checking that the path is contained within a safe folder. The method you should use depends
|
||||
on how the path is used in the application, and whether the path should be a single path component.
|
||||
</p>
|
||||
|
||||
<p>If the path should be a single path component (such as a file name), you can check for the existence
|
||||
of any path separators ("/" or "\"), or ".." sequences in the input, and reject the input if any are found.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Note that removing "../" sequences is <i>not</i> sufficient, since the input could still contain a path separator
|
||||
followed by "..". For example, the input ".../...//" would still result in the string "../" if only "../" sequences
|
||||
are removed.
|
||||
</p>
|
||||
|
||||
<p>Finally, the simplest (but most restrictive) option is to use an allow list of safe patterns and make sure that
|
||||
the user input matches one of these patterns.</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
|
||||
<p>In this example, a username and file are read from the arguments to main and then used to access a file in the
|
||||
user's home directory. However, a malicious user could enter a filename which contains special
|
||||
characters. For example, the string "../../etc/passwd" will result in the code reading the file located at
|
||||
"/home/[user]/../../etc/passwd", which is the system's password file. This could potentially allow them to
|
||||
access all the system's passwords.</p>
|
||||
<p>In this example, a file name is read from a user and then used to access a file.
|
||||
However, a malicious user could enter a file name anywhere on the file system,
|
||||
such as "/etc/passwd" or "../../../etc/passwd".</p>
|
||||
|
||||
<sample src="TaintedPath.c" />
|
||||
<sample src="examples/TaintedPath.c" />
|
||||
|
||||
<p>
|
||||
If the input should only be a file name, you can check that it doesn't contain any path separators or ".." sequences.
|
||||
</p>
|
||||
|
||||
<sample src="examples/TaintedPathNormalize.c" />
|
||||
|
||||
<p>
|
||||
If the input should be within a specific directory, you can check that the resolved path
|
||||
is still contained within that directory.
|
||||
</p>
|
||||
|
||||
<sample src="examples/TaintedPathFolder.c" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
@@ -41,6 +62,7 @@ access all the system's passwords.</p>
|
||||
OWASP:
|
||||
<a href="https://owasp.org/www-community/attacks/Path_Traversal">Path Traversal</a>.
|
||||
</li>
|
||||
<li>Linux man pages: <a href="https://man7.org/linux/man-pages/man3/realpath.3.html">realpath(3)</a>.</li>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
|
||||
@@ -88,6 +88,11 @@ module TaintedPathConfig implements DataFlow::ConfigSig {
|
||||
hasUpperBoundsCheck(checkedVar)
|
||||
)
|
||||
}
|
||||
|
||||
predicate isBarrierOut(DataFlow::Node node) {
|
||||
// make sinks barriers so that we only report the closest instance
|
||||
isSink(node)
|
||||
}
|
||||
}
|
||||
|
||||
module TaintedPath = TaintTracking::Global<TaintedPathConfig>;
|
||||
|
||||
10
cpp/ql/src/Security/CWE/CWE-022/examples/TaintedPath.c
Normal file
10
cpp/ql/src/Security/CWE/CWE-022/examples/TaintedPath.c
Normal file
@@ -0,0 +1,10 @@
|
||||
int main(int argc, char** argv) {
|
||||
char *userAndFile = argv[2];
|
||||
|
||||
{
|
||||
char fileBuffer[PATH_MAX];
|
||||
snprintf(fileBuffer, sizeof(fileBuffer), "/home/%s", userAndFile);
|
||||
// BAD: a string from the user is used in a filename
|
||||
fopen(fileBuffer, "wb+");
|
||||
}
|
||||
}
|
||||
28
cpp/ql/src/Security/CWE/CWE-022/examples/TaintedPathFolder.c
Normal file
28
cpp/ql/src/Security/CWE/CWE-022/examples/TaintedPathFolder.c
Normal file
@@ -0,0 +1,28 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
char *userAndFile = argv[2];
|
||||
const char *baseDir = "/home/user/public/";
|
||||
char fullPath[PATH_MAX];
|
||||
|
||||
// Attempt to concatenate the base directory and the user-supplied path
|
||||
snprintf(fullPath, sizeof(fullPath), "%s%s", baseDir, userAndFile);
|
||||
|
||||
// Resolve the absolute path, normalizing any ".." or "."
|
||||
char *resolvedPath = realpath(fullPath, NULL);
|
||||
if (resolvedPath == NULL) {
|
||||
perror("Error resolving path");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Check if the resolved path starts with the base directory
|
||||
if (strncmp(baseDir, resolvedPath, strlen(baseDir)) != 0) {
|
||||
free(resolvedPath);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// GOOD: Path is within the intended directory
|
||||
FILE *file = fopen(resolvedPath, "wb+");
|
||||
free(resolvedPath);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
char *fileName = argv[2];
|
||||
// Check for invalid sequences in the user input
|
||||
if (strstr(fileName , "..") || strchr(fileName , '/') || strchr(fileName , '\\')) {
|
||||
printf("Invalid filename.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
char fileBuffer[PATH_MAX];
|
||||
snprintf(fileBuffer, sizeof(fileBuffer), "/home/user/files/%s", fileName);
|
||||
// GOOD: We know that the filename is safe and stays within the public folder
|
||||
FILE *file = fopen(fileBuffer, "wb+");
|
||||
}
|
||||
@@ -30,6 +30,12 @@ This is because the temporary container is not bound to a rvalue reference.
|
||||
</p>
|
||||
<sample src="IteratorToExpiredContainerExtendedLifetime.cpp" />
|
||||
|
||||
<p>
|
||||
To fix <code>lifetime_of_temp_not_extended</code>, consider rewriting the code so that the lifetime of the temporary object is extended.
|
||||
In <code>fixed_lifetime_of_temp_not_extended</code>, the lifetime of the temporary object has been extended by storing it in an rvalue reference.
|
||||
</p>
|
||||
<sample src="IteratorToExpiredContainerExtendedLifetime-fixed.cpp" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
157
cpp/ql/src/Security/CWE/CWE-416/IteratorToExpiredContainer.ql
Normal file
157
cpp/ql/src/Security/CWE/CWE-416/IteratorToExpiredContainer.ql
Normal file
@@ -0,0 +1,157 @@
|
||||
/**
|
||||
* @name Iterator to expired container
|
||||
* @description Using an iterator owned by a container whose lifetime has expired may lead to unexpected behavior.
|
||||
* @kind problem
|
||||
* @precision medium
|
||||
* @id cpp/iterator-to-expired-container
|
||||
* @problem.severity warning
|
||||
* @security-severity 8.8
|
||||
* @tags reliability
|
||||
* security
|
||||
* external/cwe/cwe-416
|
||||
* external/cwe/cwe-664
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.ir.IR
|
||||
import semmle.code.cpp.dataflow.new.DataFlow
|
||||
import semmle.code.cpp.models.implementations.StdContainer
|
||||
import semmle.code.cpp.models.implementations.StdMap
|
||||
import semmle.code.cpp.models.implementations.Iterator
|
||||
|
||||
private predicate tempToDestructorSink(DataFlow::Node sink, CallInstruction call) {
|
||||
call = sink.asOperand().(ThisArgumentOperand).getCall() and
|
||||
call.getStaticCallTarget() instanceof Destructor
|
||||
}
|
||||
|
||||
/** Holds if `pun` is the post-update node of the qualifier of `Call`. */
|
||||
private predicate isPostUpdateOfQualifier(CallInstruction call, DataFlow::PostUpdateNode pun) {
|
||||
call.getThisArgumentOperand() = pun.getPreUpdateNode().asOperand()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a `DataFlow::Node` that represents a temporary that will be destroyed
|
||||
* by a call to a destructor when `n` is destroyed, or a `DataFlow::Node` that
|
||||
* will transitively be destroyed by a call to a destructor.
|
||||
*
|
||||
* For the latter case, consider something like:
|
||||
* ```
|
||||
* std::vector<std::vector<int>> get_2d_vector();
|
||||
* auto& v = get_2d_vector()[0];
|
||||
* ```
|
||||
* Given the above, this predicate returns the node corresponding
|
||||
* to `get_2d_vector()[0]` since the temporary `get_2d_vector()` gets
|
||||
* destroyed by a call to `std::vector<std::vector<int>>::~vector`,
|
||||
* and thus the result of `get_2d_vector()[0]` is also an invalid reference.
|
||||
*/
|
||||
DataFlow::Node getADestroyedNode(DataFlow::Node n) {
|
||||
// Case 1: The pointer that goes into the destructor call is destroyed
|
||||
exists(CallInstruction destructorCall |
|
||||
tempToDestructorSink(n, destructorCall) and
|
||||
isPostUpdateOfQualifier(destructorCall, result)
|
||||
)
|
||||
or
|
||||
// Case 2: Anything that was derived from the temporary that is now destroyed
|
||||
// is also destroyed.
|
||||
exists(CallInstruction call |
|
||||
result.asInstruction() = call and
|
||||
DataFlow::localFlow(DataFlow::operandNode(call.getThisArgumentOperand()), n)
|
||||
|
|
||||
call.getStaticCallTarget() instanceof StdSequenceContainerAt or
|
||||
call.getStaticCallTarget() instanceof StdMapAt
|
||||
)
|
||||
}
|
||||
|
||||
predicate destroyedToBeginSink(DataFlow::Node sink) {
|
||||
exists(CallInstruction call |
|
||||
call = sink.asOperand().(ThisArgumentOperand).getCall() and
|
||||
call.getStaticCallTarget() instanceof BeginOrEndFunction
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node1` is the node corresponding to a qualifier of a destructor
|
||||
* call and `node2` is a node that is destroyed as a result of `node1` being
|
||||
* destroyed.
|
||||
*/
|
||||
private predicate qualifierToDestroyed(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
tempToDestructorSink(node1, _) and
|
||||
node2 = getADestroyedNode(node1)
|
||||
}
|
||||
|
||||
/**
|
||||
* A configuration to track flow from a destroyed node to a qualifier of
|
||||
* a `begin` or `end` function call.
|
||||
*
|
||||
* This configuration exists to prevent a cartesian product between all sinks and
|
||||
* all states in `Config::isSink`.
|
||||
*/
|
||||
module Config0 implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { qualifierToDestroyed(_, source) }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { destroyedToBeginSink(sink) }
|
||||
}
|
||||
|
||||
module Flow0 = DataFlow::Global<Config0>;
|
||||
|
||||
/**
|
||||
* A configuration to track flow from a temporary variable to the qualifier of
|
||||
* a destructor call, and subsequently to a qualifier of a call to `begin` or
|
||||
* `end`.
|
||||
*/
|
||||
module Config implements DataFlow::StateConfigSig {
|
||||
newtype FlowState =
|
||||
additional TempToDestructor() or
|
||||
additional DestroyedToBegin(DataFlow::Node n) {
|
||||
any(Flow0::PathNode pn | pn.isSource()).getNode() = n
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a qualifier to a call to `begin`, and `mid` is an
|
||||
* object that is destroyed.
|
||||
*/
|
||||
private predicate relevant(DataFlow::Node mid, DataFlow::Node sink) { Flow0::flow(mid, sink) }
|
||||
|
||||
predicate isSource(DataFlow::Node source, FlowState state) {
|
||||
source.asInstruction().(VariableAddressInstruction).getIRVariable() instanceof IRTempVariable and
|
||||
state = TempToDestructor()
|
||||
}
|
||||
|
||||
predicate isAdditionalFlowStep(
|
||||
DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2
|
||||
) {
|
||||
state1 = TempToDestructor() and
|
||||
state2 = DestroyedToBegin(node2) and
|
||||
qualifierToDestroyed(node1, node2)
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink, FlowState state) {
|
||||
exists(DataFlow::Node mid |
|
||||
relevant(mid, sink) and
|
||||
state = DestroyedToBegin(mid)
|
||||
)
|
||||
}
|
||||
|
||||
DataFlow::FlowFeature getAFeature() {
|
||||
// By blocking argument-to-parameter flow we ensure that we don't enter a
|
||||
// function body where the temporary outlives anything inside the function.
|
||||
// This prevents false positives in cases like:
|
||||
// ```cpp
|
||||
// void foo(const std::vector<int>& v) {
|
||||
// for(auto x : v) { ... } // this is fine since v outlives the loop
|
||||
// }
|
||||
// ...
|
||||
// foo(create_temporary())
|
||||
// ```
|
||||
result instanceof DataFlow::FeatureHasSinkCallContext
|
||||
}
|
||||
}
|
||||
|
||||
module Flow = DataFlow::GlobalWithState<Config>;
|
||||
|
||||
from Flow::PathNode source, Flow::PathNode sink, DataFlow::Node mid
|
||||
where
|
||||
Flow::flowPath(source, sink) and
|
||||
destroyedToBeginSink(sink.getNode()) and
|
||||
sink.getState() = Config::DestroyedToBegin(mid)
|
||||
select mid, "This object is destroyed at the end of the full-expression."
|
||||
@@ -0,0 +1,6 @@
|
||||
void fixed_lifetime_of_temp_not_extended() {
|
||||
auto&& v = get_vector();
|
||||
for(auto x : log_and_return_argument(v)) {
|
||||
use(x); // GOOD: The lifetime of the container returned by `get_vector()` has been extended to the lifetime of `v`.
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user