mirror of
https://github.com/github/codeql.git
synced 2026-05-16 04:09:27 +02:00
Compare commits
905 Commits
codeql-cli
...
codeql-cli
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aef66c462a | ||
|
|
12f5732782 | ||
|
|
8edd378290 | ||
|
|
75b0676c95 | ||
|
|
d99f7b56bd | ||
|
|
c33c5ed517 | ||
|
|
fd977cc277 | ||
|
|
465a3e0ece | ||
|
|
9943d0c054 | ||
|
|
9b17ee9326 | ||
|
|
0ab982891e | ||
|
|
2772cf79f4 | ||
|
|
cd660e1067 | ||
|
|
3cf5107b45 | ||
|
|
a4fa984792 | ||
|
|
3cd2024a66 | ||
|
|
62158c5e49 | ||
|
|
4089845ea4 | ||
|
|
7271d9987e | ||
|
|
e137993acd | ||
|
|
d671cc6e43 | ||
|
|
255f989ede | ||
|
|
24527bfc4e | ||
|
|
644bc56568 | ||
|
|
407e7cbbde | ||
|
|
97b2e852c9 | ||
|
|
1df0be3ca2 | ||
|
|
834fc51a3a | ||
|
|
43b234eeb5 | ||
|
|
f7cc5f9627 | ||
|
|
96ee0f68b0 | ||
|
|
74dba953ca | ||
|
|
bc36a75bde | ||
|
|
77e014c5a4 | ||
|
|
01f6862965 | ||
|
|
837cdf7782 | ||
|
|
b9d487ac35 | ||
|
|
16049d694b | ||
|
|
373148decd | ||
|
|
da45d3aa7f | ||
|
|
0d68d88741 | ||
|
|
7f58a2222a | ||
|
|
6b0b73b5f6 | ||
|
|
a2248e6ca6 | ||
|
|
0d38ff8e8c | ||
|
|
8bc9ce749f | ||
|
|
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 | ||
|
|
2f38d363ff | ||
|
|
fb31570af3 | ||
|
|
1758e25207 | ||
|
|
051d36ee6a | ||
|
|
5710289460 | ||
|
|
c99a096c9b | ||
|
|
69ed00cdf1 | ||
|
|
cd596403a0 | ||
|
|
396d2de6e7 | ||
|
|
f23d517236 | ||
|
|
a01a4734ed | ||
|
|
3a1a9a771c | ||
|
|
f90007ae71 | ||
|
|
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 | ||
|
|
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 | ||
|
|
f192191e8c | ||
|
|
508027e0e5 | ||
|
|
13baa5b60b | ||
|
|
7921de243a | ||
|
|
295152cd32 | ||
|
|
bf02340a6a | ||
|
|
6e69acdd7e | ||
|
|
07a7a213b3 | ||
|
|
1fcfae2464 | ||
|
|
e92a5eb467 | ||
|
|
e8714c9edb | ||
|
|
b220c2f51d | ||
|
|
f6fe627f4b | ||
|
|
3dd9392f5e | ||
|
|
39e9eaf2bc | ||
|
|
f4cb920624 | ||
|
|
fe13137b48 | ||
|
|
54b0350cac | ||
|
|
531c0559a0 | ||
|
|
358ae7529b | ||
|
|
068b71bc3d | ||
|
|
e634ab771f | ||
|
|
99bad77972 | ||
|
|
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 | ||
|
|
fc66c905ff | ||
|
|
21e63a8a86 | ||
|
|
241951f53e | ||
|
|
be481d975c | ||
|
|
40a67d61d2 | ||
|
|
25bcaa3a54 | ||
|
|
e32823c3e0 | ||
|
|
19527016a5 | ||
|
|
5ddff790b6 | ||
|
|
f77d156e9a | ||
|
|
444df6fccb | ||
|
|
240248b9cf | ||
|
|
11894144aa | ||
|
|
0a0d6d0841 | ||
|
|
5be97f3761 | ||
|
|
a217017859 | ||
|
|
ae00518ddf | ||
|
|
7c6ee5f293 | ||
|
|
32c4cf5769 | ||
|
|
800077dabe | ||
|
|
a10b45e0db | ||
|
|
05ecd2e015 | ||
|
|
45aaeb897a | ||
|
|
78a2dfa7c4 | ||
|
|
fea97a22c6 | ||
|
|
8a3972049b | ||
|
|
cfc0dabad9 | ||
|
|
470abfd0aa | ||
|
|
fe0290fb39 | ||
|
|
962b651c44 | ||
|
|
3b10a2de11 | ||
|
|
55550e7980 | ||
|
|
1ee9957838 | ||
|
|
99148244a4 | ||
|
|
dc1bfa3a04 | ||
|
|
69a42d8b1f | ||
|
|
440fe80c14 | ||
|
|
21ce9b448a | ||
|
|
a2e7b83411 | ||
|
|
81e59e9005 | ||
|
|
4197d7bd20 | ||
|
|
b6df415fe8 | ||
|
|
e664662df9 | ||
|
|
b62cb6ba84 | ||
|
|
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 | ||
|
|
62125fa767 | ||
|
|
3a5bec5778 | ||
|
|
2875d8645c | ||
|
|
4c47de58c1 | ||
|
|
5f8875ff89 | ||
|
|
b033114f64 | ||
|
|
57ec5db1a9 | ||
|
|
490bd051cd | ||
|
|
e6aebd9df0 | ||
|
|
ee9b01b5e6 | ||
|
|
a3ff0725a3 | ||
|
|
e50a81cbb7 | ||
|
|
c9119848d9 | ||
|
|
14468b64fb | ||
|
|
3fda9f6e65 | ||
|
|
30f0dd8c03 | ||
|
|
015ef4c3ef | ||
|
|
2b1a7898d9 | ||
|
|
5173f10e68 | ||
|
|
35620c4c86 | ||
|
|
e26e83b8df | ||
|
|
5d6f2436e4 | ||
|
|
90517e254a | ||
|
|
48439bc252 | ||
|
|
d75a5212b2 | ||
|
|
4a89a30abd | ||
|
|
c8bcfb77b2 | ||
|
|
71c1ca53a9 | ||
|
|
e4d4873d0d | ||
|
|
4c94adb5ec | ||
|
|
3846349ba0 | ||
|
|
821d294be8 | ||
|
|
5ff89a2ccf | ||
|
|
8b231c51f9 | ||
|
|
b680795d15 | ||
|
|
62e7c22783 | ||
|
|
211af1943c | ||
|
|
3a4623b437 | ||
|
|
e0ccb9306a | ||
|
|
8910ba4f32 | ||
|
|
1477974bf1 | ||
|
|
1a3c9c8305 | ||
|
|
b8f6feb68b | ||
|
|
45316b6381 | ||
|
|
25e65e0d9f | ||
|
|
d0b627b018 | ||
|
|
f383fd1dc1 | ||
|
|
d7feb001be | ||
|
|
a3b4c32f07 | ||
|
|
f7a046ccd2 | ||
|
|
5fdb09380c | ||
|
|
7f389b9f9a | ||
|
|
353536b826 | ||
|
|
84b285a4c6 | ||
|
|
33c4a8233c | ||
|
|
c7f13f1036 | ||
|
|
c31c515205 | ||
|
|
20eaa34485 | ||
|
|
c8e894b854 | ||
|
|
f516ccb4e2 | ||
|
|
2fceee4e35 | ||
|
|
acf28ebd98 | ||
|
|
6e33dd5df6 | ||
|
|
1a64393c4c | ||
|
|
2c4c2dfeb3 | ||
|
|
8e3e6505ad | ||
|
|
571942fb21 | ||
|
|
6cd52237c3 | ||
|
|
4b74dec18f | ||
|
|
5a82012d03 | ||
|
|
a568d0af7f | ||
|
|
b3d30bfc4f | ||
|
|
ee19c3d80f | ||
|
|
06767c6760 | ||
|
|
d1cfdb97ee | ||
|
|
c8298356dc | ||
|
|
a92e1c7ea0 | ||
|
|
5e5c4e9a8c | ||
|
|
9911dd53e1 | ||
|
|
ea06ad1933 | ||
|
|
3c55cdd5be | ||
|
|
d42848bb7e | ||
|
|
28a707a956 | ||
|
|
a8ef9cc987 | ||
|
|
4c018759c8 | ||
|
|
62533501fe | ||
|
|
54b3262d9c | ||
|
|
babdee36aa | ||
|
|
d628cc5ab8 | ||
|
|
8251ad5e99 | ||
|
|
8715790fe7 | ||
|
|
037b49b454 | ||
|
|
6b43ff45a4 | ||
|
|
d9bd41b8b1 | ||
|
|
10f0975812 | ||
|
|
a562568522 | ||
|
|
9d9de18bc9 | ||
|
|
8fc3b268e8 | ||
|
|
9e5db7c6ec | ||
|
|
5f57a097ab | ||
|
|
951f6362aa | ||
|
|
a0b1c2ea79 | ||
|
|
2b0a5fd5d1 | ||
|
|
2e4f4c64fe | ||
|
|
0a792f2f61 | ||
|
|
0d32f00020 | ||
|
|
d6e0ef9ff9 | ||
|
|
f6e26211f9 | ||
|
|
74a37475db | ||
|
|
44ebc77ada | ||
|
|
8906e101cb | ||
|
|
5a4fe71529 | ||
|
|
50b9f5bba0 | ||
|
|
fec7ea6964 | ||
|
|
b08fa43fdf | ||
|
|
f07984bab2 | ||
|
|
13aaa22df5 | ||
|
|
77a9cea737 | ||
|
|
67bd8cba32 | ||
|
|
29425982a5 | ||
|
|
6106edd5e2 | ||
|
|
b22da25e05 | ||
|
|
48825442c3 | ||
|
|
cdc99b5240 | ||
|
|
449ebb8a12 | ||
|
|
e3502e2e5f | ||
|
|
63b4e5ef5c | ||
|
|
51bd1ef1e1 | ||
|
|
660e6d7085 | ||
|
|
dcc1c3d487 | ||
|
|
17de5c120a | ||
|
|
bd5619147d | ||
|
|
713599963b | ||
|
|
9e153cfb0d | ||
|
|
587adea809 | ||
|
|
2c1ecb507d | ||
|
|
1de65131fe | ||
|
|
0685732e3f | ||
|
|
1d62751e15 | ||
|
|
0017461e2d | ||
|
|
cca6a13fbb | ||
|
|
8981d4c06b | ||
|
|
2f602a629f | ||
|
|
8ccc384043 | ||
|
|
59a8b21851 | ||
|
|
3552a41552 | ||
|
|
dc50b6bad3 | ||
|
|
8c2931cbb8 | ||
|
|
d072ed969e | ||
|
|
874fe2b8f9 | ||
|
|
fdb3b65bce | ||
|
|
7f880a24df | ||
|
|
738412260f | ||
|
|
cf7189bb28 | ||
|
|
1a416884d4 | ||
|
|
316117f5c9 | ||
|
|
2f09f0e2c1 | ||
|
|
13ae15b867 | ||
|
|
fb6725ddaa | ||
|
|
2dbacbc302 | ||
|
|
601b43ac0a | ||
|
|
c9a0067705 | ||
|
|
c5038ed281 | ||
|
|
6e6f2115c0 | ||
|
|
2c35af51cd | ||
|
|
8a77906296 | ||
|
|
2d46272295 | ||
|
|
b36be009d4 | ||
|
|
71af8ab022 | ||
|
|
2283eacc0b | ||
|
|
59072f9e81 | ||
|
|
dd8bead21a | ||
|
|
cf9998b932 | ||
|
|
5865b51a94 | ||
|
|
dc6f5f60d1 | ||
|
|
28c9b52dce | ||
|
|
46063c7d04 | ||
|
|
c4ec674057 | ||
|
|
4d3e2bb814 | ||
|
|
290167e1a3 | ||
|
|
3a887d1c92 | ||
|
|
792d4a83f9 | ||
|
|
600412db48 | ||
|
|
3b15f2359b | ||
|
|
a43f3cf95f | ||
|
|
f040ff2d8d | ||
|
|
337a747bde | ||
|
|
b2b45237c6 | ||
|
|
31b61b1aa6 | ||
|
|
938176c9da | ||
|
|
7586762b10 | ||
|
|
488368ecde | ||
|
|
23281410e3 | ||
|
|
6db728190e | ||
|
|
02f1957919 | ||
|
|
71b93d125e | ||
|
|
fb1ef07e9f | ||
|
|
c062d5e206 | ||
|
|
c10733f926 | ||
|
|
b8c43d7a71 | ||
|
|
eebada46b1 | ||
|
|
edbe95837f | ||
|
|
c3a1d088ac | ||
|
|
a39b2aaaac | ||
|
|
ffb267937a | ||
|
|
4acd1ababe | ||
|
|
af8cb65b2e | ||
|
|
fd80974210 | ||
|
|
c1c0ff4308 | ||
|
|
1384aa669b | ||
|
|
5e89119b3d | ||
|
|
529be7ef18 | ||
|
|
5c466f3319 | ||
|
|
49a87e152a | ||
|
|
30738103f0 | ||
|
|
148dc6de5a | ||
|
|
f48eda829f | ||
|
|
d92b226041 | ||
|
|
5bb8f8ed5c | ||
|
|
f74c7c28ae | ||
|
|
0f032c5be9 | ||
|
|
2568318460 | ||
|
|
d06a877709 | ||
|
|
9eb7933778 | ||
|
|
99b7bc3b73 | ||
|
|
14c92e6eb3 | ||
|
|
902b0a60d0 | ||
|
|
36980bbf42 | ||
|
|
a9f1c95513 | ||
|
|
8aa2c23ba8 | ||
|
|
09d8a50494 | ||
|
|
aefb43324b | ||
|
|
8f9ad1f5ba | ||
|
|
7f31c9c7e5 | ||
|
|
e0444449c8 | ||
|
|
e29e077a03 | ||
|
|
7ae27bcc34 | ||
|
|
15ead6d0e6 | ||
|
|
70f1015fba | ||
|
|
9ebe59d353 | ||
|
|
3d1b2fdbda | ||
|
|
e12febfd96 | ||
|
|
b565f997a0 | ||
|
|
639c42c9e9 | ||
|
|
f52db7f9a3 | ||
|
|
a88759283a | ||
|
|
7e7b5b4488 | ||
|
|
8449dabefe | ||
|
|
de565f9ccc | ||
|
|
b96edb9c64 | ||
|
|
418d593a97 | ||
|
|
d0eb167d47 | ||
|
|
4e5483744f | ||
|
|
48d0eccbf6 | ||
|
|
3749a1bd4d | ||
|
|
218f553fef | ||
|
|
ebb3485a73 | ||
|
|
5fd687d3df | ||
|
|
0f993a0d26 | ||
|
|
1ae52b6c7e | ||
|
|
4f5f0aad45 | ||
|
|
18a815ca8b | ||
|
|
006eaf3e2a | ||
|
|
994ea704da | ||
|
|
fd593fd4f0 | ||
|
|
33a1469a56 | ||
|
|
ce74c9d959 | ||
|
|
6bb865ad05 | ||
|
|
1d4b2fd0bb | ||
|
|
8219465389 | ||
|
|
0626d693f5 | ||
|
|
4e1f772f23 | ||
|
|
6a0b56bf40 | ||
|
|
2622de9747 | ||
|
|
82f9903bf0 | ||
|
|
0c7ffb0554 | ||
|
|
c3116b3f0f | ||
|
|
2a99af0e6d | ||
|
|
99ee6c95a1 | ||
|
|
ac064ac2a7 | ||
|
|
7e4f7a0c17 | ||
|
|
67cbe38255 | ||
|
|
8112058a0a | ||
|
|
372ecf402f | ||
|
|
80a4197604 | ||
|
|
c01361a1fd | ||
|
|
59a9e255c7 | ||
|
|
6622eda04c | ||
|
|
6a047d6916 | ||
|
|
ea173f9516 | ||
|
|
7422029e49 | ||
|
|
4b47b08ed2 | ||
|
|
c6d9e1ec81 | ||
|
|
c115a9fee4 | ||
|
|
5d54482c71 | ||
|
|
11ca3f49f6 | ||
|
|
787b4743ee | ||
|
|
178fd0e9e1 | ||
|
|
6914e9a17a | ||
|
|
466f24663e | ||
|
|
3fa6a7cbff | ||
|
|
a4f813183e | ||
|
|
74a58f64aa | ||
|
|
ed2dd87bda | ||
|
|
ecf568629b | ||
|
|
181a711f04 | ||
|
|
76e121e359 | ||
|
|
dc88bdccc7 | ||
|
|
ce06df3152 | ||
|
|
38ca68febb | ||
|
|
4c1c12dd70 | ||
|
|
1d7881e03f | ||
|
|
b7364f5428 | ||
|
|
3fa11c21c3 | ||
|
|
0ad585cfe6 | ||
|
|
54c780bdf9 | ||
|
|
ae8c75ac97 | ||
|
|
72a11e737d | ||
|
|
3367da82c4 | ||
|
|
b7eb521fa0 | ||
|
|
c71ea80029 | ||
|
|
20b35e5d02 | ||
|
|
c4119761cc | ||
|
|
89b3363761 | ||
|
|
b51c3aae85 | ||
|
|
6fbda1a9f0 | ||
|
|
f7d8d16ed3 | ||
|
|
62b69bbd3e | ||
|
|
6623e5fbf3 | ||
|
|
ce8836fb65 | ||
|
|
43696f5e27 | ||
|
|
23a847b1cf | ||
|
|
9f8d10de11 | ||
|
|
2fb5621527 | ||
|
|
943763a026 | ||
|
|
28d79eeb77 | ||
|
|
1a4048d238 | ||
|
|
5c388c554c | ||
|
|
e02b67af63 | ||
|
|
50cd40ed20 | ||
|
|
7ef8099a8b | ||
|
|
c9d1cd97fb | ||
|
|
f6c3f77602 | ||
|
|
d87c8c75d6 | ||
|
|
3b6dae41cd | ||
|
|
32471d326e | ||
|
|
7a92970d89 | ||
|
|
2ca0df0369 | ||
|
|
3fcc99e5cb | ||
|
|
f2658a0936 | ||
|
|
79e161e046 | ||
|
|
da90ae0e8f | ||
|
|
8d46642de3 | ||
|
|
2fc6484162 | ||
|
|
1bab950023 | ||
|
|
282ca1094e | ||
|
|
e215c4c94c | ||
|
|
16cd148961 | ||
|
|
1729319ebe | ||
|
|
18a0abdb4c | ||
|
|
293a203756 | ||
|
|
2edbfbf8bc | ||
|
|
7f5344e025 | ||
|
|
8e0a018673 | ||
|
|
49a41c98ee | ||
|
|
160d89fb4e | ||
|
|
33029b0ed8 | ||
|
|
7e0869965c | ||
|
|
c1f19dd145 | ||
|
|
b203a9eb6e | ||
|
|
aad56097ac | ||
|
|
c142495a8b | ||
|
|
5fe62e293a | ||
|
|
9f100ef2c6 | ||
|
|
eb78661c1f | ||
|
|
90f9e3f825 | ||
|
|
909b1d70d9 | ||
|
|
c723df3ca7 | ||
|
|
785c21f462 | ||
|
|
f626d4794a | ||
|
|
972b4629c8 | ||
|
|
64668883a4 | ||
|
|
2ec73c50f9 | ||
|
|
664fdc3b2a | ||
|
|
5157d4df7b | ||
|
|
e9bbb5d7fa | ||
|
|
c01ce955ba | ||
|
|
2e26fb1171 | ||
|
|
efe23c1da7 | ||
|
|
994a46289f | ||
|
|
ef27f9fe96 | ||
|
|
45c732a6f9 | ||
|
|
381301e552 | ||
|
|
9333e80def | ||
|
|
06ea249997 | ||
|
|
6bb09ef289 | ||
|
|
9be9636816 | ||
|
|
5b117084db | ||
|
|
82d9edfabf | ||
|
|
f15291a9de | ||
|
|
d079c7a5ef | ||
|
|
a3c7b2c3a2 | ||
|
|
bb50a99b36 | ||
|
|
b5dd815249 | ||
|
|
c598d9b882 | ||
|
|
8a9a69fa00 | ||
|
|
a743fbcc95 | ||
|
|
04f87a26a9 | ||
|
|
538adb47a3 | ||
|
|
831255e9c0 | ||
|
|
c1bc097355 | ||
|
|
cdb34bb1f9 | ||
|
|
b9b0c8091f | ||
|
|
f3914ffe25 | ||
|
|
f5e5f6dfd1 | ||
|
|
03bd6cb414 | ||
|
|
10308f5875 | ||
|
|
4c5f149afd | ||
|
|
1ef1d63c11 | ||
|
|
9af9b32722 | ||
|
|
bb451f3911 | ||
|
|
cdb8f67601 | ||
|
|
8d9e94a00f | ||
|
|
0a1769657d | ||
|
|
19d2b49562 | ||
|
|
1a27441cfb | ||
|
|
0e6028a7f3 | ||
|
|
d42bb119fe | ||
|
|
ad95225272 | ||
|
|
f98ff65b11 | ||
|
|
d9176541c6 | ||
|
|
44213f0144 | ||
|
|
4e6baf83c8 | ||
|
|
0beca9d96c | ||
|
|
f370cd840c | ||
|
|
b3e82498fa | ||
|
|
f03f687d61 | ||
|
|
bb26c31f84 | ||
|
|
0640bd9d8b | ||
|
|
8afb541718 | ||
|
|
a74062cd51 | ||
|
|
3b31da8a62 | ||
|
|
46baf9d5e5 | ||
|
|
c160e0b2a4 | ||
|
|
823c767aac | ||
|
|
52297c0b23 | ||
|
|
c8f35ea1ea | ||
|
|
2023abdc60 | ||
|
|
a9aa67177b | ||
|
|
10ca2dac19 | ||
|
|
14283f01ac | ||
|
|
c3b3c05cf3 | ||
|
|
745d30252c | ||
|
|
a62a8d9960 | ||
|
|
d859e1e9a3 | ||
|
|
2e46919e10 | ||
|
|
e45750a8ca | ||
|
|
799e0c1bcc | ||
|
|
65fe9abcfe | ||
|
|
9ad572fa29 | ||
|
|
4d228bcddf | ||
|
|
9944252c43 | ||
|
|
698a679c78 | ||
|
|
0fbb6bf608 | ||
|
|
abe501c1af | ||
|
|
5d92792e40 | ||
|
|
feaae16f7c | ||
|
|
0be8648a9d | ||
|
|
28f555c2b2 | ||
|
|
ab7ca1d642 | ||
|
|
f9b8200009 | ||
|
|
69fd5e93bc | ||
|
|
81df89f93e | ||
|
|
28ad9d00fb | ||
|
|
29221ae426 | ||
|
|
21a018e5c5 | ||
|
|
fc646a6d48 | ||
|
|
e05bb7fcee | ||
|
|
17cd182d72 | ||
|
|
9d608a78a3 | ||
|
|
bfe5a0c438 | ||
|
|
3811eae679 | ||
|
|
3815a5a096 | ||
|
|
35de551f6b | ||
|
|
515fa21aad | ||
|
|
df1a4d2ed1 | ||
|
|
68392aa8d8 | ||
|
|
02f70f3536 | ||
|
|
1d345c6101 | ||
|
|
9ef319f659 | ||
|
|
5265cb4b03 | ||
|
|
973f649e76 | ||
|
|
0e15dd9fa9 | ||
|
|
778749184b | ||
|
|
da25c586e6 | ||
|
|
8a763015e6 | ||
|
|
7cc53126f3 | ||
|
|
a023726c03 | ||
|
|
e4e13d38b7 | ||
|
|
e259ef5d1d | ||
|
|
a6571a05ab | ||
|
|
d3812f5906 | ||
|
|
07d99bd643 | ||
|
|
4215a89bc8 | ||
|
|
ac39aeb6b6 | ||
|
|
939279af38 | ||
|
|
673d37cc3d | ||
|
|
a81c54b58c | ||
|
|
e6331dc2e6 | ||
|
|
997219a280 | ||
|
|
6007827dd3 | ||
|
|
36ca97e4f6 | ||
|
|
b3dd50bc36 | ||
|
|
08e9d3391f | ||
|
|
b0d7998342 | ||
|
|
f28eb6bf31 | ||
|
|
0d6c643d77 | ||
|
|
4667068017 | ||
|
|
a9b232bff4 | ||
|
|
d97e185994 | ||
|
|
e8d3802ee5 | ||
|
|
b767dcfd18 | ||
|
|
de5965525f | ||
|
|
c251da799f | ||
|
|
16de30e07e | ||
|
|
1db829e55c | ||
|
|
573de92441 | ||
|
|
a8c55ee4b7 | ||
|
|
db0d24fdd1 | ||
|
|
cfe075ef54 | ||
|
|
8e20aeb314 | ||
|
|
b9ce588076 | ||
|
|
1544f49f91 | ||
|
|
ed534b06d5 | ||
|
|
99ddd484be | ||
|
|
7d0018c897 | ||
|
|
ac0c42c5c6 | ||
|
|
227e099854 | ||
|
|
aa1d49cb95 | ||
|
|
454af0d721 | ||
|
|
a1161c6efe | ||
|
|
725861626c | ||
|
|
30aa9b230c | ||
|
|
d1d4163b79 | ||
|
|
b7c33734b1 | ||
|
|
1e5426fca2 | ||
|
|
640f894b00 | ||
|
|
46ea067449 | ||
|
|
1f7d96a74a | ||
|
|
2034b00772 | ||
|
|
0f038ee93a | ||
|
|
7f505d8715 | ||
|
|
208388e04d | ||
|
|
6837af97bc | ||
|
|
11c03fb8c9 | ||
|
|
3084eda28a | ||
|
|
e42ae09e1c | ||
|
|
5fd5ebc26e | ||
|
|
793de3196b | ||
|
|
14fd89d482 | ||
|
|
45c0c7fe6c | ||
|
|
b35a1d4206 | ||
|
|
4ec401a3f6 | ||
|
|
d95a4a7baf | ||
|
|
ccf520a5cd | ||
|
|
85b3092b16 | ||
|
|
d72d096c86 | ||
|
|
250ac686a2 | ||
|
|
a93e361aca | ||
|
|
7162692656 | ||
|
|
4a41bb4061 | ||
|
|
26ae8f177b | ||
|
|
d35c5e90ee | ||
|
|
bf1b32f210 | ||
|
|
944adfe727 | ||
|
|
219ed64b74 | ||
|
|
24ce1c27bc | ||
|
|
07ea006cee | ||
|
|
dba344451f | ||
|
|
cf3345ee8f | ||
|
|
85a0a42da9 | ||
|
|
f7ebd1312e | ||
|
|
157a7829ca | ||
|
|
ad05cc3cb1 | ||
|
|
43596869e7 | ||
|
|
87fa159384 | ||
|
|
58e9a0436e | ||
|
|
2b61f26a64 | ||
|
|
32c4728f83 | ||
|
|
a2210959b5 | ||
|
|
fd7442868f | ||
|
|
f75b853ae4 | ||
|
|
53f24a5281 | ||
|
|
0817238177 | ||
|
|
378cc1aed2 | ||
|
|
80c92dc3e6 | ||
|
|
3461404bbb | ||
|
|
0f2a48f461 | ||
|
|
2033dd2dcc | ||
|
|
e7c6571f52 | ||
|
|
f1668801d3 | ||
|
|
5f6cb1684b | ||
|
|
0a7cfad048 | ||
|
|
f486c44b00 |
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}"
|
||||
|
||||
32
.github/actions/os-version/action.yml
vendored
Normal file
32
.github/actions/os-version/action.yml
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
name: OS Version
|
||||
description: Get OS version.
|
||||
|
||||
outputs:
|
||||
version:
|
||||
description: "OS version"
|
||||
value: ${{ steps.version.outputs.version }}
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- if: runner.os == 'Linux'
|
||||
shell: bash
|
||||
run: |
|
||||
. /etc/os-release
|
||||
echo "VERSION=${NAME} ${VERSION}" >> $GITHUB_ENV
|
||||
- if: runner.os == 'Windows'
|
||||
shell: powershell
|
||||
run: |
|
||||
$objects = systeminfo.exe /FO CSV | ConvertFrom-Csv
|
||||
"VERSION=$($objects.'OS Name') $($objects.'OS Version')" >> $env:GITHUB_ENV
|
||||
- if: runner.os == 'macOS'
|
||||
shell: bash
|
||||
run: |
|
||||
echo "VERSION=$(sw_vers -productName) $(sw_vers -productVersion)" >> $GITHUB_ENV
|
||||
- name: Emit OS version
|
||||
id: version
|
||||
shell: bash
|
||||
run: |
|
||||
echo "$VERSION"
|
||||
echo "version=${VERSION}" >> $GITHUB_OUTPUT
|
||||
|
||||
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"
|
||||
|
||||
|
||||
6
.github/workflows/check-change-note.yml
vendored
6
.github/workflows/check-change-note.yml
vendored
@@ -26,3 +26,9 @@ jobs:
|
||||
run: |
|
||||
gh api 'repos/${{github.repository}}/pulls/${{github.event.number}}/files' --paginate --jq 'any(.[].filename ; test("/change-notes/.*[.]md$"))' |
|
||||
grep true -c
|
||||
- name: Fail if the change note filename doesn't match the expected format. The file name must be of the form 'YYYY-MM-DD.md' or 'YYYY-MM-DD-{title}.md', where '{title}' is arbitrary text.
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
gh api 'repos/${{github.repository}}/pulls/${{github.event.number}}/files' --paginate --jq '[.[].filename | select(test("/change-notes/.*[.]md$"))] | all(test("/change-notes/[0-9]{4}-[0-9]{2}-[0-9]{2}.*[.]md$"))' |
|
||||
grep true -c
|
||||
|
||||
2
.github/workflows/codeql-analysis.yml
vendored
2
.github/workflows/codeql-analysis.yml
vendored
@@ -30,7 +30,7 @@ jobs:
|
||||
- name: Setup dotnet
|
||||
uses: actions/setup-dotnet@v2
|
||||
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"
|
||||
|
||||
10
.github/workflows/mad_modelDiff.yml
vendored
10
.github/workflows/mad_modelDiff.yml
vendored
@@ -11,7 +11,7 @@ on:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- "java/ql/src/utils/model-generator/**/*.*"
|
||||
- "java/ql/src/utils/modelgenerator/**/*.*"
|
||||
- ".github/workflows/mad_modelDiff.yml"
|
||||
|
||||
permissions:
|
||||
@@ -40,12 +40,12 @@ jobs:
|
||||
- name: Download database
|
||||
env:
|
||||
SLUG: ${{ matrix.slug }}
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
set -x
|
||||
mkdir lib-dbs
|
||||
SHORTNAME=${SLUG//[^a-zA-Z0-9_]/}
|
||||
projectId=`curl -s https://lgtm.com/api/v1.0/projects/g/${SLUG} | jq .id`
|
||||
curl -L "https://lgtm.com/api/v1.0/snapshots/$projectId/java" -o "$SHORTNAME.zip"
|
||||
gh api -H "Accept: application/zip" "/repos/${SLUG}/code-scanning/codeql/databases/java" > "$SHORTNAME.zip"
|
||||
unzip -q -d "${SHORTNAME}-db" "${SHORTNAME}.zip"
|
||||
mkdir "lib-dbs/$SHORTNAME/"
|
||||
mv "${SHORTNAME}-db/"$(ls -1 "${SHORTNAME}"-db)/* "lib-dbs/${SHORTNAME}/"
|
||||
@@ -61,7 +61,7 @@ jobs:
|
||||
DATABASE=$2
|
||||
cd codeql-$QL_VARIANT
|
||||
SHORTNAME=`basename $DATABASE`
|
||||
python java/ql/src/utils/model-generator/GenerateFlowModel.py --with-summaries --with-sinks $DATABASE ${SHORTNAME}.temp.model.yml
|
||||
python java/ql/src/utils/modelgenerator/GenerateFlowModel.py --with-summaries --with-sinks $DATABASE ${SHORTNAME}.temp.model.yml
|
||||
mv java/ql/lib/ext/generated/${SHORTNAME}.temp.model.yml $MODELS/${SHORTNAME}Generated_${QL_VARIANT}.model.yml
|
||||
cd ..
|
||||
}
|
||||
@@ -100,4 +100,6 @@ jobs:
|
||||
with:
|
||||
name: diffs
|
||||
path: tmp-models/*.html
|
||||
# An html file is only produced if the generated models differ.
|
||||
if-no-files-found: ignore
|
||||
retention-days: 20
|
||||
|
||||
2
.github/workflows/mad_regenerate-models.yml
vendored
2
.github/workflows/mad_regenerate-models.yml
vendored
@@ -50,7 +50,7 @@ jobs:
|
||||
SLUG: ${{ matrix.slug }}
|
||||
run: |
|
||||
SHORTNAME=${SLUG//[^a-zA-Z0-9_]/}
|
||||
java/ql/src/utils/model-generator/RegenerateModels.py "${SLUG}" dbs/${SHORTNAME}
|
||||
java/ql/src/utils/modelgenerator/RegenerateModels.py "${SLUG}" dbs/${SHORTNAME}
|
||||
- name: Stage changes
|
||||
run: |
|
||||
find java -name "*.model.yml" -print0 | xargs -0 git add
|
||||
|
||||
20
.github/workflows/ql-for-ql-build.yml
vendored
20
.github/workflows/ql-for-ql-build.yml
vendored
@@ -27,7 +27,7 @@ jobs:
|
||||
uses: ./.github/actions/find-latest-bundle
|
||||
- name: Find codeql
|
||||
id: find-codeql
|
||||
uses: github/codeql-action/init@77a8d2d10c0b403a8b4aadbd223dc489ecd22683
|
||||
uses: github/codeql-action/init@beae46e6b1da530ed5e9fc6a756f92433ca47ae1
|
||||
with:
|
||||
languages: javascript # does not matter
|
||||
tools: ${{ steps.find-latest-bundle.outputs.url }}
|
||||
@@ -38,12 +38,14 @@ jobs:
|
||||
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 }}-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') }}
|
||||
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
|
||||
@@ -77,7 +79,7 @@ jobs:
|
||||
ql/target/release/ql-autobuilder.exe
|
||||
ql/target/release/ql-extractor
|
||||
ql/target/release/ql-extractor.exe
|
||||
key: ${{ runner.os }}-extractor-${{ hashFiles('ql/**/Cargo.lock') }}-${{ hashFiles('ql/**/*.rs') }}
|
||||
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'
|
||||
uses: actions/cache@v3
|
||||
@@ -86,7 +88,7 @@ jobs:
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
ql/target
|
||||
key: ${{ runner.os }}-rust-cargo-${{ hashFiles('ql/**/Cargo.lock') }}
|
||||
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
|
||||
@@ -137,20 +139,20 @@ jobs:
|
||||
env:
|
||||
CONF: ./ql-for-ql-config.yml
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@77a8d2d10c0b403a8b4aadbd223dc489ecd22683
|
||||
uses: github/codeql-action/init@beae46e6b1da530ed5e9fc6a756f92433ca47ae1
|
||||
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 cache
|
||||
- name: Move pack queries
|
||||
run: |
|
||||
cp -r ${PACK}/.cache ql/ql/src/.cache
|
||||
cp -r ${PACK}/queries ql/ql/src
|
||||
env:
|
||||
PACK: ${{ runner.temp }}/pack
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@77a8d2d10c0b403a8b4aadbd223dc489ecd22683
|
||||
uses: github/codeql-action/analyze@beae46e6b1da530ed5e9fc6a756f92433ca47ae1
|
||||
with:
|
||||
category: "ql-for-ql"
|
||||
- name: Copy sarif file to CWD
|
||||
@@ -172,4 +174,4 @@ jobs:
|
||||
with:
|
||||
name: ql-for-ql-langs
|
||||
path: split-sarif
|
||||
retention-days: 1
|
||||
retention-days: 1
|
||||
|
||||
@@ -25,16 +25,18 @@ jobs:
|
||||
|
||||
- name: Find codeql
|
||||
id: find-codeql
|
||||
uses: github/codeql-action/init@77a8d2d10c0b403a8b4aadbd223dc489ecd22683
|
||||
uses: github/codeql-action/init@beae46e6b1da530ed5e9fc6a756f92433ca47ae1
|
||||
with:
|
||||
languages: javascript # does not matter
|
||||
- uses: ./.github/actions/os-version
|
||||
id: os_version
|
||||
- uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
ql/target
|
||||
key: ${{ runner.os }}-qltest-cargo-${{ hashFiles('ql/**/Cargo.lock') }}
|
||||
key: ${{ runner.os }}-${{ steps.os_version.outputs.version }}-qltest-cargo-${{ hashFiles('ql/**/Cargo.lock') }}
|
||||
- name: Build Extractor
|
||||
run: cd ql; env "PATH=$PATH:`dirname ${CODEQL}`" ./scripts/create-extractor-pack.sh
|
||||
env:
|
||||
|
||||
68
.github/workflows/ql-for-ql-tests.yml
vendored
68
.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,28 +24,84 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Find codeql
|
||||
id: find-codeql
|
||||
uses: github/codeql-action/init@77a8d2d10c0b403a8b4aadbd223dc489ecd22683
|
||||
uses: github/codeql-action/init@beae46e6b1da530ed5e9fc6a756f92433ca47ae1
|
||||
with:
|
||||
languages: javascript # does not matter
|
||||
- uses: ./.github/actions/os-version
|
||||
id: os_version
|
||||
- uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
ql/target
|
||||
key: ${{ runner.os }}-qltest-cargo-${{ hashFiles('ql/**/Cargo.lock') }}
|
||||
key: ${{ runner.os }}-${{ steps.os_version.outputs.version }}-qltest-cargo-${{ hashFiles('ql/rust-toolchain.toml', 'ql/**/Cargo.lock') }}
|
||||
- name: Build extractor
|
||||
run: |
|
||||
cd ql;
|
||||
codeqlpath=$(dirname ${{ steps.find-codeql.outputs.codeql-path }});
|
||||
env "PATH=$PATH:$codeqlpath" ./scripts/create-extractor-pack.sh
|
||||
- name: Cache compilation cache
|
||||
id: query-cache
|
||||
uses: ./.github/actions/cache-query-compilation
|
||||
with:
|
||||
key: ql-for-ql-tests
|
||||
- name: Run QL tests
|
||||
run: |
|
||||
"${CODEQL}" test run --check-databases --check-unused-labels --check-repeated-labels --check-redefined-labels --check-use-before-definition --search-path "${{ github.workspace }}/ql/extractor-pack" --consistency-queries ql/ql/consistency-queries ql/ql/test
|
||||
"${CODEQL}" test run --check-databases --check-unused-labels --check-repeated-labels --check-redefined-labels --check-use-before-definition --search-path "${{ github.workspace }}/ql/extractor-pack" --consistency-queries ql/ql/consistency-queries --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}" ql/ql/test
|
||||
env:
|
||||
CODEQL: ${{ steps.find-codeql.outputs.codeql-path }}
|
||||
- name: Check QL formatting
|
||||
|
||||
other-os:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [macos-latest, windows-latest]
|
||||
needs: [qltest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install GNU tar
|
||||
if: runner.os == 'macOS'
|
||||
run: |
|
||||
find ql/ql/src "(" -name "*.ql" -or -name "*.qll" ")" -print0 | xargs -0 "${CODEQL}" query format --check-only
|
||||
brew install gnu-tar
|
||||
echo "/usr/local/opt/gnu-tar/libexec/gnubin" >> $GITHUB_PATH
|
||||
- name: Find codeql
|
||||
id: find-codeql
|
||||
uses: github/codeql-action/init@beae46e6b1da530ed5e9fc6a756f92433ca47ae1
|
||||
with:
|
||||
languages: javascript # does not matter
|
||||
- uses: ./.github/actions/os-version
|
||||
id: os_version
|
||||
- uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
ql/target
|
||||
key: ${{ runner.os }}-${{ steps.os_version.outputs.version }}-qltest-cargo-${{ hashFiles('ql/rust-toolchain.toml', 'ql/**/Cargo.lock') }}
|
||||
- name: Build extractor
|
||||
if: runner.os != 'Windows'
|
||||
run: |
|
||||
cd ql;
|
||||
codeqlpath=$(dirname ${{ steps.find-codeql.outputs.codeql-path }});
|
||||
env "PATH=$PATH:$codeqlpath" ./scripts/create-extractor-pack.sh
|
||||
- name: Build extractor (Windows)
|
||||
if: runner.os == 'Windows'
|
||||
shell: pwsh
|
||||
run: |
|
||||
cd ql;
|
||||
$Env:PATH += ";$(dirname ${{ steps.find-codeql.outputs.codeql-path }})"
|
||||
pwsh ./scripts/create-extractor-pack.ps1
|
||||
- name: Run a single QL tests - Unix
|
||||
if: runner.os != 'Windows'
|
||||
run: |
|
||||
"${CODEQL}" test run --check-databases --search-path "${{ github.workspace }}/ql/extractor-pack" ql/ql/test/queries/style/DeadCode/DeadCode.qlref
|
||||
env:
|
||||
CODEQL: ${{ steps.find-codeql.outputs.codeql-path }}
|
||||
- name: Run a single QL tests - Windows
|
||||
if: runner.os == 'Windows'
|
||||
shell: pwsh
|
||||
run: |
|
||||
$Env:PATH += ";$(dirname ${{ steps.find-codeql.outputs.codeql-path }})"
|
||||
codeql test run --check-databases --search-path "${{ github.workspace }}/ql/extractor-pack" ql/ql/test/queries/style/DeadCode/DeadCode.qlref
|
||||
|
||||
28
.github/workflows/ruby-build.yml
vendored
28
.github/workflows/ruby-build.yml
vendored
@@ -48,6 +48,8 @@ jobs:
|
||||
run: |
|
||||
brew install gnu-tar
|
||||
echo "/usr/local/opt/gnu-tar/libexec/gnubin" >> $GITHUB_PATH
|
||||
- uses: ./.github/actions/os-version
|
||||
id: os_version
|
||||
- name: Cache entire extractor
|
||||
uses: actions/cache@v3
|
||||
id: cache-extractor
|
||||
@@ -58,7 +60,7 @@ jobs:
|
||||
ruby/target/release/ruby-extractor
|
||||
ruby/target/release/ruby-extractor.exe
|
||||
ruby/ql/lib/codeql/ruby/ast/internal/TreeSitter.qll
|
||||
key: ${{ runner.os }}-ruby-extractor-${{ hashFiles('ruby/rust-toolchain.toml', 'ruby/**/Cargo.lock') }}--${{ hashFiles('ruby/**/*.rs') }}
|
||||
key: ${{ runner.os }}-${{ steps.os_version.outputs.version }}-ruby-extractor-${{ hashFiles('ruby/rust-toolchain.toml', 'ruby/**/Cargo.lock') }}--${{ hashFiles('ruby/**/*.rs') }}
|
||||
- uses: actions/cache@v3
|
||||
if: steps.cache-extractor.outputs.cache-hit != 'true'
|
||||
with:
|
||||
@@ -66,7 +68,7 @@ jobs:
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
ruby/target
|
||||
key: ${{ runner.os }}-ruby-rust-cargo-${{ hashFiles('ruby/rust-toolchain.toml', 'ruby/**/Cargo.lock') }}
|
||||
key: ${{ runner.os }}-${{ steps.os_version.outputs.version }}-ruby-rust-cargo-${{ hashFiles('ruby/rust-toolchain.toml', 'ruby/**/Cargo.lock') }}
|
||||
- name: Check formatting
|
||||
if: steps.cache-extractor.outputs.cache-hit != 'true'
|
||||
run: cargo fmt --all -- --check
|
||||
@@ -203,11 +205,6 @@ jobs:
|
||||
- name: Fetch CodeQL
|
||||
uses: ./.github/actions/fetch-codeql
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
repository: Shopify/example-ruby-app
|
||||
ref: 67a0decc5eb550f3a9228eda53925c3afd40dfe9
|
||||
|
||||
- name: Download Ruby bundle
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
@@ -216,26 +213,15 @@ jobs:
|
||||
- name: Unzip Ruby bundle
|
||||
shell: bash
|
||||
run: unzip -q -d "${{ runner.temp }}/ruby-bundle" "${{ runner.temp }}/codeql-ruby-bundle.zip"
|
||||
- name: Prepare test files
|
||||
shell: bash
|
||||
run: |
|
||||
echo "import codeql.ruby.AST select count(File f)" > "test.ql"
|
||||
echo "| 4 |" > "test.expected"
|
||||
echo 'name: sample-tests
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
codeql/ruby-all: "*"
|
||||
extractor: ruby
|
||||
tests: .
|
||||
' > qlpack.yml
|
||||
|
||||
- name: Run QL test
|
||||
shell: bash
|
||||
run: |
|
||||
codeql test run --search-path "${{ runner.temp }}/ruby-bundle" --additional-packs "${{ runner.temp }}/ruby-bundle" .
|
||||
codeql test run --search-path "${{ runner.temp }}/ruby-bundle" --additional-packs "${{ runner.temp }}/ruby-bundle" ruby/ql/test/library-tests/ast/constants/
|
||||
- name: Create database
|
||||
shell: bash
|
||||
run: |
|
||||
codeql database create --search-path "${{ runner.temp }}/ruby-bundle" --language ruby --source-root . ../database
|
||||
codeql database create --search-path "${{ runner.temp }}/ruby-bundle" --language ruby --source-root ruby/ql/test/library-tests/ast/constants/ ../database
|
||||
- name: Analyze database
|
||||
shell: bash
|
||||
run: |
|
||||
|
||||
@@ -25,6 +25,7 @@ If you have an idea for a query that you would like to share with other CodeQL u
|
||||
|
||||
Each language-specific directory contains further subdirectories that group queries based on their `@tags` or purpose.
|
||||
- Experimental queries and libraries are stored in the `experimental` subdirectory within each language-specific directory in the [CodeQL repository](https://github.com/github/codeql). For example, experimental Java queries and libraries are stored in `java/ql/src/experimental` and any corresponding tests in `java/ql/test/experimental`.
|
||||
- Experimental queries need to include `experimental` in their `@tags`
|
||||
- The structure of an `experimental` subdirectory mirrors the structure of its parent directory.
|
||||
- Select or create an appropriate directory in `experimental` based on the existing directory structure of `experimental` or its parent directory.
|
||||
|
||||
|
||||
@@ -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"
|
||||
@@ -402,16 +403,6 @@
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/ControlFlowReachability.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/ControlFlowReachability.qll"
|
||||
],
|
||||
"Inline Test Expectations": [
|
||||
"cpp/ql/test/TestUtilities/InlineExpectationsTest.qll",
|
||||
"csharp/ql/test/TestUtilities/InlineExpectationsTest.qll",
|
||||
"java/ql/test/TestUtilities/InlineExpectationsTest.qll",
|
||||
"python/ql/test/TestUtilities/InlineExpectationsTest.qll",
|
||||
"ruby/ql/test/TestUtilities/InlineExpectationsTest.qll",
|
||||
"ql/ql/test/TestUtilities/InlineExpectationsTest.qll",
|
||||
"go/ql/test/TestUtilities/InlineExpectationsTest.qll",
|
||||
"swift/ql/test/TestUtilities/InlineExpectationsTest.qll"
|
||||
],
|
||||
"C++ ExternalAPIs": [
|
||||
"cpp/ql/src/Security/CWE/CWE-020/ExternalAPIs.qll",
|
||||
"cpp/ql/src/Security/CWE/CWE-020/ir/ExternalAPIs.qll"
|
||||
|
||||
@@ -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,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 />
|
||||
|
||||
@@ -13,5 +13,5 @@ predicate isExprWithNewBuiltin(Expr expr) {
|
||||
from Expr expr, int kind, int kind_new, Location location
|
||||
where
|
||||
exprs(expr, kind, location) and
|
||||
if isExprWithNewBuiltin(expr) then kind_new = 0 else kind_new = kind
|
||||
if isExprWithNewBuiltin(expr) then kind_new = 1 else kind_new = kind
|
||||
select expr, kind_new, location
|
||||
|
||||
@@ -9,5 +9,5 @@ class Location extends @location_expr {
|
||||
from Expr expr, int kind, int kind_new, Location location
|
||||
where
|
||||
exprs(expr, kind, location) and
|
||||
if expr instanceof @blockassignexpr then kind_new = 0 else kind_new = kind
|
||||
if expr instanceof @blockassignexpr then kind_new = 1 else kind_new = kind
|
||||
select expr, kind_new, location
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
class BuiltinType extends @builtintype {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
from BuiltinType type, string name, int kind, int kind_new, int size, int sign, int alignment
|
||||
where
|
||||
builtintypes(type, name, kind, size, sign, alignment) and
|
||||
if type instanceof @float16 or type instanceof @complex_float16
|
||||
then kind_new = 2
|
||||
else kind_new = kind
|
||||
select type, name, kind_new, size, sign, alignment
|
||||
2210
cpp/downgrades/a5bb28ed29f73855d64cc5f939cef977fa8fd19a/old.dbscheme
Normal file
2210
cpp/downgrades/a5bb28ed29f73855d64cc5f939cef977fa8fd19a/old.dbscheme
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,3 @@
|
||||
description: Introduce (_Complex) _Float16 type
|
||||
compatibility: backwards
|
||||
builtintypes.rel: run builtintypes.qlo
|
||||
2208
cpp/downgrades/ba86bebea4c7a8235c2fa0e220391fbd4446a087/old.dbscheme
Normal file
2208
cpp/downgrades/ba86bebea4c7a8235c2fa0e220391fbd4446a087/old.dbscheme
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,2 @@
|
||||
description: Uncomment case splits in dbscheme
|
||||
compatibility: full
|
||||
@@ -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
|
||||
|
||||
@@ -123,6 +123,13 @@ private predicate constructorCallTypeMention(ConstructorCall cc, TypeMention tm)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `loc` has the container `container` and is on the line starting at `startLine`. */
|
||||
pragma[nomagic]
|
||||
private predicate hasContainerAndStartLine(Location loc, Container container, int startLine) {
|
||||
loc.getStartLine() = startLine and
|
||||
loc.getContainer() = container
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an element, of kind `kind`, that element `e` uses, if any.
|
||||
* Attention: This predicate yields multiple definitions for a single location.
|
||||
@@ -159,9 +166,9 @@ Top definitionOf(Top e, string kind) {
|
||||
// Multiple type mentions can be generated when a typedef is used, and
|
||||
// in such cases we want to exclude all but the originating typedef.
|
||||
not exists(Type secondary |
|
||||
exists(TypeMention tm, File f, int startline, int startcol |
|
||||
exists(File f, int startline, int startcol |
|
||||
typeMentionStartLoc(e, result, f, startline, startcol) and
|
||||
typeMentionStartLoc(tm, secondary, f, startline, startcol) and
|
||||
typeMentionStartLoc(_, secondary, f, startline, startcol) and
|
||||
(
|
||||
result = secondary.(TypedefType).getBaseType() or
|
||||
result = secondary.(TypedefType).getBaseType().(SpecifiedType).getBaseType()
|
||||
@@ -184,11 +191,9 @@ Top definitionOf(Top e, string kind) {
|
||||
kind = "I" and
|
||||
result = e.(Include).getIncludedFile() and
|
||||
// exclude `#include` directives containing macros
|
||||
not exists(MacroInvocation mi, Location l1, Location l2 |
|
||||
l1 = e.(Include).getLocation() and
|
||||
l2 = mi.getLocation() and
|
||||
l1.getContainer() = l2.getContainer() and
|
||||
l1.getStartLine() = l2.getStartLine()
|
||||
not exists(MacroInvocation mi, Container container, int startLine |
|
||||
hasContainerAndStartLine(e.(Include).getLocation(), container, startLine) and
|
||||
hasContainerAndStartLine(mi.getLocation(), container, startLine)
|
||||
// (an #include directive must be always on it's own line)
|
||||
)
|
||||
) and
|
||||
|
||||
@@ -876,9 +876,9 @@ private module Stage1 implements StageSig {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowOut(ReturnPosition pos, Configuration config) {
|
||||
exists(DataFlowCall call, NodeEx out |
|
||||
exists(NodeEx out |
|
||||
revFlow(out, _, config) and
|
||||
viableReturnPosOutNodeCandFwd1(call, pos, out, config)
|
||||
viableReturnPosOutNodeCandFwd1(_, pos, out, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1487,6 +1487,10 @@ private module MkStage<StageSig PrevStage> {
|
||||
PrevStage::readStepCand(node1, _, _, config)
|
||||
}
|
||||
|
||||
bindingset[ap, c]
|
||||
pragma[inline_late]
|
||||
private predicate hasHeadContent(Ap ap, Content c) { getHeadContent(ap) = c }
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead(
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
@@ -1494,7 +1498,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
) {
|
||||
fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
getHeadContent(ap) = c
|
||||
hasHeadContent(ap, c)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1731,8 +1735,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ParamNodeEx p, ReturnPosition pos, Ap innerReturnAp |
|
||||
revFlowThrough(call, returnCtx, p, state, pos, returnAp, ap, innerReturnAp, config) and
|
||||
exists(DataFlowCall call, ParamNodeEx p, Ap innerReturnAp |
|
||||
revFlowThrough(call, returnCtx, p, state, _, returnAp, ap, innerReturnAp, config) and
|
||||
flowThroughIntoCall(call, node, p, _, ap, innerReturnAp, config)
|
||||
)
|
||||
or
|
||||
@@ -1901,8 +1905,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
|
||||
exists(RetNodeEx ret, ReturnPosition pos |
|
||||
returnFlowsThrough(ret, pos, _, _, p, ap, _, config) and
|
||||
exists(ReturnPosition pos |
|
||||
returnFlowsThrough(_, pos, _, _, p, ap, _, config) and
|
||||
parameterFlowsThroughRev(p, ap, pos, _, config)
|
||||
)
|
||||
}
|
||||
@@ -1923,8 +1927,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, ReturnPosition pos, Ap innerReturnAp |
|
||||
revFlowThrough(call, returnCtx, p, state, pos, returnAp, ap, innerReturnAp, config) and
|
||||
exists(ParamNodeEx p, Ap innerReturnAp |
|
||||
revFlowThrough(call, returnCtx, p, state, _, returnAp, ap, innerReturnAp, config) and
|
||||
flowThroughIntoCall(call, arg, p, _, ap, innerReturnAp, config)
|
||||
)
|
||||
}
|
||||
@@ -3749,8 +3753,8 @@ private predicate paramFlowsThrough(
|
||||
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
|
||||
AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid, RetNodeEx ret |
|
||||
pathNode(mid, ret, state, cc, sc, ap, config, _) and
|
||||
exists(RetNodeEx ret |
|
||||
pathNode(_, ret, state, cc, sc, ap, config, _) and
|
||||
kind = ret.getKind() and
|
||||
apa = ap.getApprox() and
|
||||
parameterFlowThroughAllowed(sc.getParamNode(), kind)
|
||||
@@ -4212,17 +4216,15 @@ private module FlowExploration {
|
||||
ap = TRevPartialNil() and
|
||||
exists(config.explorationLimit())
|
||||
or
|
||||
exists(PartialPathNodeRev mid |
|
||||
revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and
|
||||
not clearsContentEx(node, ap.getHead()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead())
|
||||
) and
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
|
||||
)
|
||||
revPartialPathStep(_, node, state, sc1, sc2, sc3, ap, config) and
|
||||
not clearsContentEx(node, ap.getHead()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead())
|
||||
) and
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -4230,19 +4232,17 @@ private module FlowExploration {
|
||||
NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
|
||||
TSummaryCtx3 sc3, PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(PartialPathNodeFwd mid |
|
||||
partialPathStep(mid, node, state, cc, sc1, sc2, sc3, ap, config) and
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
not clearsContentEx(node, ap.getHead().getContent()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead().getContent())
|
||||
) and
|
||||
if node.asNode() instanceof CastingNode
|
||||
then compatibleTypes(node.getDataFlowType(), ap.getType())
|
||||
else any()
|
||||
)
|
||||
partialPathStep(_, node, state, cc, sc1, sc2, sc3, ap, config) and
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
not clearsContentEx(node, ap.getHead().getContent()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead().getContent())
|
||||
) and
|
||||
if node.asNode() instanceof CastingNode
|
||||
then compatibleTypes(node.getDataFlowType(), ap.getType())
|
||||
else any()
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -876,9 +876,9 @@ private module Stage1 implements StageSig {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowOut(ReturnPosition pos, Configuration config) {
|
||||
exists(DataFlowCall call, NodeEx out |
|
||||
exists(NodeEx out |
|
||||
revFlow(out, _, config) and
|
||||
viableReturnPosOutNodeCandFwd1(call, pos, out, config)
|
||||
viableReturnPosOutNodeCandFwd1(_, pos, out, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1487,6 +1487,10 @@ private module MkStage<StageSig PrevStage> {
|
||||
PrevStage::readStepCand(node1, _, _, config)
|
||||
}
|
||||
|
||||
bindingset[ap, c]
|
||||
pragma[inline_late]
|
||||
private predicate hasHeadContent(Ap ap, Content c) { getHeadContent(ap) = c }
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead(
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
@@ -1494,7 +1498,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
) {
|
||||
fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
getHeadContent(ap) = c
|
||||
hasHeadContent(ap, c)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1731,8 +1735,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ParamNodeEx p, ReturnPosition pos, Ap innerReturnAp |
|
||||
revFlowThrough(call, returnCtx, p, state, pos, returnAp, ap, innerReturnAp, config) and
|
||||
exists(DataFlowCall call, ParamNodeEx p, Ap innerReturnAp |
|
||||
revFlowThrough(call, returnCtx, p, state, _, returnAp, ap, innerReturnAp, config) and
|
||||
flowThroughIntoCall(call, node, p, _, ap, innerReturnAp, config)
|
||||
)
|
||||
or
|
||||
@@ -1901,8 +1905,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
|
||||
exists(RetNodeEx ret, ReturnPosition pos |
|
||||
returnFlowsThrough(ret, pos, _, _, p, ap, _, config) and
|
||||
exists(ReturnPosition pos |
|
||||
returnFlowsThrough(_, pos, _, _, p, ap, _, config) and
|
||||
parameterFlowsThroughRev(p, ap, pos, _, config)
|
||||
)
|
||||
}
|
||||
@@ -1923,8 +1927,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, ReturnPosition pos, Ap innerReturnAp |
|
||||
revFlowThrough(call, returnCtx, p, state, pos, returnAp, ap, innerReturnAp, config) and
|
||||
exists(ParamNodeEx p, Ap innerReturnAp |
|
||||
revFlowThrough(call, returnCtx, p, state, _, returnAp, ap, innerReturnAp, config) and
|
||||
flowThroughIntoCall(call, arg, p, _, ap, innerReturnAp, config)
|
||||
)
|
||||
}
|
||||
@@ -3749,8 +3753,8 @@ private predicate paramFlowsThrough(
|
||||
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
|
||||
AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid, RetNodeEx ret |
|
||||
pathNode(mid, ret, state, cc, sc, ap, config, _) and
|
||||
exists(RetNodeEx ret |
|
||||
pathNode(_, ret, state, cc, sc, ap, config, _) and
|
||||
kind = ret.getKind() and
|
||||
apa = ap.getApprox() and
|
||||
parameterFlowThroughAllowed(sc.getParamNode(), kind)
|
||||
@@ -4212,17 +4216,15 @@ private module FlowExploration {
|
||||
ap = TRevPartialNil() and
|
||||
exists(config.explorationLimit())
|
||||
or
|
||||
exists(PartialPathNodeRev mid |
|
||||
revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and
|
||||
not clearsContentEx(node, ap.getHead()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead())
|
||||
) and
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
|
||||
)
|
||||
revPartialPathStep(_, node, state, sc1, sc2, sc3, ap, config) and
|
||||
not clearsContentEx(node, ap.getHead()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead())
|
||||
) and
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -4230,19 +4232,17 @@ private module FlowExploration {
|
||||
NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
|
||||
TSummaryCtx3 sc3, PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(PartialPathNodeFwd mid |
|
||||
partialPathStep(mid, node, state, cc, sc1, sc2, sc3, ap, config) and
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
not clearsContentEx(node, ap.getHead().getContent()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead().getContent())
|
||||
) and
|
||||
if node.asNode() instanceof CastingNode
|
||||
then compatibleTypes(node.getDataFlowType(), ap.getType())
|
||||
else any()
|
||||
)
|
||||
partialPathStep(_, node, state, cc, sc1, sc2, sc3, ap, config) and
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
not clearsContentEx(node, ap.getHead().getContent()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead().getContent())
|
||||
) and
|
||||
if node.asNode() instanceof CastingNode
|
||||
then compatibleTypes(node.getDataFlowType(), ap.getType())
|
||||
else any()
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -876,9 +876,9 @@ private module Stage1 implements StageSig {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowOut(ReturnPosition pos, Configuration config) {
|
||||
exists(DataFlowCall call, NodeEx out |
|
||||
exists(NodeEx out |
|
||||
revFlow(out, _, config) and
|
||||
viableReturnPosOutNodeCandFwd1(call, pos, out, config)
|
||||
viableReturnPosOutNodeCandFwd1(_, pos, out, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1487,6 +1487,10 @@ private module MkStage<StageSig PrevStage> {
|
||||
PrevStage::readStepCand(node1, _, _, config)
|
||||
}
|
||||
|
||||
bindingset[ap, c]
|
||||
pragma[inline_late]
|
||||
private predicate hasHeadContent(Ap ap, Content c) { getHeadContent(ap) = c }
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead(
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
@@ -1494,7 +1498,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
) {
|
||||
fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
getHeadContent(ap) = c
|
||||
hasHeadContent(ap, c)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1731,8 +1735,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ParamNodeEx p, ReturnPosition pos, Ap innerReturnAp |
|
||||
revFlowThrough(call, returnCtx, p, state, pos, returnAp, ap, innerReturnAp, config) and
|
||||
exists(DataFlowCall call, ParamNodeEx p, Ap innerReturnAp |
|
||||
revFlowThrough(call, returnCtx, p, state, _, returnAp, ap, innerReturnAp, config) and
|
||||
flowThroughIntoCall(call, node, p, _, ap, innerReturnAp, config)
|
||||
)
|
||||
or
|
||||
@@ -1901,8 +1905,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
|
||||
exists(RetNodeEx ret, ReturnPosition pos |
|
||||
returnFlowsThrough(ret, pos, _, _, p, ap, _, config) and
|
||||
exists(ReturnPosition pos |
|
||||
returnFlowsThrough(_, pos, _, _, p, ap, _, config) and
|
||||
parameterFlowsThroughRev(p, ap, pos, _, config)
|
||||
)
|
||||
}
|
||||
@@ -1923,8 +1927,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, ReturnPosition pos, Ap innerReturnAp |
|
||||
revFlowThrough(call, returnCtx, p, state, pos, returnAp, ap, innerReturnAp, config) and
|
||||
exists(ParamNodeEx p, Ap innerReturnAp |
|
||||
revFlowThrough(call, returnCtx, p, state, _, returnAp, ap, innerReturnAp, config) and
|
||||
flowThroughIntoCall(call, arg, p, _, ap, innerReturnAp, config)
|
||||
)
|
||||
}
|
||||
@@ -3749,8 +3753,8 @@ private predicate paramFlowsThrough(
|
||||
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
|
||||
AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid, RetNodeEx ret |
|
||||
pathNode(mid, ret, state, cc, sc, ap, config, _) and
|
||||
exists(RetNodeEx ret |
|
||||
pathNode(_, ret, state, cc, sc, ap, config, _) and
|
||||
kind = ret.getKind() and
|
||||
apa = ap.getApprox() and
|
||||
parameterFlowThroughAllowed(sc.getParamNode(), kind)
|
||||
@@ -4212,17 +4216,15 @@ private module FlowExploration {
|
||||
ap = TRevPartialNil() and
|
||||
exists(config.explorationLimit())
|
||||
or
|
||||
exists(PartialPathNodeRev mid |
|
||||
revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and
|
||||
not clearsContentEx(node, ap.getHead()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead())
|
||||
) and
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
|
||||
)
|
||||
revPartialPathStep(_, node, state, sc1, sc2, sc3, ap, config) and
|
||||
not clearsContentEx(node, ap.getHead()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead())
|
||||
) and
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -4230,19 +4232,17 @@ private module FlowExploration {
|
||||
NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
|
||||
TSummaryCtx3 sc3, PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(PartialPathNodeFwd mid |
|
||||
partialPathStep(mid, node, state, cc, sc1, sc2, sc3, ap, config) and
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
not clearsContentEx(node, ap.getHead().getContent()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead().getContent())
|
||||
) and
|
||||
if node.asNode() instanceof CastingNode
|
||||
then compatibleTypes(node.getDataFlowType(), ap.getType())
|
||||
else any()
|
||||
)
|
||||
partialPathStep(_, node, state, cc, sc1, sc2, sc3, ap, config) and
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
not clearsContentEx(node, ap.getHead().getContent()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead().getContent())
|
||||
) and
|
||||
if node.asNode() instanceof CastingNode
|
||||
then compatibleTypes(node.getDataFlowType(), ap.getType())
|
||||
else any()
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -876,9 +876,9 @@ private module Stage1 implements StageSig {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowOut(ReturnPosition pos, Configuration config) {
|
||||
exists(DataFlowCall call, NodeEx out |
|
||||
exists(NodeEx out |
|
||||
revFlow(out, _, config) and
|
||||
viableReturnPosOutNodeCandFwd1(call, pos, out, config)
|
||||
viableReturnPosOutNodeCandFwd1(_, pos, out, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1487,6 +1487,10 @@ private module MkStage<StageSig PrevStage> {
|
||||
PrevStage::readStepCand(node1, _, _, config)
|
||||
}
|
||||
|
||||
bindingset[ap, c]
|
||||
pragma[inline_late]
|
||||
private predicate hasHeadContent(Ap ap, Content c) { getHeadContent(ap) = c }
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead(
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
@@ -1494,7 +1498,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
) {
|
||||
fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
getHeadContent(ap) = c
|
||||
hasHeadContent(ap, c)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1731,8 +1735,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ParamNodeEx p, ReturnPosition pos, Ap innerReturnAp |
|
||||
revFlowThrough(call, returnCtx, p, state, pos, returnAp, ap, innerReturnAp, config) and
|
||||
exists(DataFlowCall call, ParamNodeEx p, Ap innerReturnAp |
|
||||
revFlowThrough(call, returnCtx, p, state, _, returnAp, ap, innerReturnAp, config) and
|
||||
flowThroughIntoCall(call, node, p, _, ap, innerReturnAp, config)
|
||||
)
|
||||
or
|
||||
@@ -1901,8 +1905,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
|
||||
exists(RetNodeEx ret, ReturnPosition pos |
|
||||
returnFlowsThrough(ret, pos, _, _, p, ap, _, config) and
|
||||
exists(ReturnPosition pos |
|
||||
returnFlowsThrough(_, pos, _, _, p, ap, _, config) and
|
||||
parameterFlowsThroughRev(p, ap, pos, _, config)
|
||||
)
|
||||
}
|
||||
@@ -1923,8 +1927,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, ReturnPosition pos, Ap innerReturnAp |
|
||||
revFlowThrough(call, returnCtx, p, state, pos, returnAp, ap, innerReturnAp, config) and
|
||||
exists(ParamNodeEx p, Ap innerReturnAp |
|
||||
revFlowThrough(call, returnCtx, p, state, _, returnAp, ap, innerReturnAp, config) and
|
||||
flowThroughIntoCall(call, arg, p, _, ap, innerReturnAp, config)
|
||||
)
|
||||
}
|
||||
@@ -3749,8 +3753,8 @@ private predicate paramFlowsThrough(
|
||||
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
|
||||
AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid, RetNodeEx ret |
|
||||
pathNode(mid, ret, state, cc, sc, ap, config, _) and
|
||||
exists(RetNodeEx ret |
|
||||
pathNode(_, ret, state, cc, sc, ap, config, _) and
|
||||
kind = ret.getKind() and
|
||||
apa = ap.getApprox() and
|
||||
parameterFlowThroughAllowed(sc.getParamNode(), kind)
|
||||
@@ -4212,17 +4216,15 @@ private module FlowExploration {
|
||||
ap = TRevPartialNil() and
|
||||
exists(config.explorationLimit())
|
||||
or
|
||||
exists(PartialPathNodeRev mid |
|
||||
revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and
|
||||
not clearsContentEx(node, ap.getHead()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead())
|
||||
) and
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
|
||||
)
|
||||
revPartialPathStep(_, node, state, sc1, sc2, sc3, ap, config) and
|
||||
not clearsContentEx(node, ap.getHead()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead())
|
||||
) and
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -4230,19 +4232,17 @@ private module FlowExploration {
|
||||
NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
|
||||
TSummaryCtx3 sc3, PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(PartialPathNodeFwd mid |
|
||||
partialPathStep(mid, node, state, cc, sc1, sc2, sc3, ap, config) and
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
not clearsContentEx(node, ap.getHead().getContent()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead().getContent())
|
||||
) and
|
||||
if node.asNode() instanceof CastingNode
|
||||
then compatibleTypes(node.getDataFlowType(), ap.getType())
|
||||
else any()
|
||||
)
|
||||
partialPathStep(_, node, state, cc, sc1, sc2, sc3, ap, config) and
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
not clearsContentEx(node, ap.getHead().getContent()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead().getContent())
|
||||
) and
|
||||
if node.asNode() instanceof CastingNode
|
||||
then compatibleTypes(node.getDataFlowType(), ap.getType())
|
||||
else any()
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -45,6 +45,16 @@ module Consistency {
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
/** Holds if `(c, pos, p)` should be excluded from the consistency test `uniqueParameterNodeAtPosition`. */
|
||||
predicate uniqueParameterNodeAtPositionExclude(DataFlowCallable c, ParameterPosition pos, Node p) {
|
||||
none()
|
||||
}
|
||||
|
||||
/** Holds if `(c, pos, p)` should be excluded from the consistency test `uniqueParameterNodePosition`. */
|
||||
predicate uniqueParameterNodePositionExclude(DataFlowCallable c, ParameterPosition pos, Node p) {
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
private class RelevantNode extends Node {
|
||||
@@ -101,9 +111,7 @@ module Consistency {
|
||||
exists(int c |
|
||||
c =
|
||||
strictcount(Node n |
|
||||
not exists(string filepath, int startline, int startcolumn, int endline, int endcolumn |
|
||||
n.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
) and
|
||||
not n.hasLocationInfo(_, _, _, _, _) and
|
||||
not any(ConsistencyConfiguration conf).missingLocationExclude(n)
|
||||
) and
|
||||
msg = "Nodes without location: " + c
|
||||
@@ -248,6 +256,7 @@ module Consistency {
|
||||
query predicate uniqueParameterNodeAtPosition(
|
||||
DataFlowCallable c, ParameterPosition pos, Node p, string msg
|
||||
) {
|
||||
not any(ConsistencyConfiguration conf).uniqueParameterNodeAtPositionExclude(c, pos, p) and
|
||||
isParameterNode(p, c, pos) and
|
||||
not exists(unique(Node p0 | isParameterNode(p0, c, pos))) and
|
||||
msg = "Parameters with overlapping positions."
|
||||
@@ -256,6 +265,7 @@ module Consistency {
|
||||
query predicate uniqueParameterNodePosition(
|
||||
DataFlowCallable c, ParameterPosition pos, Node p, string msg
|
||||
) {
|
||||
not any(ConsistencyConfiguration conf).uniqueParameterNodePositionExclude(c, pos, p) and
|
||||
isParameterNode(p, c, pos) and
|
||||
not exists(unique(ParameterPosition pos0 | isParameterNode(p, c, pos0))) and
|
||||
msg = "Parameter node with multiple positions."
|
||||
|
||||
@@ -218,7 +218,7 @@ private predicate allocation(Instruction array, Length length, int delta) {
|
||||
length.(VNLength).getInstruction().getConvertedResultExpression() = lengthExpr
|
||||
)
|
||||
or
|
||||
not exists(int d | deconstructMallocSizeExpr(alloc.getSizeExpr(), _, d)) and
|
||||
not deconstructMallocSizeExpr(alloc.getSizeExpr(), _, _) and
|
||||
length.(VNLength).getInstruction().getConvertedResultExpression() = alloc.getSizeExpr() and
|
||||
delta = 0
|
||||
)
|
||||
|
||||
@@ -543,9 +543,7 @@ private predicate boundedPhiCand(
|
||||
PhiInstruction phi, boolean upper, Bound b, int delta, boolean fromBackEdge, int origdelta,
|
||||
Reason reason
|
||||
) {
|
||||
exists(PhiInputOperand op |
|
||||
boundedPhiInp(phi, op, b, delta, upper, fromBackEdge, origdelta, reason)
|
||||
)
|
||||
boundedPhiInp(phi, _, b, delta, upper, fromBackEdge, origdelta, reason)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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,832 +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
|
||||
) {
|
||||
exists(SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge |
|
||||
boundedPhiInp(phi, inp, edge, 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.0
|
||||
version: 0.5.2
|
||||
groups: cpp
|
||||
dbscheme: semmlecode.cpp.dbscheme
|
||||
extractor: cpp
|
||||
|
||||
@@ -318,7 +318,7 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
|
||||
MetricFunction getMetrics() { result = this }
|
||||
|
||||
/** Holds if this function calls the function `f`. */
|
||||
predicate calls(Function f) { exists(Locatable l | this.calls(f, l)) }
|
||||
predicate calls(Function f) { this.calls(f, _) }
|
||||
|
||||
/**
|
||||
* Holds if this function calls the function `f` in the `FunctionCall`
|
||||
@@ -335,7 +335,7 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
|
||||
}
|
||||
|
||||
/** Holds if this function accesses a function or variable or enumerator `a`. */
|
||||
predicate accesses(Declaration a) { exists(Locatable l | this.accesses(a, l)) }
|
||||
predicate accesses(Declaration a) { this.accesses(a, _) }
|
||||
|
||||
/**
|
||||
* Holds if this function accesses a function or variable or enumerator `a`
|
||||
|
||||
@@ -10,12 +10,14 @@ import semmle.code.cpp.File
|
||||
*/
|
||||
class Location extends @location {
|
||||
/** Gets the container corresponding to this location. */
|
||||
pragma[nomagic]
|
||||
Container getContainer() { this.fullLocationInfo(result, _, _, _, _) }
|
||||
|
||||
/** Gets the file corresponding to this location, if any. */
|
||||
File getFile() { result = this.getContainer() }
|
||||
|
||||
/** Gets the 1-based line number (inclusive) where this location starts. */
|
||||
pragma[nomagic]
|
||||
int getStartLine() { this.fullLocationInfo(_, result, _, _, _) }
|
||||
|
||||
/** Gets the 1-based column number (inclusive) where this location starts. */
|
||||
|
||||
@@ -816,6 +816,12 @@ private predicate floatingPointTypeMapping(
|
||||
or
|
||||
// _Float128x
|
||||
kind = 50 and base = 2 and domain = TRealDomain() and realKind = 50 and extended = true
|
||||
or
|
||||
// _Float16
|
||||
kind = 52 and base = 2 and domain = TRealDomain() and realKind = 52 and extended = false
|
||||
or
|
||||
// _Complex _Float16
|
||||
kind = 53 and base = 2 and domain = TComplexDomain() and realKind = 52 and extended = false
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -33,7 +33,7 @@ DependencyOptions getDependencyOptions() { any() }
|
||||
class DependsSource extends Element {
|
||||
DependsSource() {
|
||||
// not inside a template instantiation
|
||||
not exists(Element other | this.isFromTemplateInstantiation(other)) or
|
||||
not this.isFromTemplateInstantiation(_) or
|
||||
// allow DeclarationEntrys of template specializations
|
||||
this.(DeclarationEntry).getDeclaration().(Function).isConstructedFrom(_) or
|
||||
this.(DeclarationEntry).getDeclaration().(Class).isConstructedFrom(_)
|
||||
|
||||
@@ -69,12 +69,9 @@ predicate functionContainsDisabledCode(Function f) {
|
||||
*/
|
||||
predicate functionContainsPreprocCode(Function f) {
|
||||
// `f` contains a preprocessor branch
|
||||
exists(
|
||||
PreprocessorBranchDirective pbd, string file, int pbdStartLine, int fBlockStartLine,
|
||||
int fBlockEndLine
|
||||
|
|
||||
exists(string file, int pbdStartLine, int fBlockStartLine, int fBlockEndLine |
|
||||
functionLocation(f, file, fBlockStartLine, fBlockEndLine) and
|
||||
pbdLocation(pbd, file, pbdStartLine) and
|
||||
pbdLocation(_, file, pbdStartLine) and
|
||||
pbdStartLine <= fBlockEndLine and
|
||||
pbdStartLine >= fBlockStartLine
|
||||
)
|
||||
|
||||
@@ -244,9 +244,7 @@ class ScanfFormatLiteral extends Expr {
|
||||
/**
|
||||
* Gets the maximum width option of the nth input (empty string if none is given).
|
||||
*/
|
||||
string getMaxWidthOpt(int n) {
|
||||
exists(string spec, string len, string conv | this.parseConvSpec(n, spec, result, len, conv))
|
||||
}
|
||||
string getMaxWidthOpt(int n) { this.parseConvSpec(n, _, result, _, _) }
|
||||
|
||||
/**
|
||||
* Gets the maximum width of the nth input.
|
||||
@@ -256,18 +254,12 @@ class ScanfFormatLiteral extends Expr {
|
||||
/**
|
||||
* Gets the length flag of the nth conversion specifier.
|
||||
*/
|
||||
string getLength(int n) {
|
||||
exists(string spec, string width, string conv |
|
||||
this.parseConvSpec(n, spec, width, result, conv)
|
||||
)
|
||||
}
|
||||
string getLength(int n) { this.parseConvSpec(n, _, _, result, _) }
|
||||
|
||||
/**
|
||||
* Gets the conversion character of the nth conversion specifier.
|
||||
*/
|
||||
string getConversionChar(int n) {
|
||||
exists(string spec, string width, string len | this.parseConvSpec(n, spec, width, len, result))
|
||||
}
|
||||
string getConversionChar(int n) { this.parseConvSpec(n, _, _, _, result) }
|
||||
|
||||
/**
|
||||
* Gets the maximum length of the string that can be produced by the nth
|
||||
|
||||
@@ -54,7 +54,7 @@ class SubBasicBlock extends ControlFlowNodeBase {
|
||||
* only condition under which a `SubBasicBlock` may have multiple
|
||||
* predecessors.
|
||||
*/
|
||||
predicate firstInBB() { exists(BasicBlock bb | this.getRankInBasicBlock(bb) = 1) }
|
||||
predicate firstInBB() { this.getRankInBasicBlock(_) = 1 }
|
||||
|
||||
/**
|
||||
* Holds if this `SubBasicBlock` comes last in its basic block. This is the
|
||||
|
||||
@@ -441,8 +441,8 @@ library class ExprEvaluator extends int {
|
||||
req = mid.(AssignExpr).getRValue()
|
||||
)
|
||||
or
|
||||
exists(VariableAccess va, Variable v, boolean sub1 |
|
||||
this.interestingVariableAccess(e, va, v, sub1) and
|
||||
exists(Variable v, boolean sub1 |
|
||||
this.interestingVariableAccess(e, _, v, sub1) and
|
||||
req = v.getAnAssignedValue() and
|
||||
(sub1 = true implies not this.ignoreVariableAssignment(e, v, req)) and
|
||||
sub = false
|
||||
@@ -876,7 +876,7 @@ private predicate nonAnalyzableVariableDefinition(Variable v, StmtParent def) {
|
||||
* empirically to have effect only on a few rare and pathological examples.
|
||||
*/
|
||||
private predicate tractableVariable(Variable v) {
|
||||
not exists(StmtParent def | nonAnalyzableVariableDefinition(v, def)) or
|
||||
not nonAnalyzableVariableDefinition(v, _) or
|
||||
strictcount(StmtParent def | nonAnalyzableVariableDefinition(v, def)) < 1000
|
||||
}
|
||||
|
||||
|
||||
@@ -876,9 +876,9 @@ private module Stage1 implements StageSig {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowOut(ReturnPosition pos, Configuration config) {
|
||||
exists(DataFlowCall call, NodeEx out |
|
||||
exists(NodeEx out |
|
||||
revFlow(out, _, config) and
|
||||
viableReturnPosOutNodeCandFwd1(call, pos, out, config)
|
||||
viableReturnPosOutNodeCandFwd1(_, pos, out, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1487,6 +1487,10 @@ private module MkStage<StageSig PrevStage> {
|
||||
PrevStage::readStepCand(node1, _, _, config)
|
||||
}
|
||||
|
||||
bindingset[ap, c]
|
||||
pragma[inline_late]
|
||||
private predicate hasHeadContent(Ap ap, Content c) { getHeadContent(ap) = c }
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead(
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
@@ -1494,7 +1498,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
) {
|
||||
fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
getHeadContent(ap) = c
|
||||
hasHeadContent(ap, c)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1731,8 +1735,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ParamNodeEx p, ReturnPosition pos, Ap innerReturnAp |
|
||||
revFlowThrough(call, returnCtx, p, state, pos, returnAp, ap, innerReturnAp, config) and
|
||||
exists(DataFlowCall call, ParamNodeEx p, Ap innerReturnAp |
|
||||
revFlowThrough(call, returnCtx, p, state, _, returnAp, ap, innerReturnAp, config) and
|
||||
flowThroughIntoCall(call, node, p, _, ap, innerReturnAp, config)
|
||||
)
|
||||
or
|
||||
@@ -1901,8 +1905,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
|
||||
exists(RetNodeEx ret, ReturnPosition pos |
|
||||
returnFlowsThrough(ret, pos, _, _, p, ap, _, config) and
|
||||
exists(ReturnPosition pos |
|
||||
returnFlowsThrough(_, pos, _, _, p, ap, _, config) and
|
||||
parameterFlowsThroughRev(p, ap, pos, _, config)
|
||||
)
|
||||
}
|
||||
@@ -1923,8 +1927,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, ReturnPosition pos, Ap innerReturnAp |
|
||||
revFlowThrough(call, returnCtx, p, state, pos, returnAp, ap, innerReturnAp, config) and
|
||||
exists(ParamNodeEx p, Ap innerReturnAp |
|
||||
revFlowThrough(call, returnCtx, p, state, _, returnAp, ap, innerReturnAp, config) and
|
||||
flowThroughIntoCall(call, arg, p, _, ap, innerReturnAp, config)
|
||||
)
|
||||
}
|
||||
@@ -3749,8 +3753,8 @@ private predicate paramFlowsThrough(
|
||||
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
|
||||
AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid, RetNodeEx ret |
|
||||
pathNode(mid, ret, state, cc, sc, ap, config, _) and
|
||||
exists(RetNodeEx ret |
|
||||
pathNode(_, ret, state, cc, sc, ap, config, _) and
|
||||
kind = ret.getKind() and
|
||||
apa = ap.getApprox() and
|
||||
parameterFlowThroughAllowed(sc.getParamNode(), kind)
|
||||
@@ -4212,17 +4216,15 @@ private module FlowExploration {
|
||||
ap = TRevPartialNil() and
|
||||
exists(config.explorationLimit())
|
||||
or
|
||||
exists(PartialPathNodeRev mid |
|
||||
revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and
|
||||
not clearsContentEx(node, ap.getHead()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead())
|
||||
) and
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
|
||||
)
|
||||
revPartialPathStep(_, node, state, sc1, sc2, sc3, ap, config) and
|
||||
not clearsContentEx(node, ap.getHead()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead())
|
||||
) and
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -4230,19 +4232,17 @@ private module FlowExploration {
|
||||
NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
|
||||
TSummaryCtx3 sc3, PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(PartialPathNodeFwd mid |
|
||||
partialPathStep(mid, node, state, cc, sc1, sc2, sc3, ap, config) and
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
not clearsContentEx(node, ap.getHead().getContent()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead().getContent())
|
||||
) and
|
||||
if node.asNode() instanceof CastingNode
|
||||
then compatibleTypes(node.getDataFlowType(), ap.getType())
|
||||
else any()
|
||||
)
|
||||
partialPathStep(_, node, state, cc, sc1, sc2, sc3, ap, config) and
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
not clearsContentEx(node, ap.getHead().getContent()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead().getContent())
|
||||
) and
|
||||
if node.asNode() instanceof CastingNode
|
||||
then compatibleTypes(node.getDataFlowType(), ap.getType())
|
||||
else any()
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -876,9 +876,9 @@ private module Stage1 implements StageSig {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowOut(ReturnPosition pos, Configuration config) {
|
||||
exists(DataFlowCall call, NodeEx out |
|
||||
exists(NodeEx out |
|
||||
revFlow(out, _, config) and
|
||||
viableReturnPosOutNodeCandFwd1(call, pos, out, config)
|
||||
viableReturnPosOutNodeCandFwd1(_, pos, out, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1487,6 +1487,10 @@ private module MkStage<StageSig PrevStage> {
|
||||
PrevStage::readStepCand(node1, _, _, config)
|
||||
}
|
||||
|
||||
bindingset[ap, c]
|
||||
pragma[inline_late]
|
||||
private predicate hasHeadContent(Ap ap, Content c) { getHeadContent(ap) = c }
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead(
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
@@ -1494,7 +1498,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
) {
|
||||
fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
getHeadContent(ap) = c
|
||||
hasHeadContent(ap, c)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1731,8 +1735,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ParamNodeEx p, ReturnPosition pos, Ap innerReturnAp |
|
||||
revFlowThrough(call, returnCtx, p, state, pos, returnAp, ap, innerReturnAp, config) and
|
||||
exists(DataFlowCall call, ParamNodeEx p, Ap innerReturnAp |
|
||||
revFlowThrough(call, returnCtx, p, state, _, returnAp, ap, innerReturnAp, config) and
|
||||
flowThroughIntoCall(call, node, p, _, ap, innerReturnAp, config)
|
||||
)
|
||||
or
|
||||
@@ -1901,8 +1905,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
|
||||
exists(RetNodeEx ret, ReturnPosition pos |
|
||||
returnFlowsThrough(ret, pos, _, _, p, ap, _, config) and
|
||||
exists(ReturnPosition pos |
|
||||
returnFlowsThrough(_, pos, _, _, p, ap, _, config) and
|
||||
parameterFlowsThroughRev(p, ap, pos, _, config)
|
||||
)
|
||||
}
|
||||
@@ -1923,8 +1927,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, ReturnPosition pos, Ap innerReturnAp |
|
||||
revFlowThrough(call, returnCtx, p, state, pos, returnAp, ap, innerReturnAp, config) and
|
||||
exists(ParamNodeEx p, Ap innerReturnAp |
|
||||
revFlowThrough(call, returnCtx, p, state, _, returnAp, ap, innerReturnAp, config) and
|
||||
flowThroughIntoCall(call, arg, p, _, ap, innerReturnAp, config)
|
||||
)
|
||||
}
|
||||
@@ -3749,8 +3753,8 @@ private predicate paramFlowsThrough(
|
||||
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
|
||||
AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid, RetNodeEx ret |
|
||||
pathNode(mid, ret, state, cc, sc, ap, config, _) and
|
||||
exists(RetNodeEx ret |
|
||||
pathNode(_, ret, state, cc, sc, ap, config, _) and
|
||||
kind = ret.getKind() and
|
||||
apa = ap.getApprox() and
|
||||
parameterFlowThroughAllowed(sc.getParamNode(), kind)
|
||||
@@ -4212,17 +4216,15 @@ private module FlowExploration {
|
||||
ap = TRevPartialNil() and
|
||||
exists(config.explorationLimit())
|
||||
or
|
||||
exists(PartialPathNodeRev mid |
|
||||
revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and
|
||||
not clearsContentEx(node, ap.getHead()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead())
|
||||
) and
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
|
||||
)
|
||||
revPartialPathStep(_, node, state, sc1, sc2, sc3, ap, config) and
|
||||
not clearsContentEx(node, ap.getHead()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead())
|
||||
) and
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -4230,19 +4232,17 @@ private module FlowExploration {
|
||||
NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
|
||||
TSummaryCtx3 sc3, PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(PartialPathNodeFwd mid |
|
||||
partialPathStep(mid, node, state, cc, sc1, sc2, sc3, ap, config) and
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
not clearsContentEx(node, ap.getHead().getContent()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead().getContent())
|
||||
) and
|
||||
if node.asNode() instanceof CastingNode
|
||||
then compatibleTypes(node.getDataFlowType(), ap.getType())
|
||||
else any()
|
||||
)
|
||||
partialPathStep(_, node, state, cc, sc1, sc2, sc3, ap, config) and
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
not clearsContentEx(node, ap.getHead().getContent()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead().getContent())
|
||||
) and
|
||||
if node.asNode() instanceof CastingNode
|
||||
then compatibleTypes(node.getDataFlowType(), ap.getType())
|
||||
else any()
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -876,9 +876,9 @@ private module Stage1 implements StageSig {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowOut(ReturnPosition pos, Configuration config) {
|
||||
exists(DataFlowCall call, NodeEx out |
|
||||
exists(NodeEx out |
|
||||
revFlow(out, _, config) and
|
||||
viableReturnPosOutNodeCandFwd1(call, pos, out, config)
|
||||
viableReturnPosOutNodeCandFwd1(_, pos, out, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1487,6 +1487,10 @@ private module MkStage<StageSig PrevStage> {
|
||||
PrevStage::readStepCand(node1, _, _, config)
|
||||
}
|
||||
|
||||
bindingset[ap, c]
|
||||
pragma[inline_late]
|
||||
private predicate hasHeadContent(Ap ap, Content c) { getHeadContent(ap) = c }
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead(
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
@@ -1494,7 +1498,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
) {
|
||||
fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
getHeadContent(ap) = c
|
||||
hasHeadContent(ap, c)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1731,8 +1735,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ParamNodeEx p, ReturnPosition pos, Ap innerReturnAp |
|
||||
revFlowThrough(call, returnCtx, p, state, pos, returnAp, ap, innerReturnAp, config) and
|
||||
exists(DataFlowCall call, ParamNodeEx p, Ap innerReturnAp |
|
||||
revFlowThrough(call, returnCtx, p, state, _, returnAp, ap, innerReturnAp, config) and
|
||||
flowThroughIntoCall(call, node, p, _, ap, innerReturnAp, config)
|
||||
)
|
||||
or
|
||||
@@ -1901,8 +1905,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
|
||||
exists(RetNodeEx ret, ReturnPosition pos |
|
||||
returnFlowsThrough(ret, pos, _, _, p, ap, _, config) and
|
||||
exists(ReturnPosition pos |
|
||||
returnFlowsThrough(_, pos, _, _, p, ap, _, config) and
|
||||
parameterFlowsThroughRev(p, ap, pos, _, config)
|
||||
)
|
||||
}
|
||||
@@ -1923,8 +1927,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, ReturnPosition pos, Ap innerReturnAp |
|
||||
revFlowThrough(call, returnCtx, p, state, pos, returnAp, ap, innerReturnAp, config) and
|
||||
exists(ParamNodeEx p, Ap innerReturnAp |
|
||||
revFlowThrough(call, returnCtx, p, state, _, returnAp, ap, innerReturnAp, config) and
|
||||
flowThroughIntoCall(call, arg, p, _, ap, innerReturnAp, config)
|
||||
)
|
||||
}
|
||||
@@ -3749,8 +3753,8 @@ private predicate paramFlowsThrough(
|
||||
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
|
||||
AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid, RetNodeEx ret |
|
||||
pathNode(mid, ret, state, cc, sc, ap, config, _) and
|
||||
exists(RetNodeEx ret |
|
||||
pathNode(_, ret, state, cc, sc, ap, config, _) and
|
||||
kind = ret.getKind() and
|
||||
apa = ap.getApprox() and
|
||||
parameterFlowThroughAllowed(sc.getParamNode(), kind)
|
||||
@@ -4212,17 +4216,15 @@ private module FlowExploration {
|
||||
ap = TRevPartialNil() and
|
||||
exists(config.explorationLimit())
|
||||
or
|
||||
exists(PartialPathNodeRev mid |
|
||||
revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and
|
||||
not clearsContentEx(node, ap.getHead()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead())
|
||||
) and
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
|
||||
)
|
||||
revPartialPathStep(_, node, state, sc1, sc2, sc3, ap, config) and
|
||||
not clearsContentEx(node, ap.getHead()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead())
|
||||
) and
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -4230,19 +4232,17 @@ private module FlowExploration {
|
||||
NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
|
||||
TSummaryCtx3 sc3, PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(PartialPathNodeFwd mid |
|
||||
partialPathStep(mid, node, state, cc, sc1, sc2, sc3, ap, config) and
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
not clearsContentEx(node, ap.getHead().getContent()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead().getContent())
|
||||
) and
|
||||
if node.asNode() instanceof CastingNode
|
||||
then compatibleTypes(node.getDataFlowType(), ap.getType())
|
||||
else any()
|
||||
)
|
||||
partialPathStep(_, node, state, cc, sc1, sc2, sc3, ap, config) and
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
not clearsContentEx(node, ap.getHead().getContent()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead().getContent())
|
||||
) and
|
||||
if node.asNode() instanceof CastingNode
|
||||
then compatibleTypes(node.getDataFlowType(), ap.getType())
|
||||
else any()
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -876,9 +876,9 @@ private module Stage1 implements StageSig {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowOut(ReturnPosition pos, Configuration config) {
|
||||
exists(DataFlowCall call, NodeEx out |
|
||||
exists(NodeEx out |
|
||||
revFlow(out, _, config) and
|
||||
viableReturnPosOutNodeCandFwd1(call, pos, out, config)
|
||||
viableReturnPosOutNodeCandFwd1(_, pos, out, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1487,6 +1487,10 @@ private module MkStage<StageSig PrevStage> {
|
||||
PrevStage::readStepCand(node1, _, _, config)
|
||||
}
|
||||
|
||||
bindingset[ap, c]
|
||||
pragma[inline_late]
|
||||
private predicate hasHeadContent(Ap ap, Content c) { getHeadContent(ap) = c }
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead(
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
@@ -1494,7 +1498,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
) {
|
||||
fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
getHeadContent(ap) = c
|
||||
hasHeadContent(ap, c)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1731,8 +1735,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ParamNodeEx p, ReturnPosition pos, Ap innerReturnAp |
|
||||
revFlowThrough(call, returnCtx, p, state, pos, returnAp, ap, innerReturnAp, config) and
|
||||
exists(DataFlowCall call, ParamNodeEx p, Ap innerReturnAp |
|
||||
revFlowThrough(call, returnCtx, p, state, _, returnAp, ap, innerReturnAp, config) and
|
||||
flowThroughIntoCall(call, node, p, _, ap, innerReturnAp, config)
|
||||
)
|
||||
or
|
||||
@@ -1901,8 +1905,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
|
||||
exists(RetNodeEx ret, ReturnPosition pos |
|
||||
returnFlowsThrough(ret, pos, _, _, p, ap, _, config) and
|
||||
exists(ReturnPosition pos |
|
||||
returnFlowsThrough(_, pos, _, _, p, ap, _, config) and
|
||||
parameterFlowsThroughRev(p, ap, pos, _, config)
|
||||
)
|
||||
}
|
||||
@@ -1923,8 +1927,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, ReturnPosition pos, Ap innerReturnAp |
|
||||
revFlowThrough(call, returnCtx, p, state, pos, returnAp, ap, innerReturnAp, config) and
|
||||
exists(ParamNodeEx p, Ap innerReturnAp |
|
||||
revFlowThrough(call, returnCtx, p, state, _, returnAp, ap, innerReturnAp, config) and
|
||||
flowThroughIntoCall(call, arg, p, _, ap, innerReturnAp, config)
|
||||
)
|
||||
}
|
||||
@@ -3749,8 +3753,8 @@ private predicate paramFlowsThrough(
|
||||
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
|
||||
AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid, RetNodeEx ret |
|
||||
pathNode(mid, ret, state, cc, sc, ap, config, _) and
|
||||
exists(RetNodeEx ret |
|
||||
pathNode(_, ret, state, cc, sc, ap, config, _) and
|
||||
kind = ret.getKind() and
|
||||
apa = ap.getApprox() and
|
||||
parameterFlowThroughAllowed(sc.getParamNode(), kind)
|
||||
@@ -4212,17 +4216,15 @@ private module FlowExploration {
|
||||
ap = TRevPartialNil() and
|
||||
exists(config.explorationLimit())
|
||||
or
|
||||
exists(PartialPathNodeRev mid |
|
||||
revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and
|
||||
not clearsContentEx(node, ap.getHead()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead())
|
||||
) and
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
|
||||
)
|
||||
revPartialPathStep(_, node, state, sc1, sc2, sc3, ap, config) and
|
||||
not clearsContentEx(node, ap.getHead()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead())
|
||||
) and
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -4230,19 +4232,17 @@ private module FlowExploration {
|
||||
NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
|
||||
TSummaryCtx3 sc3, PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(PartialPathNodeFwd mid |
|
||||
partialPathStep(mid, node, state, cc, sc1, sc2, sc3, ap, config) and
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
not clearsContentEx(node, ap.getHead().getContent()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead().getContent())
|
||||
) and
|
||||
if node.asNode() instanceof CastingNode
|
||||
then compatibleTypes(node.getDataFlowType(), ap.getType())
|
||||
else any()
|
||||
)
|
||||
partialPathStep(_, node, state, cc, sc1, sc2, sc3, ap, config) and
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
not clearsContentEx(node, ap.getHead().getContent()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead().getContent())
|
||||
) and
|
||||
if node.asNode() instanceof CastingNode
|
||||
then compatibleTypes(node.getDataFlowType(), ap.getType())
|
||||
else any()
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -45,6 +45,16 @@ module Consistency {
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
/** Holds if `(c, pos, p)` should be excluded from the consistency test `uniqueParameterNodeAtPosition`. */
|
||||
predicate uniqueParameterNodeAtPositionExclude(DataFlowCallable c, ParameterPosition pos, Node p) {
|
||||
none()
|
||||
}
|
||||
|
||||
/** Holds if `(c, pos, p)` should be excluded from the consistency test `uniqueParameterNodePosition`. */
|
||||
predicate uniqueParameterNodePositionExclude(DataFlowCallable c, ParameterPosition pos, Node p) {
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
private class RelevantNode extends Node {
|
||||
@@ -101,9 +111,7 @@ module Consistency {
|
||||
exists(int c |
|
||||
c =
|
||||
strictcount(Node n |
|
||||
not exists(string filepath, int startline, int startcolumn, int endline, int endcolumn |
|
||||
n.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
) and
|
||||
not n.hasLocationInfo(_, _, _, _, _) and
|
||||
not any(ConsistencyConfiguration conf).missingLocationExclude(n)
|
||||
) and
|
||||
msg = "Nodes without location: " + c
|
||||
@@ -248,6 +256,7 @@ module Consistency {
|
||||
query predicate uniqueParameterNodeAtPosition(
|
||||
DataFlowCallable c, ParameterPosition pos, Node p, string msg
|
||||
) {
|
||||
not any(ConsistencyConfiguration conf).uniqueParameterNodeAtPositionExclude(c, pos, p) and
|
||||
isParameterNode(p, c, pos) and
|
||||
not exists(unique(Node p0 | isParameterNode(p0, c, pos))) and
|
||||
msg = "Parameters with overlapping positions."
|
||||
@@ -256,6 +265,7 @@ module Consistency {
|
||||
query predicate uniqueParameterNodePosition(
|
||||
DataFlowCallable c, ParameterPosition pos, Node p, string msg
|
||||
) {
|
||||
not any(ConsistencyConfiguration conf).uniqueParameterNodePositionExclude(c, pos, p) and
|
||||
isParameterNode(p, c, pos) and
|
||||
not exists(unique(ParameterPosition pos0 | isParameterNode(p, c, pos0))) and
|
||||
msg = "Parameter node with multiple positions."
|
||||
|
||||
@@ -876,9 +876,9 @@ private module Stage1 implements StageSig {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowOut(ReturnPosition pos, Configuration config) {
|
||||
exists(DataFlowCall call, NodeEx out |
|
||||
exists(NodeEx out |
|
||||
revFlow(out, _, config) and
|
||||
viableReturnPosOutNodeCandFwd1(call, pos, out, config)
|
||||
viableReturnPosOutNodeCandFwd1(_, pos, out, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1487,6 +1487,10 @@ private module MkStage<StageSig PrevStage> {
|
||||
PrevStage::readStepCand(node1, _, _, config)
|
||||
}
|
||||
|
||||
bindingset[ap, c]
|
||||
pragma[inline_late]
|
||||
private predicate hasHeadContent(Ap ap, Content c) { getHeadContent(ap) = c }
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead(
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
@@ -1494,7 +1498,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
) {
|
||||
fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
getHeadContent(ap) = c
|
||||
hasHeadContent(ap, c)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1731,8 +1735,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ParamNodeEx p, ReturnPosition pos, Ap innerReturnAp |
|
||||
revFlowThrough(call, returnCtx, p, state, pos, returnAp, ap, innerReturnAp, config) and
|
||||
exists(DataFlowCall call, ParamNodeEx p, Ap innerReturnAp |
|
||||
revFlowThrough(call, returnCtx, p, state, _, returnAp, ap, innerReturnAp, config) and
|
||||
flowThroughIntoCall(call, node, p, _, ap, innerReturnAp, config)
|
||||
)
|
||||
or
|
||||
@@ -1901,8 +1905,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
|
||||
exists(RetNodeEx ret, ReturnPosition pos |
|
||||
returnFlowsThrough(ret, pos, _, _, p, ap, _, config) and
|
||||
exists(ReturnPosition pos |
|
||||
returnFlowsThrough(_, pos, _, _, p, ap, _, config) and
|
||||
parameterFlowsThroughRev(p, ap, pos, _, config)
|
||||
)
|
||||
}
|
||||
@@ -1923,8 +1927,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, ReturnPosition pos, Ap innerReturnAp |
|
||||
revFlowThrough(call, returnCtx, p, state, pos, returnAp, ap, innerReturnAp, config) and
|
||||
exists(ParamNodeEx p, Ap innerReturnAp |
|
||||
revFlowThrough(call, returnCtx, p, state, _, returnAp, ap, innerReturnAp, config) and
|
||||
flowThroughIntoCall(call, arg, p, _, ap, innerReturnAp, config)
|
||||
)
|
||||
}
|
||||
@@ -3749,8 +3753,8 @@ private predicate paramFlowsThrough(
|
||||
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
|
||||
AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid, RetNodeEx ret |
|
||||
pathNode(mid, ret, state, cc, sc, ap, config, _) and
|
||||
exists(RetNodeEx ret |
|
||||
pathNode(_, ret, state, cc, sc, ap, config, _) and
|
||||
kind = ret.getKind() and
|
||||
apa = ap.getApprox() and
|
||||
parameterFlowThroughAllowed(sc.getParamNode(), kind)
|
||||
@@ -4212,17 +4216,15 @@ private module FlowExploration {
|
||||
ap = TRevPartialNil() and
|
||||
exists(config.explorationLimit())
|
||||
or
|
||||
exists(PartialPathNodeRev mid |
|
||||
revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and
|
||||
not clearsContentEx(node, ap.getHead()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead())
|
||||
) and
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
|
||||
)
|
||||
revPartialPathStep(_, node, state, sc1, sc2, sc3, ap, config) and
|
||||
not clearsContentEx(node, ap.getHead()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead())
|
||||
) and
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -4230,19 +4232,17 @@ private module FlowExploration {
|
||||
NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
|
||||
TSummaryCtx3 sc3, PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(PartialPathNodeFwd mid |
|
||||
partialPathStep(mid, node, state, cc, sc1, sc2, sc3, ap, config) and
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
not clearsContentEx(node, ap.getHead().getContent()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead().getContent())
|
||||
) and
|
||||
if node.asNode() instanceof CastingNode
|
||||
then compatibleTypes(node.getDataFlowType(), ap.getType())
|
||||
else any()
|
||||
)
|
||||
partialPathStep(_, node, state, cc, sc1, sc2, sc3, ap, config) and
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
not clearsContentEx(node, ap.getHead().getContent()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead().getContent())
|
||||
) and
|
||||
if node.asNode() instanceof CastingNode
|
||||
then compatibleTypes(node.getDataFlowType(), ap.getType())
|
||||
else any()
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -450,10 +450,8 @@ module FlowVar_internal {
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
exists(Expr e |
|
||||
this.definedByExpr(e, _) and
|
||||
result = "assignment to " + v
|
||||
)
|
||||
this.definedByExpr(_, _) and
|
||||
result = "assignment to " + v
|
||||
or
|
||||
this.definedByInitialValue(_) and
|
||||
result = "initial value of " + v
|
||||
|
||||
@@ -54,7 +54,7 @@ class SubBasicBlock extends ControlFlowNodeBase {
|
||||
* only condition under which a `SubBasicBlock` may have multiple
|
||||
* predecessors.
|
||||
*/
|
||||
predicate firstInBB() { exists(BasicBlock bb | this.getRankInBasicBlock(bb) = 1) }
|
||||
predicate firstInBB() { this.getRankInBasicBlock(_) = 1 }
|
||||
|
||||
/**
|
||||
* Holds if this `SubBasicBlock` comes last in its basic block. This is the
|
||||
|
||||
@@ -70,8 +70,8 @@ module VirtualDispatch {
|
||||
* that is, `c` or one of its supertypes overrides `f`.
|
||||
*/
|
||||
private predicate cannotInherit(Class c, MemberFunction f) {
|
||||
exists(Class overridingType, MemberFunction override |
|
||||
cannotInheritHelper(c, f, overridingType, override) and
|
||||
exists(MemberFunction override |
|
||||
cannotInheritHelper(c, f, _, override) and
|
||||
override.overrides+(f)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ class Expr extends StmtParent, @expr {
|
||||
Declaration getEnclosingDeclaration() { result = exprEnclosingElement(this) }
|
||||
|
||||
/** Gets a child of this expression. */
|
||||
Expr getAChild() { exists(int n | result = this.getChild(n)) }
|
||||
Expr getAChild() { result = this.getChild(_) }
|
||||
|
||||
/** Gets the parent of this expression, if any. */
|
||||
Element getParent() { exprparents(underlyingElement(this), _, unresolveElement(result)) }
|
||||
|
||||
@@ -876,9 +876,9 @@ private module Stage1 implements StageSig {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowOut(ReturnPosition pos, Configuration config) {
|
||||
exists(DataFlowCall call, NodeEx out |
|
||||
exists(NodeEx out |
|
||||
revFlow(out, _, config) and
|
||||
viableReturnPosOutNodeCandFwd1(call, pos, out, config)
|
||||
viableReturnPosOutNodeCandFwd1(_, pos, out, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1487,6 +1487,10 @@ private module MkStage<StageSig PrevStage> {
|
||||
PrevStage::readStepCand(node1, _, _, config)
|
||||
}
|
||||
|
||||
bindingset[ap, c]
|
||||
pragma[inline_late]
|
||||
private predicate hasHeadContent(Ap ap, Content c) { getHeadContent(ap) = c }
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead(
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
@@ -1494,7 +1498,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
) {
|
||||
fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
getHeadContent(ap) = c
|
||||
hasHeadContent(ap, c)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1731,8 +1735,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ParamNodeEx p, ReturnPosition pos, Ap innerReturnAp |
|
||||
revFlowThrough(call, returnCtx, p, state, pos, returnAp, ap, innerReturnAp, config) and
|
||||
exists(DataFlowCall call, ParamNodeEx p, Ap innerReturnAp |
|
||||
revFlowThrough(call, returnCtx, p, state, _, returnAp, ap, innerReturnAp, config) and
|
||||
flowThroughIntoCall(call, node, p, _, ap, innerReturnAp, config)
|
||||
)
|
||||
or
|
||||
@@ -1901,8 +1905,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
|
||||
exists(RetNodeEx ret, ReturnPosition pos |
|
||||
returnFlowsThrough(ret, pos, _, _, p, ap, _, config) and
|
||||
exists(ReturnPosition pos |
|
||||
returnFlowsThrough(_, pos, _, _, p, ap, _, config) and
|
||||
parameterFlowsThroughRev(p, ap, pos, _, config)
|
||||
)
|
||||
}
|
||||
@@ -1923,8 +1927,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, ReturnPosition pos, Ap innerReturnAp |
|
||||
revFlowThrough(call, returnCtx, p, state, pos, returnAp, ap, innerReturnAp, config) and
|
||||
exists(ParamNodeEx p, Ap innerReturnAp |
|
||||
revFlowThrough(call, returnCtx, p, state, _, returnAp, ap, innerReturnAp, config) and
|
||||
flowThroughIntoCall(call, arg, p, _, ap, innerReturnAp, config)
|
||||
)
|
||||
}
|
||||
@@ -3749,8 +3753,8 @@ private predicate paramFlowsThrough(
|
||||
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
|
||||
AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid, RetNodeEx ret |
|
||||
pathNode(mid, ret, state, cc, sc, ap, config, _) and
|
||||
exists(RetNodeEx ret |
|
||||
pathNode(_, ret, state, cc, sc, ap, config, _) and
|
||||
kind = ret.getKind() and
|
||||
apa = ap.getApprox() and
|
||||
parameterFlowThroughAllowed(sc.getParamNode(), kind)
|
||||
@@ -4212,17 +4216,15 @@ private module FlowExploration {
|
||||
ap = TRevPartialNil() and
|
||||
exists(config.explorationLimit())
|
||||
or
|
||||
exists(PartialPathNodeRev mid |
|
||||
revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and
|
||||
not clearsContentEx(node, ap.getHead()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead())
|
||||
) and
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
|
||||
)
|
||||
revPartialPathStep(_, node, state, sc1, sc2, sc3, ap, config) and
|
||||
not clearsContentEx(node, ap.getHead()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead())
|
||||
) and
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -4230,19 +4232,17 @@ private module FlowExploration {
|
||||
NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
|
||||
TSummaryCtx3 sc3, PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(PartialPathNodeFwd mid |
|
||||
partialPathStep(mid, node, state, cc, sc1, sc2, sc3, ap, config) and
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
not clearsContentEx(node, ap.getHead().getContent()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead().getContent())
|
||||
) and
|
||||
if node.asNode() instanceof CastingNode
|
||||
then compatibleTypes(node.getDataFlowType(), ap.getType())
|
||||
else any()
|
||||
)
|
||||
partialPathStep(_, node, state, cc, sc1, sc2, sc3, ap, config) and
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
not clearsContentEx(node, ap.getHead().getContent()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead().getContent())
|
||||
) and
|
||||
if node.asNode() instanceof CastingNode
|
||||
then compatibleTypes(node.getDataFlowType(), ap.getType())
|
||||
else any()
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -876,9 +876,9 @@ private module Stage1 implements StageSig {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowOut(ReturnPosition pos, Configuration config) {
|
||||
exists(DataFlowCall call, NodeEx out |
|
||||
exists(NodeEx out |
|
||||
revFlow(out, _, config) and
|
||||
viableReturnPosOutNodeCandFwd1(call, pos, out, config)
|
||||
viableReturnPosOutNodeCandFwd1(_, pos, out, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1487,6 +1487,10 @@ private module MkStage<StageSig PrevStage> {
|
||||
PrevStage::readStepCand(node1, _, _, config)
|
||||
}
|
||||
|
||||
bindingset[ap, c]
|
||||
pragma[inline_late]
|
||||
private predicate hasHeadContent(Ap ap, Content c) { getHeadContent(ap) = c }
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead(
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
@@ -1494,7 +1498,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
) {
|
||||
fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
getHeadContent(ap) = c
|
||||
hasHeadContent(ap, c)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1731,8 +1735,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ParamNodeEx p, ReturnPosition pos, Ap innerReturnAp |
|
||||
revFlowThrough(call, returnCtx, p, state, pos, returnAp, ap, innerReturnAp, config) and
|
||||
exists(DataFlowCall call, ParamNodeEx p, Ap innerReturnAp |
|
||||
revFlowThrough(call, returnCtx, p, state, _, returnAp, ap, innerReturnAp, config) and
|
||||
flowThroughIntoCall(call, node, p, _, ap, innerReturnAp, config)
|
||||
)
|
||||
or
|
||||
@@ -1901,8 +1905,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
|
||||
exists(RetNodeEx ret, ReturnPosition pos |
|
||||
returnFlowsThrough(ret, pos, _, _, p, ap, _, config) and
|
||||
exists(ReturnPosition pos |
|
||||
returnFlowsThrough(_, pos, _, _, p, ap, _, config) and
|
||||
parameterFlowsThroughRev(p, ap, pos, _, config)
|
||||
)
|
||||
}
|
||||
@@ -1923,8 +1927,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, ReturnPosition pos, Ap innerReturnAp |
|
||||
revFlowThrough(call, returnCtx, p, state, pos, returnAp, ap, innerReturnAp, config) and
|
||||
exists(ParamNodeEx p, Ap innerReturnAp |
|
||||
revFlowThrough(call, returnCtx, p, state, _, returnAp, ap, innerReturnAp, config) and
|
||||
flowThroughIntoCall(call, arg, p, _, ap, innerReturnAp, config)
|
||||
)
|
||||
}
|
||||
@@ -3749,8 +3753,8 @@ private predicate paramFlowsThrough(
|
||||
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
|
||||
AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid, RetNodeEx ret |
|
||||
pathNode(mid, ret, state, cc, sc, ap, config, _) and
|
||||
exists(RetNodeEx ret |
|
||||
pathNode(_, ret, state, cc, sc, ap, config, _) and
|
||||
kind = ret.getKind() and
|
||||
apa = ap.getApprox() and
|
||||
parameterFlowThroughAllowed(sc.getParamNode(), kind)
|
||||
@@ -4212,17 +4216,15 @@ private module FlowExploration {
|
||||
ap = TRevPartialNil() and
|
||||
exists(config.explorationLimit())
|
||||
or
|
||||
exists(PartialPathNodeRev mid |
|
||||
revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and
|
||||
not clearsContentEx(node, ap.getHead()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead())
|
||||
) and
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
|
||||
)
|
||||
revPartialPathStep(_, node, state, sc1, sc2, sc3, ap, config) and
|
||||
not clearsContentEx(node, ap.getHead()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead())
|
||||
) and
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -4230,19 +4232,17 @@ private module FlowExploration {
|
||||
NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
|
||||
TSummaryCtx3 sc3, PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(PartialPathNodeFwd mid |
|
||||
partialPathStep(mid, node, state, cc, sc1, sc2, sc3, ap, config) and
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
not clearsContentEx(node, ap.getHead().getContent()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead().getContent())
|
||||
) and
|
||||
if node.asNode() instanceof CastingNode
|
||||
then compatibleTypes(node.getDataFlowType(), ap.getType())
|
||||
else any()
|
||||
)
|
||||
partialPathStep(_, node, state, cc, sc1, sc2, sc3, ap, config) and
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
not clearsContentEx(node, ap.getHead().getContent()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead().getContent())
|
||||
) and
|
||||
if node.asNode() instanceof CastingNode
|
||||
then compatibleTypes(node.getDataFlowType(), ap.getType())
|
||||
else any()
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -876,9 +876,9 @@ private module Stage1 implements StageSig {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowOut(ReturnPosition pos, Configuration config) {
|
||||
exists(DataFlowCall call, NodeEx out |
|
||||
exists(NodeEx out |
|
||||
revFlow(out, _, config) and
|
||||
viableReturnPosOutNodeCandFwd1(call, pos, out, config)
|
||||
viableReturnPosOutNodeCandFwd1(_, pos, out, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1487,6 +1487,10 @@ private module MkStage<StageSig PrevStage> {
|
||||
PrevStage::readStepCand(node1, _, _, config)
|
||||
}
|
||||
|
||||
bindingset[ap, c]
|
||||
pragma[inline_late]
|
||||
private predicate hasHeadContent(Ap ap, Content c) { getHeadContent(ap) = c }
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead(
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
@@ -1494,7 +1498,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
) {
|
||||
fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
getHeadContent(ap) = c
|
||||
hasHeadContent(ap, c)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1731,8 +1735,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ParamNodeEx p, ReturnPosition pos, Ap innerReturnAp |
|
||||
revFlowThrough(call, returnCtx, p, state, pos, returnAp, ap, innerReturnAp, config) and
|
||||
exists(DataFlowCall call, ParamNodeEx p, Ap innerReturnAp |
|
||||
revFlowThrough(call, returnCtx, p, state, _, returnAp, ap, innerReturnAp, config) and
|
||||
flowThroughIntoCall(call, node, p, _, ap, innerReturnAp, config)
|
||||
)
|
||||
or
|
||||
@@ -1901,8 +1905,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
|
||||
exists(RetNodeEx ret, ReturnPosition pos |
|
||||
returnFlowsThrough(ret, pos, _, _, p, ap, _, config) and
|
||||
exists(ReturnPosition pos |
|
||||
returnFlowsThrough(_, pos, _, _, p, ap, _, config) and
|
||||
parameterFlowsThroughRev(p, ap, pos, _, config)
|
||||
)
|
||||
}
|
||||
@@ -1923,8 +1927,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, ReturnPosition pos, Ap innerReturnAp |
|
||||
revFlowThrough(call, returnCtx, p, state, pos, returnAp, ap, innerReturnAp, config) and
|
||||
exists(ParamNodeEx p, Ap innerReturnAp |
|
||||
revFlowThrough(call, returnCtx, p, state, _, returnAp, ap, innerReturnAp, config) and
|
||||
flowThroughIntoCall(call, arg, p, _, ap, innerReturnAp, config)
|
||||
)
|
||||
}
|
||||
@@ -3749,8 +3753,8 @@ private predicate paramFlowsThrough(
|
||||
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
|
||||
AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid, RetNodeEx ret |
|
||||
pathNode(mid, ret, state, cc, sc, ap, config, _) and
|
||||
exists(RetNodeEx ret |
|
||||
pathNode(_, ret, state, cc, sc, ap, config, _) and
|
||||
kind = ret.getKind() and
|
||||
apa = ap.getApprox() and
|
||||
parameterFlowThroughAllowed(sc.getParamNode(), kind)
|
||||
@@ -4212,17 +4216,15 @@ private module FlowExploration {
|
||||
ap = TRevPartialNil() and
|
||||
exists(config.explorationLimit())
|
||||
or
|
||||
exists(PartialPathNodeRev mid |
|
||||
revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and
|
||||
not clearsContentEx(node, ap.getHead()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead())
|
||||
) and
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
|
||||
)
|
||||
revPartialPathStep(_, node, state, sc1, sc2, sc3, ap, config) and
|
||||
not clearsContentEx(node, ap.getHead()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead())
|
||||
) and
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -4230,19 +4232,17 @@ private module FlowExploration {
|
||||
NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
|
||||
TSummaryCtx3 sc3, PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(PartialPathNodeFwd mid |
|
||||
partialPathStep(mid, node, state, cc, sc1, sc2, sc3, ap, config) and
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
not clearsContentEx(node, ap.getHead().getContent()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead().getContent())
|
||||
) and
|
||||
if node.asNode() instanceof CastingNode
|
||||
then compatibleTypes(node.getDataFlowType(), ap.getType())
|
||||
else any()
|
||||
)
|
||||
partialPathStep(_, node, state, cc, sc1, sc2, sc3, ap, config) and
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
not clearsContentEx(node, ap.getHead().getContent()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead().getContent())
|
||||
) and
|
||||
if node.asNode() instanceof CastingNode
|
||||
then compatibleTypes(node.getDataFlowType(), ap.getType())
|
||||
else any()
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -876,9 +876,9 @@ private module Stage1 implements StageSig {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowOut(ReturnPosition pos, Configuration config) {
|
||||
exists(DataFlowCall call, NodeEx out |
|
||||
exists(NodeEx out |
|
||||
revFlow(out, _, config) and
|
||||
viableReturnPosOutNodeCandFwd1(call, pos, out, config)
|
||||
viableReturnPosOutNodeCandFwd1(_, pos, out, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1487,6 +1487,10 @@ private module MkStage<StageSig PrevStage> {
|
||||
PrevStage::readStepCand(node1, _, _, config)
|
||||
}
|
||||
|
||||
bindingset[ap, c]
|
||||
pragma[inline_late]
|
||||
private predicate hasHeadContent(Ap ap, Content c) { getHeadContent(ap) = c }
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead(
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
@@ -1494,7 +1498,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
) {
|
||||
fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
getHeadContent(ap) = c
|
||||
hasHeadContent(ap, c)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1731,8 +1735,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ParamNodeEx p, ReturnPosition pos, Ap innerReturnAp |
|
||||
revFlowThrough(call, returnCtx, p, state, pos, returnAp, ap, innerReturnAp, config) and
|
||||
exists(DataFlowCall call, ParamNodeEx p, Ap innerReturnAp |
|
||||
revFlowThrough(call, returnCtx, p, state, _, returnAp, ap, innerReturnAp, config) and
|
||||
flowThroughIntoCall(call, node, p, _, ap, innerReturnAp, config)
|
||||
)
|
||||
or
|
||||
@@ -1901,8 +1905,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
|
||||
exists(RetNodeEx ret, ReturnPosition pos |
|
||||
returnFlowsThrough(ret, pos, _, _, p, ap, _, config) and
|
||||
exists(ReturnPosition pos |
|
||||
returnFlowsThrough(_, pos, _, _, p, ap, _, config) and
|
||||
parameterFlowsThroughRev(p, ap, pos, _, config)
|
||||
)
|
||||
}
|
||||
@@ -1923,8 +1927,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, ReturnPosition pos, Ap innerReturnAp |
|
||||
revFlowThrough(call, returnCtx, p, state, pos, returnAp, ap, innerReturnAp, config) and
|
||||
exists(ParamNodeEx p, Ap innerReturnAp |
|
||||
revFlowThrough(call, returnCtx, p, state, _, returnAp, ap, innerReturnAp, config) and
|
||||
flowThroughIntoCall(call, arg, p, _, ap, innerReturnAp, config)
|
||||
)
|
||||
}
|
||||
@@ -3749,8 +3753,8 @@ private predicate paramFlowsThrough(
|
||||
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
|
||||
AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid, RetNodeEx ret |
|
||||
pathNode(mid, ret, state, cc, sc, ap, config, _) and
|
||||
exists(RetNodeEx ret |
|
||||
pathNode(_, ret, state, cc, sc, ap, config, _) and
|
||||
kind = ret.getKind() and
|
||||
apa = ap.getApprox() and
|
||||
parameterFlowThroughAllowed(sc.getParamNode(), kind)
|
||||
@@ -4212,17 +4216,15 @@ private module FlowExploration {
|
||||
ap = TRevPartialNil() and
|
||||
exists(config.explorationLimit())
|
||||
or
|
||||
exists(PartialPathNodeRev mid |
|
||||
revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and
|
||||
not clearsContentEx(node, ap.getHead()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead())
|
||||
) and
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
|
||||
)
|
||||
revPartialPathStep(_, node, state, sc1, sc2, sc3, ap, config) and
|
||||
not clearsContentEx(node, ap.getHead()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead())
|
||||
) and
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -4230,19 +4232,17 @@ private module FlowExploration {
|
||||
NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
|
||||
TSummaryCtx3 sc3, PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(PartialPathNodeFwd mid |
|
||||
partialPathStep(mid, node, state, cc, sc1, sc2, sc3, ap, config) and
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
not clearsContentEx(node, ap.getHead().getContent()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead().getContent())
|
||||
) and
|
||||
if node.asNode() instanceof CastingNode
|
||||
then compatibleTypes(node.getDataFlowType(), ap.getType())
|
||||
else any()
|
||||
)
|
||||
partialPathStep(_, node, state, cc, sc1, sc2, sc3, ap, config) and
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
not clearsContentEx(node, ap.getHead().getContent()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead().getContent())
|
||||
) and
|
||||
if node.asNode() instanceof CastingNode
|
||||
then compatibleTypes(node.getDataFlowType(), ap.getType())
|
||||
else any()
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -45,6 +45,16 @@ module Consistency {
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
/** Holds if `(c, pos, p)` should be excluded from the consistency test `uniqueParameterNodeAtPosition`. */
|
||||
predicate uniqueParameterNodeAtPositionExclude(DataFlowCallable c, ParameterPosition pos, Node p) {
|
||||
none()
|
||||
}
|
||||
|
||||
/** Holds if `(c, pos, p)` should be excluded from the consistency test `uniqueParameterNodePosition`. */
|
||||
predicate uniqueParameterNodePositionExclude(DataFlowCallable c, ParameterPosition pos, Node p) {
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
private class RelevantNode extends Node {
|
||||
@@ -101,9 +111,7 @@ module Consistency {
|
||||
exists(int c |
|
||||
c =
|
||||
strictcount(Node n |
|
||||
not exists(string filepath, int startline, int startcolumn, int endline, int endcolumn |
|
||||
n.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
) and
|
||||
not n.hasLocationInfo(_, _, _, _, _) and
|
||||
not any(ConsistencyConfiguration conf).missingLocationExclude(n)
|
||||
) and
|
||||
msg = "Nodes without location: " + c
|
||||
@@ -248,6 +256,7 @@ module Consistency {
|
||||
query predicate uniqueParameterNodeAtPosition(
|
||||
DataFlowCallable c, ParameterPosition pos, Node p, string msg
|
||||
) {
|
||||
not any(ConsistencyConfiguration conf).uniqueParameterNodeAtPositionExclude(c, pos, p) and
|
||||
isParameterNode(p, c, pos) and
|
||||
not exists(unique(Node p0 | isParameterNode(p0, c, pos))) and
|
||||
msg = "Parameters with overlapping positions."
|
||||
@@ -256,6 +265,7 @@ module Consistency {
|
||||
query predicate uniqueParameterNodePosition(
|
||||
DataFlowCallable c, ParameterPosition pos, Node p, string msg
|
||||
) {
|
||||
not any(ConsistencyConfiguration conf).uniqueParameterNodePositionExclude(c, pos, p) and
|
||||
isParameterNode(p, c, pos) and
|
||||
not exists(unique(ParameterPosition pos0 | isParameterNode(p, c, pos0))) and
|
||||
msg = "Parameter node with multiple positions."
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -30,6 +30,7 @@ private newtype TOpcode =
|
||||
TNegate() or
|
||||
TShiftLeft() or
|
||||
TShiftRight() or
|
||||
TUnsignedShiftRight() or
|
||||
TBitAnd() or
|
||||
TBitOr() or
|
||||
TBitXor() or
|
||||
@@ -652,6 +653,15 @@ module Opcode {
|
||||
final override string toString() { result = "ShiftRight" }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `Opcode` for a `UnsignedShiftRightInstruction`.
|
||||
*
|
||||
* See the `UnsignedShiftRightInstruction` documentation for more details.
|
||||
*/
|
||||
class UnsignedShiftRight extends BinaryBitwiseOpcode, TUnsignedShiftRight {
|
||||
final override string toString() { result = "UnsignedShiftRight" }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `Opcode` for a `BitAndInstruction`.
|
||||
*
|
||||
|
||||
@@ -1204,6 +1204,17 @@ class ShiftRightInstruction extends BinaryBitwiseInstruction {
|
||||
ShiftRightInstruction() { this.getOpcode() instanceof Opcode::ShiftRight }
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction that shifts its left operand to the right by the number of bits specified by its
|
||||
* right operand.
|
||||
*
|
||||
* Both operands must have an integer type. The result has the same type as the left operand.
|
||||
* The leftmost bits are zero-filled.
|
||||
*/
|
||||
class UnsignedShiftRightInstruction extends BinaryBitwiseInstruction {
|
||||
UnsignedShiftRightInstruction() { this.getOpcode() instanceof Opcode::UnsignedShiftRight }
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction that performs a binary arithmetic operation involving at least one pointer
|
||||
* operand.
|
||||
|
||||
@@ -45,7 +45,7 @@ class Operand extends TStageOperand {
|
||||
this = reusedPhiOperand(use, def, predecessorBlock, _)
|
||||
)
|
||||
or
|
||||
exists(Instruction use | this = chiOperand(use, _))
|
||||
this = chiOperand(_, _)
|
||||
}
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
@@ -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 }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -329,12 +329,12 @@ private module Cached {
|
||||
cached
|
||||
Instruction getChiInstructionTotalOperand(ChiInstruction chiInstr) {
|
||||
exists(
|
||||
Alias::VirtualVariable vvar, OldInstruction oldInstr, Alias::MemoryLocation defLocation,
|
||||
OldBlock defBlock, int defRank, int defOffset, OldBlock useBlock, int useRank
|
||||
Alias::VirtualVariable vvar, OldInstruction oldInstr, OldBlock defBlock, int defRank,
|
||||
int defOffset, OldBlock useBlock, int useRank
|
||||
|
|
||||
chiInstr = getChi(oldInstr) and
|
||||
vvar = Alias::getResultMemoryLocation(oldInstr).getVirtualVariable() and
|
||||
hasDefinitionAtRank(vvar, defLocation, defBlock, defRank, defOffset) and
|
||||
hasDefinitionAtRank(vvar, _, defBlock, defRank, defOffset) and
|
||||
hasUseAtRank(vvar, useBlock, useRank, oldInstr) and
|
||||
definitionReachesUse(vvar, defBlock, defRank, useBlock, useRank) and
|
||||
result = getDefinitionOrChiInstruction(defBlock, defOffset, vvar, _)
|
||||
|
||||
@@ -1204,6 +1204,17 @@ class ShiftRightInstruction extends BinaryBitwiseInstruction {
|
||||
ShiftRightInstruction() { this.getOpcode() instanceof Opcode::ShiftRight }
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction that shifts its left operand to the right by the number of bits specified by its
|
||||
* right operand.
|
||||
*
|
||||
* Both operands must have an integer type. The result has the same type as the left operand.
|
||||
* The leftmost bits are zero-filled.
|
||||
*/
|
||||
class UnsignedShiftRightInstruction extends BinaryBitwiseInstruction {
|
||||
UnsignedShiftRightInstruction() { this.getOpcode() instanceof Opcode::UnsignedShiftRight }
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction that performs a binary arithmetic operation involving at least one pointer
|
||||
* operand.
|
||||
|
||||
@@ -45,7 +45,7 @@ class Operand extends TStageOperand {
|
||||
this = reusedPhiOperand(use, def, predecessorBlock, _)
|
||||
)
|
||||
or
|
||||
exists(Instruction use | this = chiOperand(use, _))
|
||||
this = chiOperand(_, _)
|
||||
}
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
@@ -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 }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -298,11 +298,11 @@ class TranslatedStringLiteralInitialization extends TranslatedDirectInitializati
|
||||
opcode instanceof Opcode::Store and
|
||||
resultType = getTypeForPRValue(expr.getType())
|
||||
or
|
||||
exists(int startIndex, int elementCount |
|
||||
exists(int elementCount |
|
||||
// If the initializer string isn't large enough to fill the target, then
|
||||
// we have to generate another instruction sequence to store a constant
|
||||
// zero into the remainder of the array.
|
||||
zeroInitRange(startIndex, elementCount) and
|
||||
zeroInitRange(_, elementCount) and
|
||||
(
|
||||
// Create a constant zero whose size is the size of the remaining
|
||||
// space in the target array.
|
||||
|
||||
@@ -1204,6 +1204,17 @@ class ShiftRightInstruction extends BinaryBitwiseInstruction {
|
||||
ShiftRightInstruction() { this.getOpcode() instanceof Opcode::ShiftRight }
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction that shifts its left operand to the right by the number of bits specified by its
|
||||
* right operand.
|
||||
*
|
||||
* Both operands must have an integer type. The result has the same type as the left operand.
|
||||
* The leftmost bits are zero-filled.
|
||||
*/
|
||||
class UnsignedShiftRightInstruction extends BinaryBitwiseInstruction {
|
||||
UnsignedShiftRightInstruction() { this.getOpcode() instanceof Opcode::UnsignedShiftRight }
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction that performs a binary arithmetic operation involving at least one pointer
|
||||
* operand.
|
||||
|
||||
@@ -45,7 +45,7 @@ class Operand extends TStageOperand {
|
||||
this = reusedPhiOperand(use, def, predecessorBlock, _)
|
||||
)
|
||||
or
|
||||
exists(Instruction use | this = chiOperand(use, _))
|
||||
this = chiOperand(_, _)
|
||||
}
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
@@ -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 }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -329,12 +329,12 @@ private module Cached {
|
||||
cached
|
||||
Instruction getChiInstructionTotalOperand(ChiInstruction chiInstr) {
|
||||
exists(
|
||||
Alias::VirtualVariable vvar, OldInstruction oldInstr, Alias::MemoryLocation defLocation,
|
||||
OldBlock defBlock, int defRank, int defOffset, OldBlock useBlock, int useRank
|
||||
Alias::VirtualVariable vvar, OldInstruction oldInstr, OldBlock defBlock, int defRank,
|
||||
int defOffset, OldBlock useBlock, int useRank
|
||||
|
|
||||
chiInstr = getChi(oldInstr) and
|
||||
vvar = Alias::getResultMemoryLocation(oldInstr).getVirtualVariable() and
|
||||
hasDefinitionAtRank(vvar, defLocation, defBlock, defRank, defOffset) and
|
||||
hasDefinitionAtRank(vvar, _, defBlock, defRank, defOffset) and
|
||||
hasUseAtRank(vvar, useBlock, useRank, oldInstr) and
|
||||
definitionReachesUse(vvar, defBlock, defRank, useBlock, useRank) and
|
||||
result = getDefinitionOrChiInstruction(defBlock, defOffset, vvar, _)
|
||||
|
||||
@@ -67,9 +67,7 @@ class Class = Cpp::Class; // Used for inheritance conversions
|
||||
|
||||
predicate getIdentityString = Print::getIdentityString/1;
|
||||
|
||||
predicate hasCaseEdge(string minValue, string maxValue) {
|
||||
exists(Cpp::SwitchCase switchCase | hasCaseEdge(switchCase, minValue, maxValue))
|
||||
}
|
||||
predicate hasCaseEdge(string minValue, string maxValue) { hasCaseEdge(_, minValue, maxValue) }
|
||||
|
||||
predicate hasPositionalArgIndex(int argIndex) {
|
||||
exists(Cpp::FunctionCall call | exists(call.getArgument(argIndex))) or
|
||||
|
||||
@@ -99,10 +99,10 @@ class MetricClass extends Class {
|
||||
}
|
||||
|
||||
/** Gets any method that accesses some local field. */
|
||||
Function getAccessingMethod() { exists(Field f | this.accessesLocalField(result, f)) }
|
||||
Function getAccessingMethod() { this.accessesLocalField(result, _) }
|
||||
|
||||
/** Gets any field that is accessed by a local method. */
|
||||
Field getAccessedField() { exists(Function func | this.accessesLocalField(func, result)) }
|
||||
Field getAccessedField() { this.accessesLocalField(_, result) }
|
||||
|
||||
/** Gets the Henderson-Sellers lack-of-cohesion metric. */
|
||||
float getLackOfCohesionHS() {
|
||||
@@ -517,10 +517,10 @@ private predicate dependsOnClassSimple(Class source, Class dest) {
|
||||
)
|
||||
or
|
||||
// a class depends on classes for which a call to its member function is done from a function
|
||||
exists(MemberFunction target, MemberFunction f, Locatable l |
|
||||
exists(MemberFunction target, MemberFunction f |
|
||||
f.getDeclaringType() = source and
|
||||
f instanceof MemberFunction and
|
||||
f.calls(target, l) and
|
||||
f.calls(target, _) and
|
||||
target instanceof MemberFunction and
|
||||
target.getDeclaringType() = dest
|
||||
)
|
||||
|
||||
@@ -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
|
||||
(
|
||||
|
||||
@@ -133,13 +133,15 @@ abstract class HeuristicAllocationExpr extends Expr {
|
||||
|
||||
/**
|
||||
* Gets a constant multiplier for the allocation size given by `getSizeExpr`,
|
||||
* in bytes.
|
||||
* in bytes. This predicate should be used with caution as it can be
|
||||
* inaccurate for allocations identified using heuristics.
|
||||
*/
|
||||
int getSizeMult() { none() }
|
||||
|
||||
/**
|
||||
* Gets the size of this allocation in bytes, if it is a fixed size and that
|
||||
* size can be determined.
|
||||
* size can be determined. This predicate should be used with caution as it
|
||||
* can be inaccurate for allocations identified using heuristics.
|
||||
*/
|
||||
int getSizeBytes() { none() }
|
||||
|
||||
|
||||
@@ -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(_))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -296,7 +296,7 @@ private predicate analyzableExpr(Expr e) {
|
||||
or
|
||||
// Also allow variable accesses, provided that they have SSA
|
||||
// information.
|
||||
exists(RangeSsaDefinition def, StackVariable v | e = def.getAUse(v))
|
||||
exists(RangeSsaDefinition def | e = def.getAUse(_))
|
||||
or
|
||||
e instanceof UnsignedBitwiseAndExpr
|
||||
or
|
||||
|
||||
@@ -127,9 +127,9 @@ deprecated private predicate betweenFunctionsValueMoveTo(
|
||||
not unreachable(src) and
|
||||
not unreachable(dest) and
|
||||
(
|
||||
exists(Call call, Function called, int i |
|
||||
exists(Call call, int i |
|
||||
src = call.getArgument(i) and
|
||||
resolveCallWithParam(call, called, i, dest) and
|
||||
resolveCallWithParam(call, _, i, dest) and
|
||||
destFromArg = true
|
||||
)
|
||||
or
|
||||
@@ -151,8 +151,8 @@ deprecated private predicate betweenFunctionsValueMoveTo(
|
||||
)
|
||||
or
|
||||
// If a parameter of type reference is tainted inside a function, taint the argument too
|
||||
exists(Call call, Function f, int pi, Parameter p |
|
||||
resolveCallWithParam(call, f, pi, p) and
|
||||
exists(Call call, int pi, Parameter p |
|
||||
resolveCallWithParam(call, _, pi, p) and
|
||||
p.getType() instanceof ReferenceType and
|
||||
src = p and
|
||||
dest = call.getArgument(pi) and
|
||||
|
||||
@@ -34,7 +34,7 @@ class Stmt extends StmtParent, @stmt {
|
||||
}
|
||||
|
||||
/** Gets a child of this statement. */
|
||||
Element getAChild() { exists(int n | result = this.getChild(n)) }
|
||||
Element getAChild() { result = this.getChild(_) }
|
||||
|
||||
/** Gets the parent of this statement, if any. */
|
||||
StmtParent getParent() { stmtparents(underlyingElement(this), _, unresolveElement(result)) }
|
||||
|
||||
@@ -475,9 +475,7 @@ private predicate mk_NonmemberFunctionCall(Function fcn, HC_Args args, FunctionC
|
||||
fc.getTarget() = fcn and
|
||||
analyzableNonmemberFunctionCall(fc) and
|
||||
(
|
||||
exists(HashCons head, HC_Args tail |
|
||||
mk_ArgConsInner(head, tail, fc.getNumberOfArguments() - 1, args, fc)
|
||||
)
|
||||
mk_ArgConsInner(_, _, fc.getNumberOfArguments() - 1, args, fc)
|
||||
or
|
||||
fc.getNumberOfArguments() = 0 and
|
||||
args = HC_EmptyArgs()
|
||||
@@ -494,9 +492,7 @@ private predicate analyzableExprCall(ExprCall ec) {
|
||||
private predicate mk_ExprCall(HashCons hc, HC_Args args, ExprCall ec) {
|
||||
hc.getAnExpr() = ec.getExpr() and
|
||||
(
|
||||
exists(HashCons head, HC_Args tail |
|
||||
mk_ArgConsInner(head, tail, ec.getNumberOfArguments() - 1, args, ec)
|
||||
)
|
||||
mk_ArgConsInner(_, _, ec.getNumberOfArguments() - 1, args, ec)
|
||||
or
|
||||
ec.getNumberOfArguments() = 0 and
|
||||
args = HC_EmptyArgs()
|
||||
@@ -516,9 +512,7 @@ private predicate mk_MemberFunctionCall(Function fcn, HashCons qual, HC_Args arg
|
||||
analyzableMemberFunctionCall(fc) and
|
||||
hashCons(fc.getQualifier().getFullyConverted()) = qual and
|
||||
(
|
||||
exists(HashCons head, HC_Args tail |
|
||||
mk_ArgConsInner(head, tail, fc.getNumberOfArguments() - 1, args, fc)
|
||||
)
|
||||
mk_ArgConsInner(_, _, fc.getNumberOfArguments() - 1, args, fc)
|
||||
or
|
||||
fc.getNumberOfArguments() = 0 and
|
||||
args = HC_EmptyArgs()
|
||||
@@ -541,10 +535,8 @@ private predicate mk_ArgCons(HashCons hc, int i, HC_Args list, Call c) {
|
||||
analyzableCall(c) and
|
||||
hc = hashCons(c.getArgument(i).getFullyConverted()) and
|
||||
(
|
||||
exists(HashCons head, HC_Args tail |
|
||||
mk_ArgConsInner(head, tail, i - 1, list, c) and
|
||||
i > 0
|
||||
)
|
||||
mk_ArgConsInner(_, _, i - 1, list, c) and
|
||||
i > 0
|
||||
or
|
||||
i = 0 and
|
||||
list = HC_EmptyArgs()
|
||||
|
||||
@@ -303,12 +303,11 @@ affectedbymacroexpansion(
|
||||
int inv: @macroinvocation ref
|
||||
);
|
||||
|
||||
/*
|
||||
case @macroinvocations.kind of
|
||||
1 = macro expansion
|
||||
| 2 = other macro reference
|
||||
;
|
||||
*/
|
||||
case @macroinvocation.kind of
|
||||
1 = @macro_expansion
|
||||
| 2 = @other_macro_reference
|
||||
;
|
||||
|
||||
macroinvocations(
|
||||
unique int id: @macroinvocation,
|
||||
int macro_id: @ppd_define ref,
|
||||
@@ -345,28 +344,37 @@ macro_argument_expanded(
|
||||
);
|
||||
|
||||
/*
|
||||
case @function.kind of
|
||||
1 = normal
|
||||
| 2 = constructor
|
||||
| 3 = destructor
|
||||
| 4 = conversion
|
||||
| 5 = operator
|
||||
| 6 = builtin // GCC built-in functions, e.g. __builtin___memcpy_chk
|
||||
;
|
||||
case @function.kind of
|
||||
1 = @normal_function
|
||||
| 2 = @constructor
|
||||
| 3 = @destructor
|
||||
| 4 = @conversion_function
|
||||
| 5 = @operator
|
||||
| 6 = @builtin_function // GCC built-in functions, e.g. __builtin___memcpy_chk
|
||||
;
|
||||
*/
|
||||
|
||||
functions(
|
||||
unique int id: @function,
|
||||
string name: string ref,
|
||||
int kind: int ref
|
||||
);
|
||||
|
||||
function_entry_point(int id: @function ref, unique int entry_point: @stmt ref);
|
||||
function_entry_point(
|
||||
int id: @function ref,
|
||||
unique int entry_point: @stmt ref
|
||||
);
|
||||
|
||||
function_return_type(int id: @function ref, int return_type: @type ref);
|
||||
function_return_type(
|
||||
int id: @function ref,
|
||||
int return_type: @type ref
|
||||
);
|
||||
|
||||
/** If `function` is a coroutine, then this gives the
|
||||
std::experimental::resumable_traits instance associated with it,
|
||||
and the variables representing the `handle` and `promise` for it. */
|
||||
/**
|
||||
* If `function` is a coroutine, then this gives the `std::experimental::resumable_traits`
|
||||
* instance associated with it, and the variables representing the `handle` and `promise`
|
||||
* for it.
|
||||
*/
|
||||
coroutine(
|
||||
unique int function: @function ref,
|
||||
int traits: @type ref,
|
||||
@@ -392,7 +400,10 @@ function_deleted(unique int id: @function ref);
|
||||
|
||||
function_defaulted(unique int id: @function ref);
|
||||
|
||||
member_function_this_type(unique int id: @function ref, int this_type: @type ref);
|
||||
member_function_this_type(
|
||||
unique int id: @function ref,
|
||||
int this_type: @type ref
|
||||
);
|
||||
|
||||
#keyset[id, type_id]
|
||||
fun_decls(
|
||||
@@ -495,7 +506,10 @@ params(
|
||||
int type_id: @type ref
|
||||
);
|
||||
|
||||
overrides(int new: @function ref, int old: @function ref);
|
||||
overrides(
|
||||
int new: @function ref,
|
||||
int old: @function ref
|
||||
);
|
||||
|
||||
#keyset[id, type_id]
|
||||
membervariables(
|
||||
@@ -541,63 +555,65 @@ enumconstants(
|
||||
|
||||
@localscopevariable = @localvariable | @parameter;
|
||||
|
||||
/*
|
||||
Built-in types are the fundamental types, e.g., integral, floating, and void.
|
||||
/**
|
||||
* Built-in types are the fundamental types, e.g., integral, floating, and void.
|
||||
*/
|
||||
case @builtintype.kind of
|
||||
1 = @errortype
|
||||
| 2 = @unknowntype
|
||||
| 3 = @void
|
||||
| 4 = @boolean
|
||||
| 5 = @char
|
||||
| 6 = @unsigned_char
|
||||
| 7 = @signed_char
|
||||
| 8 = @short
|
||||
| 9 = @unsigned_short
|
||||
| 10 = @signed_short
|
||||
| 11 = @int
|
||||
| 12 = @unsigned_int
|
||||
| 13 = @signed_int
|
||||
| 14 = @long
|
||||
| 15 = @unsigned_long
|
||||
| 16 = @signed_long
|
||||
| 17 = @long_long
|
||||
| 18 = @unsigned_long_long
|
||||
| 19 = @signed_long_long
|
||||
// ... 20 Microsoft-specific __int8
|
||||
// ... 21 Microsoft-specific __int16
|
||||
// ... 22 Microsoft-specific __int32
|
||||
// ... 23 Microsoft-specific __int64
|
||||
| 24 = @float
|
||||
| 25 = @double
|
||||
| 26 = @long_double
|
||||
| 27 = @complex_float // C99-specific _Complex float
|
||||
| 28 = @complex_double // C99-specific _Complex double
|
||||
| 29 = @complex_long_double // C99-specific _Complex long double
|
||||
| 30 = @imaginary_float // C99-specific _Imaginary float
|
||||
| 31 = @imaginary_double // C99-specific _Imaginary double
|
||||
| 32 = @imaginary_long_double // C99-specific _Imaginary long double
|
||||
| 33 = @wchar_t // Microsoft-specific
|
||||
| 34 = @decltype_nullptr // C++11
|
||||
| 35 = @int128 // __int128
|
||||
| 36 = @unsigned_int128 // unsigned __int128
|
||||
| 37 = @signed_int128 // signed __int128
|
||||
| 38 = @float128 // __float128
|
||||
| 39 = @complex_float128 // _Complex __float128
|
||||
| 40 = @decimal32 // _Decimal32
|
||||
| 41 = @decimal64 // _Decimal64
|
||||
| 42 = @decimal128 // _Decimal128
|
||||
| 43 = @char16_t
|
||||
| 44 = @char32_t
|
||||
| 45 = @std_float32 // _Float32
|
||||
| 46 = @float32x // _Float32x
|
||||
| 47 = @std_float64 // _Float64
|
||||
| 48 = @float64x // _Float64x
|
||||
| 49 = @std_float128 // _Float128
|
||||
| 50 = @float128x // _Float128x
|
||||
| 51 = @char8_t
|
||||
| 52 = @float16 // _Float16
|
||||
| 53 = @complex_float16 // _Complex _Float16
|
||||
;
|
||||
|
||||
case @builtintype.kind of
|
||||
1 = error
|
||||
| 2 = unknown
|
||||
| 3 = void
|
||||
| 4 = boolean
|
||||
| 5 = char
|
||||
| 6 = unsigned_char
|
||||
| 7 = signed_char
|
||||
| 8 = short
|
||||
| 9 = unsigned_short
|
||||
| 10 = signed_short
|
||||
| 11 = int
|
||||
| 12 = unsigned_int
|
||||
| 13 = signed_int
|
||||
| 14 = long
|
||||
| 15 = unsigned_long
|
||||
| 16 = signed_long
|
||||
| 17 = long_long
|
||||
| 18 = unsigned_long_long
|
||||
| 19 = signed_long_long
|
||||
| 20 = __int8 // Microsoft-specific
|
||||
| 21 = __int16 // Microsoft-specific
|
||||
| 22 = __int32 // Microsoft-specific
|
||||
| 23 = __int64 // Microsoft-specific
|
||||
| 24 = float
|
||||
| 25 = double
|
||||
| 26 = long_double
|
||||
| 27 = _Complex_float // C99-specific
|
||||
| 28 = _Complex_double // C99-specific
|
||||
| 29 = _Complex_long double // C99-specific
|
||||
| 30 = _Imaginary_float // C99-specific
|
||||
| 31 = _Imaginary_double // C99-specific
|
||||
| 32 = _Imaginary_long_double // C99-specific
|
||||
| 33 = wchar_t // Microsoft-specific
|
||||
| 34 = decltype_nullptr // C++11
|
||||
| 35 = __int128
|
||||
| 36 = unsigned___int128
|
||||
| 37 = signed___int128
|
||||
| 38 = __float128
|
||||
| 39 = _Complex___float128
|
||||
| 40 = _Decimal32
|
||||
| 41 = _Decimal64
|
||||
| 42 = _Decimal128
|
||||
| 43 = char16_t
|
||||
| 44 = char32_t
|
||||
| 45 = _Float32
|
||||
| 46 = _Float32x
|
||||
| 47 = _Float64
|
||||
| 48 = _Float64x
|
||||
| 49 = _Float128
|
||||
| 50 = _Float128x
|
||||
| 51 = char8_t
|
||||
;
|
||||
*/
|
||||
builtintypes(
|
||||
unique int id: @builtintype,
|
||||
string name: string ref,
|
||||
@@ -607,23 +623,23 @@ builtintypes(
|
||||
int alignment: int ref
|
||||
);
|
||||
|
||||
/*
|
||||
Derived types are types that are directly derived from existing types and
|
||||
point to, refer to, transform type data to return a new type.
|
||||
|
||||
case @derivedtype.kind of
|
||||
1 = pointer
|
||||
| 2 = reference
|
||||
| 3 = type_with_specifiers
|
||||
| 4 = array
|
||||
| 5 = gnu_vector
|
||||
| 6 = routineptr
|
||||
| 7 = routinereference
|
||||
| 8 = rvalue_reference // C++11
|
||||
/**
|
||||
* Derived types are types that are directly derived from existing types and
|
||||
* point to, refer to, transform type data to return a new type.
|
||||
*/
|
||||
case @derivedtype.kind of
|
||||
1 = @pointer
|
||||
| 2 = @reference
|
||||
| 3 = @type_with_specifiers
|
||||
| 4 = @array
|
||||
| 5 = @gnu_vector
|
||||
| 6 = @routineptr
|
||||
| 7 = @routinereference
|
||||
| 8 = @rvalue_reference // C++11
|
||||
// ... 9 type_conforming_to_protocols deprecated
|
||||
| 10 = block
|
||||
;
|
||||
*/
|
||||
| 10 = @block
|
||||
;
|
||||
|
||||
derivedtypes(
|
||||
unique int id: @derivedtype,
|
||||
string name: string ref,
|
||||
@@ -675,23 +691,24 @@ decltypes(
|
||||
);
|
||||
|
||||
/*
|
||||
case @usertype.kind of
|
||||
1 = struct
|
||||
| 2 = class
|
||||
| 3 = union
|
||||
| 4 = enum
|
||||
| 5 = typedef // classic C: typedef typedef type name
|
||||
| 6 = template
|
||||
| 7 = template_parameter
|
||||
| 8 = template_template_parameter
|
||||
| 9 = proxy_class // a proxy class associated with a template parameter
|
||||
case @usertype.kind of
|
||||
1 = @struct
|
||||
| 2 = @class
|
||||
| 3 = @union
|
||||
| 4 = @enum
|
||||
| 5 = @typedef // classic C: typedef typedef type name
|
||||
| 6 = @template
|
||||
| 7 = @template_parameter
|
||||
| 8 = @template_template_parameter
|
||||
| 9 = @proxy_class // a proxy class associated with a template parameter
|
||||
// ... 10 objc_class deprecated
|
||||
// ... 11 objc_protocol deprecated
|
||||
// ... 12 objc_category deprecated
|
||||
| 13 = scoped_enum
|
||||
| 14 = using_alias // a using name = type style typedef
|
||||
;
|
||||
| 13 = @scoped_enum
|
||||
| 14 = @using_alias // a using name = type style typedef
|
||||
;
|
||||
*/
|
||||
|
||||
usertypes(
|
||||
unique int id: @usertype,
|
||||
string name: string ref,
|
||||
@@ -1162,7 +1179,10 @@ case @funbindexpr.kind of
|
||||
| 2 = @adl_call // a call whose target is only found by ADL
|
||||
;
|
||||
*/
|
||||
iscall(unique int caller: @funbindexpr ref, int kind: int ref);
|
||||
iscall(
|
||||
unique int caller: @funbindexpr ref,
|
||||
int kind: int ref
|
||||
);
|
||||
|
||||
numtemplatearguments(
|
||||
unique int expr_id: @expr ref,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,2 @@
|
||||
description: Uncomment case splits in dbscheme
|
||||
compatibility: full
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,2 @@
|
||||
description: Introduce (_Complex) _Float16 type
|
||||
compatibility: full
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user