mirror of
https://github.com/github/codeql.git
synced 2026-05-26 09:01:22 +02:00
Compare commits
891 Commits
mbg/fix/lo
...
mbg/csharp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2525ac3dd2 | ||
|
|
93a45fce5e | ||
|
|
49039246e1 | ||
|
|
f22c86442e | ||
|
|
fea29d5172 | ||
|
|
e3762c7f93 | ||
|
|
e60676fbde | ||
|
|
e2af8f1b43 | ||
|
|
31673431af | ||
|
|
3ef3441883 | ||
|
|
354f716ca8 | ||
|
|
430af661cd | ||
|
|
b203533fc6 | ||
|
|
7de26550ad | ||
|
|
bdbcaab0ba | ||
|
|
1e2329d0dd | ||
|
|
3bf6b6f96f | ||
|
|
b034b2f2a3 | ||
|
|
93b7a2bc92 | ||
|
|
c3e25d2549 | ||
|
|
08b51c3b06 | ||
|
|
0f320996cf | ||
|
|
8e83fd00b7 | ||
|
|
aa6efce695 | ||
|
|
5537d79d90 | ||
|
|
7e4808440e | ||
|
|
c55281a68e | ||
|
|
eda33fc5cb | ||
|
|
f68c529f04 | ||
|
|
61ff4c7896 | ||
|
|
9865c506e6 | ||
|
|
99924919be | ||
|
|
c5a2cfcf95 | ||
|
|
219b232c83 | ||
|
|
7b5e19d462 | ||
|
|
5963501368 | ||
|
|
f4c4871ab3 | ||
|
|
4e7c39a5d1 | ||
|
|
fce9cb0b28 | ||
|
|
fb6c27ba10 | ||
|
|
35b60167e1 | ||
|
|
7cfe15c304 | ||
|
|
430b432add | ||
|
|
eab3c6dd5e | ||
|
|
e28be5d98f | ||
|
|
75c75ea49c | ||
|
|
3ec2a3c711 | ||
|
|
3b1b3b46ae | ||
|
|
29c8260004 | ||
|
|
87b54e674e | ||
|
|
9db1366e4b | ||
|
|
f1adb4319a | ||
|
|
e2d7a6910c | ||
|
|
180246b99c | ||
|
|
f3124d3239 | ||
|
|
2f1bd93a49 | ||
|
|
2f576a4fe9 | ||
|
|
45c1537f06 | ||
|
|
ee5382d8a6 | ||
|
|
3ce7fafb67 | ||
|
|
14655e1d8c | ||
|
|
261a1348f0 | ||
|
|
c65fd69374 | ||
|
|
233bd8ce8c | ||
|
|
7e7850374e | ||
|
|
d9e5c6c48a | ||
|
|
7d2b78b463 | ||
|
|
95a131d0d3 | ||
|
|
8ee36a5278 | ||
|
|
c72dbc49fc | ||
|
|
7e16fa9cbe | ||
|
|
220f227707 | ||
|
|
66c3529465 | ||
|
|
df6039d6cf | ||
|
|
e1ae3c3cfb | ||
|
|
368ca6cb30 | ||
|
|
2cd1e09a7e | ||
|
|
5f0b1973ee | ||
|
|
4be2e431ea | ||
|
|
b52df0de0c | ||
|
|
fb14920281 | ||
|
|
5f07d1f385 | ||
|
|
194316d1c0 | ||
|
|
d075e016b2 | ||
|
|
9e584eb241 | ||
|
|
62d10f91d8 | ||
|
|
d5f7ef08b7 | ||
|
|
7f76d8ae55 | ||
|
|
82a2f4349a | ||
|
|
f48d87ba55 | ||
|
|
3514dd1e4d | ||
|
|
029e1d47fe | ||
|
|
e40bb6ac87 | ||
|
|
1b30043422 | ||
|
|
4f7c598ffc | ||
|
|
2f8c9a5a2c | ||
|
|
e3e2df3247 | ||
|
|
39516862c1 | ||
|
|
028fcc7edf | ||
|
|
a498936f16 | ||
|
|
bca3fa94fd | ||
|
|
f68083872d | ||
|
|
81de500301 | ||
|
|
0600a2ba96 | ||
|
|
935e22d10d | ||
|
|
8e079320f3 | ||
|
|
781aab3eb7 | ||
|
|
b0c8992eef | ||
|
|
cfe169a4f9 | ||
|
|
4140598769 | ||
|
|
c17d057520 | ||
|
|
0b722bfe30 | ||
|
|
68656274f4 | ||
|
|
e4c8387815 | ||
|
|
1c57aa0456 | ||
|
|
8372ad9d84 | ||
|
|
2113c3c3d9 | ||
|
|
29ce9bfe24 | ||
|
|
97f79602a9 | ||
|
|
6192544fb4 | ||
|
|
1c3d4b98c8 | ||
|
|
191613e8bf | ||
|
|
4fa484dad2 | ||
|
|
39e50f745d | ||
|
|
c66a44f2e2 | ||
|
|
e877b161d8 | ||
|
|
7ebe472cfe | ||
|
|
37d3793e87 | ||
|
|
1f1e2dbf98 | ||
|
|
3ee3acd8fd | ||
|
|
e635140eae | ||
|
|
f2c541a461 | ||
|
|
f062a8d204 | ||
|
|
947a9f12a1 | ||
|
|
11204987f1 | ||
|
|
34c6b24882 | ||
|
|
457a2bb2a2 | ||
|
|
ac54caac35 | ||
|
|
426425a7ca | ||
|
|
0537579b28 | ||
|
|
2f404df17c | ||
|
|
b85bfc8ba6 | ||
|
|
c258e44772 | ||
|
|
26d5fb2412 | ||
|
|
74472d786c | ||
|
|
634087b417 | ||
|
|
2ce6d5f920 | ||
|
|
5235964b07 | ||
|
|
b2e79e2948 | ||
|
|
f07c598a22 | ||
|
|
80628596dd | ||
|
|
124e4ddd4f | ||
|
|
0b8173e2e7 | ||
|
|
55aacd6fe9 | ||
|
|
483a87abe9 | ||
|
|
4fad01a739 | ||
|
|
f3e0b6e62c | ||
|
|
422eb0d1bb | ||
|
|
5555b5cd19 | ||
|
|
542c9218de | ||
|
|
f7a5a33474 | ||
|
|
91393a7bc8 | ||
|
|
6474cfd4c8 | ||
|
|
ad8849c6b8 | ||
|
|
676e4e8461 | ||
|
|
ce1c814daa | ||
|
|
c87c3e30c7 | ||
|
|
685b8b4abd | ||
|
|
7042f67375 | ||
|
|
778826b528 | ||
|
|
202c0be303 | ||
|
|
679f02c274 | ||
|
|
8624098318 | ||
|
|
d55e9d5dac | ||
|
|
eda331ebc2 | ||
|
|
11f022c69f | ||
|
|
15b8aa1979 | ||
|
|
6cb9198ee2 | ||
|
|
9d433eb420 | ||
|
|
db41463d72 | ||
|
|
38f4f65fc0 | ||
|
|
1bd223b8c8 | ||
|
|
fc7c72db77 | ||
|
|
adf47b9a9c | ||
|
|
c92fd97783 | ||
|
|
224bc9381a | ||
|
|
8d8ebeade1 | ||
|
|
7fa85b34ea | ||
|
|
770f3c24bb | ||
|
|
968f588893 | ||
|
|
1e279125dc | ||
|
|
0f469ee0f7 | ||
|
|
07947e6528 | ||
|
|
80a397b4a5 | ||
|
|
d4985a99e0 | ||
|
|
59ab353827 | ||
|
|
54b45134ef | ||
|
|
c00b089aa8 | ||
|
|
955f23d021 | ||
|
|
dd6ceb7053 | ||
|
|
7e174dce8b | ||
|
|
89bebe9d36 | ||
|
|
ad4ae1c331 | ||
|
|
70a6ff84af | ||
|
|
0f6b05dedf | ||
|
|
b895065be9 | ||
|
|
3e2bf23bfe | ||
|
|
bd98ae0dcc | ||
|
|
f2904ca29b | ||
|
|
5e2f9e1568 | ||
|
|
f113eaa77d | ||
|
|
46d49cd66f | ||
|
|
f6a02310d3 | ||
|
|
18335854b6 | ||
|
|
3ebac65167 | ||
|
|
13c7c8449c | ||
|
|
78ad9d67b4 | ||
|
|
844e372651 | ||
|
|
069c9674d1 | ||
|
|
6255298876 | ||
|
|
99bed0b089 | ||
|
|
02364d072e | ||
|
|
764155ce97 | ||
|
|
3abf321071 | ||
|
|
931c683146 | ||
|
|
eb564760be | ||
|
|
7f09684577 | ||
|
|
f8dbbe006e | ||
|
|
c427f8fc95 | ||
|
|
6e9484970f | ||
|
|
e972cb069e | ||
|
|
4f76ebbb0b | ||
|
|
30b2644f17 | ||
|
|
99d3f689dc | ||
|
|
15b293c56d | ||
|
|
60e7d6b540 | ||
|
|
7cb665cde1 | ||
|
|
d17c7bb0e8 | ||
|
|
3586926157 | ||
|
|
080ce09bd7 | ||
|
|
8dea993f41 | ||
|
|
49a3dd6131 | ||
|
|
334c41c3e1 | ||
|
|
b69d8625e5 | ||
|
|
f9f08fff83 | ||
|
|
8e8897b08b | ||
|
|
10534b62c9 | ||
|
|
80a503b65d | ||
|
|
1980e25ac9 | ||
|
|
3448cde048 | ||
|
|
712c25e6f4 | ||
|
|
522a892d32 | ||
|
|
746f04bafc | ||
|
|
984729f9b0 | ||
|
|
ff29356ae9 | ||
|
|
c9397a5abf | ||
|
|
d8616e77d5 | ||
|
|
aef66c462a | ||
|
|
a3b443c310 | ||
|
|
ad04a408c7 | ||
|
|
50889b8fa1 | ||
|
|
c0e3186607 | ||
|
|
d201c1eadd | ||
|
|
a244b825df | ||
|
|
3c580896dc | ||
|
|
43ce26e4d0 | ||
|
|
a4c3ea2efc | ||
|
|
1c35109675 | ||
|
|
868f07bc91 | ||
|
|
996536b08e | ||
|
|
4016299aa8 | ||
|
|
12f5732782 | ||
|
|
4eebeab8a8 | ||
|
|
1c9a526afa | ||
|
|
9364a85e49 | ||
|
|
9360ae9638 | ||
|
|
b4607d3fab | ||
|
|
dc59ed08f9 | ||
|
|
cd2fc6566f | ||
|
|
00fe448e3a | ||
|
|
f32fa25c1a | ||
|
|
e382d6d000 | ||
|
|
ec46f33a01 | ||
|
|
f391948b53 | ||
|
|
4af0c4bb03 | ||
|
|
3c15fd266d | ||
|
|
edbba85b96 | ||
|
|
e390ca50b0 | ||
|
|
90c51ef404 | ||
|
|
c554a10e06 | ||
|
|
f7a2a8677a | ||
|
|
6c0d2bdee1 | ||
|
|
25f907867b | ||
|
|
abe38373da | ||
|
|
c252ec0414 | ||
|
|
e05bce9863 | ||
|
|
314ecab90a | ||
|
|
85a339030b | ||
|
|
199c8641ec | ||
|
|
f648b021a9 | ||
|
|
bd6c167be6 | ||
|
|
f866e16679 | ||
|
|
5ab6056b26 | ||
|
|
3f446bc76e | ||
|
|
07041bb659 | ||
|
|
416ed57583 | ||
|
|
f321adf9f4 | ||
|
|
10109b4925 | ||
|
|
49ce91fd5b | ||
|
|
7e9617f3ce | ||
|
|
c03fe70b8d | ||
|
|
9ed7836367 | ||
|
|
33e8414fc4 | ||
|
|
03385ac0b5 | ||
|
|
8cefde36bf | ||
|
|
f8b574c654 | ||
|
|
02b09ca9f7 | ||
|
|
cfb3bc9dce | ||
|
|
0711326619 | ||
|
|
dbbef0534b | ||
|
|
b5d98d9011 | ||
|
|
6c816d5602 | ||
|
|
58d7af4018 | ||
|
|
7642245747 | ||
|
|
2d7e71dfce | ||
|
|
57f40ccd48 | ||
|
|
23c7bc8143 | ||
|
|
b6805c6913 | ||
|
|
8edd378290 | ||
|
|
364c173fc3 | ||
|
|
1f69fff26c | ||
|
|
f82a548cf9 | ||
|
|
c666f9c845 | ||
|
|
ec72c7504c | ||
|
|
e357b44943 | ||
|
|
4bb0bbf488 | ||
|
|
e230951a14 | ||
|
|
75b0676c95 | ||
|
|
41f7c7ae53 | ||
|
|
04d5b7e579 | ||
|
|
4b198f9af8 | ||
|
|
844193d065 | ||
|
|
1d0e80c2f5 | ||
|
|
3002230af9 | ||
|
|
1ec2c2591b | ||
|
|
2f637e2c8e | ||
|
|
c517eb89b2 | ||
|
|
73112e401c | ||
|
|
a354e776bf | ||
|
|
20dc30d7e8 | ||
|
|
a639f13fd9 | ||
|
|
6b3d458865 | ||
|
|
c7637a7e1f | ||
|
|
2d6d8aaa74 | ||
|
|
a1aeb995e6 | ||
|
|
e8dbd65d77 | ||
|
|
a0cf8e786c | ||
|
|
7140b956e8 | ||
|
|
d99f7b56bd | ||
|
|
c33c5ed517 | ||
|
|
f90c5346bf | ||
|
|
38eeb9c747 | ||
|
|
1077dcd2e3 | ||
|
|
b8bd98e476 | ||
|
|
142ca0c9fb | ||
|
|
d888510688 | ||
|
|
d25de8c764 | ||
|
|
e552a6206d | ||
|
|
21abe54d8d | ||
|
|
09fdf744d4 | ||
|
|
bf6ef43451 | ||
|
|
3545bb0819 | ||
|
|
4e7ca1a175 | ||
|
|
808d3e3a1f | ||
|
|
50bd0707ce | ||
|
|
56e0b19df8 | ||
|
|
8cb022713e | ||
|
|
9a4cec7691 | ||
|
|
13242df149 | ||
|
|
faf21f3edb | ||
|
|
6e712b293a | ||
|
|
30b1a2edbc | ||
|
|
61a8f5e425 | ||
|
|
34266cfa4c | ||
|
|
fd977cc277 | ||
|
|
465a3e0ece | ||
|
|
9943d0c054 | ||
|
|
9b17ee9326 | ||
|
|
0ab982891e | ||
|
|
2772cf79f4 | ||
|
|
52959d7c0a | ||
|
|
c8cfb6a0cc | ||
|
|
55cd436b06 | ||
|
|
1542353af7 | ||
|
|
2576a09af9 | ||
|
|
a3a01ddc11 | ||
|
|
cd660e1067 | ||
|
|
ab184ebd78 | ||
|
|
21b4931fbb | ||
|
|
231110ddca | ||
|
|
65863f1fc7 | ||
|
|
1472d4b782 | ||
|
|
67d4ed53b9 | ||
|
|
8cb233df1a | ||
|
|
3cf5107b45 | ||
|
|
a4fa984792 | ||
|
|
77283be6c0 | ||
|
|
960f776e29 | ||
|
|
4282e1a18e | ||
|
|
2b719d503d | ||
|
|
ae10a6beb0 | ||
|
|
3cd2024a66 | ||
|
|
ad5dbe18a4 | ||
|
|
50e5b29eb7 | ||
|
|
ce2e7f1d12 | ||
|
|
d230431006 | ||
|
|
6895c113cf | ||
|
|
87f7bc3a63 | ||
|
|
470e4b64ea | ||
|
|
62158c5e49 | ||
|
|
4089845ea4 | ||
|
|
787fe38d90 | ||
|
|
7271d9987e | ||
|
|
e137993acd | ||
|
|
dc5eb40d5f | ||
|
|
d671cc6e43 | ||
|
|
255f989ede | ||
|
|
d6f1dfa205 | ||
|
|
c5deb8544b | ||
|
|
24527bfc4e | ||
|
|
644bc56568 | ||
|
|
407e7cbbde | ||
|
|
97b2e852c9 | ||
|
|
1df0be3ca2 | ||
|
|
834fc51a3a | ||
|
|
43b234eeb5 | ||
|
|
f7cc5f9627 | ||
|
|
4d6b35f891 | ||
|
|
70a151af02 | ||
|
|
96ee0f68b0 | ||
|
|
a3188f2e10 | ||
|
|
3502ab6523 | ||
|
|
afa6b1cec5 | ||
|
|
74dba953ca | ||
|
|
bc36a75bde | ||
|
|
77e014c5a4 | ||
|
|
01f6862965 | ||
|
|
a46061541b | ||
|
|
fa81d9da18 | ||
|
|
837cdf7782 | ||
|
|
b9d487ac35 | ||
|
|
16049d694b | ||
|
|
373148decd | ||
|
|
da45d3aa7f | ||
|
|
0d68d88741 | ||
|
|
7f58a2222a | ||
|
|
6b0b73b5f6 | ||
|
|
a2248e6ca6 | ||
|
|
0d38ff8e8c | ||
|
|
8bc9ce749f | ||
|
|
57012714d6 | ||
|
|
56a0b1d2d8 | ||
|
|
597c71011e | ||
|
|
683761098d | ||
|
|
fcc4c91739 | ||
|
|
7f22c4c474 | ||
|
|
8410e46067 | ||
|
|
481dab700c | ||
|
|
478474bbed | ||
|
|
7ae389bb28 | ||
|
|
ecadb56419 | ||
|
|
5657bd0547 | ||
|
|
177fcbb4eb | ||
|
|
0ed48616a7 | ||
|
|
9808482c41 | ||
|
|
3510f465b2 | ||
|
|
f3555b1076 | ||
|
|
5ccfc4d3f4 | ||
|
|
fd74c10b06 | ||
|
|
14888d4382 | ||
|
|
89de6cb8a0 | ||
|
|
33a923a85b | ||
|
|
3a4e5700e8 | ||
|
|
86e9bf2f81 | ||
|
|
423bab54d3 | ||
|
|
38bcb2b727 | ||
|
|
198b97ca8d | ||
|
|
f2526d1784 | ||
|
|
1a109cab4d | ||
|
|
807b715320 | ||
|
|
34ca12e5d2 | ||
|
|
94cec17505 | ||
|
|
4436ec070e | ||
|
|
ee442e4d4b | ||
|
|
0cefa98490 | ||
|
|
daf7d1b7e7 | ||
|
|
7583fe2ad8 | ||
|
|
e9a46c926d | ||
|
|
95c19698c7 | ||
|
|
e5e8496084 | ||
|
|
d944c2bd79 | ||
|
|
2f38d363ff | ||
|
|
fb31570af3 | ||
|
|
1758e25207 | ||
|
|
051d36ee6a | ||
|
|
5710289460 | ||
|
|
c99a096c9b | ||
|
|
69ed00cdf1 | ||
|
|
cd596403a0 | ||
|
|
396d2de6e7 | ||
|
|
f23d517236 | ||
|
|
a01a4734ed | ||
|
|
3a1a9a771c | ||
|
|
f90007ae71 | ||
|
|
31743afa87 | ||
|
|
e01002368f | ||
|
|
7f6efae7dc | ||
|
|
89d835b9ec | ||
|
|
fb88372c0f | ||
|
|
48434f4be3 | ||
|
|
02da718786 | ||
|
|
b71c5e6c4b | ||
|
|
862948f1cc | ||
|
|
25e703e562 | ||
|
|
115a0a4318 | ||
|
|
b25414fe93 | ||
|
|
ff546c1497 | ||
|
|
2be090bb91 | ||
|
|
db047c2c4a | ||
|
|
812bc20812 | ||
|
|
23acd5c255 | ||
|
|
3a4ec90ae9 | ||
|
|
e46960e0cf | ||
|
|
e3afb1640a | ||
|
|
8198bbf893 | ||
|
|
9346f4d760 | ||
|
|
f430e83fca | ||
|
|
f647910e0c | ||
|
|
78683e4e8a | ||
|
|
40a576b775 | ||
|
|
f04a9cb523 | ||
|
|
3508a4b799 | ||
|
|
907ebb723e | ||
|
|
54c4c23b46 | ||
|
|
6a8c570915 | ||
|
|
f7cdd430a2 | ||
|
|
7778524e08 | ||
|
|
5e9210fcea | ||
|
|
708e303c01 | ||
|
|
28716866d8 | ||
|
|
246ad46eb1 | ||
|
|
a164e76a5d | ||
|
|
28c3bd3e2f | ||
|
|
fb86ef4aac | ||
|
|
e3455a9b21 | ||
|
|
d5f11dfe60 | ||
|
|
1396d07662 | ||
|
|
9a5614e8c5 | ||
|
|
f96c18a6db | ||
|
|
95b15825f9 | ||
|
|
a691535e77 | ||
|
|
85c228a0cd | ||
|
|
a3fe8c0e93 | ||
|
|
9bf43483db | ||
|
|
6c0b50c696 | ||
|
|
794ba428a7 | ||
|
|
812306cb52 | ||
|
|
1b0952c512 | ||
|
|
4fee536e6d | ||
|
|
3ef4d3118c | ||
|
|
75562e7fb5 | ||
|
|
5993b60980 | ||
|
|
f192191e8c | ||
|
|
508027e0e5 | ||
|
|
13baa5b60b | ||
|
|
7921de243a | ||
|
|
295152cd32 | ||
|
|
bf02340a6a | ||
|
|
6e69acdd7e | ||
|
|
07a7a213b3 | ||
|
|
1fcfae2464 | ||
|
|
e92a5eb467 | ||
|
|
e8714c9edb | ||
|
|
b220c2f51d | ||
|
|
f6fe627f4b | ||
|
|
3dd9392f5e | ||
|
|
39e9eaf2bc | ||
|
|
f4cb920624 | ||
|
|
fe13137b48 | ||
|
|
54b0350cac | ||
|
|
531c0559a0 | ||
|
|
358ae7529b | ||
|
|
068b71bc3d | ||
|
|
e634ab771f | ||
|
|
99bad77972 | ||
|
|
80d05c0425 | ||
|
|
a017b7500b | ||
|
|
439d9199be | ||
|
|
13d308a4d6 | ||
|
|
5375678ca6 | ||
|
|
6a210d719b | ||
|
|
ddef87f6e2 | ||
|
|
6a946f6eed | ||
|
|
4880ab41a2 | ||
|
|
a74247e5d8 | ||
|
|
6b77e6748a | ||
|
|
78eff0dc60 | ||
|
|
176b2cae19 | ||
|
|
cbfa7e7252 | ||
|
|
23344a7183 | ||
|
|
ae2fa6c1a4 | ||
|
|
522c9d640d | ||
|
|
510211a4c7 | ||
|
|
4df615f994 | ||
|
|
0b04654f33 | ||
|
|
4c966f2b8a | ||
|
|
ca5916f3dc | ||
|
|
fd22c7c73e | ||
|
|
c4bf25f33c | ||
|
|
b1f73b59cd | ||
|
|
c512eddb69 | ||
|
|
8050639b16 | ||
|
|
e6e4e29bf8 | ||
|
|
224db456af | ||
|
|
60f9635ada | ||
|
|
c1207e0938 | ||
|
|
49f5e89f36 | ||
|
|
fc66c905ff | ||
|
|
21e63a8a86 | ||
|
|
241951f53e | ||
|
|
be481d975c | ||
|
|
40a67d61d2 | ||
|
|
25bcaa3a54 | ||
|
|
e32823c3e0 | ||
|
|
19527016a5 | ||
|
|
5ddff790b6 | ||
|
|
f77d156e9a | ||
|
|
444df6fccb | ||
|
|
240248b9cf | ||
|
|
11894144aa | ||
|
|
0a0d6d0841 | ||
|
|
5be97f3761 | ||
|
|
a217017859 | ||
|
|
ae00518ddf | ||
|
|
7c6ee5f293 | ||
|
|
32c4cf5769 | ||
|
|
800077dabe | ||
|
|
a10b45e0db | ||
|
|
05ecd2e015 | ||
|
|
3cece50f78 | ||
|
|
be8ef1b324 | ||
|
|
45aaeb897a | ||
|
|
78a2dfa7c4 | ||
|
|
fea97a22c6 | ||
|
|
8a3972049b | ||
|
|
cfc0dabad9 | ||
|
|
470abfd0aa | ||
|
|
fe0290fb39 | ||
|
|
962b651c44 | ||
|
|
3b10a2de11 | ||
|
|
55550e7980 | ||
|
|
1ee9957838 | ||
|
|
99148244a4 | ||
|
|
dc1bfa3a04 | ||
|
|
69a42d8b1f | ||
|
|
440fe80c14 | ||
|
|
21ce9b448a | ||
|
|
a2e7b83411 | ||
|
|
81e59e9005 | ||
|
|
4197d7bd20 | ||
|
|
b6df415fe8 | ||
|
|
e664662df9 | ||
|
|
b62cb6ba84 | ||
|
|
2b9bc3c7e3 | ||
|
|
f5406570f7 | ||
|
|
f05be77a0b | ||
|
|
7648e8f6a3 | ||
|
|
9e6f9c2705 | ||
|
|
581c478872 | ||
|
|
bb59d055ff | ||
|
|
3059ce3070 | ||
|
|
8ae993185c | ||
|
|
c986ea1070 | ||
|
|
14c896215c | ||
|
|
03070c9fd0 | ||
|
|
f6516db105 | ||
|
|
ab72301a4c | ||
|
|
8fec4b804f | ||
|
|
fd8dd5e103 | ||
|
|
8845157d08 | ||
|
|
b5cc1087fe | ||
|
|
022171923c | ||
|
|
bea110b598 | ||
|
|
b78ae1608e | ||
|
|
e5fbc92856 | ||
|
|
e4df1f5a6f | ||
|
|
cddaa0c8fa | ||
|
|
4d321d6833 | ||
|
|
6c7c0854d1 | ||
|
|
5a1e96d671 | ||
|
|
5c5d330704 | ||
|
|
20398f1299 | ||
|
|
c52dbcbb52 | ||
|
|
c87668a91d | ||
|
|
e8eedb7b4d | ||
|
|
72fa3bd905 | ||
|
|
3b93cd45ea | ||
|
|
ad20e81d05 | ||
|
|
3998c9a89a | ||
|
|
127b1ac114 | ||
|
|
005b3e4a47 | ||
|
|
976b0401be | ||
|
|
16baea22c0 | ||
|
|
1c086aae7c | ||
|
|
05c80b3f3c | ||
|
|
3a8479614b | ||
|
|
e840b8f707 | ||
|
|
c2d5281e73 | ||
|
|
a83a98226c | ||
|
|
27a5051282 | ||
|
|
894a494186 | ||
|
|
c2c14cdddb | ||
|
|
14cc27e49b | ||
|
|
dc223cb82e | ||
|
|
9a5e1f5e28 | ||
|
|
9cd1dc70e5 | ||
|
|
13d1c88a11 | ||
|
|
d5e60dfb22 | ||
|
|
62125fa767 | ||
|
|
3a5bec5778 | ||
|
|
2875d8645c | ||
|
|
4c47de58c1 | ||
|
|
5f8875ff89 | ||
|
|
b033114f64 | ||
|
|
57ec5db1a9 | ||
|
|
490bd051cd | ||
|
|
e6aebd9df0 | ||
|
|
ee9b01b5e6 | ||
|
|
a3ff0725a3 | ||
|
|
e50a81cbb7 | ||
|
|
c9119848d9 | ||
|
|
14468b64fb | ||
|
|
3fda9f6e65 | ||
|
|
e85e61b6d7 | ||
|
|
30f0dd8c03 | ||
|
|
015ef4c3ef | ||
|
|
2b1a7898d9 | ||
|
|
5173f10e68 | ||
|
|
35620c4c86 | ||
|
|
e26e83b8df | ||
|
|
5d6f2436e4 | ||
|
|
90517e254a | ||
|
|
48439bc252 | ||
|
|
d75a5212b2 | ||
|
|
4a89a30abd | ||
|
|
c8bcfb77b2 | ||
|
|
71c1ca53a9 | ||
|
|
e4d4873d0d | ||
|
|
4c94adb5ec | ||
|
|
3846349ba0 | ||
|
|
821d294be8 | ||
|
|
5ff89a2ccf | ||
|
|
8b231c51f9 | ||
|
|
b680795d15 | ||
|
|
62e7c22783 | ||
|
|
211af1943c | ||
|
|
1477974bf1 | ||
|
|
1a3c9c8305 | ||
|
|
b8f6feb68b | ||
|
|
45316b6381 | ||
|
|
25e65e0d9f | ||
|
|
d0b627b018 | ||
|
|
f383fd1dc1 | ||
|
|
d7feb001be | ||
|
|
a3b4c32f07 | ||
|
|
f7a046ccd2 | ||
|
|
5fdb09380c | ||
|
|
7f389b9f9a | ||
|
|
353536b826 | ||
|
|
84b285a4c6 | ||
|
|
33c4a8233c | ||
|
|
c7f13f1036 | ||
|
|
c31c515205 | ||
|
|
20eaa34485 | ||
|
|
f516ccb4e2 | ||
|
|
2fceee4e35 | ||
|
|
acf28ebd98 | ||
|
|
6e33dd5df6 | ||
|
|
4b74dec18f | ||
|
|
a568d0af7f | ||
|
|
b3d30bfc4f | ||
|
|
d1cfdb97ee | ||
|
|
c8298356dc | ||
|
|
a92e1c7ea0 | ||
|
|
5e5c4e9a8c | ||
|
|
9911dd53e1 | ||
|
|
3c55cdd5be | ||
|
|
d42848bb7e | ||
|
|
28a707a956 | ||
|
|
a8ef9cc987 | ||
|
|
8251ad5e99 | ||
|
|
8715790fe7 | ||
|
|
037b49b454 | ||
|
|
a562568522 | ||
|
|
9d9de18bc9 | ||
|
|
8fc3b268e8 | ||
|
|
74a37475db | ||
|
|
44ebc77ada | ||
|
|
fec7ea6964 | ||
|
|
b08fa43fdf | ||
|
|
f07984bab2 | ||
|
|
13aaa22df5 | ||
|
|
449ebb8a12 | ||
|
|
17de5c120a | ||
|
|
cca6a13fbb | ||
|
|
601b43ac0a | ||
|
|
c9a0067705 | ||
|
|
2c35af51cd | ||
|
|
8a77906296 | ||
|
|
5865b51a94 | ||
|
|
dc6f5f60d1 | ||
|
|
28c9b52dce | ||
|
|
46063c7d04 | ||
|
|
c4ec674057 | ||
|
|
4d3e2bb814 | ||
|
|
290167e1a3 | ||
|
|
3a887d1c92 | ||
|
|
792d4a83f9 | ||
|
|
337a747bde | ||
|
|
b2b45237c6 | ||
|
|
31b61b1aa6 | ||
|
|
938176c9da | ||
|
|
7586762b10 | ||
|
|
488368ecde | ||
|
|
23281410e3 | ||
|
|
6db728190e | ||
|
|
02f1957919 | ||
|
|
71b93d125e | ||
|
|
fb1ef07e9f | ||
|
|
c062d5e206 | ||
|
|
c10733f926 | ||
|
|
b8c43d7a71 | ||
|
|
eebada46b1 | ||
|
|
edbe95837f | ||
|
|
4acd1ababe | ||
|
|
c725c447ac | ||
|
|
e12febfd96 | ||
|
|
b565f997a0 | ||
|
|
639c42c9e9 | ||
|
|
f52db7f9a3 | ||
|
|
a88759283a | ||
|
|
7e7b5b4488 | ||
|
|
8449dabefe | ||
|
|
de565f9ccc | ||
|
|
b96edb9c64 | ||
|
|
4e5483744f | ||
|
|
994ea704da | ||
|
|
33a1469a56 | ||
|
|
8219465389 | ||
|
|
0626d693f5 | ||
|
|
6a0b56bf40 | ||
|
|
2622de9747 | ||
|
|
82f9903bf0 | ||
|
|
faae811be7 | ||
|
|
65aa064838 | ||
|
|
38ca68febb | ||
|
|
79e161e046 | ||
|
|
d67e756f42 | ||
|
|
f15291a9de | ||
|
|
d079c7a5ef | ||
|
|
bb50a99b36 | ||
|
|
b5dd815249 | ||
|
|
c598d9b882 | ||
|
|
8a9a69fa00 | ||
|
|
04f87a26a9 | ||
|
|
538adb47a3 | ||
|
|
10308f5875 | ||
|
|
745d30252c | ||
|
|
a62a8d9960 | ||
|
|
d859e1e9a3 | ||
|
|
de2e92d5e1 | ||
|
|
fb5b6eab19 | ||
|
|
2ad28ab4db | ||
|
|
65c9a7b278 | ||
|
|
c1f0940b6a | ||
|
|
5609d5200b | ||
|
|
9499961a9c | ||
|
|
d489c12014 | ||
|
|
475e36e6fc | ||
|
|
df1ea5b679 | ||
|
|
5a9061e45b |
2
.github/actions/fetch-codeql/action.yml
vendored
2
.github/actions/fetch-codeql/action.yml
vendored
@@ -19,4 +19,6 @@ runs:
|
||||
gh extension install github/gh-codeql
|
||||
gh codeql set-channel "$CHANNEL"
|
||||
gh codeql version
|
||||
printf "CODEQL_FETCHED_CODEQL_PATH=" >> "${GITHUB_ENV}"
|
||||
gh codeql version --format=json | jq -r .unpackedLocation >> "${GITHUB_ENV}"
|
||||
gh codeql version --format=json | jq -r .unpackedLocation >> "${GITHUB_PATH}"
|
||||
|
||||
26
.github/actions/find-latest-bundle/action.yml
vendored
26
.github/actions/find-latest-bundle/action.yml
vendored
@@ -1,26 +0,0 @@
|
||||
name: Find Latest CodeQL Bundle
|
||||
description: Finds the URL of the latest released version of the CodeQL bundle.
|
||||
outputs:
|
||||
url:
|
||||
description: The download URL of the latest CodeQL bundle release
|
||||
value: ${{ steps.find-latest.outputs.url }}
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Find Latest Release
|
||||
id: find-latest
|
||||
shell: pwsh
|
||||
run: |
|
||||
$Latest = gh release list --repo github/codeql-action --exclude-drafts --limit 1000 |
|
||||
ForEach-Object { $C = $_ -split "`t"; return @{ type = $C[1]; tag = $C[2]; } } |
|
||||
Where-Object { $_.type -eq 'Latest' }
|
||||
|
||||
$Tag = $Latest.tag
|
||||
if ($Tag -eq '') {
|
||||
throw 'Failed to find latest bundle release.'
|
||||
}
|
||||
|
||||
Write-Output "Latest bundle tag is '${Tag}'."
|
||||
"url=https://github.com/github/codeql-action/releases/download/${Tag}/codeql-bundle-linux64.tar.gz" >> $env:GITHUB_OUTPUT
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
13
.github/dependabot.yml
vendored
13
.github/dependabot.yml
vendored
@@ -1,19 +1,12 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "cargo"
|
||||
directory: "ruby/node-types"
|
||||
directory: "ruby"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
|
||||
- package-ecosystem: "cargo"
|
||||
directory: "ruby/generator"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
- package-ecosystem: "cargo"
|
||||
directory: "ruby/extractor"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
- package-ecosystem: "cargo"
|
||||
directory: "ruby/autobuilder"
|
||||
directory: "ql"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
|
||||
|
||||
4
.github/workflows/codeql-analysis.yml
vendored
4
.github/workflows/codeql-analysis.yml
vendored
@@ -28,9 +28,9 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Setup dotnet
|
||||
uses: actions/setup-dotnet@v2
|
||||
uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: 6.0.202
|
||||
dotnet-version: 7.0.102
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
10
.github/workflows/csharp-qltest.yml
vendored
10
.github/workflows/csharp-qltest.yml
vendored
@@ -77,10 +77,10 @@ jobs:
|
||||
- name: Setup dotnet
|
||||
uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: 6.0.202
|
||||
dotnet-version: 7.0.102
|
||||
- name: Extractor unit tests
|
||||
run: |
|
||||
dotnet test -p:RuntimeFrameworkVersion=6.0.4 "${{ github.workspace }}/csharp/extractor/Semmle.Util.Tests"
|
||||
dotnet test -p:RuntimeFrameworkVersion=6.0.4 "${{ github.workspace }}/csharp/extractor/Semmle.Extraction.Tests"
|
||||
dotnet test -p:RuntimeFrameworkVersion=6.0.4 "${{ github.workspace }}/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests"
|
||||
dotnet test -p:RuntimeFrameworkVersion=6.0.4 "${{ github.workspace }}/cpp/autobuilder/Semmle.Autobuild.Cpp.Tests"
|
||||
dotnet test -p:RuntimeFrameworkVersion=7.0.2 "${{ github.workspace }}/csharp/extractor/Semmle.Util.Tests"
|
||||
dotnet test -p:RuntimeFrameworkVersion=7.0.2 "${{ github.workspace }}/csharp/extractor/Semmle.Extraction.Tests"
|
||||
dotnet test -p:RuntimeFrameworkVersion=7.0.2 "${{ github.workspace }}/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests"
|
||||
dotnet test -p:RuntimeFrameworkVersion=7.0.2 "${{ github.workspace }}/cpp/autobuilder/Semmle.Autobuild.Cpp.Tests"
|
||||
|
||||
8
.github/workflows/go-tests-other-os.yml
vendored
8
.github/workflows/go-tests-other-os.yml
vendored
@@ -12,10 +12,10 @@ jobs:
|
||||
name: Test MacOS
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- name: Set up Go 1.19
|
||||
- name: Set up Go 1.20
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.19
|
||||
go-version: 1.20.0
|
||||
id: go
|
||||
|
||||
- name: Check out code
|
||||
@@ -47,10 +47,10 @@ jobs:
|
||||
name: Test Windows
|
||||
runs-on: windows-latest-xl
|
||||
steps:
|
||||
- name: Set up Go 1.19
|
||||
- name: Set up Go 1.20
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.19
|
||||
go-version: 1.20.0
|
||||
id: go
|
||||
|
||||
- name: Check out code
|
||||
|
||||
4
.github/workflows/go-tests.yml
vendored
4
.github/workflows/go-tests.yml
vendored
@@ -20,10 +20,10 @@ jobs:
|
||||
name: Test Linux (Ubuntu)
|
||||
runs-on: ubuntu-latest-xl
|
||||
steps:
|
||||
- name: Set up Go 1.19
|
||||
- name: Set up Go 1.20
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.19
|
||||
go-version: 1.20.0
|
||||
id: go
|
||||
|
||||
- name: Check out code
|
||||
|
||||
138
.github/workflows/ql-for-ql-build.yml
vendored
138
.github/workflows/ql-for-ql-build.yml
vendored
@@ -22,66 +22,22 @@ jobs:
|
||||
steps:
|
||||
### Build the queries ###
|
||||
- uses: actions/checkout@v3
|
||||
- name: Find latest bundle
|
||||
id: find-latest-bundle
|
||||
uses: ./.github/actions/find-latest-bundle
|
||||
- name: Find codeql
|
||||
id: find-codeql
|
||||
uses: github/codeql-action/init@45955cb1830b640e2c1603ad72ad542a49d47b96
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: javascript # does not matter
|
||||
tools: ${{ steps.find-latest-bundle.outputs.url }}
|
||||
- name: Get CodeQL version
|
||||
id: get-codeql-version
|
||||
run: |
|
||||
echo "version=$("${CODEQL}" --version | head -n 1 | rev | cut -d " " -f 1 | rev)" >> $GITHUB_OUTPUT
|
||||
shell: bash
|
||||
env:
|
||||
CODEQL: ${{ steps.find-codeql.outputs.codeql-path }}
|
||||
- uses: ./.github/actions/os-version
|
||||
id: os_version
|
||||
- name: Cache entire pack
|
||||
id: cache-pack
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ${{ runner.temp }}/pack
|
||||
key: ${{ runner.os }}-${{ steps.os_version.outputs.version }}-pack-${{ hashFiles('ql/**/Cargo.lock') }}-${{ hashFiles('ql/**/*.rs') }}-${{ hashFiles('ql/**/*.ql*') }}-${{ hashFiles('ql/**/qlpack.yml') }}-${{ hashFiles('ql/ql/src/ql.dbscheme*') }}-${{ steps.get-codeql-version.outputs.version }}--${{ hashFiles('.github/workflows/ql-for-ql-build.yml') }}
|
||||
- name: Cache queries
|
||||
if: steps.cache-pack.outputs.cache-hit != 'true'
|
||||
id: cache-queries
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ${{ runner.temp }}/queries
|
||||
key: queries-${{ hashFiles('ql/**/*.ql*') }}-${{ hashFiles('ql/**/qlpack.yml') }}-${{ hashFiles('ql/ql/src/ql.dbscheme*') }}-${{ steps.get-codeql-version.outputs.version }}--${{ hashFiles('.github/workflows/ql-for-ql-build.yml') }}
|
||||
- name: Build query pack
|
||||
if: steps.cache-queries.outputs.cache-hit != 'true' && steps.cache-pack.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
cd ql/ql/src
|
||||
"${CODEQL}" pack create -j 16
|
||||
mv .codeql/pack/codeql/ql/0.0.0 ${{ runner.temp }}/queries
|
||||
env:
|
||||
CODEQL: ${{ steps.find-codeql.outputs.codeql-path }}
|
||||
- name: Move cache queries to pack
|
||||
if: steps.cache-pack.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
cp -r ${{ runner.temp }}/queries ${{ runner.temp }}/pack
|
||||
env:
|
||||
CODEQL: ${{ steps.find-codeql.outputs.codeql-path }}
|
||||
|
||||
### Build the extractor ###
|
||||
- name: Cache entire extractor
|
||||
if: steps.cache-pack.outputs.cache-hit != 'true'
|
||||
id: cache-extractor
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
ql/target/release/ql-autobuilder
|
||||
ql/target/release/ql-autobuilder.exe
|
||||
ql/target/release/ql-extractor
|
||||
ql/target/release/ql-extractor.exe
|
||||
path: ql/extractor-pack/
|
||||
key: ${{ runner.os }}-${{ steps.os_version.outputs.version }}-extractor-${{ hashFiles('ql/**/Cargo.lock') }}-${{ hashFiles('ql/**/*.rs') }}
|
||||
- name: Cache cargo
|
||||
if: steps.cache-extractor.outputs.cache-hit != 'true' && steps.cache-pack.outputs.cache-hit != 'true'
|
||||
if: steps.cache-extractor.outputs.cache-hit != 'true'
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
@@ -89,77 +45,31 @@ jobs:
|
||||
~/.cargo/git
|
||||
ql/target
|
||||
key: ${{ runner.os }}-${{ steps.os_version.outputs.version }}-rust-cargo-${{ hashFiles('ql/**/Cargo.lock') }}
|
||||
- name: Check formatting
|
||||
if: steps.cache-extractor.outputs.cache-hit != 'true' && steps.cache-pack.outputs.cache-hit != 'true'
|
||||
run: cd ql; cargo fmt --all -- --check
|
||||
- name: Build
|
||||
if: steps.cache-extractor.outputs.cache-hit != 'true' && steps.cache-pack.outputs.cache-hit != 'true'
|
||||
run: cd ql; cargo build --verbose
|
||||
- name: Run tests
|
||||
if: steps.cache-extractor.outputs.cache-hit != 'true' && steps.cache-pack.outputs.cache-hit != 'true'
|
||||
run: cd ql; cargo test --verbose
|
||||
- name: Release build
|
||||
if: steps.cache-extractor.outputs.cache-hit != 'true' && steps.cache-pack.outputs.cache-hit != 'true'
|
||||
run: cd ql; cargo build --release
|
||||
- name: Generate dbscheme
|
||||
if: steps.cache-extractor.outputs.cache-hit != 'true' && steps.cache-pack.outputs.cache-hit != 'true'
|
||||
run: ql/target/release/ql-generator --dbscheme ql/ql/src/ql.dbscheme --library ql/ql/src/codeql_ql/ast/internal/TreeSitter.qll
|
||||
|
||||
### Package the queries and extractor ###
|
||||
- name: Package pack
|
||||
if: steps.cache-pack.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
cp -r ql/codeql-extractor.yml ql/tools ql/ql/src/ql.dbscheme.stats ${PACK}/
|
||||
mkdir -p ${PACK}/tools/linux64
|
||||
cp ql/target/release/ql-autobuilder ${PACK}/tools/linux64/autobuilder
|
||||
cp ql/target/release/ql-extractor ${PACK}/tools/linux64/extractor
|
||||
chmod +x ${PACK}/tools/linux64/autobuilder
|
||||
chmod +x ${PACK}/tools/linux64/extractor
|
||||
if: steps.cache-extractor.outputs.cache-hit != 'true'
|
||||
run: cd ql; ./scripts/create-extractor-pack.sh
|
||||
env:
|
||||
PACK: ${{ runner.temp }}/pack
|
||||
|
||||
### Run the analysis ###
|
||||
- name: Hack codeql-action options
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
- name: Cache compilation cache
|
||||
id: query-cache
|
||||
uses: ./.github/actions/cache-query-compilation
|
||||
with:
|
||||
key: run-ql-for-ql
|
||||
- name: Make database and analyze
|
||||
run: |
|
||||
JSON=$(jq -nc --arg pack "${PACK}" '.database."run-queries"=["--search-path", $pack] | .resolve.queries=["--search-path", $pack] | .resolve.extractor=["--search-path", $pack] | .resolve.languages=["--search-path", $pack] | .database.init=["--search-path", $pack]')
|
||||
echo "CODEQL_ACTION_EXTRA_OPTIONS=${JSON}" >> ${GITHUB_ENV}
|
||||
env:
|
||||
PACK: ${{ runner.temp }}/pack
|
||||
|
||||
- name: Create CodeQL config file
|
||||
run: |
|
||||
echo "paths-ignore:" >> ${CONF}
|
||||
echo " - ql/ql/test" >> ${CONF}
|
||||
echo " - \"*/ql/lib/upgrades/\"" >> ${CONF}
|
||||
echo "disable-default-queries: true" >> ${CONF}
|
||||
echo "queries:" >> ${CONF}
|
||||
echo " - uses: ./ql/ql/src/codeql-suites/ql-code-scanning.qls" >> ${CONF}
|
||||
echo "Config file: "
|
||||
cat ${CONF}
|
||||
env:
|
||||
CONF: ./ql-for-ql-config.yml
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@45955cb1830b640e2c1603ad72ad542a49d47b96
|
||||
${CODEQL} database create -l=ql --search-path ql/extractor-pack ${DB}
|
||||
${CODEQL} database analyze -j0 --format=sarif-latest --output=ql-for-ql.sarif ${DB} ql/ql/src/codeql-suites/ql-code-scanning.qls --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}"
|
||||
env:
|
||||
CODEQL: ${{ steps.find-codeql.outputs.codeql-path }}
|
||||
DB: ${{ runner.temp }}/DB
|
||||
LGTM_INDEX_FILTERS: |
|
||||
exclude:ql/ql/test
|
||||
exclude:*/ql/lib/upgrades/
|
||||
- name: Upload sarif to code-scanning
|
||||
uses: github/codeql-action/upload-sarif@v2
|
||||
with:
|
||||
languages: ql
|
||||
db-location: ${{ runner.temp }}/db
|
||||
config-file: ./ql-for-ql-config.yml
|
||||
tools: ${{ steps.find-latest-bundle.outputs.url }}
|
||||
- name: Move pack queries
|
||||
run: |
|
||||
cp -r ${PACK}/queries ql/ql/src
|
||||
env:
|
||||
PACK: ${{ runner.temp }}/pack
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@45955cb1830b640e2c1603ad72ad542a49d47b96
|
||||
with:
|
||||
category: "ql-for-ql"
|
||||
- name: Copy sarif file to CWD
|
||||
run: cp ../results/ql.sarif ./ql-for-ql.sarif
|
||||
- name: Fixup the $scema in sarif # Until https://github.com/microsoft/sarif-vscode-extension/pull/436/ is part in a stable release
|
||||
run: |
|
||||
sed -i 's/\$schema.*/\$schema": "https:\/\/raw.githubusercontent.com\/oasis-tcs\/sarif-spec\/master\/Schemata\/sarif-schema-2.1.0",/' ql-for-ql.sarif
|
||||
sarif_file: ql-for-ql.sarif
|
||||
category: ql-for-ql
|
||||
- name: Sarif as artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
|
||||
@@ -25,7 +25,7 @@ jobs:
|
||||
|
||||
- name: Find codeql
|
||||
id: find-codeql
|
||||
uses: github/codeql-action/init@45955cb1830b640e2c1603ad72ad542a49d47b96
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: javascript # does not matter
|
||||
- uses: ./.github/actions/os-version
|
||||
|
||||
8
.github/workflows/ql-for-ql-tests.yml
vendored
8
.github/workflows/ql-for-ql-tests.yml
vendored
@@ -6,11 +6,13 @@ on:
|
||||
paths:
|
||||
- "ql/**"
|
||||
- codeql-workspace.yml
|
||||
- .github/workflows/ql-for-ql-tests.yml
|
||||
pull_request:
|
||||
branches: [main]
|
||||
paths:
|
||||
- "ql/**"
|
||||
- codeql-workspace.yml
|
||||
- .github/workflows/ql-for-ql-tests.yml
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
@@ -22,7 +24,7 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Find codeql
|
||||
id: find-codeql
|
||||
uses: github/codeql-action/init@45955cb1830b640e2c1603ad72ad542a49d47b96
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: javascript # does not matter
|
||||
- uses: ./.github/actions/os-version
|
||||
@@ -34,6 +36,8 @@ jobs:
|
||||
~/.cargo/git
|
||||
ql/target
|
||||
key: ${{ runner.os }}-${{ steps.os_version.outputs.version }}-qltest-cargo-${{ hashFiles('ql/rust-toolchain.toml', 'ql/**/Cargo.lock') }}
|
||||
- name: Check formatting
|
||||
run: cd ql; cargo fmt --all -- --check
|
||||
- name: Build extractor
|
||||
run: |
|
||||
cd ql;
|
||||
@@ -65,7 +69,7 @@ jobs:
|
||||
echo "/usr/local/opt/gnu-tar/libexec/gnubin" >> $GITHUB_PATH
|
||||
- name: Find codeql
|
||||
id: find-codeql
|
||||
uses: github/codeql-action/init@77a8d2d10c0b403a8b4aadbd223dc489ecd22683
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: javascript # does not matter
|
||||
- uses: ./.github/actions/os-version
|
||||
|
||||
@@ -10,6 +10,8 @@ There is [extensive documentation](https://codeql.github.com/docs/) on getting s
|
||||
|
||||
We welcome contributions to our standard library and standard checks. Do you have an idea for a new check, or how to improve an existing query? Then please go ahead and open a pull request! Before you do, though, please take the time to read our [contributing guidelines](CONTRIBUTING.md). You can also consult our [style guides](https://github.com/github/codeql/tree/main/docs) to learn how to format your code for consistency and clarity, how to write query metadata, and how to write query help documentation for your query.
|
||||
|
||||
For information on contributing to CodeQL documentation, see the "[contributing guide](docs/codeql/CONTRIBUTING.md)" for docs.
|
||||
|
||||
## License
|
||||
|
||||
The code in this repository is licensed under the [MIT License](LICENSE) by [GitHub](https://github.com).
|
||||
|
||||
@@ -29,13 +29,14 @@
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplForContentDataFlow.qll",
|
||||
"go/ql/lib/semmle/go/dataflow/internal/DataFlowImpl.qll",
|
||||
"go/ql/lib/semmle/go/dataflow/internal/DataFlowImpl2.qll",
|
||||
"go/ql/lib/semmle/go/dataflow/internal/DataFlowImplForStringsNewReplacer.qll",
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl.qll",
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl2.qll",
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl3.qll",
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl4.qll",
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplForRegExp.qll",
|
||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll",
|
||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl2.qll",
|
||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForRegExp.qll",
|
||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForHttpClientLibraries.qll",
|
||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForPathname.qll",
|
||||
"swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImpl.qll"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Xunit;
|
||||
using Semmle.Autobuild.Shared;
|
||||
using Semmle.Util;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Linq;
|
||||
@@ -75,6 +76,15 @@ namespace Semmle.Autobuild.Cpp.Tests
|
||||
throw new ArgumentException("Missing RunProcess " + pattern);
|
||||
}
|
||||
|
||||
int IBuildActions.RunProcess(string cmd, string args, string? workingDirectory, IDictionary<string, string>? env, BuildOutputHandler onOutput, BuildOutputHandler onError)
|
||||
{
|
||||
var ret = (this as IBuildActions).RunProcess(cmd, args, workingDirectory, env, out var stdout);
|
||||
|
||||
stdout.ForEach(line => onOutput(line));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public IList<string> DirectoryDeleteIn = new List<string>();
|
||||
|
||||
void IBuildActions.DirectoryDelete(string dir, bool recursive)
|
||||
@@ -131,6 +141,14 @@ namespace Semmle.Autobuild.Cpp.Tests
|
||||
|
||||
bool IBuildActions.IsWindows() => IsWindows;
|
||||
|
||||
public bool IsMacOs { get; set; }
|
||||
|
||||
bool IBuildActions.IsMacOs() => IsMacOs;
|
||||
|
||||
public bool IsArm { get; set; }
|
||||
|
||||
bool IBuildActions.IsArm() => IsArm;
|
||||
|
||||
string IBuildActions.PathCombine(params string[] parts)
|
||||
{
|
||||
return string.Join(IsWindows ? '\\' : '/', parts.Where(p => !string.IsNullOrWhiteSpace(p)));
|
||||
@@ -235,6 +253,7 @@ namespace Semmle.Autobuild.Cpp.Tests
|
||||
Actions.GetEnvironmentVariable[$"CODEQL_EXTRACTOR_{codeqlUpperLanguage}_TRAP_DIR"] = "";
|
||||
Actions.GetEnvironmentVariable[$"CODEQL_EXTRACTOR_{codeqlUpperLanguage}_SOURCE_ARCHIVE_DIR"] = "";
|
||||
Actions.GetEnvironmentVariable[$"CODEQL_EXTRACTOR_{codeqlUpperLanguage}_ROOT"] = $@"C:\codeql\{codeqlUpperLanguage.ToLowerInvariant()}";
|
||||
Actions.GetEnvironmentVariable[$"CODEQL_EXTRACTOR_{codeqlUpperLanguage}_DIAGNOSTIC_DIR"] = Path.GetTempPath();
|
||||
Actions.GetEnvironmentVariable["CODEQL_JAVA_HOME"] = @"C:\codeql\tools\java";
|
||||
Actions.GetEnvironmentVariable["CODEQL_PLATFORM"] = "win64";
|
||||
Actions.GetEnvironmentVariable["SEMMLE_DIST"] = @"C:\odasa";
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<RuntimeIdentifiers>win-x64;linux-x64;osx-x64</RuntimeIdentifiers>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Semmle.Autobuild.Shared;
|
||||
using Semmle.Util;
|
||||
|
||||
namespace Semmle.Autobuild.Cpp
|
||||
{
|
||||
@@ -21,7 +22,7 @@ namespace Semmle.Autobuild.Cpp
|
||||
|
||||
public class CppAutobuilder : Autobuilder<CppAutobuildOptions>
|
||||
{
|
||||
public CppAutobuilder(IBuildActions actions, CppAutobuildOptions options) : base(actions, options) { }
|
||||
public CppAutobuilder(IBuildActions actions, CppAutobuildOptions options) : base(actions, options, new DiagnosticClassifier()) { }
|
||||
|
||||
public override BuildScript GetBuildScript()
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<AssemblyName>Semmle.Autobuild.Cpp</AssemblyName>
|
||||
<RootNamespace>Semmle.Autobuild.Cpp</RootNamespace>
|
||||
<ApplicationIcon />
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
## 0.5.2
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.5.1
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.5.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
3
cpp/ql/lib/change-notes/released/0.5.1.md
Normal file
3
cpp/ql/lib/change-notes/released/0.5.1.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.5.1
|
||||
|
||||
No user-facing changes.
|
||||
3
cpp/ql/lib/change-notes/released/0.5.2.md
Normal file
3
cpp/ql/lib/change-notes/released/0.5.2.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.5.2
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.5.0
|
||||
lastReleaseVersion: 0.5.2
|
||||
|
||||
@@ -667,23 +667,78 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(NodeEx arg |
|
||||
fwdFlow(arg, _, config) and
|
||||
viableParamArgEx(_, node, arg) and
|
||||
cc = true and
|
||||
not fullBarrier(node, config)
|
||||
)
|
||||
fwdFlowIn(_, _, _, node, config) and
|
||||
cc = true
|
||||
or
|
||||
// flow out of a callable
|
||||
fwdFlowOut(_, node, false, config) and
|
||||
cc = false
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call |
|
||||
fwdFlowOut(call, node, false, config) and
|
||||
cc = false
|
||||
or
|
||||
fwdFlowOutFromArg(call, node, config) and
|
||||
fwdFlowIsEntered(call, cc, config)
|
||||
)
|
||||
}
|
||||
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate fwdFlowIn(
|
||||
DataFlowCall call, NodeEx arg, Cc cc, ParamNodeEx p, Configuration config
|
||||
) {
|
||||
// call context cannot help reduce virtual dispatch
|
||||
fwdFlow(arg, cc, config) and
|
||||
viableParamArgEx(call, p, arg) and
|
||||
not fullBarrier(p, config) and
|
||||
(
|
||||
cc = false
|
||||
or
|
||||
cc = true and
|
||||
not reducedViableImplInCallContext(call, _, _)
|
||||
)
|
||||
or
|
||||
// call context may help reduce virtual dispatch
|
||||
exists(DataFlowCallable target |
|
||||
fwdFlowInReducedViableImplInSomeCallContext(call, arg, p, target, config) and
|
||||
target = viableImplInSomeFwdFlowCallContextExt(call, config) and
|
||||
cc = true
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an argument to `call` is reached in the flow covered by `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) {
|
||||
fwdFlowIn(call, _, cc, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowInReducedViableImplInSomeCallContext(
|
||||
DataFlowCall call, NodeEx arg, ParamNodeEx p, DataFlowCallable target, Configuration config
|
||||
) {
|
||||
fwdFlow(arg, true, config) and
|
||||
viableParamArgEx(call, p, arg) and
|
||||
reducedViableImplInCallContext(call, _, _) and
|
||||
target = p.getEnclosingCallable() and
|
||||
not fullBarrier(p, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a viable dispatch target of `call` in the context `ctx`. This is
|
||||
* restricted to those `call`s for which a context might make a difference,
|
||||
* and to `ctx`s that are reachable in `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private DataFlowCallable viableImplInSomeFwdFlowCallContextExt(
|
||||
DataFlowCall call, Configuration config
|
||||
) {
|
||||
exists(DataFlowCall ctx |
|
||||
fwdFlowIsEntered(ctx, _, config) and
|
||||
result = viableImplInCallContextExt(call, ctx)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate fwdFlow(NodeEx node, Configuration config) { fwdFlow(node, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -726,7 +781,8 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate fwdFlowOut(DataFlowCall call, NodeEx out, Cc cc, Configuration config) {
|
||||
exists(ReturnPosition pos |
|
||||
fwdFlowReturnPosition(pos, cc, config) and
|
||||
@@ -740,17 +796,6 @@ private module Stage1 implements StageSig {
|
||||
fwdFlowOut(call, out, true, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an argument to `call` is reached in the flow covered by `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) {
|
||||
exists(ArgNodeEx arg |
|
||||
fwdFlow(arg, cc, config) and
|
||||
viableParamArgEx(call, _, arg)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate stateStepFwd(FlowState state1, FlowState state2, Configuration config) {
|
||||
exists(NodeEx node1 |
|
||||
additionalLocalStateStep(node1, state1, _, state2, config) or
|
||||
@@ -817,13 +862,8 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(DataFlowCall call |
|
||||
revFlowIn(call, node, false, config) and
|
||||
toReturn = false
|
||||
or
|
||||
revFlowInToReturn(call, node, config) and
|
||||
revFlowIsReturned(call, toReturn, config)
|
||||
)
|
||||
revFlowIn(_, node, false, config) and
|
||||
toReturn = false
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(ReturnPosition pos |
|
||||
@@ -831,6 +871,12 @@ private module Stage1 implements StageSig {
|
||||
node.(RetNodeEx).getReturnPosition() = pos and
|
||||
toReturn = true
|
||||
)
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call |
|
||||
revFlowInToReturn(call, node, config) and
|
||||
revFlowIsReturned(call, toReturn, config)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -886,11 +932,11 @@ private module Stage1 implements StageSig {
|
||||
additional predicate viableParamArgNodeCandFwd1(
|
||||
DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
|
||||
) {
|
||||
viableParamArgEx(call, p, arg) and
|
||||
fwdFlow(arg, config)
|
||||
fwdFlowIn(call, arg, _, p, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate revFlowIn(
|
||||
DataFlowCall call, ArgNodeEx arg, boolean toReturn, Configuration config
|
||||
) {
|
||||
@@ -1223,7 +1269,16 @@ private module MkStage<StageSig PrevStage> {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail);
|
||||
|
||||
Content getHeadContent(Ap ap);
|
||||
/**
|
||||
* An approximation of `Content` that corresponds to the precision level of
|
||||
* `Ap`, such that the mappings from both `Ap` and `Content` to this type
|
||||
* are functional.
|
||||
*/
|
||||
class ApHeadContent;
|
||||
|
||||
ApHeadContent getHeadContent(Ap ap);
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c);
|
||||
|
||||
class ApOption;
|
||||
|
||||
@@ -1471,20 +1526,20 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
}
|
||||
|
||||
private class ApNonNil instanceof Ap {
|
||||
pragma[nomagic]
|
||||
ApNonNil() { not this instanceof ApNil }
|
||||
|
||||
string toString() { result = "" }
|
||||
pragma[nomagic]
|
||||
private predicate readStepCand(
|
||||
NodeEx node1, ApHeadContent apc, Content c, NodeEx node2, Configuration config
|
||||
) {
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
apc = projectToHeadContent(c)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead0(
|
||||
NodeEx node1, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ApNonNil ap,
|
||||
Configuration config
|
||||
bindingset[node1, apc]
|
||||
pragma[inline_late]
|
||||
private predicate readStepCand0(
|
||||
NodeEx node1, ApHeadContent apc, Content c, NodeEx node2, Configuration config
|
||||
) {
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, _, _, config)
|
||||
readStepCand(node1, apc, c, node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1492,9 +1547,11 @@ private module MkStage<StageSig PrevStage> {
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
ParamNodeOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
getHeadContent(ap) = c
|
||||
exists(ApHeadContent apc |
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
apc = getHeadContent(ap) and
|
||||
readStepCand0(node1, apc, c, node2, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -2068,8 +2125,12 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) }
|
||||
|
||||
class ApHeadContent = Unit;
|
||||
|
||||
pragma[inline]
|
||||
Content getHeadContent(Ap ap) { exists(result) and ap = true }
|
||||
ApHeadContent getHeadContent(Ap ap) { exists(result) and ap = true }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { any() }
|
||||
|
||||
class ApOption = BooleanOption;
|
||||
|
||||
@@ -2333,8 +2394,12 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result.getAHead() = tc and exists(tail) }
|
||||
|
||||
class ApHeadContent = ContentApprox;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getAHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
predicate projectToHeadContent = getContentApprox/1;
|
||||
|
||||
class ApOption = ApproxAccessPathFrontOption;
|
||||
|
||||
@@ -2409,8 +2474,12 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) }
|
||||
|
||||
class ApHeadContent = Content;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { result = c }
|
||||
|
||||
class ApOption = AccessPathFrontOption;
|
||||
|
||||
@@ -2739,8 +2808,12 @@ private module Stage5Param implements MkStage<Stage4>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) }
|
||||
|
||||
class ApHeadContent = Content;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { result = c }
|
||||
|
||||
class ApOption = AccessPathApproxOption;
|
||||
|
||||
|
||||
@@ -667,23 +667,78 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(NodeEx arg |
|
||||
fwdFlow(arg, _, config) and
|
||||
viableParamArgEx(_, node, arg) and
|
||||
cc = true and
|
||||
not fullBarrier(node, config)
|
||||
)
|
||||
fwdFlowIn(_, _, _, node, config) and
|
||||
cc = true
|
||||
or
|
||||
// flow out of a callable
|
||||
fwdFlowOut(_, node, false, config) and
|
||||
cc = false
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call |
|
||||
fwdFlowOut(call, node, false, config) and
|
||||
cc = false
|
||||
or
|
||||
fwdFlowOutFromArg(call, node, config) and
|
||||
fwdFlowIsEntered(call, cc, config)
|
||||
)
|
||||
}
|
||||
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate fwdFlowIn(
|
||||
DataFlowCall call, NodeEx arg, Cc cc, ParamNodeEx p, Configuration config
|
||||
) {
|
||||
// call context cannot help reduce virtual dispatch
|
||||
fwdFlow(arg, cc, config) and
|
||||
viableParamArgEx(call, p, arg) and
|
||||
not fullBarrier(p, config) and
|
||||
(
|
||||
cc = false
|
||||
or
|
||||
cc = true and
|
||||
not reducedViableImplInCallContext(call, _, _)
|
||||
)
|
||||
or
|
||||
// call context may help reduce virtual dispatch
|
||||
exists(DataFlowCallable target |
|
||||
fwdFlowInReducedViableImplInSomeCallContext(call, arg, p, target, config) and
|
||||
target = viableImplInSomeFwdFlowCallContextExt(call, config) and
|
||||
cc = true
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an argument to `call` is reached in the flow covered by `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) {
|
||||
fwdFlowIn(call, _, cc, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowInReducedViableImplInSomeCallContext(
|
||||
DataFlowCall call, NodeEx arg, ParamNodeEx p, DataFlowCallable target, Configuration config
|
||||
) {
|
||||
fwdFlow(arg, true, config) and
|
||||
viableParamArgEx(call, p, arg) and
|
||||
reducedViableImplInCallContext(call, _, _) and
|
||||
target = p.getEnclosingCallable() and
|
||||
not fullBarrier(p, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a viable dispatch target of `call` in the context `ctx`. This is
|
||||
* restricted to those `call`s for which a context might make a difference,
|
||||
* and to `ctx`s that are reachable in `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private DataFlowCallable viableImplInSomeFwdFlowCallContextExt(
|
||||
DataFlowCall call, Configuration config
|
||||
) {
|
||||
exists(DataFlowCall ctx |
|
||||
fwdFlowIsEntered(ctx, _, config) and
|
||||
result = viableImplInCallContextExt(call, ctx)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate fwdFlow(NodeEx node, Configuration config) { fwdFlow(node, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -726,7 +781,8 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate fwdFlowOut(DataFlowCall call, NodeEx out, Cc cc, Configuration config) {
|
||||
exists(ReturnPosition pos |
|
||||
fwdFlowReturnPosition(pos, cc, config) and
|
||||
@@ -740,17 +796,6 @@ private module Stage1 implements StageSig {
|
||||
fwdFlowOut(call, out, true, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an argument to `call` is reached in the flow covered by `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) {
|
||||
exists(ArgNodeEx arg |
|
||||
fwdFlow(arg, cc, config) and
|
||||
viableParamArgEx(call, _, arg)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate stateStepFwd(FlowState state1, FlowState state2, Configuration config) {
|
||||
exists(NodeEx node1 |
|
||||
additionalLocalStateStep(node1, state1, _, state2, config) or
|
||||
@@ -817,13 +862,8 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(DataFlowCall call |
|
||||
revFlowIn(call, node, false, config) and
|
||||
toReturn = false
|
||||
or
|
||||
revFlowInToReturn(call, node, config) and
|
||||
revFlowIsReturned(call, toReturn, config)
|
||||
)
|
||||
revFlowIn(_, node, false, config) and
|
||||
toReturn = false
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(ReturnPosition pos |
|
||||
@@ -831,6 +871,12 @@ private module Stage1 implements StageSig {
|
||||
node.(RetNodeEx).getReturnPosition() = pos and
|
||||
toReturn = true
|
||||
)
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call |
|
||||
revFlowInToReturn(call, node, config) and
|
||||
revFlowIsReturned(call, toReturn, config)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -886,11 +932,11 @@ private module Stage1 implements StageSig {
|
||||
additional predicate viableParamArgNodeCandFwd1(
|
||||
DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
|
||||
) {
|
||||
viableParamArgEx(call, p, arg) and
|
||||
fwdFlow(arg, config)
|
||||
fwdFlowIn(call, arg, _, p, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate revFlowIn(
|
||||
DataFlowCall call, ArgNodeEx arg, boolean toReturn, Configuration config
|
||||
) {
|
||||
@@ -1223,7 +1269,16 @@ private module MkStage<StageSig PrevStage> {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail);
|
||||
|
||||
Content getHeadContent(Ap ap);
|
||||
/**
|
||||
* An approximation of `Content` that corresponds to the precision level of
|
||||
* `Ap`, such that the mappings from both `Ap` and `Content` to this type
|
||||
* are functional.
|
||||
*/
|
||||
class ApHeadContent;
|
||||
|
||||
ApHeadContent getHeadContent(Ap ap);
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c);
|
||||
|
||||
class ApOption;
|
||||
|
||||
@@ -1471,20 +1526,20 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
}
|
||||
|
||||
private class ApNonNil instanceof Ap {
|
||||
pragma[nomagic]
|
||||
ApNonNil() { not this instanceof ApNil }
|
||||
|
||||
string toString() { result = "" }
|
||||
pragma[nomagic]
|
||||
private predicate readStepCand(
|
||||
NodeEx node1, ApHeadContent apc, Content c, NodeEx node2, Configuration config
|
||||
) {
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
apc = projectToHeadContent(c)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead0(
|
||||
NodeEx node1, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ApNonNil ap,
|
||||
Configuration config
|
||||
bindingset[node1, apc]
|
||||
pragma[inline_late]
|
||||
private predicate readStepCand0(
|
||||
NodeEx node1, ApHeadContent apc, Content c, NodeEx node2, Configuration config
|
||||
) {
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, _, _, config)
|
||||
readStepCand(node1, apc, c, node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1492,9 +1547,11 @@ private module MkStage<StageSig PrevStage> {
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
ParamNodeOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
getHeadContent(ap) = c
|
||||
exists(ApHeadContent apc |
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
apc = getHeadContent(ap) and
|
||||
readStepCand0(node1, apc, c, node2, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -2068,8 +2125,12 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) }
|
||||
|
||||
class ApHeadContent = Unit;
|
||||
|
||||
pragma[inline]
|
||||
Content getHeadContent(Ap ap) { exists(result) and ap = true }
|
||||
ApHeadContent getHeadContent(Ap ap) { exists(result) and ap = true }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { any() }
|
||||
|
||||
class ApOption = BooleanOption;
|
||||
|
||||
@@ -2333,8 +2394,12 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result.getAHead() = tc and exists(tail) }
|
||||
|
||||
class ApHeadContent = ContentApprox;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getAHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
predicate projectToHeadContent = getContentApprox/1;
|
||||
|
||||
class ApOption = ApproxAccessPathFrontOption;
|
||||
|
||||
@@ -2409,8 +2474,12 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) }
|
||||
|
||||
class ApHeadContent = Content;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { result = c }
|
||||
|
||||
class ApOption = AccessPathFrontOption;
|
||||
|
||||
@@ -2739,8 +2808,12 @@ private module Stage5Param implements MkStage<Stage4>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) }
|
||||
|
||||
class ApHeadContent = Content;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { result = c }
|
||||
|
||||
class ApOption = AccessPathApproxOption;
|
||||
|
||||
|
||||
@@ -667,23 +667,78 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(NodeEx arg |
|
||||
fwdFlow(arg, _, config) and
|
||||
viableParamArgEx(_, node, arg) and
|
||||
cc = true and
|
||||
not fullBarrier(node, config)
|
||||
)
|
||||
fwdFlowIn(_, _, _, node, config) and
|
||||
cc = true
|
||||
or
|
||||
// flow out of a callable
|
||||
fwdFlowOut(_, node, false, config) and
|
||||
cc = false
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call |
|
||||
fwdFlowOut(call, node, false, config) and
|
||||
cc = false
|
||||
or
|
||||
fwdFlowOutFromArg(call, node, config) and
|
||||
fwdFlowIsEntered(call, cc, config)
|
||||
)
|
||||
}
|
||||
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate fwdFlowIn(
|
||||
DataFlowCall call, NodeEx arg, Cc cc, ParamNodeEx p, Configuration config
|
||||
) {
|
||||
// call context cannot help reduce virtual dispatch
|
||||
fwdFlow(arg, cc, config) and
|
||||
viableParamArgEx(call, p, arg) and
|
||||
not fullBarrier(p, config) and
|
||||
(
|
||||
cc = false
|
||||
or
|
||||
cc = true and
|
||||
not reducedViableImplInCallContext(call, _, _)
|
||||
)
|
||||
or
|
||||
// call context may help reduce virtual dispatch
|
||||
exists(DataFlowCallable target |
|
||||
fwdFlowInReducedViableImplInSomeCallContext(call, arg, p, target, config) and
|
||||
target = viableImplInSomeFwdFlowCallContextExt(call, config) and
|
||||
cc = true
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an argument to `call` is reached in the flow covered by `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) {
|
||||
fwdFlowIn(call, _, cc, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowInReducedViableImplInSomeCallContext(
|
||||
DataFlowCall call, NodeEx arg, ParamNodeEx p, DataFlowCallable target, Configuration config
|
||||
) {
|
||||
fwdFlow(arg, true, config) and
|
||||
viableParamArgEx(call, p, arg) and
|
||||
reducedViableImplInCallContext(call, _, _) and
|
||||
target = p.getEnclosingCallable() and
|
||||
not fullBarrier(p, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a viable dispatch target of `call` in the context `ctx`. This is
|
||||
* restricted to those `call`s for which a context might make a difference,
|
||||
* and to `ctx`s that are reachable in `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private DataFlowCallable viableImplInSomeFwdFlowCallContextExt(
|
||||
DataFlowCall call, Configuration config
|
||||
) {
|
||||
exists(DataFlowCall ctx |
|
||||
fwdFlowIsEntered(ctx, _, config) and
|
||||
result = viableImplInCallContextExt(call, ctx)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate fwdFlow(NodeEx node, Configuration config) { fwdFlow(node, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -726,7 +781,8 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate fwdFlowOut(DataFlowCall call, NodeEx out, Cc cc, Configuration config) {
|
||||
exists(ReturnPosition pos |
|
||||
fwdFlowReturnPosition(pos, cc, config) and
|
||||
@@ -740,17 +796,6 @@ private module Stage1 implements StageSig {
|
||||
fwdFlowOut(call, out, true, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an argument to `call` is reached in the flow covered by `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) {
|
||||
exists(ArgNodeEx arg |
|
||||
fwdFlow(arg, cc, config) and
|
||||
viableParamArgEx(call, _, arg)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate stateStepFwd(FlowState state1, FlowState state2, Configuration config) {
|
||||
exists(NodeEx node1 |
|
||||
additionalLocalStateStep(node1, state1, _, state2, config) or
|
||||
@@ -817,13 +862,8 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(DataFlowCall call |
|
||||
revFlowIn(call, node, false, config) and
|
||||
toReturn = false
|
||||
or
|
||||
revFlowInToReturn(call, node, config) and
|
||||
revFlowIsReturned(call, toReturn, config)
|
||||
)
|
||||
revFlowIn(_, node, false, config) and
|
||||
toReturn = false
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(ReturnPosition pos |
|
||||
@@ -831,6 +871,12 @@ private module Stage1 implements StageSig {
|
||||
node.(RetNodeEx).getReturnPosition() = pos and
|
||||
toReturn = true
|
||||
)
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call |
|
||||
revFlowInToReturn(call, node, config) and
|
||||
revFlowIsReturned(call, toReturn, config)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -886,11 +932,11 @@ private module Stage1 implements StageSig {
|
||||
additional predicate viableParamArgNodeCandFwd1(
|
||||
DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
|
||||
) {
|
||||
viableParamArgEx(call, p, arg) and
|
||||
fwdFlow(arg, config)
|
||||
fwdFlowIn(call, arg, _, p, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate revFlowIn(
|
||||
DataFlowCall call, ArgNodeEx arg, boolean toReturn, Configuration config
|
||||
) {
|
||||
@@ -1223,7 +1269,16 @@ private module MkStage<StageSig PrevStage> {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail);
|
||||
|
||||
Content getHeadContent(Ap ap);
|
||||
/**
|
||||
* An approximation of `Content` that corresponds to the precision level of
|
||||
* `Ap`, such that the mappings from both `Ap` and `Content` to this type
|
||||
* are functional.
|
||||
*/
|
||||
class ApHeadContent;
|
||||
|
||||
ApHeadContent getHeadContent(Ap ap);
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c);
|
||||
|
||||
class ApOption;
|
||||
|
||||
@@ -1471,20 +1526,20 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
}
|
||||
|
||||
private class ApNonNil instanceof Ap {
|
||||
pragma[nomagic]
|
||||
ApNonNil() { not this instanceof ApNil }
|
||||
|
||||
string toString() { result = "" }
|
||||
pragma[nomagic]
|
||||
private predicate readStepCand(
|
||||
NodeEx node1, ApHeadContent apc, Content c, NodeEx node2, Configuration config
|
||||
) {
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
apc = projectToHeadContent(c)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead0(
|
||||
NodeEx node1, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ApNonNil ap,
|
||||
Configuration config
|
||||
bindingset[node1, apc]
|
||||
pragma[inline_late]
|
||||
private predicate readStepCand0(
|
||||
NodeEx node1, ApHeadContent apc, Content c, NodeEx node2, Configuration config
|
||||
) {
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, _, _, config)
|
||||
readStepCand(node1, apc, c, node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1492,9 +1547,11 @@ private module MkStage<StageSig PrevStage> {
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
ParamNodeOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
getHeadContent(ap) = c
|
||||
exists(ApHeadContent apc |
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
apc = getHeadContent(ap) and
|
||||
readStepCand0(node1, apc, c, node2, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -2068,8 +2125,12 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) }
|
||||
|
||||
class ApHeadContent = Unit;
|
||||
|
||||
pragma[inline]
|
||||
Content getHeadContent(Ap ap) { exists(result) and ap = true }
|
||||
ApHeadContent getHeadContent(Ap ap) { exists(result) and ap = true }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { any() }
|
||||
|
||||
class ApOption = BooleanOption;
|
||||
|
||||
@@ -2333,8 +2394,12 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result.getAHead() = tc and exists(tail) }
|
||||
|
||||
class ApHeadContent = ContentApprox;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getAHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
predicate projectToHeadContent = getContentApprox/1;
|
||||
|
||||
class ApOption = ApproxAccessPathFrontOption;
|
||||
|
||||
@@ -2409,8 +2474,12 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) }
|
||||
|
||||
class ApHeadContent = Content;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { result = c }
|
||||
|
||||
class ApOption = AccessPathFrontOption;
|
||||
|
||||
@@ -2739,8 +2808,12 @@ private module Stage5Param implements MkStage<Stage4>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) }
|
||||
|
||||
class ApHeadContent = Content;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { result = c }
|
||||
|
||||
class ApOption = AccessPathApproxOption;
|
||||
|
||||
|
||||
@@ -667,23 +667,78 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(NodeEx arg |
|
||||
fwdFlow(arg, _, config) and
|
||||
viableParamArgEx(_, node, arg) and
|
||||
cc = true and
|
||||
not fullBarrier(node, config)
|
||||
)
|
||||
fwdFlowIn(_, _, _, node, config) and
|
||||
cc = true
|
||||
or
|
||||
// flow out of a callable
|
||||
fwdFlowOut(_, node, false, config) and
|
||||
cc = false
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call |
|
||||
fwdFlowOut(call, node, false, config) and
|
||||
cc = false
|
||||
or
|
||||
fwdFlowOutFromArg(call, node, config) and
|
||||
fwdFlowIsEntered(call, cc, config)
|
||||
)
|
||||
}
|
||||
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate fwdFlowIn(
|
||||
DataFlowCall call, NodeEx arg, Cc cc, ParamNodeEx p, Configuration config
|
||||
) {
|
||||
// call context cannot help reduce virtual dispatch
|
||||
fwdFlow(arg, cc, config) and
|
||||
viableParamArgEx(call, p, arg) and
|
||||
not fullBarrier(p, config) and
|
||||
(
|
||||
cc = false
|
||||
or
|
||||
cc = true and
|
||||
not reducedViableImplInCallContext(call, _, _)
|
||||
)
|
||||
or
|
||||
// call context may help reduce virtual dispatch
|
||||
exists(DataFlowCallable target |
|
||||
fwdFlowInReducedViableImplInSomeCallContext(call, arg, p, target, config) and
|
||||
target = viableImplInSomeFwdFlowCallContextExt(call, config) and
|
||||
cc = true
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an argument to `call` is reached in the flow covered by `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) {
|
||||
fwdFlowIn(call, _, cc, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowInReducedViableImplInSomeCallContext(
|
||||
DataFlowCall call, NodeEx arg, ParamNodeEx p, DataFlowCallable target, Configuration config
|
||||
) {
|
||||
fwdFlow(arg, true, config) and
|
||||
viableParamArgEx(call, p, arg) and
|
||||
reducedViableImplInCallContext(call, _, _) and
|
||||
target = p.getEnclosingCallable() and
|
||||
not fullBarrier(p, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a viable dispatch target of `call` in the context `ctx`. This is
|
||||
* restricted to those `call`s for which a context might make a difference,
|
||||
* and to `ctx`s that are reachable in `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private DataFlowCallable viableImplInSomeFwdFlowCallContextExt(
|
||||
DataFlowCall call, Configuration config
|
||||
) {
|
||||
exists(DataFlowCall ctx |
|
||||
fwdFlowIsEntered(ctx, _, config) and
|
||||
result = viableImplInCallContextExt(call, ctx)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate fwdFlow(NodeEx node, Configuration config) { fwdFlow(node, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -726,7 +781,8 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate fwdFlowOut(DataFlowCall call, NodeEx out, Cc cc, Configuration config) {
|
||||
exists(ReturnPosition pos |
|
||||
fwdFlowReturnPosition(pos, cc, config) and
|
||||
@@ -740,17 +796,6 @@ private module Stage1 implements StageSig {
|
||||
fwdFlowOut(call, out, true, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an argument to `call` is reached in the flow covered by `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) {
|
||||
exists(ArgNodeEx arg |
|
||||
fwdFlow(arg, cc, config) and
|
||||
viableParamArgEx(call, _, arg)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate stateStepFwd(FlowState state1, FlowState state2, Configuration config) {
|
||||
exists(NodeEx node1 |
|
||||
additionalLocalStateStep(node1, state1, _, state2, config) or
|
||||
@@ -817,13 +862,8 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(DataFlowCall call |
|
||||
revFlowIn(call, node, false, config) and
|
||||
toReturn = false
|
||||
or
|
||||
revFlowInToReturn(call, node, config) and
|
||||
revFlowIsReturned(call, toReturn, config)
|
||||
)
|
||||
revFlowIn(_, node, false, config) and
|
||||
toReturn = false
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(ReturnPosition pos |
|
||||
@@ -831,6 +871,12 @@ private module Stage1 implements StageSig {
|
||||
node.(RetNodeEx).getReturnPosition() = pos and
|
||||
toReturn = true
|
||||
)
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call |
|
||||
revFlowInToReturn(call, node, config) and
|
||||
revFlowIsReturned(call, toReturn, config)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -886,11 +932,11 @@ private module Stage1 implements StageSig {
|
||||
additional predicate viableParamArgNodeCandFwd1(
|
||||
DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
|
||||
) {
|
||||
viableParamArgEx(call, p, arg) and
|
||||
fwdFlow(arg, config)
|
||||
fwdFlowIn(call, arg, _, p, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate revFlowIn(
|
||||
DataFlowCall call, ArgNodeEx arg, boolean toReturn, Configuration config
|
||||
) {
|
||||
@@ -1223,7 +1269,16 @@ private module MkStage<StageSig PrevStage> {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail);
|
||||
|
||||
Content getHeadContent(Ap ap);
|
||||
/**
|
||||
* An approximation of `Content` that corresponds to the precision level of
|
||||
* `Ap`, such that the mappings from both `Ap` and `Content` to this type
|
||||
* are functional.
|
||||
*/
|
||||
class ApHeadContent;
|
||||
|
||||
ApHeadContent getHeadContent(Ap ap);
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c);
|
||||
|
||||
class ApOption;
|
||||
|
||||
@@ -1471,20 +1526,20 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
}
|
||||
|
||||
private class ApNonNil instanceof Ap {
|
||||
pragma[nomagic]
|
||||
ApNonNil() { not this instanceof ApNil }
|
||||
|
||||
string toString() { result = "" }
|
||||
pragma[nomagic]
|
||||
private predicate readStepCand(
|
||||
NodeEx node1, ApHeadContent apc, Content c, NodeEx node2, Configuration config
|
||||
) {
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
apc = projectToHeadContent(c)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead0(
|
||||
NodeEx node1, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ApNonNil ap,
|
||||
Configuration config
|
||||
bindingset[node1, apc]
|
||||
pragma[inline_late]
|
||||
private predicate readStepCand0(
|
||||
NodeEx node1, ApHeadContent apc, Content c, NodeEx node2, Configuration config
|
||||
) {
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, _, _, config)
|
||||
readStepCand(node1, apc, c, node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1492,9 +1547,11 @@ private module MkStage<StageSig PrevStage> {
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
ParamNodeOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
getHeadContent(ap) = c
|
||||
exists(ApHeadContent apc |
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
apc = getHeadContent(ap) and
|
||||
readStepCand0(node1, apc, c, node2, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -2068,8 +2125,12 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) }
|
||||
|
||||
class ApHeadContent = Unit;
|
||||
|
||||
pragma[inline]
|
||||
Content getHeadContent(Ap ap) { exists(result) and ap = true }
|
||||
ApHeadContent getHeadContent(Ap ap) { exists(result) and ap = true }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { any() }
|
||||
|
||||
class ApOption = BooleanOption;
|
||||
|
||||
@@ -2333,8 +2394,12 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result.getAHead() = tc and exists(tail) }
|
||||
|
||||
class ApHeadContent = ContentApprox;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getAHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
predicate projectToHeadContent = getContentApprox/1;
|
||||
|
||||
class ApOption = ApproxAccessPathFrontOption;
|
||||
|
||||
@@ -2409,8 +2474,12 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) }
|
||||
|
||||
class ApHeadContent = Content;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { result = c }
|
||||
|
||||
class ApOption = AccessPathFrontOption;
|
||||
|
||||
@@ -2739,8 +2808,12 @@ private module Stage5Param implements MkStage<Stage4>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) }
|
||||
|
||||
class ApHeadContent = Content;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { result = c }
|
||||
|
||||
class ApOption = AccessPathApproxOption;
|
||||
|
||||
|
||||
@@ -707,8 +707,8 @@ private module Cached {
|
||||
* Gets a viable dispatch target of `call` in the context `ctx`. This is
|
||||
* restricted to those `call`s for which a context might make a difference.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private DataFlowCallable viableImplInCallContextExt(DataFlowCall call, DataFlowCall ctx) {
|
||||
cached
|
||||
DataFlowCallable viableImplInCallContextExt(DataFlowCall call, DataFlowCall ctx) {
|
||||
result = viableImplInCallContext(call, ctx) and
|
||||
result = viableCallable(call)
|
||||
or
|
||||
@@ -1391,6 +1391,9 @@ class TypedContentApprox extends MkTypedContentApprox {
|
||||
/** Gets a typed content approximated by this value. */
|
||||
TypedContent getATypedContent() { result = getATypedContent(this) }
|
||||
|
||||
/** Gets the content. */
|
||||
ContentApprox getContent() { result = c }
|
||||
|
||||
/** Gets the container type. */
|
||||
DataFlowType getContainerType() { result = t }
|
||||
|
||||
@@ -1408,6 +1411,8 @@ abstract class ApproxAccessPathFront extends TApproxAccessPathFront {
|
||||
|
||||
abstract boolean toBoolNonEmpty();
|
||||
|
||||
TypedContentApprox getHead() { this = TApproxFrontHead(result) }
|
||||
|
||||
pragma[nomagic]
|
||||
TypedContent getAHead() {
|
||||
exists(TypedContentApprox cont |
|
||||
|
||||
@@ -0,0 +1,263 @@
|
||||
private import cpp
|
||||
private import experimental.semmle.code.cpp.models.interfaces.SimpleRangeAnalysisExpr
|
||||
private import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils
|
||||
|
||||
float evaluateConstantExpr(Expr e) {
|
||||
result = e.getValue().toFloat()
|
||||
or
|
||||
// This handles when a constant value is put into a variable
|
||||
// and the variable is used later
|
||||
exists(SsaDefinition defn, StackVariable sv |
|
||||
defn.getAUse(sv) = e and
|
||||
result = defn.getDefiningValue(sv).getValue().toFloat()
|
||||
)
|
||||
}
|
||||
|
||||
// If the constant right operand is negative or is greater than or equal to the number of
|
||||
// bits in the left operands type, then the result is undefined (except on the IA-32
|
||||
// architecture where the shift value is masked with 0b00011111, but we can't
|
||||
// assume the architecture).
|
||||
bindingset[val]
|
||||
private predicate isValidShiftExprShift(float val, Expr l) {
|
||||
val >= 0 and
|
||||
// We use getFullyConverted because the spec says to use the *promoted* left operand
|
||||
val < (l.getFullyConverted().getUnderlyingType().getSize() * 8)
|
||||
}
|
||||
|
||||
bindingset[val, shift, max_val]
|
||||
private predicate canLShiftOverflow(int val, int shift, int max_val) {
|
||||
// val << shift = val * 2^shift > max_val => val > max_val/2^shift = max_val >> b
|
||||
val > max_val.bitShiftRight(shift)
|
||||
}
|
||||
|
||||
/**
|
||||
* A range analysis expression consisting of the `>>` or `>>=` operator when at least
|
||||
* one operand is a constant (and if the right operand is a constant, it must be "valid"
|
||||
* (see `isValidShiftExprShift`)). When handling any undefined behavior, it leaves the
|
||||
* values unconstrained. From the C++ standard: "The behavior is undefined if the right
|
||||
* operand is negative, or greater than or equal to the length in bits of the promoted
|
||||
* left operand. The value of E1 >> E2 is E1 right-shifted E2 bit positions. If E1 has an
|
||||
* unsigned type or if E1 has a signed type and a non-negative value, the value of the
|
||||
* result is the integral part of the quotient of E1/2^E2. If E1 has a signed type and a
|
||||
* negative value, the resulting value is implementation-defined."
|
||||
*/
|
||||
class ConstantRShiftExprRange extends SimpleRangeAnalysisExpr {
|
||||
/**
|
||||
* Holds for `a >> b` or `a >>= b` in one of the following two cases:
|
||||
* 1. `a` is a constant and `b` is not
|
||||
* 2. `b` is constant
|
||||
*
|
||||
* We don't handle the case where `a` and `b` are both non-constant values.
|
||||
*/
|
||||
ConstantRShiftExprRange() {
|
||||
getUnspecifiedType() instanceof IntegralType and
|
||||
exists(Expr l, Expr r |
|
||||
l = this.(RShiftExpr).getLeftOperand() and
|
||||
r = this.(RShiftExpr).getRightOperand()
|
||||
or
|
||||
l = this.(AssignRShiftExpr).getLValue() and
|
||||
r = this.(AssignRShiftExpr).getRValue()
|
||||
|
|
||||
l.getUnspecifiedType() instanceof IntegralType and
|
||||
r.getUnspecifiedType() instanceof IntegralType and
|
||||
(
|
||||
// If the left operand is a constant, verify that the right operand is not a constant
|
||||
exists(evaluateConstantExpr(l)) and not exists(evaluateConstantExpr(r))
|
||||
or
|
||||
// If the right operand is a constant, check if it is a valid shift expression
|
||||
exists(float constROp |
|
||||
constROp = evaluateConstantExpr(r) and isValidShiftExprShift(constROp, l)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
Expr getLeftOperand() {
|
||||
result = this.(RShiftExpr).getLeftOperand() or
|
||||
result = this.(AssignRShiftExpr).getLValue()
|
||||
}
|
||||
|
||||
Expr getRightOperand() {
|
||||
result = this.(RShiftExpr).getRightOperand() or
|
||||
result = this.(AssignRShiftExpr).getRValue()
|
||||
}
|
||||
|
||||
override float getLowerBounds() {
|
||||
exists(int lLower, int lUpper, int rLower, int rUpper |
|
||||
lLower = getFullyConvertedLowerBounds(getLeftOperand()) and
|
||||
lUpper = getFullyConvertedUpperBounds(getLeftOperand()) and
|
||||
rLower = getFullyConvertedLowerBounds(getRightOperand()) and
|
||||
rUpper = getFullyConvertedUpperBounds(getRightOperand()) and
|
||||
lLower <= lUpper and
|
||||
rLower <= rUpper
|
||||
|
|
||||
if
|
||||
lLower < 0
|
||||
or
|
||||
not (
|
||||
isValidShiftExprShift(rLower, getLeftOperand()) and
|
||||
isValidShiftExprShift(rUpper, getLeftOperand())
|
||||
)
|
||||
then
|
||||
// We don't want to deal with shifting negative numbers at the moment,
|
||||
// and a negative shift is implementation defined, so we set the result
|
||||
// to the minimum value
|
||||
result = exprMinVal(this)
|
||||
else
|
||||
// We can get the smallest value by shifting the smallest bound by the largest bound
|
||||
result = lLower.bitShiftRight(rUpper)
|
||||
)
|
||||
}
|
||||
|
||||
override float getUpperBounds() {
|
||||
exists(int lLower, int lUpper, int rLower, int rUpper |
|
||||
lLower = getFullyConvertedLowerBounds(getLeftOperand()) and
|
||||
lUpper = getFullyConvertedUpperBounds(getLeftOperand()) and
|
||||
rLower = getFullyConvertedLowerBounds(getRightOperand()) and
|
||||
rUpper = getFullyConvertedUpperBounds(getRightOperand()) and
|
||||
lLower <= lUpper and
|
||||
rLower <= rUpper
|
||||
|
|
||||
if
|
||||
lLower < 0
|
||||
or
|
||||
not (
|
||||
isValidShiftExprShift(rLower, getLeftOperand()) and
|
||||
isValidShiftExprShift(rUpper, getLeftOperand())
|
||||
)
|
||||
then
|
||||
// We don't want to deal with shifting negative numbers at the moment,
|
||||
// and a negative shift is implementation defined, so we set the result
|
||||
// to the maximum value
|
||||
result = exprMaxVal(this)
|
||||
else
|
||||
// We can get the largest value by shifting the largest bound by the smallest bound
|
||||
result = lUpper.bitShiftRight(rLower)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate dependsOnChild(Expr child) {
|
||||
child = getLeftOperand() or child = getRightOperand()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A range analysis expression consisting of the `<<` or `<<=` operator when at least
|
||||
* one operand is a constant (and if the right operand is a constant, it must be "valid"
|
||||
* (see `isValidShiftExprShift`)). When handling any undefined behavior, it leaves the
|
||||
* values unconstrained. From the C++ standard: "The behavior is undefined if the right
|
||||
* operand is negative, or greater than or equal to the length in bits of the promoted left operand.
|
||||
* The value of E1 << E2 is E1 left-shifted E2 bit positions; vacated bits are zero-filled. If E1
|
||||
* has an unsigned type, the value of the result is E1 x 2 E2, reduced modulo one more than the
|
||||
* maximum value representable in the result type. Otherwise, if E1 has a signed type and
|
||||
* non-negative value, and E1 x 2 E2 is representable in the corresponding unsigned type of the
|
||||
* result type, then that value, converted to the result type, is the resulting value; otherwise,
|
||||
* the behavior is undefined."
|
||||
*/
|
||||
class ConstantLShiftExprRange extends SimpleRangeAnalysisExpr {
|
||||
/**
|
||||
* Holds for `a << b` or `a <<= b` in one of the following two cases:
|
||||
* 1. `a` is a constant and `b` is not
|
||||
* 2. `b` is constant
|
||||
*
|
||||
* We don't handle the case where `a` and `b` are both non-constant values.
|
||||
*/
|
||||
ConstantLShiftExprRange() {
|
||||
getUnspecifiedType() instanceof IntegralType and
|
||||
exists(Expr l, Expr r |
|
||||
l = this.(LShiftExpr).getLeftOperand() and
|
||||
r = this.(LShiftExpr).getRightOperand()
|
||||
or
|
||||
l = this.(AssignLShiftExpr).getLValue() and
|
||||
r = this.(AssignLShiftExpr).getRValue()
|
||||
|
|
||||
l.getUnspecifiedType() instanceof IntegralType and
|
||||
r.getUnspecifiedType() instanceof IntegralType and
|
||||
(
|
||||
// If the left operand is a constant, verify that the right operand is not a constant
|
||||
exists(evaluateConstantExpr(l)) and not exists(evaluateConstantExpr(r))
|
||||
or
|
||||
// If the right operand is a constant, check if it is a valid shift expression
|
||||
exists(float constROp |
|
||||
constROp = evaluateConstantExpr(r) and isValidShiftExprShift(constROp, l)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
Expr getLeftOperand() {
|
||||
result = this.(LShiftExpr).getLeftOperand() or
|
||||
result = this.(AssignLShiftExpr).getLValue()
|
||||
}
|
||||
|
||||
Expr getRightOperand() {
|
||||
result = this.(LShiftExpr).getRightOperand() or
|
||||
result = this.(AssignLShiftExpr).getRValue()
|
||||
}
|
||||
|
||||
override float getLowerBounds() {
|
||||
exists(int lLower, int lUpper, int rLower, int rUpper |
|
||||
lLower = getFullyConvertedLowerBounds(getLeftOperand()) and
|
||||
lUpper = getFullyConvertedUpperBounds(getLeftOperand()) and
|
||||
rLower = getFullyConvertedLowerBounds(getRightOperand()) and
|
||||
rUpper = getFullyConvertedUpperBounds(getRightOperand()) and
|
||||
lLower <= lUpper and
|
||||
rLower <= rUpper
|
||||
|
|
||||
if
|
||||
lLower < 0
|
||||
or
|
||||
not (
|
||||
isValidShiftExprShift(rLower, getLeftOperand()) and
|
||||
isValidShiftExprShift(rUpper, getLeftOperand())
|
||||
)
|
||||
then
|
||||
// We don't want to deal with shifting negative numbers at the moment,
|
||||
// and a negative shift is undefined, so we set to the minimum value
|
||||
result = exprMinVal(this)
|
||||
else
|
||||
// If we have `0b01010000 << [0, 2]`, the max value for 8 bits is 0b10100000
|
||||
// (a shift of 1) but doing a shift by the upper bound would give 0b01000000.
|
||||
// So if the left shift operation causes an overflow, we just assume the max value
|
||||
// If necessary, we may be able to improve this bound in the future
|
||||
if canLShiftOverflow(lUpper, rUpper, exprMaxVal(this))
|
||||
then result = exprMinVal(this)
|
||||
else result = lLower.bitShiftLeft(rLower)
|
||||
)
|
||||
}
|
||||
|
||||
override float getUpperBounds() {
|
||||
exists(int lLower, int lUpper, int rLower, int rUpper |
|
||||
lLower = getFullyConvertedLowerBounds(getLeftOperand()) and
|
||||
lUpper = getFullyConvertedUpperBounds(getLeftOperand()) and
|
||||
rLower = getFullyConvertedLowerBounds(getRightOperand()) and
|
||||
rUpper = getFullyConvertedUpperBounds(getRightOperand()) and
|
||||
lLower <= lUpper and
|
||||
rLower <= rUpper
|
||||
|
|
||||
if
|
||||
lLower < 0
|
||||
or
|
||||
not (
|
||||
isValidShiftExprShift(rLower, getLeftOperand()) and
|
||||
isValidShiftExprShift(rUpper, getLeftOperand())
|
||||
)
|
||||
then
|
||||
// We don't want to deal with shifting negative numbers at the moment,
|
||||
// and a negative shift is undefined, so we set it to the maximum value
|
||||
result = exprMaxVal(this)
|
||||
else
|
||||
// If we have `0b01010000 << [0, 2]`, the max value for 8 bits is 0b10100000
|
||||
// (a shift of 1) but doing a shift by the upper bound would give 0b01000000.
|
||||
// So if the left shift operation causes an overflow, we just assume the max value
|
||||
// If necessary, we may be able to improve this bound in the future
|
||||
if canLShiftOverflow(lUpper, rUpper, exprMaxVal(this))
|
||||
then result = exprMaxVal(this)
|
||||
else result = lUpper.bitShiftLeft(rUpper)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate dependsOnChild(Expr child) {
|
||||
child = getLeftOperand() or child = getRightOperand()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
private import RangeAnalysisStage
|
||||
|
||||
module FloatDelta implements DeltaSig {
|
||||
class Delta = float;
|
||||
|
||||
bindingset[d]
|
||||
bindingset[result]
|
||||
float toFloat(Delta d) { result = d }
|
||||
|
||||
bindingset[d]
|
||||
bindingset[result]
|
||||
int toInt(Delta d) { result = d }
|
||||
|
||||
bindingset[n]
|
||||
bindingset[result]
|
||||
Delta fromInt(int n) { result = n }
|
||||
|
||||
bindingset[f]
|
||||
Delta fromFloat(float f) {
|
||||
result =
|
||||
min(float diff, float res |
|
||||
diff = (res - f) and res = f.ceil()
|
||||
or
|
||||
diff = (f - res) and res = f.floor()
|
||||
|
|
||||
res order by diff
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -14,321 +14,328 @@ private import ModulusAnalysisSpecific::Private
|
||||
private import experimental.semmle.code.cpp.semantic.Semantic
|
||||
private import ConstantAnalysis
|
||||
private import RangeUtils
|
||||
private import RangeAnalysisStage
|
||||
|
||||
/**
|
||||
* Holds if `e + delta` equals `v` at `pos`.
|
||||
*/
|
||||
private predicate valueFlowStepSsa(SemSsaVariable v, SemSsaReadPosition pos, SemExpr e, int delta) {
|
||||
semSsaUpdateStep(v, e, delta) and pos.hasReadOfVar(v)
|
||||
or
|
||||
exists(SemGuard guard, boolean testIsTrue |
|
||||
pos.hasReadOfVar(v) and
|
||||
guard = semEqFlowCond(v, e, delta, true, testIsTrue) and
|
||||
semGuardDirectlyControlsSsaRead(guard, pos, testIsTrue)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `add` is the addition of `larg` and `rarg`, neither of which are
|
||||
* `ConstantIntegerExpr`s.
|
||||
*/
|
||||
private predicate nonConstAddition(SemExpr add, SemExpr larg, SemExpr rarg) {
|
||||
exists(SemAddExpr a | a = add |
|
||||
larg = a.getLeftOperand() and
|
||||
rarg = a.getRightOperand()
|
||||
) and
|
||||
not larg instanceof SemConstantIntegerExpr and
|
||||
not rarg instanceof SemConstantIntegerExpr
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `sub` is the subtraction of `larg` and `rarg`, where `rarg` is not
|
||||
* a `ConstantIntegerExpr`.
|
||||
*/
|
||||
private predicate nonConstSubtraction(SemExpr sub, SemExpr larg, SemExpr rarg) {
|
||||
exists(SemSubExpr s | s = sub |
|
||||
larg = s.getLeftOperand() and
|
||||
rarg = s.getRightOperand()
|
||||
) and
|
||||
not rarg instanceof SemConstantIntegerExpr
|
||||
}
|
||||
|
||||
/** Gets an expression that is the remainder modulo `mod` of `arg`. */
|
||||
private SemExpr modExpr(SemExpr arg, int mod) {
|
||||
exists(SemRemExpr rem |
|
||||
result = rem and
|
||||
arg = rem.getLeftOperand() and
|
||||
rem.getRightOperand().(SemConstantIntegerExpr).getIntValue() = mod and
|
||||
mod >= 2
|
||||
)
|
||||
or
|
||||
exists(SemConstantIntegerExpr c |
|
||||
mod = 2.pow([1 .. 30]) and
|
||||
c.getIntValue() = mod - 1 and
|
||||
result.(SemBitAndExpr).hasOperands(arg, c)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a guard that tests whether `v` is congruent with `val` modulo `mod` on
|
||||
* its `testIsTrue` branch.
|
||||
*/
|
||||
private SemGuard moduloCheck(SemSsaVariable v, int val, int mod, boolean testIsTrue) {
|
||||
exists(SemExpr rem, SemConstantIntegerExpr c, int r, boolean polarity |
|
||||
result.isEquality(rem, c, polarity) and
|
||||
c.getIntValue() = r and
|
||||
rem = modExpr(v.getAUse(), mod) and
|
||||
(
|
||||
testIsTrue = polarity and val = r
|
||||
or
|
||||
testIsTrue = polarity.booleanNot() and
|
||||
mod = 2 and
|
||||
val = 1 - r and
|
||||
(r = 0 or r = 1)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a guard ensures that `v` at `pos` is congruent with `val` modulo `mod`.
|
||||
*/
|
||||
private predicate moduloGuardedRead(SemSsaVariable v, SemSsaReadPosition pos, int val, int mod) {
|
||||
exists(SemGuard guard, boolean testIsTrue |
|
||||
pos.hasReadOfVar(v) and
|
||||
guard = moduloCheck(v, val, mod, testIsTrue) and
|
||||
semGuardControlsSsaRead(guard, pos, testIsTrue)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `factor` is a power of 2 that divides `mask`. */
|
||||
bindingset[mask]
|
||||
private predicate andmaskFactor(int mask, int factor) {
|
||||
mask % factor = 0 and
|
||||
factor = 2.pow([1 .. 30])
|
||||
}
|
||||
|
||||
/** Holds if `e` is evenly divisible by `factor`. */
|
||||
private predicate evenlyDivisibleExpr(SemExpr e, int factor) {
|
||||
exists(SemConstantIntegerExpr c, int k | k = c.getIntValue() |
|
||||
e.(SemMulExpr).getAnOperand() = c and factor = k.abs() and factor >= 2
|
||||
or
|
||||
e.(SemShiftLeftExpr).getRightOperand() = c and factor = 2.pow(k) and k > 0
|
||||
or
|
||||
e.(SemBitAndExpr).getAnOperand() = c and factor = max(int f | andmaskFactor(k, f))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `rix` is the number of input edges to `phi`.
|
||||
*/
|
||||
private predicate maxPhiInputRank(SemSsaPhiNode phi, int rix) {
|
||||
rix = max(int r | rankedPhiInput(phi, _, _, r))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the remainder of `val` modulo `mod`.
|
||||
*
|
||||
* For `mod = 0` the result equals `val` and for `mod > 1` the result is within
|
||||
* the range `[0 .. mod-1]`.
|
||||
*/
|
||||
bindingset[val, mod]
|
||||
private int remainder(int val, int mod) {
|
||||
mod = 0 and result = val
|
||||
or
|
||||
mod > 1 and result = ((val % mod) + mod) % mod
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `inp` is an input to `phi` and equals `phi` modulo `mod` along `edge`.
|
||||
*/
|
||||
private predicate phiSelfModulus(
|
||||
SemSsaPhiNode phi, SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge, int mod
|
||||
) {
|
||||
exists(SemSsaBound phibound, int v, int m |
|
||||
edge.phiInput(phi, inp) and
|
||||
phibound.getAVariable() = phi and
|
||||
ssaModulus(inp, edge, phibound, v, m) and
|
||||
mod = m.gcd(v) and
|
||||
mod != 1
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `b + val` modulo `mod` is a candidate congruence class for `phi`.
|
||||
*/
|
||||
private predicate phiModulusInit(SemSsaPhiNode phi, SemBound b, int val, int mod) {
|
||||
exists(SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge |
|
||||
edge.phiInput(phi, inp) and
|
||||
ssaModulus(inp, edge, b, val, mod)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if all inputs to `phi` numbered `1` to `rix` are equal to `b + val` modulo `mod`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate phiModulusRankStep(SemSsaPhiNode phi, SemBound b, int val, int mod, int rix) {
|
||||
/*
|
||||
* base case. If any phi input is equal to `b + val` modulo `mod`, that's a potential congruence
|
||||
* class for the phi node.
|
||||
module ModulusAnalysis<DeltaSig D, BoundSig<D> Bounds, UtilSig<D> U> {
|
||||
/**
|
||||
* Holds if `e + delta` equals `v` at `pos`.
|
||||
*/
|
||||
private predicate valueFlowStepSsa(SemSsaVariable v, SemSsaReadPosition pos, SemExpr e, int delta) {
|
||||
U::semSsaUpdateStep(v, e, D::fromInt(delta)) and pos.hasReadOfVar(v)
|
||||
or
|
||||
exists(SemGuard guard, boolean testIsTrue |
|
||||
pos.hasReadOfVar(v) and
|
||||
guard = U::semEqFlowCond(v, e, D::fromInt(delta), true, testIsTrue) and
|
||||
semGuardDirectlyControlsSsaRead(guard, pos, testIsTrue)
|
||||
)
|
||||
}
|
||||
|
||||
rix = 0 and
|
||||
phiModulusInit(phi, b, val, mod)
|
||||
or
|
||||
exists(SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge, int v1, int m1 |
|
||||
mod != 1 and
|
||||
val = remainder(v1, mod)
|
||||
|
|
||||
/**
|
||||
* Holds if `add` is the addition of `larg` and `rarg`, neither of which are
|
||||
* `ConstantIntegerExpr`s.
|
||||
*/
|
||||
private predicate nonConstAddition(SemExpr add, SemExpr larg, SemExpr rarg) {
|
||||
exists(SemAddExpr a | a = add |
|
||||
larg = a.getLeftOperand() and
|
||||
rarg = a.getRightOperand()
|
||||
) and
|
||||
not larg instanceof SemConstantIntegerExpr and
|
||||
not rarg instanceof SemConstantIntegerExpr
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `sub` is the subtraction of `larg` and `rarg`, where `rarg` is not
|
||||
* a `ConstantIntegerExpr`.
|
||||
*/
|
||||
private predicate nonConstSubtraction(SemExpr sub, SemExpr larg, SemExpr rarg) {
|
||||
exists(SemSubExpr s | s = sub |
|
||||
larg = s.getLeftOperand() and
|
||||
rarg = s.getRightOperand()
|
||||
) and
|
||||
not rarg instanceof SemConstantIntegerExpr
|
||||
}
|
||||
|
||||
/** Gets an expression that is the remainder modulo `mod` of `arg`. */
|
||||
private SemExpr modExpr(SemExpr arg, int mod) {
|
||||
exists(SemRemExpr rem |
|
||||
result = rem and
|
||||
arg = rem.getLeftOperand() and
|
||||
rem.getRightOperand().(SemConstantIntegerExpr).getIntValue() = mod and
|
||||
mod >= 2
|
||||
)
|
||||
or
|
||||
exists(SemConstantIntegerExpr c |
|
||||
mod = 2.pow([1 .. 30]) and
|
||||
c.getIntValue() = mod - 1 and
|
||||
result.(SemBitAndExpr).hasOperands(arg, c)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a guard that tests whether `v` is congruent with `val` modulo `mod` on
|
||||
* its `testIsTrue` branch.
|
||||
*/
|
||||
private SemGuard moduloCheck(SemSsaVariable v, int val, int mod, boolean testIsTrue) {
|
||||
exists(SemExpr rem, SemConstantIntegerExpr c, int r, boolean polarity |
|
||||
result.isEquality(rem, c, polarity) and
|
||||
c.getIntValue() = r and
|
||||
rem = modExpr(v.getAUse(), mod) and
|
||||
(
|
||||
testIsTrue = polarity and val = r
|
||||
or
|
||||
testIsTrue = polarity.booleanNot() and
|
||||
mod = 2 and
|
||||
val = 1 - r and
|
||||
(r = 0 or r = 1)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a guard ensures that `v` at `pos` is congruent with `val` modulo `mod`.
|
||||
*/
|
||||
private predicate moduloGuardedRead(SemSsaVariable v, SemSsaReadPosition pos, int val, int mod) {
|
||||
exists(SemGuard guard, boolean testIsTrue |
|
||||
pos.hasReadOfVar(v) and
|
||||
guard = moduloCheck(v, val, mod, testIsTrue) and
|
||||
semGuardControlsSsaRead(guard, pos, testIsTrue)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `factor` is a power of 2 that divides `mask`. */
|
||||
bindingset[mask]
|
||||
private predicate andmaskFactor(int mask, int factor) {
|
||||
mask % factor = 0 and
|
||||
factor = 2.pow([1 .. 30])
|
||||
}
|
||||
|
||||
/** Holds if `e` is evenly divisible by `factor`. */
|
||||
private predicate evenlyDivisibleExpr(SemExpr e, int factor) {
|
||||
exists(SemConstantIntegerExpr c, int k | k = c.getIntValue() |
|
||||
e.(SemMulExpr).getAnOperand() = c and factor = k.abs() and factor >= 2
|
||||
or
|
||||
e.(SemShiftLeftExpr).getRightOperand() = c and factor = 2.pow(k) and k > 0
|
||||
or
|
||||
e.(SemBitAndExpr).getAnOperand() = c and factor = max(int f | andmaskFactor(k, f))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `rix` is the number of input edges to `phi`.
|
||||
*/
|
||||
private predicate maxPhiInputRank(SemSsaPhiNode phi, int rix) {
|
||||
rix = max(int r | rankedPhiInput(phi, _, _, r))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the remainder of `val` modulo `mod`.
|
||||
*
|
||||
* For `mod = 0` the result equals `val` and for `mod > 1` the result is within
|
||||
* the range `[0 .. mod-1]`.
|
||||
*/
|
||||
bindingset[val, mod]
|
||||
private int remainder(int val, int mod) {
|
||||
mod = 0 and result = val
|
||||
or
|
||||
mod > 1 and result = ((val % mod) + mod) % mod
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `inp` is an input to `phi` and equals `phi` modulo `mod` along `edge`.
|
||||
*/
|
||||
private predicate phiSelfModulus(
|
||||
SemSsaPhiNode phi, SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge, int mod
|
||||
) {
|
||||
exists(Bounds::SemSsaBound phibound, int v, int m |
|
||||
edge.phiInput(phi, inp) and
|
||||
phibound.getAVariable() = phi and
|
||||
ssaModulus(inp, edge, phibound, v, m) and
|
||||
mod = m.gcd(v) and
|
||||
mod != 1
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `b + val` modulo `mod` is a candidate congruence class for `phi`.
|
||||
*/
|
||||
private predicate phiModulusInit(SemSsaPhiNode phi, Bounds::SemBound b, int val, int mod) {
|
||||
exists(SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge |
|
||||
edge.phiInput(phi, inp) and
|
||||
ssaModulus(inp, edge, b, val, mod)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if all inputs to `phi` numbered `1` to `rix` are equal to `b + val` modulo `mod`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate phiModulusRankStep(
|
||||
SemSsaPhiNode phi, Bounds::SemBound b, int val, int mod, int rix
|
||||
) {
|
||||
/*
|
||||
* Recursive case. If `inp` = `b + v2` mod `m2`, we combine that with the preceding potential
|
||||
* congruence class `b + v1` mod `m1`. The result will be the congruence class of `v1` modulo
|
||||
* the greatest common denominator of `m1`, `m2`, and `v1 - v2`.
|
||||
* base case. If any phi input is equal to `b + val` modulo `mod`, that's a potential congruence
|
||||
* class for the phi node.
|
||||
*/
|
||||
|
||||
exists(int v2, int m2 |
|
||||
rankedPhiInput(pragma[only_bind_out](phi), inp, edge, rix) and
|
||||
phiModulusRankStep(phi, b, v1, m1, rix - 1) and
|
||||
ssaModulus(inp, edge, b, v2, m2) and
|
||||
mod = m1.gcd(m2).gcd(v1 - v2)
|
||||
rix = 0 and
|
||||
phiModulusInit(phi, b, val, mod)
|
||||
or
|
||||
exists(SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge, int v1, int m1 |
|
||||
mod != 1 and
|
||||
val = remainder(v1, mod)
|
||||
|
|
||||
/*
|
||||
* Recursive case. If `inp` = `b + v2` mod `m2`, we combine that with the preceding potential
|
||||
* congruence class `b + v1` mod `m1`. The result will be the congruence class of `v1` modulo
|
||||
* the greatest common denominator of `m1`, `m2`, and `v1 - v2`.
|
||||
*/
|
||||
|
||||
exists(int v2, int m2 |
|
||||
rankedPhiInput(pragma[only_bind_out](phi), inp, edge, rix) and
|
||||
phiModulusRankStep(phi, b, v1, m1, rix - 1) and
|
||||
ssaModulus(inp, edge, b, v2, m2) and
|
||||
mod = m1.gcd(m2).gcd(v1 - v2)
|
||||
)
|
||||
or
|
||||
/*
|
||||
* Recursive case. If `inp` = `phi` mod `m2`, we combine that with the preceding potential
|
||||
* congruence class `b + v1` mod `m1`. The result will be a congruence class modulo the greatest
|
||||
* common denominator of `m1` and `m2`.
|
||||
*/
|
||||
|
||||
exists(int m2 |
|
||||
rankedPhiInput(phi, inp, edge, rix) and
|
||||
phiModulusRankStep(phi, b, v1, m1, rix - 1) and
|
||||
phiSelfModulus(phi, inp, edge, m2) and
|
||||
mod = m1.gcd(m2)
|
||||
)
|
||||
)
|
||||
or
|
||||
/*
|
||||
* Recursive case. If `inp` = `phi` mod `m2`, we combine that with the preceding potential
|
||||
* congruence class `b + v1` mod `m1`. The result will be a congruence class modulo the greatest
|
||||
* common denominator of `m1` and `m2`.
|
||||
*/
|
||||
}
|
||||
|
||||
exists(int m2 |
|
||||
rankedPhiInput(phi, inp, edge, rix) and
|
||||
phiModulusRankStep(phi, b, v1, m1, rix - 1) and
|
||||
phiSelfModulus(phi, inp, edge, m2) and
|
||||
mod = m1.gcd(m2)
|
||||
/**
|
||||
* Holds if `phi` is equal to `b + val` modulo `mod`.
|
||||
*/
|
||||
private predicate phiModulus(SemSsaPhiNode phi, Bounds::SemBound b, int val, int mod) {
|
||||
exists(int r |
|
||||
maxPhiInputRank(phi, r) and
|
||||
phiModulusRankStep(phi, b, val, mod, r)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `phi` is equal to `b + val` modulo `mod`.
|
||||
*/
|
||||
private predicate phiModulus(SemSsaPhiNode phi, SemBound b, int val, int mod) {
|
||||
exists(int r |
|
||||
maxPhiInputRank(phi, r) and
|
||||
phiModulusRankStep(phi, b, val, mod, r)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `v` at `pos` is equal to `b + val` modulo `mod`.
|
||||
*/
|
||||
private predicate ssaModulus(SemSsaVariable v, SemSsaReadPosition pos, SemBound b, int val, int mod) {
|
||||
phiModulus(v, b, val, mod) and pos.hasReadOfVar(v)
|
||||
or
|
||||
b.(SemSsaBound).getAVariable() = v and pos.hasReadOfVar(v) and val = 0 and mod = 0
|
||||
or
|
||||
exists(SemExpr e, int val0, int delta |
|
||||
semExprModulus(e, b, val0, mod) and
|
||||
valueFlowStepSsa(v, pos, e, delta) and
|
||||
val = remainder(val0 + delta, mod)
|
||||
)
|
||||
or
|
||||
moduloGuardedRead(v, pos, val, mod) and b instanceof SemZeroBound
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `e` is equal to `b + val` modulo `mod`.
|
||||
*
|
||||
* There are two cases for the modulus:
|
||||
* - `mod = 0`: The equality `e = b + val` is an ordinary equality.
|
||||
* - `mod > 1`: `val` lies within the range `[0 .. mod-1]`.
|
||||
*/
|
||||
cached
|
||||
predicate semExprModulus(SemExpr e, SemBound b, int val, int mod) {
|
||||
not ignoreExprModulus(e) and
|
||||
(
|
||||
e = b.getExpr(val) and mod = 0
|
||||
/**
|
||||
* Holds if `v` at `pos` is equal to `b + val` modulo `mod`.
|
||||
*/
|
||||
private predicate ssaModulus(
|
||||
SemSsaVariable v, SemSsaReadPosition pos, Bounds::SemBound b, int val, int mod
|
||||
) {
|
||||
phiModulus(v, b, val, mod) and pos.hasReadOfVar(v)
|
||||
or
|
||||
evenlyDivisibleExpr(e, mod) and
|
||||
val = 0 and
|
||||
b instanceof SemZeroBound
|
||||
b.(Bounds::SemSsaBound).getAVariable() = v and pos.hasReadOfVar(v) and val = 0 and mod = 0
|
||||
or
|
||||
exists(SemSsaVariable v, SemSsaReadPositionBlock bb |
|
||||
ssaModulus(v, bb, b, val, mod) and
|
||||
e = v.getAUse() and
|
||||
bb.getAnExpr() = e
|
||||
)
|
||||
or
|
||||
exists(SemExpr mid, int val0, int delta |
|
||||
semExprModulus(mid, b, val0, mod) and
|
||||
semValueFlowStep(e, mid, delta) and
|
||||
exists(SemExpr e, int val0, int delta |
|
||||
semExprModulus(e, b, val0, mod) and
|
||||
valueFlowStepSsa(v, pos, e, delta) and
|
||||
val = remainder(val0 + delta, mod)
|
||||
)
|
||||
or
|
||||
exists(SemConditionalExpr cond, int v1, int v2, int m1, int m2 |
|
||||
cond = e and
|
||||
condExprBranchModulus(cond, true, b, v1, m1) and
|
||||
condExprBranchModulus(cond, false, b, v2, m2) and
|
||||
mod = m1.gcd(m2).gcd(v1 - v2) and
|
||||
mod != 1 and
|
||||
val = remainder(v1, mod)
|
||||
)
|
||||
or
|
||||
exists(SemBound b1, SemBound b2, int v1, int v2, int m1, int m2 |
|
||||
addModulus(e, true, b1, v1, m1) and
|
||||
addModulus(e, false, b2, v2, m2) and
|
||||
mod = m1.gcd(m2) and
|
||||
mod != 1 and
|
||||
val = remainder(v1 + v2, mod)
|
||||
|
|
||||
b = b1 and b2 instanceof SemZeroBound
|
||||
moduloGuardedRead(v, pos, val, mod) and b instanceof Bounds::SemZeroBound
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `e` is equal to `b + val` modulo `mod`.
|
||||
*
|
||||
* There are two cases for the modulus:
|
||||
* - `mod = 0`: The equality `e = b + val` is an ordinary equality.
|
||||
* - `mod > 1`: `val` lies within the range `[0 .. mod-1]`.
|
||||
*/
|
||||
cached
|
||||
predicate semExprModulus(SemExpr e, Bounds::SemBound b, int val, int mod) {
|
||||
not ignoreExprModulus(e) and
|
||||
(
|
||||
e = b.getExpr(D::fromInt(val)) and mod = 0
|
||||
or
|
||||
b = b2 and b1 instanceof SemZeroBound
|
||||
evenlyDivisibleExpr(e, mod) and
|
||||
val = 0 and
|
||||
b instanceof Bounds::SemZeroBound
|
||||
or
|
||||
exists(SemSsaVariable v, SemSsaReadPositionBlock bb |
|
||||
ssaModulus(v, bb, b, val, mod) and
|
||||
e = v.getAUse() and
|
||||
bb.getAnExpr() = e
|
||||
)
|
||||
or
|
||||
exists(SemExpr mid, int val0, int delta |
|
||||
semExprModulus(mid, b, val0, mod) and
|
||||
U::semValueFlowStep(e, mid, D::fromInt(delta)) and
|
||||
val = remainder(val0 + delta, mod)
|
||||
)
|
||||
or
|
||||
exists(SemConditionalExpr cond, int v1, int v2, int m1, int m2 |
|
||||
cond = e and
|
||||
condExprBranchModulus(cond, true, b, v1, m1) and
|
||||
condExprBranchModulus(cond, false, b, v2, m2) and
|
||||
mod = m1.gcd(m2).gcd(v1 - v2) and
|
||||
mod != 1 and
|
||||
val = remainder(v1, mod)
|
||||
)
|
||||
or
|
||||
exists(Bounds::SemBound b1, Bounds::SemBound b2, int v1, int v2, int m1, int m2 |
|
||||
addModulus(e, true, b1, v1, m1) and
|
||||
addModulus(e, false, b2, v2, m2) and
|
||||
mod = m1.gcd(m2) and
|
||||
mod != 1 and
|
||||
val = remainder(v1 + v2, mod)
|
||||
|
|
||||
b = b1 and b2 instanceof Bounds::SemZeroBound
|
||||
or
|
||||
b = b2 and b1 instanceof Bounds::SemZeroBound
|
||||
)
|
||||
or
|
||||
exists(int v1, int v2, int m1, int m2 |
|
||||
subModulus(e, true, b, v1, m1) and
|
||||
subModulus(e, false, any(Bounds::SemZeroBound zb), v2, m2) and
|
||||
mod = m1.gcd(m2) and
|
||||
mod != 1 and
|
||||
val = remainder(v1 - v2, mod)
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(int v1, int v2, int m1, int m2 |
|
||||
subModulus(e, true, b, v1, m1) and
|
||||
subModulus(e, false, any(SemZeroBound zb), v2, m2) and
|
||||
mod = m1.gcd(m2) and
|
||||
mod != 1 and
|
||||
val = remainder(v1 - v2, mod)
|
||||
}
|
||||
|
||||
private predicate condExprBranchModulus(
|
||||
SemConditionalExpr cond, boolean branch, Bounds::SemBound b, int val, int mod
|
||||
) {
|
||||
semExprModulus(cond.getBranchExpr(branch), b, val, mod)
|
||||
}
|
||||
|
||||
private predicate addModulus(SemExpr add, boolean isLeft, Bounds::SemBound b, int val, int mod) {
|
||||
exists(SemExpr larg, SemExpr rarg | nonConstAddition(add, larg, rarg) |
|
||||
semExprModulus(larg, b, val, mod) and isLeft = true
|
||||
or
|
||||
semExprModulus(rarg, b, val, mod) and isLeft = false
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private predicate condExprBranchModulus(
|
||||
SemConditionalExpr cond, boolean branch, SemBound b, int val, int mod
|
||||
) {
|
||||
semExprModulus(cond.getBranchExpr(branch), b, val, mod)
|
||||
}
|
||||
|
||||
private predicate addModulus(SemExpr add, boolean isLeft, SemBound b, int val, int mod) {
|
||||
exists(SemExpr larg, SemExpr rarg | nonConstAddition(add, larg, rarg) |
|
||||
semExprModulus(larg, b, val, mod) and isLeft = true
|
||||
or
|
||||
semExprModulus(rarg, b, val, mod) and isLeft = false
|
||||
)
|
||||
}
|
||||
|
||||
private predicate subModulus(SemExpr sub, boolean isLeft, SemBound b, int val, int mod) {
|
||||
exists(SemExpr larg, SemExpr rarg | nonConstSubtraction(sub, larg, rarg) |
|
||||
semExprModulus(larg, b, val, mod) and isLeft = true
|
||||
or
|
||||
semExprModulus(rarg, b, val, mod) and isLeft = false
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `inp` is an input to `phi` along `edge` and this input has index `r`
|
||||
* in an arbitrary 1-based numbering of the input edges to `phi`.
|
||||
*/
|
||||
private predicate rankedPhiInput(
|
||||
SemSsaPhiNode phi, SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge, int r
|
||||
) {
|
||||
edge.phiInput(phi, inp) and
|
||||
edge =
|
||||
rank[r](SemSsaReadPositionPhiInputEdge e |
|
||||
e.phiInput(phi, _)
|
||||
|
|
||||
e order by e.getOrigBlock().getUniqueId()
|
||||
private predicate subModulus(SemExpr sub, boolean isLeft, Bounds::SemBound b, int val, int mod) {
|
||||
exists(SemExpr larg, SemExpr rarg | nonConstSubtraction(sub, larg, rarg) |
|
||||
semExprModulus(larg, b, val, mod) and isLeft = true
|
||||
or
|
||||
semExprModulus(rarg, b, val, mod) and isLeft = false
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `inp` is an input to `phi` along `edge` and this input has index `r`
|
||||
* in an arbitrary 1-based numbering of the input edges to `phi`.
|
||||
*/
|
||||
private predicate rankedPhiInput(
|
||||
SemSsaPhiNode phi, SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge, int r
|
||||
) {
|
||||
edge.phiInput(phi, inp) and
|
||||
edge =
|
||||
rank[r](SemSsaReadPositionPhiInputEdge e |
|
||||
e.phiInput(phi, _)
|
||||
|
|
||||
e order by e.getOrigBlock().getUniqueId()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,830 +1,24 @@
|
||||
/**
|
||||
* Provides classes and predicates for range analysis.
|
||||
*
|
||||
* An inferred bound can either be a specific integer, the abstract value of an
|
||||
* SSA variable, or the abstract value of an interesting expression. The latter
|
||||
* category includes array lengths that are not SSA variables.
|
||||
*
|
||||
* If an inferred bound relies directly on a condition, then this condition is
|
||||
* reported as the reason for the bound.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This library tackles range analysis as a flow problem. Consider e.g.:
|
||||
* ```
|
||||
* len = arr.length;
|
||||
* if (x < len) { ... y = x-1; ... y ... }
|
||||
* ```
|
||||
* In this case we would like to infer `y <= arr.length - 2`, and this is
|
||||
* accomplished by tracking the bound through a sequence of steps:
|
||||
* ```
|
||||
* arr.length --> len = .. --> x < len --> x-1 --> y = .. --> y
|
||||
* ```
|
||||
*
|
||||
* In its simplest form the step relation `E1 --> E2` relates two expressions
|
||||
* such that `E1 <= B` implies `E2 <= B` for any `B` (with a second separate
|
||||
* step relation handling lower bounds). Examples of such steps include
|
||||
* assignments `E2 = E1` and conditions `x <= E1` where `E2` is a use of `x`
|
||||
* guarded by the condition.
|
||||
*
|
||||
* In order to handle subtractions and additions with constants, and strict
|
||||
* comparisons, the step relation is augmented with an integer delta. With this
|
||||
* generalization `E1 --(delta)--> E2` relates two expressions and an integer
|
||||
* such that `E1 <= B` implies `E2 <= B + delta` for any `B`. This corresponds
|
||||
* to the predicate `boundFlowStep`.
|
||||
*
|
||||
* The complete range analysis is then implemented as the transitive closure of
|
||||
* the step relation summing the deltas along the way. If `E1` transitively
|
||||
* steps to `E2`, `delta` is the sum of deltas along the path, and `B` is an
|
||||
* interesting bound equal to the value of `E1` then `E2 <= B + delta`. This
|
||||
* corresponds to the predicate `bounded`.
|
||||
*
|
||||
* Phi nodes need a little bit of extra handling. Consider `x0 = phi(x1, x2)`.
|
||||
* There are essentially two cases:
|
||||
* - If `x1 <= B + d1` and `x2 <= B + d2` then `x0 <= B + max(d1,d2)`.
|
||||
* - If `x1 <= B + d1` and `x2 <= x0 + d2` with `d2 <= 0` then `x0 <= B + d1`.
|
||||
* The first case is for whenever a bound can be proven without taking looping
|
||||
* into account. The second case is relevant when `x2` comes from a back-edge
|
||||
* where we can prove that the variable has been non-increasing through the
|
||||
* loop-iteration as this means that any upper bound that holds prior to the
|
||||
* loop also holds for the variable during the loop.
|
||||
* This generalizes to a phi node with `n` inputs, so if
|
||||
* `x0 = phi(x1, ..., xn)` and `xi <= B + delta` for one of the inputs, then we
|
||||
* also have `x0 <= B + delta` if we can prove either:
|
||||
* - `xj <= B + d` with `d <= delta` or
|
||||
* - `xj <= x0 + d` with `d <= 0`
|
||||
* for each input `xj`.
|
||||
*
|
||||
* As all inferred bounds can be related directly to a path in the source code
|
||||
* the only source of non-termination is if successive redundant (and thereby
|
||||
* increasingly worse) bounds are calculated along a loop in the source code.
|
||||
* We prevent this by weakening the bound to a small finite set of bounds when
|
||||
* a path follows a second back-edge (we postpone weakening till the second
|
||||
* back-edge as a precise bound might require traversing a loop once).
|
||||
*/
|
||||
|
||||
private import RangeAnalysisSpecific as Specific
|
||||
private import RangeAnalysisStage
|
||||
private import RangeAnalysisSpecific
|
||||
private import experimental.semmle.code.cpp.semantic.analysis.FloatDelta
|
||||
private import RangeUtils
|
||||
private import SignAnalysisCommon
|
||||
private import ModulusAnalysis
|
||||
private import experimental.semmle.code.cpp.semantic.Semantic
|
||||
private import ConstantAnalysis
|
||||
private import experimental.semmle.code.cpp.semantic.SemanticBound as SemanticBound
|
||||
|
||||
cached
|
||||
private module RangeAnalysisCache {
|
||||
cached
|
||||
module RangeAnalysisPublic {
|
||||
/**
|
||||
* Holds if `b + delta` is a valid bound for `e`.
|
||||
* - `upper = true` : `e <= b + delta`
|
||||
* - `upper = false` : `e >= b + delta`
|
||||
*
|
||||
* The reason for the bound is given by `reason` and may be either a condition
|
||||
* or `NoReason` if the bound was proven directly without the use of a bounding
|
||||
* condition.
|
||||
*/
|
||||
cached
|
||||
predicate semBounded(SemExpr e, SemBound b, int delta, boolean upper, SemReason reason) {
|
||||
bounded(e, b, delta, upper, _, _, reason) and
|
||||
bestBound(e, b, delta, upper)
|
||||
}
|
||||
module Bounds implements BoundSig<FloatDelta> {
|
||||
class SemBound instanceof SemanticBound::SemBound {
|
||||
string toString() { result = super.toString() }
|
||||
|
||||
SemExpr getExpr(float delta) { result = super.getExpr(delta) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `guard = boundFlowCond(_, _, _, _, _) or guard = eqFlowCond(_, _, _, _, _)`.
|
||||
*/
|
||||
cached
|
||||
predicate possibleReason(SemGuard guard) {
|
||||
guard = boundFlowCond(_, _, _, _, _) or guard = semEqFlowCond(_, _, _, _, _)
|
||||
class SemZeroBound extends SemBound instanceof SemanticBound::SemZeroBound { }
|
||||
|
||||
class SemSsaBound extends SemBound instanceof SemanticBound::SemSsaBound {
|
||||
SemSsaVariable getAVariable() { result = this.(SemanticBound::SemSsaBound).getAVariable() }
|
||||
}
|
||||
}
|
||||
|
||||
private import RangeAnalysisCache
|
||||
import RangeAnalysisPublic
|
||||
private module CppRangeAnalysis =
|
||||
RangeStage<FloatDelta, Bounds, CppLangImpl, RangeUtil<FloatDelta, CppLangImpl>>;
|
||||
|
||||
/**
|
||||
* Holds if `b + delta` is a valid bound for `e` and this is the best such delta.
|
||||
* - `upper = true` : `e <= b + delta`
|
||||
* - `upper = false` : `e >= b + delta`
|
||||
*/
|
||||
private predicate bestBound(SemExpr e, SemBound b, int delta, boolean upper) {
|
||||
delta = min(int d | bounded(e, b, d, upper, _, _, _)) and upper = true
|
||||
or
|
||||
delta = max(int d | bounded(e, b, d, upper, _, _, _)) and upper = false
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `comp` corresponds to:
|
||||
* - `upper = true` : `v <= e + delta` or `v < e + delta`
|
||||
* - `upper = false` : `v >= e + delta` or `v > e + delta`
|
||||
*/
|
||||
private predicate boundCondition(
|
||||
SemRelationalExpr comp, SemSsaVariable v, SemExpr e, int delta, boolean upper
|
||||
) {
|
||||
comp.getLesserOperand() = semSsaRead(v, delta) and e = comp.getGreaterOperand() and upper = true
|
||||
or
|
||||
comp.getGreaterOperand() = semSsaRead(v, delta) and e = comp.getLesserOperand() and upper = false
|
||||
or
|
||||
exists(SemSubExpr sub, SemConstantIntegerExpr c, int d |
|
||||
// (v - d) - e < c
|
||||
comp.getLesserOperand() = sub and
|
||||
comp.getGreaterOperand() = c and
|
||||
sub.getLeftOperand() = semSsaRead(v, d) and
|
||||
sub.getRightOperand() = e and
|
||||
upper = true and
|
||||
delta = d + c.getIntValue()
|
||||
or
|
||||
// (v - d) - e > c
|
||||
comp.getGreaterOperand() = sub and
|
||||
comp.getLesserOperand() = c and
|
||||
sub.getLeftOperand() = semSsaRead(v, d) and
|
||||
sub.getRightOperand() = e and
|
||||
upper = false and
|
||||
delta = d + c.getIntValue()
|
||||
or
|
||||
// e - (v - d) < c
|
||||
comp.getLesserOperand() = sub and
|
||||
comp.getGreaterOperand() = c and
|
||||
sub.getLeftOperand() = e and
|
||||
sub.getRightOperand() = semSsaRead(v, d) and
|
||||
upper = false and
|
||||
delta = d - c.getIntValue()
|
||||
or
|
||||
// e - (v - d) > c
|
||||
comp.getGreaterOperand() = sub and
|
||||
comp.getLesserOperand() = c and
|
||||
sub.getLeftOperand() = e and
|
||||
sub.getRightOperand() = semSsaRead(v, d) and
|
||||
upper = true and
|
||||
delta = d - c.getIntValue()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `comp` is a comparison between `x` and `y` for which `y - x` has a
|
||||
* fixed value modulo some `mod > 1`, such that the comparison can be
|
||||
* strengthened by `strengthen` when evaluating to `testIsTrue`.
|
||||
*/
|
||||
private predicate modulusComparison(SemRelationalExpr comp, boolean testIsTrue, int strengthen) {
|
||||
exists(
|
||||
SemBound b, int v1, int v2, int mod1, int mod2, int mod, boolean resultIsStrict, int d, int k
|
||||
|
|
||||
// If `x <= y` and `x =(mod) b + v1` and `y =(mod) b + v2` then
|
||||
// `0 <= y - x =(mod) v2 - v1`. By choosing `k =(mod) v2 - v1` with
|
||||
// `0 <= k < mod` we get `k <= y - x`. If the resulting comparison is
|
||||
// strict then the strengthening amount is instead `k - 1` modulo `mod`:
|
||||
// `x < y` means `0 <= y - x - 1 =(mod) k - 1` so `k - 1 <= y - x - 1` and
|
||||
// thus `k - 1 < y - x` with `0 <= k - 1 < mod`.
|
||||
semExprModulus(comp.getLesserOperand(), b, v1, mod1) and
|
||||
semExprModulus(comp.getGreaterOperand(), b, v2, mod2) and
|
||||
mod = mod1.gcd(mod2) and
|
||||
mod != 1 and
|
||||
(testIsTrue = true or testIsTrue = false) and
|
||||
(
|
||||
if comp.isStrict()
|
||||
then resultIsStrict = testIsTrue
|
||||
else resultIsStrict = testIsTrue.booleanNot()
|
||||
) and
|
||||
(
|
||||
resultIsStrict = true and d = 1
|
||||
or
|
||||
resultIsStrict = false and d = 0
|
||||
) and
|
||||
(
|
||||
testIsTrue = true and k = v2 - v1
|
||||
or
|
||||
testIsTrue = false and k = v1 - v2
|
||||
) and
|
||||
strengthen = (((k - d) % mod) + mod) % mod
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a condition that tests whether `v` is bounded by `e + delta`.
|
||||
*
|
||||
* If the condition evaluates to `testIsTrue`:
|
||||
* - `upper = true` : `v <= e + delta`
|
||||
* - `upper = false` : `v >= e + delta`
|
||||
*/
|
||||
private SemGuard boundFlowCond(
|
||||
SemSsaVariable v, SemExpr e, int delta, boolean upper, boolean testIsTrue
|
||||
) {
|
||||
exists(
|
||||
SemRelationalExpr comp, int d1, int d2, int d3, int strengthen, boolean compIsUpper,
|
||||
boolean resultIsStrict
|
||||
|
|
||||
comp = result.asExpr() and
|
||||
boundCondition(comp, v, e, d1, compIsUpper) and
|
||||
(testIsTrue = true or testIsTrue = false) and
|
||||
upper = compIsUpper.booleanXor(testIsTrue.booleanNot()) and
|
||||
(
|
||||
if comp.isStrict()
|
||||
then resultIsStrict = testIsTrue
|
||||
else resultIsStrict = testIsTrue.booleanNot()
|
||||
) and
|
||||
(
|
||||
if
|
||||
getTrackedTypeForSsaVariable(v) instanceof SemIntegerType or
|
||||
getTrackedTypeForSsaVariable(v) instanceof SemAddressType
|
||||
then
|
||||
upper = true and strengthen = -1
|
||||
or
|
||||
upper = false and strengthen = 1
|
||||
else strengthen = 0
|
||||
) and
|
||||
(
|
||||
exists(int k | modulusComparison(comp, testIsTrue, k) and d2 = strengthen * k)
|
||||
or
|
||||
not modulusComparison(comp, testIsTrue, _) and d2 = 0
|
||||
) and
|
||||
// A strict inequality `x < y` can be strengthened to `x <= y - 1`.
|
||||
(
|
||||
resultIsStrict = true and d3 = strengthen
|
||||
or
|
||||
resultIsStrict = false and d3 = 0
|
||||
) and
|
||||
delta = d1 + d2 + d3
|
||||
)
|
||||
or
|
||||
exists(boolean testIsTrue0 |
|
||||
semImplies_v2(result, testIsTrue, boundFlowCond(v, e, delta, upper, testIsTrue0), testIsTrue0)
|
||||
)
|
||||
or
|
||||
result = semEqFlowCond(v, e, delta, true, testIsTrue) and
|
||||
(upper = true or upper = false)
|
||||
or
|
||||
// guard that tests whether `v2` is bounded by `e + delta + d1 - d2` and
|
||||
// exists a guard `guardEq` such that `v = v2 - d1 + d2`.
|
||||
exists(SemSsaVariable v2, SemGuard guardEq, boolean eqIsTrue, int d1, int d2 |
|
||||
guardEq = semEqFlowCond(v, semSsaRead(v2, d1), d2, true, eqIsTrue) and
|
||||
result = boundFlowCond(v2, e, delta + d1 - d2, upper, testIsTrue) and
|
||||
// guardEq needs to control guard
|
||||
guardEq.directlyControls(result.getBasicBlock(), eqIsTrue)
|
||||
)
|
||||
}
|
||||
|
||||
private newtype TSemReason =
|
||||
TSemNoReason() or
|
||||
TSemCondReason(SemGuard guard) { possibleReason(guard) }
|
||||
|
||||
/**
|
||||
* A reason for an inferred bound. This can either be `CondReason` if the bound
|
||||
* is due to a specific condition, or `NoReason` if the bound is inferred
|
||||
* without going through a bounding condition.
|
||||
*/
|
||||
abstract class SemReason extends TSemReason {
|
||||
/** Gets a textual representation of this reason. */
|
||||
abstract string toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* A reason for an inferred bound that indicates that the bound is inferred
|
||||
* without going through a bounding condition.
|
||||
*/
|
||||
class SemNoReason extends SemReason, TSemNoReason {
|
||||
override string toString() { result = "NoReason" }
|
||||
}
|
||||
|
||||
/** A reason for an inferred bound pointing to a condition. */
|
||||
class SemCondReason extends SemReason, TSemCondReason {
|
||||
/** Gets the condition that is the reason for the bound. */
|
||||
SemGuard getCond() { this = TSemCondReason(result) }
|
||||
|
||||
override string toString() { result = getCond().toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `e + delta` is a valid bound for `v` at `pos`.
|
||||
* - `upper = true` : `v <= e + delta`
|
||||
* - `upper = false` : `v >= e + delta`
|
||||
*/
|
||||
private predicate boundFlowStepSsa(
|
||||
SemSsaVariable v, SemSsaReadPosition pos, SemExpr e, int delta, boolean upper, SemReason reason
|
||||
) {
|
||||
semSsaUpdateStep(v, e, delta) and
|
||||
pos.hasReadOfVar(v) and
|
||||
(upper = true or upper = false) and
|
||||
reason = TSemNoReason()
|
||||
or
|
||||
exists(SemGuard guard, boolean testIsTrue |
|
||||
pos.hasReadOfVar(v) and
|
||||
guard = boundFlowCond(v, e, delta, upper, testIsTrue) and
|
||||
semGuardDirectlyControlsSsaRead(guard, pos, testIsTrue) and
|
||||
reason = TSemCondReason(guard)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `v != e + delta` at `pos` and `v` is of integral type. */
|
||||
private predicate unequalFlowStepIntegralSsa(
|
||||
SemSsaVariable v, SemSsaReadPosition pos, SemExpr e, int delta, SemReason reason
|
||||
) {
|
||||
getTrackedTypeForSsaVariable(v) instanceof SemIntegerType and
|
||||
exists(SemGuard guard, boolean testIsTrue |
|
||||
pos.hasReadOfVar(v) and
|
||||
guard = semEqFlowCond(v, e, delta, false, testIsTrue) and
|
||||
semGuardDirectlyControlsSsaRead(guard, pos, testIsTrue) and
|
||||
reason = TSemCondReason(guard)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression that does conversion, boxing, or unboxing
|
||||
*/
|
||||
private class ConvertOrBoxExpr extends SemUnaryExpr {
|
||||
ConvertOrBoxExpr() {
|
||||
this instanceof SemConvertExpr
|
||||
or
|
||||
this instanceof SemBoxExpr
|
||||
or
|
||||
this instanceof SemUnboxExpr
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A cast that can be ignored for the purpose of range analysis.
|
||||
*/
|
||||
private class SafeCastExpr extends ConvertOrBoxExpr {
|
||||
SafeCastExpr() {
|
||||
conversionCannotOverflow(getTrackedType(pragma[only_bind_into](getOperand())),
|
||||
getTrackedType(this))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `typ` is a small integral type with the given lower and upper bounds.
|
||||
*/
|
||||
private predicate typeBound(SemIntegerType typ, int lowerbound, int upperbound) {
|
||||
exists(int bitSize | bitSize = typ.getByteSize() * 8 |
|
||||
bitSize < 32 and
|
||||
(
|
||||
if typ.isSigned()
|
||||
then (
|
||||
upperbound = 1.bitShiftLeft(bitSize - 1) - 1 and
|
||||
lowerbound = -upperbound - 1
|
||||
) else (
|
||||
lowerbound = 0 and
|
||||
upperbound = 1.bitShiftLeft(bitSize) - 1
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A cast to a small integral type that may overflow or underflow.
|
||||
*/
|
||||
private class NarrowingCastExpr extends ConvertOrBoxExpr {
|
||||
NarrowingCastExpr() {
|
||||
not this instanceof SafeCastExpr and
|
||||
typeBound(getTrackedType(this), _, _)
|
||||
}
|
||||
|
||||
/** Gets the lower bound of the resulting type. */
|
||||
int getLowerBound() { typeBound(getTrackedType(this), result, _) }
|
||||
|
||||
/** Gets the upper bound of the resulting type. */
|
||||
int getUpperBound() { typeBound(getTrackedType(this), _, result) }
|
||||
}
|
||||
|
||||
/** Holds if `e >= 1` as determined by sign analysis. */
|
||||
private predicate strictlyPositiveIntegralExpr(SemExpr e) {
|
||||
semStrictlyPositive(e) and getTrackedType(e) instanceof SemIntegerType
|
||||
}
|
||||
|
||||
/** Holds if `e <= -1` as determined by sign analysis. */
|
||||
private predicate strictlyNegativeIntegralExpr(SemExpr e) {
|
||||
semStrictlyNegative(e) and getTrackedType(e) instanceof SemIntegerType
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `e1 + delta` is a valid bound for `e2`.
|
||||
* - `upper = true` : `e2 <= e1 + delta`
|
||||
* - `upper = false` : `e2 >= e1 + delta`
|
||||
*/
|
||||
private predicate boundFlowStep(SemExpr e2, SemExpr e1, int delta, boolean upper) {
|
||||
semValueFlowStep(e2, e1, delta) and
|
||||
(upper = true or upper = false)
|
||||
or
|
||||
e2.(SafeCastExpr).getOperand() = e1 and
|
||||
delta = 0 and
|
||||
(upper = true or upper = false)
|
||||
or
|
||||
exists(SemExpr x | e2.(SemAddExpr).hasOperands(e1, x) |
|
||||
// `x instanceof ConstantIntegerExpr` is covered by valueFlowStep
|
||||
not x instanceof SemConstantIntegerExpr and
|
||||
not e1 instanceof SemConstantIntegerExpr and
|
||||
if strictlyPositiveIntegralExpr(x)
|
||||
then upper = false and delta = 1
|
||||
else
|
||||
if semPositive(x)
|
||||
then upper = false and delta = 0
|
||||
else
|
||||
if strictlyNegativeIntegralExpr(x)
|
||||
then upper = true and delta = -1
|
||||
else
|
||||
if semNegative(x)
|
||||
then upper = true and delta = 0
|
||||
else none()
|
||||
)
|
||||
or
|
||||
exists(SemExpr x, SemSubExpr sub |
|
||||
e2 = sub and
|
||||
sub.getLeftOperand() = e1 and
|
||||
sub.getRightOperand() = x
|
||||
|
|
||||
// `x instanceof ConstantIntegerExpr` is covered by valueFlowStep
|
||||
not x instanceof SemConstantIntegerExpr and
|
||||
if strictlyPositiveIntegralExpr(x)
|
||||
then upper = true and delta = -1
|
||||
else
|
||||
if semPositive(x)
|
||||
then upper = true and delta = 0
|
||||
else
|
||||
if strictlyNegativeIntegralExpr(x)
|
||||
then upper = false and delta = 1
|
||||
else
|
||||
if semNegative(x)
|
||||
then upper = false and delta = 0
|
||||
else none()
|
||||
)
|
||||
or
|
||||
e2.(SemRemExpr).getRightOperand() = e1 and
|
||||
semPositive(e1) and
|
||||
delta = -1 and
|
||||
upper = true
|
||||
or
|
||||
e2.(SemRemExpr).getLeftOperand() = e1 and semPositive(e1) and delta = 0 and upper = true
|
||||
or
|
||||
e2.(SemBitAndExpr).getAnOperand() = e1 and
|
||||
semPositive(e1) and
|
||||
delta = 0 and
|
||||
upper = true
|
||||
or
|
||||
e2.(SemBitOrExpr).getAnOperand() = e1 and
|
||||
semPositive(e2) and
|
||||
delta = 0 and
|
||||
upper = false
|
||||
or
|
||||
Specific::hasBound(e2, e1, delta, upper)
|
||||
}
|
||||
|
||||
/** Holds if `e2 = e1 * factor` and `factor > 0`. */
|
||||
private predicate boundFlowStepMul(SemExpr e2, SemExpr e1, int factor) {
|
||||
exists(SemConstantIntegerExpr c, int k | k = c.getIntValue() and k > 0 |
|
||||
e2.(SemMulExpr).hasOperands(e1, c) and factor = k
|
||||
or
|
||||
exists(SemShiftLeftExpr e |
|
||||
e = e2 and e.getLeftOperand() = e1 and e.getRightOperand() = c and factor = 2.pow(k)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `e2 = e1 / factor` and `factor > 0`.
|
||||
*
|
||||
* This conflates division, right shift, and unsigned right shift and is
|
||||
* therefore only valid for non-negative numbers.
|
||||
*/
|
||||
private predicate boundFlowStepDiv(SemExpr e2, SemExpr e1, int factor) {
|
||||
exists(SemConstantIntegerExpr c, int k | k = c.getIntValue() and k > 0 |
|
||||
exists(SemDivExpr e |
|
||||
e = e2 and e.getLeftOperand() = e1 and e.getRightOperand() = c and factor = k
|
||||
)
|
||||
or
|
||||
exists(SemShiftRightExpr e |
|
||||
e = e2 and e.getLeftOperand() = e1 and e.getRightOperand() = c and factor = 2.pow(k)
|
||||
)
|
||||
or
|
||||
exists(SemShiftRightUnsignedExpr e |
|
||||
e = e2 and e.getLeftOperand() = e1 and e.getRightOperand() = c and factor = 2.pow(k)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `b + delta` is a valid bound for `v` at `pos`.
|
||||
* - `upper = true` : `v <= b + delta`
|
||||
* - `upper = false` : `v >= b + delta`
|
||||
*/
|
||||
private predicate boundedSsa(
|
||||
SemSsaVariable v, SemSsaReadPosition pos, SemBound b, int delta, boolean upper,
|
||||
boolean fromBackEdge, int origdelta, SemReason reason
|
||||
) {
|
||||
exists(SemExpr mid, int d1, int d2, SemReason r1, SemReason r2 |
|
||||
boundFlowStepSsa(v, pos, mid, d1, upper, r1) and
|
||||
bounded(mid, b, d2, upper, fromBackEdge, origdelta, r2) and
|
||||
// upper = true: v <= mid + d1 <= b + d1 + d2 = b + delta
|
||||
// upper = false: v >= mid + d1 >= b + d1 + d2 = b + delta
|
||||
delta = d1 + d2 and
|
||||
(if r1 instanceof SemNoReason then reason = r2 else reason = r1)
|
||||
)
|
||||
or
|
||||
exists(int d, SemReason r1, SemReason r2 |
|
||||
boundedSsa(v, pos, b, d, upper, fromBackEdge, origdelta, r2) or
|
||||
boundedPhi(v, b, d, upper, fromBackEdge, origdelta, r2)
|
||||
|
|
||||
unequalIntegralSsa(v, pos, b, d, r1) and
|
||||
(
|
||||
upper = true and delta = d - 1
|
||||
or
|
||||
upper = false and delta = d + 1
|
||||
) and
|
||||
(
|
||||
reason = r1
|
||||
or
|
||||
reason = r2 and not r2 instanceof SemNoReason
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `v != b + delta` at `pos` and `v` is of integral type.
|
||||
*/
|
||||
private predicate unequalIntegralSsa(
|
||||
SemSsaVariable v, SemSsaReadPosition pos, SemBound b, int delta, SemReason reason
|
||||
) {
|
||||
exists(SemExpr e, int d1, int d2 |
|
||||
unequalFlowStepIntegralSsa(v, pos, e, d1, reason) and
|
||||
boundedUpper(e, b, d1) and
|
||||
boundedLower(e, b, d2) and
|
||||
delta = d2 + d1
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `b + delta` is an upper bound for `e`.
|
||||
*
|
||||
* This predicate only exists to prevent a bad standard order in `unequalIntegralSsa`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate boundedUpper(SemExpr e, SemBound b, int delta) {
|
||||
bounded(e, b, delta, true, _, _, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `b + delta` is a lower bound for `e`.
|
||||
*
|
||||
* This predicate only exists to prevent a bad standard order in `unequalIntegralSsa`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate boundedLower(SemExpr e, SemBound b, int delta) {
|
||||
bounded(e, b, delta, false, _, _, _)
|
||||
}
|
||||
|
||||
/** Weakens a delta to lie in the range `[-1..1]`. */
|
||||
bindingset[delta, upper]
|
||||
private int weakenDelta(boolean upper, int delta) {
|
||||
delta in [-1 .. 1] and result = delta
|
||||
or
|
||||
upper = true and result = -1 and delta < -1
|
||||
or
|
||||
upper = false and result = 1 and delta > 1
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `b + delta` is a valid bound for `inp` when used as an input to
|
||||
* `phi` along `edge`.
|
||||
* - `upper = true` : `inp <= b + delta`
|
||||
* - `upper = false` : `inp >= b + delta`
|
||||
*/
|
||||
private predicate boundedPhiInp(
|
||||
SemSsaPhiNode phi, SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge, SemBound b, int delta,
|
||||
boolean upper, boolean fromBackEdge, int origdelta, SemReason reason
|
||||
) {
|
||||
edge.phiInput(phi, inp) and
|
||||
exists(int d, boolean fromBackEdge0 |
|
||||
boundedSsa(inp, edge, b, d, upper, fromBackEdge0, origdelta, reason)
|
||||
or
|
||||
boundedPhi(inp, b, d, upper, fromBackEdge0, origdelta, reason)
|
||||
or
|
||||
b.(SemSsaBound).getAVariable() = inp and
|
||||
d = 0 and
|
||||
(upper = true or upper = false) and
|
||||
fromBackEdge0 = false and
|
||||
origdelta = 0 and
|
||||
reason = TSemNoReason()
|
||||
|
|
||||
if semBackEdge(phi, inp, edge)
|
||||
then
|
||||
fromBackEdge = true and
|
||||
(
|
||||
fromBackEdge0 = true and delta = weakenDelta(upper, d - origdelta) + origdelta
|
||||
or
|
||||
fromBackEdge0 = false and delta = d
|
||||
)
|
||||
else (
|
||||
delta = d and fromBackEdge = fromBackEdge0
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `b + delta` is a valid bound for `inp` when used as an input to
|
||||
* `phi` along `edge`.
|
||||
* - `upper = true` : `inp <= b + delta`
|
||||
* - `upper = false` : `inp >= b + delta`
|
||||
*
|
||||
* Equivalent to `boundedPhiInp(phi, inp, edge, b, delta, upper, _, _, _)`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate boundedPhiInp1(
|
||||
SemSsaPhiNode phi, SemBound b, boolean upper, SemSsaVariable inp,
|
||||
SemSsaReadPositionPhiInputEdge edge, int delta
|
||||
) {
|
||||
boundedPhiInp(phi, inp, edge, b, delta, upper, _, _, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `phi` is a valid bound for `inp` when used as an input to `phi`
|
||||
* along `edge`.
|
||||
* - `upper = true` : `inp <= phi`
|
||||
* - `upper = false` : `inp >= phi`
|
||||
*/
|
||||
private predicate selfBoundedPhiInp(
|
||||
SemSsaPhiNode phi, SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge, boolean upper
|
||||
) {
|
||||
exists(int d, SemSsaBound phibound |
|
||||
phibound.getAVariable() = phi and
|
||||
boundedPhiInp(phi, inp, edge, phibound, d, upper, _, _, _) and
|
||||
(
|
||||
upper = true and d <= 0
|
||||
or
|
||||
upper = false and d >= 0
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `b + delta` is a valid bound for some input, `inp`, to `phi`, and
|
||||
* thus a candidate bound for `phi`.
|
||||
* - `upper = true` : `inp <= b + delta`
|
||||
* - `upper = false` : `inp >= b + delta`
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate boundedPhiCand(
|
||||
SemSsaPhiNode phi, boolean upper, SemBound b, int delta, boolean fromBackEdge, int origdelta,
|
||||
SemReason reason
|
||||
) {
|
||||
boundedPhiInp(phi, _, _, b, delta, upper, fromBackEdge, origdelta, reason)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the candidate bound `b + delta` for `phi` is valid for the phi input
|
||||
* `inp` along `edge`.
|
||||
*/
|
||||
private predicate boundedPhiCandValidForEdge(
|
||||
SemSsaPhiNode phi, SemBound b, int delta, boolean upper, boolean fromBackEdge, int origdelta,
|
||||
SemReason reason, SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge
|
||||
) {
|
||||
boundedPhiCand(phi, upper, b, delta, fromBackEdge, origdelta, reason) and
|
||||
(
|
||||
exists(int d | boundedPhiInp1(phi, b, upper, inp, edge, d) | upper = true and d <= delta)
|
||||
or
|
||||
exists(int d | boundedPhiInp1(phi, b, upper, inp, edge, d) | upper = false and d >= delta)
|
||||
or
|
||||
selfBoundedPhiInp(phi, inp, edge, upper)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `b + delta` is a valid bound for `phi`.
|
||||
* - `upper = true` : `phi <= b + delta`
|
||||
* - `upper = false` : `phi >= b + delta`
|
||||
*/
|
||||
private predicate boundedPhi(
|
||||
SemSsaPhiNode phi, SemBound b, int delta, boolean upper, boolean fromBackEdge, int origdelta,
|
||||
SemReason reason
|
||||
) {
|
||||
forex(SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge | edge.phiInput(phi, inp) |
|
||||
boundedPhiCandValidForEdge(phi, b, delta, upper, fromBackEdge, origdelta, reason, inp, edge)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `e` has an upper (for `upper = true`) or lower
|
||||
* (for `upper = false`) bound of `b`.
|
||||
*/
|
||||
private predicate baseBound(SemExpr e, int b, boolean upper) {
|
||||
Specific::hasConstantBound(e, b, upper)
|
||||
or
|
||||
upper = false and
|
||||
b = 0 and
|
||||
semPositive(e.(SemBitAndExpr).getAnOperand()) and
|
||||
// REVIEW: We let the language opt out here to preserve original results.
|
||||
not Specific::ignoreZeroLowerBound(e)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the value being cast has an upper (for `upper = true`) or lower
|
||||
* (for `upper = false`) bound within the bounds of the resulting type.
|
||||
* For `upper = true` this means that the cast will not overflow and for
|
||||
* `upper = false` this means that the cast will not underflow.
|
||||
*/
|
||||
private predicate safeNarrowingCast(NarrowingCastExpr cast, boolean upper) {
|
||||
exists(int bound | bounded(cast.getOperand(), any(SemZeroBound zb), bound, upper, _, _, _) |
|
||||
upper = true and bound <= cast.getUpperBound()
|
||||
or
|
||||
upper = false and bound >= cast.getLowerBound()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate boundedCastExpr(
|
||||
NarrowingCastExpr cast, SemBound b, int delta, boolean upper, boolean fromBackEdge, int origdelta,
|
||||
SemReason reason
|
||||
) {
|
||||
bounded(cast.getOperand(), b, delta, upper, fromBackEdge, origdelta, reason)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `b + delta` is a valid bound for `e`.
|
||||
* - `upper = true` : `e <= b + delta`
|
||||
* - `upper = false` : `e >= b + delta`
|
||||
*/
|
||||
private predicate bounded(
|
||||
SemExpr e, SemBound b, int delta, boolean upper, boolean fromBackEdge, int origdelta,
|
||||
SemReason reason
|
||||
) {
|
||||
not Specific::ignoreExprBound(e) and
|
||||
(
|
||||
e = b.getExpr(delta) and
|
||||
(upper = true or upper = false) and
|
||||
fromBackEdge = false and
|
||||
origdelta = delta and
|
||||
reason = TSemNoReason()
|
||||
or
|
||||
baseBound(e, delta, upper) and
|
||||
b instanceof SemZeroBound and
|
||||
fromBackEdge = false and
|
||||
origdelta = delta and
|
||||
reason = TSemNoReason()
|
||||
or
|
||||
exists(SemSsaVariable v, SemSsaReadPositionBlock bb |
|
||||
boundedSsa(v, bb, b, delta, upper, fromBackEdge, origdelta, reason) and
|
||||
e = v.getAUse() and
|
||||
bb.getBlock() = e.getBasicBlock()
|
||||
)
|
||||
or
|
||||
exists(SemExpr mid, int d1, int d2 |
|
||||
boundFlowStep(e, mid, d1, upper) and
|
||||
// Constants have easy, base-case bounds, so let's not infer any recursive bounds.
|
||||
not e instanceof SemConstantIntegerExpr and
|
||||
bounded(mid, b, d2, upper, fromBackEdge, origdelta, reason) and
|
||||
// upper = true: e <= mid + d1 <= b + d1 + d2 = b + delta
|
||||
// upper = false: e >= mid + d1 >= b + d1 + d2 = b + delta
|
||||
delta = d1 + d2
|
||||
)
|
||||
or
|
||||
exists(SemSsaPhiNode phi |
|
||||
boundedPhi(phi, b, delta, upper, fromBackEdge, origdelta, reason) and
|
||||
e = phi.getAUse()
|
||||
)
|
||||
or
|
||||
exists(SemExpr mid, int factor, int d |
|
||||
boundFlowStepMul(e, mid, factor) and
|
||||
not e instanceof SemConstantIntegerExpr and
|
||||
bounded(mid, b, d, upper, fromBackEdge, origdelta, reason) and
|
||||
b instanceof SemZeroBound and
|
||||
delta = d * factor
|
||||
)
|
||||
or
|
||||
exists(SemExpr mid, int factor, int d |
|
||||
boundFlowStepDiv(e, mid, factor) and
|
||||
not e instanceof SemConstantIntegerExpr and
|
||||
bounded(mid, b, d, upper, fromBackEdge, origdelta, reason) and
|
||||
b instanceof SemZeroBound and
|
||||
d >= 0 and
|
||||
delta = d / factor
|
||||
)
|
||||
or
|
||||
exists(NarrowingCastExpr cast |
|
||||
cast = e and
|
||||
safeNarrowingCast(cast, upper.booleanNot()) and
|
||||
boundedCastExpr(cast, b, delta, upper, fromBackEdge, origdelta, reason)
|
||||
)
|
||||
or
|
||||
exists(
|
||||
SemConditionalExpr cond, int d1, int d2, boolean fbe1, boolean fbe2, int od1, int od2,
|
||||
SemReason r1, SemReason r2
|
||||
|
|
||||
cond = e and
|
||||
boundedConditionalExpr(cond, b, upper, true, d1, fbe1, od1, r1) and
|
||||
boundedConditionalExpr(cond, b, upper, false, d2, fbe2, od2, r2) and
|
||||
(
|
||||
delta = d1 and fromBackEdge = fbe1 and origdelta = od1 and reason = r1
|
||||
or
|
||||
delta = d2 and fromBackEdge = fbe2 and origdelta = od2 and reason = r2
|
||||
)
|
||||
|
|
||||
upper = true and delta = d1.maximum(d2)
|
||||
or
|
||||
upper = false and delta = d1.minimum(d2)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate boundedConditionalExpr(
|
||||
SemConditionalExpr cond, SemBound b, boolean upper, boolean branch, int delta,
|
||||
boolean fromBackEdge, int origdelta, SemReason reason
|
||||
) {
|
||||
bounded(cond.getBranchExpr(branch), b, delta, upper, fromBackEdge, origdelta, reason)
|
||||
}
|
||||
import CppRangeAnalysis
|
||||
|
||||
@@ -3,86 +3,90 @@
|
||||
*/
|
||||
|
||||
private import experimental.semmle.code.cpp.semantic.Semantic
|
||||
private import RangeAnalysisStage
|
||||
private import experimental.semmle.code.cpp.semantic.analysis.FloatDelta
|
||||
|
||||
/**
|
||||
* Holds if the specified expression should be excluded from the result of `ssaRead()`.
|
||||
*
|
||||
* This predicate is to keep the results identical to the original Java implementation. It should be
|
||||
* removed once we have the new implementation matching the old results exactly.
|
||||
*/
|
||||
predicate ignoreSsaReadCopy(SemExpr e) { none() }
|
||||
module CppLangImpl implements LangSig<FloatDelta> {
|
||||
/**
|
||||
* Holds if the specified expression should be excluded from the result of `ssaRead()`.
|
||||
*
|
||||
* This predicate is to keep the results identical to the original Java implementation. It should be
|
||||
* removed once we have the new implementation matching the old results exactly.
|
||||
*/
|
||||
predicate ignoreSsaReadCopy(SemExpr e) { none() }
|
||||
|
||||
/**
|
||||
* Ignore the bound on this expression.
|
||||
*
|
||||
* This predicate is to keep the results identical to the original Java implementation. It should be
|
||||
* removed once we have the new implementation matching the old results exactly.
|
||||
*/
|
||||
predicate ignoreExprBound(SemExpr e) { none() }
|
||||
/**
|
||||
* Ignore the bound on this expression.
|
||||
*
|
||||
* This predicate is to keep the results identical to the original Java implementation. It should be
|
||||
* removed once we have the new implementation matching the old results exactly.
|
||||
*/
|
||||
predicate ignoreExprBound(SemExpr e) { none() }
|
||||
|
||||
/**
|
||||
* Ignore any inferred zero lower bound on this expression.
|
||||
*
|
||||
* This predicate is to keep the results identical to the original Java implementation. It should be
|
||||
* removed once we have the new implementation matching the old results exactly.
|
||||
*/
|
||||
predicate ignoreZeroLowerBound(SemExpr e) { none() }
|
||||
/**
|
||||
* Ignore any inferred zero lower bound on this expression.
|
||||
*
|
||||
* This predicate is to keep the results identical to the original Java implementation. It should be
|
||||
* removed once we have the new implementation matching the old results exactly.
|
||||
*/
|
||||
predicate ignoreZeroLowerBound(SemExpr e) { none() }
|
||||
|
||||
/**
|
||||
* Holds if the specified expression should be excluded from the result of `ssaRead()`.
|
||||
*
|
||||
* This predicate is to keep the results identical to the original Java implementation. It should be
|
||||
* removed once we have the new implementation matching the old results exactly.
|
||||
*/
|
||||
predicate ignoreSsaReadArithmeticExpr(SemExpr e) { none() }
|
||||
/**
|
||||
* Holds if the specified expression should be excluded from the result of `ssaRead()`.
|
||||
*
|
||||
* This predicate is to keep the results identical to the original Java implementation. It should be
|
||||
* removed once we have the new implementation matching the old results exactly.
|
||||
*/
|
||||
predicate ignoreSsaReadArithmeticExpr(SemExpr e) { none() }
|
||||
|
||||
/**
|
||||
* Holds if the specified variable should be excluded from the result of `ssaRead()`.
|
||||
*
|
||||
* This predicate is to keep the results identical to the original Java implementation. It should be
|
||||
* removed once we have the new implementation matching the old results exactly.
|
||||
*/
|
||||
predicate ignoreSsaReadAssignment(SemSsaVariable v) { none() }
|
||||
/**
|
||||
* Holds if the specified variable should be excluded from the result of `ssaRead()`.
|
||||
*
|
||||
* This predicate is to keep the results identical to the original Java implementation. It should be
|
||||
* removed once we have the new implementation matching the old results exactly.
|
||||
*/
|
||||
predicate ignoreSsaReadAssignment(SemSsaVariable v) { none() }
|
||||
|
||||
/**
|
||||
* Adds additional results to `ssaRead()` that are specific to Java.
|
||||
*
|
||||
* This predicate handles propagation of offsets for post-increment and post-decrement expressions
|
||||
* in exactly the same way as the old Java implementation. Once the new implementation matches the
|
||||
* old one, we should remove this predicate and propagate deltas for all similar patterns, whether
|
||||
* or not they come from a post-increment/decrement expression.
|
||||
*/
|
||||
SemExpr specificSsaRead(SemSsaVariable v, int delta) { none() }
|
||||
/**
|
||||
* Adds additional results to `ssaRead()` that are specific to Java.
|
||||
*
|
||||
* This predicate handles propagation of offsets for post-increment and post-decrement expressions
|
||||
* in exactly the same way as the old Java implementation. Once the new implementation matches the
|
||||
* old one, we should remove this predicate and propagate deltas for all similar patterns, whether
|
||||
* or not they come from a post-increment/decrement expression.
|
||||
*/
|
||||
SemExpr specificSsaRead(SemSsaVariable v, float delta) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `e >= bound` (if `upper = false`) or `e <= bound` (if `upper = true`).
|
||||
*/
|
||||
predicate hasConstantBound(SemExpr e, int bound, boolean upper) { none() }
|
||||
/**
|
||||
* Holds if `e >= bound` (if `upper = false`) or `e <= bound` (if `upper = true`).
|
||||
*/
|
||||
predicate hasConstantBound(SemExpr e, float bound, boolean upper) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `e >= bound + delta` (if `upper = false`) or `e <= bound + delta` (if `upper = true`).
|
||||
*/
|
||||
predicate hasBound(SemExpr e, SemExpr bound, int delta, boolean upper) { none() }
|
||||
/**
|
||||
* Holds if `e >= bound + delta` (if `upper = false`) or `e <= bound + delta` (if `upper = true`).
|
||||
*/
|
||||
predicate hasBound(SemExpr e, SemExpr bound, float delta, boolean upper) { none() }
|
||||
|
||||
/**
|
||||
* Holds if the value of `dest` is known to be `src + delta`.
|
||||
*/
|
||||
predicate additionalValueFlowStep(SemExpr dest, SemExpr src, int delta) { none() }
|
||||
/**
|
||||
* Holds if the value of `dest` is known to be `src + delta`.
|
||||
*/
|
||||
predicate additionalValueFlowStep(SemExpr dest, SemExpr src, float delta) { none() }
|
||||
|
||||
/**
|
||||
* Gets the type that range analysis should use to track the result of the specified expression,
|
||||
* if a type other than the original type of the expression is to be used.
|
||||
*
|
||||
* This predicate is commonly used in languages that support immutable "boxed" types that are
|
||||
* actually references but whose values can be tracked as the type contained in the box.
|
||||
*/
|
||||
SemType getAlternateType(SemExpr e) { none() }
|
||||
/**
|
||||
* Gets the type that range analysis should use to track the result of the specified expression,
|
||||
* if a type other than the original type of the expression is to be used.
|
||||
*
|
||||
* This predicate is commonly used in languages that support immutable "boxed" types that are
|
||||
* actually references but whose values can be tracked as the type contained in the box.
|
||||
*/
|
||||
SemType getAlternateType(SemExpr e) { none() }
|
||||
|
||||
/**
|
||||
* Gets the type that range analysis should use to track the result of the specified source
|
||||
* variable, if a type other than the original type of the expression is to be used.
|
||||
*
|
||||
* This predicate is commonly used in languages that support immutable "boxed" types that are
|
||||
* actually references but whose values can be tracked as the type contained in the box.
|
||||
*/
|
||||
SemType getAlternateTypeForSsaVariable(SemSsaVariable var) { none() }
|
||||
/**
|
||||
* Gets the type that range analysis should use to track the result of the specified source
|
||||
* variable, if a type other than the original type of the expression is to be used.
|
||||
*
|
||||
* This predicate is commonly used in languages that support immutable "boxed" types that are
|
||||
* actually references but whose values can be tracked as the type contained in the box.
|
||||
*/
|
||||
SemType getAlternateTypeForSsaVariable(SemSsaVariable var) { none() }
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3,133 +3,138 @@
|
||||
*/
|
||||
|
||||
private import experimental.semmle.code.cpp.semantic.Semantic
|
||||
private import RangeAnalysisSpecific as Specific
|
||||
private import RangeAnalysisSpecific
|
||||
private import RangeAnalysisStage as Range
|
||||
private import ConstantAnalysis
|
||||
|
||||
/**
|
||||
* Gets an expression that equals `v - d`.
|
||||
*/
|
||||
SemExpr semSsaRead(SemSsaVariable v, int delta) {
|
||||
// There are various language-specific extension points that can be removed once we no longer
|
||||
// expect to match the original Java implementation's results exactly.
|
||||
result = v.getAUse() and delta = 0
|
||||
or
|
||||
exists(int d1, SemConstantIntegerExpr c |
|
||||
result.(SemAddExpr).hasOperands(semSsaRead(v, d1), c) and
|
||||
delta = d1 - c.getIntValue() and
|
||||
not Specific::ignoreSsaReadArithmeticExpr(result)
|
||||
)
|
||||
or
|
||||
exists(SemSubExpr sub, int d1, SemConstantIntegerExpr c |
|
||||
result = sub and
|
||||
sub.getLeftOperand() = semSsaRead(v, d1) and
|
||||
sub.getRightOperand() = c and
|
||||
delta = d1 + c.getIntValue() and
|
||||
not Specific::ignoreSsaReadArithmeticExpr(result)
|
||||
)
|
||||
or
|
||||
result = v.(SemSsaExplicitUpdate).getSourceExpr() and
|
||||
delta = 0 and
|
||||
not Specific::ignoreSsaReadAssignment(v)
|
||||
or
|
||||
result = Specific::specificSsaRead(v, delta)
|
||||
or
|
||||
result.(SemCopyValueExpr).getOperand() = semSsaRead(v, delta) and
|
||||
not Specific::ignoreSsaReadCopy(result)
|
||||
or
|
||||
result.(SemStoreExpr).getOperand() = semSsaRead(v, delta)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a condition that tests whether `v` equals `e + delta`.
|
||||
*
|
||||
* If the condition evaluates to `testIsTrue`:
|
||||
* - `isEq = true` : `v == e + delta`
|
||||
* - `isEq = false` : `v != e + delta`
|
||||
*/
|
||||
SemGuard semEqFlowCond(SemSsaVariable v, SemExpr e, int delta, boolean isEq, boolean testIsTrue) {
|
||||
exists(boolean eqpolarity |
|
||||
result.isEquality(semSsaRead(v, delta), e, eqpolarity) and
|
||||
(testIsTrue = true or testIsTrue = false) and
|
||||
eqpolarity.booleanXor(testIsTrue).booleanNot() = isEq
|
||||
)
|
||||
or
|
||||
exists(boolean testIsTrue0 |
|
||||
semImplies_v2(result, testIsTrue, semEqFlowCond(v, e, delta, isEq, testIsTrue0), testIsTrue0)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `v` is an `SsaExplicitUpdate` that equals `e + delta`.
|
||||
*/
|
||||
predicate semSsaUpdateStep(SemSsaExplicitUpdate v, SemExpr e, int delta) {
|
||||
exists(SemExpr defExpr | defExpr = v.getSourceExpr() |
|
||||
defExpr.(SemCopyValueExpr).getOperand() = e and delta = 0
|
||||
module RangeUtil<Range::DeltaSig D, Range::LangSig<D> Lang> implements Range::UtilSig<D> {
|
||||
/**
|
||||
* Gets an expression that equals `v - d`.
|
||||
*/
|
||||
SemExpr semSsaRead(SemSsaVariable v, D::Delta delta) {
|
||||
// There are various language-specific extension points that can be removed once we no longer
|
||||
// expect to match the original Java implementation's results exactly.
|
||||
result = v.getAUse() and delta = D::fromInt(0)
|
||||
or
|
||||
defExpr.(SemStoreExpr).getOperand() = e and delta = 0
|
||||
exists(D::Delta d1, SemConstantIntegerExpr c |
|
||||
result.(SemAddExpr).hasOperands(semSsaRead(v, d1), c) and
|
||||
delta = D::fromFloat(D::toFloat(d1) - c.getIntValue()) and
|
||||
not Lang::ignoreSsaReadArithmeticExpr(result)
|
||||
)
|
||||
or
|
||||
defExpr.(SemAddOneExpr).getOperand() = e and delta = 1
|
||||
exists(SemSubExpr sub, D::Delta d1, SemConstantIntegerExpr c |
|
||||
result = sub and
|
||||
sub.getLeftOperand() = semSsaRead(v, d1) and
|
||||
sub.getRightOperand() = c and
|
||||
delta = D::fromFloat(D::toFloat(d1) + c.getIntValue()) and
|
||||
not Lang::ignoreSsaReadArithmeticExpr(result)
|
||||
)
|
||||
or
|
||||
defExpr.(SemSubOneExpr).getOperand() = e and delta = -1
|
||||
result = v.(SemSsaExplicitUpdate).getSourceExpr() and
|
||||
delta = D::fromFloat(0) and
|
||||
not Lang::ignoreSsaReadAssignment(v)
|
||||
or
|
||||
e = defExpr and
|
||||
not (
|
||||
defExpr instanceof SemCopyValueExpr or
|
||||
defExpr instanceof SemStoreExpr or
|
||||
defExpr instanceof SemAddOneExpr or
|
||||
defExpr instanceof SemSubOneExpr
|
||||
) and
|
||||
delta = 0
|
||||
)
|
||||
}
|
||||
result = Lang::specificSsaRead(v, delta)
|
||||
or
|
||||
result.(SemCopyValueExpr).getOperand() = semSsaRead(v, delta) and
|
||||
not Lang::ignoreSsaReadCopy(result)
|
||||
or
|
||||
result.(SemStoreExpr).getOperand() = semSsaRead(v, delta)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `e1 + delta` equals `e2`.
|
||||
*/
|
||||
predicate semValueFlowStep(SemExpr e2, SemExpr e1, int delta) {
|
||||
e2.(SemCopyValueExpr).getOperand() = e1 and delta = 0
|
||||
or
|
||||
e2.(SemStoreExpr).getOperand() = e1 and delta = 0
|
||||
or
|
||||
e2.(SemAddOneExpr).getOperand() = e1 and delta = 1
|
||||
or
|
||||
e2.(SemSubOneExpr).getOperand() = e1 and delta = -1
|
||||
or
|
||||
Specific::additionalValueFlowStep(e2, e1, delta)
|
||||
or
|
||||
exists(SemExpr x | e2.(SemAddExpr).hasOperands(e1, x) |
|
||||
x.(SemConstantIntegerExpr).getIntValue() = delta
|
||||
)
|
||||
or
|
||||
exists(SemExpr x, SemSubExpr sub |
|
||||
e2 = sub and
|
||||
sub.getLeftOperand() = e1 and
|
||||
sub.getRightOperand() = x
|
||||
|
|
||||
x.(SemConstantIntegerExpr).getIntValue() = -delta
|
||||
)
|
||||
}
|
||||
/**
|
||||
* Gets a condition that tests whether `v` equals `e + delta`.
|
||||
*
|
||||
* If the condition evaluates to `testIsTrue`:
|
||||
* - `isEq = true` : `v == e + delta`
|
||||
* - `isEq = false` : `v != e + delta`
|
||||
*/
|
||||
SemGuard semEqFlowCond(
|
||||
SemSsaVariable v, SemExpr e, D::Delta delta, boolean isEq, boolean testIsTrue
|
||||
) {
|
||||
exists(boolean eqpolarity |
|
||||
result.isEquality(semSsaRead(v, delta), e, eqpolarity) and
|
||||
(testIsTrue = true or testIsTrue = false) and
|
||||
eqpolarity.booleanXor(testIsTrue).booleanNot() = isEq
|
||||
)
|
||||
or
|
||||
exists(boolean testIsTrue0 |
|
||||
semImplies_v2(result, testIsTrue, semEqFlowCond(v, e, delta, isEq, testIsTrue0), testIsTrue0)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type used to track the specified expression's range information.
|
||||
*
|
||||
* Usually, this just `e.getSemType()`, but the language can override this to track immutable boxed
|
||||
* primitive types as the underlying primitive type.
|
||||
*/
|
||||
SemType getTrackedType(SemExpr e) {
|
||||
result = Specific::getAlternateType(e)
|
||||
or
|
||||
not exists(Specific::getAlternateType(e)) and result = e.getSemType()
|
||||
}
|
||||
/**
|
||||
* Holds if `v` is an `SsaExplicitUpdate` that equals `e + delta`.
|
||||
*/
|
||||
predicate semSsaUpdateStep(SemSsaExplicitUpdate v, SemExpr e, D::Delta delta) {
|
||||
exists(SemExpr defExpr | defExpr = v.getSourceExpr() |
|
||||
defExpr.(SemCopyValueExpr).getOperand() = e and delta = D::fromFloat(0)
|
||||
or
|
||||
defExpr.(SemStoreExpr).getOperand() = e and delta = D::fromFloat(0)
|
||||
or
|
||||
defExpr.(SemAddOneExpr).getOperand() = e and delta = D::fromFloat(1)
|
||||
or
|
||||
defExpr.(SemSubOneExpr).getOperand() = e and delta = D::fromFloat(-1)
|
||||
or
|
||||
e = defExpr and
|
||||
not (
|
||||
defExpr instanceof SemCopyValueExpr or
|
||||
defExpr instanceof SemStoreExpr or
|
||||
defExpr instanceof SemAddOneExpr or
|
||||
defExpr instanceof SemSubOneExpr
|
||||
) and
|
||||
delta = D::fromFloat(0)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type used to track the specified source variable's range information.
|
||||
*
|
||||
* Usually, this just `e.getType()`, but the language can override this to track immutable boxed
|
||||
* primitive types as the underlying primitive type.
|
||||
*/
|
||||
SemType getTrackedTypeForSsaVariable(SemSsaVariable var) {
|
||||
result = Specific::getAlternateTypeForSsaVariable(var)
|
||||
or
|
||||
not exists(Specific::getAlternateTypeForSsaVariable(var)) and result = var.getType()
|
||||
/**
|
||||
* Holds if `e1 + delta` equals `e2`.
|
||||
*/
|
||||
predicate semValueFlowStep(SemExpr e2, SemExpr e1, D::Delta delta) {
|
||||
e2.(SemCopyValueExpr).getOperand() = e1 and delta = D::fromFloat(0)
|
||||
or
|
||||
e2.(SemStoreExpr).getOperand() = e1 and delta = D::fromFloat(0)
|
||||
or
|
||||
e2.(SemAddOneExpr).getOperand() = e1 and delta = D::fromFloat(1)
|
||||
or
|
||||
e2.(SemSubOneExpr).getOperand() = e1 and delta = D::fromFloat(-1)
|
||||
or
|
||||
Lang::additionalValueFlowStep(e2, e1, delta)
|
||||
or
|
||||
exists(SemExpr x | e2.(SemAddExpr).hasOperands(e1, x) |
|
||||
D::fromInt(x.(SemConstantIntegerExpr).getIntValue()) = delta
|
||||
)
|
||||
or
|
||||
exists(SemExpr x, SemSubExpr sub |
|
||||
e2 = sub and
|
||||
sub.getLeftOperand() = e1 and
|
||||
sub.getRightOperand() = x
|
||||
|
|
||||
D::fromInt(-x.(SemConstantIntegerExpr).getIntValue()) = delta
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type used to track the specified expression's range information.
|
||||
*
|
||||
* Usually, this just `e.getSemType()`, but the language can override this to track immutable boxed
|
||||
* primitive types as the underlying primitive type.
|
||||
*/
|
||||
SemType getTrackedType(SemExpr e) {
|
||||
result = Lang::getAlternateType(e)
|
||||
or
|
||||
not exists(Lang::getAlternateType(e)) and result = e.getSemType()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type used to track the specified source variable's range information.
|
||||
*
|
||||
* Usually, this just `e.getType()`, but the language can override this to track immutable boxed
|
||||
* primitive types as the underlying primitive type.
|
||||
*/
|
||||
SemType getTrackedTypeForSsaVariable(SemSsaVariable var) {
|
||||
result = Lang::getAlternateTypeForSsaVariable(var)
|
||||
or
|
||||
not exists(Lang::getAlternateTypeForSsaVariable(var)) and result = var.getType()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,488 +6,494 @@
|
||||
* three-valued domain `{negative, zero, positive}`.
|
||||
*/
|
||||
|
||||
private import RangeAnalysisStage
|
||||
private import SignAnalysisSpecific as Specific
|
||||
private import experimental.semmle.code.cpp.semantic.Semantic
|
||||
private import ConstantAnalysis
|
||||
private import RangeUtils
|
||||
private import Sign
|
||||
|
||||
/**
|
||||
* An SSA definition for which the analysis can compute the sign.
|
||||
*
|
||||
* The actual computation of the sign is done in an override of the `getSign()` predicate. The
|
||||
* charpred of any subclass must _not_ invoke `getSign()`, directly or indirectly. This ensures
|
||||
* that the charpred does not introduce negative recursion. The `getSign()` predicate may be
|
||||
* recursive.
|
||||
*/
|
||||
abstract private class SignDef instanceof SemSsaVariable {
|
||||
final string toString() { result = super.toString() }
|
||||
module SignAnalysis<DeltaSig D, UtilSig<D> Utils> {
|
||||
/**
|
||||
* An SSA definition for which the analysis can compute the sign.
|
||||
*
|
||||
* The actual computation of the sign is done in an override of the `getSign()` predicate. The
|
||||
* charpred of any subclass must _not_ invoke `getSign()`, directly or indirectly. This ensures
|
||||
* that the charpred does not introduce negative recursion. The `getSign()` predicate may be
|
||||
* recursive.
|
||||
*/
|
||||
abstract private class SignDef instanceof SemSsaVariable {
|
||||
final string toString() { result = super.toString() }
|
||||
|
||||
/** Gets the possible signs of this SSA definition. */
|
||||
abstract Sign getSign();
|
||||
}
|
||||
|
||||
/** An SSA definition whose sign is computed based on standard flow. */
|
||||
abstract private class FlowSignDef extends SignDef {
|
||||
abstract override Sign getSign();
|
||||
}
|
||||
|
||||
/** An SSA definition whose sign is determined by the sign of that definitions source expression. */
|
||||
private class ExplicitSignDef extends FlowSignDef instanceof SemSsaExplicitUpdate {
|
||||
final override Sign getSign() { result = semExprSign(super.getSourceExpr()) }
|
||||
}
|
||||
|
||||
/** An SSA Phi definition, whose sign is the union of the signs of its inputs. */
|
||||
private class PhiSignDef extends FlowSignDef instanceof SemSsaPhiNode {
|
||||
final override Sign getSign() {
|
||||
exists(SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge |
|
||||
edge.phiInput(this, inp) and
|
||||
result = semSsaSign(inp, edge)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** An SSA definition whose sign is computed by a language-specific implementation. */
|
||||
abstract class CustomSignDef extends SignDef {
|
||||
abstract override Sign getSign();
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression for which the analysis can compute the sign.
|
||||
*
|
||||
* The actual computation of the sign is done in an override of the `getSign()` predicate. The
|
||||
* charpred of any subclass must _not_ invoke `getSign()`, directly or indirectly. This ensures
|
||||
* that the charpred does not introduce negative recursion. The `getSign()` predicate may be
|
||||
* recursive.
|
||||
*
|
||||
* Concrete implementations extend one of the following subclasses:
|
||||
* - `ConstantSignExpr`, for expressions with a compile-time constant value.
|
||||
* - `FlowSignExpr`, for expressions whose sign can be computed from the signs of their operands.
|
||||
* - `CustomsignExpr`, for expressions whose sign can be computed by a language-specific
|
||||
* implementation.
|
||||
*
|
||||
* If the same expression matches more than one of the above subclasses, the sign is computed as
|
||||
* follows:
|
||||
* - The sign of a `ConstantSignExpr` is computed solely from `ConstantSignExpr.getSign()`,
|
||||
* regardless of any other subclasses.
|
||||
* - If a non-`ConstantSignExpr` expression matches exactly one of `FlowSignExpr` or
|
||||
* `CustomSignExpr`, the sign is computed by that class' `getSign()` predicate.
|
||||
* - If a non-`ConstantSignExpr` expression matches both `FlowSignExpr` and `CustomSignExpr`, the
|
||||
* sign is the _intersection_ of the signs of those two classes' `getSign()` predicates. Thus,
|
||||
* both classes have the opportunity to _restrict_ the set of possible signs, not to generate new
|
||||
* possible signs.
|
||||
* - If an expression does not match any of the three subclasses, then it can have any sign.
|
||||
*
|
||||
* Note that the `getSign()` predicate is introduced only in subclasses of `SignExpr`.
|
||||
*/
|
||||
abstract class SignExpr instanceof SemExpr {
|
||||
SignExpr() { not Specific::ignoreExprSign(this) }
|
||||
|
||||
final string toString() { result = super.toString() }
|
||||
|
||||
abstract Sign getSign();
|
||||
}
|
||||
|
||||
/** An expression whose sign is determined by its constant numeric value. */
|
||||
private class ConstantSignExpr extends SignExpr {
|
||||
ConstantSignExpr() {
|
||||
this instanceof SemConstantIntegerExpr or
|
||||
exists(this.(SemNumericLiteralExpr).getApproximateFloatValue())
|
||||
/** Gets the possible signs of this SSA definition. */
|
||||
abstract Sign getSign();
|
||||
}
|
||||
|
||||
final override Sign getSign() {
|
||||
exists(int i | this.(SemConstantIntegerExpr).getIntValue() = i |
|
||||
i < 0 and result = TNeg()
|
||||
/** An SSA definition whose sign is computed based on standard flow. */
|
||||
abstract private class FlowSignDef extends SignDef {
|
||||
abstract override Sign getSign();
|
||||
}
|
||||
|
||||
/** An SSA definition whose sign is determined by the sign of that definitions source expression. */
|
||||
private class ExplicitSignDef extends FlowSignDef instanceof SemSsaExplicitUpdate {
|
||||
final override Sign getSign() { result = semExprSign(super.getSourceExpr()) }
|
||||
}
|
||||
|
||||
/** An SSA Phi definition, whose sign is the union of the signs of its inputs. */
|
||||
private class PhiSignDef extends FlowSignDef instanceof SemSsaPhiNode {
|
||||
final override Sign getSign() {
|
||||
exists(SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge |
|
||||
edge.phiInput(this, inp) and
|
||||
result = semSsaSign(inp, edge)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** An SSA definition whose sign is computed by a language-specific implementation. */
|
||||
abstract class CustomSignDef extends SignDef {
|
||||
abstract override Sign getSign();
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression for which the analysis can compute the sign.
|
||||
*
|
||||
* The actual computation of the sign is done in an override of the `getSign()` predicate. The
|
||||
* charpred of any subclass must _not_ invoke `getSign()`, directly or indirectly. This ensures
|
||||
* that the charpred does not introduce negative recursion. The `getSign()` predicate may be
|
||||
* recursive.
|
||||
*
|
||||
* Concrete implementations extend one of the following subclasses:
|
||||
* - `ConstantSignExpr`, for expressions with a compile-time constant value.
|
||||
* - `FlowSignExpr`, for expressions whose sign can be computed from the signs of their operands.
|
||||
* - `CustomsignExpr`, for expressions whose sign can be computed by a language-specific
|
||||
* implementation.
|
||||
*
|
||||
* If the same expression matches more than one of the above subclasses, the sign is computed as
|
||||
* follows:
|
||||
* - The sign of a `ConstantSignExpr` is computed solely from `ConstantSignExpr.getSign()`,
|
||||
* regardless of any other subclasses.
|
||||
* - If a non-`ConstantSignExpr` expression matches exactly one of `FlowSignExpr` or
|
||||
* `CustomSignExpr`, the sign is computed by that class' `getSign()` predicate.
|
||||
* - If a non-`ConstantSignExpr` expression matches both `FlowSignExpr` and `CustomSignExpr`, the
|
||||
* sign is the _intersection_ of the signs of those two classes' `getSign()` predicates. Thus,
|
||||
* both classes have the opportunity to _restrict_ the set of possible signs, not to generate new
|
||||
* possible signs.
|
||||
* - If an expression does not match any of the three subclasses, then it can have any sign.
|
||||
*
|
||||
* Note that the `getSign()` predicate is introduced only in subclasses of `SignExpr`.
|
||||
*/
|
||||
abstract class SignExpr instanceof SemExpr {
|
||||
SignExpr() { not Specific::ignoreExprSign(this) }
|
||||
|
||||
final string toString() { result = super.toString() }
|
||||
|
||||
abstract Sign getSign();
|
||||
}
|
||||
|
||||
/** An expression whose sign is determined by its constant numeric value. */
|
||||
private class ConstantSignExpr extends SignExpr {
|
||||
ConstantSignExpr() {
|
||||
this instanceof SemConstantIntegerExpr or
|
||||
exists(this.(SemNumericLiteralExpr).getApproximateFloatValue())
|
||||
}
|
||||
|
||||
final override Sign getSign() {
|
||||
exists(int i | this.(SemConstantIntegerExpr).getIntValue() = i |
|
||||
i < 0 and result = TNeg()
|
||||
or
|
||||
i = 0 and result = TZero()
|
||||
or
|
||||
i > 0 and result = TPos()
|
||||
)
|
||||
or
|
||||
i = 0 and result = TZero()
|
||||
or
|
||||
i > 0 and result = TPos()
|
||||
)
|
||||
or
|
||||
not exists(this.(SemConstantIntegerExpr).getIntValue()) and
|
||||
exists(float f | f = this.(SemNumericLiteralExpr).getApproximateFloatValue() |
|
||||
f < 0 and result = TNeg()
|
||||
or
|
||||
f = 0 and result = TZero()
|
||||
or
|
||||
f > 0 and result = TPos()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
abstract private class NonConstantSignExpr extends SignExpr {
|
||||
NonConstantSignExpr() { not this instanceof ConstantSignExpr }
|
||||
|
||||
final override Sign getSign() {
|
||||
// The result is the _intersection_ of the signs computed from flow and by the language.
|
||||
(result = this.(FlowSignExpr).getSignRestriction() or not this instanceof FlowSignExpr) and
|
||||
(result = this.(CustomSignExpr).getSignRestriction() or not this instanceof CustomSignExpr)
|
||||
}
|
||||
}
|
||||
|
||||
/** An expression whose sign is computed from the signs of its operands. */
|
||||
abstract private class FlowSignExpr extends NonConstantSignExpr {
|
||||
abstract Sign getSignRestriction();
|
||||
}
|
||||
|
||||
/** An expression whose sign is computed by a language-specific implementation. */
|
||||
abstract class CustomSignExpr extends NonConstantSignExpr {
|
||||
abstract Sign getSignRestriction();
|
||||
}
|
||||
|
||||
/** An expression whose sign is unknown. */
|
||||
private class UnknownSignExpr extends SignExpr {
|
||||
UnknownSignExpr() {
|
||||
not this instanceof FlowSignExpr and
|
||||
not this instanceof CustomSignExpr and
|
||||
not this instanceof ConstantSignExpr and
|
||||
(
|
||||
// Only track numeric types.
|
||||
getTrackedType(this) instanceof SemNumericType
|
||||
or
|
||||
// Unless the language says to track this expression anyway.
|
||||
Specific::trackUnknownNonNumericExpr(this)
|
||||
)
|
||||
not exists(this.(SemConstantIntegerExpr).getIntValue()) and
|
||||
exists(float f | f = this.(SemNumericLiteralExpr).getApproximateFloatValue() |
|
||||
f < 0 and result = TNeg()
|
||||
or
|
||||
f = 0 and result = TZero()
|
||||
or
|
||||
f > 0 and result = TPos()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
final override Sign getSign() { semAnySign(result) }
|
||||
}
|
||||
abstract private class NonConstantSignExpr extends SignExpr {
|
||||
NonConstantSignExpr() { not this instanceof ConstantSignExpr }
|
||||
|
||||
/**
|
||||
* A `Load` expression whose sign is computed from the sign of its SSA definition, restricted by
|
||||
* inference from any intervening guards.
|
||||
*/
|
||||
class UseSignExpr extends FlowSignExpr {
|
||||
SemSsaVariable v;
|
||||
|
||||
UseSignExpr() { v.getAUse() = this }
|
||||
|
||||
override Sign getSignRestriction() {
|
||||
// Propagate via SSA
|
||||
// Propagate the sign from the def of `v`, incorporating any inference from guards.
|
||||
result = semSsaSign(v, any(SemSsaReadPositionBlock bb | bb.getAnExpr() = this))
|
||||
or
|
||||
// No block for this read. Just use the sign of the def.
|
||||
// REVIEW: How can this happen?
|
||||
not exists(SemSsaReadPositionBlock bb | bb.getAnExpr() = this) and
|
||||
result = semSsaDefSign(v)
|
||||
final override Sign getSign() {
|
||||
// The result is the _intersection_ of the signs computed from flow and by the language.
|
||||
(result = this.(FlowSignExpr).getSignRestriction() or not this instanceof FlowSignExpr) and
|
||||
(result = this.(CustomSignExpr).getSignRestriction() or not this instanceof CustomSignExpr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** A binary expression whose sign is computed from the signs of its operands. */
|
||||
private class BinarySignExpr extends FlowSignExpr {
|
||||
SemBinaryExpr binary;
|
||||
/** An expression whose sign is computed from the signs of its operands. */
|
||||
abstract private class FlowSignExpr extends NonConstantSignExpr {
|
||||
abstract Sign getSignRestriction();
|
||||
}
|
||||
|
||||
BinarySignExpr() { binary = this }
|
||||
/** An expression whose sign is computed by a language-specific implementation. */
|
||||
abstract class CustomSignExpr extends NonConstantSignExpr {
|
||||
abstract Sign getSignRestriction();
|
||||
}
|
||||
|
||||
override Sign getSignRestriction() {
|
||||
exists(SemExpr left, SemExpr right |
|
||||
binaryExprOperands(binary, left, right) and
|
||||
/** An expression whose sign is unknown. */
|
||||
private class UnknownSignExpr extends SignExpr {
|
||||
UnknownSignExpr() {
|
||||
not this instanceof FlowSignExpr and
|
||||
not this instanceof CustomSignExpr and
|
||||
not this instanceof ConstantSignExpr and
|
||||
(
|
||||
// Only track numeric types.
|
||||
Utils::getTrackedType(this) instanceof SemNumericType
|
||||
or
|
||||
// Unless the language says to track this expression anyway.
|
||||
Specific::trackUnknownNonNumericExpr(this)
|
||||
)
|
||||
}
|
||||
|
||||
final override Sign getSign() { semAnySign(result) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Load` expression whose sign is computed from the sign of its SSA definition, restricted by
|
||||
* inference from any intervening guards.
|
||||
*/
|
||||
class UseSignExpr extends FlowSignExpr {
|
||||
SemSsaVariable v;
|
||||
|
||||
UseSignExpr() { v.getAUse() = this }
|
||||
|
||||
override Sign getSignRestriction() {
|
||||
// Propagate via SSA
|
||||
// Propagate the sign from the def of `v`, incorporating any inference from guards.
|
||||
result = semSsaSign(v, any(SemSsaReadPositionBlock bb | bb.getAnExpr() = this))
|
||||
or
|
||||
// No block for this read. Just use the sign of the def.
|
||||
// REVIEW: How can this happen?
|
||||
not exists(SemSsaReadPositionBlock bb | bb.getAnExpr() = this) and
|
||||
result = semSsaDefSign(v)
|
||||
}
|
||||
}
|
||||
|
||||
/** A binary expression whose sign is computed from the signs of its operands. */
|
||||
private class BinarySignExpr extends FlowSignExpr {
|
||||
SemBinaryExpr binary;
|
||||
|
||||
BinarySignExpr() { binary = this }
|
||||
|
||||
override Sign getSignRestriction() {
|
||||
exists(SemExpr left, SemExpr right |
|
||||
binaryExprOperands(binary, left, right) and
|
||||
result =
|
||||
semExprSign(pragma[only_bind_out](left))
|
||||
.applyBinaryOp(semExprSign(pragma[only_bind_out](right)), binary.getOpcode())
|
||||
)
|
||||
or
|
||||
exists(SemDivExpr div | div = binary |
|
||||
result = semExprSign(div.getLeftOperand()) and
|
||||
result != TZero() and
|
||||
div.getRightOperand().(SemFloatingPointLiteralExpr).getFloatValue() = 0
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate binaryExprOperands(SemBinaryExpr binary, SemExpr left, SemExpr right) {
|
||||
binary.getLeftOperand() = left and binary.getRightOperand() = right
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Convert`, `Box`, or `Unbox` expression.
|
||||
*/
|
||||
private class SemCastExpr instanceof SemUnaryExpr {
|
||||
string toString() { result = super.toString() }
|
||||
|
||||
SemCastExpr() {
|
||||
this instanceof SemConvertExpr
|
||||
or
|
||||
this instanceof SemBoxExpr
|
||||
or
|
||||
this instanceof SemUnboxExpr
|
||||
}
|
||||
}
|
||||
|
||||
/** A unary expression whose sign is computed from the sign of its operand. */
|
||||
private class UnarySignExpr extends FlowSignExpr {
|
||||
SemUnaryExpr unary;
|
||||
|
||||
UnarySignExpr() { unary = this and not this instanceof SemCastExpr }
|
||||
|
||||
override Sign getSignRestriction() {
|
||||
result =
|
||||
semExprSign(pragma[only_bind_out](left))
|
||||
.applyBinaryOp(semExprSign(pragma[only_bind_out](right)), binary.getOpcode())
|
||||
)
|
||||
or
|
||||
exists(SemDivExpr div | div = binary |
|
||||
result = semExprSign(div.getLeftOperand()) and
|
||||
result != TZero() and
|
||||
div.getRightOperand().(SemFloatingPointLiteralExpr).getFloatValue() = 0
|
||||
semExprSign(pragma[only_bind_out](unary.getOperand())).applyUnaryOp(unary.getOpcode())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Convert`, `Box`, or `Unbox` expression, whose sign is computed based on
|
||||
* the sign of its operand and the source and destination types.
|
||||
*/
|
||||
abstract private class CastSignExpr extends FlowSignExpr {
|
||||
SemUnaryExpr cast;
|
||||
|
||||
CastSignExpr() { cast = this and cast instanceof SemCastExpr }
|
||||
|
||||
override Sign getSignRestriction() { result = semExprSign(cast.getOperand()) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Convert` expression.
|
||||
*/
|
||||
private class ConvertSignExpr extends CastSignExpr {
|
||||
override SemConvertExpr cast;
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Box` expression.
|
||||
*/
|
||||
private class BoxSignExpr extends CastSignExpr {
|
||||
override SemBoxExpr cast;
|
||||
}
|
||||
|
||||
/**
|
||||
* An `Unbox` expression.
|
||||
*/
|
||||
private class UnboxSignExpr extends CastSignExpr {
|
||||
override SemUnboxExpr cast;
|
||||
|
||||
UnboxSignExpr() {
|
||||
exists(SemType fromType | fromType = Utils::getTrackedType(cast.getOperand()) |
|
||||
// Only numeric source types are handled here.
|
||||
fromType instanceof SemNumericType
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private predicate unknownSign(SemExpr e) { e instanceof UnknownSignExpr }
|
||||
|
||||
/**
|
||||
* Holds if `lowerbound` is a lower bound for `v` at `pos`. This is restricted
|
||||
* to only include bounds for which we might determine a sign.
|
||||
*/
|
||||
private predicate lowerBound(
|
||||
SemExpr lowerbound, SemSsaVariable v, SemSsaReadPosition pos, boolean isStrict
|
||||
) {
|
||||
exists(boolean testIsTrue, SemRelationalExpr comp |
|
||||
pos.hasReadOfVar(v) and
|
||||
semGuardControlsSsaRead(semGetComparisonGuard(comp), pos, testIsTrue) and
|
||||
not unknownSign(lowerbound)
|
||||
|
|
||||
testIsTrue = true and
|
||||
comp.getLesserOperand() = lowerbound and
|
||||
comp.getGreaterOperand() = Utils::semSsaRead(v, D::fromInt(0)) and
|
||||
(if comp.isStrict() then isStrict = true else isStrict = false)
|
||||
or
|
||||
testIsTrue = false and
|
||||
comp.getGreaterOperand() = lowerbound and
|
||||
comp.getLesserOperand() = Utils::semSsaRead(v, D::fromInt(0)) and
|
||||
(if comp.isStrict() then isStrict = false else isStrict = true)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate binaryExprOperands(SemBinaryExpr binary, SemExpr left, SemExpr right) {
|
||||
binary.getLeftOperand() = left and binary.getRightOperand() = right
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Convert`, `Box`, or `Unbox` expression.
|
||||
*/
|
||||
private class SemCastExpr extends SemUnaryExpr {
|
||||
SemCastExpr() {
|
||||
this instanceof SemConvertExpr
|
||||
or
|
||||
this instanceof SemBoxExpr
|
||||
or
|
||||
this instanceof SemUnboxExpr
|
||||
}
|
||||
}
|
||||
|
||||
/** A unary expression whose sign is computed from the sign of its operand. */
|
||||
private class UnarySignExpr extends FlowSignExpr {
|
||||
SemUnaryExpr unary;
|
||||
|
||||
UnarySignExpr() { unary = this and not this instanceof SemCastExpr }
|
||||
|
||||
override Sign getSignRestriction() {
|
||||
result = semExprSign(pragma[only_bind_out](unary.getOperand())).applyUnaryOp(unary.getOpcode())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Convert`, `Box`, or `Unbox` expression, whose sign is computed based on
|
||||
* the sign of its operand and the source and destination types.
|
||||
*/
|
||||
abstract private class CastSignExpr extends FlowSignExpr {
|
||||
SemUnaryExpr cast;
|
||||
|
||||
CastSignExpr() { cast = this and cast instanceof SemCastExpr }
|
||||
|
||||
override Sign getSignRestriction() { result = semExprSign(cast.getOperand()) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Convert` expression.
|
||||
*/
|
||||
private class ConvertSignExpr extends CastSignExpr {
|
||||
override SemConvertExpr cast;
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Box` expression.
|
||||
*/
|
||||
private class BoxSignExpr extends CastSignExpr {
|
||||
override SemBoxExpr cast;
|
||||
}
|
||||
|
||||
/**
|
||||
* An `Unbox` expression.
|
||||
*/
|
||||
private class UnboxSignExpr extends CastSignExpr {
|
||||
override SemUnboxExpr cast;
|
||||
|
||||
UnboxSignExpr() {
|
||||
exists(SemType fromType | fromType = getTrackedType(cast.getOperand()) |
|
||||
// Only numeric source types are handled here.
|
||||
fromType instanceof SemNumericType
|
||||
/**
|
||||
* Holds if `upperbound` is an upper bound for `v` at `pos`. This is restricted
|
||||
* to only include bounds for which we might determine a sign.
|
||||
*/
|
||||
private predicate upperBound(
|
||||
SemExpr upperbound, SemSsaVariable v, SemSsaReadPosition pos, boolean isStrict
|
||||
) {
|
||||
exists(boolean testIsTrue, SemRelationalExpr comp |
|
||||
pos.hasReadOfVar(v) and
|
||||
semGuardControlsSsaRead(semGetComparisonGuard(comp), pos, testIsTrue) and
|
||||
not unknownSign(upperbound)
|
||||
|
|
||||
testIsTrue = true and
|
||||
comp.getGreaterOperand() = upperbound and
|
||||
comp.getLesserOperand() = Utils::semSsaRead(v, D::fromInt(0)) and
|
||||
(if comp.isStrict() then isStrict = true else isStrict = false)
|
||||
or
|
||||
testIsTrue = false and
|
||||
comp.getLesserOperand() = upperbound and
|
||||
comp.getGreaterOperand() = Utils::semSsaRead(v, D::fromInt(0)) and
|
||||
(if comp.isStrict() then isStrict = false else isStrict = true)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private predicate unknownSign(SemExpr e) { e instanceof UnknownSignExpr }
|
||||
/**
|
||||
* Holds if `eqbound` is an equality/inequality for `v` at `pos`. This is
|
||||
* restricted to only include bounds for which we might determine a sign. The
|
||||
* boolean `isEq` gives the polarity:
|
||||
* - `isEq = true` : `v = eqbound`
|
||||
* - `isEq = false` : `v != eqbound`
|
||||
*/
|
||||
private predicate eqBound(SemExpr eqbound, SemSsaVariable v, SemSsaReadPosition pos, boolean isEq) {
|
||||
exists(SemGuard guard, boolean testIsTrue, boolean polarity |
|
||||
pos.hasReadOfVar(v) and
|
||||
semGuardControlsSsaRead(guard, pos, testIsTrue) and
|
||||
guard.isEquality(eqbound, Utils::semSsaRead(v, D::fromInt(0)), polarity) and
|
||||
isEq = polarity.booleanXor(testIsTrue).booleanNot() and
|
||||
not unknownSign(eqbound)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `lowerbound` is a lower bound for `v` at `pos`. This is restricted
|
||||
* to only include bounds for which we might determine a sign.
|
||||
*/
|
||||
private predicate lowerBound(
|
||||
SemExpr lowerbound, SemSsaVariable v, SemSsaReadPosition pos, boolean isStrict
|
||||
) {
|
||||
exists(boolean testIsTrue, SemRelationalExpr comp |
|
||||
pos.hasReadOfVar(v) and
|
||||
semGuardControlsSsaRead(semGetComparisonGuard(comp), pos, testIsTrue) and
|
||||
not unknownSign(lowerbound)
|
||||
|
|
||||
testIsTrue = true and
|
||||
comp.getLesserOperand() = lowerbound and
|
||||
comp.getGreaterOperand() = semSsaRead(v, 0) and
|
||||
(if comp.isStrict() then isStrict = true else isStrict = false)
|
||||
/**
|
||||
* Holds if `bound` is a bound for `v` at `pos` that needs to be positive in
|
||||
* order for `v` to be positive.
|
||||
*/
|
||||
private predicate posBound(SemExpr bound, SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
upperBound(bound, v, pos, _) or
|
||||
eqBound(bound, v, pos, true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `bound` is a bound for `v` at `pos` that needs to be negative in
|
||||
* order for `v` to be negative.
|
||||
*/
|
||||
private predicate negBound(SemExpr bound, SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
lowerBound(bound, v, pos, _) or
|
||||
eqBound(bound, v, pos, true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `bound` is a bound for `v` at `pos` that can restrict whether `v`
|
||||
* can be zero.
|
||||
*/
|
||||
private predicate zeroBound(SemExpr bound, SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
lowerBound(bound, v, pos, _) or
|
||||
upperBound(bound, v, pos, _) or
|
||||
eqBound(bound, v, pos, _)
|
||||
}
|
||||
|
||||
/** Holds if `bound` allows `v` to be positive at `pos`. */
|
||||
private predicate posBoundOk(SemExpr bound, SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
posBound(bound, v, pos) and TPos() = semExprSign(bound)
|
||||
}
|
||||
|
||||
/** Holds if `bound` allows `v` to be negative at `pos`. */
|
||||
private predicate negBoundOk(SemExpr bound, SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
negBound(bound, v, pos) and TNeg() = semExprSign(bound)
|
||||
}
|
||||
|
||||
/** Holds if `bound` allows `v` to be zero at `pos`. */
|
||||
private predicate zeroBoundOk(SemExpr bound, SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
lowerBound(bound, v, pos, _) and TNeg() = semExprSign(bound)
|
||||
or
|
||||
testIsTrue = false and
|
||||
comp.getGreaterOperand() = lowerbound and
|
||||
comp.getLesserOperand() = semSsaRead(v, 0) and
|
||||
(if comp.isStrict() then isStrict = false else isStrict = true)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `upperbound` is an upper bound for `v` at `pos`. This is restricted
|
||||
* to only include bounds for which we might determine a sign.
|
||||
*/
|
||||
private predicate upperBound(
|
||||
SemExpr upperbound, SemSsaVariable v, SemSsaReadPosition pos, boolean isStrict
|
||||
) {
|
||||
exists(boolean testIsTrue, SemRelationalExpr comp |
|
||||
pos.hasReadOfVar(v) and
|
||||
semGuardControlsSsaRead(semGetComparisonGuard(comp), pos, testIsTrue) and
|
||||
not unknownSign(upperbound)
|
||||
|
|
||||
testIsTrue = true and
|
||||
comp.getGreaterOperand() = upperbound and
|
||||
comp.getLesserOperand() = semSsaRead(v, 0) and
|
||||
(if comp.isStrict() then isStrict = true else isStrict = false)
|
||||
lowerBound(bound, v, pos, false) and TZero() = semExprSign(bound)
|
||||
or
|
||||
testIsTrue = false and
|
||||
comp.getLesserOperand() = upperbound and
|
||||
comp.getGreaterOperand() = semSsaRead(v, 0) and
|
||||
(if comp.isStrict() then isStrict = false else isStrict = true)
|
||||
)
|
||||
}
|
||||
upperBound(bound, v, pos, _) and TPos() = semExprSign(bound)
|
||||
or
|
||||
upperBound(bound, v, pos, false) and TZero() = semExprSign(bound)
|
||||
or
|
||||
eqBound(bound, v, pos, true) and TZero() = semExprSign(bound)
|
||||
or
|
||||
eqBound(bound, v, pos, false) and TZero() != semExprSign(bound)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `eqbound` is an equality/inequality for `v` at `pos`. This is
|
||||
* restricted to only include bounds for which we might determine a sign. The
|
||||
* boolean `isEq` gives the polarity:
|
||||
* - `isEq = true` : `v = eqbound`
|
||||
* - `isEq = false` : `v != eqbound`
|
||||
*/
|
||||
private predicate eqBound(SemExpr eqbound, SemSsaVariable v, SemSsaReadPosition pos, boolean isEq) {
|
||||
exists(SemGuard guard, boolean testIsTrue, boolean polarity |
|
||||
/**
|
||||
* Holds if there is a bound that might restrict whether `v` has the sign `s`
|
||||
* at `pos`.
|
||||
*/
|
||||
private predicate hasGuard(SemSsaVariable v, SemSsaReadPosition pos, Sign s) {
|
||||
s = TPos() and posBound(_, v, pos)
|
||||
or
|
||||
s = TNeg() and negBound(_, v, pos)
|
||||
or
|
||||
s = TZero() and zeroBound(_, v, pos)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a possible sign of `v` at `pos` based on its definition, where the sign
|
||||
* might be ruled out by a guard.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private Sign guardedSsaSign(SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
result = semSsaDefSign(v) and
|
||||
pos.hasReadOfVar(v) and
|
||||
semGuardControlsSsaRead(guard, pos, testIsTrue) and
|
||||
guard.isEquality(eqbound, semSsaRead(v, 0), polarity) and
|
||||
isEq = polarity.booleanXor(testIsTrue).booleanNot() and
|
||||
not unknownSign(eqbound)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `bound` is a bound for `v` at `pos` that needs to be positive in
|
||||
* order for `v` to be positive.
|
||||
*/
|
||||
private predicate posBound(SemExpr bound, SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
upperBound(bound, v, pos, _) or
|
||||
eqBound(bound, v, pos, true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `bound` is a bound for `v` at `pos` that needs to be negative in
|
||||
* order for `v` to be negative.
|
||||
*/
|
||||
private predicate negBound(SemExpr bound, SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
lowerBound(bound, v, pos, _) or
|
||||
eqBound(bound, v, pos, true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `bound` is a bound for `v` at `pos` that can restrict whether `v`
|
||||
* can be zero.
|
||||
*/
|
||||
private predicate zeroBound(SemExpr bound, SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
lowerBound(bound, v, pos, _) or
|
||||
upperBound(bound, v, pos, _) or
|
||||
eqBound(bound, v, pos, _)
|
||||
}
|
||||
|
||||
/** Holds if `bound` allows `v` to be positive at `pos`. */
|
||||
private predicate posBoundOk(SemExpr bound, SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
posBound(bound, v, pos) and TPos() = semExprSign(bound)
|
||||
}
|
||||
|
||||
/** Holds if `bound` allows `v` to be negative at `pos`. */
|
||||
private predicate negBoundOk(SemExpr bound, SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
negBound(bound, v, pos) and TNeg() = semExprSign(bound)
|
||||
}
|
||||
|
||||
/** Holds if `bound` allows `v` to be zero at `pos`. */
|
||||
private predicate zeroBoundOk(SemExpr bound, SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
lowerBound(bound, v, pos, _) and TNeg() = semExprSign(bound)
|
||||
or
|
||||
lowerBound(bound, v, pos, false) and TZero() = semExprSign(bound)
|
||||
or
|
||||
upperBound(bound, v, pos, _) and TPos() = semExprSign(bound)
|
||||
or
|
||||
upperBound(bound, v, pos, false) and TZero() = semExprSign(bound)
|
||||
or
|
||||
eqBound(bound, v, pos, true) and TZero() = semExprSign(bound)
|
||||
or
|
||||
eqBound(bound, v, pos, false) and TZero() != semExprSign(bound)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there is a bound that might restrict whether `v` has the sign `s`
|
||||
* at `pos`.
|
||||
*/
|
||||
private predicate hasGuard(SemSsaVariable v, SemSsaReadPosition pos, Sign s) {
|
||||
s = TPos() and posBound(_, v, pos)
|
||||
or
|
||||
s = TNeg() and negBound(_, v, pos)
|
||||
or
|
||||
s = TZero() and zeroBound(_, v, pos)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a possible sign of `v` at `pos` based on its definition, where the sign
|
||||
* might be ruled out by a guard.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private Sign guardedSsaSign(SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
result = semSsaDefSign(v) and
|
||||
pos.hasReadOfVar(v) and
|
||||
hasGuard(v, pos, result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a possible sign of `v` at `pos` based on its definition, where no guard
|
||||
* can rule it out.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private Sign unguardedSsaSign(SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
result = semSsaDefSign(v) and
|
||||
pos.hasReadOfVar(v) and
|
||||
not hasGuard(v, pos, result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a possible sign of `v` at read position `pos`, where a guard could have
|
||||
* ruled out the sign but does not.
|
||||
* This does not check that the definition of `v` also allows the sign.
|
||||
*/
|
||||
private Sign guardedSsaSignOk(SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
result = TPos() and
|
||||
forex(SemExpr bound | posBound(bound, v, pos) | posBoundOk(bound, v, pos))
|
||||
or
|
||||
result = TNeg() and
|
||||
forex(SemExpr bound | negBound(bound, v, pos) | negBoundOk(bound, v, pos))
|
||||
or
|
||||
result = TZero() and
|
||||
forex(SemExpr bound | zeroBound(bound, v, pos) | zeroBoundOk(bound, v, pos))
|
||||
}
|
||||
|
||||
/** Gets a possible sign for `v` at `pos`. */
|
||||
private Sign semSsaSign(SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
result = unguardedSsaSign(v, pos)
|
||||
or
|
||||
result = guardedSsaSign(v, pos) and
|
||||
result = guardedSsaSignOk(v, pos)
|
||||
}
|
||||
|
||||
/** Gets a possible sign for `v`. */
|
||||
pragma[nomagic]
|
||||
Sign semSsaDefSign(SemSsaVariable v) { result = v.(SignDef).getSign() }
|
||||
|
||||
/** Gets a possible sign for `e`. */
|
||||
cached
|
||||
Sign semExprSign(SemExpr e) {
|
||||
exists(Sign s | s = e.(SignExpr).getSign() |
|
||||
if
|
||||
getTrackedType(e) instanceof SemUnsignedIntegerType and
|
||||
s = TNeg() and
|
||||
not Specific::ignoreTypeRestrictions(e)
|
||||
then result = TPos()
|
||||
else result = s
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Dummy predicate that holds for any sign. This is added to improve readability
|
||||
* of cases where the sign is unrestricted.
|
||||
*/
|
||||
predicate semAnySign(Sign s) { any() }
|
||||
|
||||
/** Holds if `e` can be positive and cannot be negative. */
|
||||
predicate semPositive(SemExpr e) {
|
||||
semExprSign(e) = TPos() and
|
||||
not semExprSign(e) = TNeg()
|
||||
}
|
||||
|
||||
/** Holds if `e` can be negative and cannot be positive. */
|
||||
predicate semNegative(SemExpr e) {
|
||||
semExprSign(e) = TNeg() and
|
||||
not semExprSign(e) = TPos()
|
||||
}
|
||||
|
||||
/** Holds if `e` is strictly positive. */
|
||||
predicate semStrictlyPositive(SemExpr e) {
|
||||
semExprSign(e) = TPos() and
|
||||
not semExprSign(e) = TNeg() and
|
||||
not semExprSign(e) = TZero()
|
||||
}
|
||||
|
||||
/** Holds if `e` is strictly negative. */
|
||||
predicate semStrictlyNegative(SemExpr e) {
|
||||
semExprSign(e) = TNeg() and
|
||||
not semExprSign(e) = TPos() and
|
||||
not semExprSign(e) = TZero()
|
||||
hasGuard(v, pos, result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a possible sign of `v` at `pos` based on its definition, where no guard
|
||||
* can rule it out.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private Sign unguardedSsaSign(SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
result = semSsaDefSign(v) and
|
||||
pos.hasReadOfVar(v) and
|
||||
not hasGuard(v, pos, result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a possible sign of `v` at read position `pos`, where a guard could have
|
||||
* ruled out the sign but does not.
|
||||
* This does not check that the definition of `v` also allows the sign.
|
||||
*/
|
||||
private Sign guardedSsaSignOk(SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
result = TPos() and
|
||||
forex(SemExpr bound | posBound(bound, v, pos) | posBoundOk(bound, v, pos))
|
||||
or
|
||||
result = TNeg() and
|
||||
forex(SemExpr bound | negBound(bound, v, pos) | negBoundOk(bound, v, pos))
|
||||
or
|
||||
result = TZero() and
|
||||
forex(SemExpr bound | zeroBound(bound, v, pos) | zeroBoundOk(bound, v, pos))
|
||||
}
|
||||
|
||||
/** Gets a possible sign for `v` at `pos`. */
|
||||
private Sign semSsaSign(SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
result = unguardedSsaSign(v, pos)
|
||||
or
|
||||
result = guardedSsaSign(v, pos) and
|
||||
result = guardedSsaSignOk(v, pos)
|
||||
}
|
||||
|
||||
/** Gets a possible sign for `v`. */
|
||||
pragma[nomagic]
|
||||
Sign semSsaDefSign(SemSsaVariable v) { result = v.(SignDef).getSign() }
|
||||
|
||||
/** Gets a possible sign for `e`. */
|
||||
cached
|
||||
Sign semExprSign(SemExpr e) {
|
||||
exists(Sign s | s = e.(SignExpr).getSign() |
|
||||
if
|
||||
Utils::getTrackedType(e) instanceof SemUnsignedIntegerType and
|
||||
s = TNeg() and
|
||||
not Specific::ignoreTypeRestrictions(e)
|
||||
then result = TPos()
|
||||
else result = s
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Dummy predicate that holds for any sign. This is added to improve readability
|
||||
* of cases where the sign is unrestricted.
|
||||
*/
|
||||
predicate semAnySign(Sign s) { any() }
|
||||
|
||||
/** Holds if `e` can be positive and cannot be negative. */
|
||||
predicate semPositive(SemExpr e) {
|
||||
semExprSign(e) = TPos() and
|
||||
not semExprSign(e) = TNeg()
|
||||
}
|
||||
|
||||
/** Holds if `e` can be negative and cannot be positive. */
|
||||
predicate semNegative(SemExpr e) {
|
||||
semExprSign(e) = TNeg() and
|
||||
not semExprSign(e) = TPos()
|
||||
}
|
||||
|
||||
/** Holds if `e` is strictly positive. */
|
||||
predicate semStrictlyPositive(SemExpr e) {
|
||||
semExprSign(e) = TPos() and
|
||||
not semExprSign(e) = TNeg() and
|
||||
not semExprSign(e) = TZero()
|
||||
}
|
||||
|
||||
/** Holds if `e` is strictly negative. */
|
||||
predicate semStrictlyNegative(SemExpr e) {
|
||||
semExprSign(e) = TNeg() and
|
||||
not semExprSign(e) = TPos() and
|
||||
not semExprSign(e) = TZero()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/cpp-all
|
||||
version: 0.5.1-dev
|
||||
version: 0.5.3-dev
|
||||
groups: cpp
|
||||
dbscheme: semmlecode.cpp.dbscheme
|
||||
extractor: cpp
|
||||
|
||||
@@ -667,23 +667,78 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(NodeEx arg |
|
||||
fwdFlow(arg, _, config) and
|
||||
viableParamArgEx(_, node, arg) and
|
||||
cc = true and
|
||||
not fullBarrier(node, config)
|
||||
)
|
||||
fwdFlowIn(_, _, _, node, config) and
|
||||
cc = true
|
||||
or
|
||||
// flow out of a callable
|
||||
fwdFlowOut(_, node, false, config) and
|
||||
cc = false
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call |
|
||||
fwdFlowOut(call, node, false, config) and
|
||||
cc = false
|
||||
or
|
||||
fwdFlowOutFromArg(call, node, config) and
|
||||
fwdFlowIsEntered(call, cc, config)
|
||||
)
|
||||
}
|
||||
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate fwdFlowIn(
|
||||
DataFlowCall call, NodeEx arg, Cc cc, ParamNodeEx p, Configuration config
|
||||
) {
|
||||
// call context cannot help reduce virtual dispatch
|
||||
fwdFlow(arg, cc, config) and
|
||||
viableParamArgEx(call, p, arg) and
|
||||
not fullBarrier(p, config) and
|
||||
(
|
||||
cc = false
|
||||
or
|
||||
cc = true and
|
||||
not reducedViableImplInCallContext(call, _, _)
|
||||
)
|
||||
or
|
||||
// call context may help reduce virtual dispatch
|
||||
exists(DataFlowCallable target |
|
||||
fwdFlowInReducedViableImplInSomeCallContext(call, arg, p, target, config) and
|
||||
target = viableImplInSomeFwdFlowCallContextExt(call, config) and
|
||||
cc = true
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an argument to `call` is reached in the flow covered by `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) {
|
||||
fwdFlowIn(call, _, cc, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowInReducedViableImplInSomeCallContext(
|
||||
DataFlowCall call, NodeEx arg, ParamNodeEx p, DataFlowCallable target, Configuration config
|
||||
) {
|
||||
fwdFlow(arg, true, config) and
|
||||
viableParamArgEx(call, p, arg) and
|
||||
reducedViableImplInCallContext(call, _, _) and
|
||||
target = p.getEnclosingCallable() and
|
||||
not fullBarrier(p, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a viable dispatch target of `call` in the context `ctx`. This is
|
||||
* restricted to those `call`s for which a context might make a difference,
|
||||
* and to `ctx`s that are reachable in `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private DataFlowCallable viableImplInSomeFwdFlowCallContextExt(
|
||||
DataFlowCall call, Configuration config
|
||||
) {
|
||||
exists(DataFlowCall ctx |
|
||||
fwdFlowIsEntered(ctx, _, config) and
|
||||
result = viableImplInCallContextExt(call, ctx)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate fwdFlow(NodeEx node, Configuration config) { fwdFlow(node, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -726,7 +781,8 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate fwdFlowOut(DataFlowCall call, NodeEx out, Cc cc, Configuration config) {
|
||||
exists(ReturnPosition pos |
|
||||
fwdFlowReturnPosition(pos, cc, config) and
|
||||
@@ -740,17 +796,6 @@ private module Stage1 implements StageSig {
|
||||
fwdFlowOut(call, out, true, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an argument to `call` is reached in the flow covered by `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) {
|
||||
exists(ArgNodeEx arg |
|
||||
fwdFlow(arg, cc, config) and
|
||||
viableParamArgEx(call, _, arg)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate stateStepFwd(FlowState state1, FlowState state2, Configuration config) {
|
||||
exists(NodeEx node1 |
|
||||
additionalLocalStateStep(node1, state1, _, state2, config) or
|
||||
@@ -817,13 +862,8 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(DataFlowCall call |
|
||||
revFlowIn(call, node, false, config) and
|
||||
toReturn = false
|
||||
or
|
||||
revFlowInToReturn(call, node, config) and
|
||||
revFlowIsReturned(call, toReturn, config)
|
||||
)
|
||||
revFlowIn(_, node, false, config) and
|
||||
toReturn = false
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(ReturnPosition pos |
|
||||
@@ -831,6 +871,12 @@ private module Stage1 implements StageSig {
|
||||
node.(RetNodeEx).getReturnPosition() = pos and
|
||||
toReturn = true
|
||||
)
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call |
|
||||
revFlowInToReturn(call, node, config) and
|
||||
revFlowIsReturned(call, toReturn, config)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -886,11 +932,11 @@ private module Stage1 implements StageSig {
|
||||
additional predicate viableParamArgNodeCandFwd1(
|
||||
DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
|
||||
) {
|
||||
viableParamArgEx(call, p, arg) and
|
||||
fwdFlow(arg, config)
|
||||
fwdFlowIn(call, arg, _, p, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate revFlowIn(
|
||||
DataFlowCall call, ArgNodeEx arg, boolean toReturn, Configuration config
|
||||
) {
|
||||
@@ -1223,7 +1269,16 @@ private module MkStage<StageSig PrevStage> {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail);
|
||||
|
||||
Content getHeadContent(Ap ap);
|
||||
/**
|
||||
* An approximation of `Content` that corresponds to the precision level of
|
||||
* `Ap`, such that the mappings from both `Ap` and `Content` to this type
|
||||
* are functional.
|
||||
*/
|
||||
class ApHeadContent;
|
||||
|
||||
ApHeadContent getHeadContent(Ap ap);
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c);
|
||||
|
||||
class ApOption;
|
||||
|
||||
@@ -1471,20 +1526,20 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
}
|
||||
|
||||
private class ApNonNil instanceof Ap {
|
||||
pragma[nomagic]
|
||||
ApNonNil() { not this instanceof ApNil }
|
||||
|
||||
string toString() { result = "" }
|
||||
pragma[nomagic]
|
||||
private predicate readStepCand(
|
||||
NodeEx node1, ApHeadContent apc, Content c, NodeEx node2, Configuration config
|
||||
) {
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
apc = projectToHeadContent(c)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead0(
|
||||
NodeEx node1, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ApNonNil ap,
|
||||
Configuration config
|
||||
bindingset[node1, apc]
|
||||
pragma[inline_late]
|
||||
private predicate readStepCand0(
|
||||
NodeEx node1, ApHeadContent apc, Content c, NodeEx node2, Configuration config
|
||||
) {
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, _, _, config)
|
||||
readStepCand(node1, apc, c, node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1492,9 +1547,11 @@ private module MkStage<StageSig PrevStage> {
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
ParamNodeOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
getHeadContent(ap) = c
|
||||
exists(ApHeadContent apc |
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
apc = getHeadContent(ap) and
|
||||
readStepCand0(node1, apc, c, node2, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -2068,8 +2125,12 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) }
|
||||
|
||||
class ApHeadContent = Unit;
|
||||
|
||||
pragma[inline]
|
||||
Content getHeadContent(Ap ap) { exists(result) and ap = true }
|
||||
ApHeadContent getHeadContent(Ap ap) { exists(result) and ap = true }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { any() }
|
||||
|
||||
class ApOption = BooleanOption;
|
||||
|
||||
@@ -2333,8 +2394,12 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result.getAHead() = tc and exists(tail) }
|
||||
|
||||
class ApHeadContent = ContentApprox;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getAHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
predicate projectToHeadContent = getContentApprox/1;
|
||||
|
||||
class ApOption = ApproxAccessPathFrontOption;
|
||||
|
||||
@@ -2409,8 +2474,12 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) }
|
||||
|
||||
class ApHeadContent = Content;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { result = c }
|
||||
|
||||
class ApOption = AccessPathFrontOption;
|
||||
|
||||
@@ -2739,8 +2808,12 @@ private module Stage5Param implements MkStage<Stage4>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) }
|
||||
|
||||
class ApHeadContent = Content;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { result = c }
|
||||
|
||||
class ApOption = AccessPathApproxOption;
|
||||
|
||||
|
||||
@@ -667,23 +667,78 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(NodeEx arg |
|
||||
fwdFlow(arg, _, config) and
|
||||
viableParamArgEx(_, node, arg) and
|
||||
cc = true and
|
||||
not fullBarrier(node, config)
|
||||
)
|
||||
fwdFlowIn(_, _, _, node, config) and
|
||||
cc = true
|
||||
or
|
||||
// flow out of a callable
|
||||
fwdFlowOut(_, node, false, config) and
|
||||
cc = false
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call |
|
||||
fwdFlowOut(call, node, false, config) and
|
||||
cc = false
|
||||
or
|
||||
fwdFlowOutFromArg(call, node, config) and
|
||||
fwdFlowIsEntered(call, cc, config)
|
||||
)
|
||||
}
|
||||
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate fwdFlowIn(
|
||||
DataFlowCall call, NodeEx arg, Cc cc, ParamNodeEx p, Configuration config
|
||||
) {
|
||||
// call context cannot help reduce virtual dispatch
|
||||
fwdFlow(arg, cc, config) and
|
||||
viableParamArgEx(call, p, arg) and
|
||||
not fullBarrier(p, config) and
|
||||
(
|
||||
cc = false
|
||||
or
|
||||
cc = true and
|
||||
not reducedViableImplInCallContext(call, _, _)
|
||||
)
|
||||
or
|
||||
// call context may help reduce virtual dispatch
|
||||
exists(DataFlowCallable target |
|
||||
fwdFlowInReducedViableImplInSomeCallContext(call, arg, p, target, config) and
|
||||
target = viableImplInSomeFwdFlowCallContextExt(call, config) and
|
||||
cc = true
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an argument to `call` is reached in the flow covered by `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) {
|
||||
fwdFlowIn(call, _, cc, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowInReducedViableImplInSomeCallContext(
|
||||
DataFlowCall call, NodeEx arg, ParamNodeEx p, DataFlowCallable target, Configuration config
|
||||
) {
|
||||
fwdFlow(arg, true, config) and
|
||||
viableParamArgEx(call, p, arg) and
|
||||
reducedViableImplInCallContext(call, _, _) and
|
||||
target = p.getEnclosingCallable() and
|
||||
not fullBarrier(p, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a viable dispatch target of `call` in the context `ctx`. This is
|
||||
* restricted to those `call`s for which a context might make a difference,
|
||||
* and to `ctx`s that are reachable in `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private DataFlowCallable viableImplInSomeFwdFlowCallContextExt(
|
||||
DataFlowCall call, Configuration config
|
||||
) {
|
||||
exists(DataFlowCall ctx |
|
||||
fwdFlowIsEntered(ctx, _, config) and
|
||||
result = viableImplInCallContextExt(call, ctx)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate fwdFlow(NodeEx node, Configuration config) { fwdFlow(node, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -726,7 +781,8 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate fwdFlowOut(DataFlowCall call, NodeEx out, Cc cc, Configuration config) {
|
||||
exists(ReturnPosition pos |
|
||||
fwdFlowReturnPosition(pos, cc, config) and
|
||||
@@ -740,17 +796,6 @@ private module Stage1 implements StageSig {
|
||||
fwdFlowOut(call, out, true, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an argument to `call` is reached in the flow covered by `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) {
|
||||
exists(ArgNodeEx arg |
|
||||
fwdFlow(arg, cc, config) and
|
||||
viableParamArgEx(call, _, arg)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate stateStepFwd(FlowState state1, FlowState state2, Configuration config) {
|
||||
exists(NodeEx node1 |
|
||||
additionalLocalStateStep(node1, state1, _, state2, config) or
|
||||
@@ -817,13 +862,8 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(DataFlowCall call |
|
||||
revFlowIn(call, node, false, config) and
|
||||
toReturn = false
|
||||
or
|
||||
revFlowInToReturn(call, node, config) and
|
||||
revFlowIsReturned(call, toReturn, config)
|
||||
)
|
||||
revFlowIn(_, node, false, config) and
|
||||
toReturn = false
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(ReturnPosition pos |
|
||||
@@ -831,6 +871,12 @@ private module Stage1 implements StageSig {
|
||||
node.(RetNodeEx).getReturnPosition() = pos and
|
||||
toReturn = true
|
||||
)
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call |
|
||||
revFlowInToReturn(call, node, config) and
|
||||
revFlowIsReturned(call, toReturn, config)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -886,11 +932,11 @@ private module Stage1 implements StageSig {
|
||||
additional predicate viableParamArgNodeCandFwd1(
|
||||
DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
|
||||
) {
|
||||
viableParamArgEx(call, p, arg) and
|
||||
fwdFlow(arg, config)
|
||||
fwdFlowIn(call, arg, _, p, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate revFlowIn(
|
||||
DataFlowCall call, ArgNodeEx arg, boolean toReturn, Configuration config
|
||||
) {
|
||||
@@ -1223,7 +1269,16 @@ private module MkStage<StageSig PrevStage> {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail);
|
||||
|
||||
Content getHeadContent(Ap ap);
|
||||
/**
|
||||
* An approximation of `Content` that corresponds to the precision level of
|
||||
* `Ap`, such that the mappings from both `Ap` and `Content` to this type
|
||||
* are functional.
|
||||
*/
|
||||
class ApHeadContent;
|
||||
|
||||
ApHeadContent getHeadContent(Ap ap);
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c);
|
||||
|
||||
class ApOption;
|
||||
|
||||
@@ -1471,20 +1526,20 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
}
|
||||
|
||||
private class ApNonNil instanceof Ap {
|
||||
pragma[nomagic]
|
||||
ApNonNil() { not this instanceof ApNil }
|
||||
|
||||
string toString() { result = "" }
|
||||
pragma[nomagic]
|
||||
private predicate readStepCand(
|
||||
NodeEx node1, ApHeadContent apc, Content c, NodeEx node2, Configuration config
|
||||
) {
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
apc = projectToHeadContent(c)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead0(
|
||||
NodeEx node1, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ApNonNil ap,
|
||||
Configuration config
|
||||
bindingset[node1, apc]
|
||||
pragma[inline_late]
|
||||
private predicate readStepCand0(
|
||||
NodeEx node1, ApHeadContent apc, Content c, NodeEx node2, Configuration config
|
||||
) {
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, _, _, config)
|
||||
readStepCand(node1, apc, c, node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1492,9 +1547,11 @@ private module MkStage<StageSig PrevStage> {
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
ParamNodeOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
getHeadContent(ap) = c
|
||||
exists(ApHeadContent apc |
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
apc = getHeadContent(ap) and
|
||||
readStepCand0(node1, apc, c, node2, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -2068,8 +2125,12 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) }
|
||||
|
||||
class ApHeadContent = Unit;
|
||||
|
||||
pragma[inline]
|
||||
Content getHeadContent(Ap ap) { exists(result) and ap = true }
|
||||
ApHeadContent getHeadContent(Ap ap) { exists(result) and ap = true }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { any() }
|
||||
|
||||
class ApOption = BooleanOption;
|
||||
|
||||
@@ -2333,8 +2394,12 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result.getAHead() = tc and exists(tail) }
|
||||
|
||||
class ApHeadContent = ContentApprox;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getAHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
predicate projectToHeadContent = getContentApprox/1;
|
||||
|
||||
class ApOption = ApproxAccessPathFrontOption;
|
||||
|
||||
@@ -2409,8 +2474,12 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) }
|
||||
|
||||
class ApHeadContent = Content;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { result = c }
|
||||
|
||||
class ApOption = AccessPathFrontOption;
|
||||
|
||||
@@ -2739,8 +2808,12 @@ private module Stage5Param implements MkStage<Stage4>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) }
|
||||
|
||||
class ApHeadContent = Content;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { result = c }
|
||||
|
||||
class ApOption = AccessPathApproxOption;
|
||||
|
||||
|
||||
@@ -667,23 +667,78 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(NodeEx arg |
|
||||
fwdFlow(arg, _, config) and
|
||||
viableParamArgEx(_, node, arg) and
|
||||
cc = true and
|
||||
not fullBarrier(node, config)
|
||||
)
|
||||
fwdFlowIn(_, _, _, node, config) and
|
||||
cc = true
|
||||
or
|
||||
// flow out of a callable
|
||||
fwdFlowOut(_, node, false, config) and
|
||||
cc = false
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call |
|
||||
fwdFlowOut(call, node, false, config) and
|
||||
cc = false
|
||||
or
|
||||
fwdFlowOutFromArg(call, node, config) and
|
||||
fwdFlowIsEntered(call, cc, config)
|
||||
)
|
||||
}
|
||||
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate fwdFlowIn(
|
||||
DataFlowCall call, NodeEx arg, Cc cc, ParamNodeEx p, Configuration config
|
||||
) {
|
||||
// call context cannot help reduce virtual dispatch
|
||||
fwdFlow(arg, cc, config) and
|
||||
viableParamArgEx(call, p, arg) and
|
||||
not fullBarrier(p, config) and
|
||||
(
|
||||
cc = false
|
||||
or
|
||||
cc = true and
|
||||
not reducedViableImplInCallContext(call, _, _)
|
||||
)
|
||||
or
|
||||
// call context may help reduce virtual dispatch
|
||||
exists(DataFlowCallable target |
|
||||
fwdFlowInReducedViableImplInSomeCallContext(call, arg, p, target, config) and
|
||||
target = viableImplInSomeFwdFlowCallContextExt(call, config) and
|
||||
cc = true
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an argument to `call` is reached in the flow covered by `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) {
|
||||
fwdFlowIn(call, _, cc, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowInReducedViableImplInSomeCallContext(
|
||||
DataFlowCall call, NodeEx arg, ParamNodeEx p, DataFlowCallable target, Configuration config
|
||||
) {
|
||||
fwdFlow(arg, true, config) and
|
||||
viableParamArgEx(call, p, arg) and
|
||||
reducedViableImplInCallContext(call, _, _) and
|
||||
target = p.getEnclosingCallable() and
|
||||
not fullBarrier(p, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a viable dispatch target of `call` in the context `ctx`. This is
|
||||
* restricted to those `call`s for which a context might make a difference,
|
||||
* and to `ctx`s that are reachable in `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private DataFlowCallable viableImplInSomeFwdFlowCallContextExt(
|
||||
DataFlowCall call, Configuration config
|
||||
) {
|
||||
exists(DataFlowCall ctx |
|
||||
fwdFlowIsEntered(ctx, _, config) and
|
||||
result = viableImplInCallContextExt(call, ctx)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate fwdFlow(NodeEx node, Configuration config) { fwdFlow(node, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -726,7 +781,8 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate fwdFlowOut(DataFlowCall call, NodeEx out, Cc cc, Configuration config) {
|
||||
exists(ReturnPosition pos |
|
||||
fwdFlowReturnPosition(pos, cc, config) and
|
||||
@@ -740,17 +796,6 @@ private module Stage1 implements StageSig {
|
||||
fwdFlowOut(call, out, true, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an argument to `call` is reached in the flow covered by `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) {
|
||||
exists(ArgNodeEx arg |
|
||||
fwdFlow(arg, cc, config) and
|
||||
viableParamArgEx(call, _, arg)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate stateStepFwd(FlowState state1, FlowState state2, Configuration config) {
|
||||
exists(NodeEx node1 |
|
||||
additionalLocalStateStep(node1, state1, _, state2, config) or
|
||||
@@ -817,13 +862,8 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(DataFlowCall call |
|
||||
revFlowIn(call, node, false, config) and
|
||||
toReturn = false
|
||||
or
|
||||
revFlowInToReturn(call, node, config) and
|
||||
revFlowIsReturned(call, toReturn, config)
|
||||
)
|
||||
revFlowIn(_, node, false, config) and
|
||||
toReturn = false
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(ReturnPosition pos |
|
||||
@@ -831,6 +871,12 @@ private module Stage1 implements StageSig {
|
||||
node.(RetNodeEx).getReturnPosition() = pos and
|
||||
toReturn = true
|
||||
)
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call |
|
||||
revFlowInToReturn(call, node, config) and
|
||||
revFlowIsReturned(call, toReturn, config)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -886,11 +932,11 @@ private module Stage1 implements StageSig {
|
||||
additional predicate viableParamArgNodeCandFwd1(
|
||||
DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
|
||||
) {
|
||||
viableParamArgEx(call, p, arg) and
|
||||
fwdFlow(arg, config)
|
||||
fwdFlowIn(call, arg, _, p, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate revFlowIn(
|
||||
DataFlowCall call, ArgNodeEx arg, boolean toReturn, Configuration config
|
||||
) {
|
||||
@@ -1223,7 +1269,16 @@ private module MkStage<StageSig PrevStage> {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail);
|
||||
|
||||
Content getHeadContent(Ap ap);
|
||||
/**
|
||||
* An approximation of `Content` that corresponds to the precision level of
|
||||
* `Ap`, such that the mappings from both `Ap` and `Content` to this type
|
||||
* are functional.
|
||||
*/
|
||||
class ApHeadContent;
|
||||
|
||||
ApHeadContent getHeadContent(Ap ap);
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c);
|
||||
|
||||
class ApOption;
|
||||
|
||||
@@ -1471,20 +1526,20 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
}
|
||||
|
||||
private class ApNonNil instanceof Ap {
|
||||
pragma[nomagic]
|
||||
ApNonNil() { not this instanceof ApNil }
|
||||
|
||||
string toString() { result = "" }
|
||||
pragma[nomagic]
|
||||
private predicate readStepCand(
|
||||
NodeEx node1, ApHeadContent apc, Content c, NodeEx node2, Configuration config
|
||||
) {
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
apc = projectToHeadContent(c)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead0(
|
||||
NodeEx node1, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ApNonNil ap,
|
||||
Configuration config
|
||||
bindingset[node1, apc]
|
||||
pragma[inline_late]
|
||||
private predicate readStepCand0(
|
||||
NodeEx node1, ApHeadContent apc, Content c, NodeEx node2, Configuration config
|
||||
) {
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, _, _, config)
|
||||
readStepCand(node1, apc, c, node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1492,9 +1547,11 @@ private module MkStage<StageSig PrevStage> {
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
ParamNodeOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
getHeadContent(ap) = c
|
||||
exists(ApHeadContent apc |
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
apc = getHeadContent(ap) and
|
||||
readStepCand0(node1, apc, c, node2, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -2068,8 +2125,12 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) }
|
||||
|
||||
class ApHeadContent = Unit;
|
||||
|
||||
pragma[inline]
|
||||
Content getHeadContent(Ap ap) { exists(result) and ap = true }
|
||||
ApHeadContent getHeadContent(Ap ap) { exists(result) and ap = true }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { any() }
|
||||
|
||||
class ApOption = BooleanOption;
|
||||
|
||||
@@ -2333,8 +2394,12 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result.getAHead() = tc and exists(tail) }
|
||||
|
||||
class ApHeadContent = ContentApprox;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getAHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
predicate projectToHeadContent = getContentApprox/1;
|
||||
|
||||
class ApOption = ApproxAccessPathFrontOption;
|
||||
|
||||
@@ -2409,8 +2474,12 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) }
|
||||
|
||||
class ApHeadContent = Content;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { result = c }
|
||||
|
||||
class ApOption = AccessPathFrontOption;
|
||||
|
||||
@@ -2739,8 +2808,12 @@ private module Stage5Param implements MkStage<Stage4>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) }
|
||||
|
||||
class ApHeadContent = Content;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { result = c }
|
||||
|
||||
class ApOption = AccessPathApproxOption;
|
||||
|
||||
|
||||
@@ -667,23 +667,78 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(NodeEx arg |
|
||||
fwdFlow(arg, _, config) and
|
||||
viableParamArgEx(_, node, arg) and
|
||||
cc = true and
|
||||
not fullBarrier(node, config)
|
||||
)
|
||||
fwdFlowIn(_, _, _, node, config) and
|
||||
cc = true
|
||||
or
|
||||
// flow out of a callable
|
||||
fwdFlowOut(_, node, false, config) and
|
||||
cc = false
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call |
|
||||
fwdFlowOut(call, node, false, config) and
|
||||
cc = false
|
||||
or
|
||||
fwdFlowOutFromArg(call, node, config) and
|
||||
fwdFlowIsEntered(call, cc, config)
|
||||
)
|
||||
}
|
||||
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate fwdFlowIn(
|
||||
DataFlowCall call, NodeEx arg, Cc cc, ParamNodeEx p, Configuration config
|
||||
) {
|
||||
// call context cannot help reduce virtual dispatch
|
||||
fwdFlow(arg, cc, config) and
|
||||
viableParamArgEx(call, p, arg) and
|
||||
not fullBarrier(p, config) and
|
||||
(
|
||||
cc = false
|
||||
or
|
||||
cc = true and
|
||||
not reducedViableImplInCallContext(call, _, _)
|
||||
)
|
||||
or
|
||||
// call context may help reduce virtual dispatch
|
||||
exists(DataFlowCallable target |
|
||||
fwdFlowInReducedViableImplInSomeCallContext(call, arg, p, target, config) and
|
||||
target = viableImplInSomeFwdFlowCallContextExt(call, config) and
|
||||
cc = true
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an argument to `call` is reached in the flow covered by `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) {
|
||||
fwdFlowIn(call, _, cc, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowInReducedViableImplInSomeCallContext(
|
||||
DataFlowCall call, NodeEx arg, ParamNodeEx p, DataFlowCallable target, Configuration config
|
||||
) {
|
||||
fwdFlow(arg, true, config) and
|
||||
viableParamArgEx(call, p, arg) and
|
||||
reducedViableImplInCallContext(call, _, _) and
|
||||
target = p.getEnclosingCallable() and
|
||||
not fullBarrier(p, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a viable dispatch target of `call` in the context `ctx`. This is
|
||||
* restricted to those `call`s for which a context might make a difference,
|
||||
* and to `ctx`s that are reachable in `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private DataFlowCallable viableImplInSomeFwdFlowCallContextExt(
|
||||
DataFlowCall call, Configuration config
|
||||
) {
|
||||
exists(DataFlowCall ctx |
|
||||
fwdFlowIsEntered(ctx, _, config) and
|
||||
result = viableImplInCallContextExt(call, ctx)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate fwdFlow(NodeEx node, Configuration config) { fwdFlow(node, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -726,7 +781,8 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate fwdFlowOut(DataFlowCall call, NodeEx out, Cc cc, Configuration config) {
|
||||
exists(ReturnPosition pos |
|
||||
fwdFlowReturnPosition(pos, cc, config) and
|
||||
@@ -740,17 +796,6 @@ private module Stage1 implements StageSig {
|
||||
fwdFlowOut(call, out, true, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an argument to `call` is reached in the flow covered by `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) {
|
||||
exists(ArgNodeEx arg |
|
||||
fwdFlow(arg, cc, config) and
|
||||
viableParamArgEx(call, _, arg)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate stateStepFwd(FlowState state1, FlowState state2, Configuration config) {
|
||||
exists(NodeEx node1 |
|
||||
additionalLocalStateStep(node1, state1, _, state2, config) or
|
||||
@@ -817,13 +862,8 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(DataFlowCall call |
|
||||
revFlowIn(call, node, false, config) and
|
||||
toReturn = false
|
||||
or
|
||||
revFlowInToReturn(call, node, config) and
|
||||
revFlowIsReturned(call, toReturn, config)
|
||||
)
|
||||
revFlowIn(_, node, false, config) and
|
||||
toReturn = false
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(ReturnPosition pos |
|
||||
@@ -831,6 +871,12 @@ private module Stage1 implements StageSig {
|
||||
node.(RetNodeEx).getReturnPosition() = pos and
|
||||
toReturn = true
|
||||
)
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call |
|
||||
revFlowInToReturn(call, node, config) and
|
||||
revFlowIsReturned(call, toReturn, config)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -886,11 +932,11 @@ private module Stage1 implements StageSig {
|
||||
additional predicate viableParamArgNodeCandFwd1(
|
||||
DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
|
||||
) {
|
||||
viableParamArgEx(call, p, arg) and
|
||||
fwdFlow(arg, config)
|
||||
fwdFlowIn(call, arg, _, p, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate revFlowIn(
|
||||
DataFlowCall call, ArgNodeEx arg, boolean toReturn, Configuration config
|
||||
) {
|
||||
@@ -1223,7 +1269,16 @@ private module MkStage<StageSig PrevStage> {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail);
|
||||
|
||||
Content getHeadContent(Ap ap);
|
||||
/**
|
||||
* An approximation of `Content` that corresponds to the precision level of
|
||||
* `Ap`, such that the mappings from both `Ap` and `Content` to this type
|
||||
* are functional.
|
||||
*/
|
||||
class ApHeadContent;
|
||||
|
||||
ApHeadContent getHeadContent(Ap ap);
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c);
|
||||
|
||||
class ApOption;
|
||||
|
||||
@@ -1471,20 +1526,20 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
}
|
||||
|
||||
private class ApNonNil instanceof Ap {
|
||||
pragma[nomagic]
|
||||
ApNonNil() { not this instanceof ApNil }
|
||||
|
||||
string toString() { result = "" }
|
||||
pragma[nomagic]
|
||||
private predicate readStepCand(
|
||||
NodeEx node1, ApHeadContent apc, Content c, NodeEx node2, Configuration config
|
||||
) {
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
apc = projectToHeadContent(c)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead0(
|
||||
NodeEx node1, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ApNonNil ap,
|
||||
Configuration config
|
||||
bindingset[node1, apc]
|
||||
pragma[inline_late]
|
||||
private predicate readStepCand0(
|
||||
NodeEx node1, ApHeadContent apc, Content c, NodeEx node2, Configuration config
|
||||
) {
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, _, _, config)
|
||||
readStepCand(node1, apc, c, node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1492,9 +1547,11 @@ private module MkStage<StageSig PrevStage> {
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
ParamNodeOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
getHeadContent(ap) = c
|
||||
exists(ApHeadContent apc |
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
apc = getHeadContent(ap) and
|
||||
readStepCand0(node1, apc, c, node2, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -2068,8 +2125,12 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) }
|
||||
|
||||
class ApHeadContent = Unit;
|
||||
|
||||
pragma[inline]
|
||||
Content getHeadContent(Ap ap) { exists(result) and ap = true }
|
||||
ApHeadContent getHeadContent(Ap ap) { exists(result) and ap = true }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { any() }
|
||||
|
||||
class ApOption = BooleanOption;
|
||||
|
||||
@@ -2333,8 +2394,12 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result.getAHead() = tc and exists(tail) }
|
||||
|
||||
class ApHeadContent = ContentApprox;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getAHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
predicate projectToHeadContent = getContentApprox/1;
|
||||
|
||||
class ApOption = ApproxAccessPathFrontOption;
|
||||
|
||||
@@ -2409,8 +2474,12 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) }
|
||||
|
||||
class ApHeadContent = Content;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { result = c }
|
||||
|
||||
class ApOption = AccessPathFrontOption;
|
||||
|
||||
@@ -2739,8 +2808,12 @@ private module Stage5Param implements MkStage<Stage4>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) }
|
||||
|
||||
class ApHeadContent = Content;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { result = c }
|
||||
|
||||
class ApOption = AccessPathApproxOption;
|
||||
|
||||
|
||||
@@ -707,8 +707,8 @@ private module Cached {
|
||||
* Gets a viable dispatch target of `call` in the context `ctx`. This is
|
||||
* restricted to those `call`s for which a context might make a difference.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private DataFlowCallable viableImplInCallContextExt(DataFlowCall call, DataFlowCall ctx) {
|
||||
cached
|
||||
DataFlowCallable viableImplInCallContextExt(DataFlowCall call, DataFlowCall ctx) {
|
||||
result = viableImplInCallContext(call, ctx) and
|
||||
result = viableCallable(call)
|
||||
or
|
||||
@@ -1391,6 +1391,9 @@ class TypedContentApprox extends MkTypedContentApprox {
|
||||
/** Gets a typed content approximated by this value. */
|
||||
TypedContent getATypedContent() { result = getATypedContent(this) }
|
||||
|
||||
/** Gets the content. */
|
||||
ContentApprox getContent() { result = c }
|
||||
|
||||
/** Gets the container type. */
|
||||
DataFlowType getContainerType() { result = t }
|
||||
|
||||
@@ -1408,6 +1411,8 @@ abstract class ApproxAccessPathFront extends TApproxAccessPathFront {
|
||||
|
||||
abstract boolean toBoolNonEmpty();
|
||||
|
||||
TypedContentApprox getHead() { this = TApproxFrontHead(result) }
|
||||
|
||||
pragma[nomagic]
|
||||
TypedContent getAHead() {
|
||||
exists(TypedContentApprox cont |
|
||||
|
||||
@@ -667,23 +667,78 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(NodeEx arg |
|
||||
fwdFlow(arg, _, config) and
|
||||
viableParamArgEx(_, node, arg) and
|
||||
cc = true and
|
||||
not fullBarrier(node, config)
|
||||
)
|
||||
fwdFlowIn(_, _, _, node, config) and
|
||||
cc = true
|
||||
or
|
||||
// flow out of a callable
|
||||
fwdFlowOut(_, node, false, config) and
|
||||
cc = false
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call |
|
||||
fwdFlowOut(call, node, false, config) and
|
||||
cc = false
|
||||
or
|
||||
fwdFlowOutFromArg(call, node, config) and
|
||||
fwdFlowIsEntered(call, cc, config)
|
||||
)
|
||||
}
|
||||
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate fwdFlowIn(
|
||||
DataFlowCall call, NodeEx arg, Cc cc, ParamNodeEx p, Configuration config
|
||||
) {
|
||||
// call context cannot help reduce virtual dispatch
|
||||
fwdFlow(arg, cc, config) and
|
||||
viableParamArgEx(call, p, arg) and
|
||||
not fullBarrier(p, config) and
|
||||
(
|
||||
cc = false
|
||||
or
|
||||
cc = true and
|
||||
not reducedViableImplInCallContext(call, _, _)
|
||||
)
|
||||
or
|
||||
// call context may help reduce virtual dispatch
|
||||
exists(DataFlowCallable target |
|
||||
fwdFlowInReducedViableImplInSomeCallContext(call, arg, p, target, config) and
|
||||
target = viableImplInSomeFwdFlowCallContextExt(call, config) and
|
||||
cc = true
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an argument to `call` is reached in the flow covered by `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) {
|
||||
fwdFlowIn(call, _, cc, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowInReducedViableImplInSomeCallContext(
|
||||
DataFlowCall call, NodeEx arg, ParamNodeEx p, DataFlowCallable target, Configuration config
|
||||
) {
|
||||
fwdFlow(arg, true, config) and
|
||||
viableParamArgEx(call, p, arg) and
|
||||
reducedViableImplInCallContext(call, _, _) and
|
||||
target = p.getEnclosingCallable() and
|
||||
not fullBarrier(p, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a viable dispatch target of `call` in the context `ctx`. This is
|
||||
* restricted to those `call`s for which a context might make a difference,
|
||||
* and to `ctx`s that are reachable in `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private DataFlowCallable viableImplInSomeFwdFlowCallContextExt(
|
||||
DataFlowCall call, Configuration config
|
||||
) {
|
||||
exists(DataFlowCall ctx |
|
||||
fwdFlowIsEntered(ctx, _, config) and
|
||||
result = viableImplInCallContextExt(call, ctx)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate fwdFlow(NodeEx node, Configuration config) { fwdFlow(node, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -726,7 +781,8 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate fwdFlowOut(DataFlowCall call, NodeEx out, Cc cc, Configuration config) {
|
||||
exists(ReturnPosition pos |
|
||||
fwdFlowReturnPosition(pos, cc, config) and
|
||||
@@ -740,17 +796,6 @@ private module Stage1 implements StageSig {
|
||||
fwdFlowOut(call, out, true, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an argument to `call` is reached in the flow covered by `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) {
|
||||
exists(ArgNodeEx arg |
|
||||
fwdFlow(arg, cc, config) and
|
||||
viableParamArgEx(call, _, arg)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate stateStepFwd(FlowState state1, FlowState state2, Configuration config) {
|
||||
exists(NodeEx node1 |
|
||||
additionalLocalStateStep(node1, state1, _, state2, config) or
|
||||
@@ -817,13 +862,8 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(DataFlowCall call |
|
||||
revFlowIn(call, node, false, config) and
|
||||
toReturn = false
|
||||
or
|
||||
revFlowInToReturn(call, node, config) and
|
||||
revFlowIsReturned(call, toReturn, config)
|
||||
)
|
||||
revFlowIn(_, node, false, config) and
|
||||
toReturn = false
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(ReturnPosition pos |
|
||||
@@ -831,6 +871,12 @@ private module Stage1 implements StageSig {
|
||||
node.(RetNodeEx).getReturnPosition() = pos and
|
||||
toReturn = true
|
||||
)
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call |
|
||||
revFlowInToReturn(call, node, config) and
|
||||
revFlowIsReturned(call, toReturn, config)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -886,11 +932,11 @@ private module Stage1 implements StageSig {
|
||||
additional predicate viableParamArgNodeCandFwd1(
|
||||
DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
|
||||
) {
|
||||
viableParamArgEx(call, p, arg) and
|
||||
fwdFlow(arg, config)
|
||||
fwdFlowIn(call, arg, _, p, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate revFlowIn(
|
||||
DataFlowCall call, ArgNodeEx arg, boolean toReturn, Configuration config
|
||||
) {
|
||||
@@ -1223,7 +1269,16 @@ private module MkStage<StageSig PrevStage> {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail);
|
||||
|
||||
Content getHeadContent(Ap ap);
|
||||
/**
|
||||
* An approximation of `Content` that corresponds to the precision level of
|
||||
* `Ap`, such that the mappings from both `Ap` and `Content` to this type
|
||||
* are functional.
|
||||
*/
|
||||
class ApHeadContent;
|
||||
|
||||
ApHeadContent getHeadContent(Ap ap);
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c);
|
||||
|
||||
class ApOption;
|
||||
|
||||
@@ -1471,20 +1526,20 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
}
|
||||
|
||||
private class ApNonNil instanceof Ap {
|
||||
pragma[nomagic]
|
||||
ApNonNil() { not this instanceof ApNil }
|
||||
|
||||
string toString() { result = "" }
|
||||
pragma[nomagic]
|
||||
private predicate readStepCand(
|
||||
NodeEx node1, ApHeadContent apc, Content c, NodeEx node2, Configuration config
|
||||
) {
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
apc = projectToHeadContent(c)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead0(
|
||||
NodeEx node1, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ApNonNil ap,
|
||||
Configuration config
|
||||
bindingset[node1, apc]
|
||||
pragma[inline_late]
|
||||
private predicate readStepCand0(
|
||||
NodeEx node1, ApHeadContent apc, Content c, NodeEx node2, Configuration config
|
||||
) {
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, _, _, config)
|
||||
readStepCand(node1, apc, c, node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1492,9 +1547,11 @@ private module MkStage<StageSig PrevStage> {
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
ParamNodeOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
getHeadContent(ap) = c
|
||||
exists(ApHeadContent apc |
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
apc = getHeadContent(ap) and
|
||||
readStepCand0(node1, apc, c, node2, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -2068,8 +2125,12 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) }
|
||||
|
||||
class ApHeadContent = Unit;
|
||||
|
||||
pragma[inline]
|
||||
Content getHeadContent(Ap ap) { exists(result) and ap = true }
|
||||
ApHeadContent getHeadContent(Ap ap) { exists(result) and ap = true }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { any() }
|
||||
|
||||
class ApOption = BooleanOption;
|
||||
|
||||
@@ -2333,8 +2394,12 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result.getAHead() = tc and exists(tail) }
|
||||
|
||||
class ApHeadContent = ContentApprox;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getAHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
predicate projectToHeadContent = getContentApprox/1;
|
||||
|
||||
class ApOption = ApproxAccessPathFrontOption;
|
||||
|
||||
@@ -2409,8 +2474,12 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) }
|
||||
|
||||
class ApHeadContent = Content;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { result = c }
|
||||
|
||||
class ApOption = AccessPathFrontOption;
|
||||
|
||||
@@ -2739,8 +2808,12 @@ private module Stage5Param implements MkStage<Stage4>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) }
|
||||
|
||||
class ApHeadContent = Content;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { result = c }
|
||||
|
||||
class ApOption = AccessPathApproxOption;
|
||||
|
||||
|
||||
@@ -667,23 +667,78 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(NodeEx arg |
|
||||
fwdFlow(arg, _, config) and
|
||||
viableParamArgEx(_, node, arg) and
|
||||
cc = true and
|
||||
not fullBarrier(node, config)
|
||||
)
|
||||
fwdFlowIn(_, _, _, node, config) and
|
||||
cc = true
|
||||
or
|
||||
// flow out of a callable
|
||||
fwdFlowOut(_, node, false, config) and
|
||||
cc = false
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call |
|
||||
fwdFlowOut(call, node, false, config) and
|
||||
cc = false
|
||||
or
|
||||
fwdFlowOutFromArg(call, node, config) and
|
||||
fwdFlowIsEntered(call, cc, config)
|
||||
)
|
||||
}
|
||||
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate fwdFlowIn(
|
||||
DataFlowCall call, NodeEx arg, Cc cc, ParamNodeEx p, Configuration config
|
||||
) {
|
||||
// call context cannot help reduce virtual dispatch
|
||||
fwdFlow(arg, cc, config) and
|
||||
viableParamArgEx(call, p, arg) and
|
||||
not fullBarrier(p, config) and
|
||||
(
|
||||
cc = false
|
||||
or
|
||||
cc = true and
|
||||
not reducedViableImplInCallContext(call, _, _)
|
||||
)
|
||||
or
|
||||
// call context may help reduce virtual dispatch
|
||||
exists(DataFlowCallable target |
|
||||
fwdFlowInReducedViableImplInSomeCallContext(call, arg, p, target, config) and
|
||||
target = viableImplInSomeFwdFlowCallContextExt(call, config) and
|
||||
cc = true
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an argument to `call` is reached in the flow covered by `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) {
|
||||
fwdFlowIn(call, _, cc, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowInReducedViableImplInSomeCallContext(
|
||||
DataFlowCall call, NodeEx arg, ParamNodeEx p, DataFlowCallable target, Configuration config
|
||||
) {
|
||||
fwdFlow(arg, true, config) and
|
||||
viableParamArgEx(call, p, arg) and
|
||||
reducedViableImplInCallContext(call, _, _) and
|
||||
target = p.getEnclosingCallable() and
|
||||
not fullBarrier(p, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a viable dispatch target of `call` in the context `ctx`. This is
|
||||
* restricted to those `call`s for which a context might make a difference,
|
||||
* and to `ctx`s that are reachable in `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private DataFlowCallable viableImplInSomeFwdFlowCallContextExt(
|
||||
DataFlowCall call, Configuration config
|
||||
) {
|
||||
exists(DataFlowCall ctx |
|
||||
fwdFlowIsEntered(ctx, _, config) and
|
||||
result = viableImplInCallContextExt(call, ctx)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate fwdFlow(NodeEx node, Configuration config) { fwdFlow(node, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -726,7 +781,8 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate fwdFlowOut(DataFlowCall call, NodeEx out, Cc cc, Configuration config) {
|
||||
exists(ReturnPosition pos |
|
||||
fwdFlowReturnPosition(pos, cc, config) and
|
||||
@@ -740,17 +796,6 @@ private module Stage1 implements StageSig {
|
||||
fwdFlowOut(call, out, true, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an argument to `call` is reached in the flow covered by `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) {
|
||||
exists(ArgNodeEx arg |
|
||||
fwdFlow(arg, cc, config) and
|
||||
viableParamArgEx(call, _, arg)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate stateStepFwd(FlowState state1, FlowState state2, Configuration config) {
|
||||
exists(NodeEx node1 |
|
||||
additionalLocalStateStep(node1, state1, _, state2, config) or
|
||||
@@ -817,13 +862,8 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(DataFlowCall call |
|
||||
revFlowIn(call, node, false, config) and
|
||||
toReturn = false
|
||||
or
|
||||
revFlowInToReturn(call, node, config) and
|
||||
revFlowIsReturned(call, toReturn, config)
|
||||
)
|
||||
revFlowIn(_, node, false, config) and
|
||||
toReturn = false
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(ReturnPosition pos |
|
||||
@@ -831,6 +871,12 @@ private module Stage1 implements StageSig {
|
||||
node.(RetNodeEx).getReturnPosition() = pos and
|
||||
toReturn = true
|
||||
)
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call |
|
||||
revFlowInToReturn(call, node, config) and
|
||||
revFlowIsReturned(call, toReturn, config)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -886,11 +932,11 @@ private module Stage1 implements StageSig {
|
||||
additional predicate viableParamArgNodeCandFwd1(
|
||||
DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
|
||||
) {
|
||||
viableParamArgEx(call, p, arg) and
|
||||
fwdFlow(arg, config)
|
||||
fwdFlowIn(call, arg, _, p, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate revFlowIn(
|
||||
DataFlowCall call, ArgNodeEx arg, boolean toReturn, Configuration config
|
||||
) {
|
||||
@@ -1223,7 +1269,16 @@ private module MkStage<StageSig PrevStage> {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail);
|
||||
|
||||
Content getHeadContent(Ap ap);
|
||||
/**
|
||||
* An approximation of `Content` that corresponds to the precision level of
|
||||
* `Ap`, such that the mappings from both `Ap` and `Content` to this type
|
||||
* are functional.
|
||||
*/
|
||||
class ApHeadContent;
|
||||
|
||||
ApHeadContent getHeadContent(Ap ap);
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c);
|
||||
|
||||
class ApOption;
|
||||
|
||||
@@ -1471,20 +1526,20 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
}
|
||||
|
||||
private class ApNonNil instanceof Ap {
|
||||
pragma[nomagic]
|
||||
ApNonNil() { not this instanceof ApNil }
|
||||
|
||||
string toString() { result = "" }
|
||||
pragma[nomagic]
|
||||
private predicate readStepCand(
|
||||
NodeEx node1, ApHeadContent apc, Content c, NodeEx node2, Configuration config
|
||||
) {
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
apc = projectToHeadContent(c)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead0(
|
||||
NodeEx node1, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ApNonNil ap,
|
||||
Configuration config
|
||||
bindingset[node1, apc]
|
||||
pragma[inline_late]
|
||||
private predicate readStepCand0(
|
||||
NodeEx node1, ApHeadContent apc, Content c, NodeEx node2, Configuration config
|
||||
) {
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, _, _, config)
|
||||
readStepCand(node1, apc, c, node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1492,9 +1547,11 @@ private module MkStage<StageSig PrevStage> {
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
ParamNodeOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
getHeadContent(ap) = c
|
||||
exists(ApHeadContent apc |
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
apc = getHeadContent(ap) and
|
||||
readStepCand0(node1, apc, c, node2, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -2068,8 +2125,12 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) }
|
||||
|
||||
class ApHeadContent = Unit;
|
||||
|
||||
pragma[inline]
|
||||
Content getHeadContent(Ap ap) { exists(result) and ap = true }
|
||||
ApHeadContent getHeadContent(Ap ap) { exists(result) and ap = true }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { any() }
|
||||
|
||||
class ApOption = BooleanOption;
|
||||
|
||||
@@ -2333,8 +2394,12 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result.getAHead() = tc and exists(tail) }
|
||||
|
||||
class ApHeadContent = ContentApprox;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getAHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
predicate projectToHeadContent = getContentApprox/1;
|
||||
|
||||
class ApOption = ApproxAccessPathFrontOption;
|
||||
|
||||
@@ -2409,8 +2474,12 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) }
|
||||
|
||||
class ApHeadContent = Content;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { result = c }
|
||||
|
||||
class ApOption = AccessPathFrontOption;
|
||||
|
||||
@@ -2739,8 +2808,12 @@ private module Stage5Param implements MkStage<Stage4>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) }
|
||||
|
||||
class ApHeadContent = Content;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { result = c }
|
||||
|
||||
class ApOption = AccessPathApproxOption;
|
||||
|
||||
|
||||
@@ -667,23 +667,78 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(NodeEx arg |
|
||||
fwdFlow(arg, _, config) and
|
||||
viableParamArgEx(_, node, arg) and
|
||||
cc = true and
|
||||
not fullBarrier(node, config)
|
||||
)
|
||||
fwdFlowIn(_, _, _, node, config) and
|
||||
cc = true
|
||||
or
|
||||
// flow out of a callable
|
||||
fwdFlowOut(_, node, false, config) and
|
||||
cc = false
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call |
|
||||
fwdFlowOut(call, node, false, config) and
|
||||
cc = false
|
||||
or
|
||||
fwdFlowOutFromArg(call, node, config) and
|
||||
fwdFlowIsEntered(call, cc, config)
|
||||
)
|
||||
}
|
||||
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate fwdFlowIn(
|
||||
DataFlowCall call, NodeEx arg, Cc cc, ParamNodeEx p, Configuration config
|
||||
) {
|
||||
// call context cannot help reduce virtual dispatch
|
||||
fwdFlow(arg, cc, config) and
|
||||
viableParamArgEx(call, p, arg) and
|
||||
not fullBarrier(p, config) and
|
||||
(
|
||||
cc = false
|
||||
or
|
||||
cc = true and
|
||||
not reducedViableImplInCallContext(call, _, _)
|
||||
)
|
||||
or
|
||||
// call context may help reduce virtual dispatch
|
||||
exists(DataFlowCallable target |
|
||||
fwdFlowInReducedViableImplInSomeCallContext(call, arg, p, target, config) and
|
||||
target = viableImplInSomeFwdFlowCallContextExt(call, config) and
|
||||
cc = true
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an argument to `call` is reached in the flow covered by `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) {
|
||||
fwdFlowIn(call, _, cc, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowInReducedViableImplInSomeCallContext(
|
||||
DataFlowCall call, NodeEx arg, ParamNodeEx p, DataFlowCallable target, Configuration config
|
||||
) {
|
||||
fwdFlow(arg, true, config) and
|
||||
viableParamArgEx(call, p, arg) and
|
||||
reducedViableImplInCallContext(call, _, _) and
|
||||
target = p.getEnclosingCallable() and
|
||||
not fullBarrier(p, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a viable dispatch target of `call` in the context `ctx`. This is
|
||||
* restricted to those `call`s for which a context might make a difference,
|
||||
* and to `ctx`s that are reachable in `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private DataFlowCallable viableImplInSomeFwdFlowCallContextExt(
|
||||
DataFlowCall call, Configuration config
|
||||
) {
|
||||
exists(DataFlowCall ctx |
|
||||
fwdFlowIsEntered(ctx, _, config) and
|
||||
result = viableImplInCallContextExt(call, ctx)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate fwdFlow(NodeEx node, Configuration config) { fwdFlow(node, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -726,7 +781,8 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate fwdFlowOut(DataFlowCall call, NodeEx out, Cc cc, Configuration config) {
|
||||
exists(ReturnPosition pos |
|
||||
fwdFlowReturnPosition(pos, cc, config) and
|
||||
@@ -740,17 +796,6 @@ private module Stage1 implements StageSig {
|
||||
fwdFlowOut(call, out, true, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an argument to `call` is reached in the flow covered by `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) {
|
||||
exists(ArgNodeEx arg |
|
||||
fwdFlow(arg, cc, config) and
|
||||
viableParamArgEx(call, _, arg)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate stateStepFwd(FlowState state1, FlowState state2, Configuration config) {
|
||||
exists(NodeEx node1 |
|
||||
additionalLocalStateStep(node1, state1, _, state2, config) or
|
||||
@@ -817,13 +862,8 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(DataFlowCall call |
|
||||
revFlowIn(call, node, false, config) and
|
||||
toReturn = false
|
||||
or
|
||||
revFlowInToReturn(call, node, config) and
|
||||
revFlowIsReturned(call, toReturn, config)
|
||||
)
|
||||
revFlowIn(_, node, false, config) and
|
||||
toReturn = false
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(ReturnPosition pos |
|
||||
@@ -831,6 +871,12 @@ private module Stage1 implements StageSig {
|
||||
node.(RetNodeEx).getReturnPosition() = pos and
|
||||
toReturn = true
|
||||
)
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call |
|
||||
revFlowInToReturn(call, node, config) and
|
||||
revFlowIsReturned(call, toReturn, config)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -886,11 +932,11 @@ private module Stage1 implements StageSig {
|
||||
additional predicate viableParamArgNodeCandFwd1(
|
||||
DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
|
||||
) {
|
||||
viableParamArgEx(call, p, arg) and
|
||||
fwdFlow(arg, config)
|
||||
fwdFlowIn(call, arg, _, p, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate revFlowIn(
|
||||
DataFlowCall call, ArgNodeEx arg, boolean toReturn, Configuration config
|
||||
) {
|
||||
@@ -1223,7 +1269,16 @@ private module MkStage<StageSig PrevStage> {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail);
|
||||
|
||||
Content getHeadContent(Ap ap);
|
||||
/**
|
||||
* An approximation of `Content` that corresponds to the precision level of
|
||||
* `Ap`, such that the mappings from both `Ap` and `Content` to this type
|
||||
* are functional.
|
||||
*/
|
||||
class ApHeadContent;
|
||||
|
||||
ApHeadContent getHeadContent(Ap ap);
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c);
|
||||
|
||||
class ApOption;
|
||||
|
||||
@@ -1471,20 +1526,20 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
}
|
||||
|
||||
private class ApNonNil instanceof Ap {
|
||||
pragma[nomagic]
|
||||
ApNonNil() { not this instanceof ApNil }
|
||||
|
||||
string toString() { result = "" }
|
||||
pragma[nomagic]
|
||||
private predicate readStepCand(
|
||||
NodeEx node1, ApHeadContent apc, Content c, NodeEx node2, Configuration config
|
||||
) {
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
apc = projectToHeadContent(c)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead0(
|
||||
NodeEx node1, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ApNonNil ap,
|
||||
Configuration config
|
||||
bindingset[node1, apc]
|
||||
pragma[inline_late]
|
||||
private predicate readStepCand0(
|
||||
NodeEx node1, ApHeadContent apc, Content c, NodeEx node2, Configuration config
|
||||
) {
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, _, _, config)
|
||||
readStepCand(node1, apc, c, node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1492,9 +1547,11 @@ private module MkStage<StageSig PrevStage> {
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
ParamNodeOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
getHeadContent(ap) = c
|
||||
exists(ApHeadContent apc |
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
apc = getHeadContent(ap) and
|
||||
readStepCand0(node1, apc, c, node2, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -2068,8 +2125,12 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) }
|
||||
|
||||
class ApHeadContent = Unit;
|
||||
|
||||
pragma[inline]
|
||||
Content getHeadContent(Ap ap) { exists(result) and ap = true }
|
||||
ApHeadContent getHeadContent(Ap ap) { exists(result) and ap = true }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { any() }
|
||||
|
||||
class ApOption = BooleanOption;
|
||||
|
||||
@@ -2333,8 +2394,12 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result.getAHead() = tc and exists(tail) }
|
||||
|
||||
class ApHeadContent = ContentApprox;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getAHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
predicate projectToHeadContent = getContentApprox/1;
|
||||
|
||||
class ApOption = ApproxAccessPathFrontOption;
|
||||
|
||||
@@ -2409,8 +2474,12 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) }
|
||||
|
||||
class ApHeadContent = Content;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { result = c }
|
||||
|
||||
class ApOption = AccessPathFrontOption;
|
||||
|
||||
@@ -2739,8 +2808,12 @@ private module Stage5Param implements MkStage<Stage4>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) }
|
||||
|
||||
class ApHeadContent = Content;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { result = c }
|
||||
|
||||
class ApOption = AccessPathApproxOption;
|
||||
|
||||
|
||||
@@ -667,23 +667,78 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(NodeEx arg |
|
||||
fwdFlow(arg, _, config) and
|
||||
viableParamArgEx(_, node, arg) and
|
||||
cc = true and
|
||||
not fullBarrier(node, config)
|
||||
)
|
||||
fwdFlowIn(_, _, _, node, config) and
|
||||
cc = true
|
||||
or
|
||||
// flow out of a callable
|
||||
fwdFlowOut(_, node, false, config) and
|
||||
cc = false
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call |
|
||||
fwdFlowOut(call, node, false, config) and
|
||||
cc = false
|
||||
or
|
||||
fwdFlowOutFromArg(call, node, config) and
|
||||
fwdFlowIsEntered(call, cc, config)
|
||||
)
|
||||
}
|
||||
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate fwdFlowIn(
|
||||
DataFlowCall call, NodeEx arg, Cc cc, ParamNodeEx p, Configuration config
|
||||
) {
|
||||
// call context cannot help reduce virtual dispatch
|
||||
fwdFlow(arg, cc, config) and
|
||||
viableParamArgEx(call, p, arg) and
|
||||
not fullBarrier(p, config) and
|
||||
(
|
||||
cc = false
|
||||
or
|
||||
cc = true and
|
||||
not reducedViableImplInCallContext(call, _, _)
|
||||
)
|
||||
or
|
||||
// call context may help reduce virtual dispatch
|
||||
exists(DataFlowCallable target |
|
||||
fwdFlowInReducedViableImplInSomeCallContext(call, arg, p, target, config) and
|
||||
target = viableImplInSomeFwdFlowCallContextExt(call, config) and
|
||||
cc = true
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an argument to `call` is reached in the flow covered by `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) {
|
||||
fwdFlowIn(call, _, cc, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowInReducedViableImplInSomeCallContext(
|
||||
DataFlowCall call, NodeEx arg, ParamNodeEx p, DataFlowCallable target, Configuration config
|
||||
) {
|
||||
fwdFlow(arg, true, config) and
|
||||
viableParamArgEx(call, p, arg) and
|
||||
reducedViableImplInCallContext(call, _, _) and
|
||||
target = p.getEnclosingCallable() and
|
||||
not fullBarrier(p, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a viable dispatch target of `call` in the context `ctx`. This is
|
||||
* restricted to those `call`s for which a context might make a difference,
|
||||
* and to `ctx`s that are reachable in `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private DataFlowCallable viableImplInSomeFwdFlowCallContextExt(
|
||||
DataFlowCall call, Configuration config
|
||||
) {
|
||||
exists(DataFlowCall ctx |
|
||||
fwdFlowIsEntered(ctx, _, config) and
|
||||
result = viableImplInCallContextExt(call, ctx)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate fwdFlow(NodeEx node, Configuration config) { fwdFlow(node, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -726,7 +781,8 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate fwdFlowOut(DataFlowCall call, NodeEx out, Cc cc, Configuration config) {
|
||||
exists(ReturnPosition pos |
|
||||
fwdFlowReturnPosition(pos, cc, config) and
|
||||
@@ -740,17 +796,6 @@ private module Stage1 implements StageSig {
|
||||
fwdFlowOut(call, out, true, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an argument to `call` is reached in the flow covered by `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) {
|
||||
exists(ArgNodeEx arg |
|
||||
fwdFlow(arg, cc, config) and
|
||||
viableParamArgEx(call, _, arg)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate stateStepFwd(FlowState state1, FlowState state2, Configuration config) {
|
||||
exists(NodeEx node1 |
|
||||
additionalLocalStateStep(node1, state1, _, state2, config) or
|
||||
@@ -817,13 +862,8 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(DataFlowCall call |
|
||||
revFlowIn(call, node, false, config) and
|
||||
toReturn = false
|
||||
or
|
||||
revFlowInToReturn(call, node, config) and
|
||||
revFlowIsReturned(call, toReturn, config)
|
||||
)
|
||||
revFlowIn(_, node, false, config) and
|
||||
toReturn = false
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(ReturnPosition pos |
|
||||
@@ -831,6 +871,12 @@ private module Stage1 implements StageSig {
|
||||
node.(RetNodeEx).getReturnPosition() = pos and
|
||||
toReturn = true
|
||||
)
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call |
|
||||
revFlowInToReturn(call, node, config) and
|
||||
revFlowIsReturned(call, toReturn, config)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -886,11 +932,11 @@ private module Stage1 implements StageSig {
|
||||
additional predicate viableParamArgNodeCandFwd1(
|
||||
DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
|
||||
) {
|
||||
viableParamArgEx(call, p, arg) and
|
||||
fwdFlow(arg, config)
|
||||
fwdFlowIn(call, arg, _, p, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate revFlowIn(
|
||||
DataFlowCall call, ArgNodeEx arg, boolean toReturn, Configuration config
|
||||
) {
|
||||
@@ -1223,7 +1269,16 @@ private module MkStage<StageSig PrevStage> {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail);
|
||||
|
||||
Content getHeadContent(Ap ap);
|
||||
/**
|
||||
* An approximation of `Content` that corresponds to the precision level of
|
||||
* `Ap`, such that the mappings from both `Ap` and `Content` to this type
|
||||
* are functional.
|
||||
*/
|
||||
class ApHeadContent;
|
||||
|
||||
ApHeadContent getHeadContent(Ap ap);
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c);
|
||||
|
||||
class ApOption;
|
||||
|
||||
@@ -1471,20 +1526,20 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
}
|
||||
|
||||
private class ApNonNil instanceof Ap {
|
||||
pragma[nomagic]
|
||||
ApNonNil() { not this instanceof ApNil }
|
||||
|
||||
string toString() { result = "" }
|
||||
pragma[nomagic]
|
||||
private predicate readStepCand(
|
||||
NodeEx node1, ApHeadContent apc, Content c, NodeEx node2, Configuration config
|
||||
) {
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
apc = projectToHeadContent(c)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead0(
|
||||
NodeEx node1, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ApNonNil ap,
|
||||
Configuration config
|
||||
bindingset[node1, apc]
|
||||
pragma[inline_late]
|
||||
private predicate readStepCand0(
|
||||
NodeEx node1, ApHeadContent apc, Content c, NodeEx node2, Configuration config
|
||||
) {
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, _, _, config)
|
||||
readStepCand(node1, apc, c, node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1492,9 +1547,11 @@ private module MkStage<StageSig PrevStage> {
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
ParamNodeOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
getHeadContent(ap) = c
|
||||
exists(ApHeadContent apc |
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
apc = getHeadContent(ap) and
|
||||
readStepCand0(node1, apc, c, node2, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -2068,8 +2125,12 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) }
|
||||
|
||||
class ApHeadContent = Unit;
|
||||
|
||||
pragma[inline]
|
||||
Content getHeadContent(Ap ap) { exists(result) and ap = true }
|
||||
ApHeadContent getHeadContent(Ap ap) { exists(result) and ap = true }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { any() }
|
||||
|
||||
class ApOption = BooleanOption;
|
||||
|
||||
@@ -2333,8 +2394,12 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result.getAHead() = tc and exists(tail) }
|
||||
|
||||
class ApHeadContent = ContentApprox;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getAHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
predicate projectToHeadContent = getContentApprox/1;
|
||||
|
||||
class ApOption = ApproxAccessPathFrontOption;
|
||||
|
||||
@@ -2409,8 +2474,12 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) }
|
||||
|
||||
class ApHeadContent = Content;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { result = c }
|
||||
|
||||
class ApOption = AccessPathFrontOption;
|
||||
|
||||
@@ -2739,8 +2808,12 @@ private module Stage5Param implements MkStage<Stage4>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) }
|
||||
|
||||
class ApHeadContent = Content;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { result = c }
|
||||
|
||||
class ApOption = AccessPathApproxOption;
|
||||
|
||||
|
||||
@@ -667,23 +667,78 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(NodeEx arg |
|
||||
fwdFlow(arg, _, config) and
|
||||
viableParamArgEx(_, node, arg) and
|
||||
cc = true and
|
||||
not fullBarrier(node, config)
|
||||
)
|
||||
fwdFlowIn(_, _, _, node, config) and
|
||||
cc = true
|
||||
or
|
||||
// flow out of a callable
|
||||
fwdFlowOut(_, node, false, config) and
|
||||
cc = false
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call |
|
||||
fwdFlowOut(call, node, false, config) and
|
||||
cc = false
|
||||
or
|
||||
fwdFlowOutFromArg(call, node, config) and
|
||||
fwdFlowIsEntered(call, cc, config)
|
||||
)
|
||||
}
|
||||
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate fwdFlowIn(
|
||||
DataFlowCall call, NodeEx arg, Cc cc, ParamNodeEx p, Configuration config
|
||||
) {
|
||||
// call context cannot help reduce virtual dispatch
|
||||
fwdFlow(arg, cc, config) and
|
||||
viableParamArgEx(call, p, arg) and
|
||||
not fullBarrier(p, config) and
|
||||
(
|
||||
cc = false
|
||||
or
|
||||
cc = true and
|
||||
not reducedViableImplInCallContext(call, _, _)
|
||||
)
|
||||
or
|
||||
// call context may help reduce virtual dispatch
|
||||
exists(DataFlowCallable target |
|
||||
fwdFlowInReducedViableImplInSomeCallContext(call, arg, p, target, config) and
|
||||
target = viableImplInSomeFwdFlowCallContextExt(call, config) and
|
||||
cc = true
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an argument to `call` is reached in the flow covered by `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) {
|
||||
fwdFlowIn(call, _, cc, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowInReducedViableImplInSomeCallContext(
|
||||
DataFlowCall call, NodeEx arg, ParamNodeEx p, DataFlowCallable target, Configuration config
|
||||
) {
|
||||
fwdFlow(arg, true, config) and
|
||||
viableParamArgEx(call, p, arg) and
|
||||
reducedViableImplInCallContext(call, _, _) and
|
||||
target = p.getEnclosingCallable() and
|
||||
not fullBarrier(p, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a viable dispatch target of `call` in the context `ctx`. This is
|
||||
* restricted to those `call`s for which a context might make a difference,
|
||||
* and to `ctx`s that are reachable in `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private DataFlowCallable viableImplInSomeFwdFlowCallContextExt(
|
||||
DataFlowCall call, Configuration config
|
||||
) {
|
||||
exists(DataFlowCall ctx |
|
||||
fwdFlowIsEntered(ctx, _, config) and
|
||||
result = viableImplInCallContextExt(call, ctx)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate fwdFlow(NodeEx node, Configuration config) { fwdFlow(node, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -726,7 +781,8 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate fwdFlowOut(DataFlowCall call, NodeEx out, Cc cc, Configuration config) {
|
||||
exists(ReturnPosition pos |
|
||||
fwdFlowReturnPosition(pos, cc, config) and
|
||||
@@ -740,17 +796,6 @@ private module Stage1 implements StageSig {
|
||||
fwdFlowOut(call, out, true, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an argument to `call` is reached in the flow covered by `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) {
|
||||
exists(ArgNodeEx arg |
|
||||
fwdFlow(arg, cc, config) and
|
||||
viableParamArgEx(call, _, arg)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate stateStepFwd(FlowState state1, FlowState state2, Configuration config) {
|
||||
exists(NodeEx node1 |
|
||||
additionalLocalStateStep(node1, state1, _, state2, config) or
|
||||
@@ -817,13 +862,8 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(DataFlowCall call |
|
||||
revFlowIn(call, node, false, config) and
|
||||
toReturn = false
|
||||
or
|
||||
revFlowInToReturn(call, node, config) and
|
||||
revFlowIsReturned(call, toReturn, config)
|
||||
)
|
||||
revFlowIn(_, node, false, config) and
|
||||
toReturn = false
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(ReturnPosition pos |
|
||||
@@ -831,6 +871,12 @@ private module Stage1 implements StageSig {
|
||||
node.(RetNodeEx).getReturnPosition() = pos and
|
||||
toReturn = true
|
||||
)
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call |
|
||||
revFlowInToReturn(call, node, config) and
|
||||
revFlowIsReturned(call, toReturn, config)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -886,11 +932,11 @@ private module Stage1 implements StageSig {
|
||||
additional predicate viableParamArgNodeCandFwd1(
|
||||
DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
|
||||
) {
|
||||
viableParamArgEx(call, p, arg) and
|
||||
fwdFlow(arg, config)
|
||||
fwdFlowIn(call, arg, _, p, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate revFlowIn(
|
||||
DataFlowCall call, ArgNodeEx arg, boolean toReturn, Configuration config
|
||||
) {
|
||||
@@ -1223,7 +1269,16 @@ private module MkStage<StageSig PrevStage> {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail);
|
||||
|
||||
Content getHeadContent(Ap ap);
|
||||
/**
|
||||
* An approximation of `Content` that corresponds to the precision level of
|
||||
* `Ap`, such that the mappings from both `Ap` and `Content` to this type
|
||||
* are functional.
|
||||
*/
|
||||
class ApHeadContent;
|
||||
|
||||
ApHeadContent getHeadContent(Ap ap);
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c);
|
||||
|
||||
class ApOption;
|
||||
|
||||
@@ -1471,20 +1526,20 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
}
|
||||
|
||||
private class ApNonNil instanceof Ap {
|
||||
pragma[nomagic]
|
||||
ApNonNil() { not this instanceof ApNil }
|
||||
|
||||
string toString() { result = "" }
|
||||
pragma[nomagic]
|
||||
private predicate readStepCand(
|
||||
NodeEx node1, ApHeadContent apc, Content c, NodeEx node2, Configuration config
|
||||
) {
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
apc = projectToHeadContent(c)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead0(
|
||||
NodeEx node1, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ApNonNil ap,
|
||||
Configuration config
|
||||
bindingset[node1, apc]
|
||||
pragma[inline_late]
|
||||
private predicate readStepCand0(
|
||||
NodeEx node1, ApHeadContent apc, Content c, NodeEx node2, Configuration config
|
||||
) {
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, _, _, config)
|
||||
readStepCand(node1, apc, c, node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1492,9 +1547,11 @@ private module MkStage<StageSig PrevStage> {
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
ParamNodeOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
getHeadContent(ap) = c
|
||||
exists(ApHeadContent apc |
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
apc = getHeadContent(ap) and
|
||||
readStepCand0(node1, apc, c, node2, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -2068,8 +2125,12 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) }
|
||||
|
||||
class ApHeadContent = Unit;
|
||||
|
||||
pragma[inline]
|
||||
Content getHeadContent(Ap ap) { exists(result) and ap = true }
|
||||
ApHeadContent getHeadContent(Ap ap) { exists(result) and ap = true }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { any() }
|
||||
|
||||
class ApOption = BooleanOption;
|
||||
|
||||
@@ -2333,8 +2394,12 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result.getAHead() = tc and exists(tail) }
|
||||
|
||||
class ApHeadContent = ContentApprox;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getAHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
predicate projectToHeadContent = getContentApprox/1;
|
||||
|
||||
class ApOption = ApproxAccessPathFrontOption;
|
||||
|
||||
@@ -2409,8 +2474,12 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) }
|
||||
|
||||
class ApHeadContent = Content;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { result = c }
|
||||
|
||||
class ApOption = AccessPathFrontOption;
|
||||
|
||||
@@ -2739,8 +2808,12 @@ private module Stage5Param implements MkStage<Stage4>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) }
|
||||
|
||||
class ApHeadContent = Content;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { result = c }
|
||||
|
||||
class ApOption = AccessPathApproxOption;
|
||||
|
||||
|
||||
@@ -707,8 +707,8 @@ private module Cached {
|
||||
* Gets a viable dispatch target of `call` in the context `ctx`. This is
|
||||
* restricted to those `call`s for which a context might make a difference.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private DataFlowCallable viableImplInCallContextExt(DataFlowCall call, DataFlowCall ctx) {
|
||||
cached
|
||||
DataFlowCallable viableImplInCallContextExt(DataFlowCall call, DataFlowCall ctx) {
|
||||
result = viableImplInCallContext(call, ctx) and
|
||||
result = viableCallable(call)
|
||||
or
|
||||
@@ -1391,6 +1391,9 @@ class TypedContentApprox extends MkTypedContentApprox {
|
||||
/** Gets a typed content approximated by this value. */
|
||||
TypedContent getATypedContent() { result = getATypedContent(this) }
|
||||
|
||||
/** Gets the content. */
|
||||
ContentApprox getContent() { result = c }
|
||||
|
||||
/** Gets the container type. */
|
||||
DataFlowType getContainerType() { result = t }
|
||||
|
||||
@@ -1408,6 +1411,8 @@ abstract class ApproxAccessPathFront extends TApproxAccessPathFront {
|
||||
|
||||
abstract boolean toBoolNonEmpty();
|
||||
|
||||
TypedContentApprox getHead() { this = TApproxFrontHead(result) }
|
||||
|
||||
pragma[nomagic]
|
||||
TypedContent getAHead() {
|
||||
exists(TypedContentApprox cont |
|
||||
|
||||
@@ -169,19 +169,11 @@ predicate defaultTaintSanitizer(DataFlow::Node node) { none() }
|
||||
*/
|
||||
predicate modeledTaintStep(Operand nodeIn, Instruction nodeOut) {
|
||||
exists(CallInstruction call, TaintFunction func, FunctionInput modelIn, FunctionOutput modelOut |
|
||||
(
|
||||
nodeIn = callInput(call, modelIn)
|
||||
or
|
||||
exists(int n |
|
||||
modelIn.isParameterDerefOrQualifierObject(n) and
|
||||
if n = -1
|
||||
then nodeIn = callInput(call, any(InQualifierObject inQualifier))
|
||||
else nodeIn = callInput(call, any(InParameter inParam | inParam.getIndex() = n))
|
||||
)
|
||||
) and
|
||||
nodeOut = callOutput(call, modelOut) and
|
||||
call.getStaticCallTarget() = func and
|
||||
func.hasTaintFlow(modelIn, modelOut)
|
||||
|
|
||||
nodeIn = callInput(call, modelIn) and
|
||||
nodeOut = callOutput(call, modelOut)
|
||||
)
|
||||
or
|
||||
// Taint flow from one argument to another and data flow from an argument to a
|
||||
|
||||
@@ -412,6 +412,9 @@ class CallTargetOperand extends RegisterOperand {
|
||||
*/
|
||||
class ArgumentOperand extends RegisterOperand {
|
||||
override ArgumentOperandTag tag;
|
||||
|
||||
/** Gets the `CallInstruction` for which this is an argument. */
|
||||
CallInstruction getCall() { result.getAnArgumentOperand() = this }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -412,6 +412,9 @@ class CallTargetOperand extends RegisterOperand {
|
||||
*/
|
||||
class ArgumentOperand extends RegisterOperand {
|
||||
override ArgumentOperandTag tag;
|
||||
|
||||
/** Gets the `CallInstruction` for which this is an argument. */
|
||||
CallInstruction getCall() { result.getAnArgumentOperand() = this }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -412,6 +412,9 @@ class CallTargetOperand extends RegisterOperand {
|
||||
*/
|
||||
class ArgumentOperand extends RegisterOperand {
|
||||
override ArgumentOperandTag tag;
|
||||
|
||||
/** Gets the `CallInstruction` for which this is an argument. */
|
||||
CallInstruction getCall() { result.getAnArgumentOperand() = this }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -107,130 +107,34 @@ private FunctionInput getIteratorArgumentInput(Operator op, int index) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A non-member prefix `operator*` function for an iterator type.
|
||||
*/
|
||||
private class IteratorPointerDereferenceOperator extends Operator, TaintFunction,
|
||||
IteratorReferenceFunction {
|
||||
FunctionInput iteratorInput;
|
||||
|
||||
IteratorPointerDereferenceOperator() {
|
||||
this.hasName("operator*") and
|
||||
iteratorInput = getIteratorArgumentInput(this, 0)
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input = iteratorInput and
|
||||
output.isReturnValue()
|
||||
or
|
||||
input.isReturnValueDeref() and
|
||||
output.isParameterDeref(0)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A non-member `operator++` or `operator--` function for an iterator type.
|
||||
*
|
||||
* Note that this class _only_ matches non-member functions. To find both
|
||||
* non-member and versions, use `IteratorCrementOperator`.
|
||||
*/
|
||||
private class IteratorCrementOperator extends Operator, DataFlowFunction {
|
||||
FunctionInput iteratorInput;
|
||||
|
||||
IteratorCrementOperator() {
|
||||
class IteratorCrementNonMemberOperator extends Operator {
|
||||
IteratorCrementNonMemberOperator() {
|
||||
this.hasName(["operator++", "operator--"]) and
|
||||
iteratorInput = getIteratorArgumentInput(this, 0)
|
||||
}
|
||||
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
input = iteratorInput and
|
||||
output.isReturnValue()
|
||||
or
|
||||
input.isParameterDeref(0) and output.isReturnValueDeref()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A non-member `operator+` function for an iterator type.
|
||||
*/
|
||||
private class IteratorAddOperator extends Operator, TaintFunction {
|
||||
FunctionInput iteratorInput;
|
||||
|
||||
IteratorAddOperator() {
|
||||
this.hasName("operator+") and
|
||||
iteratorInput = getIteratorArgumentInput(this, [0, 1])
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input = iteratorInput and
|
||||
output.isReturnValue()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A non-member `operator-` function that takes a pointer difference type as its second argument.
|
||||
*/
|
||||
private class IteratorSubOperator extends Operator, TaintFunction {
|
||||
FunctionInput iteratorInput;
|
||||
|
||||
IteratorSubOperator() {
|
||||
this.hasName("operator-") and
|
||||
iteratorInput = getIteratorArgumentInput(this, 0) and
|
||||
this.getParameter(1).getUnspecifiedType() instanceof IntegralType // not an iterator difference
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input = iteratorInput and
|
||||
output.isReturnValue()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A non-member `operator+=` or `operator-=` function for an iterator type.
|
||||
*/
|
||||
class IteratorAssignArithmeticOperator extends Operator {
|
||||
IteratorAssignArithmeticOperator() {
|
||||
this.hasName(["operator+=", "operator-="]) and
|
||||
exists(getIteratorArgumentInput(this, 0))
|
||||
}
|
||||
}
|
||||
|
||||
private class IteratorAssignArithmeticOperatorModel extends IteratorAssignArithmeticOperator,
|
||||
DataFlowFunction, TaintFunction {
|
||||
private class IteratorCrementNonMemberOperatorModel extends IteratorCrementNonMemberOperator,
|
||||
DataFlowFunction {
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isParameter(0) and
|
||||
input = getIteratorArgumentInput(this, 0) and
|
||||
output.isReturnValue()
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
or
|
||||
input.isParameterDeref(0) and output.isReturnValueDeref()
|
||||
or
|
||||
// reverse flow from returned reference to the object referenced by the first parameter
|
||||
input.isReturnValueDeref() and
|
||||
output.isParameterDeref(0)
|
||||
or
|
||||
input.isParameterDeref(1) and
|
||||
output.isParameterDeref(0)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A prefix `operator*` member function for an iterator type.
|
||||
*/
|
||||
class IteratorPointerDereferenceMemberOperator extends MemberFunction, TaintFunction,
|
||||
IteratorReferenceFunction {
|
||||
IteratorPointerDereferenceMemberOperator() {
|
||||
this.getClassAndName("operator*") instanceof Iterator
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isQualifierObject() and
|
||||
output.isReturnValue()
|
||||
or
|
||||
input.isReturnValueDeref() and
|
||||
output.isQualifierObject()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An `operator++` or `operator--` member function for an iterator type.
|
||||
*
|
||||
* Note that this class _only_ matches member functions. To find both
|
||||
* non-member and member versions, use `IteratorCrementOperator`.
|
||||
*/
|
||||
class IteratorCrementMemberOperator extends MemberFunction {
|
||||
IteratorCrementMemberOperator() {
|
||||
@@ -258,25 +162,49 @@ private class IteratorCrementMemberOperatorModel extends IteratorCrementMemberOp
|
||||
}
|
||||
|
||||
/**
|
||||
* A member `operator->` function for an iterator type.
|
||||
* A (member or non-member) `operator++` or `operator--` function for an iterator type.
|
||||
*/
|
||||
private class IteratorFieldMemberOperator extends Operator, TaintFunction {
|
||||
IteratorFieldMemberOperator() { this.getClassAndName("operator->") instanceof Iterator }
|
||||
class IteratorCrementOperator extends Function {
|
||||
IteratorCrementOperator() {
|
||||
this instanceof IteratorCrementNonMemberOperator or
|
||||
this instanceof IteratorCrementMemberOperator
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A non-member `operator+` function for an iterator type.
|
||||
*
|
||||
* Note that this class _only_ matches non-member functions. To find both
|
||||
* non-member and member versions, use `IteratorBinaryAddOperator`.
|
||||
*/
|
||||
class IteratorAddNonMemberOperator extends Operator {
|
||||
IteratorAddNonMemberOperator() {
|
||||
this.hasName("operator+") and
|
||||
exists(getIteratorArgumentInput(this, [0, 1]))
|
||||
}
|
||||
}
|
||||
|
||||
private class IteratorAddNonMemberOperatorModel extends IteratorAddNonMemberOperator, TaintFunction {
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isQualifierObject() and
|
||||
input = getIteratorArgumentInput(this, [0, 1]) and
|
||||
output.isReturnValue()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An `operator+` or `operator-` member function of an iterator class.
|
||||
*
|
||||
* Note that this class _only_ matches member functions. To find both
|
||||
* non-member and member versions, use `IteratorBinaryAddOperator`.
|
||||
*/
|
||||
private class IteratorBinaryArithmeticMemberOperator extends MemberFunction, TaintFunction {
|
||||
class IteratorBinaryArithmeticMemberOperator extends MemberFunction {
|
||||
IteratorBinaryArithmeticMemberOperator() {
|
||||
this.getClassAndName(["operator+", "operator-"]) instanceof Iterator
|
||||
}
|
||||
}
|
||||
|
||||
private class IteratorBinaryArithmeticMemberOperatorModel extends IteratorBinaryArithmeticMemberOperator,
|
||||
TaintFunction {
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isQualifierObject() and
|
||||
output.isReturnValue()
|
||||
@@ -284,14 +212,84 @@ private class IteratorBinaryArithmeticMemberOperator extends MemberFunction, Tai
|
||||
}
|
||||
|
||||
/**
|
||||
* An `operator+=` or `operator-=` member function of an iterator class.
|
||||
* A (member or non-member) `operator+` or `operator-` function for an iterator type.
|
||||
*/
|
||||
private class IteratorAssignArithmeticMemberOperator extends MemberFunction, DataFlowFunction,
|
||||
TaintFunction {
|
||||
class IteratorBinaryArithmeticOperator extends Function {
|
||||
IteratorBinaryArithmeticOperator() {
|
||||
this instanceof IteratorAddNonMemberOperator or
|
||||
this instanceof IteratorSubNonMemberOperator or
|
||||
this instanceof IteratorBinaryArithmeticMemberOperator
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A non-member `operator-` function that takes a pointer difference type as its second argument.
|
||||
*
|
||||
* Note that this class _only_ matches non-member functions. To find both
|
||||
* non-member and member versions, use `IteratorBinaryArithmeticOperator` (which also
|
||||
* includes `operator+` versions).
|
||||
*/
|
||||
class IteratorSubNonMemberOperator extends Operator {
|
||||
IteratorSubNonMemberOperator() {
|
||||
this.hasName("operator-") and
|
||||
exists(getIteratorArgumentInput(this, 0)) and
|
||||
this.getParameter(1).getUnspecifiedType() instanceof IntegralType // not an iterator difference
|
||||
}
|
||||
}
|
||||
|
||||
private class IteratorSubOperatorModel extends IteratorSubNonMemberOperator, TaintFunction {
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input = getIteratorArgumentInput(this, 0) and
|
||||
output.isReturnValue()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A non-member `operator+=` or `operator-=` function for an iterator type.
|
||||
*
|
||||
* Note that this class _only_ matches non-member functions. To find both
|
||||
* non-member and member versions, use `IteratorAssignArithmeticOperator`.
|
||||
*/
|
||||
class IteratorAssignArithmeticNonMemberOperator extends Operator {
|
||||
IteratorAssignArithmeticNonMemberOperator() {
|
||||
this.hasName(["operator+=", "operator-="]) and
|
||||
exists(getIteratorArgumentInput(this, 0))
|
||||
}
|
||||
}
|
||||
|
||||
private class IteratorAssignArithmeticNonMemberOperatorModel extends IteratorAssignArithmeticNonMemberOperator,
|
||||
DataFlowFunction, TaintFunction {
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isParameter(0) and
|
||||
output.isReturnValue()
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isParameterDeref(0) and output.isReturnValueDeref()
|
||||
or
|
||||
// reverse flow from returned reference to the object referenced by the first parameter
|
||||
input.isReturnValueDeref() and
|
||||
output.isParameterDeref(0)
|
||||
or
|
||||
(input.isParameter(1) or input.isParameterDeref(1)) and
|
||||
output.isParameterDeref(0)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An `operator+=` or `operator-=` member function of an iterator class.
|
||||
*
|
||||
* Note that this class _only_ matches member functions. To find both
|
||||
* non-member and member versions, use `IteratorAssignArithmeticOperator`.
|
||||
*/
|
||||
class IteratorAssignArithmeticMemberOperator extends MemberFunction {
|
||||
IteratorAssignArithmeticMemberOperator() {
|
||||
this.getClassAndName(["operator+=", "operator-="]) instanceof Iterator
|
||||
}
|
||||
}
|
||||
|
||||
private class IteratorAssignArithmeticMemberOperatorModel extends IteratorAssignArithmeticMemberOperator,
|
||||
DataFlowFunction, TaintFunction {
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isQualifierAddress() and
|
||||
output.isReturnValue()
|
||||
@@ -305,11 +303,88 @@ private class IteratorAssignArithmeticMemberOperator extends MemberFunction, Dat
|
||||
input.isReturnValueDeref() and
|
||||
output.isQualifierObject()
|
||||
or
|
||||
input.isParameterDeref(0) and
|
||||
(input.isParameter(0) or input.isParameterDeref(0)) and
|
||||
output.isQualifierObject()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A (member or non-member) `operator+=` or `operator-=` function for an iterator type.
|
||||
*/
|
||||
class IteratorAssignArithmeticOperator extends Function {
|
||||
IteratorAssignArithmeticOperator() {
|
||||
this instanceof IteratorAssignArithmeticNonMemberOperator or
|
||||
this instanceof IteratorAssignArithmeticMemberOperator
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A prefix `operator*` member function for an iterator type.
|
||||
*
|
||||
* Note that this class _only_ matches member functions. To find both
|
||||
* non-member and member versions, use `IteratorPointerDereferenceOperator`.
|
||||
*/
|
||||
class IteratorPointerDereferenceMemberOperator extends MemberFunction, TaintFunction,
|
||||
IteratorReferenceFunction {
|
||||
IteratorPointerDereferenceMemberOperator() {
|
||||
this.getClassAndName("operator*") instanceof Iterator
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isQualifierObject() and
|
||||
output.isReturnValue()
|
||||
or
|
||||
input.isReturnValueDeref() and
|
||||
output.isQualifierObject()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A non-member prefix `operator*` function for an iterator type.
|
||||
*
|
||||
* Note that this class _only_ matches non-member functions. To find both
|
||||
* non-member and member versions, use `IteratorPointerDereferenceOperator`.
|
||||
*/
|
||||
class IteratorPointerDereferenceNonMemberOperator extends Operator, IteratorReferenceFunction {
|
||||
IteratorPointerDereferenceNonMemberOperator() {
|
||||
this.hasName("operator*") and
|
||||
exists(getIteratorArgumentInput(this, 0))
|
||||
}
|
||||
}
|
||||
|
||||
private class IteratorPointerDereferenceNonMemberOperatorModel extends IteratorPointerDereferenceNonMemberOperator,
|
||||
TaintFunction {
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input = getIteratorArgumentInput(this, 0) and
|
||||
output.isReturnValue()
|
||||
or
|
||||
input.isReturnValueDeref() and
|
||||
output.isParameterDeref(0)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A (member or non-member) prefix `operator*` function for an iterator type.
|
||||
*/
|
||||
class IteratorPointerDereferenceOperator extends Function {
|
||||
IteratorPointerDereferenceOperator() {
|
||||
this instanceof IteratorPointerDereferenceNonMemberOperator or
|
||||
this instanceof IteratorPointerDereferenceMemberOperator
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A member `operator->` function for an iterator type.
|
||||
*/
|
||||
private class IteratorFieldMemberOperator extends Operator, TaintFunction {
|
||||
IteratorFieldMemberOperator() { this.getClassAndName("operator->") instanceof Iterator }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isQualifierObject() and
|
||||
output.isReturnValue()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An `operator[]` member function of an iterator class.
|
||||
*/
|
||||
@@ -326,17 +401,24 @@ private class IteratorArrayMemberOperator extends MemberFunction, TaintFunction,
|
||||
/**
|
||||
* An `operator=` member function of an iterator class that is not a copy or move assignment
|
||||
* operator.
|
||||
*
|
||||
* The `hasTaintFlow` override provides flow through output iterators that return themselves with
|
||||
* `operator*` and use their own `operator=` to assign to the container.
|
||||
*/
|
||||
private class IteratorAssignmentMemberOperator extends MemberFunction, TaintFunction {
|
||||
class IteratorAssignmentMemberOperator extends MemberFunction {
|
||||
IteratorAssignmentMemberOperator() {
|
||||
this.getClassAndName("operator=") instanceof Iterator and
|
||||
not this instanceof CopyAssignmentOperator and
|
||||
not this instanceof MoveAssignmentOperator
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An `operator=` member function of an iterator class that is not a copy or move assignment
|
||||
* operator.
|
||||
*
|
||||
* The `hasTaintFlow` override provides flow through output iterators that return themselves with
|
||||
* `operator*` and use their own `operator=` to assign to the container.
|
||||
*/
|
||||
private class IteratorAssignmentMemberOperatorModel extends IteratorAssignmentMemberOperator,
|
||||
TaintFunction {
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isParameterDeref(0) and
|
||||
output.isQualifierObject()
|
||||
|
||||
@@ -27,7 +27,12 @@ private class StdSetConstructor extends Constructor, TaintFunction {
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// taint flow from any parameter of an iterator type to the qualifier
|
||||
input.isParameterDeref(this.getAnIteratorParameterIndex()) and
|
||||
(
|
||||
// AST dataflow doesn't have indirection for iterators.
|
||||
// Once we deprecate AST dataflow we can delete this first disjunct.
|
||||
input.isParameter(this.getAnIteratorParameterIndex()) or
|
||||
input.isParameterDeref(this.getAnIteratorParameterIndex())
|
||||
) and
|
||||
(
|
||||
output.isReturnValue() // TODO: this is only needed for AST data flow, which treats constructors as returning the new object
|
||||
or
|
||||
@@ -45,7 +50,12 @@ private class StdSetInsert extends TaintFunction {
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from last parameter to qualifier and return value
|
||||
// (where the return value is a pair, this should really flow just to the first part of it)
|
||||
input.isParameterDeref(this.getNumberOfParameters() - 1) and
|
||||
(
|
||||
// AST dataflow doesn't have indirection for iterators.
|
||||
// Once we deprecate AST dataflow we can delete this first disjunct.
|
||||
input.isParameter(this.getNumberOfParameters() - 1) or
|
||||
input.isParameterDeref(this.getNumberOfParameters() - 1)
|
||||
) and
|
||||
(
|
||||
output.isQualifierObject() or
|
||||
output.isReturnValue()
|
||||
|
||||
@@ -38,8 +38,7 @@ private class StdBasicStringIterator extends Iterator, Type {
|
||||
*/
|
||||
abstract private class StdStringTaintFunction extends TaintFunction {
|
||||
/**
|
||||
* Gets the index of a parameter to this function that is a string (or
|
||||
* character).
|
||||
* Gets the index of a parameter to this function that is a string.
|
||||
*/
|
||||
final int getAStringParameterIndex() {
|
||||
exists(Type paramType | paramType = this.getParameter(result).getUnspecifiedType() |
|
||||
@@ -50,7 +49,14 @@ abstract private class StdStringTaintFunction extends TaintFunction {
|
||||
paramType instanceof ReferenceType and
|
||||
not paramType.(ReferenceType).getBaseType() =
|
||||
this.getDeclaringType().getTemplateArgument(2).(Type).getUnspecifiedType()
|
||||
or
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the index of a parameter to this function that is a character.
|
||||
*/
|
||||
final int getACharParameterIndex() {
|
||||
exists(Type paramType | paramType = this.getParameter(result).getUnspecifiedType() |
|
||||
// i.e. `std::basic_string::CharT`
|
||||
paramType = this.getDeclaringType().getTemplateArgument(0).(Type).getUnspecifiedType()
|
||||
)
|
||||
@@ -79,6 +85,7 @@ private class StdStringConstructor extends Constructor, StdStringTaintFunction {
|
||||
// taint flow from any parameter of the value type to the returned object
|
||||
(
|
||||
input.isParameterDeref(this.getAStringParameterIndex()) or
|
||||
input.isParameter(this.getACharParameterIndex()) or
|
||||
input.isParameter(this.getAnIteratorParameterIndex())
|
||||
) and
|
||||
(
|
||||
@@ -128,7 +135,7 @@ private class StdStringPush extends StdStringTaintFunction {
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from parameter to qualifier
|
||||
input.isParameterDeref(0) and
|
||||
input.isParameter(0) and
|
||||
output.isQualifierObject()
|
||||
}
|
||||
}
|
||||
@@ -180,6 +187,7 @@ private class StdStringAppend extends StdStringTaintFunction {
|
||||
(
|
||||
input.isQualifierObject() or
|
||||
input.isParameterDeref(this.getAStringParameterIndex()) or
|
||||
input.isParameter(this.getACharParameterIndex()) or
|
||||
input.isParameter(this.getAnIteratorParameterIndex())
|
||||
) and
|
||||
(
|
||||
@@ -210,6 +218,7 @@ private class StdStringInsert extends StdStringTaintFunction {
|
||||
(
|
||||
input.isQualifierObject() or
|
||||
input.isParameterDeref(this.getAStringParameterIndex()) or
|
||||
input.isParameter(this.getACharParameterIndex()) or
|
||||
input.isParameter(this.getAnIteratorParameterIndex())
|
||||
) and
|
||||
(
|
||||
@@ -236,6 +245,7 @@ private class StdStringAssign extends StdStringTaintFunction {
|
||||
// flow from parameter to string itself (qualifier) and return value
|
||||
(
|
||||
input.isParameterDeref(this.getAStringParameterIndex()) or
|
||||
input.isParameter(this.getACharParameterIndex()) or
|
||||
input.isParameter(this.getAnIteratorParameterIndex())
|
||||
) and
|
||||
(
|
||||
|
||||
@@ -158,11 +158,10 @@ abstract class FormattingFunction extends ArrayFunction, TaintFunction {
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
exists(int arg |
|
||||
(
|
||||
arg = getFormatParameterIndex() or
|
||||
arg >= getFirstFormatArgumentIndex()
|
||||
) and
|
||||
input.isParameterDeref(arg) and
|
||||
arg = getFormatParameterIndex() or
|
||||
arg >= getFirstFormatArgumentIndex()
|
||||
|
|
||||
(input.isParameterDeref(arg) or input.isParameter(arg)) and
|
||||
output.isParameterDeref(getOutputParameterIndex(_))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ predicate isInsecureEncryption(string name) { name.regexpMatch(getInsecureAlgori
|
||||
*/
|
||||
bindingset[name]
|
||||
predicate isEncryptionAdditionalEvidence(string name) {
|
||||
name.toUpperCase().matches("%" + ["CRYPT", "CODE", "CODING", "CBC", "KEY", "CIPHER", "MAC"] + "%")
|
||||
name.regexpMatch("(?i).*(crypt|code|coding|cbc|key|cipher|mac).*")
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -14,8 +14,8 @@ import cpp
|
||||
*/
|
||||
bindingset[s]
|
||||
private predicate suspicious(string s) {
|
||||
s.regexpMatch(".*(password|passwd|accountid|account.?key|accnt.?key|license.?key|trusted).*") and
|
||||
not s.matches(["%hash%", "%crypt%", "%file%", "%path%", "%invalid%"])
|
||||
s.regexpMatch("(?i).*(password|passwd|accountid|account.?key|accnt.?key|license.?key|trusted).*") and
|
||||
not s.regexpMatch("(?i).*(hash|crypt|file|path|invalid).*")
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -23,7 +23,7 @@ private predicate suspicious(string s) {
|
||||
*/
|
||||
class SensitiveVariable extends Variable {
|
||||
SensitiveVariable() {
|
||||
suspicious(this.getName().toLowerCase()) and
|
||||
suspicious(this.getName()) and
|
||||
not this.getUnspecifiedType() instanceof IntegralType
|
||||
}
|
||||
}
|
||||
@@ -33,7 +33,7 @@ class SensitiveVariable extends Variable {
|
||||
*/
|
||||
class SensitiveFunction extends Function {
|
||||
SensitiveFunction() {
|
||||
suspicious(this.getName().toLowerCase()) and
|
||||
suspicious(this.getName()) and
|
||||
not this.getUnspecifiedType() instanceof IntegralType
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
## 0.5.2
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.5.1
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* The `cpp/no-space-for-terminator` and `cpp/uncontrolled-allocation-size` queries have been enhanced with heuristic detection of allocations. These queries now find more results.
|
||||
|
||||
## 0.5.0
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
@@ -100,7 +100,7 @@ private predicate fwdFlow(Instruction instr, ValueNumber vn) {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate revFlow(Instruction instr, ValueNumber vn) {
|
||||
fwdFlow(instr, vn) and
|
||||
fwdFlow(instr, pragma[only_bind_out](vn)) and
|
||||
(
|
||||
isSink(instr, _, vn)
|
||||
or
|
||||
@@ -126,7 +126,7 @@ class Node extends MkNode {
|
||||
|
||||
final string toString() { result = instr.toString() }
|
||||
|
||||
final Node getASuccessor() { result = MkNode(instr.getASuccessor(), vn) }
|
||||
final Node getASuccessor() { result = MkNode(pragma[only_bind_out](instr.getASuccessor()), vn) }
|
||||
|
||||
final Location getLocation() { result = instr.getLocation() }
|
||||
}
|
||||
@@ -167,7 +167,7 @@ predicate hasFlow(
|
||||
) {
|
||||
exists(ValueNumber vn |
|
||||
isSource(call, index, source, vn, _) and
|
||||
hasFlow(getNode(source, vn), getNode(sink, vn)) and
|
||||
hasFlow(getNode(source, pragma[only_bind_into](vn)), getNode(sink, pragma[only_bind_into](vn))) and
|
||||
isSink(sink, access, vn)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
## 0.5.1
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* The `cpp/no-space-for-terminator` and `cpp/uncontrolled-allocation-size` queries have been enhanced with heuristic detection of allocations. These queries now find more results.
|
||||
3
cpp/ql/src/change-notes/released/0.5.2.md
Normal file
3
cpp/ql/src/change-notes/released/0.5.2.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.5.2
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.5.0
|
||||
lastReleaseVersion: 0.5.2
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
|
||||
...
|
||||
char buf[256];
|
||||
X509_NAME_oneline(X509_get_subject_name(peer),buf,sizeof(buf)); // GOOD
|
||||
...
|
||||
char buf[256];
|
||||
X509_NAME_oneline(X509_get_subject_name(peer),buf,1024); // BAD
|
||||
...
|
||||
@@ -0,0 +1,23 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>Using a size argument that is larger than the buffer size will result in an out-of-bounds memory access and possibly overflow. You need to limit the value of the length argument.</p>
|
||||
|
||||
</overview>
|
||||
|
||||
<example>
|
||||
<p>The following example shows the use of a function with and without an error in the size argument.</p>
|
||||
<sample src="BufferAccessWithIncorrectLengthValue.cpp" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>
|
||||
CERT Coding Standard:
|
||||
<a href="https://wiki.sei.cmu.edu/confluence/display/c/ARR38-C.+Guarantee+that+library+functions+do+not+form+invalid+pointers">ARR38-C. Guarantee that library functions do not form invalid pointers - SEI CERT C Coding Standard - Confluence</a>.
|
||||
</li>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
* @name Buffer access with incorrect length value
|
||||
* @description Incorrect use of the length argument in some functions will result in out-of-memory accesses.
|
||||
* @kind problem
|
||||
* @id cpp/buffer-access-with-incorrect-length-value
|
||||
* @problem.severity warning
|
||||
* @precision medium
|
||||
* @tags correctness
|
||||
* security
|
||||
* experimental
|
||||
* external/cwe/cwe-805
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
/** Holds for a function `f`, which has an argument at index `bpos` that points to a buffer and an argument at index `spos` that points to a size. */
|
||||
predicate numberArgument(Function f, int bpos, int spos) {
|
||||
f.hasGlobalOrStdName([
|
||||
"X509_NAME_oneline", "SSL_CIPHER_description", "SSL_get_shared_ciphers",
|
||||
"SSL_export_keying_material_early", "SSL_export_keying_material", "SSL_set_alpn_protos",
|
||||
"SSL_CTX_set_alpn_protos", "SSL_read", "SSL_read_ex", "SSL_read_early_data",
|
||||
"SSL_bytes_to_cipher_list", "SSL_write", "SSL_SESSION_set1_master_key",
|
||||
"SSL_CTX_set_session_id_context", "BIO_gets", "BIO_read", "BIO_read_ex", "BIO_write",
|
||||
"BIO_write_ex", "BIO_ctrl", "BN_bn2binpad", "BN_signed_bn2bin", "BN_signed_bn2lebin",
|
||||
"EVP_PKEY_get_default_digest_name", "EVP_DigestUpdate", "EVP_PKEY_CTX_set1_tls1_prf_secret",
|
||||
"EVP_KDF_derive", "EVP_CIPHER_CTX_get_updated_iv", "EVP_PKEY_get_group_name", "EVP_MAC_init",
|
||||
"write", "read", "send", "sendto", "recv", "recvfrom", "strerror_r"
|
||||
]) and
|
||||
bpos = 1 and
|
||||
spos = 2
|
||||
or
|
||||
f.hasGlobalOrStdName(["X509_NAME_get_text_by_NID", "EVP_PKEY_get_utf8_string_param"]) and
|
||||
bpos = 2 and
|
||||
spos = 3
|
||||
or
|
||||
f.hasGlobalOrStdName([
|
||||
"BIO_snprintf", "BN_signed_lebin2bn", "BIO_new_mem_buf", "BN_lebin2bn", "BN_bin2bn",
|
||||
"EVP_read_pw_string", "EVP_read_pw_string", "strftime", "strnlen", "fgets", "snprintf",
|
||||
"vsnprintf"
|
||||
]) and
|
||||
bpos = 0 and
|
||||
spos = 1
|
||||
or
|
||||
f.hasGlobalOrStdName(["AES_ige_encrypt", "memchr"]) and bpos = 0 and spos = 2
|
||||
or
|
||||
f.hasGlobalOrStdName("EVP_MAC_final") and bpos = 1 and spos = 3
|
||||
or
|
||||
f.hasGlobalOrStdName("OBJ_obj2txt") and bpos = 2 and spos = 1
|
||||
or
|
||||
f.hasGlobalOrStdName("EVP_CIPHER_CTX_ctrl") and bpos = 3 and spos = 2
|
||||
or
|
||||
f.hasGlobalOrStdName(["EVP_PKEY_get_octet_string_param", "getnameinfo"]) and bpos = 2 and spos = 3
|
||||
or
|
||||
f.hasGlobalOrStdName([
|
||||
"EVP_DecryptUpdate", "EVP_EncryptUpdate", "EVP_PKEY_encrypt", "EVP_PKEY_sign",
|
||||
"EVP_CipherUpdate"
|
||||
]) and
|
||||
bpos = 3 and
|
||||
spos = 4
|
||||
or
|
||||
f.hasGlobalOrStdName("getnameinfo") and bpos = 4 and spos = 5
|
||||
}
|
||||
|
||||
from FunctionCall fc
|
||||
where
|
||||
exists(ArrayType array, int bufArgPos, int sizeArgPos |
|
||||
numberArgument(fc.getTarget(), bufArgPos, sizeArgPos) and
|
||||
fc.getArgument(pragma[only_bind_into](sizeArgPos)).getValue().toInt() > array.getByteSize() and
|
||||
fc.getArgument(pragma[only_bind_into](bufArgPos))
|
||||
.(VariableAccess)
|
||||
.getTarget()
|
||||
.getADeclarationEntry()
|
||||
.getType() = array
|
||||
)
|
||||
select fc,
|
||||
"Access beyond the bounds of the allocated memory is possible, the size argument used is greater than the size of the buffer."
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/cpp-queries
|
||||
version: 0.5.1-dev
|
||||
version: 0.5.3-dev
|
||||
groups:
|
||||
- cpp
|
||||
- queries
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
| bitshift.cpp:23:3:23:9 | ... <<= ... | 0.0 | 255.0 | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | int | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:25:5:25:11 | ... <<= ... | 0.0 | 240.0 | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | int | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:29:3:29:8 | ... << ... | 0.0 | 1020.0 | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | int | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:32:3:32:9 | ... << ... | -2.147483648E9 | 2.147483647E9 | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | int | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:35:3:35:9 | ... << ... | -2.147483648E9 | 2.147483647E9 | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | int | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:38:3:38:22 | ... << ... | 0.0 | 32640.0 | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:39:3:39:22 | ... << ... | 0.0 | 32640.0 | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:40:3:40:22 | ... << ... | 0.0 | 32640.0 | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:43:3:43:19 | ... << ... | -2.147483648E9 | 2.147483647E9 | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | signed char | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:46:3:46:22 | ... << ... | 128.0 | 128.0 | file://:0:0:0:0 | int | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:49:3:49:8 | ... << ... | -2.147483648E9 | 2.147483647E9 | file://:0:0:0:0 | int | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:52:5:52:10 | ... << ... | 1.0 | 128.0 | file://:0:0:0:0 | int | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:57:3:57:8 | ... << ... | -2.147483648E9 | 2.147483647E9 | file://:0:0:0:0 | signed char | file://:0:0:0:0 | int | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:58:3:58:9 | ... << ... | -2.147483648E9 | 2.147483647E9 | file://:0:0:0:0 | signed char | file://:0:0:0:0 | int | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:59:3:59:9 | ... << ... | -2.147483648E9 | 2.147483647E9 | file://:0:0:0:0 | signed char | file://:0:0:0:0 | int | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:60:3:60:22 | ... << ... | -2.147483648E9 | 2.147483647E9 | file://:0:0:0:0 | signed char | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:61:3:61:19 | ... << ... | -2.147483648E9 | 2.147483647E9 | file://:0:0:0:0 | signed char | file://:0:0:0:0 | signed char | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:64:3:64:19 | ... << ... | -2.147483648E9 | 2.147483647E9 | file://:0:0:0:0 | int | file://:0:0:0:0 | signed char | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:67:3:67:8 | ... << ... | -2.147483648E9 | 2.147483647E9 | file://:0:0:0:0 | int | file://:0:0:0:0 | signed char | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:70:5:70:10 | ... << ... | 1.0 | 128.0 | file://:0:0:0:0 | int | file://:0:0:0:0 | signed char | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:75:5:75:10 | ... << ... | -2.147483648E9 | 2.147483647E9 | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | signed char | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:76:5:76:10 | ... << ... | -2.147483648E9 | 2.147483647E9 | file://:0:0:0:0 | signed char | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:90:3:90:9 | ... >>= ... | 0.0 | 63.0 | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | int | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:92:5:92:11 | ... >>= ... | 0.0 | 15.0 | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | int | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:96:3:96:8 | ... >> ... | 0.0 | 63.0 | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | int | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:99:3:99:9 | ... >> ... | 0.0 | 0.0 | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | int | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:103:3:103:9 | ... >> ... | 0.0 | 0.0 | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | int | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:106:3:106:22 | ... >> ... | 0.0 | 63.0 | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:107:3:107:22 | ... >> ... | 0.0 | 63.0 | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:108:3:108:22 | ... >> ... | 0.0 | 63.0 | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:111:3:111:19 | ... >> ... | -2.147483648E9 | 2.147483647E9 | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | signed char | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:114:3:114:24 | ... >> ... | 32.0 | 32.0 | file://:0:0:0:0 | int | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:117:3:117:10 | ... >> ... | -2.147483648E9 | 2.147483647E9 | file://:0:0:0:0 | int | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:120:5:120:12 | ... >> ... | 32.0 | 128.0 | file://:0:0:0:0 | int | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:126:3:126:8 | ... >> ... | -2.147483648E9 | 2.147483647E9 | file://:0:0:0:0 | signed char | file://:0:0:0:0 | int | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:127:3:127:9 | ... >> ... | -2.147483648E9 | 2.147483647E9 | file://:0:0:0:0 | signed char | file://:0:0:0:0 | int | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:128:3:128:9 | ... >> ... | -1.0 | 0.0 | file://:0:0:0:0 | signed char | file://:0:0:0:0 | int | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:129:3:129:22 | ... >> ... | -2.147483648E9 | 2.147483647E9 | file://:0:0:0:0 | signed char | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:130:3:130:19 | ... >> ... | -2.147483648E9 | 2.147483647E9 | file://:0:0:0:0 | signed char | file://:0:0:0:0 | signed char | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:133:3:133:21 | ... >> ... | -2.147483648E9 | 2.147483647E9 | file://:0:0:0:0 | int | file://:0:0:0:0 | signed char | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:136:3:136:10 | ... >> ... | -2.147483648E9 | 2.147483647E9 | file://:0:0:0:0 | int | file://:0:0:0:0 | signed char | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:139:5:139:12 | ... >> ... | 32.0 | 128.0 | file://:0:0:0:0 | int | file://:0:0:0:0 | signed char | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:144:5:144:10 | ... >> ... | -2.147483648E9 | 2.147483647E9 | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | signed char | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:145:5:145:10 | ... >> ... | -2.147483648E9 | 2.147483647E9 | file://:0:0:0:0 | signed char | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
@@ -0,0 +1,24 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
|
||||
import experimental.semmle.code.cpp.rangeanalysis.extensions.ConstantShiftExprRange
|
||||
|
||||
Expr getLOp(Operation o) {
|
||||
result = o.(BinaryOperation).getLeftOperand() or
|
||||
result = o.(Assignment).getLValue()
|
||||
}
|
||||
|
||||
Expr getROp(Operation o) {
|
||||
result = o.(BinaryOperation).getRightOperand() or
|
||||
result = o.(Assignment).getRValue()
|
||||
}
|
||||
|
||||
from Operation o
|
||||
where
|
||||
(
|
||||
o instanceof BinaryBitwiseOperation
|
||||
or
|
||||
o instanceof AssignBitwiseOperation
|
||||
)
|
||||
select o, lowerBound(o), upperBound(o), getLOp(o).getUnderlyingType(),
|
||||
getROp(o).getUnderlyingType(), getLOp(o).getFullyConverted().getUnderlyingType(),
|
||||
getROp(o).getFullyConverted().getUnderlyingType()
|
||||
@@ -0,0 +1,147 @@
|
||||
typedef signed char int8_t;
|
||||
typedef short int16_t;
|
||||
typedef int int32_t;
|
||||
typedef long int64_t;
|
||||
|
||||
typedef unsigned char uint8_t;
|
||||
typedef unsigned short uint16_t;
|
||||
typedef unsigned int uint32_t;
|
||||
typedef unsigned long uint64_t;
|
||||
|
||||
extern uint8_t value_known_at_runtime8();
|
||||
|
||||
void testLShiftOperator() {
|
||||
uint8_t unsigned_const1 = 7;
|
||||
uint8_t unsigned_const2(7);
|
||||
uint8_t unsigned_const3{7};
|
||||
int8_t signed_const = -7;
|
||||
uint8_t x = value_known_at_runtime8();
|
||||
int8_t y = (int8_t)value_known_at_runtime8();
|
||||
uint8_t z = value_known_at_runtime8();
|
||||
|
||||
// An assign left shift operator. Note that no promotion occurs here
|
||||
z <<= 2; // [0, 255]
|
||||
if (z <= 60) {
|
||||
z <<= 2; // [0, 240]
|
||||
}
|
||||
|
||||
// A normal shift
|
||||
x << 2; // [0, 1020]
|
||||
|
||||
// Possible to exceed the maximum size
|
||||
x << 25; // [-2147483648, 2147483648]
|
||||
|
||||
// Undefined behavior
|
||||
x << 34; // [-2147483648, 2147483648]
|
||||
|
||||
// A normal shift by a constant in a variable
|
||||
x << unsigned_const1; // [0, 32640]
|
||||
x << unsigned_const2; // [0, 32640]
|
||||
x << unsigned_const3; // [0, 32640]
|
||||
|
||||
// Negative shifts are undefined
|
||||
x << signed_const; // [-2147483648, 2147483648]
|
||||
|
||||
// Now the left operand is a constant
|
||||
1 << unsigned_const1; // [128, 128]
|
||||
|
||||
// x could be large enough to cause undefined behavior
|
||||
1 << x; // [-2147483648, 2147483647]
|
||||
if (x < 8) {
|
||||
// x is now constrained so the shift is defined
|
||||
1 << x; // [1, 128]
|
||||
}
|
||||
|
||||
// We don't support shifting negative values (and some of these are undefined
|
||||
// anyway)
|
||||
y << 2; // [-2147483648, 2147483647]
|
||||
y << 25; // [-2147483648, 2147483648]
|
||||
y << 34; // [-2147483648, 2147483648]
|
||||
y << unsigned_const1; // [-2147483648, 2147483647]
|
||||
y << signed_const; // [-2147483648, 2147483648]
|
||||
|
||||
// Negative shifts are undefined
|
||||
1 << signed_const; // [-2147483648, 2147483648]
|
||||
|
||||
// We don't handle cases where the shift range could be negative
|
||||
1 << y; // [-2147483648, 2147483648]
|
||||
if (y >= 0 && y < 8) {
|
||||
// The shift range is now positive
|
||||
1 << y; // [1, 128]
|
||||
}
|
||||
|
||||
if (x > 0 and x < 2 and y > 0 and x < 2) {
|
||||
// We don't support shifts where neither operand is a constant at the moment
|
||||
x << y; // [-2147483648, 2147483648]
|
||||
y << x; // [-2147483648, 2147483648]
|
||||
}
|
||||
}
|
||||
|
||||
void testRShiftOperator() {
|
||||
uint8_t unsigned_const1 = 2;
|
||||
uint8_t unsigned_const2(2);
|
||||
uint8_t unsigned_const3{2};
|
||||
int8_t signed_const = -2;
|
||||
uint8_t x = value_known_at_runtime8();
|
||||
int8_t y = (int8_t)value_known_at_runtime8();
|
||||
uint8_t z = value_known_at_runtime8();
|
||||
|
||||
// An assign right shift operator. Note that no promotion occurs here
|
||||
z >>= 2; // [0, 63]
|
||||
if (z <= 60) {
|
||||
z >>= 2; // [0, 15]
|
||||
}
|
||||
|
||||
// A normal shift
|
||||
x >> 2; // [0, 63]
|
||||
|
||||
// Possible to exceed the maximum size
|
||||
x >> 25; // [0, 0]
|
||||
|
||||
// Undefined behavior, but this case is handled by the SimpleRangeAnalysis
|
||||
// library and sets the the bounds to [0, 0], which is fine
|
||||
x >> 34; // [0, 0]
|
||||
|
||||
// A normal shift by a constant in a variable
|
||||
x >> unsigned_const1; // [0, 63]
|
||||
x >> unsigned_const2; // [0, 63]
|
||||
x >> unsigned_const3; // [0, 63]
|
||||
|
||||
// Negative shifts are undefined
|
||||
x >> signed_const; // [-2147483648, 2147483648]
|
||||
|
||||
// Now the left operand is a constant
|
||||
128 >> unsigned_const1; // [32, 32]
|
||||
|
||||
// x could be large enough to cause undefined behavior
|
||||
128 >> x; // [-2147483648, 2147483647]
|
||||
if (x < 3) {
|
||||
// x is now constrained so the shift is defined
|
||||
128 >> x; // [32, 128]
|
||||
}
|
||||
|
||||
// We don't support shifting negative values, but the SimpleRangeAnalysis
|
||||
// library handles the first three cases even though they're implementation
|
||||
// defined or undefined behavior (TODO: Check ideone)
|
||||
y >> 2; // [-2147483648, 2147483647] (Default is [-32, 31])
|
||||
y >> 25; // -2147483648, 2147483647] (Default is [-1, 0])
|
||||
y >> 34; // [-1, 0] (My code doesn't touch this, so default code is used)
|
||||
y >> unsigned_const1; // [-2147483648, 2147483647]
|
||||
y >> signed_const; // [-2147483648, 2147483648]
|
||||
|
||||
// Negative shifts are undefined
|
||||
128 >> signed_const; // [-2147483648, 2147483648]
|
||||
|
||||
// We don't handle cases where the shift range could be negative
|
||||
128 >> y; // [-2147483648, 2147483648]
|
||||
if (y >= 0 && y < 3) {
|
||||
// The shift range is now positive
|
||||
128 >> y; // [32, 128]
|
||||
}
|
||||
|
||||
if (x > 0 and x < 2 and y > 0 and x < 2) {
|
||||
// We don't support shifts where neither operand is a constant at the moment
|
||||
x >> y; // [-2147483648, 2147483648]
|
||||
y >> x; // [-2147483648, 2147483648]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
| test.cpp:27:5:27:21 | call to X509_NAME_oneline | Access beyond the bounds of the allocated memory is possible, the size argument used is greater than the size of the buffer. |
|
||||
@@ -0,0 +1 @@
|
||||
experimental/Security/CWE/CWE-805/BufferAccessWithIncorrectLengthValue.ql
|
||||
@@ -0,0 +1,31 @@
|
||||
struct X509_NAME {};
|
||||
struct SSL {};
|
||||
struct X509 {};
|
||||
|
||||
char * X509_NAME_oneline(X509_NAME *a,char *buf,int size);
|
||||
X509 *SSL_get_peer_certificate(const SSL *ssl);
|
||||
X509_NAME *X509_get_subject_name(const X509 *x);
|
||||
char *strcasestr(char *a, char *b);
|
||||
|
||||
bool goodTest1(SSL *ssl,char *text)
|
||||
{
|
||||
X509 *peer;
|
||||
char buf[256];
|
||||
if( peer = SSL_get_peer_certificate(ssl))
|
||||
{
|
||||
X509_NAME_oneline(X509_get_subject_name(peer),buf,sizeof(buf)); // GOOD
|
||||
if((char*)strcasestr(buf,text)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool badTest1(SSL *ssl,char *text)
|
||||
{
|
||||
X509 *peer;
|
||||
char buf[256];
|
||||
if( peer = SSL_get_peer_certificate(ssl))
|
||||
{
|
||||
X509_NAME_oneline(X509_get_subject_name(peer),buf,1024); // BAD
|
||||
if((char*)strcasestr(buf,text)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -92,6 +92,12 @@ postWithInFlow
|
||||
| test.cpp:499:3:499:4 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| test.cpp:499:4:499:4 | p [inner post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| test.cpp:505:35:505:35 | x [inner post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| test.cpp:511:5:511:6 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| test.cpp:511:6:511:6 | p [inner post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| test.cpp:516:5:516:6 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| test.cpp:516:6:516:6 | p [inner post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| test.cpp:522:25:522:25 | x [inner post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| test.cpp:526:25:526:25 | y [inner post update] | PostUpdateNode should not be the target of local flow. |
|
||||
viableImplInCallContextTooLarge
|
||||
uniqueParameterNodeAtPosition
|
||||
uniqueParameterNodePosition
|
||||
|
||||
@@ -591,6 +591,18 @@ postWithInFlow
|
||||
| test.cpp:505:34:505:35 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| test.cpp:505:34:505:35 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| test.cpp:505:35:505:35 | x [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| test.cpp:511:5:511:6 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| test.cpp:511:6:511:6 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| test.cpp:511:6:511:6 | p [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| test.cpp:516:5:516:6 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| test.cpp:516:6:516:6 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| test.cpp:516:6:516:6 | p [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| test.cpp:522:24:522:25 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| test.cpp:522:24:522:25 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| test.cpp:522:25:522:25 | x [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| test.cpp:526:24:526:25 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| test.cpp:526:24:526:25 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| test.cpp:526:25:526:25 | y [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| true_upon_entry.cpp:9:7:9:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| true_upon_entry.cpp:10:12:10:12 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| true_upon_entry.cpp:10:27:10:27 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
|
||||
@@ -505,3 +505,24 @@ void viaOutparamMissingReturn() {
|
||||
intOutparamSourceMissingReturn(&x);
|
||||
sink(x); // $ ast,ir
|
||||
}
|
||||
|
||||
void sink_then_source_1(int* p) {
|
||||
sink(*p); // clean
|
||||
*p = source();
|
||||
}
|
||||
|
||||
void sink_then_source_2(int* p, int y) {
|
||||
sink(y); // $ SPURIOUS: ast
|
||||
*p = source();
|
||||
}
|
||||
|
||||
void test_sink_then_source() {
|
||||
{
|
||||
int x;
|
||||
sink_then_source_1(&x);
|
||||
}
|
||||
{
|
||||
int y;
|
||||
sink_then_source_2(&y, y);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,3 +34,6 @@
|
||||
| test.cpp:441:7:441:11 | local | test.cpp:442:18:442:22 | local |
|
||||
| test.cpp:441:7:441:11 | local | test.cpp:443:8:443:12 | local |
|
||||
| test.cpp:441:7:441:11 | local | test.cpp:444:9:444:13 | local |
|
||||
| test.cpp:521:9:521:9 | x | test.cpp:522:25:522:25 | x |
|
||||
| test.cpp:525:9:525:9 | y | test.cpp:526:25:526:25 | y |
|
||||
| test.cpp:525:9:525:9 | y | test.cpp:526:28:526:28 | y |
|
||||
|
||||
@@ -3482,106 +3482,106 @@
|
||||
| smart_pointer.cpp:137:10:137:11 | p2 | smart_pointer.cpp:137:9:137:9 | call to operator* | TAINT |
|
||||
| smart_pointer.cpp:137:10:137:11 | ref arg p2 | smart_pointer.cpp:132:95:132:96 | p2 | |
|
||||
| smart_pointer.cpp:137:10:137:11 | ref arg p2 | smart_pointer.cpp:137:10:137:11 | p2 [inner post update] | |
|
||||
| standalone_iterators.cpp:39:45:39:51 | source1 | standalone_iterators.cpp:39:45:39:51 | source1 | |
|
||||
| standalone_iterators.cpp:39:45:39:51 | source1 | standalone_iterators.cpp:40:11:40:17 | source1 | |
|
||||
| standalone_iterators.cpp:39:45:39:51 | source1 | standalone_iterators.cpp:41:12:41:18 | source1 | |
|
||||
| standalone_iterators.cpp:39:45:39:51 | source1 | standalone_iterators.cpp:42:14:42:20 | source1 | |
|
||||
| standalone_iterators.cpp:40:11:40:17 | source1 | standalone_iterators.cpp:40:10:40:10 | call to operator* | TAINT |
|
||||
| standalone_iterators.cpp:41:12:41:18 | ref arg source1 | standalone_iterators.cpp:39:45:39:51 | source1 | |
|
||||
| standalone_iterators.cpp:41:12:41:18 | ref arg source1 | standalone_iterators.cpp:42:14:42:20 | source1 | |
|
||||
| standalone_iterators.cpp:41:12:41:18 | source1 | standalone_iterators.cpp:41:19:41:19 | call to operator++ | |
|
||||
| standalone_iterators.cpp:41:19:41:19 | call to operator++ | standalone_iterators.cpp:41:10:41:10 | call to operator* | TAINT |
|
||||
| standalone_iterators.cpp:42:12:42:12 | call to operator++ | standalone_iterators.cpp:42:10:42:10 | call to operator* | TAINT |
|
||||
| standalone_iterators.cpp:42:14:42:20 | ref arg source1 | standalone_iterators.cpp:39:45:39:51 | source1 | |
|
||||
| standalone_iterators.cpp:42:14:42:20 | source1 | standalone_iterators.cpp:42:12:42:12 | call to operator++ | |
|
||||
| standalone_iterators.cpp:45:39:45:45 | source1 | standalone_iterators.cpp:45:39:45:45 | source1 | |
|
||||
| standalone_iterators.cpp:45:39:45:45 | source1 | standalone_iterators.cpp:46:11:46:17 | source1 | |
|
||||
| standalone_iterators.cpp:45:39:45:45 | source1 | standalone_iterators.cpp:47:12:47:18 | source1 | |
|
||||
| standalone_iterators.cpp:45:39:45:45 | source1 | standalone_iterators.cpp:48:14:48:20 | source1 | |
|
||||
| standalone_iterators.cpp:46:11:46:17 | source1 | standalone_iterators.cpp:46:10:46:10 | call to operator* | TAINT |
|
||||
| standalone_iterators.cpp:47:12:47:18 | ref arg source1 | standalone_iterators.cpp:45:39:45:45 | source1 | |
|
||||
| standalone_iterators.cpp:47:12:47:18 | ref arg source1 | standalone_iterators.cpp:48:14:48:20 | source1 | |
|
||||
| standalone_iterators.cpp:47:12:47:18 | source1 | standalone_iterators.cpp:47:19:47:19 | call to operator++ | |
|
||||
| standalone_iterators.cpp:47:19:47:19 | call to operator++ | standalone_iterators.cpp:47:10:47:10 | call to operator* | TAINT |
|
||||
| standalone_iterators.cpp:48:12:48:12 | call to operator++ | standalone_iterators.cpp:48:10:48:10 | call to operator* | TAINT |
|
||||
| standalone_iterators.cpp:48:14:48:20 | ref arg source1 | standalone_iterators.cpp:45:39:45:45 | source1 | |
|
||||
| standalone_iterators.cpp:48:14:48:20 | source1 | standalone_iterators.cpp:48:12:48:12 | call to operator++ | |
|
||||
| standalone_iterators.cpp:51:37:51:43 | source1 | standalone_iterators.cpp:52:11:52:17 | source1 | |
|
||||
| standalone_iterators.cpp:51:37:51:43 | source1 | standalone_iterators.cpp:53:12:53:18 | source1 | |
|
||||
| standalone_iterators.cpp:51:37:51:43 | source1 | standalone_iterators.cpp:54:14:54:20 | source1 | |
|
||||
| standalone_iterators.cpp:53:12:53:18 | ref arg source1 | standalone_iterators.cpp:54:14:54:20 | source1 | |
|
||||
| standalone_iterators.cpp:83:15:83:16 | call to container | standalone_iterators.cpp:85:35:85:36 | c1 | |
|
||||
| standalone_iterators.cpp:83:15:83:16 | call to container | standalone_iterators.cpp:87:10:87:11 | c1 | |
|
||||
| standalone_iterators.cpp:83:19:83:20 | call to container | standalone_iterators.cpp:89:35:89:36 | c2 | |
|
||||
| standalone_iterators.cpp:83:19:83:20 | call to container | standalone_iterators.cpp:91:10:91:11 | c2 | |
|
||||
| standalone_iterators.cpp:85:35:85:36 | c1 | standalone_iterators.cpp:85:38:85:42 | call to begin | TAINT |
|
||||
| standalone_iterators.cpp:85:35:85:36 | ref arg c1 | standalone_iterators.cpp:87:10:87:11 | c1 | |
|
||||
| standalone_iterators.cpp:85:38:85:42 | call to begin | standalone_iterators.cpp:86:6:86:7 | i1 | |
|
||||
| standalone_iterators.cpp:86:5:86:5 | ref arg call to operator* | standalone_iterators.cpp:86:8:86:8 | ref arg call to operator-- | TAINT |
|
||||
| standalone_iterators.cpp:86:5:86:5 | ref arg call to operator* | standalone_iterators.cpp:87:10:87:11 | c1 | |
|
||||
| standalone_iterators.cpp:86:6:86:7 | i1 | standalone_iterators.cpp:86:8:86:8 | call to operator-- | |
|
||||
| standalone_iterators.cpp:86:8:86:8 | call to operator-- | standalone_iterators.cpp:86:5:86:5 | call to operator* | TAINT |
|
||||
| standalone_iterators.cpp:86:8:86:8 | ref arg call to operator-- | standalone_iterators.cpp:86:6:86:7 | ref arg i1 | |
|
||||
| standalone_iterators.cpp:86:13:86:18 | call to source | standalone_iterators.cpp:86:5:86:5 | ref arg call to operator* | TAINT |
|
||||
| standalone_iterators.cpp:89:35:89:36 | c2 | standalone_iterators.cpp:89:38:89:42 | call to begin | TAINT |
|
||||
| standalone_iterators.cpp:89:35:89:36 | ref arg c2 | standalone_iterators.cpp:91:10:91:11 | c2 | |
|
||||
| standalone_iterators.cpp:89:38:89:42 | call to begin | standalone_iterators.cpp:90:6:90:7 | i2 | |
|
||||
| standalone_iterators.cpp:90:5:90:5 | ref arg call to operator* | standalone_iterators.cpp:90:8:90:8 | ref arg call to operator-- | TAINT |
|
||||
| standalone_iterators.cpp:90:5:90:5 | ref arg call to operator* | standalone_iterators.cpp:91:10:91:11 | c2 | |
|
||||
| standalone_iterators.cpp:90:6:90:7 | i2 | standalone_iterators.cpp:90:8:90:8 | call to operator-- | |
|
||||
| standalone_iterators.cpp:90:8:90:8 | call to operator-- | standalone_iterators.cpp:90:5:90:5 | call to operator* | TAINT |
|
||||
| standalone_iterators.cpp:90:8:90:8 | ref arg call to operator-- | standalone_iterators.cpp:90:6:90:7 | ref arg i2 | |
|
||||
| standalone_iterators.cpp:90:13:90:13 | 0 | standalone_iterators.cpp:90:5:90:5 | ref arg call to operator* | TAINT |
|
||||
| standalone_iterators.cpp:98:15:98:16 | call to container | standalone_iterators.cpp:101:6:101:7 | c1 | |
|
||||
| standalone_iterators.cpp:98:15:98:16 | call to container | standalone_iterators.cpp:102:6:102:7 | c1 | |
|
||||
| standalone_iterators.cpp:98:15:98:16 | call to container | standalone_iterators.cpp:106:6:106:7 | c1 | |
|
||||
| standalone_iterators.cpp:98:15:98:16 | call to container | standalone_iterators.cpp:109:7:109:8 | c1 | |
|
||||
| standalone_iterators.cpp:101:6:101:7 | c1 | standalone_iterators.cpp:101:9:101:13 | call to begin | TAINT |
|
||||
| standalone_iterators.cpp:101:6:101:7 | ref arg c1 | standalone_iterators.cpp:102:6:102:7 | c1 | |
|
||||
| standalone_iterators.cpp:101:6:101:7 | ref arg c1 | standalone_iterators.cpp:106:6:106:7 | c1 | |
|
||||
| standalone_iterators.cpp:101:6:101:7 | ref arg c1 | standalone_iterators.cpp:109:7:109:8 | c1 | |
|
||||
| standalone_iterators.cpp:101:9:101:13 | call to begin | standalone_iterators.cpp:101:2:101:15 | ... = ... | |
|
||||
| standalone_iterators.cpp:101:9:101:13 | call to begin | standalone_iterators.cpp:103:3:103:3 | a | |
|
||||
| standalone_iterators.cpp:101:9:101:13 | call to begin | standalone_iterators.cpp:104:7:104:7 | a | |
|
||||
| standalone_iterators.cpp:102:6:102:7 | c1 | standalone_iterators.cpp:102:9:102:13 | call to begin | TAINT |
|
||||
| standalone_iterators.cpp:102:6:102:7 | ref arg c1 | standalone_iterators.cpp:106:6:106:7 | c1 | |
|
||||
| standalone_iterators.cpp:102:6:102:7 | ref arg c1 | standalone_iterators.cpp:109:7:109:8 | c1 | |
|
||||
| standalone_iterators.cpp:102:9:102:13 | call to begin | standalone_iterators.cpp:102:2:102:15 | ... = ... | |
|
||||
| standalone_iterators.cpp:102:9:102:13 | call to begin | standalone_iterators.cpp:107:7:107:7 | b | |
|
||||
| standalone_iterators.cpp:103:2:103:2 | ref arg call to operator* | standalone_iterators.cpp:103:3:103:3 | ref arg a | TAINT |
|
||||
| standalone_iterators.cpp:103:2:103:2 | ref arg call to operator* | standalone_iterators.cpp:106:6:106:7 | c1 | |
|
||||
| standalone_iterators.cpp:103:2:103:2 | ref arg call to operator* | standalone_iterators.cpp:109:7:109:8 | c1 | |
|
||||
| standalone_iterators.cpp:103:3:103:3 | a | standalone_iterators.cpp:103:2:103:2 | call to operator* | TAINT |
|
||||
| standalone_iterators.cpp:103:3:103:3 | ref arg a | standalone_iterators.cpp:104:7:104:7 | a | |
|
||||
| standalone_iterators.cpp:103:7:103:12 | call to source | standalone_iterators.cpp:103:2:103:2 | ref arg call to operator* | TAINT |
|
||||
| standalone_iterators.cpp:104:7:104:7 | a [post update] | standalone_iterators.cpp:106:6:106:7 | c1 | |
|
||||
| standalone_iterators.cpp:104:7:104:7 | a [post update] | standalone_iterators.cpp:109:7:109:8 | c1 | |
|
||||
| standalone_iterators.cpp:106:6:106:7 | c1 | standalone_iterators.cpp:106:9:106:13 | call to begin | TAINT |
|
||||
| standalone_iterators.cpp:106:6:106:7 | ref arg c1 | standalone_iterators.cpp:109:7:109:8 | c1 | |
|
||||
| standalone_iterators.cpp:106:9:106:13 | call to begin | standalone_iterators.cpp:106:2:106:15 | ... = ... | |
|
||||
| standalone_iterators.cpp:106:9:106:13 | call to begin | standalone_iterators.cpp:108:7:108:7 | c | |
|
||||
| standalone_iterators.cpp:107:7:107:7 | b [post update] | standalone_iterators.cpp:109:7:109:8 | c1 | |
|
||||
| standalone_iterators.cpp:108:7:108:7 | c [post update] | standalone_iterators.cpp:109:7:109:8 | c1 | |
|
||||
| standalone_iterators.cpp:113:15:113:16 | call to container | standalone_iterators.cpp:116:7:116:8 | c1 | |
|
||||
| standalone_iterators.cpp:113:15:113:16 | call to container | standalone_iterators.cpp:122:7:122:8 | c1 | |
|
||||
| standalone_iterators.cpp:116:7:116:8 | c1 | standalone_iterators.cpp:116:10:116:14 | call to begin | TAINT |
|
||||
| standalone_iterators.cpp:116:7:116:8 | ref arg c1 | standalone_iterators.cpp:122:7:122:8 | c1 | |
|
||||
| standalone_iterators.cpp:116:10:116:14 | call to begin | standalone_iterators.cpp:116:2:116:16 | ... = ... | |
|
||||
| standalone_iterators.cpp:116:10:116:14 | call to begin | standalone_iterators.cpp:117:7:117:8 | it | |
|
||||
| standalone_iterators.cpp:116:10:116:14 | call to begin | standalone_iterators.cpp:118:2:118:3 | it | |
|
||||
| standalone_iterators.cpp:116:10:116:14 | call to begin | standalone_iterators.cpp:119:7:119:8 | it | |
|
||||
| standalone_iterators.cpp:116:10:116:14 | call to begin | standalone_iterators.cpp:120:2:120:3 | it | |
|
||||
| standalone_iterators.cpp:116:10:116:14 | call to begin | standalone_iterators.cpp:121:7:121:8 | it | |
|
||||
| standalone_iterators.cpp:117:7:117:8 | it [post update] | standalone_iterators.cpp:122:7:122:8 | c1 | |
|
||||
| standalone_iterators.cpp:118:2:118:3 | it | standalone_iterators.cpp:118:5:118:5 | call to operator+= | TAINT |
|
||||
| standalone_iterators.cpp:118:2:118:3 | ref arg it | standalone_iterators.cpp:119:7:119:8 | it | |
|
||||
| standalone_iterators.cpp:118:2:118:3 | ref arg it | standalone_iterators.cpp:120:2:120:3 | it | |
|
||||
| standalone_iterators.cpp:118:2:118:3 | ref arg it | standalone_iterators.cpp:121:7:121:8 | it | |
|
||||
| standalone_iterators.cpp:118:2:118:3 | ref arg it | standalone_iterators.cpp:122:7:122:8 | c1 | |
|
||||
| standalone_iterators.cpp:118:8:118:8 | 1 | standalone_iterators.cpp:118:2:118:3 | ref arg it | TAINT |
|
||||
| standalone_iterators.cpp:120:2:120:3 | it | standalone_iterators.cpp:120:5:120:5 | call to operator+= | TAINT |
|
||||
| standalone_iterators.cpp:120:2:120:3 | ref arg it | standalone_iterators.cpp:121:7:121:8 | it | |
|
||||
| standalone_iterators.cpp:120:8:120:13 | call to source | standalone_iterators.cpp:120:2:120:3 | ref arg it | TAINT |
|
||||
| standalone_iterators.cpp:43:45:43:51 | source1 | standalone_iterators.cpp:43:45:43:51 | source1 | |
|
||||
| standalone_iterators.cpp:43:45:43:51 | source1 | standalone_iterators.cpp:44:11:44:17 | source1 | |
|
||||
| standalone_iterators.cpp:43:45:43:51 | source1 | standalone_iterators.cpp:45:12:45:18 | source1 | |
|
||||
| standalone_iterators.cpp:43:45:43:51 | source1 | standalone_iterators.cpp:46:14:46:20 | source1 | |
|
||||
| standalone_iterators.cpp:44:11:44:17 | source1 | standalone_iterators.cpp:44:10:44:10 | call to operator* | TAINT |
|
||||
| standalone_iterators.cpp:45:12:45:18 | ref arg source1 | standalone_iterators.cpp:43:45:43:51 | source1 | |
|
||||
| standalone_iterators.cpp:45:12:45:18 | ref arg source1 | standalone_iterators.cpp:46:14:46:20 | source1 | |
|
||||
| standalone_iterators.cpp:45:12:45:18 | source1 | standalone_iterators.cpp:45:19:45:19 | call to operator++ | |
|
||||
| standalone_iterators.cpp:45:19:45:19 | call to operator++ | standalone_iterators.cpp:45:10:45:10 | call to operator* | TAINT |
|
||||
| standalone_iterators.cpp:46:12:46:12 | call to operator++ | standalone_iterators.cpp:46:10:46:10 | call to operator* | TAINT |
|
||||
| standalone_iterators.cpp:46:14:46:20 | ref arg source1 | standalone_iterators.cpp:43:45:43:51 | source1 | |
|
||||
| standalone_iterators.cpp:46:14:46:20 | source1 | standalone_iterators.cpp:46:12:46:12 | call to operator++ | |
|
||||
| standalone_iterators.cpp:49:39:49:45 | source1 | standalone_iterators.cpp:49:39:49:45 | source1 | |
|
||||
| standalone_iterators.cpp:49:39:49:45 | source1 | standalone_iterators.cpp:50:11:50:17 | source1 | |
|
||||
| standalone_iterators.cpp:49:39:49:45 | source1 | standalone_iterators.cpp:51:12:51:18 | source1 | |
|
||||
| standalone_iterators.cpp:49:39:49:45 | source1 | standalone_iterators.cpp:52:14:52:20 | source1 | |
|
||||
| standalone_iterators.cpp:50:11:50:17 | source1 | standalone_iterators.cpp:50:10:50:10 | call to operator* | TAINT |
|
||||
| standalone_iterators.cpp:51:12:51:18 | ref arg source1 | standalone_iterators.cpp:49:39:49:45 | source1 | |
|
||||
| standalone_iterators.cpp:51:12:51:18 | ref arg source1 | standalone_iterators.cpp:52:14:52:20 | source1 | |
|
||||
| standalone_iterators.cpp:51:12:51:18 | source1 | standalone_iterators.cpp:51:19:51:19 | call to operator++ | |
|
||||
| standalone_iterators.cpp:51:19:51:19 | call to operator++ | standalone_iterators.cpp:51:10:51:10 | call to operator* | TAINT |
|
||||
| standalone_iterators.cpp:52:12:52:12 | call to operator++ | standalone_iterators.cpp:52:10:52:10 | call to operator* | TAINT |
|
||||
| standalone_iterators.cpp:52:14:52:20 | ref arg source1 | standalone_iterators.cpp:49:39:49:45 | source1 | |
|
||||
| standalone_iterators.cpp:52:14:52:20 | source1 | standalone_iterators.cpp:52:12:52:12 | call to operator++ | |
|
||||
| standalone_iterators.cpp:55:37:55:43 | source1 | standalone_iterators.cpp:56:11:56:17 | source1 | |
|
||||
| standalone_iterators.cpp:55:37:55:43 | source1 | standalone_iterators.cpp:57:12:57:18 | source1 | |
|
||||
| standalone_iterators.cpp:55:37:55:43 | source1 | standalone_iterators.cpp:58:14:58:20 | source1 | |
|
||||
| standalone_iterators.cpp:57:12:57:18 | ref arg source1 | standalone_iterators.cpp:58:14:58:20 | source1 | |
|
||||
| standalone_iterators.cpp:91:15:91:16 | call to container | standalone_iterators.cpp:93:35:93:36 | c1 | |
|
||||
| standalone_iterators.cpp:91:15:91:16 | call to container | standalone_iterators.cpp:95:10:95:11 | c1 | |
|
||||
| standalone_iterators.cpp:91:19:91:20 | call to container | standalone_iterators.cpp:97:35:97:36 | c2 | |
|
||||
| standalone_iterators.cpp:91:19:91:20 | call to container | standalone_iterators.cpp:99:10:99:11 | c2 | |
|
||||
| standalone_iterators.cpp:93:35:93:36 | c1 | standalone_iterators.cpp:93:38:93:42 | call to begin | TAINT |
|
||||
| standalone_iterators.cpp:93:35:93:36 | ref arg c1 | standalone_iterators.cpp:95:10:95:11 | c1 | |
|
||||
| standalone_iterators.cpp:93:38:93:42 | call to begin | standalone_iterators.cpp:94:6:94:7 | i1 | |
|
||||
| standalone_iterators.cpp:94:5:94:5 | ref arg call to operator* | standalone_iterators.cpp:94:8:94:8 | ref arg call to operator-- | TAINT |
|
||||
| standalone_iterators.cpp:94:5:94:5 | ref arg call to operator* | standalone_iterators.cpp:95:10:95:11 | c1 | |
|
||||
| standalone_iterators.cpp:94:6:94:7 | i1 | standalone_iterators.cpp:94:8:94:8 | call to operator-- | |
|
||||
| standalone_iterators.cpp:94:8:94:8 | call to operator-- | standalone_iterators.cpp:94:5:94:5 | call to operator* | TAINT |
|
||||
| standalone_iterators.cpp:94:8:94:8 | ref arg call to operator-- | standalone_iterators.cpp:94:6:94:7 | ref arg i1 | |
|
||||
| standalone_iterators.cpp:94:13:94:18 | call to source | standalone_iterators.cpp:94:5:94:5 | ref arg call to operator* | TAINT |
|
||||
| standalone_iterators.cpp:97:35:97:36 | c2 | standalone_iterators.cpp:97:38:97:42 | call to begin | TAINT |
|
||||
| standalone_iterators.cpp:97:35:97:36 | ref arg c2 | standalone_iterators.cpp:99:10:99:11 | c2 | |
|
||||
| standalone_iterators.cpp:97:38:97:42 | call to begin | standalone_iterators.cpp:98:6:98:7 | i2 | |
|
||||
| standalone_iterators.cpp:98:5:98:5 | ref arg call to operator* | standalone_iterators.cpp:98:8:98:8 | ref arg call to operator-- | TAINT |
|
||||
| standalone_iterators.cpp:98:5:98:5 | ref arg call to operator* | standalone_iterators.cpp:99:10:99:11 | c2 | |
|
||||
| standalone_iterators.cpp:98:6:98:7 | i2 | standalone_iterators.cpp:98:8:98:8 | call to operator-- | |
|
||||
| standalone_iterators.cpp:98:8:98:8 | call to operator-- | standalone_iterators.cpp:98:5:98:5 | call to operator* | TAINT |
|
||||
| standalone_iterators.cpp:98:8:98:8 | ref arg call to operator-- | standalone_iterators.cpp:98:6:98:7 | ref arg i2 | |
|
||||
| standalone_iterators.cpp:98:13:98:13 | 0 | standalone_iterators.cpp:98:5:98:5 | ref arg call to operator* | TAINT |
|
||||
| standalone_iterators.cpp:106:15:106:16 | call to container | standalone_iterators.cpp:109:6:109:7 | c1 | |
|
||||
| standalone_iterators.cpp:106:15:106:16 | call to container | standalone_iterators.cpp:110:6:110:7 | c1 | |
|
||||
| standalone_iterators.cpp:106:15:106:16 | call to container | standalone_iterators.cpp:114:6:114:7 | c1 | |
|
||||
| standalone_iterators.cpp:106:15:106:16 | call to container | standalone_iterators.cpp:117:7:117:8 | c1 | |
|
||||
| standalone_iterators.cpp:109:6:109:7 | c1 | standalone_iterators.cpp:109:9:109:13 | call to begin | TAINT |
|
||||
| standalone_iterators.cpp:109:6:109:7 | ref arg c1 | standalone_iterators.cpp:110:6:110:7 | c1 | |
|
||||
| standalone_iterators.cpp:109:6:109:7 | ref arg c1 | standalone_iterators.cpp:114:6:114:7 | c1 | |
|
||||
| standalone_iterators.cpp:109:6:109:7 | ref arg c1 | standalone_iterators.cpp:117:7:117:8 | c1 | |
|
||||
| standalone_iterators.cpp:109:9:109:13 | call to begin | standalone_iterators.cpp:109:2:109:15 | ... = ... | |
|
||||
| standalone_iterators.cpp:109:9:109:13 | call to begin | standalone_iterators.cpp:111:3:111:3 | a | |
|
||||
| standalone_iterators.cpp:109:9:109:13 | call to begin | standalone_iterators.cpp:112:7:112:7 | a | |
|
||||
| standalone_iterators.cpp:110:6:110:7 | c1 | standalone_iterators.cpp:110:9:110:13 | call to begin | TAINT |
|
||||
| standalone_iterators.cpp:110:6:110:7 | ref arg c1 | standalone_iterators.cpp:114:6:114:7 | c1 | |
|
||||
| standalone_iterators.cpp:110:6:110:7 | ref arg c1 | standalone_iterators.cpp:117:7:117:8 | c1 | |
|
||||
| standalone_iterators.cpp:110:9:110:13 | call to begin | standalone_iterators.cpp:110:2:110:15 | ... = ... | |
|
||||
| standalone_iterators.cpp:110:9:110:13 | call to begin | standalone_iterators.cpp:115:7:115:7 | b | |
|
||||
| standalone_iterators.cpp:111:2:111:2 | ref arg call to operator* | standalone_iterators.cpp:111:3:111:3 | ref arg a | TAINT |
|
||||
| standalone_iterators.cpp:111:2:111:2 | ref arg call to operator* | standalone_iterators.cpp:114:6:114:7 | c1 | |
|
||||
| standalone_iterators.cpp:111:2:111:2 | ref arg call to operator* | standalone_iterators.cpp:117:7:117:8 | c1 | |
|
||||
| standalone_iterators.cpp:111:3:111:3 | a | standalone_iterators.cpp:111:2:111:2 | call to operator* | TAINT |
|
||||
| standalone_iterators.cpp:111:3:111:3 | ref arg a | standalone_iterators.cpp:112:7:112:7 | a | |
|
||||
| standalone_iterators.cpp:111:7:111:12 | call to source | standalone_iterators.cpp:111:2:111:2 | ref arg call to operator* | TAINT |
|
||||
| standalone_iterators.cpp:112:7:112:7 | a [post update] | standalone_iterators.cpp:114:6:114:7 | c1 | |
|
||||
| standalone_iterators.cpp:112:7:112:7 | a [post update] | standalone_iterators.cpp:117:7:117:8 | c1 | |
|
||||
| standalone_iterators.cpp:114:6:114:7 | c1 | standalone_iterators.cpp:114:9:114:13 | call to begin | TAINT |
|
||||
| standalone_iterators.cpp:114:6:114:7 | ref arg c1 | standalone_iterators.cpp:117:7:117:8 | c1 | |
|
||||
| standalone_iterators.cpp:114:9:114:13 | call to begin | standalone_iterators.cpp:114:2:114:15 | ... = ... | |
|
||||
| standalone_iterators.cpp:114:9:114:13 | call to begin | standalone_iterators.cpp:116:7:116:7 | c | |
|
||||
| standalone_iterators.cpp:115:7:115:7 | b [post update] | standalone_iterators.cpp:117:7:117:8 | c1 | |
|
||||
| standalone_iterators.cpp:116:7:116:7 | c [post update] | standalone_iterators.cpp:117:7:117:8 | c1 | |
|
||||
| standalone_iterators.cpp:121:15:121:16 | call to container | standalone_iterators.cpp:124:7:124:8 | c1 | |
|
||||
| standalone_iterators.cpp:121:15:121:16 | call to container | standalone_iterators.cpp:130:7:130:8 | c1 | |
|
||||
| standalone_iterators.cpp:124:7:124:8 | c1 | standalone_iterators.cpp:124:10:124:14 | call to begin | TAINT |
|
||||
| standalone_iterators.cpp:124:7:124:8 | ref arg c1 | standalone_iterators.cpp:130:7:130:8 | c1 | |
|
||||
| standalone_iterators.cpp:124:10:124:14 | call to begin | standalone_iterators.cpp:124:2:124:16 | ... = ... | |
|
||||
| standalone_iterators.cpp:124:10:124:14 | call to begin | standalone_iterators.cpp:125:7:125:8 | it | |
|
||||
| standalone_iterators.cpp:124:10:124:14 | call to begin | standalone_iterators.cpp:126:2:126:3 | it | |
|
||||
| standalone_iterators.cpp:124:10:124:14 | call to begin | standalone_iterators.cpp:127:7:127:8 | it | |
|
||||
| standalone_iterators.cpp:124:10:124:14 | call to begin | standalone_iterators.cpp:128:2:128:3 | it | |
|
||||
| standalone_iterators.cpp:124:10:124:14 | call to begin | standalone_iterators.cpp:129:7:129:8 | it | |
|
||||
| standalone_iterators.cpp:125:7:125:8 | it [post update] | standalone_iterators.cpp:130:7:130:8 | c1 | |
|
||||
| standalone_iterators.cpp:126:2:126:3 | it | standalone_iterators.cpp:126:5:126:5 | call to operator+= | TAINT |
|
||||
| standalone_iterators.cpp:126:2:126:3 | ref arg it | standalone_iterators.cpp:127:7:127:8 | it | |
|
||||
| standalone_iterators.cpp:126:2:126:3 | ref arg it | standalone_iterators.cpp:128:2:128:3 | it | |
|
||||
| standalone_iterators.cpp:126:2:126:3 | ref arg it | standalone_iterators.cpp:129:7:129:8 | it | |
|
||||
| standalone_iterators.cpp:126:2:126:3 | ref arg it | standalone_iterators.cpp:130:7:130:8 | c1 | |
|
||||
| standalone_iterators.cpp:126:8:126:8 | 1 | standalone_iterators.cpp:126:2:126:3 | ref arg it | TAINT |
|
||||
| standalone_iterators.cpp:128:2:128:3 | it | standalone_iterators.cpp:128:5:128:5 | call to operator+= | TAINT |
|
||||
| standalone_iterators.cpp:128:2:128:3 | ref arg it | standalone_iterators.cpp:129:7:129:8 | it | |
|
||||
| standalone_iterators.cpp:128:8:128:13 | call to source | standalone_iterators.cpp:128:2:128:3 | ref arg it | TAINT |
|
||||
| stl.h:75:8:75:8 | container | stl.h:75:8:75:8 | constructor init of field container | TAINT |
|
||||
| stl.h:75:8:75:8 | container | stl.h:75:8:75:8 | constructor init of field container | TAINT |
|
||||
| stl.h:75:8:75:8 | container | stl.h:75:8:75:8 | container | |
|
||||
|
||||
@@ -27,6 +27,10 @@ public:
|
||||
template<>
|
||||
struct std::iterator_traits<int_iterator_by_trait> {
|
||||
typedef input_iterator_tag iterator_category;
|
||||
typedef int value_type;
|
||||
typedef size_t difference_type;
|
||||
typedef int* pointer;
|
||||
typedef int& reference;
|
||||
};
|
||||
|
||||
class non_iterator {
|
||||
@@ -69,6 +73,10 @@ public:
|
||||
template<>
|
||||
struct std::iterator_traits<insert_iterator_by_trait> {
|
||||
typedef output_iterator_tag iterator_category;
|
||||
typedef int value_type;
|
||||
typedef size_t difference_type;
|
||||
typedef int* pointer;
|
||||
typedef int& reference;
|
||||
};
|
||||
|
||||
class container {
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
import cpp
|
||||
import experimental.semmle.code.cpp.semantic.analysis.ModulusAnalysis
|
||||
import experimental.semmle.code.cpp.semantic.Semantic
|
||||
import experimental.semmle.code.cpp.semantic.analysis.RangeUtils
|
||||
import experimental.semmle.code.cpp.semantic.analysis.FloatDelta
|
||||
import experimental.semmle.code.cpp.semantic.analysis.RangeAnalysisSpecific
|
||||
import experimental.semmle.code.cpp.semantic.analysis.RangeAnalysis
|
||||
import semmle.code.cpp.ir.IR as IR
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
|
||||
module ModulusAnalysisInstantiated =
|
||||
ModulusAnalysis<FloatDelta, Bounds, RangeUtil<FloatDelta, CppLangImpl>>;
|
||||
|
||||
class ModulusAnalysisTest extends InlineExpectationsTest {
|
||||
ModulusAnalysisTest() { this = "ModulusAnalysisTest" }
|
||||
|
||||
@@ -23,7 +30,7 @@ class ModulusAnalysisTest extends InlineExpectationsTest {
|
||||
|
||||
private string getAModString(SemExpr e) {
|
||||
exists(SemBound b, int delta, int mod |
|
||||
semExprModulus(e, b, delta, mod) and
|
||||
ModulusAnalysisInstantiated::semExprModulus(e, b, delta, mod) and
|
||||
result = b.toString() + "," + delta.toString() + "," + mod.toString() and
|
||||
not (delta = 0 and mod = 0)
|
||||
)
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
import cpp
|
||||
import experimental.semmle.code.cpp.semantic.analysis.SignAnalysisCommon
|
||||
import experimental.semmle.code.cpp.semantic.Semantic
|
||||
import experimental.semmle.code.cpp.semantic.analysis.RangeUtils
|
||||
import experimental.semmle.code.cpp.semantic.analysis.FloatDelta
|
||||
import experimental.semmle.code.cpp.semantic.analysis.RangeAnalysisSpecific
|
||||
import semmle.code.cpp.ir.IR as IR
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
|
||||
module SignAnalysisInstantiated = SignAnalysis<FloatDelta, RangeUtil<FloatDelta, CppLangImpl>>;
|
||||
|
||||
class SignAnalysisTest extends InlineExpectationsTest {
|
||||
SignAnalysisTest() { this = "SignAnalysisTest" }
|
||||
|
||||
@@ -21,4 +26,6 @@ class SignAnalysisTest extends InlineExpectationsTest {
|
||||
}
|
||||
}
|
||||
|
||||
private string getASignString(SemExpr e) { result = strictconcat(semExprSign(e).toString(), "") }
|
||||
private string getASignString(SemExpr e) {
|
||||
result = strictconcat(SignAnalysisInstantiated::semExprSign(e).toString(), "")
|
||||
}
|
||||
|
||||
@@ -168,4 +168,34 @@ int main(int argc, char **argv) {
|
||||
int i10 = (int) argv[1];
|
||||
printf((char *) i10);
|
||||
printWrapper((char *) i10);
|
||||
|
||||
// BAD: b value comes from argv
|
||||
{
|
||||
char b[64];
|
||||
char *bp = &b[0];
|
||||
char *t;
|
||||
if (0) {
|
||||
t = 0;
|
||||
} else {
|
||||
t = bp;
|
||||
}
|
||||
memcpy(t, argv[1] + 1, 1);
|
||||
printf(bp);
|
||||
printWrapper(bp);
|
||||
}
|
||||
|
||||
// BAD: b value comes from argv
|
||||
{
|
||||
char b[64];
|
||||
char *bp = &b[0];
|
||||
char *t;
|
||||
if (1) {
|
||||
t = ++bp;
|
||||
} else {
|
||||
t = 0;
|
||||
}
|
||||
memcpy(t, argv[1] + 1, 1);
|
||||
printf(bp);
|
||||
printWrapper(bp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -260,6 +260,30 @@ edges
|
||||
| argvLocal.c:168:18:168:21 | argv | argvLocal.c:170:24:170:26 | i10 |
|
||||
| argvLocal.c:168:18:168:21 | argv | argvLocal.c:170:24:170:26 | i10 |
|
||||
| argvLocal.c:168:18:168:21 | argv | argvLocal.c:170:24:170:26 | i10 |
|
||||
| argvLocal.c:182:13:182:16 | argv | argvLocal.c:183:10:183:11 | (const char *)... |
|
||||
| argvLocal.c:182:13:182:16 | argv | argvLocal.c:183:10:183:11 | (const char *)... |
|
||||
| argvLocal.c:182:13:182:16 | argv | argvLocal.c:183:10:183:11 | bp |
|
||||
| argvLocal.c:182:13:182:16 | argv | argvLocal.c:183:10:183:11 | bp |
|
||||
| argvLocal.c:182:13:182:16 | argv | argvLocal.c:183:10:183:11 | bp indirection |
|
||||
| argvLocal.c:182:13:182:16 | argv | argvLocal.c:183:10:183:11 | bp indirection |
|
||||
| argvLocal.c:182:13:182:16 | argv | argvLocal.c:184:16:184:17 | bp |
|
||||
| argvLocal.c:182:13:182:16 | argv | argvLocal.c:184:16:184:17 | bp |
|
||||
| argvLocal.c:182:13:182:16 | argv | argvLocal.c:184:16:184:17 | bp |
|
||||
| argvLocal.c:182:13:182:16 | argv | argvLocal.c:184:16:184:17 | bp |
|
||||
| argvLocal.c:182:13:182:16 | argv | argvLocal.c:184:16:184:17 | bp indirection |
|
||||
| argvLocal.c:182:13:182:16 | argv | argvLocal.c:184:16:184:17 | bp indirection |
|
||||
| argvLocal.c:197:13:197:16 | argv | argvLocal.c:198:10:198:11 | (const char *)... |
|
||||
| argvLocal.c:197:13:197:16 | argv | argvLocal.c:198:10:198:11 | (const char *)... |
|
||||
| argvLocal.c:197:13:197:16 | argv | argvLocal.c:198:10:198:11 | bp |
|
||||
| argvLocal.c:197:13:197:16 | argv | argvLocal.c:198:10:198:11 | bp |
|
||||
| argvLocal.c:197:13:197:16 | argv | argvLocal.c:198:10:198:11 | bp indirection |
|
||||
| argvLocal.c:197:13:197:16 | argv | argvLocal.c:198:10:198:11 | bp indirection |
|
||||
| argvLocal.c:197:13:197:16 | argv | argvLocal.c:199:16:199:17 | bp |
|
||||
| argvLocal.c:197:13:197:16 | argv | argvLocal.c:199:16:199:17 | bp |
|
||||
| argvLocal.c:197:13:197:16 | argv | argvLocal.c:199:16:199:17 | bp |
|
||||
| argvLocal.c:197:13:197:16 | argv | argvLocal.c:199:16:199:17 | bp |
|
||||
| argvLocal.c:197:13:197:16 | argv | argvLocal.c:199:16:199:17 | bp indirection |
|
||||
| argvLocal.c:197:13:197:16 | argv | argvLocal.c:199:16:199:17 | bp indirection |
|
||||
subpaths
|
||||
| argvLocal.c:102:15:102:16 | i1 indirection | argvLocal.c:9:25:9:31 | *correct | argvLocal.c:9:25:9:31 | ReturnIndirection | argvLocal.c:102:15:102:16 | printWrapper output argument |
|
||||
| argvLocal.c:107:15:107:19 | access to array indirection | argvLocal.c:9:25:9:31 | *correct | argvLocal.c:9:25:9:31 | ReturnIndirection | argvLocal.c:107:15:107:19 | printWrapper output argument |
|
||||
@@ -396,6 +420,22 @@ nodes
|
||||
| argvLocal.c:170:15:170:26 | i10 indirection | semmle.label | i10 indirection |
|
||||
| argvLocal.c:170:24:170:26 | i10 | semmle.label | i10 |
|
||||
| argvLocal.c:170:24:170:26 | i10 | semmle.label | i10 |
|
||||
| argvLocal.c:182:13:182:16 | argv | semmle.label | argv |
|
||||
| argvLocal.c:182:13:182:16 | argv | semmle.label | argv |
|
||||
| argvLocal.c:183:10:183:11 | (const char *)... | semmle.label | (const char *)... |
|
||||
| argvLocal.c:183:10:183:11 | bp | semmle.label | bp |
|
||||
| argvLocal.c:183:10:183:11 | bp indirection | semmle.label | bp indirection |
|
||||
| argvLocal.c:184:16:184:17 | bp | semmle.label | bp |
|
||||
| argvLocal.c:184:16:184:17 | bp | semmle.label | bp |
|
||||
| argvLocal.c:184:16:184:17 | bp indirection | semmle.label | bp indirection |
|
||||
| argvLocal.c:197:13:197:16 | argv | semmle.label | argv |
|
||||
| argvLocal.c:197:13:197:16 | argv | semmle.label | argv |
|
||||
| argvLocal.c:198:10:198:11 | (const char *)... | semmle.label | (const char *)... |
|
||||
| argvLocal.c:198:10:198:11 | bp | semmle.label | bp |
|
||||
| argvLocal.c:198:10:198:11 | bp indirection | semmle.label | bp indirection |
|
||||
| argvLocal.c:199:16:199:17 | bp | semmle.label | bp |
|
||||
| argvLocal.c:199:16:199:17 | bp | semmle.label | bp |
|
||||
| argvLocal.c:199:16:199:17 | bp indirection | semmle.label | bp indirection |
|
||||
#select
|
||||
| argvLocal.c:95:9:95:15 | access to array | argvLocal.c:95:9:95:12 | argv | argvLocal.c:95:9:95:15 | access to array | The value of this argument may come from $@ and is being used as a formatting argument to printf(format). | argvLocal.c:95:9:95:12 | argv | argv |
|
||||
| argvLocal.c:96:15:96:21 | access to array | argvLocal.c:96:15:96:18 | argv | argvLocal.c:96:15:96:21 | access to array | The value of this argument may come from $@ and is being used as a formatting argument to printWrapper(correct), which calls printf(format). | argvLocal.c:96:15:96:18 | argv | argv |
|
||||
@@ -425,3 +465,7 @@ nodes
|
||||
| argvLocal.c:165:15:165:17 | i91 | argvLocal.c:163:22:163:25 | argv | argvLocal.c:165:15:165:17 | i91 | The value of this argument may come from $@ and is being used as a formatting argument to printWrapper(correct), which calls printf(format). | argvLocal.c:163:22:163:25 | argv | argv |
|
||||
| argvLocal.c:169:18:169:20 | i10 | argvLocal.c:168:18:168:21 | argv | argvLocal.c:169:18:169:20 | i10 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format). | argvLocal.c:168:18:168:21 | argv | argv |
|
||||
| argvLocal.c:170:24:170:26 | i10 | argvLocal.c:168:18:168:21 | argv | argvLocal.c:170:24:170:26 | i10 | The value of this argument may come from $@ and is being used as a formatting argument to printWrapper(correct), which calls printf(format). | argvLocal.c:168:18:168:21 | argv | argv |
|
||||
| argvLocal.c:183:10:183:11 | bp | argvLocal.c:182:13:182:16 | argv | argvLocal.c:183:10:183:11 | bp | The value of this argument may come from $@ and is being used as a formatting argument to printf(format). | argvLocal.c:182:13:182:16 | argv | argv |
|
||||
| argvLocal.c:184:16:184:17 | bp | argvLocal.c:182:13:182:16 | argv | argvLocal.c:184:16:184:17 | bp | The value of this argument may come from $@ and is being used as a formatting argument to printWrapper(correct), which calls printf(format). | argvLocal.c:182:13:182:16 | argv | argv |
|
||||
| argvLocal.c:198:10:198:11 | bp | argvLocal.c:197:13:197:16 | argv | argvLocal.c:198:10:198:11 | bp | The value of this argument may come from $@ and is being used as a formatting argument to printf(format). | argvLocal.c:197:13:197:16 | argv | argv |
|
||||
| argvLocal.c:199:16:199:17 | bp | argvLocal.c:197:13:197:16 | argv | argvLocal.c:199:16:199:17 | bp | The value of this argument may come from $@ and is being used as a formatting argument to printWrapper(correct), which calls printf(format). | argvLocal.c:197:13:197:16 | argv | argv |
|
||||
|
||||
@@ -6,7 +6,7 @@ runs:
|
||||
- name: Setup dotnet
|
||||
uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: 6.0.202
|
||||
dotnet-version: 7.0.102
|
||||
- name: Build Extractor
|
||||
shell: bash
|
||||
run: scripts/create-extractor-pack.sh
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Xunit;
|
||||
using Semmle.Autobuild.Shared;
|
||||
using Semmle.Util;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Linq;
|
||||
@@ -85,6 +86,15 @@ namespace Semmle.Autobuild.CSharp.Tests
|
||||
return ret;
|
||||
}
|
||||
|
||||
int IBuildActions.RunProcess(string cmd, string args, string? workingDirectory, IDictionary<string, string>? env, BuildOutputHandler onOutput, BuildOutputHandler onError)
|
||||
{
|
||||
var ret = (this as IBuildActions).RunProcess(cmd, args, workingDirectory, env, out var stdout);
|
||||
|
||||
stdout.ForEach(line => onOutput(line));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public IList<string> DirectoryDeleteIn { get; } = new List<string>();
|
||||
|
||||
void IBuildActions.DirectoryDelete(string dir, bool recursive)
|
||||
@@ -145,6 +155,14 @@ namespace Semmle.Autobuild.CSharp.Tests
|
||||
|
||||
bool IBuildActions.IsWindows() => IsWindows;
|
||||
|
||||
public bool IsMacOs { get; set; }
|
||||
|
||||
bool IBuildActions.IsMacOs() => IsMacOs;
|
||||
|
||||
public bool IsArm { get; set; }
|
||||
|
||||
bool IBuildActions.IsArm() => IsArm;
|
||||
|
||||
public string PathCombine(params string[] parts)
|
||||
{
|
||||
return string.Join(IsWindows ? '\\' : '/', parts.Where(p => !string.IsNullOrWhiteSpace(p)));
|
||||
@@ -383,6 +401,7 @@ namespace Semmle.Autobuild.CSharp.Tests
|
||||
actions.GetEnvironmentVariable[$"CODEQL_EXTRACTOR_{codeqlUpperLanguage}_TRAP_DIR"] = "";
|
||||
actions.GetEnvironmentVariable[$"CODEQL_EXTRACTOR_{codeqlUpperLanguage}_SOURCE_ARCHIVE_DIR"] = "";
|
||||
actions.GetEnvironmentVariable[$"CODEQL_EXTRACTOR_{codeqlUpperLanguage}_ROOT"] = $@"C:\codeql\{codeqlUpperLanguage.ToLowerInvariant()}";
|
||||
actions.GetEnvironmentVariable[$"CODEQL_EXTRACTOR_{codeqlUpperLanguage}_DIAGNOSTIC_DIR"] = Path.GetTempPath();
|
||||
actions.GetEnvironmentVariable["CODEQL_JAVA_HOME"] = @"C:\codeql\tools\java";
|
||||
actions.GetEnvironmentVariable["CODEQL_PLATFORM"] = isWindows ? "win64" : "linux64";
|
||||
actions.GetEnvironmentVariable["LGTM_INDEX_VSTOOLS_VERSION"] = vsToolsVersion;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<RuntimeIdentifiers>win-x64;linux-x64;osx-x64</RuntimeIdentifiers>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
69
csharp/autobuilder/Semmle.Autobuild.CSharp/AutoBuildRule.cs
Normal file
69
csharp/autobuilder/Semmle.Autobuild.CSharp/AutoBuildRule.cs
Normal file
@@ -0,0 +1,69 @@
|
||||
using Semmle.Autobuild.Shared;
|
||||
using Semmle.Extraction.CSharp;
|
||||
|
||||
namespace Semmle.Autobuild.CSharp
|
||||
{
|
||||
internal class AutoBuildRule : IBuildRule<CSharpAutobuildOptions>
|
||||
{
|
||||
private readonly CSharpAutobuilder autobuilder;
|
||||
|
||||
public DotNetRule DotNetRule { get; }
|
||||
|
||||
public MsBuildRule MsBuildRule { get; }
|
||||
|
||||
public BuildCommandAutoRule BuildCommandAutoRule { get; }
|
||||
|
||||
public AutoBuildRule(CSharpAutobuilder autobuilder)
|
||||
{
|
||||
this.autobuilder = autobuilder;
|
||||
this.DotNetRule = new DotNetRule();
|
||||
this.MsBuildRule = new MsBuildRule();
|
||||
this.BuildCommandAutoRule = new BuildCommandAutoRule(DotNetRule.WithDotNet);
|
||||
}
|
||||
|
||||
public BuildScript Analyse(IAutobuilder<CSharpAutobuildOptions> builder, bool auto)
|
||||
{
|
||||
var cleanTrapFolder =
|
||||
BuildScript.DeleteDirectory(this.autobuilder.TrapDir);
|
||||
var cleanSourceArchive =
|
||||
BuildScript.DeleteDirectory(this.autobuilder.SourceArchiveDir);
|
||||
var tryCleanExtractorArgsLogs =
|
||||
BuildScript.Create(actions =>
|
||||
{
|
||||
foreach (var file in Extractor.GetCSharpArgsLogs())
|
||||
{
|
||||
try
|
||||
{
|
||||
actions.FileDelete(file);
|
||||
}
|
||||
catch // lgtm[cs/catch-of-all-exceptions] lgtm[cs/empty-catch-block]
|
||||
{ }
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
var attemptExtractorCleanup =
|
||||
BuildScript.Try(cleanTrapFolder) &
|
||||
BuildScript.Try(cleanSourceArchive) &
|
||||
tryCleanExtractorArgsLogs &
|
||||
BuildScript.DeleteFile(Extractor.GetCSharpLogPath());
|
||||
|
||||
/// <summary>
|
||||
/// Execute script `s` and check that the C# extractor has been executed.
|
||||
/// If either fails, attempt to cleanup any artifacts produced by the extractor,
|
||||
/// and exit with code 1, in order to proceed to the next attempt.
|
||||
/// </summary>
|
||||
BuildScript IntermediateAttempt(BuildScript s) =>
|
||||
(s & this.autobuilder.CheckExtractorRun(false)) |
|
||||
(attemptExtractorCleanup & BuildScript.Failure);
|
||||
|
||||
return
|
||||
// First try .NET Core
|
||||
IntermediateAttempt(this.DotNetRule.Analyse(this.autobuilder, true)) |
|
||||
// Then MSBuild
|
||||
(() => IntermediateAttempt(this.MsBuildRule.Analyse(this.autobuilder, true))) |
|
||||
// And finally look for a script that might be a build script
|
||||
(() => this.BuildCommandAutoRule.Analyse(this.autobuilder, true) & this.autobuilder.CheckExtractorRun(true));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
using Semmle.Extraction.CSharp;
|
||||
using Semmle.Util.Logging;
|
||||
using Semmle.Autobuild.Shared;
|
||||
using Semmle.Util;
|
||||
using System.Linq;
|
||||
|
||||
namespace Semmle.Autobuild.CSharp
|
||||
{
|
||||
@@ -29,25 +31,16 @@ namespace Semmle.Autobuild.CSharp
|
||||
|
||||
public class CSharpAutobuilder : Autobuilder<CSharpAutobuildOptions>
|
||||
{
|
||||
public CSharpAutobuilder(IBuildActions actions, CSharpAutobuildOptions options) : base(actions, options) { }
|
||||
private const string buildCommandDocsUrl =
|
||||
"https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-the-codeql-workflow-for-compiled-languages";
|
||||
|
||||
private readonly AutoBuildRule autoBuildRule;
|
||||
|
||||
public CSharpAutobuilder(IBuildActions actions, CSharpAutobuildOptions options) : base(actions, options, new CSharpDiagnosticClassifier()) =>
|
||||
this.autoBuildRule = new AutoBuildRule(this);
|
||||
|
||||
public override BuildScript GetBuildScript()
|
||||
{
|
||||
/// <summary>
|
||||
/// A script that checks that the C# extractor has been executed.
|
||||
/// </summary>
|
||||
BuildScript CheckExtractorRun(bool warnOnFailure) =>
|
||||
BuildScript.Create(actions =>
|
||||
{
|
||||
if (actions.FileExists(Extractor.GetCSharpLogPath()))
|
||||
return 0;
|
||||
|
||||
if (warnOnFailure)
|
||||
Log(Severity.Error, "No C# code detected during build.");
|
||||
|
||||
return 1;
|
||||
});
|
||||
|
||||
var attempt = BuildScript.Failure;
|
||||
switch (GetCSharpBuildStrategy())
|
||||
{
|
||||
@@ -65,47 +58,9 @@ namespace Semmle.Autobuild.CSharp
|
||||
attempt = new DotNetRule().Analyse(this, false) & CheckExtractorRun(true);
|
||||
break;
|
||||
case CSharpBuildStrategy.Auto:
|
||||
var cleanTrapFolder =
|
||||
BuildScript.DeleteDirectory(TrapDir);
|
||||
var cleanSourceArchive =
|
||||
BuildScript.DeleteDirectory(SourceArchiveDir);
|
||||
var tryCleanExtractorArgsLogs =
|
||||
BuildScript.Create(actions =>
|
||||
{
|
||||
foreach (var file in Extractor.GetCSharpArgsLogs())
|
||||
{
|
||||
try
|
||||
{
|
||||
actions.FileDelete(file);
|
||||
}
|
||||
catch // lgtm[cs/catch-of-all-exceptions] lgtm[cs/empty-catch-block]
|
||||
{ }
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
var attemptExtractorCleanup =
|
||||
BuildScript.Try(cleanTrapFolder) &
|
||||
BuildScript.Try(cleanSourceArchive) &
|
||||
tryCleanExtractorArgsLogs &
|
||||
BuildScript.DeleteFile(Extractor.GetCSharpLogPath());
|
||||
|
||||
/// <summary>
|
||||
/// Execute script `s` and check that the C# extractor has been executed.
|
||||
/// If either fails, attempt to cleanup any artifacts produced by the extractor,
|
||||
/// and exit with code 1, in order to proceed to the next attempt.
|
||||
/// </summary>
|
||||
BuildScript IntermediateAttempt(BuildScript s) =>
|
||||
(s & CheckExtractorRun(false)) |
|
||||
(attemptExtractorCleanup & BuildScript.Failure);
|
||||
|
||||
attempt =
|
||||
// First try .NET Core
|
||||
IntermediateAttempt(new DotNetRule().Analyse(this, true)) |
|
||||
// Then MSBuild
|
||||
(() => IntermediateAttempt(new MsBuildRule().Analyse(this, true))) |
|
||||
// And finally look for a script that might be a build script
|
||||
(() => new BuildCommandAutoRule(DotNetRule.WithDotNet).Analyse(this, true) & CheckExtractorRun(true)) |
|
||||
// Attempt a few different build strategies to see if one works
|
||||
this.autoBuildRule.Analyse(this, true) |
|
||||
// All attempts failed: print message
|
||||
AutobuildFailure();
|
||||
break;
|
||||
@@ -114,6 +69,127 @@ namespace Semmle.Autobuild.CSharp
|
||||
return attempt;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A script that checks that the C# extractor has been executed.
|
||||
/// </summary>
|
||||
public BuildScript CheckExtractorRun(bool warnOnFailure) =>
|
||||
BuildScript.Create(actions =>
|
||||
{
|
||||
if (actions.FileExists(Extractor.GetCSharpLogPath()))
|
||||
return 0;
|
||||
|
||||
if (warnOnFailure)
|
||||
Log(Severity.Error, "No C# code detected during build.");
|
||||
|
||||
return 1;
|
||||
});
|
||||
|
||||
protected override void AutobuildFailureDiagnostic()
|
||||
{
|
||||
// if `ScriptPath` is not null here, the `BuildCommandAuto` rule was
|
||||
// run and found at least one script to execute
|
||||
if (this.autoBuildRule.BuildCommandAutoRule.ScriptPath is not null)
|
||||
{
|
||||
var relScriptPath = this.MakeRelative(autoBuildRule.BuildCommandAutoRule.ScriptPath);
|
||||
|
||||
// if we found multiple build scripts in the project directory, then we can say
|
||||
// as much to indicate that we may have picked the wrong one;
|
||||
// otherwise, we just report that the one script we found didn't work
|
||||
DiagnosticMessage message =
|
||||
this.autoBuildRule.BuildCommandAutoRule.CandidatePaths.Count() > 1 ?
|
||||
new(
|
||||
this.Options.Language,
|
||||
"multiple-build-scripts",
|
||||
"There are multiple potential build scripts",
|
||||
markdownMessage:
|
||||
"CodeQL found multiple potential build scripts for your project and " +
|
||||
$"attempted to run `{relScriptPath}`, which failed. " +
|
||||
"This may not be the right build script for your project. " +
|
||||
$"Set up a [manual build command]({buildCommandDocsUrl})."
|
||||
) :
|
||||
new(
|
||||
this.Options.Language,
|
||||
"script-failure",
|
||||
"Unable to build project using build script",
|
||||
markdownMessage:
|
||||
"CodeQL attempted to build your project using a script located at " +
|
||||
$"`{relScriptPath}`, which failed. " +
|
||||
$"Set up a [manual build command]({buildCommandDocsUrl})."
|
||||
);
|
||||
|
||||
AddDiagnostic(message);
|
||||
}
|
||||
|
||||
// project files which don't exist get marked as not .NET core projects, but we don't want
|
||||
// to show an error for this if the files don't exist
|
||||
var foundNotDotNetProjects = autoBuildRule.DotNetRule.NotDotNetProjects.Where(
|
||||
proj => this.Actions.FileExists(proj.FullPath)
|
||||
);
|
||||
|
||||
// both dotnet and msbuild builds require project or solution files; if we haven't found any
|
||||
// then neither of those rules would've worked
|
||||
if (this.ProjectsOrSolutionsToBuild.Count == 0)
|
||||
{
|
||||
this.AddDiagnostic(new(
|
||||
this.Options.Language,
|
||||
"no-projects-or-solutions",
|
||||
"No project or solutions files found",
|
||||
markdownMessage:
|
||||
"CodeQL could not find any project or solution files in your repository. " +
|
||||
$"Set up a [manual build command]({buildCommandDocsUrl})."
|
||||
));
|
||||
}
|
||||
// show a warning if there are projects which are not compatible with .NET Core, in case that is unintentional
|
||||
else if (foundNotDotNetProjects.Any())
|
||||
{
|
||||
this.AddDiagnostic(new(
|
||||
this.Options.Language,
|
||||
"dotnet-incompatible-projects",
|
||||
"Some projects are incompatible with .NET Core",
|
||||
severity: DiagnosticMessage.TspSeverity.Warning,
|
||||
markdownMessage: $"""
|
||||
CodeQL found some projects which cannot be built with .NET Core:
|
||||
|
||||
{autoBuildRule.DotNetRule.NotDotNetProjects.Select(p => this.MakeRelative(p.FullPath)).ToMarkdownList(MarkdownUtil.CodeFormatter, 5)}
|
||||
"""
|
||||
));
|
||||
}
|
||||
|
||||
// report any projects that failed to build with .NET Core
|
||||
if (autoBuildRule.DotNetRule.FailedProjectsOrSolutions.Any())
|
||||
{
|
||||
this.AddDiagnostic(new(
|
||||
this.Options.Language,
|
||||
"dotnet-build-failure",
|
||||
"Some projects or solutions failed to build using .NET Core",
|
||||
markdownMessage: $"""
|
||||
CodeQL was unable to build the following projects using .NET Core:
|
||||
|
||||
{autoBuildRule.DotNetRule.FailedProjectsOrSolutions.Select(p => this.MakeRelative(p.FullPath)).ToMarkdownList(MarkdownUtil.CodeFormatter, 10)}
|
||||
|
||||
Set up a [manual build command]({buildCommandDocsUrl}).
|
||||
"""
|
||||
));
|
||||
}
|
||||
|
||||
// report any projects that failed to build with MSBuild
|
||||
if (autoBuildRule.MsBuildRule.FailedProjectsOrSolutions.Any())
|
||||
{
|
||||
this.AddDiagnostic(new(
|
||||
this.Options.Language,
|
||||
"msbuild-build-failure",
|
||||
"Some projects or solutions failed to build using MSBuild",
|
||||
markdownMessage: $"""
|
||||
CodeQL was unable to build the following projects using MSBuild:
|
||||
|
||||
{autoBuildRule.MsBuildRule.FailedProjectsOrSolutions.Select(p => this.MakeRelative(p.FullPath)).ToMarkdownList(MarkdownUtil.CodeFormatter, 10)}
|
||||
|
||||
Set up a [manual build command]({buildCommandDocsUrl}).
|
||||
"""
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the build strategy that the autobuilder should apply, based on the
|
||||
/// options in the `lgtm.yml` file.
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using Semmle.Autobuild.Shared;
|
||||
using Semmle.Util;
|
||||
|
||||
namespace Semmle.Autobuild.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// A diagnostic rule which tries to identify missing Xamarin SDKs.
|
||||
/// </summary>
|
||||
public class MissingXamarinSdkRule : DiagnosticRule
|
||||
{
|
||||
private const string docsUrl = "https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-xamarin-applications";
|
||||
|
||||
public class Result : IDiagnosticsResult
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of the SDK that is missing.
|
||||
/// </summary>
|
||||
public string SDKName { get; }
|
||||
|
||||
public Result(string sdkName)
|
||||
{
|
||||
this.SDKName = sdkName;
|
||||
}
|
||||
|
||||
public DiagnosticMessage ToDiagnosticMessage<T>(Autobuilder<T> builder, DiagnosticMessage.TspSeverity? severity = null) where T : AutobuildOptionsShared => new(
|
||||
builder.Options.Language,
|
||||
$"missing-xamarin-{this.SDKName.ToLower()}-sdk",
|
||||
$"Missing Xamarin SDK for {this.SDKName}",
|
||||
severity: severity ?? DiagnosticMessage.TspSeverity.Error,
|
||||
markdownMessage: $"[Configure your workflow]({docsUrl}) for this SDK before running CodeQL."
|
||||
);
|
||||
}
|
||||
|
||||
public MissingXamarinSdkRule() :
|
||||
base("MSB4019:[^\"]*\"[^\"]*Xamarin\\.(?<sdkName>[^\\.]*)\\.CSharp\\.targets\"")
|
||||
{
|
||||
}
|
||||
|
||||
public override void Fire(DiagnosticClassifier classifier, Match match)
|
||||
{
|
||||
if (!match.Groups.TryGetValue("sdkName", out var sdkName))
|
||||
throw new ArgumentException("Expected regular expression match to contain sdkName");
|
||||
|
||||
var xamarinResults = classifier.Results.OfType<Result>().Where(result =>
|
||||
result.SDKName.Equals(sdkName.Value)
|
||||
);
|
||||
|
||||
if (!xamarinResults.Any())
|
||||
classifier.Results.Add(new Result(sdkName.Value));
|
||||
}
|
||||
}
|
||||
|
||||
public class MissingProjectFileRule : DiagnosticRule
|
||||
{
|
||||
private const string runsOnDocsUrl = "https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idruns-on";
|
||||
private const string checkoutDocsUrl = "https://github.com/actions/checkout#usage";
|
||||
|
||||
public class Result : IDiagnosticsResult
|
||||
{
|
||||
/// <summary>
|
||||
/// A set of missing project files.
|
||||
/// </summary>
|
||||
public HashSet<string> MissingProjectFiles { get; }
|
||||
|
||||
public Result()
|
||||
{
|
||||
this.MissingProjectFiles = new HashSet<string>();
|
||||
}
|
||||
|
||||
public DiagnosticMessage ToDiagnosticMessage<T>(Autobuilder<T> builder, DiagnosticMessage.TspSeverity? severity = null) where T : AutobuildOptionsShared => new(
|
||||
builder.Options.Language,
|
||||
"missing-project-files",
|
||||
"Missing project files",
|
||||
severity: severity ?? DiagnosticMessage.TspSeverity.Warning,
|
||||
markdownMessage: $"""
|
||||
Some project files were not found when CodeQL built your project:
|
||||
|
||||
{this.MissingProjectFiles.AsEnumerable().Select(p => builder.MakeRelative(p)).ToMarkdownList(MarkdownUtil.CodeFormatter, 5)}
|
||||
|
||||
This may lead to subsequent failures. You can check for common causes for missing project files:
|
||||
|
||||
- Ensure that the project is built using the {runsOnDocsUrl.ToMarkdownLink("intended operating system")} and that filenames on case-sensitive platforms are correctly specified.
|
||||
- If your repository uses Git submodules, ensure that those are {checkoutDocsUrl.ToMarkdownLink("checked out")} before the CodeQL action is run.
|
||||
- If you auto-generate some project files as part of your build process, ensure that these are generated before the CodeQL action is run.
|
||||
"""
|
||||
);
|
||||
}
|
||||
|
||||
public MissingProjectFileRule() :
|
||||
base("MSB3202: The project file \"(?<projectFile>[^\"]+)\" was not found. \\[(?<location>[^\\]]+)\\]")
|
||||
{
|
||||
}
|
||||
|
||||
public override void Fire(DiagnosticClassifier classifier, Match match)
|
||||
{
|
||||
if (!match.Groups.TryGetValue("projectFile", out var projectFile))
|
||||
throw new ArgumentException("Expected regular expression match to contain projectFile");
|
||||
if (!match.Groups.TryGetValue("location", out var location))
|
||||
throw new ArgumentException("Expected regular expression match to contain location");
|
||||
|
||||
var result = classifier.Results.OfType<Result>().FirstOrDefault();
|
||||
|
||||
// if we do not yet have a result for this rule, create one and add it to the list
|
||||
// of results the classifier knows about
|
||||
if (result is null)
|
||||
{
|
||||
result = new Result();
|
||||
classifier.Results.Add(result);
|
||||
}
|
||||
|
||||
// then add the missing project file
|
||||
result.MissingProjectFiles.Add(projectFile.Value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implements a <see cref="DiagnosticClassifier" /> which applies C#-specific rules to
|
||||
/// the build output.
|
||||
/// </summary>
|
||||
public class CSharpDiagnosticClassifier : DiagnosticClassifier
|
||||
{
|
||||
public CSharpDiagnosticClassifier()
|
||||
{
|
||||
// add C#-specific rules to this classifier
|
||||
this.AddRule(new MissingXamarinSdkRule());
|
||||
this.AddRule(new MissingProjectFileRule());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,15 @@ namespace Semmle.Autobuild.CSharp
|
||||
/// </summary>
|
||||
internal class DotNetRule : IBuildRule<CSharpAutobuildOptions>
|
||||
{
|
||||
public readonly List<IProjectOrSolution> FailedProjectsOrSolutions = new();
|
||||
|
||||
/// <summary>
|
||||
/// A list of projects which are incompatible with DotNet.
|
||||
/// </summary>
|
||||
public IEnumerable<Project<CSharpAutobuildOptions>> NotDotNetProjects { get; private set; }
|
||||
|
||||
public DotNetRule() => NotDotNetProjects = new List<Project<CSharpAutobuildOptions>>();
|
||||
|
||||
public BuildScript Analyse(IAutobuilder<CSharpAutobuildOptions> builder, bool auto)
|
||||
{
|
||||
if (!builder.ProjectsOrSolutionsToBuild.Any())
|
||||
@@ -22,10 +31,12 @@ namespace Semmle.Autobuild.CSharp
|
||||
|
||||
if (auto)
|
||||
{
|
||||
var notDotNetProject = builder.ProjectsOrSolutionsToBuild
|
||||
NotDotNetProjects = builder.ProjectsOrSolutionsToBuild
|
||||
.SelectMany(p => Enumerators.Singleton(p).Concat(p.IncludedProjects))
|
||||
.OfType<Project<CSharpAutobuildOptions>>()
|
||||
.FirstOrDefault(p => !p.DotNetProject);
|
||||
.Where(p => !p.DotNetProject);
|
||||
var notDotNetProject = NotDotNetProjects.FirstOrDefault();
|
||||
|
||||
if (notDotNetProject is not null)
|
||||
{
|
||||
builder.Log(Severity.Info, "Not using .NET Core because of incompatible project {0}", notDotNetProject);
|
||||
@@ -50,7 +61,10 @@ namespace Semmle.Autobuild.CSharp
|
||||
|
||||
var build = GetBuildScript(builder, dotNetPath, environment, projectOrSolution.FullPath);
|
||||
|
||||
ret &= BuildScript.Try(clean) & BuildScript.Try(restore) & build;
|
||||
ret &= BuildScript.Try(clean) & BuildScript.Try(restore) & BuildScript.OnFailure(build, ret =>
|
||||
{
|
||||
FailedProjectsOrSolutions.Add(projectOrSolution);
|
||||
});
|
||||
}
|
||||
return ret;
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<AssemblyName>Semmle.Autobuild.CSharp</AssemblyName>
|
||||
<RootNamespace>Semmle.Autobuild.CSharp</RootNamespace>
|
||||
<ApplicationIcon/>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using Semmle.Util;
|
||||
|
||||
namespace Semmle.Autobuild.Shared
|
||||
{
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using Semmle.Util;
|
||||
using Semmle.Util.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -189,10 +190,11 @@ namespace Semmle.Autobuild.Shared
|
||||
/// solution file and tools.
|
||||
/// </summary>
|
||||
/// <param name="options">The command line options.</param>
|
||||
protected Autobuilder(IBuildActions actions, TAutobuildOptions options)
|
||||
protected Autobuilder(IBuildActions actions, TAutobuildOptions options, DiagnosticClassifier diagnosticClassifier)
|
||||
{
|
||||
Actions = actions;
|
||||
Options = options;
|
||||
DiagnosticClassifier = diagnosticClassifier;
|
||||
|
||||
pathsLazy = new Lazy<IEnumerable<(string, int)>>(() =>
|
||||
{
|
||||
@@ -232,24 +234,53 @@ namespace Semmle.Autobuild.Shared
|
||||
return ret ?? new List<IProjectOrSolution>();
|
||||
});
|
||||
|
||||
CodeQLExtractorLangRoot = Actions.GetEnvironmentVariable($"CODEQL_EXTRACTOR_{this.Options.Language.UpperCaseName}_ROOT");
|
||||
CodeQlPlatform = Actions.GetEnvironmentVariable("CODEQL_PLATFORM");
|
||||
CodeQLExtractorLangRoot = Actions.GetEnvironmentVariable(EnvVars.Root(this.Options.Language));
|
||||
CodeQlPlatform = Actions.GetEnvironmentVariable(EnvVars.Platform);
|
||||
|
||||
TrapDir =
|
||||
Actions.GetEnvironmentVariable($"CODEQL_EXTRACTOR_{this.Options.Language.UpperCaseName}_TRAP_DIR") ??
|
||||
throw new InvalidEnvironmentException($"The environment variable CODEQL_EXTRACTOR_{this.Options.Language.UpperCaseName}_TRAP_DIR has not been set.");
|
||||
TrapDir = RequireEnvironmentVariable(EnvVars.TrapDir(this.Options.Language));
|
||||
SourceArchiveDir = RequireEnvironmentVariable(EnvVars.SourceArchiveDir(this.Options.Language));
|
||||
DiagnosticsDir = RequireEnvironmentVariable(EnvVars.DiagnosticDir(this.Options.Language));
|
||||
|
||||
SourceArchiveDir =
|
||||
Actions.GetEnvironmentVariable($"CODEQL_EXTRACTOR_{this.Options.Language.UpperCaseName}_SOURCE_ARCHIVE_DIR") ??
|
||||
throw new InvalidEnvironmentException($"The environment variable CODEQL_EXTRACTOR_{this.Options.Language.UpperCaseName}_SOURCE_ARCHIVE_DIR has not been set.");
|
||||
this.diagnostics = new DiagnosticsStream(Path.Combine(DiagnosticsDir, $"autobuilder-{DateTime.UtcNow:yyyyMMddHHmm}.jsonc"));
|
||||
}
|
||||
|
||||
protected string TrapDir { get; }
|
||||
/// <summary>
|
||||
/// Retrieves the value of an environment variable named <paramref name="name"> or throws
|
||||
/// an exception if no such environment variable has been set.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the environment variable.</param>
|
||||
/// <returns>The value of the environment variable.</returns>
|
||||
/// <exception cref="InvalidEnvironmentException">
|
||||
/// Thrown if the environment variable is not set.
|
||||
/// </exception>
|
||||
protected string RequireEnvironmentVariable(string name)
|
||||
{
|
||||
return Actions.GetEnvironmentVariable(name) ??
|
||||
throw new InvalidEnvironmentException($"The environment variable {name} has not been set.");
|
||||
}
|
||||
|
||||
protected string SourceArchiveDir { get; }
|
||||
public string TrapDir { get; }
|
||||
|
||||
public string SourceArchiveDir { get; }
|
||||
|
||||
public string DiagnosticsDir { get; }
|
||||
|
||||
protected DiagnosticClassifier DiagnosticClassifier { get; }
|
||||
|
||||
private readonly ILogger logger = new ConsoleLogger(Verbosity.Info);
|
||||
|
||||
private readonly DiagnosticsStream diagnostics;
|
||||
|
||||
/// <summary>
|
||||
/// Makes <see cref="path" /> relative to the root source directory.
|
||||
/// </summary>
|
||||
/// <param name="path">The path which to make relative.</param>
|
||||
/// <returns>The relative path.</returns>
|
||||
public string MakeRelative(string path)
|
||||
{
|
||||
return Path.GetRelativePath(this.RootDirectory, path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log a given build event to the console.
|
||||
/// </summary>
|
||||
@@ -260,6 +291,15 @@ namespace Semmle.Autobuild.Shared
|
||||
logger.Log(severity, format, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write <paramref name="diagnostic"/> to the diagnostics file.
|
||||
/// </summary>
|
||||
/// <param name="diagnostic">The diagnostics entry to write.</param>
|
||||
public void AddDiagnostic(DiagnosticMessage diagnostic)
|
||||
{
|
||||
diagnostics.AddEntry(diagnostic);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempt to build this project.
|
||||
/// </summary>
|
||||
@@ -283,7 +323,19 @@ namespace Semmle.Autobuild.Shared
|
||||
Log(silent ? Severity.Debug : Severity.Info, $"Exit code {ret}{(string.IsNullOrEmpty(msg) ? "" : $": {msg}")}");
|
||||
}
|
||||
|
||||
return script.Run(Actions, startCallback, exitCallback);
|
||||
var onOutput = BuildOutputHandler(Console.Out);
|
||||
var onError = BuildOutputHandler(Console.Error);
|
||||
|
||||
var buildResult = script.Run(Actions, startCallback, exitCallback, onOutput, onError);
|
||||
|
||||
// if the build succeeded, all diagnostics we captured from the build output should be warnings;
|
||||
// otherwise they should all be errors
|
||||
var diagSeverity = buildResult == 0 ? DiagnosticMessage.TspSeverity.Warning : DiagnosticMessage.TspSeverity.Error;
|
||||
this.DiagnosticClassifier.Results
|
||||
.Select(result => result.ToDiagnosticMessage(this, diagSeverity))
|
||||
.ForEach(AddDiagnostic);
|
||||
|
||||
return buildResult;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -291,13 +343,58 @@ namespace Semmle.Autobuild.Shared
|
||||
/// </summary>
|
||||
public abstract BuildScript GetBuildScript();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Produces a diagnostic for the tool status page that we were unable to automatically
|
||||
/// build the user's project and that they can manually specify a build command. This
|
||||
/// can be overriden to implement more specific messages depending on the origin of
|
||||
/// the failure.
|
||||
/// </summary>
|
||||
protected virtual void AutobuildFailureDiagnostic() => AddDiagnostic(new DiagnosticMessage(
|
||||
this.Options.Language,
|
||||
"autobuild-failure",
|
||||
"Unable to build project",
|
||||
visibility: new DiagnosticMessage.TspVisibility(statusPage: true),
|
||||
plaintextMessage: """
|
||||
We were unable to automatically build your project.
|
||||
Set up a manual build command.
|
||||
"""
|
||||
));
|
||||
|
||||
/// <summary>
|
||||
/// Returns a build script that can be run upon autobuild failure.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A build script that reports that we could not automatically detect a suitable build method.
|
||||
/// </returns>
|
||||
protected BuildScript AutobuildFailure() =>
|
||||
BuildScript.Create(actions =>
|
||||
{
|
||||
Log(Severity.Error, "Could not auto-detect a suitable build method");
|
||||
|
||||
AutobuildFailureDiagnostic();
|
||||
|
||||
return 1;
|
||||
});
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a <see cref="BuildOutputHandler" /> which uses the <see cref="DiagnosticClassifier" />
|
||||
/// to classify build output. All data also gets written to <paramref name="writer" />.
|
||||
/// </summary>
|
||||
/// <param name="writer">
|
||||
/// The <see cref="TextWriter" /> to which the build output would have normally been written to.
|
||||
/// This is normally <see cref="Console.Out" /> or <see cref="Console.Error" />.
|
||||
/// </param>
|
||||
/// <returns>The constructed <see cref="BuildOutputHandler" />.</returns>
|
||||
protected BuildOutputHandler BuildOutputHandler(TextWriter writer) => new(data =>
|
||||
{
|
||||
if (data is not null)
|
||||
{
|
||||
writer.WriteLine(data);
|
||||
DiagnosticClassifier.ClassifyLine(data);
|
||||
}
|
||||
});
|
||||
|
||||
/// <summary>
|
||||
/// Value of CODEQL_EXTRACTOR_<LANG>_ROOT environment variable.
|
||||
/// </summary>
|
||||
|
||||
@@ -7,14 +7,30 @@ using System.Xml;
|
||||
using System.Net.Http;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Threading.Tasks;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Semmle.Autobuild.Shared
|
||||
{
|
||||
public delegate void BuildOutputHandler(string? data);
|
||||
|
||||
/// <summary>
|
||||
/// Wrapper around system calls so that the build scripts can be unit-tested.
|
||||
/// </summary>
|
||||
public interface IBuildActions
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Runs a process, captures its output, and provides it asynchronously.
|
||||
/// </summary>
|
||||
/// <param name="exe">The exe to run.</param>
|
||||
/// <param name="args">The other command line arguments.</param>
|
||||
/// <param name="workingDirectory">The working directory (<code>null</code> for current directory).</param>
|
||||
/// <param name="env">Additional environment variables.</param>
|
||||
/// <param name="onOutput">A handler for stdout output.</param>
|
||||
/// <param name="onError">A handler for stderr output.</param>
|
||||
/// <returns>The process exit code.</returns>
|
||||
int RunProcess(string exe, string args, string? workingDirectory, IDictionary<string, string>? env, BuildOutputHandler onOutput, BuildOutputHandler onError);
|
||||
|
||||
/// <summary>
|
||||
/// Runs a process and captures its output.
|
||||
/// </summary>
|
||||
@@ -98,6 +114,18 @@ namespace Semmle.Autobuild.Shared
|
||||
/// </summary>
|
||||
bool IsWindows();
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether we are running on macOS.
|
||||
/// </summary>
|
||||
/// <returns>True if we are running on macOS.</returns>
|
||||
bool IsMacOs();
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether we are running on arm.
|
||||
/// </summary>
|
||||
/// <returns>True if we are running on arm.</returns>
|
||||
bool IsArm();
|
||||
|
||||
/// <summary>
|
||||
/// Combine path segments, Path.Combine().
|
||||
/// </summary>
|
||||
@@ -169,6 +197,26 @@ namespace Semmle.Autobuild.Shared
|
||||
return pi;
|
||||
}
|
||||
|
||||
int IBuildActions.RunProcess(string exe, string args, string? workingDirectory, System.Collections.Generic.IDictionary<string, string>? env, BuildOutputHandler onOutput, BuildOutputHandler onError)
|
||||
{
|
||||
var pi = GetProcessStartInfo(exe, args, workingDirectory, env, true);
|
||||
using var p = new Process
|
||||
{
|
||||
StartInfo = pi
|
||||
};
|
||||
p.StartInfo.RedirectStandardError = true;
|
||||
p.OutputDataReceived += new DataReceivedEventHandler((sender, e) => onOutput(e.Data));
|
||||
p.ErrorDataReceived += new DataReceivedEventHandler((sender, e) => onError(e.Data));
|
||||
|
||||
p.Start();
|
||||
|
||||
p.BeginErrorReadLine();
|
||||
p.BeginOutputReadLine();
|
||||
|
||||
p.WaitForExit();
|
||||
return p.ExitCode;
|
||||
}
|
||||
|
||||
int IBuildActions.RunProcess(string cmd, string args, string? workingDirectory, IDictionary<string, string>? environment)
|
||||
{
|
||||
var pi = GetProcessStartInfo(cmd, args, workingDirectory, environment, false);
|
||||
@@ -203,6 +251,12 @@ namespace Semmle.Autobuild.Shared
|
||||
|
||||
bool IBuildActions.IsWindows() => Win32.IsWindows();
|
||||
|
||||
bool IBuildActions.IsMacOs() => RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
|
||||
|
||||
bool IBuildActions.IsArm() =>
|
||||
RuntimeInformation.ProcessArchitecture == Architecture.Arm64 ||
|
||||
RuntimeInformation.ProcessArchitecture == Architecture.Arm;
|
||||
|
||||
string IBuildActions.PathCombine(params string[] parts) => Path.Combine(parts);
|
||||
|
||||
void IBuildActions.WriteAllText(string filename, string contents) => File.WriteAllText(filename, contents);
|
||||
|
||||
@@ -11,22 +11,42 @@ namespace Semmle.Autobuild.Shared
|
||||
{
|
||||
private readonly WithDotNet<AutobuildOptionsShared> withDotNet;
|
||||
|
||||
/// <summary>
|
||||
/// A list of paths to files in the project directory which we classified as scripts.
|
||||
/// </summary>
|
||||
public IEnumerable<string> CandidatePaths { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The path of the script we decided to run, if any.
|
||||
/// </summary>
|
||||
public string? ScriptPath { get; private set; }
|
||||
|
||||
public BuildCommandAutoRule(WithDotNet<AutobuildOptionsShared> withDotNet)
|
||||
{
|
||||
this.withDotNet = withDotNet;
|
||||
this.CandidatePaths = new List<string>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A list of extensions that we consider to be for scripts on Windows.
|
||||
/// </summary>
|
||||
private readonly IEnumerable<string> winExtensions = new List<string> {
|
||||
".bat",
|
||||
".cmd",
|
||||
".exe"
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// A list of extensions that we consider to be for scripts on Linux.
|
||||
/// </summary>
|
||||
private readonly IEnumerable<string> linuxExtensions = new List<string> {
|
||||
"",
|
||||
".sh"
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// A list of filenames without extensions that we think might be build scripts.
|
||||
/// </summary>
|
||||
private readonly IEnumerable<string> buildScripts = new List<string> {
|
||||
"build"
|
||||
};
|
||||
@@ -35,18 +55,25 @@ namespace Semmle.Autobuild.Shared
|
||||
{
|
||||
builder.Log(Severity.Info, "Attempting to locate build script");
|
||||
|
||||
// a list of extensions for files that we consider to be scripts on the current platform
|
||||
var extensions = builder.Actions.IsWindows() ? winExtensions : linuxExtensions;
|
||||
// a list of combined base script names with the current platform's script extensions
|
||||
// e.g. for Linux: build, build.sh
|
||||
var scripts = buildScripts.SelectMany(s => extensions.Select(e => s + e));
|
||||
var scriptPath = builder.Paths.Where(p => scripts.Any(p.Item1.ToLower().EndsWith)).OrderBy(p => p.Item2).Select(p => p.Item1).FirstOrDefault();
|
||||
// search through the files in the project directory for paths which end in one of
|
||||
// the names given by `scripts`, then order them by their distance from the root
|
||||
this.CandidatePaths = builder.Paths.Where(p => scripts.Any(p.Item1.ToLower().EndsWith)).OrderBy(p => p.Item2).Select(p => p.Item1);
|
||||
// pick the first matching path, if there is one
|
||||
this.ScriptPath = this.CandidatePaths.FirstOrDefault();
|
||||
|
||||
if (scriptPath is null)
|
||||
if (this.ScriptPath is null)
|
||||
return BuildScript.Failure;
|
||||
|
||||
var chmod = new CommandBuilder(builder.Actions);
|
||||
chmod.RunCommand("/bin/chmod", $"u+x {scriptPath}");
|
||||
chmod.RunCommand("/bin/chmod", $"u+x {this.ScriptPath}");
|
||||
var chmodScript = builder.Actions.IsWindows() ? BuildScript.Success : BuildScript.Try(chmod.Script);
|
||||
|
||||
var dir = builder.Actions.GetDirectoryName(scriptPath);
|
||||
var dir = builder.Actions.GetDirectoryName(this.ScriptPath);
|
||||
|
||||
// A specific .NET Core version may be required
|
||||
return chmodScript & withDotNet(builder, environment =>
|
||||
@@ -58,7 +85,7 @@ namespace Semmle.Autobuild.Shared
|
||||
if (vsTools is not null)
|
||||
command.CallBatFile(vsTools.Path);
|
||||
|
||||
command.RunCommand(scriptPath);
|
||||
command.RunCommand(this.ScriptPath);
|
||||
return command.Script;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -46,6 +46,33 @@ namespace Semmle.Autobuild.Shared
|
||||
/// <returns>The exit code from this build script.</returns>
|
||||
public abstract int Run(IBuildActions actions, Action<string, bool> startCallback, Action<int, string, bool> exitCallBack, out IList<string> stdout);
|
||||
|
||||
/// <summary>
|
||||
/// Runs this build command.
|
||||
/// </summary>
|
||||
/// <param name="actions">
|
||||
/// The interface used to implement the build actions.
|
||||
/// </param>
|
||||
/// <param name="startCallback">
|
||||
/// A call back that is called every time a new process is started. The
|
||||
/// argument to the call back is a textual representation of the process.
|
||||
/// </param>
|
||||
/// <param name="exitCallBack">
|
||||
/// A call back that is called every time a new process exits. The first
|
||||
/// argument to the call back is the exit code, and the second argument is
|
||||
/// an exit message.
|
||||
/// </param>
|
||||
/// <param name="onOutput">
|
||||
/// A handler for data read from stdout.
|
||||
/// </param>
|
||||
/// <param name="onError">
|
||||
/// A handler for data read from stderr.
|
||||
/// </param>
|
||||
/// <returns>The exit code from this build script.</returns>
|
||||
public abstract int Run(IBuildActions actions, Action<string, bool> startCallback, Action<int, string, bool> exitCallBack, BuildOutputHandler onOutput, BuildOutputHandler onError);
|
||||
|
||||
/// <summary>
|
||||
/// A build script which executes an external program or script.
|
||||
/// </summary>
|
||||
private class BuildCommand : BuildScript
|
||||
{
|
||||
private readonly string exe, arguments;
|
||||
@@ -110,8 +137,29 @@ namespace Semmle.Autobuild.Shared
|
||||
return ret;
|
||||
}
|
||||
|
||||
public override int Run(IBuildActions actions, Action<string, bool> startCallback, Action<int, string, bool> exitCallBack, BuildOutputHandler onOutput, BuildOutputHandler onError)
|
||||
{
|
||||
startCallback(this.ToString(), silent);
|
||||
var ret = 1;
|
||||
var retMessage = "";
|
||||
try
|
||||
{
|
||||
ret = actions.RunProcess(exe, arguments, workingDirectory, environment, onOutput, onError);
|
||||
}
|
||||
catch (Exception ex)
|
||||
when (ex is System.ComponentModel.Win32Exception || ex is FileNotFoundException)
|
||||
{
|
||||
retMessage = ex.Message;
|
||||
}
|
||||
exitCallBack(ret, retMessage, silent);
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A build script which runs a C# function.
|
||||
/// </summary>
|
||||
private class ReturnBuildCommand : BuildScript
|
||||
{
|
||||
private readonly Func<IBuildActions, int> func;
|
||||
@@ -127,8 +175,13 @@ namespace Semmle.Autobuild.Shared
|
||||
stdout = Array.Empty<string>();
|
||||
return func(actions);
|
||||
}
|
||||
|
||||
public override int Run(IBuildActions actions, Action<string, bool> startCallback, Action<int, string, bool> exitCallBack, BuildOutputHandler onOutput, BuildOutputHandler onError) => func(actions);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allows two build scripts to be composed sequentially.
|
||||
/// </summary>
|
||||
private class BindBuildScript : BuildScript
|
||||
{
|
||||
private readonly BuildScript s1;
|
||||
@@ -175,6 +228,32 @@ namespace Semmle.Autobuild.Shared
|
||||
stdout = @out;
|
||||
return ret2;
|
||||
}
|
||||
|
||||
public override int Run(IBuildActions actions, Action<string, bool> startCallback, Action<int, string, bool> exitCallBack, BuildOutputHandler onOutput, BuildOutputHandler onError)
|
||||
{
|
||||
int ret1;
|
||||
if (s2a is not null)
|
||||
{
|
||||
var stdout1 = new List<string>();
|
||||
var onOutputWrapper = new BuildOutputHandler(data =>
|
||||
{
|
||||
if (data is not null)
|
||||
stdout1.Add(data);
|
||||
|
||||
onOutput(data);
|
||||
});
|
||||
ret1 = s1.Run(actions, startCallback, exitCallBack, onOutputWrapper, onError);
|
||||
return s2a(stdout1, ret1).Run(actions, startCallback, exitCallBack, onOutput, onError);
|
||||
}
|
||||
|
||||
if (s2b is not null)
|
||||
{
|
||||
ret1 = s1.Run(actions, startCallback, exitCallBack, onOutput, onError);
|
||||
return s2b(ret1).Run(actions, startCallback, exitCallBack, onOutput, onError);
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("Unexpected error");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -260,6 +339,23 @@ namespace Semmle.Autobuild.Shared
|
||||
/// </summary>
|
||||
public static BuildScript Try(BuildScript s) => s | Success;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a build script that runs the build script <paramref name="s" />. If
|
||||
/// running <paramref name="s" /> fails, <paramref name="k" /> is invoked with
|
||||
/// the exit code.
|
||||
/// </summary>
|
||||
/// <param name="s">The build script to run.</param>
|
||||
/// <param name="k">
|
||||
/// The callback that is invoked if <paramref name="s" /> failed.
|
||||
/// </param>
|
||||
/// <returns>The build script which implements this.</returns>
|
||||
public static BuildScript OnFailure(BuildScript s, Action<int> k) =>
|
||||
new BindBuildScript(s, ret => Create(actions =>
|
||||
{
|
||||
if (!Succeeded(ret)) k(ret);
|
||||
return ret;
|
||||
}));
|
||||
|
||||
/// <summary>
|
||||
/// Creates a build script that deletes the given directory.
|
||||
/// </summary>
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
using Semmle.Util;
|
||||
|
||||
namespace Semmle.Autobuild.Shared
|
||||
{
|
||||
/// <summary>
|
||||
/// Direct results result from the successful application of a <see cref="DiagnosticRule" />,
|
||||
/// which can later be converted to a corresponding <see cref="DiagnosticMessage" />.
|
||||
/// </summary>
|
||||
public interface IDiagnosticsResult
|
||||
{
|
||||
/// <summary>
|
||||
/// Produces a <see cref="DiagnosticMessage" /> corresponding to this result.
|
||||
/// </summary>
|
||||
/// <param name="builder">
|
||||
/// The autobuilder to use for constructing the base <see cref="DiagnosticMessage" />.
|
||||
/// </param>
|
||||
/// <param name="severity">
|
||||
/// An optional severity value which overrides the default severity of the diagnostic.
|
||||
/// </param>
|
||||
/// <returns>The <see cref="DiagnosticMessage" /> corresponding to this result.</returns>
|
||||
DiagnosticMessage ToDiagnosticMessage<T>(Autobuilder<T> builder, DiagnosticMessage.TspSeverity? severity = null) where T : AutobuildOptionsShared;
|
||||
}
|
||||
|
||||
public class DiagnosticRule
|
||||
{
|
||||
/// <summary>
|
||||
/// The pattern against which this rule matches build output.
|
||||
/// </summary>
|
||||
public Regex Pattern { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a diagnostic rule for the given <paramref name="pattern" />.
|
||||
/// </summary>
|
||||
/// <param name="pattern"></param>
|
||||
public DiagnosticRule(Regex pattern)
|
||||
{
|
||||
this.Pattern = pattern;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a diagnostic rule for the given regular expression <paramref name="pattern" />.
|
||||
/// </summary>
|
||||
/// <param name="pattern"></param>
|
||||
public DiagnosticRule(string pattern)
|
||||
{
|
||||
this.Pattern = new Regex(pattern, RegexOptions.Compiled);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used by a <see cref="DiagnosticClassifier" /> <paramref name="classifier" /> to
|
||||
/// signal that the rule has matched some build output with <paramref name="match" />.
|
||||
/// </summary>
|
||||
/// <param name="classifier">The classifier which is firing the rule.</param>
|
||||
/// <param name="match">The <see cref="Match" /> that resulted from applying the rule.</param>
|
||||
public virtual void Fire(DiagnosticClassifier classifier, Match match) { }
|
||||
}
|
||||
|
||||
public class DiagnosticClassifier
|
||||
{
|
||||
private readonly List<DiagnosticRule> rules;
|
||||
public readonly List<IDiagnosticsResult> Results;
|
||||
|
||||
public DiagnosticClassifier()
|
||||
{
|
||||
this.rules = new List<DiagnosticRule>();
|
||||
this.Results = new List<IDiagnosticsResult>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds <paramref name="rule" /> to this classifier.
|
||||
/// </summary>
|
||||
/// <param name="rule">The rule to add.</param>
|
||||
protected void AddRule(DiagnosticRule rule)
|
||||
{
|
||||
this.rules.Add(rule);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies all of this classifier's rules to <paramref name="line" /> to see which match.
|
||||
/// </summary>
|
||||
/// <param name="line">The line to which the rules should be applied to.</param>
|
||||
public void ClassifyLine(string line)
|
||||
{
|
||||
this.rules.ForEach(rule =>
|
||||
{
|
||||
var match = rule.Pattern.Match(line);
|
||||
if (match.Success)
|
||||
{
|
||||
rule.Fire(this, match);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
13
csharp/autobuilder/Semmle.Autobuild.Shared/EnvVars.cs
Normal file
13
csharp/autobuilder/Semmle.Autobuild.Shared/EnvVars.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using Semmle.Util;
|
||||
|
||||
namespace Semmle.Autobuild.Shared
|
||||
{
|
||||
public static class EnvVars
|
||||
{
|
||||
public const string Platform = "CODEQL_PLATFORM";
|
||||
public static string Root(Language language) => $"CODEQL_EXTRACTOR_{language.UpperCaseName}_ROOT";
|
||||
public static string TrapDir(Language language) => $"CODEQL_EXTRACTOR_{language.UpperCaseName}_TRAP_DIR";
|
||||
public static string SourceArchiveDir(Language language) => $"CODEQL_EXTRACTOR_{language.UpperCaseName}_SOURCE_ARCHIVE_DIR";
|
||||
public static string DiagnosticDir(Language language) => $"CODEQL_EXTRACTOR_{language.UpperCaseName}_DIAGNOSTIC_DIR";
|
||||
}
|
||||
}
|
||||
65
csharp/autobuilder/Semmle.Autobuild.Shared/MarkdownUtil.cs
Normal file
65
csharp/autobuilder/Semmle.Autobuild.Shared/MarkdownUtil.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Semmle.Autobuild.Shared
|
||||
{
|
||||
public static class MarkdownUtil
|
||||
{
|
||||
/// <summary>
|
||||
/// Formats items as markdown inline code.
|
||||
/// </summary>
|
||||
/// <returns>A function which formats items as markdown inline code.</returns>
|
||||
public static readonly Func<string, string> CodeFormatter = item => $"`{item}`";
|
||||
|
||||
/// <summary>
|
||||
/// Formats the string as a markdown link.
|
||||
/// </summary>
|
||||
/// <param name="link">The URL for the link.</param>
|
||||
/// <param name="title">The text that is displayed.</param>
|
||||
/// <returns>A string containing a markdown-formatted link.</returns>
|
||||
public static string ToMarkdownLink(this string link, string title) => $"[{title}]({link})";
|
||||
|
||||
/// <summary>
|
||||
/// Renders <see cref="projects" /> as a markdown list of the project paths.
|
||||
/// </summary>
|
||||
/// <param name="projects">
|
||||
/// The list of projects whose paths should be rendered as a markdown list.
|
||||
/// </param>
|
||||
/// <param name="limit">The maximum number of items to include in the list.</param>
|
||||
/// <returns>Returns the markdown list as a string.</returns>
|
||||
public static string ToMarkdownList(this IEnumerable<IProjectOrSolution> projects, int? limit = null)
|
||||
{
|
||||
return projects.ToMarkdownList(p => $"`{p.FullPath}`", limit);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders <see cref="items" /> as a markdown list.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The item type.</typeparam>
|
||||
/// <param name="items">The list that should be formatted as a markdown list.</param>
|
||||
/// <param name="formatter">A function which converts individual items into a string representation.</param>
|
||||
/// <param name="limit">The maximum number of items to include in the list.</param>
|
||||
/// <returns>Returns the markdown list as a string.</returns>
|
||||
public static string ToMarkdownList<T>(this IEnumerable<T> items, Func<T, string> formatter, int? limit = null)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
|
||||
// if there is a limit, take at most that many items from the start of the list
|
||||
var list = limit is not null ? items.Take(limit.Value) : items;
|
||||
sb.Append(string.Join('\n', list.Select(item => $"- {formatter(item)}")));
|
||||
|
||||
// if there were more items than allowed in the list, add an extra item indicating
|
||||
// how many more items there were
|
||||
var length = items.Count();
|
||||
|
||||
if (limit is not null && length > limit)
|
||||
{
|
||||
sb.Append($"\n- and {length - limit} more. View the CodeQL logs for a full list.");
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,39 @@
|
||||
using Semmle.Util.Logging;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Semmle.Autobuild.Shared
|
||||
{
|
||||
internal static class MsBuildCommandExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Appends a call to msbuild.
|
||||
/// </summary>
|
||||
/// <param name="cmdBuilder"></param>
|
||||
/// <param name="builder"></param>
|
||||
/// <returns></returns>
|
||||
public static CommandBuilder MsBuildCommand(this CommandBuilder cmdBuilder, IAutobuilder<AutobuildOptionsShared> builder)
|
||||
{
|
||||
var isArmMac = builder.Actions.IsMacOs() && builder.Actions.IsArm();
|
||||
|
||||
// mono doesn't ship with `msbuild` on Arm-based Macs, but we can fall back to
|
||||
// msbuild that ships with `dotnet` which can be invoked with `dotnet msbuild`
|
||||
// perhaps we should do this on all platforms?
|
||||
return isArmMac ?
|
||||
cmdBuilder.RunCommand("dotnet").Argument("msbuild") :
|
||||
cmdBuilder.RunCommand("msbuild");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A build rule using msbuild.
|
||||
/// </summary>
|
||||
public class MsBuildRule : IBuildRule<AutobuildOptionsShared>
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of the msbuild command.
|
||||
/// A list of solutions or projects which failed to build.
|
||||
/// </summary>
|
||||
private const string msBuild = "msbuild";
|
||||
public readonly List<IProjectOrSolution> FailedProjectsOrSolutions = new();
|
||||
|
||||
public BuildScript Analyse(IAutobuilder<AutobuildOptionsShared> builder, bool auto)
|
||||
{
|
||||
@@ -57,7 +79,7 @@ namespace Semmle.Autobuild.Shared
|
||||
Script;
|
||||
var nugetRestore = GetNugetRestoreScript();
|
||||
var msbuildRestoreCommand = new CommandBuilder(builder.Actions).
|
||||
RunCommand(msBuild).
|
||||
MsBuildCommand(builder).
|
||||
Argument("/t:restore").
|
||||
QuoteArgument(projectOrSolution.FullPath);
|
||||
|
||||
@@ -95,7 +117,7 @@ namespace Semmle.Autobuild.Shared
|
||||
command.RunCommand("set Platform=&& type NUL", quoteExe: false);
|
||||
}
|
||||
|
||||
command.RunCommand(msBuild);
|
||||
command.MsBuildCommand(builder);
|
||||
command.QuoteArgument(projectOrSolution.FullPath);
|
||||
|
||||
var target = builder.Options.MsBuildTarget ?? "rebuild";
|
||||
@@ -110,7 +132,13 @@ namespace Semmle.Autobuild.Shared
|
||||
|
||||
command.Argument(builder.Options.MsBuildArguments);
|
||||
|
||||
ret &= command.Script;
|
||||
// append the build script which invokes msbuild to the overall build script `ret`;
|
||||
// we insert a check that building the current project or solution was successful:
|
||||
// if it was not successful, we add it to `FailedProjectsOrSolutions`
|
||||
ret &= BuildScript.OnFailure(command.Script, ret =>
|
||||
{
|
||||
FailedProjectsOrSolutions.Add(projectOrSolution);
|
||||
});
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Semmle.Util;
|
||||
|
||||
namespace Semmle.Autobuild.Shared
|
||||
{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<AssemblyName>Semmle.Autobuild.Shared</AssemblyName>
|
||||
<RootNamespace>Semmle.Autobuild.Shared</RootNamespace>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
class Expression extends @expr {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
class TypeOrRef extends @type_or_ref {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
from Expression e, int k, int kind, TypeOrRef t
|
||||
where
|
||||
expressions(e, k, t) and
|
||||
if k = 135 then kind = 106 else kind = k
|
||||
select e, kind, t
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user