mirror of
https://github.com/github/codeql.git
synced 2026-06-02 20:30:15 +02:00
Compare commits
1334 Commits
criemen/ex
...
mathiasvp/
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8f0519fcd1 | ||
|
|
5f098a5b90 | ||
|
|
08ac352bb4 | ||
|
|
80000cd890 | ||
|
|
589842b252 | ||
|
|
98ef06c6ec | ||
|
|
c3a5a4fc36 | ||
|
|
f8265ea095 | ||
|
|
870d80ba43 | ||
|
|
c5e51fd3de | ||
|
|
07db62d90b | ||
|
|
090675fe04 | ||
|
|
34a209bc8a | ||
|
|
c29fd61e6f | ||
|
|
9e14aefa65 | ||
|
|
b9c6785b1f | ||
|
|
12e56ec9e6 | ||
|
|
02b1fe27d2 | ||
|
|
1333f67a69 | ||
|
|
562a57b75b | ||
|
|
852e9875bd | ||
|
|
1fe772a2ab | ||
|
|
174df98762 | ||
|
|
a1ce81c3d7 | ||
|
|
8167e83ae5 | ||
|
|
d0fd907582 | ||
|
|
8f28684d10 | ||
|
|
605494c3d1 | ||
|
|
7feab27bf4 | ||
|
|
da412178ce | ||
|
|
8f15dc4bd0 | ||
|
|
19589bef27 | ||
|
|
aaa5046533 | ||
|
|
f9fb046e9f | ||
|
|
29cdc8a49a | ||
|
|
dd31d5ffb3 | ||
|
|
0bf5238f39 | ||
|
|
53d4d72fe5 | ||
|
|
dd138b0429 | ||
|
|
ec5d8ab2db | ||
|
|
a1511e13d8 | ||
|
|
1196d0c624 | ||
|
|
ed73d9bab4 | ||
|
|
9331b3538d | ||
|
|
d4b18fe6a3 | ||
|
|
112d408fb9 | ||
|
|
233a3346a8 | ||
|
|
7015be7cad | ||
|
|
38257a58f0 | ||
|
|
057d0fb7e0 | ||
|
|
12d7f0c9e2 | ||
|
|
8a4fa0a7e2 | ||
|
|
9d9a7abd06 | ||
|
|
283376eb19 | ||
|
|
679652e63a | ||
|
|
952b34a163 | ||
|
|
d0eec1e381 | ||
|
|
70ffbae091 | ||
|
|
662852bd1d | ||
|
|
548a344d34 | ||
|
|
c7c35401e0 | ||
|
|
d46b897492 | ||
|
|
beaa1cffd2 | ||
|
|
3bf9abb4ce | ||
|
|
0d66cebfba | ||
|
|
3c25301593 | ||
|
|
d0d17e3b84 | ||
|
|
b71920209e | ||
|
|
b975e12f41 | ||
|
|
b0af805460 | ||
|
|
90a50e7ca9 | ||
|
|
6508afe824 | ||
|
|
6f34735f64 | ||
|
|
4d7a8285ad | ||
|
|
86d53931aa | ||
|
|
3f3c79f48f | ||
|
|
b67032d1cc | ||
|
|
a10bde5795 | ||
|
|
8e68eae83d | ||
|
|
e6954292aa | ||
|
|
91ea064980 | ||
|
|
df9836cce0 | ||
|
|
493a37ba5e | ||
|
|
9371737331 | ||
|
|
54d2028920 | ||
|
|
ea67ca22a9 | ||
|
|
5216bbab93 | ||
|
|
47ae76fb7d | ||
|
|
e3b46f25a5 | ||
|
|
43b7bc52ca | ||
|
|
af6a21f5d9 | ||
|
|
2871bdb206 | ||
|
|
271e2e4c49 | ||
|
|
14c50e993b | ||
|
|
45146bc798 | ||
|
|
bf76d9cd8b | ||
|
|
2db1ffef1e | ||
|
|
0e01b91c7e | ||
|
|
e485a16993 | ||
|
|
5861fcf443 | ||
|
|
42c5af3cdf | ||
|
|
87adcc2e6b | ||
|
|
5265ed6b64 | ||
|
|
bf5cc212e1 | ||
|
|
48077a5757 | ||
|
|
86c5b5d944 | ||
|
|
579753b0fc | ||
|
|
2d61519ec6 | ||
|
|
f38dade578 | ||
|
|
fe891746bf | ||
|
|
a0e501c3a9 | ||
|
|
caeeebf572 | ||
|
|
888a1b38aa | ||
|
|
b2e4276bc8 | ||
|
|
86b1305e35 | ||
|
|
4de1deefc4 | ||
|
|
0b82289950 | ||
|
|
4f247bab4e | ||
|
|
a9c5fd2cc0 | ||
|
|
8f30b8b586 | ||
|
|
eb0a88d39c | ||
|
|
75066813ee | ||
|
|
f6a517c998 | ||
|
|
310eec07c1 | ||
|
|
cb5f2559ea | ||
|
|
f08d2ee759 | ||
|
|
9d63efe495 | ||
|
|
b9cce57db4 | ||
|
|
882adc8e50 | ||
|
|
a82c76d2f9 | ||
|
|
3983587682 | ||
|
|
083214f85a | ||
|
|
ed6a182cd1 | ||
|
|
8b6baa250c | ||
|
|
7cd5e681dd | ||
|
|
47a85bbb1d | ||
|
|
a5ab0b9100 | ||
|
|
10d6803b05 | ||
|
|
8049d3f738 | ||
|
|
69ed7c543f | ||
|
|
f5420333e2 | ||
|
|
57cb300759 | ||
|
|
a358a192c4 | ||
|
|
a2371370ff | ||
|
|
4991301f36 | ||
|
|
c14dcfbfe4 | ||
|
|
2b0415e238 | ||
|
|
5be7a97a16 | ||
|
|
ee44e742f6 | ||
|
|
878203f1d0 | ||
|
|
0d1632a5d2 | ||
|
|
2e61ae244a | ||
|
|
169cc75c88 | ||
|
|
a6115687aa | ||
|
|
a9c8163ab3 | ||
|
|
a204b7f3e7 | ||
|
|
c215838531 | ||
|
|
6ece3c2b46 | ||
|
|
e80c1ad91f | ||
|
|
a80860cdc6 | ||
|
|
f3bb0a676e | ||
|
|
d85d009a54 | ||
|
|
955344e175 | ||
|
|
c9c0c7f24f | ||
|
|
bdc54bcda7 | ||
|
|
887849857d | ||
|
|
7690625114 | ||
|
|
ba981c525b | ||
|
|
6b9ddf1f65 | ||
|
|
e3e741251f | ||
|
|
aa656f7542 | ||
|
|
4a0aac8505 | ||
|
|
d0ecabad19 | ||
|
|
59e4a6ff7b | ||
|
|
f575139180 | ||
|
|
d4fd8780e9 | ||
|
|
306388a6bc | ||
|
|
2f27a0c9f9 | ||
|
|
8285878504 | ||
|
|
bbb2637bcc | ||
|
|
83490e9a03 | ||
|
|
a517a05ca8 | ||
|
|
30a00b22c9 | ||
|
|
0e5f89a03c | ||
|
|
6853f491f4 | ||
|
|
10739b11ee | ||
|
|
6c108e43d9 | ||
|
|
cf92e1eee7 | ||
|
|
83c6406167 | ||
|
|
cc305ed766 | ||
|
|
296e268339 | ||
|
|
3c96e62be7 | ||
|
|
8816aa1431 | ||
|
|
205b6fe6d7 | ||
|
|
5da392ebfe | ||
|
|
1afc03b9b5 | ||
|
|
9e0b112f05 | ||
|
|
490168fb05 | ||
|
|
1dffbcd0bd | ||
|
|
81c0e66b1d | ||
|
|
fc0b18cf61 | ||
|
|
cd2c9e9ca3 | ||
|
|
6b4ca31783 | ||
|
|
1c3239972c | ||
|
|
75c4d6a8a0 | ||
|
|
61008fd3d0 | ||
|
|
b093aaaf27 | ||
|
|
43f7eede0b | ||
|
|
c007c9460c | ||
|
|
f34d1ee997 | ||
|
|
97bbb12e06 | ||
|
|
d52f95d24d | ||
|
|
df8c399efb | ||
|
|
4334acb6f2 | ||
|
|
a8aa8e3bb4 | ||
|
|
0629ce00de | ||
|
|
ac6acfb660 | ||
|
|
058a04f756 | ||
|
|
c7fbddce54 | ||
|
|
ba0dbd5871 | ||
|
|
19f6cc00c8 | ||
|
|
5aee715931 | ||
|
|
68ea3e7b49 | ||
|
|
d5955f1ae1 | ||
|
|
30bf2aade4 | ||
|
|
61973c399e | ||
|
|
c75e2d306d | ||
|
|
c2d0fcfbe6 | ||
|
|
29ddc76e2f | ||
|
|
8444388ec7 | ||
|
|
1552c108b0 | ||
|
|
b05d76a131 | ||
|
|
0919746f1a | ||
|
|
ea0a0522a7 | ||
|
|
a7670fbcab | ||
|
|
12936ff5fe | ||
|
|
b009886664 | ||
|
|
a74d423d82 | ||
|
|
64b1aeaecd | ||
|
|
0aa632d149 | ||
|
|
9c9c5c09ff | ||
|
|
f6122c8a6c | ||
|
|
5b26d41d27 | ||
|
|
a50b193c40 | ||
|
|
15476c2513 | ||
|
|
79f13cae55 | ||
|
|
705970cedd | ||
|
|
84883d115d | ||
|
|
8ba01abcd6 | ||
|
|
2185a654de | ||
|
|
5d0e72755d | ||
|
|
1c56573194 | ||
|
|
dd95131630 | ||
|
|
4807f50c00 | ||
|
|
8c42545d1c | ||
|
|
fd0c386a4c | ||
|
|
5e6f042f6e | ||
|
|
75b06d8a25 | ||
|
|
4820be3b10 | ||
|
|
9180257afe | ||
|
|
f9333fc551 | ||
|
|
42980a1ab4 | ||
|
|
2df30dc107 | ||
|
|
446c738f20 | ||
|
|
d79596354e | ||
|
|
951df380a9 | ||
|
|
06e59f3b17 | ||
|
|
1bec58dee5 | ||
|
|
062250741a | ||
|
|
a81d359669 | ||
|
|
1b61296ea5 | ||
|
|
27c368a444 | ||
|
|
3592b09d56 | ||
|
|
a31bf75169 | ||
|
|
2539e3247a | ||
|
|
2ed572095c | ||
|
|
933412eb8d | ||
|
|
9a80ab31c4 | ||
|
|
39640efc9b | ||
|
|
2b88a2aa0c | ||
|
|
f885751107 | ||
|
|
764a987b09 | ||
|
|
538bf7c321 | ||
|
|
fc69acee46 | ||
|
|
b7448d55ed | ||
|
|
4b069d41f6 | ||
|
|
a0dd3d9e75 | ||
|
|
f651bc3668 | ||
|
|
d8d9073bc2 | ||
|
|
e2b1f6ac50 | ||
|
|
0452512de2 | ||
|
|
f88c8a64a1 | ||
|
|
b33daa3d3a | ||
|
|
4be2347a30 | ||
|
|
83cbc86f50 | ||
|
|
c9c45808b4 | ||
|
|
91d8b3da23 | ||
|
|
f24e310ace | ||
|
|
ffdfc0549a | ||
|
|
987bfa6ca7 | ||
|
|
8c2fddb297 | ||
|
|
b0e652a3af | ||
|
|
9e0cf5a2fd | ||
|
|
3607d50994 | ||
|
|
02bfa1ca57 | ||
|
|
0621e65827 | ||
|
|
91b2ee2f10 | ||
|
|
4c6f4ef14b | ||
|
|
d0b307ecfb | ||
|
|
9505846088 | ||
|
|
33ee947f8d | ||
|
|
57ef989a89 | ||
|
|
0590e2a5fb | ||
|
|
5b13232a9d | ||
|
|
9133adac30 | ||
|
|
04892df45a | ||
|
|
af7d633f2f | ||
|
|
ef80263106 | ||
|
|
5d63a76e25 | ||
|
|
b956238efa | ||
|
|
a17b0d4e5c | ||
|
|
b089e6d84e | ||
|
|
3a20ca96c4 | ||
|
|
c4e8af983a | ||
|
|
682a71176d | ||
|
|
83ca4ef6d9 | ||
|
|
a86cbd884e | ||
|
|
3323f7ab1a | ||
|
|
9f54b1065a | ||
|
|
9c1021134a | ||
|
|
2d1278ece5 | ||
|
|
baffb0ed89 | ||
|
|
a2e9c2f4ab | ||
|
|
c13bf2a2a1 | ||
|
|
73653f77aa | ||
|
|
8c6d58e6d8 | ||
|
|
0e149f0523 | ||
|
|
40d98ad678 | ||
|
|
9913221010 | ||
|
|
f230a37004 | ||
|
|
11b8d4191f | ||
|
|
2c64fa50d2 | ||
|
|
7f7f90681f | ||
|
|
0bce8234d8 | ||
|
|
745ece6e6d | ||
|
|
eac0222f2c | ||
|
|
70e41b180e | ||
|
|
9762ce706b | ||
|
|
041aff6bfd | ||
|
|
2a44cd8c98 | ||
|
|
54aec7bb96 | ||
|
|
064aba810b | ||
|
|
5aec84b672 | ||
|
|
cbd577694c | ||
|
|
a315640082 | ||
|
|
f06632a8e7 | ||
|
|
aa91c26792 | ||
|
|
4521a9fdf0 | ||
|
|
65a4f36cf8 | ||
|
|
70b9b002cb | ||
|
|
682a2aae3a | ||
|
|
ce27acd099 | ||
|
|
fb1385b3e8 | ||
|
|
938d003e5e | ||
|
|
25792b2a45 | ||
|
|
cc8b581c06 | ||
|
|
cca77ed65c | ||
|
|
b9a1a451a9 | ||
|
|
62aa7b75bd | ||
|
|
0679142607 | ||
|
|
3463c28e24 | ||
|
|
e3fed55945 | ||
|
|
085701c7db | ||
|
|
51188aa93f | ||
|
|
5e4498a53a | ||
|
|
a62772c274 | ||
|
|
924b7320bc | ||
|
|
bb6e6f4808 | ||
|
|
ada30800c9 | ||
|
|
99ba80d492 | ||
|
|
ebe0988d9a | ||
|
|
11d7a0b712 | ||
|
|
d41e517757 | ||
|
|
ec2e4f432a | ||
|
|
74957dcb2e | ||
|
|
6359c44622 | ||
|
|
1ce9426adf | ||
|
|
98f68cb053 | ||
|
|
490df2027b | ||
|
|
d4f1a9602f | ||
|
|
eb26b4a04b | ||
|
|
c8e7df7900 | ||
|
|
600e5bad0d | ||
|
|
af1b04de9c | ||
|
|
a3cf721b9e | ||
|
|
679b0f9b73 | ||
|
|
175a06fe73 | ||
|
|
799e099d1d | ||
|
|
3d61c81456 | ||
|
|
f48c418d6d | ||
|
|
ec4cb7c90f | ||
|
|
cb4ce36d3c | ||
|
|
b0983cb726 | ||
|
|
b57a58c253 | ||
|
|
24c9bb2fb7 | ||
|
|
168fc4170d | ||
|
|
f3bde56de9 | ||
|
|
60a023d064 | ||
|
|
35d9005eae | ||
|
|
f3fc56a167 | ||
|
|
d19d37bf9b | ||
|
|
c1c63d0c28 | ||
|
|
46e62cd963 | ||
|
|
02e91b3902 | ||
|
|
45cf6344cd | ||
|
|
e9b4e571e1 | ||
|
|
447eb23356 | ||
|
|
3ae5f13c3d | ||
|
|
089bb33113 | ||
|
|
ba990f72f2 | ||
|
|
987b573709 | ||
|
|
dea8dde566 | ||
|
|
e17071723f | ||
|
|
8dcf7926de | ||
|
|
d09c3bf863 | ||
|
|
1a92fa5d92 | ||
|
|
8d2ad4ed17 | ||
|
|
5317022d2e | ||
|
|
89098f54be | ||
|
|
5f659f6e48 | ||
|
|
10323ac819 | ||
|
|
ee46717c76 | ||
|
|
914e621d1b | ||
|
|
976190e84d | ||
|
|
06eb93da3f | ||
|
|
7197f41e75 | ||
|
|
5488872044 | ||
|
|
413ac4e8f4 | ||
|
|
e95dc82087 | ||
|
|
cec6cd0830 | ||
|
|
39a12a8464 | ||
|
|
b11cb88a9f | ||
|
|
9a9bbe3123 | ||
|
|
46eb27cd01 | ||
|
|
ba98c0c1cb | ||
|
|
fc8b439263 | ||
|
|
a358ea8667 | ||
|
|
c294b75f6c | ||
|
|
d5e17f9ebf | ||
|
|
c7ea7ca5cd | ||
|
|
49c656d904 | ||
|
|
79549c2285 | ||
|
|
787f36f056 | ||
|
|
d47c4732e2 | ||
|
|
b7b229d59b | ||
|
|
dfb27d170c | ||
|
|
64fcbe05c3 | ||
|
|
31c34870ef | ||
|
|
3c17ac424d | ||
|
|
bb6c079e5a | ||
|
|
a3c1975a84 | ||
|
|
bdd78d2bc7 | ||
|
|
29db42c3cd | ||
|
|
e6df8164cf | ||
|
|
cf03bd8bd1 | ||
|
|
342c14887b | ||
|
|
e1101e582e | ||
|
|
768203bd36 | ||
|
|
c51fb00082 | ||
|
|
0a92b04c8b | ||
|
|
15161d8867 | ||
|
|
53ee465726 | ||
|
|
a20acfee25 | ||
|
|
f69787afd0 | ||
|
|
9b12980688 | ||
|
|
cb0f82c36e | ||
|
|
3b678bfbc5 | ||
|
|
e1750adc38 | ||
|
|
fd0fb9483e | ||
|
|
3d1d491e6b | ||
|
|
18020707b8 | ||
|
|
cfa0d46b73 | ||
|
|
e027d514f1 | ||
|
|
d5f675c2dc | ||
|
|
ded3088529 | ||
|
|
78c12dc505 | ||
|
|
ad08ccb50b | ||
|
|
95751fcc21 | ||
|
|
13417dbf14 | ||
|
|
ff21662b23 | ||
|
|
6967b06dee | ||
|
|
fc58ada92e | ||
|
|
108118afa3 | ||
|
|
d8bb5273e7 | ||
|
|
c792567904 | ||
|
|
6d9a88d1c8 | ||
|
|
3520fed752 | ||
|
|
d10dbbdd9d | ||
|
|
6bf1e87bbe | ||
|
|
91f46624b6 | ||
|
|
94f32d2985 | ||
|
|
569426b04e | ||
|
|
b0852f6c16 | ||
|
|
b985ddb868 | ||
|
|
079769ed2e | ||
|
|
fc6af0476f | ||
|
|
03bd7d7f96 | ||
|
|
92ffd8c465 | ||
|
|
b0836a620c | ||
|
|
06b36f742e | ||
|
|
c977cfe40a | ||
|
|
e0921ac983 | ||
|
|
7e7dfe2cc4 | ||
|
|
91a8b9fdd9 | ||
|
|
8debae1a3b | ||
|
|
547cbb6322 | ||
|
|
1adc5c2a5b | ||
|
|
6901d9d9c2 | ||
|
|
9f59bc8f7b | ||
|
|
d39df18544 | ||
|
|
e664711f47 | ||
|
|
7d3a219f63 | ||
|
|
23e4ad1abb | ||
|
|
3e1bc66984 | ||
|
|
49f8f46354 | ||
|
|
26d2fbd217 | ||
|
|
913a679ef5 | ||
|
|
aebde189f8 | ||
|
|
66c206cc61 | ||
|
|
c9640ffdbc | ||
|
|
24214002a1 | ||
|
|
eba1b0bc15 | ||
|
|
289660067c | ||
|
|
854f2a046a | ||
|
|
69541d3628 | ||
|
|
70e1724463 | ||
|
|
cbdabe35de | ||
|
|
c9c41252e3 | ||
|
|
3437cf2909 | ||
|
|
d7afd86a27 | ||
|
|
38ca5aba98 | ||
|
|
4e6a8d991e | ||
|
|
cb0a567c03 | ||
|
|
032ac50034 | ||
|
|
3e5f7d0db5 | ||
|
|
b08eabec68 | ||
|
|
a4a9e2aa96 | ||
|
|
3189c578a4 | ||
|
|
9b969e15fc | ||
|
|
c2b356ab08 | ||
|
|
ceb9a0bd6b | ||
|
|
a031b2a090 | ||
|
|
b52a2cd292 | ||
|
|
f2fbeed490 | ||
|
|
81adb7dd2a | ||
|
|
6be4b3bac6 | ||
|
|
4841c3037d | ||
|
|
0919042692 | ||
|
|
13741ba137 | ||
|
|
18de9f0aa3 | ||
|
|
f14e3f6007 | ||
|
|
83fb41e414 | ||
|
|
d0b9920cac | ||
|
|
51d2b5225e | ||
|
|
563e8a2bd6 | ||
|
|
a30554e97c | ||
|
|
ef6e502ff0 | ||
|
|
93daaf5b5b | ||
|
|
70489b2fc2 | ||
|
|
d4564d5dd1 | ||
|
|
27c45d8dda | ||
|
|
49f8fd2164 | ||
|
|
0c5d642489 | ||
|
|
6f03c3e252 | ||
|
|
8faeab18b9 | ||
|
|
21ed5c430d | ||
|
|
3123abfac3 | ||
|
|
805d1d170c | ||
|
|
522c6e01d2 | ||
|
|
14a31a2299 | ||
|
|
35baff8bac | ||
|
|
5969c227ab | ||
|
|
8badba26b8 | ||
|
|
6c07a3e260 | ||
|
|
24e3ad4e18 | ||
|
|
a66f83644b | ||
|
|
364dab6990 | ||
|
|
b960857fc2 | ||
|
|
65d3373ad3 | ||
|
|
3108817717 | ||
|
|
d6fd83dd6c | ||
|
|
d44f279339 | ||
|
|
a83bb39d0f | ||
|
|
88a257fcdc | ||
|
|
d62f76afa6 | ||
|
|
97c2917c16 | ||
|
|
3cd675bfff | ||
|
|
478093aa89 | ||
|
|
dcae1c5c04 | ||
|
|
6e9bee1be7 | ||
|
|
25d6e00b1a | ||
|
|
a47897bdf9 | ||
|
|
2c41de6648 | ||
|
|
dd1bed02e8 | ||
|
|
bd5edc7ae5 | ||
|
|
dfe932d053 | ||
|
|
99881db8bd | ||
|
|
0c1f3ed0b3 | ||
|
|
a811ab3aff | ||
|
|
a055c86c4f | ||
|
|
237a7d34b8 | ||
|
|
5a28a796af | ||
|
|
4adb0c75bd | ||
|
|
4a16be2cba | ||
|
|
f8e6ba633a | ||
|
|
c7c8e2f3e3 | ||
|
|
eaf05305ff | ||
|
|
044623a360 | ||
|
|
07c05528ef | ||
|
|
c72e385a47 | ||
|
|
797966fd3d | ||
|
|
82d463e86e | ||
|
|
97c0f1c7b7 | ||
|
|
64507ab316 | ||
|
|
b9c4abe7dc | ||
|
|
6d315a5d16 | ||
|
|
187b7e117c | ||
|
|
f0e7be7d56 | ||
|
|
c6c1ad1b90 | ||
|
|
07fe29cc67 | ||
|
|
a9add04ee3 | ||
|
|
d3d708bc68 | ||
|
|
4929c66e60 | ||
|
|
3ef09da1df | ||
|
|
9e41f43ee2 | ||
|
|
e7c82d7370 | ||
|
|
24668b2281 | ||
|
|
51243454c8 | ||
|
|
2cbad4aed6 | ||
|
|
90bc138049 | ||
|
|
3247794e2f | ||
|
|
e946f49b64 | ||
|
|
0bff1b4afb | ||
|
|
99167539fb | ||
|
|
a3de94e868 | ||
|
|
8232698254 | ||
|
|
6a78aa7840 | ||
|
|
7383988988 | ||
|
|
e906ded0d1 | ||
|
|
032a7e71fe | ||
|
|
b73a2f7d56 | ||
|
|
4d7aa5c945 | ||
|
|
af8b2b6d9c | ||
|
|
b6bcf9fa44 | ||
|
|
b393c6a285 | ||
|
|
c36292bfd0 | ||
|
|
a67db45454 | ||
|
|
64685f31dc | ||
|
|
1111afc031 | ||
|
|
54dbd7c0bd | ||
|
|
ef5bf87672 | ||
|
|
eb45e67784 | ||
|
|
1eacbd88b8 | ||
|
|
a89bd32eb0 | ||
|
|
56a2dc632b | ||
|
|
7dded52de2 | ||
|
|
8425a94729 | ||
|
|
7bf55fbc49 | ||
|
|
39349f3763 | ||
|
|
60c6158152 | ||
|
|
225e70a8d0 | ||
|
|
338a6f2114 | ||
|
|
cd7c7c3152 | ||
|
|
84748cda76 | ||
|
|
f94a61cc8a | ||
|
|
b51ffadd27 | ||
|
|
0f2c50f1f5 | ||
|
|
c8e2b027ee | ||
|
|
839c9e35c8 | ||
|
|
46eec3c8eb | ||
|
|
6ae11b5b2c | ||
|
|
ff733e0334 | ||
|
|
693d729ec6 | ||
|
|
2150c1d58e | ||
|
|
1273b063f4 | ||
|
|
a755633405 | ||
|
|
19579f0d9a | ||
|
|
ca583bffd5 | ||
|
|
5fee6d2d19 | ||
|
|
10f0f3038c | ||
|
|
73aba09eee | ||
|
|
035d655e72 | ||
|
|
020aa4d94c | ||
|
|
58d2d5d14e | ||
|
|
0277601705 | ||
|
|
72bf390ec5 | ||
|
|
1c1c46591e | ||
|
|
9f10018d48 | ||
|
|
783233dfe4 | ||
|
|
f18c163408 | ||
|
|
8022530f34 | ||
|
|
28e5dcef52 | ||
|
|
37ec83a68b | ||
|
|
94b5c4eada | ||
|
|
d3caa80274 | ||
|
|
f015cea590 | ||
|
|
05dd3fa0e7 | ||
|
|
0198cf6318 | ||
|
|
236ffc8972 | ||
|
|
563878d28d | ||
|
|
5c73fed83a | ||
|
|
c85cc1455b | ||
|
|
080867a390 | ||
|
|
a3e1f54e33 | ||
|
|
509a3493b6 | ||
|
|
09ef8f639e | ||
|
|
83cc098412 | ||
|
|
3cd08bc724 | ||
|
|
fe1f9878ba | ||
|
|
e874fbbea2 | ||
|
|
5dc6e13ab5 | ||
|
|
4d2036fa26 | ||
|
|
c30e7ec41a | ||
|
|
181eb803e1 | ||
|
|
37c92178a5 | ||
|
|
5e265f45e1 | ||
|
|
9926892c8a | ||
|
|
9c478c502e | ||
|
|
562c8b97ad | ||
|
|
6f408f949c | ||
|
|
8f4df8603a | ||
|
|
f76ce8b33b | ||
|
|
33ef634ea8 | ||
|
|
05d83e487d | ||
|
|
21079a1315 | ||
|
|
d3cf697b07 | ||
|
|
5ed9949498 | ||
|
|
2e08c5dd2b | ||
|
|
c3c73377b8 | ||
|
|
023264660b | ||
|
|
0e7cbbfeb8 | ||
|
|
e58b90ef1c | ||
|
|
e159351179 | ||
|
|
30178d4f23 | ||
|
|
90df3fa94c | ||
|
|
49c6a56f97 | ||
|
|
148443fae1 | ||
|
|
2cada386b4 | ||
|
|
905be67aae | ||
|
|
c0fd44c909 | ||
|
|
3abe1b4fc6 | ||
|
|
c4714b55a3 | ||
|
|
65f4ec403f | ||
|
|
947ab8a14d | ||
|
|
3f736d3eb8 | ||
|
|
b228398b87 | ||
|
|
8ea7a28a77 | ||
|
|
758b6bd4dd | ||
|
|
0e7afb24cf | ||
|
|
9ad51fbc02 | ||
|
|
cf149bd8c8 | ||
|
|
03db15af9a | ||
|
|
0b83d033d7 | ||
|
|
0f6e845418 | ||
|
|
01a06d1f5c | ||
|
|
e49cd83868 | ||
|
|
8485b6f0b3 | ||
|
|
220f2ded85 | ||
|
|
3f7d6e6f85 | ||
|
|
2a9e3da24f | ||
|
|
b5db4047a0 | ||
|
|
c60eded2de | ||
|
|
baab70bea6 | ||
|
|
b25b19f71b | ||
|
|
5a7785776c | ||
|
|
fdbf5f73b1 | ||
|
|
cc0d86403e | ||
|
|
48b763c7e9 | ||
|
|
7ce87a7118 | ||
|
|
c8c7a1f772 | ||
|
|
e3ed6c2523 | ||
|
|
23e28ae5d4 | ||
|
|
2a03a84315 | ||
|
|
68ab210dc8 | ||
|
|
ffd51e725f | ||
|
|
9585481d0b | ||
|
|
59f15eb4eb | ||
|
|
02a0eed8ee | ||
|
|
3b6c8c5191 | ||
|
|
136d04390d | ||
|
|
8fd848701e | ||
|
|
e5b84fb795 | ||
|
|
5d737934c3 | ||
|
|
367a53dd71 | ||
|
|
ca87768a93 | ||
|
|
44dca68463 | ||
|
|
406466de9a | ||
|
|
adbeba291b | ||
|
|
6cff0d0376 | ||
|
|
bc22e0d9aa | ||
|
|
4e93330cb9 | ||
|
|
abd770a027 | ||
|
|
a1ad1ddc10 | ||
|
|
d37c14880f | ||
|
|
9e63aa9d84 | ||
|
|
b936a04826 | ||
|
|
c62a21e04f | ||
|
|
6d12c4aab1 | ||
|
|
26eafcb55a | ||
|
|
c2d2037726 | ||
|
|
0640b41f00 | ||
|
|
8b7fad8595 | ||
|
|
49f5f1e2c2 | ||
|
|
6af5c5fc86 | ||
|
|
26dbf058c8 | ||
|
|
1c7982b319 | ||
|
|
fcc0f1d5a7 | ||
|
|
e439b7d7f8 | ||
|
|
b740cf9664 | ||
|
|
097927226b | ||
|
|
f8d1e2ac11 | ||
|
|
1f7990d6bb | ||
|
|
a484e9fb06 | ||
|
|
36e27f2aa4 | ||
|
|
b69033f4ff | ||
|
|
6c32b92929 | ||
|
|
98a12cef26 | ||
|
|
abbd1d1dc5 | ||
|
|
104873e8ee | ||
|
|
6811441459 | ||
|
|
b7fc068cee | ||
|
|
023c533745 | ||
|
|
cb8096f636 | ||
|
|
cca9ad06b4 | ||
|
|
76e4077b56 | ||
|
|
24addd5c10 | ||
|
|
e92b9cbe99 | ||
|
|
f9918cc63c | ||
|
|
6c82daef3d | ||
|
|
3d5192d6d3 | ||
|
|
a30f697537 | ||
|
|
e71173d953 | ||
|
|
57b5b2af2e | ||
|
|
25e1da0150 | ||
|
|
63e28c57cd | ||
|
|
199e015a06 | ||
|
|
b505662ef9 | ||
|
|
d52616b687 | ||
|
|
3f5ab60fb4 | ||
|
|
93f9097b02 | ||
|
|
2ccc6dc092 | ||
|
|
8fa3cefb8c | ||
|
|
d150c9a6be | ||
|
|
bf7c26e681 | ||
|
|
4d24be04a1 | ||
|
|
b889674486 | ||
|
|
8569d261f7 | ||
|
|
8e98dcefb1 | ||
|
|
3983aceb48 | ||
|
|
bac80bf686 | ||
|
|
f402475dd3 | ||
|
|
69fe2a36e5 | ||
|
|
ba7cdec2ea | ||
|
|
a9694bf0ef | ||
|
|
a714966e9b | ||
|
|
930a36df37 | ||
|
|
034899367d | ||
|
|
47a543e086 | ||
|
|
3bdc92ba8e | ||
|
|
122ffca049 | ||
|
|
24d740b2da | ||
|
|
bf5a46f6d8 | ||
|
|
b51ce1d2b3 | ||
|
|
8f152a5bfb | ||
|
|
7b764aec92 | ||
|
|
3c7b39f089 | ||
|
|
f202ddc5aa | ||
|
|
80f5ec29d4 | ||
|
|
1d8fae44cc | ||
|
|
b60f1cd531 | ||
|
|
9fdcacd865 | ||
|
|
ab862276fc | ||
|
|
12aeaeed56 | ||
|
|
67c6b35845 | ||
|
|
0e8064dbf9 | ||
|
|
47b5165f2a | ||
|
|
818e75bb8f | ||
|
|
8e9ac18026 | ||
|
|
902fa7d44a | ||
|
|
acd1acd869 | ||
|
|
befd1a7ccc | ||
|
|
abdd3a5dbe | ||
|
|
05cc6bcf8a | ||
|
|
f24d7c4212 | ||
|
|
487ebdf173 | ||
|
|
ee7ccd7936 | ||
|
|
89a6cdc711 | ||
|
|
3404bcf265 | ||
|
|
4fbb165dce | ||
|
|
9a35a699cb | ||
|
|
68ed3250e8 | ||
|
|
f58177f292 | ||
|
|
1d76578202 | ||
|
|
c86311e879 | ||
|
|
342b2df93f | ||
|
|
3172d5727a | ||
|
|
d0563c80be | ||
|
|
2db039fb77 | ||
|
|
dde07fd2ee | ||
|
|
4ab9b81a9a | ||
|
|
4628f880b4 | ||
|
|
e696eaaa2f | ||
|
|
3ba9e80635 | ||
|
|
1707d67adb | ||
|
|
29ad3bf7f8 | ||
|
|
31739cdae6 | ||
|
|
2730423ab2 | ||
|
|
5d048a9518 | ||
|
|
cc1374b832 | ||
|
|
0abfb00032 | ||
|
|
26e8e89aca | ||
|
|
353c0a9ee7 | ||
|
|
3cf28ad6ce | ||
|
|
18b05bc56e | ||
|
|
54012eba23 | ||
|
|
131d63c374 | ||
|
|
d117593d72 | ||
|
|
5009ed618f | ||
|
|
edbaceceb3 | ||
|
|
fb22931e2d | ||
|
|
e8fc3c8ead | ||
|
|
9c0f18b88d | ||
|
|
95046b9bb1 | ||
|
|
451a46bf0e | ||
|
|
5e7a3ca2e6 | ||
|
|
62ecab8432 | ||
|
|
f1c3a11103 | ||
|
|
d83ed33252 | ||
|
|
9b488207eb | ||
|
|
9d31641bb1 | ||
|
|
655236c70d | ||
|
|
b47939c737 | ||
|
|
d940085384 | ||
|
|
bdd135dbff | ||
|
|
701d0bcdca | ||
|
|
4397371a50 | ||
|
|
b9b34eb0ee | ||
|
|
3b6cc97557 | ||
|
|
0ebbb333ba | ||
|
|
38cc9bef02 | ||
|
|
29028c5d46 | ||
|
|
2d03840fde | ||
|
|
2eb11731e2 | ||
|
|
02fd63ce20 | ||
|
|
5d137ce9c5 | ||
|
|
db78e3a7da | ||
|
|
b45743b562 | ||
|
|
27f2d417c1 | ||
|
|
7cfa08abc8 | ||
|
|
649c2ce188 | ||
|
|
af0b9abab7 | ||
|
|
2c93bce9ad | ||
|
|
0673355f31 | ||
|
|
b20232db3c | ||
|
|
1bbadb57a2 | ||
|
|
a756ffa3a6 | ||
|
|
296d10fe2a | ||
|
|
3b3350e648 | ||
|
|
3e17fdcaa3 | ||
|
|
32b3e416b3 | ||
|
|
97ed9edd32 | ||
|
|
5b8b27a2aa | ||
|
|
ad04099ac2 | ||
|
|
4c74709019 | ||
|
|
13c4b93d3d | ||
|
|
9d5e48430e | ||
|
|
a0bf170d02 | ||
|
|
ec3990c619 | ||
|
|
c4956a4ade | ||
|
|
6c5596d17e | ||
|
|
270dbd2bf7 | ||
|
|
6c44b0e6e7 | ||
|
|
c0e65e71b4 | ||
|
|
eef044f4d0 | ||
|
|
abe6c90829 | ||
|
|
0a17ab9325 | ||
|
|
cc7471f37d | ||
|
|
e27b3162e5 | ||
|
|
cbb37f70c4 | ||
|
|
a1b7437f8d | ||
|
|
1fe9e9262f | ||
|
|
a173d9593b | ||
|
|
fb90bb4241 | ||
|
|
ec5435befd | ||
|
|
55c605998c | ||
|
|
96a34b6165 | ||
|
|
5d58edb3b9 | ||
|
|
44f477d552 | ||
|
|
b47507293a | ||
|
|
9ab6c29cd3 | ||
|
|
baca9edbb1 | ||
|
|
a9c409403c | ||
|
|
cd26d97dd7 | ||
|
|
5d37748973 | ||
|
|
67eaa1b735 | ||
|
|
db1de18cc2 | ||
|
|
ceaf2b3727 | ||
|
|
7c94dd94e9 | ||
|
|
1f6df4e70d | ||
|
|
1af39f0776 | ||
|
|
2e9876f58f | ||
|
|
f91bd91d02 | ||
|
|
2b7882e6e5 | ||
|
|
3f5b9d0f54 | ||
|
|
e93dc0b4c4 | ||
|
|
9b198c6d0a | ||
|
|
4a5f70e6c8 | ||
|
|
eee9b3f39e | ||
|
|
352eab0eca | ||
|
|
190bc2f0da | ||
|
|
4e261c61ae | ||
|
|
800801177d | ||
|
|
b802d7903a | ||
|
|
ee98c0c587 | ||
|
|
1bc16fb31e | ||
|
|
81f4822b8d | ||
|
|
be1d4c04f2 | ||
|
|
726feb3f4d | ||
|
|
f90d1fd70e | ||
|
|
9e66ee1da0 | ||
|
|
3641b28c3e | ||
|
|
a6b47208e1 | ||
|
|
2d9b4b33d4 | ||
|
|
da3b7a2b69 | ||
|
|
ff73e46c95 | ||
|
|
1ead522705 | ||
|
|
efd5dc94e6 | ||
|
|
eba414e31b | ||
|
|
3bc70f0ce6 | ||
|
|
7a0fc6ae61 | ||
|
|
41891959a3 | ||
|
|
2d13906e0e | ||
|
|
24d43689b2 | ||
|
|
8830f1531f | ||
|
|
acb055400d | ||
|
|
d1a1f57e77 | ||
|
|
995a8192a9 | ||
|
|
3d4db42da4 | ||
|
|
469993f6d3 | ||
|
|
43effd2b40 | ||
|
|
bea8a457a2 | ||
|
|
1ab86892a0 | ||
|
|
5b229e9392 | ||
|
|
79c3ccd56e | ||
|
|
d7934865c9 | ||
|
|
f7ad894495 | ||
|
|
5ac32f145f | ||
|
|
22df141761 | ||
|
|
51c0ceea38 | ||
|
|
5f5285955b | ||
|
|
b99c075282 | ||
|
|
8f52089475 | ||
|
|
203ca3f91b | ||
|
|
fcd346c2af | ||
|
|
bcaf0658e4 | ||
|
|
ebdda885f9 | ||
|
|
e8644f6f2a | ||
|
|
b48caaf465 | ||
|
|
8729701b66 | ||
|
|
29cb067769 | ||
|
|
ae8408bcab | ||
|
|
4998a48f99 | ||
|
|
cd5a5347fc | ||
|
|
246302453f | ||
|
|
79ff7baaf6 | ||
|
|
3a9cf639bd | ||
|
|
5fa9f16c01 | ||
|
|
f6366e1e1f | ||
|
|
207d8f6030 | ||
|
|
9af4d560dd | ||
|
|
5014ef2337 | ||
|
|
43ccc14162 | ||
|
|
e3a49f8213 | ||
|
|
270b56af1b | ||
|
|
39a88d2e43 | ||
|
|
85e1c87d14 | ||
|
|
8d4af3ad81 | ||
|
|
ddbaf585ec | ||
|
|
e4d22ea628 | ||
|
|
8b0d5a2e7b | ||
|
|
286c102358 | ||
|
|
863eede75b | ||
|
|
913990bc62 | ||
|
|
c6eb795e76 | ||
|
|
51475d2fb0 | ||
|
|
ab5c1d6bdd | ||
|
|
7149ad8ac4 | ||
|
|
cd646c819d | ||
|
|
9ed14b438e | ||
|
|
35b0e83370 | ||
|
|
4b02e266fd | ||
|
|
7d3131ca49 | ||
|
|
89ce04dcb9 | ||
|
|
6e025186ab | ||
|
|
abaa0633d7 | ||
|
|
d2f833d02c | ||
|
|
2edb32f344 | ||
|
|
6ede08e3c9 | ||
|
|
8c37e90a77 | ||
|
|
f2047ee4d0 | ||
|
|
d962fc4ce1 | ||
|
|
c110508b4e | ||
|
|
a0b712d44b | ||
|
|
d73604d1c5 | ||
|
|
f755659f5d | ||
|
|
d1ab2d2e8c | ||
|
|
fdb4a2acdb | ||
|
|
75d367a6c5 | ||
|
|
ee13efbffd | ||
|
|
dbda1bf5c0 | ||
|
|
2e995839bb | ||
|
|
81a9ce2baa | ||
|
|
dbc95cadb4 | ||
|
|
9f590dbf2d | ||
|
|
414bf12f86 | ||
|
|
88c6d4bb20 | ||
|
|
d55f18f8e3 | ||
|
|
f1744890b1 | ||
|
|
c34d6d1162 | ||
|
|
81dbe36e99 | ||
|
|
ba99e21875 | ||
|
|
91442e100c | ||
|
|
feb2303e1f | ||
|
|
1ab04a7276 | ||
|
|
2acf518037 | ||
|
|
fe143c7dfa | ||
|
|
b39bb24fcf | ||
|
|
1e88470ad8 | ||
|
|
9f4b7255aa | ||
|
|
c3ecae503b | ||
|
|
136c8b5192 | ||
|
|
ee8958ba03 | ||
|
|
c1d34d7d6f | ||
|
|
190bf90bc8 | ||
|
|
7ddf7ff211 | ||
|
|
d178fe4e5d | ||
|
|
93bc8aa7b2 | ||
|
|
7be179cf6c | ||
|
|
3928ffd30d | ||
|
|
b7e608abc9 | ||
|
|
dab626270d | ||
|
|
5df5805d36 | ||
|
|
e0d978fd58 | ||
|
|
cc838326e1 | ||
|
|
7daa6481e3 | ||
|
|
4b1f918feb | ||
|
|
cce3c0256e | ||
|
|
27f10123c7 | ||
|
|
8833ff7854 | ||
|
|
ebf17e10d6 | ||
|
|
607f2d66b8 | ||
|
|
999f22f548 | ||
|
|
9f02ae29ec | ||
|
|
7dd65d8ac6 | ||
|
|
5b0e26c814 | ||
|
|
4ff135e827 | ||
|
|
5cd0996d92 | ||
|
|
7be4b76abb | ||
|
|
0ee1e8bd97 | ||
|
|
881951368d | ||
|
|
ecda79834d | ||
|
|
e4901eda91 | ||
|
|
2a79817c3b | ||
|
|
4d4443c3cf | ||
|
|
f450476b27 | ||
|
|
cd6a60dc70 | ||
|
|
b223049682 | ||
|
|
b9d1b5584e | ||
|
|
63b7c6a8d9 | ||
|
|
f7f69dc3ab | ||
|
|
76c38a564d | ||
|
|
7fc536db15 | ||
|
|
a01fca5d48 | ||
|
|
13c5857241 | ||
|
|
a855074588 | ||
|
|
0de621edf9 | ||
|
|
789e2e48cf | ||
|
|
1903cb8f82 | ||
|
|
a762373ad6 | ||
|
|
81742528a2 | ||
|
|
1b6e1dbd13 | ||
|
|
285c659541 | ||
|
|
64b305cf7a | ||
|
|
49ae549e89 | ||
|
|
097c23e437 | ||
|
|
786edb72df | ||
|
|
d458464e6b | ||
|
|
d834cec9b9 | ||
|
|
8614563b42 | ||
|
|
5bff5188ac | ||
|
|
e865a290de | ||
|
|
e3765ced78 | ||
|
|
18440710b4 | ||
|
|
66bdbf4a28 | ||
|
|
dbf7487a9b | ||
|
|
34d7772a0d | ||
|
|
c4554836ca | ||
|
|
3c647c65bf | ||
|
|
21f683d531 | ||
|
|
dcbf766217 | ||
|
|
8443d344a2 | ||
|
|
18b8244406 | ||
|
|
429decd7b6 | ||
|
|
ad2850dd5d | ||
|
|
babec9bf79 | ||
|
|
99e19e6d59 | ||
|
|
035f7b57e9 | ||
|
|
dee5535fbb | ||
|
|
1d3bcdf522 | ||
|
|
87ef540b52 | ||
|
|
89f4a35273 | ||
|
|
6be78d442c | ||
|
|
2df846ee4b | ||
|
|
54e65ce765 | ||
|
|
61bbddeb0c | ||
|
|
8aba0b04bc | ||
|
|
26ffe6c03d | ||
|
|
6287e6d8e9 | ||
|
|
ec7f4d18e1 | ||
|
|
c08f94ec04 | ||
|
|
34b054ff53 | ||
|
|
2437546009 | ||
|
|
8127f63b1e | ||
|
|
26d4269071 | ||
|
|
c48586ff80 | ||
|
|
5b55a83aaa | ||
|
|
a3bacc76f1 | ||
|
|
cf40d0ae4d | ||
|
|
489ac04f86 | ||
|
|
19bb8e8c17 | ||
|
|
e07516585a | ||
|
|
60c7003667 | ||
|
|
fda394858b | ||
|
|
8595ae71f7 | ||
|
|
3365634259 | ||
|
|
aab633eced | ||
|
|
2064915d3b | ||
|
|
0c04c9a2c2 | ||
|
|
722889e881 | ||
|
|
d9285e78c0 | ||
|
|
07303ccbb3 | ||
|
|
b9f6b60c4d | ||
|
|
32f52ac30d | ||
|
|
18e3763f90 | ||
|
|
9b6ae9029f | ||
|
|
8d0386b049 | ||
|
|
f02b6d60a5 | ||
|
|
b03e75e3d1 | ||
|
|
a34d6d390e | ||
|
|
edb273ace5 | ||
|
|
6591a86aad | ||
|
|
e53b86fbbc | ||
|
|
bbcbcefedc | ||
|
|
233ae5a54b | ||
|
|
8b3fa789da | ||
|
|
f91e826781 | ||
|
|
c6c925d67a | ||
|
|
4f3f93f267 | ||
|
|
3fd0ec74f0 | ||
|
|
04940a1105 | ||
|
|
5a9fca48e8 | ||
|
|
ec9063b4a5 | ||
|
|
c3789811c8 | ||
|
|
8b6b4dde69 | ||
|
|
df8a6b984a | ||
|
|
e8d0827916 | ||
|
|
b866f1b21e | ||
|
|
90633b9ce1 | ||
|
|
90fe5c5aca | ||
|
|
2e2673aff6 | ||
|
|
440793b5ff | ||
|
|
222cd41aa3 | ||
|
|
092fbd60d9 | ||
|
|
b10ade17be | ||
|
|
017a778a20 | ||
|
|
eac5254a88 | ||
|
|
dcb1da338b | ||
|
|
4963caf506 | ||
|
|
066504e79e | ||
|
|
ec48d0ac29 | ||
|
|
7af1984348 | ||
|
|
1a072f3bb9 | ||
|
|
9ef58e378c | ||
|
|
4d014717b6 | ||
|
|
fc7d340a89 | ||
|
|
f02c2855ad | ||
|
|
632dc61d5e | ||
|
|
e9c4574552 | ||
|
|
789c5857fa | ||
|
|
ce3fb6be21 | ||
|
|
b0c498629a | ||
|
|
6158dd6bce | ||
|
|
bd894ae8b3 | ||
|
|
3be916e82b | ||
|
|
46c5cb1136 | ||
|
|
6f89b3f3d9 | ||
|
|
bf2d7b3a16 | ||
|
|
13997caa32 | ||
|
|
3e889c398e | ||
|
|
858c0e67a1 | ||
|
|
957b3e1e85 | ||
|
|
3ce0a9c8c0 | ||
|
|
7de9214c99 | ||
|
|
402ed04189 | ||
|
|
059d6b0e0f | ||
|
|
563dc62c33 | ||
|
|
3f1f83f667 | ||
|
|
6d5f9035e6 | ||
|
|
7d47bffd53 | ||
|
|
d4acccb13c | ||
|
|
0a7e4b6840 | ||
|
|
4e0f3a30ee | ||
|
|
ba46eaa143 | ||
|
|
3c493511e9 | ||
|
|
12e8107492 | ||
|
|
5c7dedffb3 | ||
|
|
71a08c3237 | ||
|
|
d408ae7e10 | ||
|
|
386eb2d56b | ||
|
|
a2615339f7 | ||
|
|
cae6f91729 | ||
|
|
dbe0170249 | ||
|
|
188dbde2d6 | ||
|
|
96d11b7966 |
@@ -4,5 +4,7 @@
|
||||
"cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/qlpack.yml",
|
||||
"*/ql/examples/qlpack.yml",
|
||||
"*/upgrades/qlpack.yml",
|
||||
"javascript/ql/experimental/adaptivethreatmodeling/lib/qlpack.yml",
|
||||
"javascript/ql/experimental/adaptivethreatmodeling/src/qlpack.yml",
|
||||
"misc/legacy-support/*/qlpack.yml",
|
||||
"misc/suite-helpers/qlpack.yml" ] }
|
||||
|
||||
11
.github/workflows/codeql-analysis.yml
vendored
11
.github/workflows/codeql-analysis.yml
vendored
@@ -11,6 +11,8 @@ on:
|
||||
- 'rc/*'
|
||||
paths:
|
||||
- 'csharp/**'
|
||||
- '.github/codeql/**'
|
||||
- '.github/workflows/codeql-analysis.yml'
|
||||
schedule:
|
||||
- cron: '0 9 * * 1'
|
||||
|
||||
@@ -38,8 +40,8 @@ jobs:
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@main
|
||||
#- name: Autobuild
|
||||
# uses: github/codeql-action/autobuild@main
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
@@ -48,9 +50,8 @@ jobs:
|
||||
# and modify them (or add more) to build your code if your project
|
||||
# uses a compiled language
|
||||
|
||||
#- run: |
|
||||
# make bootstrap
|
||||
# make release
|
||||
- run: |
|
||||
dotnet build csharp
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@main
|
||||
|
||||
@@ -6,6 +6,8 @@ on:
|
||||
- '.github/workflows/csv-coverage-pr-comment.yml'
|
||||
- '*/ql/src/**/*.ql'
|
||||
- '*/ql/src/**/*.qll'
|
||||
- '*/ql/lib/**/*.ql'
|
||||
- '*/ql/lib/**/*.qll'
|
||||
- 'misc/scripts/library-coverage/*.py'
|
||||
# input data files
|
||||
- '*/documentation/library-coverage/cwe-sink.csv'
|
||||
|
||||
2
.github/workflows/csv-coverage-update.yml
vendored
2
.github/workflows/csv-coverage-update.yml
vendored
@@ -8,7 +8,7 @@ on:
|
||||
jobs:
|
||||
update:
|
||||
name: Update framework coverage report
|
||||
if: github.event.repository.fork == false
|
||||
if: github.repository == 'github/codeql'
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -24,3 +24,6 @@
|
||||
/codeql/
|
||||
|
||||
csharp/extractor/Semmle.Extraction.CSharp.Driver/Properties/launchSettings.json
|
||||
|
||||
# Avoid committing cached package components
|
||||
.codeql
|
||||
|
||||
@@ -4,8 +4,8 @@ This open source repository contains the standard CodeQL libraries and queries t
|
||||
|
||||
## How do I learn CodeQL and run queries?
|
||||
|
||||
There is [extensive documentation](https://help.semmle.com/QL/learn-ql/) on getting started with writing CodeQL.
|
||||
You can use the [interactive query console](https://lgtm.com/help/lgtm/using-query-console) on LGTM.com or the [CodeQL for Visual Studio Code](https://help.semmle.com/codeql/codeql-for-vscode.html) extension to try out your queries on any open source project that's currently being analyzed.
|
||||
There is [extensive documentation](https://codeql.github.com/docs/) on getting started with writing CodeQL.
|
||||
You can use the [interactive query console](https://lgtm.com/help/lgtm/using-query-console) on LGTM.com or the [CodeQL for Visual Studio Code](https://codeql.github.com/docs/codeql-for-visual-studio-code/) extension to try out your queries on any open source project that's currently being analyzed.
|
||||
|
||||
## Contributing
|
||||
|
||||
|
||||
@@ -367,6 +367,7 @@
|
||||
],
|
||||
"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"
|
||||
],
|
||||
@@ -461,5 +462,12 @@
|
||||
"ReDoS Polynomial Python/JS": [
|
||||
"javascript/ql/lib/semmle/javascript/security/performance/SuperlinearBackTracking.qll",
|
||||
"python/ql/lib/semmle/python/security/performance/SuperlinearBackTracking.qll"
|
||||
],
|
||||
"CodeQL Tutorial": [
|
||||
"cpp/ql/lib/tutorial.qll",
|
||||
"csharp/ql/lib/tutorial.qll",
|
||||
"java/ql/lib/tutorial.qll",
|
||||
"javascript/ql/lib/tutorial.qll",
|
||||
"python/ql/lib/tutorial.qll"
|
||||
]
|
||||
}
|
||||
2
cpp/change-notes/2021-06-10-cleartext-transmission.md
Normal file
2
cpp/change-notes/2021-06-10-cleartext-transmission.md
Normal file
@@ -0,0 +1,2 @@
|
||||
lgtm,codescanning
|
||||
* A new query (`cpp/cleartext-transmission`) has been added. This is similar to the `cpp/cleartext-storage-file`, `cpp/cleartext-storage-buffer` and `cpp/cleartext-storage-database` queries but looks for cases where sensitive information is most likely transmitted over a network.
|
||||
2
cpp/change-notes/2021-06-22-sql-tainted.md
Normal file
2
cpp/change-notes/2021-06-22-sql-tainted.md
Normal file
@@ -0,0 +1,2 @@
|
||||
lgtm,codescanning
|
||||
* The 'Uncontrolled data in SQL query' (cpp/sql-injection) query now supports the `libpqxx` library.
|
||||
@@ -0,0 +1,4 @@
|
||||
lgtm,codescanning
|
||||
* The `SimpleRangeAnalysis` library includes information from the
|
||||
immediate guard for determining the upper bound of a stack
|
||||
variable for improved accuracy.
|
||||
4
cpp/change-notes/2021-09-13-overflow-static.md
Normal file
4
cpp/change-notes/2021-09-13-overflow-static.md
Normal file
@@ -0,0 +1,4 @@
|
||||
lgtm,codescanning
|
||||
* The `memberMayBeVarSize` predicate considers more fields to be variable size.
|
||||
As a result, the "Static buffer overflow" query (cpp/static-buffer-overflow)
|
||||
produces fewer false positives.
|
||||
2
cpp/change-notes/2021-09-27-command-line-injection.md
Normal file
2
cpp/change-notes/2021-09-27-command-line-injection.md
Normal file
@@ -0,0 +1,2 @@
|
||||
lgtm,codescanning
|
||||
* The "Uncontrolled data used in OS command" (`cpp/command-line-injection`) query has been enhanced to reduce false positive results and its `@precision` increased to `high`
|
||||
3
cpp/change-notes/2021-09-27-overflow-static.md
Normal file
3
cpp/change-notes/2021-09-27-overflow-static.md
Normal file
@@ -0,0 +1,3 @@
|
||||
lgtm,codescanning
|
||||
* Increase precision to high for the "Static buffer overflow" query
|
||||
(`cpp/static-buffer-overflow`). This means the query is run and displayed by default on Code Scanning and LGTM.
|
||||
2
cpp/change-notes/2021-10-01-improper-null-termination.md
Normal file
2
cpp/change-notes/2021-10-01-improper-null-termination.md
Normal file
@@ -0,0 +1,2 @@
|
||||
lgtm,codescanning
|
||||
* Several improvements made to the `NullTermination.qll` library and the 'Potential improper null termination' (cpp/improper-null-termination). These changes reduce the number of false positive results for this query and related query 'User-controlled data may not be null terminated' (cpp/user-controlled-null-termination-tainted).
|
||||
3
cpp/change-notes/2021-10-07-extraction-errors.md
Normal file
3
cpp/change-notes/2021-10-07-extraction-errors.md
Normal file
@@ -0,0 +1,3 @@
|
||||
codescanning
|
||||
* Problems with extraction that in most cases won't break the analysis in a significant way are now reported as warnings rather than errors.
|
||||
* The failed extractor invocations query now has severity `error`.
|
||||
@@ -52,11 +52,8 @@ module PrivateCleartextWrite {
|
||||
|
||||
class WriteSink extends Sink {
|
||||
WriteSink() {
|
||||
exists(FileWrite f, BufferWrite b |
|
||||
this.asExpr() = f.getASource()
|
||||
or
|
||||
this.asExpr() = b.getAChild()
|
||||
)
|
||||
this.asExpr() = any(FileWrite f).getASource() or
|
||||
this.asExpr() = any(BufferWrite b).getAChild()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,26 +13,25 @@ import cpp
|
||||
|
||||
/** A string for `match` that identifies strings that look like they represent private data. */
|
||||
private string privateNames() {
|
||||
// Inspired by the list on https://cwe.mitre.org/data/definitions/359.html
|
||||
// Government identifiers, such as Social Security Numbers
|
||||
result = "%social%security%number%" or
|
||||
// Contact information, such as home addresses and telephone numbers
|
||||
result = "%postcode%" or
|
||||
result = "%zipcode%" or
|
||||
// result = "%telephone%" or
|
||||
// Geographic location - where the user is (or was)
|
||||
result = "%latitude%" or
|
||||
result = "%longitude%" or
|
||||
// Financial data - such as credit card numbers, salary, bank accounts, and debts
|
||||
result = "%creditcard%" or
|
||||
result = "%salary%" or
|
||||
result = "%bankaccount%" or
|
||||
// Communications - e-mail addresses, private e-mail messages, SMS text messages, chat logs, etc.
|
||||
// result = "%email%" or
|
||||
// result = "%mobile%" or
|
||||
result = "%employer%" or
|
||||
// Health - medical conditions, insurance status, prescription records
|
||||
result = "%medical%"
|
||||
result =
|
||||
[
|
||||
// Inspired by the list on https://cwe.mitre.org/data/definitions/359.html
|
||||
// Government identifiers, such as Social Security Numbers
|
||||
"%social%security%number%",
|
||||
// Contact information, such as home addresses and telephone numbers
|
||||
"%postcode%", "%zipcode%",
|
||||
// result = "%telephone%" or
|
||||
// Geographic location - where the user is (or was)
|
||||
"%latitude%", "%longitude%",
|
||||
// Financial data - such as credit card numbers, salary, bank accounts, and debts
|
||||
"%creditcard%", "%salary%", "%bankaccount%",
|
||||
// Communications - e-mail addresses, private e-mail messages, SMS text messages, chat logs, etc.
|
||||
// result = "%email%" or
|
||||
// result = "%mobile%" or
|
||||
"%employer%",
|
||||
// Health - medical conditions, insurance status, prescription records
|
||||
"%medical%"
|
||||
]
|
||||
}
|
||||
|
||||
/** An expression that might contain private data. */
|
||||
|
||||
18
cpp/ql/lib/external/ExternalArtifact.qll
vendored
18
cpp/ql/lib/external/ExternalArtifact.qll
vendored
@@ -15,7 +15,7 @@ class ExternalData extends @externalDataElement {
|
||||
* Gets the path of the file this data was loaded from, with its
|
||||
* extension replaced by `.ql`.
|
||||
*/
|
||||
string getQueryPath() { result = getDataPath().regexpReplaceAll("\\.[^.]*$", ".ql") }
|
||||
string getQueryPath() { result = this.getDataPath().regexpReplaceAll("\\.[^.]*$", ".ql") }
|
||||
|
||||
/** Gets the number of fields in this data item. */
|
||||
int getNumFields() { result = 1 + max(int i | externalData(this, _, i, _) | i) }
|
||||
@@ -24,22 +24,22 @@ class ExternalData extends @externalDataElement {
|
||||
string getField(int i) { externalData(this, _, i, result) }
|
||||
|
||||
/** Gets the integer value of the `i`th field of this data item. */
|
||||
int getFieldAsInt(int i) { result = getField(i).toInt() }
|
||||
int getFieldAsInt(int i) { result = this.getField(i).toInt() }
|
||||
|
||||
/** Gets the floating-point value of the `i`th field of this data item. */
|
||||
float getFieldAsFloat(int i) { result = getField(i).toFloat() }
|
||||
float getFieldAsFloat(int i) { result = this.getField(i).toFloat() }
|
||||
|
||||
/** Gets the value of the `i`th field of this data item, interpreted as a date. */
|
||||
date getFieldAsDate(int i) { result = getField(i).toDate() }
|
||||
date getFieldAsDate(int i) { result = this.getField(i).toDate() }
|
||||
|
||||
/** Gets a textual representation of this data item. */
|
||||
string toString() { result = getQueryPath() + ": " + buildTupleString(0) }
|
||||
string toString() { result = this.getQueryPath() + ": " + this.buildTupleString(0) }
|
||||
|
||||
/** Gets a textual representation of this data item, starting with the `n`th field. */
|
||||
private string buildTupleString(int n) {
|
||||
n = getNumFields() - 1 and result = getField(n)
|
||||
n = this.getNumFields() - 1 and result = this.getField(n)
|
||||
or
|
||||
n < getNumFields() - 1 and result = getField(n) + "," + buildTupleString(n + 1)
|
||||
n < this.getNumFields() - 1 and result = this.getField(n) + "," + this.buildTupleString(n + 1)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,8 +53,8 @@ class DefectExternalData extends ExternalData {
|
||||
}
|
||||
|
||||
/** Gets the URL associated with this data item. */
|
||||
string getURL() { result = getField(0) }
|
||||
string getURL() { result = this.getField(0) }
|
||||
|
||||
/** Gets the message associated with this data item. */
|
||||
string getMessage() { result = getField(1) }
|
||||
string getMessage() { result = this.getField(1) }
|
||||
}
|
||||
|
||||
@@ -269,13 +269,13 @@ class Class extends UserType {
|
||||
* DEPRECATED: name changed to `hasImplicitCopyConstructor` to reflect that
|
||||
* `= default` members are no longer included.
|
||||
*/
|
||||
deprecated predicate hasGeneratedCopyConstructor() { hasImplicitCopyConstructor() }
|
||||
deprecated predicate hasGeneratedCopyConstructor() { this.hasImplicitCopyConstructor() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: name changed to `hasImplicitCopyAssignmentOperator` to
|
||||
* reflect that `= default` members are no longer included.
|
||||
*/
|
||||
deprecated predicate hasGeneratedCopyAssignmentOperator() { hasImplicitCopyConstructor() }
|
||||
deprecated predicate hasGeneratedCopyAssignmentOperator() { this.hasImplicitCopyConstructor() }
|
||||
|
||||
/**
|
||||
* Holds if this class, struct or union has an implicitly-declared copy
|
||||
@@ -487,7 +487,7 @@ class Class extends UserType {
|
||||
exists(ClassDerivation cd |
|
||||
// Add the offset of the direct base class and the offset of `baseClass`
|
||||
// within that direct base class.
|
||||
cd = getADerivation() and
|
||||
cd = this.getADerivation() and
|
||||
result = cd.getBaseClass().getANonVirtualBaseClassByteOffset(baseClass) + cd.getByteOffset()
|
||||
)
|
||||
}
|
||||
@@ -502,12 +502,12 @@ class Class extends UserType {
|
||||
*/
|
||||
int getABaseClassByteOffset(Class baseClass) {
|
||||
// Handle the non-virtual case.
|
||||
result = getANonVirtualBaseClassByteOffset(baseClass)
|
||||
result = this.getANonVirtualBaseClassByteOffset(baseClass)
|
||||
or
|
||||
exists(Class virtualBaseClass, int virtualBaseOffset, int offsetFromVirtualBase |
|
||||
// Look for the base class as a non-virtual base of a direct or indirect
|
||||
// virtual base, adding the two offsets.
|
||||
getVirtualBaseClassByteOffset(virtualBaseClass) = virtualBaseOffset and
|
||||
this.getVirtualBaseClassByteOffset(virtualBaseClass) = virtualBaseOffset and
|
||||
offsetFromVirtualBase = virtualBaseClass.getANonVirtualBaseClassByteOffset(baseClass) and
|
||||
result = virtualBaseOffset + offsetFromVirtualBase
|
||||
)
|
||||
@@ -623,11 +623,11 @@ class Class extends UserType {
|
||||
* inherits one).
|
||||
*/
|
||||
predicate isPolymorphic() {
|
||||
exists(MemberFunction f | f.getDeclaringType() = getABaseClass*() and f.isVirtual())
|
||||
exists(MemberFunction f | f.getDeclaringType() = this.getABaseClass*() and f.isVirtual())
|
||||
}
|
||||
|
||||
override predicate involvesTemplateParameter() {
|
||||
getATemplateArgument().(Type).involvesTemplateParameter()
|
||||
this.getATemplateArgument().(Type).involvesTemplateParameter()
|
||||
}
|
||||
|
||||
/** Holds if this class, struct or union was declared 'final'. */
|
||||
@@ -765,7 +765,7 @@ class ClassDerivation extends Locatable, @derivation {
|
||||
* };
|
||||
* ```
|
||||
*/
|
||||
Class getBaseClass() { result = getBaseType().getUnderlyingType() }
|
||||
Class getBaseClass() { result = this.getBaseType().getUnderlyingType() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ClassDerivation" }
|
||||
|
||||
@@ -818,7 +818,7 @@ class ClassDerivation extends Locatable, @derivation {
|
||||
predicate hasSpecifier(string s) { this.getASpecifier().hasName(s) }
|
||||
|
||||
/** Holds if the derivation is for a virtual base class. */
|
||||
predicate isVirtual() { hasSpecifier("virtual") }
|
||||
predicate isVirtual() { this.hasSpecifier("virtual") }
|
||||
|
||||
/** Gets the location of the derivation. */
|
||||
override Location getLocation() { derivations(underlyingElement(this), _, _, _, result) }
|
||||
@@ -846,7 +846,7 @@ class ClassDerivation extends Locatable, @derivation {
|
||||
* ```
|
||||
*/
|
||||
class LocalClass extends Class {
|
||||
LocalClass() { isLocal() }
|
||||
LocalClass() { this.isLocal() }
|
||||
|
||||
override string getAPrimaryQlClass() { not this instanceof LocalStruct and result = "LocalClass" }
|
||||
|
||||
@@ -989,9 +989,9 @@ class ClassTemplateSpecialization extends Class {
|
||||
TemplateClass getPrimaryTemplate() {
|
||||
// Ignoring template arguments, the primary template has the same name
|
||||
// as each of its specializations.
|
||||
result.getSimpleName() = getSimpleName() and
|
||||
result.getSimpleName() = this.getSimpleName() and
|
||||
// It is in the same namespace as its specializations.
|
||||
result.getNamespace() = getNamespace() and
|
||||
result.getNamespace() = this.getNamespace() and
|
||||
// It is distinguished by the fact that each of its template arguments
|
||||
// is a distinct template parameter.
|
||||
count(TemplateParameter tp | tp = result.getATemplateArgument()) =
|
||||
@@ -1108,7 +1108,7 @@ deprecated class Interface extends Class {
|
||||
* ```
|
||||
*/
|
||||
class VirtualClassDerivation extends ClassDerivation {
|
||||
VirtualClassDerivation() { hasSpecifier("virtual") }
|
||||
VirtualClassDerivation() { this.hasSpecifier("virtual") }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "VirtualClassDerivation" }
|
||||
}
|
||||
@@ -1136,7 +1136,7 @@ class VirtualBaseClass extends Class {
|
||||
VirtualClassDerivation getAVirtualDerivation() { result.getBaseClass() = this }
|
||||
|
||||
/** A class/struct that is derived from this one using virtual inheritance. */
|
||||
Class getAVirtuallyDerivedClass() { result = getAVirtualDerivation().getDerivedClass() }
|
||||
Class getAVirtuallyDerivedClass() { result = this.getAVirtualDerivation().getDerivedClass() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1155,7 +1155,7 @@ class ProxyClass extends UserType {
|
||||
override string getAPrimaryQlClass() { result = "ProxyClass" }
|
||||
|
||||
/** Gets the location of the proxy class. */
|
||||
override Location getLocation() { result = getTemplateParameter().getDefinitionLocation() }
|
||||
override Location getLocation() { result = this.getTemplateParameter().getDefinitionLocation() }
|
||||
|
||||
/** Gets the template parameter for which this is the proxy class. */
|
||||
TemplateParameter getTemplateParameter() {
|
||||
|
||||
@@ -184,7 +184,7 @@ class Declaration extends Locatable, @declaration {
|
||||
predicate hasDefinition() { exists(this.getDefinition()) }
|
||||
|
||||
/** DEPRECATED: Use `hasDefinition` instead. */
|
||||
predicate isDefined() { hasDefinition() }
|
||||
predicate isDefined() { this.hasDefinition() }
|
||||
|
||||
/** Gets the preferred location of this declaration, if any. */
|
||||
override Location getLocation() { none() }
|
||||
@@ -209,7 +209,7 @@ class Declaration extends Locatable, @declaration {
|
||||
predicate isStatic() { this.hasSpecifier("static") }
|
||||
|
||||
/** Holds if this declaration is a member of a class/struct/union. */
|
||||
predicate isMember() { hasDeclaringType() }
|
||||
predicate isMember() { this.hasDeclaringType() }
|
||||
|
||||
/** Holds if this declaration is a member of a class/struct/union. */
|
||||
predicate hasDeclaringType() { exists(this.getDeclaringType()) }
|
||||
@@ -226,14 +226,14 @@ class Declaration extends Locatable, @declaration {
|
||||
* When called on a template, this will return a template parameter type for
|
||||
* both typed and non-typed parameters.
|
||||
*/
|
||||
final Locatable getATemplateArgument() { result = getTemplateArgument(_) }
|
||||
final Locatable getATemplateArgument() { result = this.getTemplateArgument(_) }
|
||||
|
||||
/**
|
||||
* Gets a template argument used to instantiate this declaration from a template.
|
||||
* When called on a template, this will return a non-typed template
|
||||
* parameter value.
|
||||
*/
|
||||
final Locatable getATemplateArgumentKind() { result = getTemplateArgumentKind(_) }
|
||||
final Locatable getATemplateArgumentKind() { result = this.getTemplateArgumentKind(_) }
|
||||
|
||||
/**
|
||||
* Gets the `i`th template argument used to instantiate this declaration from a
|
||||
@@ -252,9 +252,9 @@ class Declaration extends Locatable, @declaration {
|
||||
* `getTemplateArgument(1)` return `1`.
|
||||
*/
|
||||
final Locatable getTemplateArgument(int index) {
|
||||
if exists(getTemplateArgumentValue(index))
|
||||
then result = getTemplateArgumentValue(index)
|
||||
else result = getTemplateArgumentType(index)
|
||||
if exists(this.getTemplateArgumentValue(index))
|
||||
then result = this.getTemplateArgumentValue(index)
|
||||
else result = this.getTemplateArgumentType(index)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -275,14 +275,13 @@ class Declaration extends Locatable, @declaration {
|
||||
* `getTemplateArgumentKind(0)`.
|
||||
*/
|
||||
final Locatable getTemplateArgumentKind(int index) {
|
||||
if exists(getTemplateArgumentValue(index))
|
||||
then result = getTemplateArgumentType(index)
|
||||
else none()
|
||||
exists(this.getTemplateArgumentValue(index)) and
|
||||
result = this.getTemplateArgumentType(index)
|
||||
}
|
||||
|
||||
/** Gets the number of template arguments for this declaration. */
|
||||
final int getNumberOfTemplateArguments() {
|
||||
result = count(int i | exists(getTemplateArgument(i)))
|
||||
result = count(int i | exists(this.getTemplateArgument(i)))
|
||||
}
|
||||
|
||||
private Type getTemplateArgumentType(int index) {
|
||||
@@ -328,9 +327,9 @@ class DeclarationEntry extends Locatable, TDeclarationEntry {
|
||||
* available), or the name declared by this entry otherwise.
|
||||
*/
|
||||
string getCanonicalName() {
|
||||
if getDeclaration().hasDefinition()
|
||||
then result = getDeclaration().getDefinition().getName()
|
||||
else result = getName()
|
||||
if this.getDeclaration().hasDefinition()
|
||||
then result = this.getDeclaration().getDefinition().getName()
|
||||
else result = this.getName()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -371,18 +370,18 @@ class DeclarationEntry extends Locatable, TDeclarationEntry {
|
||||
/**
|
||||
* Holds if this declaration entry has a specifier with the given name.
|
||||
*/
|
||||
predicate hasSpecifier(string specifier) { getASpecifier() = specifier }
|
||||
predicate hasSpecifier(string specifier) { this.getASpecifier() = specifier }
|
||||
|
||||
/** Holds if this declaration entry is a definition. */
|
||||
predicate isDefinition() { none() } // overridden in subclasses
|
||||
|
||||
override string toString() {
|
||||
if isDefinition()
|
||||
then result = "definition of " + getName()
|
||||
if this.isDefinition()
|
||||
then result = "definition of " + this.getName()
|
||||
else
|
||||
if getName() = getCanonicalName()
|
||||
then result = "declaration of " + getName()
|
||||
else result = "declaration of " + getCanonicalName() + " as " + getName()
|
||||
if this.getName() = this.getCanonicalName()
|
||||
then result = "declaration of " + this.getName()
|
||||
else result = "declaration of " + this.getCanonicalName() + " as " + this.getName()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -581,7 +580,7 @@ private class DirectAccessHolder extends Element {
|
||||
// transitive closure with a restricted base case.
|
||||
this.thisCanAccessClassStep(base, derived)
|
||||
or
|
||||
exists(Class between | thisCanAccessClassTrans(base, between) |
|
||||
exists(Class between | this.thisCanAccessClassTrans(base, between) |
|
||||
isDirectPublicBaseOf(between, derived) or
|
||||
this.thisCanAccessClassStep(between, derived)
|
||||
)
|
||||
|
||||
@@ -61,7 +61,7 @@ class ElementBase extends @element {
|
||||
/**
|
||||
* Gets a comma-separated list of the names of the primary CodeQL classes to which this element belongs.
|
||||
*/
|
||||
final string getPrimaryQlClasses() { result = concat(getAPrimaryQlClass(), ",") }
|
||||
final string getPrimaryQlClasses() { result = concat(this.getAPrimaryQlClass(), ",") }
|
||||
|
||||
/**
|
||||
* Gets the name of a primary CodeQL class to which this element belongs.
|
||||
@@ -206,9 +206,9 @@ class Element extends ElementBase {
|
||||
/** Gets the closest `Element` enclosing this one. */
|
||||
cached
|
||||
Element getEnclosingElement() {
|
||||
result = getEnclosingElementPref()
|
||||
result = this.getEnclosingElementPref()
|
||||
or
|
||||
not exists(getEnclosingElementPref()) and
|
||||
not exists(this.getEnclosingElementPref()) and
|
||||
(
|
||||
this = result.(Class).getAMember()
|
||||
or
|
||||
@@ -281,7 +281,7 @@ private predicate isFromUninstantiatedTemplateRec(Element e, Element template) {
|
||||
* ```
|
||||
*/
|
||||
class StaticAssert extends Locatable, @static_assert {
|
||||
override string toString() { result = "static_assert(..., \"" + getMessage() + "\")" }
|
||||
override string toString() { result = "static_assert(..., \"" + this.getMessage() + "\")" }
|
||||
|
||||
/**
|
||||
* Gets the expression which this static assertion ensures is true.
|
||||
|
||||
@@ -85,7 +85,7 @@ class Enum extends UserType, IntegralOrEnumType {
|
||||
* ```
|
||||
*/
|
||||
class LocalEnum extends Enum {
|
||||
LocalEnum() { isLocal() }
|
||||
LocalEnum() { this.isLocal() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "LocalEnum" }
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ class Container extends Locatable, @container {
|
||||
* DEPRECATED: Use `getLocation` instead.
|
||||
* Gets a URL representing the location of this container.
|
||||
*
|
||||
* For more information see [Providing URLs](https://help.semmle.com/QL/learn-ql/ql/locations.html#providing-urls).
|
||||
* For more information see [Providing URLs](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/#providing-urls).
|
||||
*/
|
||||
deprecated string getURL() { none() } // overridden by subclasses
|
||||
|
||||
@@ -52,7 +52,7 @@ class Container extends Locatable, @container {
|
||||
*/
|
||||
string getRelativePath() {
|
||||
exists(string absPath, string pref |
|
||||
absPath = getAbsolutePath() and sourceLocationPrefix(pref)
|
||||
absPath = this.getAbsolutePath() and sourceLocationPrefix(pref)
|
||||
|
|
||||
absPath = pref and result = ""
|
||||
or
|
||||
@@ -79,7 +79,7 @@ class Container extends Locatable, @container {
|
||||
* </table>
|
||||
*/
|
||||
string getBaseName() {
|
||||
result = getAbsolutePath().regexpCapture(".*/(([^/]*?)(?:\\.([^.]*))?)", 1)
|
||||
result = this.getAbsolutePath().regexpCapture(".*/(([^/]*?)(?:\\.([^.]*))?)", 1)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -105,7 +105,9 @@ class Container extends Locatable, @container {
|
||||
* <tr><td>"/tmp/x.tar.gz"</td><td>"gz"</td></tr>
|
||||
* </table>
|
||||
*/
|
||||
string getExtension() { result = getAbsolutePath().regexpCapture(".*/([^/]*?)(\\.([^.]*))?", 3) }
|
||||
string getExtension() {
|
||||
result = this.getAbsolutePath().regexpCapture(".*/([^/]*?)(\\.([^.]*))?", 3)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the stem of this container, that is, the prefix of its base name up to
|
||||
@@ -124,7 +126,9 @@ class Container extends Locatable, @container {
|
||||
* <tr><td>"/tmp/x.tar.gz"</td><td>"x.tar"</td></tr>
|
||||
* </table>
|
||||
*/
|
||||
string getStem() { result = getAbsolutePath().regexpCapture(".*/([^/]*?)(?:\\.([^.]*))?", 1) }
|
||||
string getStem() {
|
||||
result = this.getAbsolutePath().regexpCapture(".*/([^/]*?)(?:\\.([^.]*))?", 1)
|
||||
}
|
||||
|
||||
/** Gets the parent container of this file or folder, if any. */
|
||||
Container getParentContainer() {
|
||||
@@ -135,20 +139,20 @@ class Container extends Locatable, @container {
|
||||
Container getAChildContainer() { this = result.getParentContainer() }
|
||||
|
||||
/** Gets a file in this container. */
|
||||
File getAFile() { result = getAChildContainer() }
|
||||
File getAFile() { result = this.getAChildContainer() }
|
||||
|
||||
/** Gets the file in this container that has the given `baseName`, if any. */
|
||||
File getFile(string baseName) {
|
||||
result = getAFile() and
|
||||
result = this.getAFile() and
|
||||
result.getBaseName() = baseName
|
||||
}
|
||||
|
||||
/** Gets a sub-folder in this container. */
|
||||
Folder getAFolder() { result = getAChildContainer() }
|
||||
Folder getAFolder() { result = this.getAChildContainer() }
|
||||
|
||||
/** Gets the sub-folder in this container that has the given `baseName`, if any. */
|
||||
Folder getFolder(string baseName) {
|
||||
result = getAFolder() and
|
||||
result = this.getAFolder() and
|
||||
result.getBaseName() = baseName
|
||||
}
|
||||
|
||||
@@ -157,7 +161,7 @@ class Container extends Locatable, @container {
|
||||
*
|
||||
* This is the absolute path of the container.
|
||||
*/
|
||||
override string toString() { result = getAbsolutePath() }
|
||||
override string toString() { result = this.getAbsolutePath() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -171,7 +175,7 @@ class Container extends Locatable, @container {
|
||||
* To get the full path, use `getAbsolutePath`.
|
||||
*/
|
||||
class Folder extends Container, @folder {
|
||||
override string getAbsolutePath() { folders(underlyingElement(this), result, _) }
|
||||
override string getAbsolutePath() { folders(underlyingElement(this), result) }
|
||||
|
||||
override Location getLocation() {
|
||||
result.getContainer() = this and
|
||||
@@ -190,7 +194,7 @@ class Folder extends Container, @folder {
|
||||
* DEPRECATED: use `getAbsolutePath` instead.
|
||||
* Gets the name of this folder.
|
||||
*/
|
||||
deprecated string getName() { folders(underlyingElement(this), result, _) }
|
||||
deprecated string getName() { folders(underlyingElement(this), result) }
|
||||
|
||||
/**
|
||||
* DEPRECATED: use `getAbsolutePath` instead.
|
||||
@@ -208,17 +212,7 @@ class Folder extends Container, @folder {
|
||||
* DEPRECATED: use `getBaseName` instead.
|
||||
* Gets the last part of the folder name.
|
||||
*/
|
||||
deprecated string getShortName() {
|
||||
exists(string longnameRaw, string longname |
|
||||
folders(underlyingElement(this), _, longnameRaw) and
|
||||
longname = longnameRaw.replaceAll("\\", "/")
|
||||
|
|
||||
exists(int index |
|
||||
result = longname.splitAt("/", index) and
|
||||
not exists(longname.splitAt("/", index + 1))
|
||||
)
|
||||
)
|
||||
}
|
||||
deprecated string getShortName() { result = this.getBaseName() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: use `getParentContainer` instead.
|
||||
@@ -242,7 +236,7 @@ class Folder extends Container, @folder {
|
||||
* `getStem` and `getExtension`. To get the full path, use `getAbsolutePath`.
|
||||
*/
|
||||
class File extends Container, @file {
|
||||
override string getAbsolutePath() { files(underlyingElement(this), result, _, _, _) }
|
||||
override string getAbsolutePath() { files(underlyingElement(this), result) }
|
||||
|
||||
override string toString() { result = Container.super.toString() }
|
||||
|
||||
@@ -336,7 +330,13 @@ class File extends Container, @file {
|
||||
* for example, for "file.tar.gz", this predicate will have the result
|
||||
* "tar.gz", while `getExtension` will have the result "gz".
|
||||
*/
|
||||
string getExtensions() { files(underlyingElement(this), _, _, result, _) }
|
||||
string getExtensions() {
|
||||
exists(string name, int firstDotPos |
|
||||
name = this.getBaseName() and
|
||||
firstDotPos = min([name.indexOf("."), name.length() - 1]) and
|
||||
result = name.suffix(firstDotPos + 1)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the short name of this file, that is, the prefix of its base name up
|
||||
@@ -351,7 +351,16 @@ class File extends Container, @file {
|
||||
* for example, for "file.tar.gz", this predicate will have the result
|
||||
* "file", while `getStem` will have the result "file.tar".
|
||||
*/
|
||||
string getShortName() { files(underlyingElement(this), _, result, _, _) }
|
||||
string getShortName() {
|
||||
exists(string name, int firstDotPos |
|
||||
name = this.getBaseName() and
|
||||
firstDotPos = min([name.indexOf("."), name.length()]) and
|
||||
result = name.prefix(firstDotPos)
|
||||
)
|
||||
or
|
||||
this.getAbsolutePath() = "" and
|
||||
result = ""
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -43,26 +43,26 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
|
||||
*/
|
||||
string getFullSignature() {
|
||||
exists(string name, string templateArgs, string args |
|
||||
result = name + templateArgs + args + " -> " + getType().toString() and
|
||||
name = getQualifiedName() and
|
||||
result = name + templateArgs + args + " -> " + this.getType().toString() and
|
||||
name = this.getQualifiedName() and
|
||||
(
|
||||
if exists(getATemplateArgument())
|
||||
if exists(this.getATemplateArgument())
|
||||
then
|
||||
templateArgs =
|
||||
"<" +
|
||||
concat(int i |
|
||||
exists(getTemplateArgument(i))
|
||||
exists(this.getTemplateArgument(i))
|
||||
|
|
||||
getTemplateArgument(i).toString(), ", " order by i
|
||||
this.getTemplateArgument(i).toString(), ", " order by i
|
||||
) + ">"
|
||||
else templateArgs = ""
|
||||
) and
|
||||
args =
|
||||
"(" +
|
||||
concat(int i |
|
||||
exists(getParameter(i))
|
||||
exists(this.getParameter(i))
|
||||
|
|
||||
getParameter(i).getType().toString(), ", " order by i
|
||||
this.getParameter(i).getType().toString(), ", " order by i
|
||||
) + ")"
|
||||
)
|
||||
}
|
||||
@@ -70,7 +70,7 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
|
||||
/** Gets a specifier of this function. */
|
||||
override Specifier getASpecifier() {
|
||||
funspecifiers(underlyingElement(this), unresolveElement(result)) or
|
||||
result.hasName(getADeclarationEntry().getASpecifier())
|
||||
result.hasName(this.getADeclarationEntry().getASpecifier())
|
||||
}
|
||||
|
||||
/** Gets an attribute of this function. */
|
||||
@@ -149,7 +149,7 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
|
||||
* Holds if this function is declared with `__attribute__((naked))` or
|
||||
* `__declspec(naked)`.
|
||||
*/
|
||||
predicate isNaked() { getAnAttribute().hasName("naked") }
|
||||
predicate isNaked() { this.getAnAttribute().hasName("naked") }
|
||||
|
||||
/**
|
||||
* Holds if this function has a trailing return type.
|
||||
@@ -172,7 +172,7 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
|
||||
* Gets the return type of this function after specifiers have been deeply
|
||||
* stripped and typedefs have been resolved.
|
||||
*/
|
||||
Type getUnspecifiedType() { result = getType().getUnspecifiedType() }
|
||||
Type getUnspecifiedType() { result = this.getType().getUnspecifiedType() }
|
||||
|
||||
/**
|
||||
* Gets the nth parameter of this function. There is no result for the
|
||||
@@ -206,7 +206,7 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
|
||||
int getEffectiveNumberOfParameters() {
|
||||
// This method is overridden in `MemberFunction`, where the result is
|
||||
// adjusted to account for the implicit `this` parameter.
|
||||
result = getNumberOfParameters()
|
||||
result = this.getNumberOfParameters()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -216,7 +216,7 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
|
||||
* return `int p1, int p2`.
|
||||
*/
|
||||
string getParameterString() {
|
||||
result = concat(int i | | min(getParameter(i).getTypedName()), ", " order by i)
|
||||
result = concat(int i | | min(this.getParameter(i).getTypedName()), ", " order by i)
|
||||
}
|
||||
|
||||
/** Gets a call to this function. */
|
||||
@@ -229,7 +229,7 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
|
||||
*/
|
||||
override FunctionDeclarationEntry getADeclarationEntry() {
|
||||
if fun_decls(_, underlyingElement(this), _, _, _)
|
||||
then declEntry(result)
|
||||
then this.declEntry(result)
|
||||
else
|
||||
exists(Function f |
|
||||
this.isConstructedFrom(f) and
|
||||
@@ -250,7 +250,7 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
|
||||
* Gets the location of a `FunctionDeclarationEntry` corresponding to this
|
||||
* declaration.
|
||||
*/
|
||||
override Location getADeclarationLocation() { result = getADeclarationEntry().getLocation() }
|
||||
override Location getADeclarationLocation() { result = this.getADeclarationEntry().getLocation() }
|
||||
|
||||
/** Holds if this Function is a Template specialization. */
|
||||
predicate isSpecialization() {
|
||||
@@ -265,14 +265,14 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
|
||||
* definition, if any.
|
||||
*/
|
||||
override FunctionDeclarationEntry getDefinition() {
|
||||
result = getADeclarationEntry() and
|
||||
result = this.getADeclarationEntry() and
|
||||
result.isDefinition()
|
||||
}
|
||||
|
||||
/** Gets the location of the definition, if any. */
|
||||
override Location getDefinitionLocation() {
|
||||
if exists(getDefinition())
|
||||
then result = getDefinition().getLocation()
|
||||
if exists(this.getDefinition())
|
||||
then result = this.getDefinition().getLocation()
|
||||
else exists(Function f | this.isConstructedFrom(f) and result = f.getDefinition().getLocation())
|
||||
}
|
||||
|
||||
@@ -281,7 +281,7 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
|
||||
* definition, if possible.)
|
||||
*/
|
||||
override Location getLocation() {
|
||||
if exists(getDefinition())
|
||||
if exists(this.getDefinition())
|
||||
then result = this.getDefinitionLocation()
|
||||
else result = this.getADeclarationLocation()
|
||||
}
|
||||
@@ -299,7 +299,7 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
|
||||
BlockStmt getBlock() { result.getParentScope() = this }
|
||||
|
||||
/** Holds if this function has an entry point. */
|
||||
predicate hasEntryPoint() { exists(getEntryPoint()) }
|
||||
predicate hasEntryPoint() { exists(this.getEntryPoint()) }
|
||||
|
||||
/**
|
||||
* Gets the first node in this function's control flow graph.
|
||||
@@ -392,7 +392,7 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
|
||||
* Holds if this function has C linkage, as specified by one of its
|
||||
* declaration entries. For example: `extern "C" void foo();`.
|
||||
*/
|
||||
predicate hasCLinkage() { getADeclarationEntry().hasCLinkage() }
|
||||
predicate hasCLinkage() { this.getADeclarationEntry().hasCLinkage() }
|
||||
|
||||
/**
|
||||
* Holds if this function is constructed from `f` as a result
|
||||
@@ -409,27 +409,27 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
|
||||
* several functions that are not linked together have been compiled. An
|
||||
* example would be a project with many 'main' functions.
|
||||
*/
|
||||
predicate isMultiplyDefined() { strictcount(getFile()) > 1 }
|
||||
predicate isMultiplyDefined() { strictcount(this.getFile()) > 1 }
|
||||
|
||||
/** Holds if this function is a varargs function. */
|
||||
predicate isVarargs() { hasSpecifier("varargs") }
|
||||
predicate isVarargs() { this.hasSpecifier("varargs") }
|
||||
|
||||
/** Gets a type that is specified to be thrown by the function. */
|
||||
Type getAThrownType() { result = getADeclarationEntry().getAThrownType() }
|
||||
Type getAThrownType() { result = this.getADeclarationEntry().getAThrownType() }
|
||||
|
||||
/**
|
||||
* Gets the `i`th type specified to be thrown by the function.
|
||||
*/
|
||||
Type getThrownType(int i) { result = getADeclarationEntry().getThrownType(i) }
|
||||
Type getThrownType(int i) { result = this.getADeclarationEntry().getThrownType(i) }
|
||||
|
||||
/** Holds if the function has an exception specification. */
|
||||
predicate hasExceptionSpecification() { getADeclarationEntry().hasExceptionSpecification() }
|
||||
predicate hasExceptionSpecification() { this.getADeclarationEntry().hasExceptionSpecification() }
|
||||
|
||||
/** Holds if this function has a `throw()` exception specification. */
|
||||
predicate isNoThrow() { getADeclarationEntry().isNoThrow() }
|
||||
predicate isNoThrow() { this.getADeclarationEntry().isNoThrow() }
|
||||
|
||||
/** Holds if this function has a `noexcept` exception specification. */
|
||||
predicate isNoExcept() { getADeclarationEntry().isNoExcept() }
|
||||
predicate isNoExcept() { this.getADeclarationEntry().isNoExcept() }
|
||||
|
||||
/**
|
||||
* Gets a function that overloads this one.
|
||||
@@ -539,7 +539,7 @@ private predicate candGetAnOverloadNonMember(string name, Namespace namespace, F
|
||||
*/
|
||||
class FunctionDeclarationEntry extends DeclarationEntry, @fun_decl {
|
||||
/** Gets the function which is being declared or defined. */
|
||||
override Function getDeclaration() { result = getFunction() }
|
||||
override Function getDeclaration() { result = this.getFunction() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "FunctionDeclarationEntry" }
|
||||
|
||||
@@ -586,7 +586,7 @@ class FunctionDeclarationEntry extends DeclarationEntry, @fun_decl {
|
||||
* case, catch) plus the number of branching expressions (`?`, `&&`,
|
||||
* `||`) plus one.
|
||||
*/
|
||||
int getCyclomaticComplexity() { result = 1 + cyclomaticComplexityBranches(getBlock()) }
|
||||
int getCyclomaticComplexity() { result = 1 + cyclomaticComplexityBranches(this.getBlock()) }
|
||||
|
||||
/**
|
||||
* If this is a function definition, get the block containing the
|
||||
@@ -594,7 +594,7 @@ class FunctionDeclarationEntry extends DeclarationEntry, @fun_decl {
|
||||
*/
|
||||
BlockStmt getBlock() {
|
||||
this.isDefinition() and
|
||||
result = getFunction().getBlock() and
|
||||
result = this.getFunction().getBlock() and
|
||||
result.getFile() = this.getFile()
|
||||
}
|
||||
|
||||
@@ -604,7 +604,7 @@ class FunctionDeclarationEntry extends DeclarationEntry, @fun_decl {
|
||||
*/
|
||||
pragma[noopt]
|
||||
int getNumberOfLines() {
|
||||
exists(BlockStmt b, Location l, int start, int end, int diff | b = getBlock() |
|
||||
exists(BlockStmt b, Location l, int start, int end, int diff | b = this.getBlock() |
|
||||
l = b.getLocation() and
|
||||
start = l.getStartLine() and
|
||||
end = l.getEndLine() and
|
||||
@@ -618,7 +618,7 @@ class FunctionDeclarationEntry extends DeclarationEntry, @fun_decl {
|
||||
* declaration.
|
||||
*/
|
||||
ParameterDeclarationEntry getAParameterDeclarationEntry() {
|
||||
result = getParameterDeclarationEntry(_)
|
||||
result = this.getParameterDeclarationEntry(_)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -639,7 +639,8 @@ class FunctionDeclarationEntry extends DeclarationEntry, @fun_decl {
|
||||
* return 'int p1, int p2'.
|
||||
*/
|
||||
string getParameterString() {
|
||||
result = concat(int i | | min(getParameterDeclarationEntry(i).getTypedName()), ", " order by i)
|
||||
result =
|
||||
concat(int i | | min(this.getParameterDeclarationEntry(i).getTypedName()), ", " order by i)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -647,10 +648,10 @@ class FunctionDeclarationEntry extends DeclarationEntry, @fun_decl {
|
||||
*
|
||||
* `extern "C" void foo();`
|
||||
*/
|
||||
predicate hasCLinkage() { getASpecifier() = "c_linkage" }
|
||||
predicate hasCLinkage() { this.getASpecifier() = "c_linkage" }
|
||||
|
||||
/** Holds if this declaration entry has a void parameter list. */
|
||||
predicate hasVoidParamList() { getASpecifier() = "void_param_list" }
|
||||
predicate hasVoidParamList() { this.getASpecifier() = "void_param_list" }
|
||||
|
||||
/** Holds if this declaration is also a definition of its function. */
|
||||
override predicate isDefinition() { fun_def(underlyingElement(this)) }
|
||||
@@ -665,7 +666,7 @@ class FunctionDeclarationEntry extends DeclarationEntry, @fun_decl {
|
||||
predicate isImplicit() { fun_implicit(underlyingElement(this)) }
|
||||
|
||||
/** Gets a type that is specified to be thrown by the declared function. */
|
||||
Type getAThrownType() { result = getThrownType(_) }
|
||||
Type getAThrownType() { result = this.getThrownType(_) }
|
||||
|
||||
/**
|
||||
* Gets the `i`th type specified to be thrown by the declared function
|
||||
@@ -690,8 +691,8 @@ class FunctionDeclarationEntry extends DeclarationEntry, @fun_decl {
|
||||
predicate hasExceptionSpecification() {
|
||||
fun_decl_throws(underlyingElement(this), _, _) or
|
||||
fun_decl_noexcept(underlyingElement(this), _) or
|
||||
isNoThrow() or
|
||||
isNoExcept()
|
||||
this.isNoThrow() or
|
||||
this.isNoExcept()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -763,7 +764,7 @@ class Operator extends Function {
|
||||
*/
|
||||
class TemplateFunction extends Function {
|
||||
TemplateFunction() {
|
||||
is_function_template(underlyingElement(this)) and exists(getATemplateArgument())
|
||||
is_function_template(underlyingElement(this)) and exists(this.getATemplateArgument())
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() { result = "TemplateFunction" }
|
||||
|
||||
@@ -23,7 +23,7 @@ class Include extends PreprocessorDirective, @ppd_include {
|
||||
* Gets the token which occurs after `#include`, for example `"filename"`
|
||||
* or `<filename>`.
|
||||
*/
|
||||
string getIncludeText() { result = getHead() }
|
||||
string getIncludeText() { result = this.getHead() }
|
||||
|
||||
/** Gets the file directly included by this `#include`. */
|
||||
File getIncludedFile() { includes(underlyingElement(this), unresolveElement(result)) }
|
||||
@@ -53,7 +53,7 @@ class Include extends PreprocessorDirective, @ppd_include {
|
||||
* ```
|
||||
*/
|
||||
class IncludeNext extends Include, @ppd_include_next {
|
||||
override string toString() { result = "#include_next " + getIncludeText() }
|
||||
override string toString() { result = "#include_next " + this.getIncludeText() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -65,5 +65,5 @@ class IncludeNext extends Include, @ppd_include_next {
|
||||
* ```
|
||||
*/
|
||||
class Import extends Include, @ppd_objc_import {
|
||||
override string toString() { result = "#import " + getIncludeText() }
|
||||
override string toString() { result = "#import " + this.getIncludeText() }
|
||||
}
|
||||
|
||||
@@ -34,8 +34,8 @@ class Initializer extends ControlFlowNode, @initialiser {
|
||||
override predicate fromSource() { not this.getLocation() instanceof UnknownLocation }
|
||||
|
||||
override string toString() {
|
||||
if exists(getDeclaration())
|
||||
then result = "initializer for " + max(getDeclaration().getName())
|
||||
if exists(this.getDeclaration())
|
||||
then result = "initializer for " + max(this.getDeclaration().getName())
|
||||
else result = "initializer"
|
||||
}
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ class Location extends @location {
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
|
||||
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
@@ -79,8 +79,8 @@ class Location extends @location {
|
||||
|
||||
/** Holds if location `l` is completely contained within this one. */
|
||||
predicate subsumes(Location l) {
|
||||
exists(File f | f = getFile() |
|
||||
exists(int thisStart, int thisEnd | charLoc(f, thisStart, thisEnd) |
|
||||
exists(File f | f = this.getFile() |
|
||||
exists(int thisStart, int thisEnd | this.charLoc(f, thisStart, thisEnd) |
|
||||
exists(int lStart, int lEnd | l.charLoc(f, lStart, lEnd) |
|
||||
thisStart <= lStart and lEnd <= thisEnd
|
||||
)
|
||||
@@ -97,10 +97,10 @@ class Location extends @location {
|
||||
* see `subsumes`.
|
||||
*/
|
||||
predicate charLoc(File f, int start, int end) {
|
||||
f = getFile() and
|
||||
f = this.getFile() and
|
||||
exists(int maxCols | maxCols = maxCols(f) |
|
||||
start = getStartLine() * maxCols + getStartColumn() and
|
||||
end = getEndLine() * maxCols + getEndColumn()
|
||||
start = this.getStartLine() * maxCols + this.getStartColumn() and
|
||||
end = this.getEndLine() * maxCols + this.getEndColumn()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -144,7 +144,7 @@ class Locatable extends Element { }
|
||||
* expressions, one for statements and one for other program elements.
|
||||
*/
|
||||
class UnknownLocation extends Location {
|
||||
UnknownLocation() { getFile().getAbsolutePath() = "" }
|
||||
UnknownLocation() { this.getFile().getAbsolutePath() = "" }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -44,10 +44,10 @@ class Macro extends PreprocessorDirective, @ppd_define {
|
||||
* Gets the name of the macro. For example, `MAX` in
|
||||
* `#define MAX(x,y) (((x)>(y))?(x):(y))`.
|
||||
*/
|
||||
string getName() { result = getHead().splitAt("(", 0) }
|
||||
string getName() { result = this.getHead().splitAt("(", 0) }
|
||||
|
||||
/** Holds if the macro has name `name`. */
|
||||
predicate hasName(string name) { getName() = name }
|
||||
predicate hasName(string name) { this.getName() = name }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -130,7 +130,7 @@ class MacroAccess extends Locatable, @macroinvocation {
|
||||
override string toString() { result = this.getMacro().getHead() }
|
||||
|
||||
/** Gets the name of the accessed macro. */
|
||||
string getMacroName() { result = getMacro().getName() }
|
||||
string getMacroName() { result = this.getMacro().getName() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -197,8 +197,8 @@ class MacroInvocation extends MacroAccess {
|
||||
* expression. In other cases, it may have multiple results or no results.
|
||||
*/
|
||||
Expr getExpr() {
|
||||
result = getAnExpandedElement() and
|
||||
not result.getParent() = getAnExpandedElement() and
|
||||
result = this.getAnExpandedElement() and
|
||||
not result.getParent() = this.getAnExpandedElement() and
|
||||
not result instanceof Conversion
|
||||
}
|
||||
|
||||
@@ -208,8 +208,8 @@ class MacroInvocation extends MacroAccess {
|
||||
* element is not a statement (for example if it is an expression).
|
||||
*/
|
||||
Stmt getStmt() {
|
||||
result = getAnExpandedElement() and
|
||||
not result.getParent() = getAnExpandedElement()
|
||||
result = this.getAnExpandedElement() and
|
||||
not result.getParent() = this.getAnExpandedElement()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -278,7 +278,7 @@ deprecated class MacroInvocationExpr extends Expr {
|
||||
MacroInvocation getInvocation() { result.getExpr() = this }
|
||||
|
||||
/** Gets the name of the invoked macro. */
|
||||
string getMacroName() { result = getInvocation().getMacroName() }
|
||||
string getMacroName() { result = this.getInvocation().getMacroName() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -298,7 +298,7 @@ deprecated class MacroInvocationStmt extends Stmt {
|
||||
MacroInvocation getInvocation() { result.getStmt() = this }
|
||||
|
||||
/** Gets the name of the invoked macro. */
|
||||
string getMacroName() { result = getInvocation().getMacroName() }
|
||||
string getMacroName() { result = this.getInvocation().getMacroName() }
|
||||
}
|
||||
|
||||
/** Holds if `l` is the location of a macro. */
|
||||
|
||||
@@ -36,7 +36,9 @@ class MemberFunction extends Function {
|
||||
* `this` parameter.
|
||||
*/
|
||||
override int getEffectiveNumberOfParameters() {
|
||||
if isStatic() then result = getNumberOfParameters() else result = getNumberOfParameters() + 1
|
||||
if this.isStatic()
|
||||
then result = this.getNumberOfParameters()
|
||||
else result = this.getNumberOfParameters() + 1
|
||||
}
|
||||
|
||||
/** Holds if this member is private. */
|
||||
@@ -49,13 +51,13 @@ class MemberFunction extends Function {
|
||||
predicate isPublic() { this.hasSpecifier("public") }
|
||||
|
||||
/** Holds if this declaration has the lvalue ref-qualifier */
|
||||
predicate isLValueRefQualified() { hasSpecifier("&") }
|
||||
predicate isLValueRefQualified() { this.hasSpecifier("&") }
|
||||
|
||||
/** Holds if this declaration has the rvalue ref-qualifier */
|
||||
predicate isRValueRefQualified() { hasSpecifier("&&") }
|
||||
predicate isRValueRefQualified() { this.hasSpecifier("&&") }
|
||||
|
||||
/** Holds if this declaration has a ref-qualifier */
|
||||
predicate isRefQualified() { isLValueRefQualified() or isRValueRefQualified() }
|
||||
predicate isRefQualified() { this.isLValueRefQualified() or this.isRValueRefQualified() }
|
||||
|
||||
/** Holds if this function overrides that function. */
|
||||
predicate overrides(MemberFunction that) {
|
||||
@@ -73,10 +75,10 @@ class MemberFunction extends Function {
|
||||
* class body.
|
||||
*/
|
||||
FunctionDeclarationEntry getClassBodyDeclarationEntry() {
|
||||
if strictcount(getADeclarationEntry()) = 1
|
||||
then result = getDefinition()
|
||||
if strictcount(this.getADeclarationEntry()) = 1
|
||||
then result = this.getDefinition()
|
||||
else (
|
||||
result = getADeclarationEntry() and result != getDefinition()
|
||||
result = this.getADeclarationEntry() and result != this.getDefinition()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -198,7 +200,7 @@ class Constructor extends MemberFunction {
|
||||
* compiler-generated action which initializes a base class or member
|
||||
* variable.
|
||||
*/
|
||||
ConstructorInit getAnInitializer() { result = getInitializer(_) }
|
||||
ConstructorInit getAnInitializer() { result = this.getInitializer(_) }
|
||||
|
||||
/**
|
||||
* Gets an entry in the constructor's initializer list, or a
|
||||
@@ -220,8 +222,8 @@ class ImplicitConversionFunction extends MemberFunction {
|
||||
functions(underlyingElement(this), _, 4)
|
||||
or
|
||||
// ConversionConstructor (deprecated)
|
||||
strictcount(Parameter p | p = getAParameter() and not p.hasInitializer()) = 1 and
|
||||
not hasSpecifier("explicit")
|
||||
strictcount(Parameter p | p = this.getAParameter() and not p.hasInitializer()) = 1 and
|
||||
not this.hasSpecifier("explicit")
|
||||
}
|
||||
|
||||
/** Gets the type this `ImplicitConversionFunction` takes as input. */
|
||||
@@ -248,8 +250,8 @@ class ImplicitConversionFunction extends MemberFunction {
|
||||
*/
|
||||
deprecated class ConversionConstructor extends Constructor, ImplicitConversionFunction {
|
||||
ConversionConstructor() {
|
||||
strictcount(Parameter p | p = getAParameter() and not p.hasInitializer()) = 1 and
|
||||
not hasSpecifier("explicit")
|
||||
strictcount(Parameter p | p = this.getAParameter() and not p.hasInitializer()) = 1 and
|
||||
not this.hasSpecifier("explicit")
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() {
|
||||
@@ -301,15 +303,15 @@ class CopyConstructor extends Constructor {
|
||||
hasCopySignature(this) and
|
||||
(
|
||||
// The rest of the parameters all have default values
|
||||
forall(int i | i > 0 and exists(getParameter(i)) | getParameter(i).hasInitializer())
|
||||
forall(int i | i > 0 and exists(this.getParameter(i)) | this.getParameter(i).hasInitializer())
|
||||
or
|
||||
// or this is a template class, in which case the default values have
|
||||
// not been extracted even if they exist. In that case, we assume that
|
||||
// there are default values present since that is the most common case
|
||||
// in real-world code.
|
||||
getDeclaringType() instanceof TemplateClass
|
||||
this.getDeclaringType() instanceof TemplateClass
|
||||
) and
|
||||
not exists(getATemplateArgument())
|
||||
not exists(this.getATemplateArgument())
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() { result = "CopyConstructor" }
|
||||
@@ -325,8 +327,8 @@ class CopyConstructor extends Constructor {
|
||||
// type-checked for each template instantiation; if an argument in an
|
||||
// instantiation fails to type-check then the corresponding parameter has
|
||||
// no default argument in the instantiation.
|
||||
getDeclaringType() instanceof TemplateClass and
|
||||
getNumberOfParameters() > 1
|
||||
this.getDeclaringType() instanceof TemplateClass and
|
||||
this.getNumberOfParameters() > 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -358,15 +360,15 @@ class MoveConstructor extends Constructor {
|
||||
hasMoveSignature(this) and
|
||||
(
|
||||
// The rest of the parameters all have default values
|
||||
forall(int i | i > 0 and exists(getParameter(i)) | getParameter(i).hasInitializer())
|
||||
forall(int i | i > 0 and exists(this.getParameter(i)) | this.getParameter(i).hasInitializer())
|
||||
or
|
||||
// or this is a template class, in which case the default values have
|
||||
// not been extracted even if they exist. In that case, we assume that
|
||||
// there are default values present since that is the most common case
|
||||
// in real-world code.
|
||||
getDeclaringType() instanceof TemplateClass
|
||||
this.getDeclaringType() instanceof TemplateClass
|
||||
) and
|
||||
not exists(getATemplateArgument())
|
||||
not exists(this.getATemplateArgument())
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() { result = "MoveConstructor" }
|
||||
@@ -382,8 +384,8 @@ class MoveConstructor extends Constructor {
|
||||
// type-checked for each template instantiation; if an argument in an
|
||||
// instantiation fails to type-check then the corresponding parameter has
|
||||
// no default argument in the instantiation.
|
||||
getDeclaringType() instanceof TemplateClass and
|
||||
getNumberOfParameters() > 1
|
||||
this.getDeclaringType() instanceof TemplateClass and
|
||||
this.getNumberOfParameters() > 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -426,7 +428,7 @@ class Destructor extends MemberFunction {
|
||||
* Gets a compiler-generated action which destructs a base class or member
|
||||
* variable.
|
||||
*/
|
||||
DestructorDestruction getADestruction() { result = getDestruction(_) }
|
||||
DestructorDestruction getADestruction() { result = this.getDestruction(_) }
|
||||
|
||||
/**
|
||||
* Gets a compiler-generated action which destructs a base class or member
|
||||
@@ -475,16 +477,16 @@ class ConversionOperator extends MemberFunction, ImplicitConversionFunction {
|
||||
*/
|
||||
class CopyAssignmentOperator extends Operator {
|
||||
CopyAssignmentOperator() {
|
||||
hasName("operator=") and
|
||||
this.hasName("operator=") and
|
||||
(
|
||||
hasCopySignature(this)
|
||||
or
|
||||
// Unlike CopyConstructor, this member allows a non-reference
|
||||
// parameter.
|
||||
getParameter(0).getUnspecifiedType() = getDeclaringType()
|
||||
this.getParameter(0).getUnspecifiedType() = this.getDeclaringType()
|
||||
) and
|
||||
not exists(this.getParameter(1)) and
|
||||
not exists(getATemplateArgument())
|
||||
not exists(this.getATemplateArgument())
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() { result = "CopyAssignmentOperator" }
|
||||
@@ -507,10 +509,10 @@ class CopyAssignmentOperator extends Operator {
|
||||
*/
|
||||
class MoveAssignmentOperator extends Operator {
|
||||
MoveAssignmentOperator() {
|
||||
hasName("operator=") and
|
||||
this.hasName("operator=") and
|
||||
hasMoveSignature(this) and
|
||||
not exists(this.getParameter(1)) and
|
||||
not exists(getATemplateArgument())
|
||||
not exists(this.getATemplateArgument())
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() { result = "MoveAssignmentOperator" }
|
||||
|
||||
@@ -38,8 +38,8 @@ class Namespace extends NameQualifyingElement, @namespace {
|
||||
* unless the namespace has exactly one declaration entry.
|
||||
*/
|
||||
override Location getLocation() {
|
||||
if strictcount(getADeclarationEntry()) = 1
|
||||
then result = getADeclarationEntry().getLocation()
|
||||
if strictcount(this.getADeclarationEntry()) = 1
|
||||
then result = this.getADeclarationEntry().getLocation()
|
||||
else result instanceof UnknownDefaultLocation
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ class Namespace extends NameQualifyingElement, @namespace {
|
||||
predicate hasName(string name) { name = this.getName() }
|
||||
|
||||
/** Holds if this namespace is anonymous. */
|
||||
predicate isAnonymous() { hasName("(unnamed namespace)") }
|
||||
predicate isAnonymous() { this.hasName("(unnamed namespace)") }
|
||||
|
||||
/** Gets the name of the parent namespace, if it exists. */
|
||||
private string getParentName() {
|
||||
@@ -60,9 +60,9 @@ class Namespace extends NameQualifyingElement, @namespace {
|
||||
|
||||
/** Gets the qualified name of this namespace. For example: `a::b`. */
|
||||
string getQualifiedName() {
|
||||
if exists(getParentName())
|
||||
then result = getParentNamespace().getQualifiedName() + "::" + getName()
|
||||
else result = getName()
|
||||
if exists(this.getParentName())
|
||||
then result = this.getParentNamespace().getQualifiedName() + "::" + this.getName()
|
||||
else result = this.getName()
|
||||
}
|
||||
|
||||
/** Gets the parent namespace, if any. */
|
||||
@@ -99,7 +99,7 @@ class Namespace extends NameQualifyingElement, @namespace {
|
||||
/** Gets a version of the `QualifiedName` that is more suitable for display purposes. */
|
||||
string getFriendlyName() { result = this.getQualifiedName() }
|
||||
|
||||
final override string toString() { result = getFriendlyName() }
|
||||
final override string toString() { result = this.getFriendlyName() }
|
||||
|
||||
/** Gets a declaration of (part of) this namespace. */
|
||||
NamespaceDeclarationEntry getADeclarationEntry() { result.getNamespace() = this }
|
||||
|
||||
@@ -40,12 +40,12 @@ class Parameter extends LocalScopeVariable, @parameter {
|
||||
*/
|
||||
override string getName() {
|
||||
exists(VariableDeclarationEntry vde |
|
||||
vde = getANamedDeclarationEntry() and result = vde.getName()
|
||||
vde = this.getANamedDeclarationEntry() and result = vde.getName()
|
||||
|
|
||||
vde.isDefinition() or not getANamedDeclarationEntry().isDefinition()
|
||||
vde.isDefinition() or not this.getANamedDeclarationEntry().isDefinition()
|
||||
)
|
||||
or
|
||||
not exists(getANamedDeclarationEntry()) and
|
||||
not exists(this.getANamedDeclarationEntry()) and
|
||||
result = "(unnamed parameter " + this.getIndex().toString() + ")"
|
||||
}
|
||||
|
||||
@@ -58,8 +58,12 @@ class Parameter extends LocalScopeVariable, @parameter {
|
||||
*/
|
||||
string getTypedName() {
|
||||
exists(string typeString, string nameString |
|
||||
(if exists(getType().getName()) then typeString = getType().getName() else typeString = "") and
|
||||
(if exists(getName()) then nameString = getName() else nameString = "") and
|
||||
(
|
||||
if exists(this.getType().getName())
|
||||
then typeString = this.getType().getName()
|
||||
else typeString = ""
|
||||
) and
|
||||
(if exists(this.getName()) then nameString = this.getName() else nameString = "") and
|
||||
(
|
||||
if typeString != "" and nameString != ""
|
||||
then result = typeString + " " + nameString
|
||||
@@ -69,7 +73,7 @@ class Parameter extends LocalScopeVariable, @parameter {
|
||||
}
|
||||
|
||||
private VariableDeclarationEntry getANamedDeclarationEntry() {
|
||||
result = getAnEffectiveDeclarationEntry() and result.getName() != ""
|
||||
result = this.getAnEffectiveDeclarationEntry() and result.getName() != ""
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -82,13 +86,13 @@ class Parameter extends LocalScopeVariable, @parameter {
|
||||
* own).
|
||||
*/
|
||||
private VariableDeclarationEntry getAnEffectiveDeclarationEntry() {
|
||||
if getFunction().isConstructedFrom(_)
|
||||
if this.getFunction().isConstructedFrom(_)
|
||||
then
|
||||
exists(Function prototypeInstantiation |
|
||||
prototypeInstantiation.getParameter(getIndex()) = result.getVariable() and
|
||||
getFunction().isConstructedFrom(prototypeInstantiation)
|
||||
prototypeInstantiation.getParameter(this.getIndex()) = result.getVariable() and
|
||||
this.getFunction().isConstructedFrom(prototypeInstantiation)
|
||||
)
|
||||
else result = getADeclarationEntry()
|
||||
else result = this.getADeclarationEntry()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -114,7 +118,7 @@ class Parameter extends LocalScopeVariable, @parameter {
|
||||
* `getName()` is not "(unnamed parameter i)" (where `i` is the index
|
||||
* of the parameter).
|
||||
*/
|
||||
predicate isNamed() { exists(getANamedDeclarationEntry()) }
|
||||
predicate isNamed() { exists(this.getANamedDeclarationEntry()) }
|
||||
|
||||
/**
|
||||
* Gets the function to which this parameter belongs, if it is a function
|
||||
@@ -157,9 +161,9 @@ class Parameter extends LocalScopeVariable, @parameter {
|
||||
*/
|
||||
override Location getLocation() {
|
||||
exists(VariableDeclarationEntry vde |
|
||||
vde = getAnEffectiveDeclarationEntry() and result = vde.getLocation()
|
||||
vde = this.getAnEffectiveDeclarationEntry() and result = vde.getLocation()
|
||||
|
|
||||
vde.isDefinition() or not getAnEffectiveDeclarationEntry().isDefinition()
|
||||
vde.isDefinition() or not this.getAnEffectiveDeclarationEntry().isDefinition()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,8 +29,8 @@ class PreprocessorDirective extends Locatable, @preprocdirect {
|
||||
PreprocessorBranch getAGuard() {
|
||||
exists(PreprocessorEndif e, int line |
|
||||
result.getEndIf() = e and
|
||||
e.getFile() = getFile() and
|
||||
result.getFile() = getFile() and
|
||||
e.getFile() = this.getFile() and
|
||||
result.getFile() = this.getFile() and
|
||||
line = this.getLocation().getStartLine() and
|
||||
result.getLocation().getStartLine() < line and
|
||||
line < e.getLocation().getEndLine()
|
||||
@@ -69,7 +69,9 @@ class PreprocessorBranchDirective extends PreprocessorDirective, TPreprocessorBr
|
||||
* directives in different translation units, then there can be more than
|
||||
* one result.
|
||||
*/
|
||||
PreprocessorEndif getEndIf() { preprocpair(unresolveElement(getIf()), unresolveElement(result)) }
|
||||
PreprocessorEndif getEndIf() {
|
||||
preprocpair(unresolveElement(this.getIf()), unresolveElement(result))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the next `#elif`, `#else` or `#endif` matching this branching
|
||||
@@ -137,7 +139,7 @@ class PreprocessorBranch extends PreprocessorBranchDirective, @ppd_branch {
|
||||
* which evaluated it, or was not taken by any translation unit which
|
||||
* evaluated it.
|
||||
*/
|
||||
predicate wasPredictable() { not (wasTaken() and wasNotTaken()) }
|
||||
predicate wasPredictable() { not (this.wasTaken() and this.wasNotTaken()) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -268,7 +270,7 @@ class PreprocessorUndef extends PreprocessorDirective, @ppd_undef {
|
||||
/**
|
||||
* Gets the name of the macro that is undefined.
|
||||
*/
|
||||
string getName() { result = getHead() }
|
||||
string getName() { result = this.getHead() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -105,8 +105,8 @@ private class DumpType extends Type {
|
||||
// for a `SpecifiedType`, insert the qualifiers after
|
||||
// `getDeclaratorSuffixBeforeQualifiers()`.
|
||||
result =
|
||||
getTypeSpecifier() + getDeclaratorPrefix() + getDeclaratorSuffixBeforeQualifiers() +
|
||||
getDeclaratorSuffix()
|
||||
this.getTypeSpecifier() + this.getDeclaratorPrefix() +
|
||||
this.getDeclaratorSuffixBeforeQualifiers() + this.getDeclaratorSuffix()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -147,29 +147,35 @@ private class DumpType extends Type {
|
||||
}
|
||||
|
||||
private class BuiltInDumpType extends DumpType, BuiltInType {
|
||||
override string getTypeSpecifier() { result = toString() }
|
||||
override string getTypeSpecifier() { result = this.toString() }
|
||||
}
|
||||
|
||||
private class IntegralDumpType extends BuiltInDumpType, IntegralType {
|
||||
override string getTypeSpecifier() { result = getCanonicalArithmeticType().toString() }
|
||||
override string getTypeSpecifier() { result = this.getCanonicalArithmeticType().toString() }
|
||||
}
|
||||
|
||||
private class DerivedDumpType extends DumpType, DerivedType {
|
||||
override string getTypeSpecifier() { result = getBaseType().(DumpType).getTypeSpecifier() }
|
||||
override string getTypeSpecifier() { result = this.getBaseType().(DumpType).getTypeSpecifier() }
|
||||
|
||||
override string getDeclaratorSuffixBeforeQualifiers() {
|
||||
result = getBaseType().(DumpType).getDeclaratorSuffixBeforeQualifiers()
|
||||
result = this.getBaseType().(DumpType).getDeclaratorSuffixBeforeQualifiers()
|
||||
}
|
||||
|
||||
override string getDeclaratorSuffix() { result = getBaseType().(DumpType).getDeclaratorSuffix() }
|
||||
override string getDeclaratorSuffix() {
|
||||
result = this.getBaseType().(DumpType).getDeclaratorSuffix()
|
||||
}
|
||||
}
|
||||
|
||||
private class DecltypeDumpType extends DumpType, Decltype {
|
||||
override string getTypeSpecifier() { result = getBaseType().(DumpType).getTypeSpecifier() }
|
||||
override string getTypeSpecifier() { result = this.getBaseType().(DumpType).getTypeSpecifier() }
|
||||
|
||||
override string getDeclaratorPrefix() { result = getBaseType().(DumpType).getDeclaratorPrefix() }
|
||||
override string getDeclaratorPrefix() {
|
||||
result = this.getBaseType().(DumpType).getDeclaratorPrefix()
|
||||
}
|
||||
|
||||
override string getDeclaratorSuffix() { result = getBaseType().(DumpType).getDeclaratorSuffix() }
|
||||
override string getDeclaratorSuffix() {
|
||||
result = this.getBaseType().(DumpType).getDeclaratorSuffix()
|
||||
}
|
||||
}
|
||||
|
||||
private class PointerIshDumpType extends DerivedDumpType {
|
||||
@@ -180,10 +186,10 @@ private class PointerIshDumpType extends DerivedDumpType {
|
||||
|
||||
override string getDeclaratorPrefix() {
|
||||
exists(string declarator |
|
||||
result = getBaseType().(DumpType).getDeclaratorPrefix() + declarator and
|
||||
if getBaseType().getUnspecifiedType() instanceof ArrayType
|
||||
then declarator = "(" + getDeclaratorToken() + ")"
|
||||
else declarator = getDeclaratorToken()
|
||||
result = this.getBaseType().(DumpType).getDeclaratorPrefix() + declarator and
|
||||
if this.getBaseType().getUnspecifiedType() instanceof ArrayType
|
||||
then declarator = "(" + this.getDeclaratorToken() + ")"
|
||||
else declarator = this.getDeclaratorToken()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -206,13 +212,13 @@ private class RValueReferenceDumpType extends PointerIshDumpType, RValueReferenc
|
||||
}
|
||||
|
||||
private class PointerToMemberDumpType extends DumpType, PointerToMemberType {
|
||||
override string getTypeSpecifier() { result = getBaseType().(DumpType).getTypeSpecifier() }
|
||||
override string getTypeSpecifier() { result = this.getBaseType().(DumpType).getTypeSpecifier() }
|
||||
|
||||
override string getDeclaratorPrefix() {
|
||||
exists(string declarator, string parenDeclarator, Type baseType |
|
||||
declarator = getClass().(DumpType).getTypeIdentityString() + "::*" and
|
||||
result = getBaseType().(DumpType).getDeclaratorPrefix() + " " + parenDeclarator and
|
||||
baseType = getBaseType().getUnspecifiedType() and
|
||||
declarator = this.getClass().(DumpType).getTypeIdentityString() + "::*" and
|
||||
result = this.getBaseType().(DumpType).getDeclaratorPrefix() + " " + parenDeclarator and
|
||||
baseType = this.getBaseType().getUnspecifiedType() and
|
||||
if baseType instanceof ArrayType or baseType instanceof RoutineType
|
||||
then parenDeclarator = "(" + declarator
|
||||
else parenDeclarator = declarator
|
||||
@@ -221,38 +227,44 @@ private class PointerToMemberDumpType extends DumpType, PointerToMemberType {
|
||||
|
||||
override string getDeclaratorSuffixBeforeQualifiers() {
|
||||
exists(Type baseType |
|
||||
baseType = getBaseType().getUnspecifiedType() and
|
||||
baseType = this.getBaseType().getUnspecifiedType() and
|
||||
if baseType instanceof ArrayType or baseType instanceof RoutineType
|
||||
then result = ")" + getBaseType().(DumpType).getDeclaratorSuffixBeforeQualifiers()
|
||||
else result = getBaseType().(DumpType).getDeclaratorSuffixBeforeQualifiers()
|
||||
then result = ")" + this.getBaseType().(DumpType).getDeclaratorSuffixBeforeQualifiers()
|
||||
else result = this.getBaseType().(DumpType).getDeclaratorSuffixBeforeQualifiers()
|
||||
)
|
||||
}
|
||||
|
||||
override string getDeclaratorSuffix() { result = getBaseType().(DumpType).getDeclaratorSuffix() }
|
||||
override string getDeclaratorSuffix() {
|
||||
result = this.getBaseType().(DumpType).getDeclaratorSuffix()
|
||||
}
|
||||
}
|
||||
|
||||
private class ArrayDumpType extends DerivedDumpType, ArrayType {
|
||||
override string getDeclaratorPrefix() { result = getBaseType().(DumpType).getDeclaratorPrefix() }
|
||||
override string getDeclaratorPrefix() {
|
||||
result = this.getBaseType().(DumpType).getDeclaratorPrefix()
|
||||
}
|
||||
|
||||
override string getDeclaratorSuffixBeforeQualifiers() {
|
||||
if exists(getArraySize())
|
||||
if exists(this.getArraySize())
|
||||
then
|
||||
result =
|
||||
"[" + getArraySize().toString() + "]" +
|
||||
getBaseType().(DumpType).getDeclaratorSuffixBeforeQualifiers()
|
||||
else result = "[]" + getBaseType().(DumpType).getDeclaratorSuffixBeforeQualifiers()
|
||||
"[" + this.getArraySize().toString() + "]" +
|
||||
this.getBaseType().(DumpType).getDeclaratorSuffixBeforeQualifiers()
|
||||
else result = "[]" + this.getBaseType().(DumpType).getDeclaratorSuffixBeforeQualifiers()
|
||||
}
|
||||
}
|
||||
|
||||
private class FunctionPointerIshDumpType extends DerivedDumpType, FunctionPointerIshType {
|
||||
override string getDeclaratorSuffixBeforeQualifiers() {
|
||||
result = ")" + getBaseType().(DumpType).getDeclaratorSuffixBeforeQualifiers()
|
||||
result = ")" + this.getBaseType().(DumpType).getDeclaratorSuffixBeforeQualifiers()
|
||||
}
|
||||
|
||||
override string getDeclaratorSuffix() { result = getBaseType().(DumpType).getDeclaratorSuffix() }
|
||||
override string getDeclaratorSuffix() {
|
||||
result = this.getBaseType().(DumpType).getDeclaratorSuffix()
|
||||
}
|
||||
|
||||
override string getDeclaratorPrefix() {
|
||||
result = getBaseType().(DumpType).getDeclaratorPrefix() + "(" + getDeclaratorToken()
|
||||
result = this.getBaseType().(DumpType).getDeclaratorPrefix() + "(" + this.getDeclaratorToken()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -274,10 +286,10 @@ private class BlockDumpType extends FunctionPointerIshDumpType, BlockType {
|
||||
}
|
||||
|
||||
private class RoutineDumpType extends DumpType, RoutineType {
|
||||
override string getTypeSpecifier() { result = getReturnType().(DumpType).getTypeSpecifier() }
|
||||
override string getTypeSpecifier() { result = this.getReturnType().(DumpType).getTypeSpecifier() }
|
||||
|
||||
override string getDeclaratorPrefix() {
|
||||
result = getReturnType().(DumpType).getDeclaratorPrefix()
|
||||
result = this.getReturnType().(DumpType).getDeclaratorPrefix()
|
||||
}
|
||||
|
||||
language[monotonicAggregates]
|
||||
@@ -285,39 +297,41 @@ private class RoutineDumpType extends DumpType, RoutineType {
|
||||
result =
|
||||
"(" +
|
||||
concat(int i |
|
||||
exists(getParameterType(i))
|
||||
exists(this.getParameterType(i))
|
||||
|
|
||||
getParameterTypeString(getParameterType(i)), ", " order by i
|
||||
getParameterTypeString(this.getParameterType(i)), ", " order by i
|
||||
) + ")"
|
||||
}
|
||||
|
||||
override string getDeclaratorSuffix() {
|
||||
result =
|
||||
getReturnType().(DumpType).getDeclaratorSuffixBeforeQualifiers() +
|
||||
getReturnType().(DumpType).getDeclaratorSuffix()
|
||||
this.getReturnType().(DumpType).getDeclaratorSuffixBeforeQualifiers() +
|
||||
this.getReturnType().(DumpType).getDeclaratorSuffix()
|
||||
}
|
||||
}
|
||||
|
||||
private class SpecifiedDumpType extends DerivedDumpType, SpecifiedType {
|
||||
override string getDeclaratorPrefix() {
|
||||
exists(string basePrefix |
|
||||
basePrefix = getBaseType().(DumpType).getDeclaratorPrefix() and
|
||||
if getBaseType().getUnspecifiedType() instanceof RoutineType
|
||||
basePrefix = this.getBaseType().(DumpType).getDeclaratorPrefix() and
|
||||
if this.getBaseType().getUnspecifiedType() instanceof RoutineType
|
||||
then result = basePrefix
|
||||
else result = basePrefix + " " + getSpecifierString()
|
||||
else result = basePrefix + " " + this.getSpecifierString()
|
||||
)
|
||||
}
|
||||
|
||||
override string getDeclaratorSuffixBeforeQualifiers() {
|
||||
exists(string baseSuffix |
|
||||
baseSuffix = getBaseType().(DumpType).getDeclaratorSuffixBeforeQualifiers() and
|
||||
if getBaseType().getUnspecifiedType() instanceof RoutineType
|
||||
then result = baseSuffix + " " + getSpecifierString()
|
||||
baseSuffix = this.getBaseType().(DumpType).getDeclaratorSuffixBeforeQualifiers() and
|
||||
if this.getBaseType().getUnspecifiedType() instanceof RoutineType
|
||||
then result = baseSuffix + " " + this.getSpecifierString()
|
||||
else result = baseSuffix
|
||||
)
|
||||
}
|
||||
|
||||
override string getDeclaratorSuffix() { result = getBaseType().(DumpType).getDeclaratorSuffix() }
|
||||
override string getDeclaratorSuffix() {
|
||||
result = this.getBaseType().(DumpType).getDeclaratorSuffix()
|
||||
}
|
||||
}
|
||||
|
||||
private class UserDumpType extends DumpType, DumpDeclaration, UserType {
|
||||
@@ -330,18 +344,18 @@ private class UserDumpType extends DumpType, DumpDeclaration, UserType {
|
||||
// "lambda [] type at line 12, col. 40"
|
||||
// Use `min(getSimpleName())` to work around an extractor bug where a lambda can have different names
|
||||
// from different compilation units.
|
||||
simpleName = "(" + min(getSimpleName()) + ")"
|
||||
else simpleName = getSimpleName()
|
||||
simpleName = "(" + min(this.getSimpleName()) + ")"
|
||||
else simpleName = this.getSimpleName()
|
||||
) and
|
||||
result = getScopePrefix(this) + simpleName + getTemplateArgumentsString()
|
||||
result = getScopePrefix(this) + simpleName + this.getTemplateArgumentsString()
|
||||
)
|
||||
}
|
||||
|
||||
override string getTypeSpecifier() { result = getIdentityString() }
|
||||
override string getTypeSpecifier() { result = this.getIdentityString() }
|
||||
}
|
||||
|
||||
private class DumpProxyClass extends UserDumpType, ProxyClass {
|
||||
override string getIdentityString() { result = getName() }
|
||||
override string getIdentityString() { result = this.getName() }
|
||||
}
|
||||
|
||||
private class DumpVariable extends DumpDeclaration, Variable {
|
||||
@@ -360,9 +374,9 @@ private class DumpVariable extends DumpDeclaration, Variable {
|
||||
private class DumpFunction extends DumpDeclaration, Function {
|
||||
override string getIdentityString() {
|
||||
result =
|
||||
getType().(DumpType).getTypeSpecifier() + getType().(DumpType).getDeclaratorPrefix() + " " +
|
||||
getScopePrefix(this) + getName() + getTemplateArgumentsString() +
|
||||
getDeclaratorSuffixBeforeQualifiers() + getDeclaratorSuffix()
|
||||
this.getType().(DumpType).getTypeSpecifier() + this.getType().(DumpType).getDeclaratorPrefix()
|
||||
+ " " + getScopePrefix(this) + this.getName() + this.getTemplateArgumentsString() +
|
||||
this.getDeclaratorSuffixBeforeQualifiers() + this.getDeclaratorSuffix()
|
||||
}
|
||||
|
||||
language[monotonicAggregates]
|
||||
@@ -370,28 +384,29 @@ private class DumpFunction extends DumpDeclaration, Function {
|
||||
result =
|
||||
"(" +
|
||||
concat(int i |
|
||||
exists(getParameter(i).getType())
|
||||
exists(this.getParameter(i).getType())
|
||||
|
|
||||
getParameterTypeString(getParameter(i).getType()), ", " order by i
|
||||
) + ")" + getQualifierString()
|
||||
getParameterTypeString(this.getParameter(i).getType()), ", " order by i
|
||||
) + ")" + this.getQualifierString()
|
||||
}
|
||||
|
||||
private string getQualifierString() {
|
||||
if exists(getACVQualifier())
|
||||
if exists(this.getACVQualifier())
|
||||
then
|
||||
result = " " + strictconcat(string qualifier | qualifier = getACVQualifier() | qualifier, " ")
|
||||
result =
|
||||
" " + strictconcat(string qualifier | qualifier = this.getACVQualifier() | qualifier, " ")
|
||||
else result = ""
|
||||
}
|
||||
|
||||
private string getACVQualifier() {
|
||||
result = getASpecifier().getName() and
|
||||
result = this.getASpecifier().getName() and
|
||||
result = ["const", "volatile"]
|
||||
}
|
||||
|
||||
private string getDeclaratorSuffix() {
|
||||
result =
|
||||
getType().(DumpType).getDeclaratorSuffixBeforeQualifiers() +
|
||||
getType().(DumpType).getDeclaratorSuffix()
|
||||
this.getType().(DumpType).getDeclaratorSuffixBeforeQualifiers() +
|
||||
this.getType().(DumpType).getDeclaratorSuffix()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,11 +31,7 @@ class Specifier extends Element, @specifier {
|
||||
* A C/C++ function specifier: `inline`, `virtual`, or `explicit`.
|
||||
*/
|
||||
class FunctionSpecifier extends Specifier {
|
||||
FunctionSpecifier() {
|
||||
this.hasName("inline") or
|
||||
this.hasName("virtual") or
|
||||
this.hasName("explicit")
|
||||
}
|
||||
FunctionSpecifier() { this.hasName(["inline", "virtual", "explicit"]) }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "FunctionSpecifier" }
|
||||
}
|
||||
@@ -45,13 +41,7 @@ class FunctionSpecifier extends Specifier {
|
||||
* or `mutable".
|
||||
*/
|
||||
class StorageClassSpecifier extends Specifier {
|
||||
StorageClassSpecifier() {
|
||||
this.hasName("auto") or
|
||||
this.hasName("register") or
|
||||
this.hasName("static") or
|
||||
this.hasName("extern") or
|
||||
this.hasName("mutable")
|
||||
}
|
||||
StorageClassSpecifier() { this.hasName(["auto", "register", "static", "extern", "mutable"]) }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "StorageClassSpecifier" }
|
||||
}
|
||||
@@ -60,11 +50,7 @@ class StorageClassSpecifier extends Specifier {
|
||||
* A C++ access specifier: `public`, `protected`, or `private`.
|
||||
*/
|
||||
class AccessSpecifier extends Specifier {
|
||||
AccessSpecifier() {
|
||||
this.hasName("public") or
|
||||
this.hasName("protected") or
|
||||
this.hasName("private")
|
||||
}
|
||||
AccessSpecifier() { this.hasName(["public", "protected", "private"]) }
|
||||
|
||||
/**
|
||||
* Gets the visibility of a field with access specifier `this` if it is
|
||||
@@ -140,7 +126,7 @@ class Attribute extends Element, @attribute {
|
||||
AttributeArgument getArgument(int i) { result.getAttribute() = this and result.getIndex() = i }
|
||||
|
||||
/** Gets an argument of the attribute. */
|
||||
AttributeArgument getAnArgument() { result = getArgument(_) }
|
||||
AttributeArgument getAnArgument() { result = this.getArgument(_) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -166,7 +152,7 @@ class StdAttribute extends Attribute, @stdattribute {
|
||||
* Holds if this attribute has the given namespace and name.
|
||||
*/
|
||||
predicate hasQualifiedName(string namespace, string name) {
|
||||
namespace = getNamespace() and hasName(name)
|
||||
namespace = this.getNamespace() and this.hasName(name)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,7 +170,7 @@ class Declspec extends Attribute, @declspec { }
|
||||
*/
|
||||
class MicrosoftAttribute extends Attribute, @msattribute {
|
||||
AttributeArgument getNamedArgument(string name) {
|
||||
result = getAnArgument() and result.getName() = name
|
||||
result = this.getAnArgument() and result.getName() = name
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,13 +198,13 @@ class AlignAs extends Attribute, @alignas {
|
||||
* ```
|
||||
*/
|
||||
class FormatAttribute extends GnuAttribute {
|
||||
FormatAttribute() { getName() = "format" }
|
||||
FormatAttribute() { this.getName() = "format" }
|
||||
|
||||
/**
|
||||
* Gets the archetype of this format attribute, for example
|
||||
* `"printf"`.
|
||||
*/
|
||||
string getArchetype() { result = getArgument(0).getValueText() }
|
||||
string getArchetype() { result = this.getArgument(0).getValueText() }
|
||||
|
||||
/**
|
||||
* Gets the index in (1-based) format attribute notation associated
|
||||
@@ -236,7 +222,7 @@ class FormatAttribute extends GnuAttribute {
|
||||
* Gets the (0-based) index of the format string,
|
||||
* according to this attribute.
|
||||
*/
|
||||
int getFormatIndex() { result = getArgument(1).getValueInt() - firstArgumentNumber() }
|
||||
int getFormatIndex() { result = this.getArgument(1).getValueInt() - this.firstArgumentNumber() }
|
||||
|
||||
/**
|
||||
* Gets the (0-based) index of the first format argument (if any),
|
||||
@@ -244,8 +230,8 @@ class FormatAttribute extends GnuAttribute {
|
||||
*/
|
||||
int getFirstFormatArgIndex() {
|
||||
exists(int val |
|
||||
val = getArgument(2).getValueInt() and
|
||||
result = val - firstArgumentNumber() and
|
||||
val = this.getArgument(2).getValueInt() and
|
||||
result = val - this.firstArgumentNumber() and
|
||||
not val = 0 // indicates a `vprintf` style format function with arguments not directly available.
|
||||
)
|
||||
}
|
||||
@@ -277,7 +263,7 @@ class AttributeArgument extends Element, @attribute_arg {
|
||||
/**
|
||||
* Gets the value of this argument, if its value is integral.
|
||||
*/
|
||||
int getValueInt() { result = getValueText().toInt() }
|
||||
int getValueInt() { result = this.getValueText().toInt() }
|
||||
|
||||
/**
|
||||
* Gets the value of this argument, if its value is a type.
|
||||
@@ -304,11 +290,11 @@ class AttributeArgument extends Element, @attribute_arg {
|
||||
then result = "empty argument"
|
||||
else
|
||||
exists(string prefix, string tail |
|
||||
(if exists(getName()) then prefix = getName() + "=" else prefix = "") and
|
||||
(if exists(this.getName()) then prefix = this.getName() + "=" else prefix = "") and
|
||||
(
|
||||
if exists(@attribute_arg_type self | self = underlyingElement(this))
|
||||
then tail = getValueType().getName()
|
||||
else tail = getValueText()
|
||||
then tail = this.getValueType().getName()
|
||||
else tail = this.getValueText()
|
||||
) and
|
||||
result = prefix + tail
|
||||
)
|
||||
|
||||
@@ -41,7 +41,7 @@ class Struct extends Class {
|
||||
* ```
|
||||
*/
|
||||
class LocalStruct extends Struct {
|
||||
LocalStruct() { isLocal() }
|
||||
LocalStruct() { this.isLocal() }
|
||||
|
||||
override string getAPrimaryQlClass() { not this instanceof LocalUnion and result = "LocalStruct" }
|
||||
}
|
||||
|
||||
@@ -10,8 +10,8 @@ import semmle.code.cpp.File
|
||||
*/
|
||||
private class GoogleTestHeader extends File {
|
||||
GoogleTestHeader() {
|
||||
getBaseName() = "gtest.h" and
|
||||
getParentContainer().getBaseName() = "gtest"
|
||||
this.getBaseName() = "gtest.h" and
|
||||
this.getParentContainer().getBaseName() = "gtest"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,8 +30,8 @@ private class GoogleTest extends MacroInvocation {
|
||||
*/
|
||||
private class BoostTestFolder extends Folder {
|
||||
BoostTestFolder() {
|
||||
getBaseName() = "test" and
|
||||
getParentContainer().getBaseName() = "boost"
|
||||
this.getBaseName() = "test" and
|
||||
this.getParentContainer().getBaseName() = "boost"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ private class BoostTest extends MacroInvocation {
|
||||
* The `cppunit` directory.
|
||||
*/
|
||||
private class CppUnitFolder extends Folder {
|
||||
CppUnitFolder() { getBaseName() = "cppunit" }
|
||||
CppUnitFolder() { this.getBaseName() = "cppunit" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -57,8 +57,8 @@ private class CppUnitFolder extends Folder {
|
||||
*/
|
||||
private class CppUnitClass extends Class {
|
||||
CppUnitClass() {
|
||||
getFile().getParentContainer+() instanceof CppUnitFolder and
|
||||
getNamespace().getParentNamespace*().getName() = "CppUnit"
|
||||
this.getFile().getParentContainer+() instanceof CppUnitFolder and
|
||||
this.getNamespace().getParentNamespace*().getName() = "CppUnit"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@ class Type extends Locatable, @type {
|
||||
* Holds if this type refers to type `t` (by default,
|
||||
* a type always refers to itself).
|
||||
*/
|
||||
predicate refersTo(Type t) { refersToDirectly*(t) }
|
||||
predicate refersTo(Type t) { this.refersToDirectly*(t) }
|
||||
|
||||
/**
|
||||
* Holds if this type refers to type `t` directly.
|
||||
@@ -1080,11 +1080,11 @@ class DerivedType extends Type, @derivedtype {
|
||||
|
||||
override predicate refersToDirectly(Type t) { t = this.getBaseType() }
|
||||
|
||||
override predicate involvesReference() { getBaseType().involvesReference() }
|
||||
override predicate involvesReference() { this.getBaseType().involvesReference() }
|
||||
|
||||
override predicate involvesTemplateParameter() { getBaseType().involvesTemplateParameter() }
|
||||
override predicate involvesTemplateParameter() { this.getBaseType().involvesTemplateParameter() }
|
||||
|
||||
override Type stripType() { result = getBaseType().stripType() }
|
||||
override Type stripType() { result = this.getBaseType().stripType() }
|
||||
|
||||
/**
|
||||
* Holds if this type has the `__autoreleasing` specifier or if it points to
|
||||
@@ -1165,33 +1165,35 @@ class Decltype extends Type, @decltype {
|
||||
*/
|
||||
predicate parenthesesWouldChangeMeaning() { decltypes(underlyingElement(this), _, _, true) }
|
||||
|
||||
override Type getUnderlyingType() { result = getBaseType().getUnderlyingType() }
|
||||
override Type getUnderlyingType() { result = this.getBaseType().getUnderlyingType() }
|
||||
|
||||
override Type stripTopLevelSpecifiers() { result = getBaseType().stripTopLevelSpecifiers() }
|
||||
override Type stripTopLevelSpecifiers() { result = this.getBaseType().stripTopLevelSpecifiers() }
|
||||
|
||||
override Type stripType() { result = getBaseType().stripType() }
|
||||
override Type stripType() { result = this.getBaseType().stripType() }
|
||||
|
||||
override Type resolveTypedefs() { result = getBaseType().resolveTypedefs() }
|
||||
override Type resolveTypedefs() { result = this.getBaseType().resolveTypedefs() }
|
||||
|
||||
override Location getLocation() { result = getExpr().getLocation() }
|
||||
override Location getLocation() { result = this.getExpr().getLocation() }
|
||||
|
||||
override string toString() { result = "decltype(...)" }
|
||||
|
||||
override string getName() { none() }
|
||||
|
||||
override int getSize() { result = getBaseType().getSize() }
|
||||
override int getSize() { result = this.getBaseType().getSize() }
|
||||
|
||||
override int getAlignment() { result = getBaseType().getAlignment() }
|
||||
override int getAlignment() { result = this.getBaseType().getAlignment() }
|
||||
|
||||
override int getPointerIndirectionLevel() { result = getBaseType().getPointerIndirectionLevel() }
|
||||
override int getPointerIndirectionLevel() {
|
||||
result = this.getBaseType().getPointerIndirectionLevel()
|
||||
}
|
||||
|
||||
override string explain() {
|
||||
result = "decltype resulting in {" + this.getBaseType().explain() + "}"
|
||||
}
|
||||
|
||||
override predicate involvesReference() { getBaseType().involvesReference() }
|
||||
override predicate involvesReference() { this.getBaseType().involvesReference() }
|
||||
|
||||
override predicate involvesTemplateParameter() { getBaseType().involvesTemplateParameter() }
|
||||
override predicate involvesTemplateParameter() { this.getBaseType().involvesTemplateParameter() }
|
||||
|
||||
override predicate isDeeplyConst() { this.getBaseType().isDeeplyConst() }
|
||||
|
||||
@@ -1223,7 +1225,7 @@ class PointerType extends DerivedType {
|
||||
override predicate isDeeplyConstBelow() { this.getBaseType().isDeeplyConst() }
|
||||
|
||||
override Type resolveTypedefs() {
|
||||
result.(PointerType).getBaseType() = getBaseType().resolveTypedefs()
|
||||
result.(PointerType).getBaseType() = this.getBaseType().resolveTypedefs()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1240,7 +1242,9 @@ class ReferenceType extends DerivedType {
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ReferenceType" }
|
||||
|
||||
override int getPointerIndirectionLevel() { result = getBaseType().getPointerIndirectionLevel() }
|
||||
override int getPointerIndirectionLevel() {
|
||||
result = this.getBaseType().getPointerIndirectionLevel()
|
||||
}
|
||||
|
||||
override string explain() { result = "reference to {" + this.getBaseType().explain() + "}" }
|
||||
|
||||
@@ -1251,7 +1255,7 @@ class ReferenceType extends DerivedType {
|
||||
override predicate involvesReference() { any() }
|
||||
|
||||
override Type resolveTypedefs() {
|
||||
result.(ReferenceType).getBaseType() = getBaseType().resolveTypedefs()
|
||||
result.(ReferenceType).getBaseType() = this.getBaseType().resolveTypedefs()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1330,11 +1334,11 @@ class SpecifiedType extends DerivedType {
|
||||
}
|
||||
|
||||
override Type resolveTypedefs() {
|
||||
result.(SpecifiedType).getBaseType() = getBaseType().resolveTypedefs() and
|
||||
result.getASpecifier() = getASpecifier()
|
||||
result.(SpecifiedType).getBaseType() = this.getBaseType().resolveTypedefs() and
|
||||
result.getASpecifier() = this.getASpecifier()
|
||||
}
|
||||
|
||||
override Type stripTopLevelSpecifiers() { result = getBaseType().stripTopLevelSpecifiers() }
|
||||
override Type stripTopLevelSpecifiers() { result = this.getBaseType().stripTopLevelSpecifiers() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1433,7 +1437,8 @@ class GNUVectorType extends DerivedType {
|
||||
override int getAlignment() { arraysizes(underlyingElement(this), _, _, result) }
|
||||
|
||||
override string explain() {
|
||||
result = "GNU " + getNumElements() + " element vector of {" + this.getBaseType().explain() + "}"
|
||||
result =
|
||||
"GNU " + this.getNumElements() + " element vector of {" + this.getBaseType().explain() + "}"
|
||||
}
|
||||
|
||||
override predicate isDeeplyConstBelow() { this.getBaseType().isDeeplyConst() }
|
||||
@@ -1468,7 +1473,9 @@ class FunctionReferenceType extends FunctionPointerIshType {
|
||||
|
||||
override string getAPrimaryQlClass() { result = "FunctionReferenceType" }
|
||||
|
||||
override int getPointerIndirectionLevel() { result = getBaseType().getPointerIndirectionLevel() }
|
||||
override int getPointerIndirectionLevel() {
|
||||
result = this.getBaseType().getPointerIndirectionLevel()
|
||||
}
|
||||
|
||||
override string explain() {
|
||||
result = "reference to {" + this.getBaseType().(RoutineType).explain() + "}"
|
||||
@@ -1535,8 +1542,8 @@ class FunctionPointerIshType extends DerivedType {
|
||||
int getNumberOfParameters() { result = count(int i | exists(this.getParameterType(i))) }
|
||||
|
||||
override predicate involvesTemplateParameter() {
|
||||
getReturnType().involvesTemplateParameter() or
|
||||
getAParameterType().involvesTemplateParameter()
|
||||
this.getReturnType().involvesTemplateParameter() or
|
||||
this.getAParameterType().involvesTemplateParameter()
|
||||
}
|
||||
|
||||
override predicate isDeeplyConstBelow() { this.getBaseType().isDeeplyConst() }
|
||||
@@ -1581,7 +1588,7 @@ class PointerToMemberType extends Type, @ptrtomember {
|
||||
this.getBaseType().explain() + "}"
|
||||
}
|
||||
|
||||
override predicate involvesTemplateParameter() { getBaseType().involvesTemplateParameter() }
|
||||
override predicate involvesTemplateParameter() { this.getBaseType().involvesTemplateParameter() }
|
||||
|
||||
override predicate isDeeplyConstBelow() { this.getBaseType().isDeeplyConst() }
|
||||
}
|
||||
@@ -1650,7 +1657,6 @@ class RoutineType extends Type, @routinetype {
|
||||
i = 0 and result = "" and not exists(this.getAParameterType())
|
||||
or
|
||||
(
|
||||
exists(this.getParameterType(i)) and
|
||||
if i < max(int j | exists(this.getParameterType(j)))
|
||||
then
|
||||
// Not the last one
|
||||
@@ -1671,8 +1677,8 @@ class RoutineType extends Type, @routinetype {
|
||||
override predicate isDeeplyConstBelow() { none() } // Current limitation: no such thing as a const routine type
|
||||
|
||||
override predicate involvesTemplateParameter() {
|
||||
getReturnType().involvesTemplateParameter() or
|
||||
getAParameterType().involvesTemplateParameter()
|
||||
this.getReturnType().involvesTemplateParameter() or
|
||||
this.getAParameterType().involvesTemplateParameter()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ class TypedefType extends UserType {
|
||||
|
||||
override Type getUnderlyingType() { result = this.getBaseType().getUnderlyingType() }
|
||||
|
||||
override Type stripTopLevelSpecifiers() { result = getBaseType().stripTopLevelSpecifiers() }
|
||||
override Type stripTopLevelSpecifiers() { result = this.getBaseType().stripTopLevelSpecifiers() }
|
||||
|
||||
override int getSize() { result = this.getBaseType().getSize() }
|
||||
|
||||
@@ -43,11 +43,11 @@ class TypedefType extends UserType {
|
||||
result = this.getBaseType().getASpecifier()
|
||||
}
|
||||
|
||||
override predicate involvesReference() { getBaseType().involvesReference() }
|
||||
override predicate involvesReference() { this.getBaseType().involvesReference() }
|
||||
|
||||
override Type resolveTypedefs() { result = getBaseType().resolveTypedefs() }
|
||||
override Type resolveTypedefs() { result = this.getBaseType().resolveTypedefs() }
|
||||
|
||||
override Type stripType() { result = getBaseType().stripType() }
|
||||
override Type stripType() { result = this.getBaseType().stripType() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -90,7 +90,7 @@ class UsingAliasTypedefType extends TypedefType {
|
||||
* ```
|
||||
*/
|
||||
class LocalTypedefType extends TypedefType {
|
||||
LocalTypedefType() { isLocal() }
|
||||
LocalTypedefType() { this.isLocal() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "LocalTypedefType" }
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ class Union extends Struct {
|
||||
* ```
|
||||
*/
|
||||
class LocalUnion extends Union {
|
||||
LocalUnion() { isLocal() }
|
||||
LocalUnion() { this.isLocal() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "LocalUnion" }
|
||||
}
|
||||
|
||||
@@ -30,19 +30,19 @@ class UserType extends Type, Declaration, NameQualifyingElement, AccessHolder, @
|
||||
* Gets the simple name of this type, without any template parameters. For example
|
||||
* if the name of the type is `"myType<int>"`, the simple name is just `"myType"`.
|
||||
*/
|
||||
string getSimpleName() { result = getName().regexpReplaceAll("<.*", "") }
|
||||
string getSimpleName() { result = this.getName().regexpReplaceAll("<.*", "") }
|
||||
|
||||
override predicate hasName(string name) { usertypes(underlyingElement(this), name, _) }
|
||||
|
||||
/** Holds if this type is anonymous. */
|
||||
predicate isAnonymous() { getName().matches("(unnamed%") }
|
||||
predicate isAnonymous() { this.getName().matches("(unnamed%") }
|
||||
|
||||
override predicate hasSpecifier(string s) { Type.super.hasSpecifier(s) }
|
||||
|
||||
override Specifier getASpecifier() { result = Type.super.getASpecifier() }
|
||||
|
||||
override Location getLocation() {
|
||||
if hasDefinition()
|
||||
if this.hasDefinition()
|
||||
then result = this.getDefinitionLocation()
|
||||
else result = this.getADeclarationLocation()
|
||||
}
|
||||
@@ -53,16 +53,16 @@ class UserType extends Type, Declaration, NameQualifyingElement, AccessHolder, @
|
||||
else exists(Class t | this.(Class).isConstructedFrom(t) and result = t.getADeclarationEntry())
|
||||
}
|
||||
|
||||
override Location getADeclarationLocation() { result = getADeclarationEntry().getLocation() }
|
||||
override Location getADeclarationLocation() { result = this.getADeclarationEntry().getLocation() }
|
||||
|
||||
override TypeDeclarationEntry getDefinition() {
|
||||
result = getADeclarationEntry() and
|
||||
result = this.getADeclarationEntry() and
|
||||
result.isDefinition()
|
||||
}
|
||||
|
||||
override Location getDefinitionLocation() {
|
||||
if exists(getDefinition())
|
||||
then result = getDefinition().getLocation()
|
||||
if exists(this.getDefinition())
|
||||
then result = this.getDefinition().getLocation()
|
||||
else
|
||||
exists(Class t |
|
||||
this.(Class).isConstructedFrom(t) and result = t.getDefinition().getLocation()
|
||||
@@ -80,7 +80,7 @@ class UserType extends Type, Declaration, NameQualifyingElement, AccessHolder, @
|
||||
* Holds if this is a local type (that is, a type that has a directly-enclosing
|
||||
* function).
|
||||
*/
|
||||
predicate isLocal() { exists(getEnclosingFunction()) }
|
||||
predicate isLocal() { exists(this.getEnclosingFunction()) }
|
||||
|
||||
/*
|
||||
* Dummy implementations of inherited methods. This class must not be
|
||||
@@ -107,9 +107,9 @@ class UserType extends Type, Declaration, NameQualifyingElement, AccessHolder, @
|
||||
* ```
|
||||
*/
|
||||
class TypeDeclarationEntry extends DeclarationEntry, @type_decl {
|
||||
override UserType getDeclaration() { result = getType() }
|
||||
override UserType getDeclaration() { result = this.getType() }
|
||||
|
||||
override string getName() { result = getType().getName() }
|
||||
override string getName() { result = this.getType().getName() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "TypeDeclarationEntry" }
|
||||
|
||||
|
||||
@@ -104,17 +104,17 @@ class Variable extends Declaration, @variable {
|
||||
|
||||
override VariableDeclarationEntry getADeclarationEntry() { result.getDeclaration() = this }
|
||||
|
||||
override Location getADeclarationLocation() { result = getADeclarationEntry().getLocation() }
|
||||
override Location getADeclarationLocation() { result = this.getADeclarationEntry().getLocation() }
|
||||
|
||||
override VariableDeclarationEntry getDefinition() {
|
||||
result = getADeclarationEntry() and
|
||||
result = this.getADeclarationEntry() and
|
||||
result.isDefinition()
|
||||
}
|
||||
|
||||
override Location getDefinitionLocation() { result = getDefinition().getLocation() }
|
||||
override Location getDefinitionLocation() { result = this.getDefinition().getLocation() }
|
||||
|
||||
override Location getLocation() {
|
||||
if exists(getDefinition())
|
||||
if exists(this.getDefinition())
|
||||
then result = this.getDefinitionLocation()
|
||||
else result = this.getADeclarationLocation()
|
||||
}
|
||||
@@ -199,7 +199,7 @@ class Variable extends Declaration, @variable {
|
||||
* ```
|
||||
*/
|
||||
class VariableDeclarationEntry extends DeclarationEntry, @var_decl {
|
||||
override Variable getDeclaration() { result = getVariable() }
|
||||
override Variable getDeclaration() { result = this.getVariable() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "VariableDeclarationEntry" }
|
||||
|
||||
@@ -276,32 +276,33 @@ class ParameterDeclarationEntry extends VariableDeclarationEntry {
|
||||
int getIndex() { param_decl_bind(underlyingElement(this), result, _) }
|
||||
|
||||
private string getAnonymousParameterDescription() {
|
||||
not exists(getName()) and
|
||||
not exists(this.getName()) and
|
||||
exists(string idx |
|
||||
idx =
|
||||
((getIndex() + 1).toString() + "th")
|
||||
((this.getIndex() + 1).toString() + "th")
|
||||
.replaceAll("1th", "1st")
|
||||
.replaceAll("2th", "2nd")
|
||||
.replaceAll("3th", "3rd")
|
||||
.replaceAll("11st", "11th")
|
||||
.replaceAll("12nd", "12th")
|
||||
.replaceAll("13rd", "13th") and
|
||||
if exists(getCanonicalName())
|
||||
then result = "declaration of " + getCanonicalName() + " as anonymous " + idx + " parameter"
|
||||
if exists(this.getCanonicalName())
|
||||
then
|
||||
result = "declaration of " + this.getCanonicalName() + " as anonymous " + idx + " parameter"
|
||||
else result = "declaration of " + idx + " parameter"
|
||||
)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
isDefinition() and
|
||||
result = "definition of " + getName()
|
||||
this.isDefinition() and
|
||||
result = "definition of " + this.getName()
|
||||
or
|
||||
not isDefinition() and
|
||||
if getName() = getCanonicalName()
|
||||
then result = "declaration of " + getName()
|
||||
else result = "declaration of " + getCanonicalName() + " as " + getName()
|
||||
not this.isDefinition() and
|
||||
if this.getName() = this.getCanonicalName()
|
||||
then result = "declaration of " + this.getName()
|
||||
else result = "declaration of " + this.getCanonicalName() + " as " + this.getName()
|
||||
or
|
||||
result = getAnonymousParameterDescription()
|
||||
result = this.getAnonymousParameterDescription()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -311,8 +312,12 @@ class ParameterDeclarationEntry extends VariableDeclarationEntry {
|
||||
*/
|
||||
string getTypedName() {
|
||||
exists(string typeString, string nameString |
|
||||
(if exists(getType().getName()) then typeString = getType().getName() else typeString = "") and
|
||||
(if exists(getName()) then nameString = getName() else nameString = "") and
|
||||
(
|
||||
if exists(this.getType().getName())
|
||||
then typeString = this.getType().getName()
|
||||
else typeString = ""
|
||||
) and
|
||||
(if exists(this.getName()) then nameString = this.getName() else nameString = "") and
|
||||
if typeString != "" and nameString != ""
|
||||
then result = typeString + " " + nameString
|
||||
else result = typeString + nameString
|
||||
@@ -540,7 +545,7 @@ class MemberVariable extends Variable, @membervariable {
|
||||
}
|
||||
|
||||
/** Holds if this member is mutable. */
|
||||
predicate isMutable() { getADeclarationEntry().hasSpecifier("mutable") }
|
||||
predicate isMutable() { this.getADeclarationEntry().hasSpecifier("mutable") }
|
||||
|
||||
private Type getAType() { membervariables(underlyingElement(this), unresolveElement(result), _) }
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ class XMLLocatable extends @xmllocatable, TXMLLocatable {
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
|
||||
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
@@ -108,7 +108,7 @@ class XMLParent extends @xmlparent {
|
||||
}
|
||||
|
||||
/** Gets the text value contained in this XML parent. */
|
||||
string getTextValue() { result = allCharactersString() }
|
||||
string getTextValue() { result = this.allCharactersString() }
|
||||
|
||||
/** Gets a printable representation of this XML parent. */
|
||||
string toString() { result = this.getName() }
|
||||
@@ -119,7 +119,7 @@ class XMLFile extends XMLParent, File {
|
||||
XMLFile() { xmlEncoding(this, _) }
|
||||
|
||||
/** Gets a printable representation of this XML file. */
|
||||
override string toString() { result = getName() }
|
||||
override string toString() { result = this.getName() }
|
||||
|
||||
/** Gets the name of this XML file. */
|
||||
override string getName() { result = File.super.getAbsolutePath() }
|
||||
@@ -129,14 +129,14 @@ class XMLFile extends XMLParent, File {
|
||||
*
|
||||
* Gets the path of this XML file.
|
||||
*/
|
||||
deprecated string getPath() { result = getAbsolutePath() }
|
||||
deprecated string getPath() { result = this.getAbsolutePath() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `getParentContainer().getAbsolutePath()` instead.
|
||||
*
|
||||
* Gets the path of the folder that contains this XML file.
|
||||
*/
|
||||
deprecated string getFolder() { result = getParentContainer().getAbsolutePath() }
|
||||
deprecated string getFolder() { result = this.getParentContainer().getAbsolutePath() }
|
||||
|
||||
/** Gets the encoding of this XML file. */
|
||||
string getEncoding() { xmlEncoding(this, result) }
|
||||
@@ -200,7 +200,7 @@ class XMLDTD extends XMLLocatable, @xmldtd {
|
||||
*/
|
||||
class XMLElement extends @xmlelement, XMLParent, XMLLocatable {
|
||||
/** Holds if this XML element has the given `name`. */
|
||||
predicate hasName(string name) { name = getName() }
|
||||
predicate hasName(string name) { name = this.getName() }
|
||||
|
||||
/** Gets the name of this XML element. */
|
||||
override string getName() { xmlElements(this, result, _, _, _) }
|
||||
@@ -239,7 +239,7 @@ class XMLElement extends @xmlelement, XMLParent, XMLLocatable {
|
||||
string getAttributeValue(string name) { result = this.getAttribute(name).getValue() }
|
||||
|
||||
/** Gets a printable representation of this XML element. */
|
||||
override string toString() { result = getName() }
|
||||
override string toString() { result = this.getName() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,53 +2,19 @@ import cpp
|
||||
import semmle.code.cpp.dataflow.DataFlow
|
||||
|
||||
/**
|
||||
* Holds if `v` is a member variable of `c` that looks like it might be variable sized in practice. For
|
||||
* example:
|
||||
* Holds if `v` is a member variable of `c` that looks like it might be variable sized
|
||||
* in practice. For example:
|
||||
* ```
|
||||
* struct myStruct { // c
|
||||
* int amount;
|
||||
* char data[1]; // v
|
||||
* };
|
||||
* ```
|
||||
* This requires that `v` is an array of size 0 or 1, and `v` is the last member of `c`. In addition,
|
||||
* there must be at least one instance where a `c` pointer is allocated with additional space. For
|
||||
* example, holds for `c` if it occurs as
|
||||
* ```
|
||||
* malloc(sizeof(c) + 100 * sizeof(char))
|
||||
* ```
|
||||
* but not if it only ever occurs as
|
||||
* ```
|
||||
* malloc(sizeof(c))
|
||||
* ```
|
||||
* This requires that `v` is an array of size 0 or 1.
|
||||
*/
|
||||
predicate memberMayBeVarSize(Class c, MemberVariable v) {
|
||||
exists(int i |
|
||||
// `v` is the last field in `c`
|
||||
i = max(int j | c.getCanonicalMember(j) instanceof Field | j) and
|
||||
v = c.getCanonicalMember(i) and
|
||||
// v is an array of size at most 1
|
||||
v.getUnspecifiedType().(ArrayType).getArraySize() <= 1
|
||||
) and
|
||||
(
|
||||
exists(SizeofOperator so |
|
||||
// `sizeof(c)` is taken
|
||||
so.(SizeofTypeOperator).getTypeOperand().getUnspecifiedType() = c or
|
||||
so.(SizeofExprOperator).getExprOperand().getUnspecifiedType() = c
|
||||
|
|
||||
// arithmetic is performed on the result
|
||||
so.getParent*() instanceof AddExpr
|
||||
)
|
||||
or
|
||||
exists(AddressOfExpr aoe |
|
||||
// `&(c.v)` is taken
|
||||
aoe.getAddressable() = v
|
||||
)
|
||||
or
|
||||
exists(BuiltInOperationBuiltInOffsetOf oo |
|
||||
// `offsetof(c, v)` using a builtin
|
||||
oo.getAChild().(VariableAccess).getTarget() = v
|
||||
)
|
||||
)
|
||||
c = v.getDeclaringType() and
|
||||
v.getUnspecifiedType().(ArrayType).getArraySize() <= 1
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -375,8 +375,8 @@ class Wchar_t extends Type {
|
||||
class MicrosoftInt8Type extends IntegralType {
|
||||
MicrosoftInt8Type() {
|
||||
this instanceof CharType and
|
||||
not isExplicitlyUnsigned() and
|
||||
not isExplicitlySigned()
|
||||
not this.isExplicitlyUnsigned() and
|
||||
not this.isExplicitlySigned()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -391,8 +391,8 @@ class MicrosoftInt8Type extends IntegralType {
|
||||
class MicrosoftInt16Type extends IntegralType {
|
||||
MicrosoftInt16Type() {
|
||||
this instanceof ShortType and
|
||||
not isExplicitlyUnsigned() and
|
||||
not isExplicitlySigned()
|
||||
not this.isExplicitlyUnsigned() and
|
||||
not this.isExplicitlySigned()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -407,8 +407,8 @@ class MicrosoftInt16Type extends IntegralType {
|
||||
class MicrosoftInt32Type extends IntegralType {
|
||||
MicrosoftInt32Type() {
|
||||
this instanceof IntType and
|
||||
not isExplicitlyUnsigned() and
|
||||
not isExplicitlySigned()
|
||||
not this.isExplicitlyUnsigned() and
|
||||
not this.isExplicitlySigned()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -423,8 +423,8 @@ class MicrosoftInt32Type extends IntegralType {
|
||||
class MicrosoftInt64Type extends IntegralType {
|
||||
MicrosoftInt64Type() {
|
||||
this instanceof LongLongType and
|
||||
not isExplicitlyUnsigned() and
|
||||
not isExplicitlySigned()
|
||||
not this.isExplicitlyUnsigned() and
|
||||
not this.isExplicitlySigned()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ DependencyOptions getDependencyOptions() { any() }
|
||||
class DependsSource extends Element {
|
||||
DependsSource() {
|
||||
// not inside a template instantiation
|
||||
not exists(Element other | isFromTemplateInstantiation(other)) or
|
||||
not exists(Element other | this.isFromTemplateInstantiation(other)) or
|
||||
// allow DeclarationEntrys of template specializations
|
||||
this.(DeclarationEntry).getDeclaration().(Function).isConstructedFrom(_) or
|
||||
this.(DeclarationEntry).getDeclaration().(Class).isConstructedFrom(_)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import cpp
|
||||
private import semmle.code.cpp.models.interfaces.ArrayFunction
|
||||
private import semmle.code.cpp.models.implementations.Strcat
|
||||
import semmle.code.cpp.dataflow.DataFlow
|
||||
|
||||
private predicate mayAddNullTerminatorHelper(Expr e, VariableAccess va, Expr e0) {
|
||||
exists(StackVariable v0, Expr val |
|
||||
@@ -45,22 +46,28 @@ predicate mayAddNullTerminator(Expr e, VariableAccess va) {
|
||||
ae.getRValue().getAChild*() = va
|
||||
)
|
||||
or
|
||||
// Function call: library function, varargs function, function
|
||||
// containing assembler code, or function where the relevant
|
||||
// parameter is potentially added a null terminator.
|
||||
// Function calls...
|
||||
exists(Call c, Function f, int i |
|
||||
e = c and
|
||||
f = c.getTarget() and
|
||||
not functionArgumentMustBeNullTerminated(f, i) and
|
||||
c.getAnArgumentSubExpr(i) = va
|
||||
|
|
||||
not f.hasEntryPoint() and not functionArgumentMustBeNullTerminated(f, i)
|
||||
// library function
|
||||
not f.hasEntryPoint()
|
||||
or
|
||||
// function where the relevant parameter is potentially added a null terminator
|
||||
mayAddNullTerminator(_, f.getParameter(i).getAnAccess())
|
||||
or
|
||||
// varargs function
|
||||
f.isVarargs() and i >= f.getNumberOfParameters()
|
||||
or
|
||||
// function containing assembler code
|
||||
exists(AsmStmt s | s.getEnclosingFunction() = f)
|
||||
or
|
||||
// function where the relevant parameter is returned (leaking it to be potentially null terminated elsewhere)
|
||||
DataFlow::localFlow(DataFlow::parameterNode(f.getParameter(i)),
|
||||
DataFlow::exprNode(any(ReturnStmt rs).getExpr()))
|
||||
)
|
||||
or
|
||||
// Call without target (e.g., function pointer call)
|
||||
@@ -93,6 +100,15 @@ predicate variableMustBeNullTerminated(VariableAccess va) {
|
||||
fc.getArgument(i) = va
|
||||
)
|
||||
or
|
||||
// String argument to a formatting function (such as `printf`)
|
||||
exists(int n, FormatLiteral fl |
|
||||
fc.(FormattingFunctionCall).getConversionArgument(n) = va and
|
||||
fl = fc.(FormattingFunctionCall).getFormat() and
|
||||
fl.getConversionType(n) instanceof PointerType and // `%s`, `%ws` etc
|
||||
not fl.getConversionType(n) instanceof VoidPointerType and // exclude: `%p`
|
||||
not fl.hasPrecision(n) // exclude: `%.*s`
|
||||
)
|
||||
or
|
||||
// Call to a wrapper function that requires null termination
|
||||
// (not itself adding a null terminator)
|
||||
exists(Function wrapper, int i, Parameter p, VariableAccess use |
|
||||
|
||||
@@ -8,7 +8,7 @@ import semmle.code.cpp.commons.StringAnalysis
|
||||
import semmle.code.cpp.models.interfaces.FormattingFunction
|
||||
|
||||
class PrintfFormatAttribute extends FormatAttribute {
|
||||
PrintfFormatAttribute() { getArchetype() = ["printf", "__printf__"] }
|
||||
PrintfFormatAttribute() { this.getArchetype() = ["printf", "__printf__"] }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -20,13 +20,13 @@ class AttributeFormattingFunction extends FormattingFunction {
|
||||
|
||||
AttributeFormattingFunction() {
|
||||
exists(PrintfFormatAttribute printf_attrib |
|
||||
printf_attrib = getAnAttribute() and
|
||||
printf_attrib = this.getAnAttribute() and
|
||||
exists(printf_attrib.getFirstFormatArgIndex()) // exclude `vprintf` style format functions
|
||||
)
|
||||
}
|
||||
|
||||
override int getFormatParameterIndex() {
|
||||
forex(PrintfFormatAttribute printf_attrib | printf_attrib = getAnAttribute() |
|
||||
forex(PrintfFormatAttribute printf_attrib | printf_attrib = this.getAnAttribute() |
|
||||
result = printf_attrib.getFormatIndex()
|
||||
)
|
||||
}
|
||||
@@ -132,7 +132,7 @@ deprecated predicate variadicFormatter(Function f, int formatParamIndex) {
|
||||
class UserDefinedFormattingFunction extends FormattingFunction {
|
||||
override string getAPrimaryQlClass() { result = "UserDefinedFormattingFunction" }
|
||||
|
||||
UserDefinedFormattingFunction() { isVarargs() and callsVariadicFormatter(this, _, _, _) }
|
||||
UserDefinedFormattingFunction() { this.isVarargs() and callsVariadicFormatter(this, _, _, _) }
|
||||
|
||||
override int getFormatParameterIndex() { callsVariadicFormatter(this, _, result, _) }
|
||||
|
||||
@@ -191,7 +191,7 @@ class FormattingFunctionCall extends Expr {
|
||||
exists(int i |
|
||||
result = this.getArgument(i) and
|
||||
n >= 0 and
|
||||
n = i - getTarget().(FormattingFunction).getFirstFormatArgumentIndex()
|
||||
n = i - this.getTarget().(FormattingFunction).getFirstFormatArgumentIndex()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -251,7 +251,22 @@ class FormattingFunctionCall extends Expr {
|
||||
int getNumFormatArgument() {
|
||||
result = count(this.getFormatArgument(_)) and
|
||||
// format arguments must be known
|
||||
exists(getTarget().(FormattingFunction).getFirstFormatArgumentIndex())
|
||||
exists(this.getTarget().(FormattingFunction).getFirstFormatArgumentIndex())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the argument, if any, to which the output is written. If `isStream` is
|
||||
* `true`, the output argument is a stream (that is, this call behaves like
|
||||
* `fprintf`). If `isStream` is `false`, the output argument is a buffer (that
|
||||
* is, this call behaves like `sprintf`)
|
||||
*/
|
||||
Expr getOutputArgument(boolean isStream) {
|
||||
result =
|
||||
this.(Call)
|
||||
.getArgument(this.(Call)
|
||||
.getTarget()
|
||||
.(FormattingFunction)
|
||||
.getOutputParameterIndex(isStream))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -275,7 +290,7 @@ class FormatLiteral extends Literal {
|
||||
* DEPRECATED: Use getDefaultCharType() instead.
|
||||
*/
|
||||
deprecated predicate isWideCharDefault() {
|
||||
getUse().getTarget().(FormattingFunction).isWideCharDefault()
|
||||
this.getUse().getTarget().(FormattingFunction).isWideCharDefault()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -283,7 +298,7 @@ class FormatLiteral extends Literal {
|
||||
* `char` or `wchar_t`.
|
||||
*/
|
||||
Type getDefaultCharType() {
|
||||
result = getUse().getTarget().(FormattingFunction).getDefaultCharType()
|
||||
result = this.getUse().getTarget().(FormattingFunction).getDefaultCharType()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -292,7 +307,7 @@ class FormatLiteral extends Literal {
|
||||
* which is correct for a particular function.
|
||||
*/
|
||||
Type getNonDefaultCharType() {
|
||||
result = getUse().getTarget().(FormattingFunction).getNonDefaultCharType()
|
||||
result = this.getUse().getTarget().(FormattingFunction).getNonDefaultCharType()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -300,7 +315,9 @@ class FormatLiteral extends Literal {
|
||||
* snapshots there may be multiple results where we can't tell which is correct for a
|
||||
* particular function.
|
||||
*/
|
||||
Type getWideCharType() { result = getUse().getTarget().(FormattingFunction).getWideCharType() }
|
||||
Type getWideCharType() {
|
||||
result = this.getUse().getTarget().(FormattingFunction).getWideCharType()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this `FormatLiteral` is in a context that supports
|
||||
@@ -338,7 +355,7 @@ class FormatLiteral extends Literal {
|
||||
}
|
||||
|
||||
private string getFlagRegexp() {
|
||||
if isMicrosoft() then result = "[-+ #0']*" else result = "[-+ #0'I]*"
|
||||
if this.isMicrosoft() then result = "[-+ #0']*" else result = "[-+ #0'I]*"
|
||||
}
|
||||
|
||||
private string getFieldWidthRegexp() { result = "(?:[1-9][0-9]*|\\*|\\*[0-9]+\\$)?" }
|
||||
@@ -346,13 +363,13 @@ class FormatLiteral extends Literal {
|
||||
private string getPrecRegexp() { result = "(?:\\.(?:[0-9]*|\\*|\\*[0-9]+\\$))?" }
|
||||
|
||||
private string getLengthRegexp() {
|
||||
if isMicrosoft()
|
||||
if this.isMicrosoft()
|
||||
then result = "(?:hh?|ll?|L|q|j|z|t|w|I32|I64|I)?"
|
||||
else result = "(?:hh?|ll?|L|q|j|z|Z|t)?"
|
||||
}
|
||||
|
||||
private string getConvCharRegexp() {
|
||||
if isMicrosoft()
|
||||
if this.isMicrosoft()
|
||||
then result = "[aAcCdeEfFgGimnopsSuxXZ@]"
|
||||
else result = "[aAcCdeEfFgGimnopsSuxX@]"
|
||||
}
|
||||
@@ -732,16 +749,16 @@ class FormatLiteral extends Literal {
|
||||
* Gets the argument type required by the nth conversion specifier.
|
||||
*/
|
||||
Type getConversionType(int n) {
|
||||
result = getConversionType1(n) or
|
||||
result = getConversionType1b(n) or
|
||||
result = getConversionType2(n) or
|
||||
result = getConversionType3(n) or
|
||||
result = getConversionType4(n) or
|
||||
result = getConversionType6(n) or
|
||||
result = getConversionType7(n) or
|
||||
result = getConversionType8(n) or
|
||||
result = getConversionType9(n) or
|
||||
result = getConversionType10(n)
|
||||
result = this.getConversionType1(n) or
|
||||
result = this.getConversionType1b(n) or
|
||||
result = this.getConversionType2(n) or
|
||||
result = this.getConversionType3(n) or
|
||||
result = this.getConversionType4(n) or
|
||||
result = this.getConversionType6(n) or
|
||||
result = this.getConversionType7(n) or
|
||||
result = this.getConversionType8(n) or
|
||||
result = this.getConversionType9(n) or
|
||||
result = this.getConversionType10(n)
|
||||
}
|
||||
|
||||
private Type getConversionType1(int n) {
|
||||
@@ -771,15 +788,15 @@ class FormatLiteral extends Literal {
|
||||
or
|
||||
conv = ["c", "C"] and
|
||||
len = ["l", "w"] and
|
||||
result = getWideCharType()
|
||||
result = this.getWideCharType()
|
||||
or
|
||||
conv = "c" and
|
||||
(len != "l" and len != "w" and len != "h") and
|
||||
result = getDefaultCharType()
|
||||
result = this.getDefaultCharType()
|
||||
or
|
||||
conv = "C" and
|
||||
(len != "l" and len != "w" and len != "h") and
|
||||
result = getNonDefaultCharType()
|
||||
result = this.getNonDefaultCharType()
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -816,15 +833,15 @@ class FormatLiteral extends Literal {
|
||||
or
|
||||
conv = ["s", "S"] and
|
||||
len = ["l", "w"] and
|
||||
result.(PointerType).getBaseType() = getWideCharType()
|
||||
result.(PointerType).getBaseType() = this.getWideCharType()
|
||||
or
|
||||
conv = "s" and
|
||||
(len != "l" and len != "w" and len != "h") and
|
||||
result.(PointerType).getBaseType() = getDefaultCharType()
|
||||
result.(PointerType).getBaseType() = this.getDefaultCharType()
|
||||
or
|
||||
conv = "S" and
|
||||
(len != "l" and len != "w" and len != "h") and
|
||||
result.(PointerType).getBaseType() = getNonDefaultCharType()
|
||||
result.(PointerType).getBaseType() = this.getNonDefaultCharType()
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -879,19 +896,19 @@ class FormatLiteral extends Literal {
|
||||
exists(string len, string conv |
|
||||
this.parseConvSpec(n, _, _, _, _, _, len, conv) and
|
||||
(len != "l" and len != "w" and len != "h") and
|
||||
getUse().getTarget().(FormattingFunction).getFormatCharType().getSize() > 1 and // wide function
|
||||
this.getUse().getTarget().(FormattingFunction).getFormatCharType().getSize() > 1 and // wide function
|
||||
(
|
||||
conv = "c" and
|
||||
result = getNonDefaultCharType()
|
||||
result = this.getNonDefaultCharType()
|
||||
or
|
||||
conv = "C" and
|
||||
result = getDefaultCharType()
|
||||
result = this.getDefaultCharType()
|
||||
or
|
||||
conv = "s" and
|
||||
result.(PointerType).getBaseType() = getNonDefaultCharType()
|
||||
result.(PointerType).getBaseType() = this.getNonDefaultCharType()
|
||||
or
|
||||
conv = "S" and
|
||||
result.(PointerType).getBaseType() = getDefaultCharType()
|
||||
result.(PointerType).getBaseType() = this.getDefaultCharType()
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -924,9 +941,13 @@ class FormatLiteral extends Literal {
|
||||
* not account for positional arguments (`$`).
|
||||
*/
|
||||
int getFormatArgumentIndexFor(int n, int mode) {
|
||||
hasFormatArgumentIndexFor(n, mode) and
|
||||
this.hasFormatArgumentIndexFor(n, mode) and
|
||||
(3 * n) + mode =
|
||||
rank[result + 1](int n2, int mode2 | hasFormatArgumentIndexFor(n2, mode2) | (3 * n2) + mode2)
|
||||
rank[result + 1](int n2, int mode2 |
|
||||
this.hasFormatArgumentIndexFor(n2, mode2)
|
||||
|
|
||||
(3 * n2) + mode2
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -936,7 +957,7 @@ class FormatLiteral extends Literal {
|
||||
int getNumArgNeeded(int n) {
|
||||
exists(this.getConvSpecOffset(n)) and
|
||||
exists(this.getConversionChar(n)) and
|
||||
result = count(int mode | hasFormatArgumentIndexFor(n, mode))
|
||||
result = count(int mode | this.hasFormatArgumentIndexFor(n, mode))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -948,7 +969,7 @@ class FormatLiteral extends Literal {
|
||||
// At least one conversion specifier has a parameter field, in which case,
|
||||
// they all should have.
|
||||
result = max(string s | this.getParameterField(_) = s + "$" | s.toInt())
|
||||
else result = count(int n, int mode | hasFormatArgumentIndexFor(n, mode))
|
||||
else result = count(int n, int mode | this.hasFormatArgumentIndexFor(n, mode))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1038,10 +1059,10 @@ class FormatLiteral extends Literal {
|
||||
exists(int sizeBits |
|
||||
sizeBits =
|
||||
min(int bits |
|
||||
bits = getIntegralDisplayType(n).getSize() * 8
|
||||
bits = this.getIntegralDisplayType(n).getSize() * 8
|
||||
or
|
||||
exists(IntegralType t |
|
||||
t = getUse().getConversionArgument(n).getType().getUnderlyingType()
|
||||
t = this.getUse().getConversionArgument(n).getType().getUnderlyingType()
|
||||
|
|
||||
t.isSigned() and bits = t.getSize() * 8
|
||||
)
|
||||
@@ -1056,10 +1077,10 @@ class FormatLiteral extends Literal {
|
||||
exists(int sizeBits |
|
||||
sizeBits =
|
||||
min(int bits |
|
||||
bits = getIntegralDisplayType(n).getSize() * 8
|
||||
bits = this.getIntegralDisplayType(n).getSize() * 8
|
||||
or
|
||||
exists(IntegralType t |
|
||||
t = getUse().getConversionArgument(n).getType().getUnderlyingType()
|
||||
t = this.getUse().getConversionArgument(n).getType().getUnderlyingType()
|
||||
|
|
||||
t.isUnsigned() and bits = t.getSize() * 8
|
||||
)
|
||||
@@ -1074,26 +1095,26 @@ class FormatLiteral extends Literal {
|
||||
exists(int sizeBytes, int baseLen |
|
||||
sizeBytes =
|
||||
min(int bytes |
|
||||
bytes = getIntegralDisplayType(n).getSize()
|
||||
bytes = this.getIntegralDisplayType(n).getSize()
|
||||
or
|
||||
exists(IntegralType t |
|
||||
t = getUse().getConversionArgument(n).getType().getUnderlyingType()
|
||||
t = this.getUse().getConversionArgument(n).getType().getUnderlyingType()
|
||||
|
|
||||
t.isUnsigned() and bytes = t.getSize()
|
||||
)
|
||||
) and
|
||||
baseLen = sizeBytes * 2 and
|
||||
(
|
||||
if hasAlternateFlag(n) then len = 2 + baseLen else len = baseLen // "0x"
|
||||
if this.hasAlternateFlag(n) then len = 2 + baseLen else len = baseLen // "0x"
|
||||
)
|
||||
)
|
||||
or
|
||||
this.getConversionChar(n).toLowerCase() = "p" and
|
||||
exists(PointerType ptrType, int baseLen |
|
||||
ptrType = getFullyConverted().getType() and
|
||||
ptrType = this.getFullyConverted().getType() and
|
||||
baseLen = max(ptrType.getSize() * 2) and // e.g. "0x1234567812345678"; exact format is platform dependent
|
||||
(
|
||||
if hasAlternateFlag(n) then len = 2 + baseLen else len = baseLen // "0x"
|
||||
if this.hasAlternateFlag(n) then len = 2 + baseLen else len = baseLen // "0x"
|
||||
)
|
||||
)
|
||||
or
|
||||
@@ -1102,17 +1123,17 @@ class FormatLiteral extends Literal {
|
||||
exists(int sizeBits, int baseLen |
|
||||
sizeBits =
|
||||
min(int bits |
|
||||
bits = getIntegralDisplayType(n).getSize() * 8
|
||||
bits = this.getIntegralDisplayType(n).getSize() * 8
|
||||
or
|
||||
exists(IntegralType t |
|
||||
t = getUse().getConversionArgument(n).getType().getUnderlyingType()
|
||||
t = this.getUse().getConversionArgument(n).getType().getUnderlyingType()
|
||||
|
|
||||
t.isUnsigned() and bits = t.getSize() * 8
|
||||
)
|
||||
) and
|
||||
baseLen = (sizeBits / 3.0).ceil() and
|
||||
(
|
||||
if hasAlternateFlag(n) then len = 1 + baseLen else len = baseLen // "0"
|
||||
if this.hasAlternateFlag(n) then len = 1 + baseLen else len = baseLen // "0"
|
||||
)
|
||||
)
|
||||
or
|
||||
@@ -1135,8 +1156,8 @@ class FormatLiteral extends Literal {
|
||||
*/
|
||||
int getMaxConvertedLengthLimited(int n) {
|
||||
if this.getConversionChar(n).toLowerCase() = "f"
|
||||
then result = getMaxConvertedLength(n).minimum(8)
|
||||
else result = getMaxConvertedLength(n)
|
||||
then result = this.getMaxConvertedLength(n).minimum(8)
|
||||
else result = this.getMaxConvertedLength(n)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -24,7 +24,7 @@ abstract class ScanfFunction extends Function {
|
||||
* Holds if the default meaning of `%s` is a `wchar_t*` string
|
||||
* (rather than a `char*`).
|
||||
*/
|
||||
predicate isWideCharDefault() { exists(getName().indexOf("wscanf")) }
|
||||
predicate isWideCharDefault() { exists(this.getName().indexOf("wscanf")) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -34,10 +34,10 @@ class Scanf extends ScanfFunction {
|
||||
Scanf() {
|
||||
this instanceof TopLevelFunction and
|
||||
(
|
||||
hasGlobalOrStdOrBslName("scanf") or // scanf(format, args...)
|
||||
hasGlobalOrStdOrBslName("wscanf") or // wscanf(format, args...)
|
||||
hasGlobalName("_scanf_l") or // _scanf_l(format, locale, args...)
|
||||
hasGlobalName("_wscanf_l") // _wscanf_l(format, locale, args...)
|
||||
this.hasGlobalOrStdOrBslName("scanf") or // scanf(format, args...)
|
||||
this.hasGlobalOrStdOrBslName("wscanf") or // wscanf(format, args...)
|
||||
this.hasGlobalName("_scanf_l") or // _scanf_l(format, locale, args...)
|
||||
this.hasGlobalName("_wscanf_l") // _wscanf_l(format, locale, args...)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -53,10 +53,10 @@ class Fscanf extends ScanfFunction {
|
||||
Fscanf() {
|
||||
this instanceof TopLevelFunction and
|
||||
(
|
||||
hasGlobalOrStdOrBslName("fscanf") or // fscanf(src_stream, format, args...)
|
||||
hasGlobalOrStdOrBslName("fwscanf") or // fwscanf(src_stream, format, args...)
|
||||
hasGlobalName("_fscanf_l") or // _fscanf_l(src_stream, format, locale, args...)
|
||||
hasGlobalName("_fwscanf_l") // _fwscanf_l(src_stream, format, locale, args...)
|
||||
this.hasGlobalOrStdOrBslName("fscanf") or // fscanf(src_stream, format, args...)
|
||||
this.hasGlobalOrStdOrBslName("fwscanf") or // fwscanf(src_stream, format, args...)
|
||||
this.hasGlobalName("_fscanf_l") or // _fscanf_l(src_stream, format, locale, args...)
|
||||
this.hasGlobalName("_fwscanf_l") // _fwscanf_l(src_stream, format, locale, args...)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -72,10 +72,10 @@ class Sscanf extends ScanfFunction {
|
||||
Sscanf() {
|
||||
this instanceof TopLevelFunction and
|
||||
(
|
||||
hasGlobalOrStdOrBslName("sscanf") or // sscanf(src_stream, format, args...)
|
||||
hasGlobalOrStdOrBslName("swscanf") or // swscanf(src, format, args...)
|
||||
hasGlobalName("_sscanf_l") or // _sscanf_l(src, format, locale, args...)
|
||||
hasGlobalName("_swscanf_l") // _swscanf_l(src, format, locale, args...)
|
||||
this.hasGlobalOrStdOrBslName("sscanf") or // sscanf(src_stream, format, args...)
|
||||
this.hasGlobalOrStdOrBslName("swscanf") or // swscanf(src, format, args...)
|
||||
this.hasGlobalName("_sscanf_l") or // _sscanf_l(src, format, locale, args...)
|
||||
this.hasGlobalName("_swscanf_l") // _swscanf_l(src, format, locale, args...)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -91,10 +91,10 @@ class Snscanf extends ScanfFunction {
|
||||
Snscanf() {
|
||||
this instanceof TopLevelFunction and
|
||||
(
|
||||
hasGlobalName("_snscanf") or // _snscanf(src, max_amount, format, args...)
|
||||
hasGlobalName("_snwscanf") or // _snwscanf(src, max_amount, format, args...)
|
||||
hasGlobalName("_snscanf_l") or // _snscanf_l(src, max_amount, format, locale, args...)
|
||||
hasGlobalName("_snwscanf_l") // _snwscanf_l(src, max_amount, format, locale, args...)
|
||||
this.hasGlobalName("_snscanf") or // _snscanf(src, max_amount, format, args...)
|
||||
this.hasGlobalName("_snwscanf") or // _snwscanf(src, max_amount, format, args...)
|
||||
this.hasGlobalName("_snscanf_l") or // _snscanf_l(src, max_amount, format, locale, args...)
|
||||
this.hasGlobalName("_snwscanf_l") // _snwscanf_l(src, max_amount, format, locale, args...)
|
||||
// note that the max_amount is not a limit on the output length, it's an input length
|
||||
// limit used with non null-terminated strings.
|
||||
)
|
||||
@@ -120,18 +120,18 @@ class ScanfFunctionCall extends FunctionCall {
|
||||
/**
|
||||
* Gets the `scanf`-like function that is called.
|
||||
*/
|
||||
ScanfFunction getScanfFunction() { result = getTarget() }
|
||||
ScanfFunction getScanfFunction() { result = this.getTarget() }
|
||||
|
||||
/**
|
||||
* Gets the position at which the input string or stream parameter occurs,
|
||||
* if this function call does not read from standard input.
|
||||
*/
|
||||
int getInputParameterIndex() { result = getScanfFunction().getInputParameterIndex() }
|
||||
int getInputParameterIndex() { result = this.getScanfFunction().getInputParameterIndex() }
|
||||
|
||||
/**
|
||||
* Gets the position at which the format parameter occurs.
|
||||
*/
|
||||
int getFormatParameterIndex() { result = getScanfFunction().getFormatParameterIndex() }
|
||||
int getFormatParameterIndex() { result = this.getScanfFunction().getFormatParameterIndex() }
|
||||
|
||||
/**
|
||||
* Gets the format expression used in this call.
|
||||
@@ -142,7 +142,7 @@ class ScanfFunctionCall extends FunctionCall {
|
||||
* Holds if the default meaning of `%s` is a `wchar_t*` string
|
||||
* (rather than a `char*`).
|
||||
*/
|
||||
predicate isWideCharDefault() { getScanfFunction().isWideCharDefault() }
|
||||
predicate isWideCharDefault() { this.getScanfFunction().isWideCharDefault() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -158,7 +158,7 @@ class ScanfFormatLiteral extends Expr {
|
||||
ScanfFunctionCall getUse() { result.getFormat() = this }
|
||||
|
||||
/** Holds if the default meaning of `%s` is a `wchar_t*` (rather than a `char*`). */
|
||||
predicate isWideCharDefault() { getUse().getTarget().(ScanfFunction).isWideCharDefault() }
|
||||
predicate isWideCharDefault() { this.getUse().getTarget().(ScanfFunction).isWideCharDefault() }
|
||||
|
||||
/**
|
||||
* Gets the format string itself, transformed as follows:
|
||||
|
||||
@@ -40,8 +40,8 @@ abstract class MutexType extends Type {
|
||||
* Gets a call that locks or tries to lock any mutex of this type.
|
||||
*/
|
||||
FunctionCall getLockAccess() {
|
||||
result = getMustlockAccess() or
|
||||
result = getTrylockAccess()
|
||||
result = this.getMustlockAccess() or
|
||||
result = this.getTrylockAccess()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -63,22 +63,22 @@ abstract class MutexType extends Type {
|
||||
/**
|
||||
* DEPRECATED: use mustlockAccess(fc, arg) instead.
|
||||
*/
|
||||
deprecated Function getMustlockFunction() { result = getMustlockAccess().getTarget() }
|
||||
deprecated Function getMustlockFunction() { result = this.getMustlockAccess().getTarget() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: use trylockAccess(fc, arg) instead.
|
||||
*/
|
||||
deprecated Function getTrylockFunction() { result = getTrylockAccess().getTarget() }
|
||||
deprecated Function getTrylockFunction() { result = this.getTrylockAccess().getTarget() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: use lockAccess(fc, arg) instead.
|
||||
*/
|
||||
deprecated Function getLockFunction() { result = getLockAccess().getTarget() }
|
||||
deprecated Function getLockFunction() { result = this.getLockAccess().getTarget() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: use unlockAccess(fc, arg) instead.
|
||||
*/
|
||||
deprecated Function getUnlockFunction() { result = getUnlockAccess().getTarget() }
|
||||
deprecated Function getUnlockFunction() { result = this.getUnlockAccess().getTarget() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -155,17 +155,17 @@ class DefaultMutexType extends MutexType {
|
||||
|
||||
override predicate mustlockAccess(FunctionCall fc, Expr arg) {
|
||||
fc.getTarget() = mustlockCandidate() and
|
||||
lockArgType(fc, arg)
|
||||
this.lockArgType(fc, arg)
|
||||
}
|
||||
|
||||
override predicate trylockAccess(FunctionCall fc, Expr arg) {
|
||||
fc.getTarget() = trylockCandidate() and
|
||||
lockArgType(fc, arg)
|
||||
this.lockArgType(fc, arg)
|
||||
}
|
||||
|
||||
override predicate unlockAccess(FunctionCall fc, Expr arg) {
|
||||
fc.getTarget() = unlockCandidate() and
|
||||
lockArgType(fc, arg)
|
||||
this.lockArgType(fc, arg)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -194,14 +194,14 @@ class BasicBlock extends ControlFlowNodeBase {
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
|
||||
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||
*
|
||||
* Yields no result if this basic block spans multiple source files.
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
hasLocationInfoInternal(filepath, startline, startcolumn, filepath, endline, endcolumn)
|
||||
this.hasLocationInfoInternal(filepath, startline, startcolumn, filepath, endline, endcolumn)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
@@ -276,7 +276,7 @@ class EntryBasicBlock extends BasicBlock {
|
||||
*/
|
||||
class ExitBasicBlock extends BasicBlock {
|
||||
ExitBasicBlock() {
|
||||
getEnd() instanceof Function or
|
||||
aborting(getEnd())
|
||||
this.getEnd() instanceof Function or
|
||||
aborting(this.getEnd())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ class ControlFlowNode extends Locatable, ControlFlowNodeBase {
|
||||
*/
|
||||
ControlFlowNode getATrueSuccessor() {
|
||||
qlCFGTrueSuccessor(this, result) and
|
||||
result = getASuccessor()
|
||||
result = this.getASuccessor()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -75,7 +75,7 @@ class ControlFlowNode extends Locatable, ControlFlowNodeBase {
|
||||
*/
|
||||
ControlFlowNode getAFalseSuccessor() {
|
||||
qlCFGFalseSuccessor(this, result) and
|
||||
result = getASuccessor()
|
||||
result = this.getASuccessor()
|
||||
}
|
||||
|
||||
/** Gets the `BasicBlock` containing this control-flow node. */
|
||||
|
||||
@@ -121,7 +121,7 @@ private class GuardConditionFromBinaryLogicalOperator extends GuardCondition {
|
||||
|
||||
override predicate ensuresLt(Expr left, Expr right, int k, BasicBlock block, boolean isLessThan) {
|
||||
exists(boolean testIsTrue |
|
||||
comparesLt(left, right, k, isLessThan, testIsTrue) and this.controls(block, testIsTrue)
|
||||
this.comparesLt(left, right, k, isLessThan, testIsTrue) and this.controls(block, testIsTrue)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -135,7 +135,7 @@ private class GuardConditionFromBinaryLogicalOperator extends GuardCondition {
|
||||
|
||||
override predicate ensuresEq(Expr left, Expr right, int k, BasicBlock block, boolean areEqual) {
|
||||
exists(boolean testIsTrue |
|
||||
comparesEq(left, right, k, areEqual, testIsTrue) and this.controls(block, testIsTrue)
|
||||
this.comparesEq(left, right, k, areEqual, testIsTrue) and this.controls(block, testIsTrue)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -147,27 +147,29 @@ private class GuardConditionFromBinaryLogicalOperator extends GuardCondition {
|
||||
private class GuardConditionFromShortCircuitNot extends GuardCondition, NotExpr {
|
||||
GuardConditionFromShortCircuitNot() {
|
||||
not exists(Instruction inst | this.getFullyConverted() = inst.getAST()) and
|
||||
exists(IRGuardCondition ir | getOperand() = ir.getAST())
|
||||
exists(IRGuardCondition ir | this.getOperand() = ir.getAST())
|
||||
}
|
||||
|
||||
override predicate controls(BasicBlock controlled, boolean testIsTrue) {
|
||||
getOperand().(GuardCondition).controls(controlled, testIsTrue.booleanNot())
|
||||
this.getOperand().(GuardCondition).controls(controlled, testIsTrue.booleanNot())
|
||||
}
|
||||
|
||||
override predicate comparesLt(Expr left, Expr right, int k, boolean isLessThan, boolean testIsTrue) {
|
||||
getOperand().(GuardCondition).comparesLt(left, right, k, isLessThan, testIsTrue.booleanNot())
|
||||
this.getOperand()
|
||||
.(GuardCondition)
|
||||
.comparesLt(left, right, k, isLessThan, testIsTrue.booleanNot())
|
||||
}
|
||||
|
||||
override predicate ensuresLt(Expr left, Expr right, int k, BasicBlock block, boolean isLessThan) {
|
||||
getOperand().(GuardCondition).ensuresLt(left, right, k, block, isLessThan.booleanNot())
|
||||
this.getOperand().(GuardCondition).ensuresLt(left, right, k, block, isLessThan.booleanNot())
|
||||
}
|
||||
|
||||
override predicate comparesEq(Expr left, Expr right, int k, boolean areEqual, boolean testIsTrue) {
|
||||
getOperand().(GuardCondition).comparesEq(left, right, k, areEqual, testIsTrue.booleanNot())
|
||||
this.getOperand().(GuardCondition).comparesEq(left, right, k, areEqual, testIsTrue.booleanNot())
|
||||
}
|
||||
|
||||
override predicate ensuresEq(Expr left, Expr right, int k, BasicBlock block, boolean areEqual) {
|
||||
getOperand().(GuardCondition).ensuresEq(left, right, k, block, areEqual.booleanNot())
|
||||
this.getOperand().(GuardCondition).ensuresEq(left, right, k, block, areEqual.booleanNot())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -303,9 +305,9 @@ class IRGuardCondition extends Instruction {
|
||||
cached
|
||||
predicate controlsEdge(IRBlock pred, IRBlock succ, boolean testIsTrue) {
|
||||
pred.getASuccessor() = succ and
|
||||
controls(pred, testIsTrue)
|
||||
this.controls(pred, testIsTrue)
|
||||
or
|
||||
succ = getBranchSuccessor(testIsTrue) and
|
||||
succ = this.getBranchSuccessor(testIsTrue) and
|
||||
branch.getCondition() = this and
|
||||
branch.getBlock() = pred
|
||||
}
|
||||
|
||||
@@ -73,19 +73,19 @@ abstract deprecated class LocalScopeVariableReachability extends string {
|
||||
*/
|
||||
|
||||
exists(BasicBlock bb, int i |
|
||||
isSource(source, v) and
|
||||
this.isSource(source, v) and
|
||||
bb.getNode(i) = source and
|
||||
not bb.isUnreachable()
|
||||
|
|
||||
exists(int j |
|
||||
j > i and
|
||||
sink = bb.getNode(j) and
|
||||
isSink(sink, v) and
|
||||
not exists(int k | isBarrier(bb.getNode(k), v) | k in [i + 1 .. j - 1])
|
||||
this.isSink(sink, v) and
|
||||
not exists(int k | this.isBarrier(bb.getNode(k), v) | k in [i + 1 .. j - 1])
|
||||
)
|
||||
or
|
||||
not exists(int k | isBarrier(bb.getNode(k), v) | k > i) and
|
||||
bbSuccessorEntryReaches(bb, v, sink, _)
|
||||
not exists(int k | this.isBarrier(bb.getNode(k), v) | k > i) and
|
||||
this.bbSuccessorEntryReaches(bb, v, sink, _)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -97,11 +97,11 @@ abstract deprecated class LocalScopeVariableReachability extends string {
|
||||
bbSuccessorEntryReachesLoopInvariant(bb, succ, skipsFirstLoopAlwaysTrueUponEntry,
|
||||
succSkipsFirstLoopAlwaysTrueUponEntry)
|
||||
|
|
||||
bbEntryReachesLocally(succ, v, node) and
|
||||
this.bbEntryReachesLocally(succ, v, node) and
|
||||
succSkipsFirstLoopAlwaysTrueUponEntry = false
|
||||
or
|
||||
not isBarrier(succ.getNode(_), v) and
|
||||
bbSuccessorEntryReaches(succ, v, node, succSkipsFirstLoopAlwaysTrueUponEntry)
|
||||
not this.isBarrier(succ.getNode(_), v) and
|
||||
this.bbSuccessorEntryReaches(succ, v, node, succSkipsFirstLoopAlwaysTrueUponEntry)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -110,7 +110,7 @@ abstract deprecated class LocalScopeVariableReachability extends string {
|
||||
) {
|
||||
exists(int n |
|
||||
node = bb.getNode(n) and
|
||||
isSink(node, v)
|
||||
this.isSink(node, v)
|
||||
|
|
||||
not exists(this.firstBarrierIndexIn(bb, v))
|
||||
or
|
||||
@@ -119,7 +119,7 @@ abstract deprecated class LocalScopeVariableReachability extends string {
|
||||
}
|
||||
|
||||
private int firstBarrierIndexIn(BasicBlock bb, SemanticStackVariable v) {
|
||||
result = min(int m | isBarrier(bb.getNode(m), v))
|
||||
result = min(int m | this.isBarrier(bb.getNode(m), v))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -271,7 +271,7 @@ abstract deprecated class LocalScopeVariableReachabilityWithReassignment extends
|
||||
* accounts for loops where the condition is provably true upon entry.
|
||||
*/
|
||||
override predicate reaches(ControlFlowNode source, SemanticStackVariable v, ControlFlowNode sink) {
|
||||
reachesTo(source, v, sink, _)
|
||||
this.reachesTo(source, v, sink, _)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -281,21 +281,21 @@ abstract deprecated class LocalScopeVariableReachabilityWithReassignment extends
|
||||
ControlFlowNode source, SemanticStackVariable v, ControlFlowNode sink, SemanticStackVariable v0
|
||||
) {
|
||||
exists(ControlFlowNode def |
|
||||
actualSourceReaches(source, v, def, v0) and
|
||||
this.actualSourceReaches(source, v, def, v0) and
|
||||
LocalScopeVariableReachability.super.reaches(def, v0, sink) and
|
||||
isSinkActual(sink, v0)
|
||||
this.isSinkActual(sink, v0)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate actualSourceReaches(
|
||||
ControlFlowNode source, SemanticStackVariable v, ControlFlowNode def, SemanticStackVariable v0
|
||||
) {
|
||||
isSourceActual(source, v) and def = source and v0 = v
|
||||
this.isSourceActual(source, v) and def = source and v0 = v
|
||||
or
|
||||
exists(ControlFlowNode source1, SemanticStackVariable v1 |
|
||||
actualSourceReaches(source, v, source1, v1)
|
||||
this.actualSourceReaches(source, v, source1, v1)
|
||||
|
|
||||
reassignment(source1, v1, def, v0)
|
||||
this.reassignment(source1, v1, def, v0)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -307,14 +307,14 @@ abstract deprecated class LocalScopeVariableReachabilityWithReassignment extends
|
||||
}
|
||||
|
||||
final override predicate isSource(ControlFlowNode node, LocalScopeVariable v) {
|
||||
isSourceActual(node, v)
|
||||
this.isSourceActual(node, v)
|
||||
or
|
||||
// Reassignment generates a new (non-actual) source
|
||||
reassignment(_, _, node, v)
|
||||
this.reassignment(_, _, node, v)
|
||||
}
|
||||
|
||||
final override predicate isSink(ControlFlowNode node, LocalScopeVariable v) {
|
||||
isSinkActual(node, v)
|
||||
this.isSinkActual(node, v)
|
||||
or
|
||||
// Reassignment generates a new (non-actual) sink
|
||||
exprDefinition(_, node, v.getAnAccess())
|
||||
@@ -347,21 +347,21 @@ abstract deprecated class LocalScopeVariableReachabilityExt extends string {
|
||||
/** See `LocalScopeVariableReachability.reaches`. */
|
||||
predicate reaches(ControlFlowNode source, SemanticStackVariable v, ControlFlowNode sink) {
|
||||
exists(BasicBlock bb, int i |
|
||||
isSource(source, v) and
|
||||
this.isSource(source, v) and
|
||||
bb.getNode(i) = source and
|
||||
not bb.isUnreachable()
|
||||
|
|
||||
exists(int j |
|
||||
j > i and
|
||||
sink = bb.getNode(j) and
|
||||
isSink(sink, v) and
|
||||
not exists(int k | isBarrier(source, bb.getNode(k), bb.getNode(k + 1), v) |
|
||||
this.isSink(sink, v) and
|
||||
not exists(int k | this.isBarrier(source, bb.getNode(k), bb.getNode(k + 1), v) |
|
||||
k in [i .. j - 1]
|
||||
)
|
||||
)
|
||||
or
|
||||
not exists(int k | isBarrier(source, bb.getNode(k), bb.getNode(k + 1), v) | k >= i) and
|
||||
bbSuccessorEntryReaches(source, bb, v, sink, _)
|
||||
not exists(int k | this.isBarrier(source, bb.getNode(k), bb.getNode(k + 1), v) | k >= i) and
|
||||
this.bbSuccessorEntryReaches(source, bb, v, sink, _)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -372,22 +372,22 @@ abstract deprecated class LocalScopeVariableReachabilityExt extends string {
|
||||
exists(BasicBlock succ, boolean succSkipsFirstLoopAlwaysTrueUponEntry |
|
||||
bbSuccessorEntryReachesLoopInvariant(bb, succ, skipsFirstLoopAlwaysTrueUponEntry,
|
||||
succSkipsFirstLoopAlwaysTrueUponEntry) and
|
||||
not isBarrier(source, bb.getEnd(), succ.getStart(), v)
|
||||
not this.isBarrier(source, bb.getEnd(), succ.getStart(), v)
|
||||
|
|
||||
bbEntryReachesLocally(source, succ, v, node) and
|
||||
this.bbEntryReachesLocally(source, succ, v, node) and
|
||||
succSkipsFirstLoopAlwaysTrueUponEntry = false
|
||||
or
|
||||
not exists(int k | isBarrier(source, succ.getNode(k), succ.getNode(k + 1), v)) and
|
||||
bbSuccessorEntryReaches(source, succ, v, node, succSkipsFirstLoopAlwaysTrueUponEntry)
|
||||
not exists(int k | this.isBarrier(source, succ.getNode(k), succ.getNode(k + 1), v)) and
|
||||
this.bbSuccessorEntryReaches(source, succ, v, node, succSkipsFirstLoopAlwaysTrueUponEntry)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate bbEntryReachesLocally(
|
||||
ControlFlowNode source, BasicBlock bb, SemanticStackVariable v, ControlFlowNode node
|
||||
) {
|
||||
isSource(source, v) and
|
||||
exists(int n | node = bb.getNode(n) and isSink(node, v) |
|
||||
not exists(int m | m < n | isBarrier(source, bb.getNode(m), bb.getNode(m + 1), v))
|
||||
this.isSource(source, v) and
|
||||
exists(int n | node = bb.getNode(n) and this.isSink(node, v) |
|
||||
not exists(int m | m < n | this.isBarrier(source, bb.getNode(m), bb.getNode(m + 1), v))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ class SsaDefinition extends ControlFlowNodeBase {
|
||||
ControlFlowNode getDefinition() { result = this }
|
||||
|
||||
/** Gets the `BasicBlock` containing this definition. */
|
||||
BasicBlock getBasicBlock() { result.contains(getDefinition()) }
|
||||
BasicBlock getBasicBlock() { result.contains(this.getDefinition()) }
|
||||
|
||||
/** Holds if this definition is a phi node for variable `v`. */
|
||||
predicate isPhiNode(StackVariable v) { exists(StandardSSA x | x.phi_node(v, this.(BasicBlock))) }
|
||||
|
||||
@@ -72,19 +72,19 @@ abstract class StackVariableReachability extends string {
|
||||
*/
|
||||
|
||||
exists(BasicBlock bb, int i |
|
||||
isSource(source, v) and
|
||||
this.isSource(source, v) and
|
||||
bb.getNode(i) = source and
|
||||
not bb.isUnreachable()
|
||||
|
|
||||
exists(int j |
|
||||
j > i and
|
||||
sink = bb.getNode(j) and
|
||||
isSink(sink, v) and
|
||||
not exists(int k | isBarrier(bb.getNode(k), v) | k in [i + 1 .. j - 1])
|
||||
this.isSink(sink, v) and
|
||||
not exists(int k | this.isBarrier(bb.getNode(k), v) | k in [i + 1 .. j - 1])
|
||||
)
|
||||
or
|
||||
not exists(int k | isBarrier(bb.getNode(k), v) | k > i) and
|
||||
bbSuccessorEntryReaches(bb, v, sink, _)
|
||||
not exists(int k | this.isBarrier(bb.getNode(k), v) | k > i) and
|
||||
this.bbSuccessorEntryReaches(bb, v, sink, _)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -96,11 +96,11 @@ abstract class StackVariableReachability extends string {
|
||||
bbSuccessorEntryReachesLoopInvariant(bb, succ, skipsFirstLoopAlwaysTrueUponEntry,
|
||||
succSkipsFirstLoopAlwaysTrueUponEntry)
|
||||
|
|
||||
bbEntryReachesLocally(succ, v, node) and
|
||||
this.bbEntryReachesLocally(succ, v, node) and
|
||||
succSkipsFirstLoopAlwaysTrueUponEntry = false
|
||||
or
|
||||
not isBarrier(succ.getNode(_), v) and
|
||||
bbSuccessorEntryReaches(succ, v, node, succSkipsFirstLoopAlwaysTrueUponEntry)
|
||||
not this.isBarrier(succ.getNode(_), v) and
|
||||
this.bbSuccessorEntryReaches(succ, v, node, succSkipsFirstLoopAlwaysTrueUponEntry)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -109,7 +109,7 @@ abstract class StackVariableReachability extends string {
|
||||
) {
|
||||
exists(int n |
|
||||
node = bb.getNode(n) and
|
||||
isSink(node, v)
|
||||
this.isSink(node, v)
|
||||
|
|
||||
not exists(this.firstBarrierIndexIn(bb, v))
|
||||
or
|
||||
@@ -118,7 +118,7 @@ abstract class StackVariableReachability extends string {
|
||||
}
|
||||
|
||||
private int firstBarrierIndexIn(BasicBlock bb, SemanticStackVariable v) {
|
||||
result = min(int m | isBarrier(bb.getNode(m), v))
|
||||
result = min(int m | this.isBarrier(bb.getNode(m), v))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -268,7 +268,7 @@ abstract class StackVariableReachabilityWithReassignment extends StackVariableRe
|
||||
* accounts for loops where the condition is provably true upon entry.
|
||||
*/
|
||||
override predicate reaches(ControlFlowNode source, SemanticStackVariable v, ControlFlowNode sink) {
|
||||
reachesTo(source, v, sink, _)
|
||||
this.reachesTo(source, v, sink, _)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -278,21 +278,21 @@ abstract class StackVariableReachabilityWithReassignment extends StackVariableRe
|
||||
ControlFlowNode source, SemanticStackVariable v, ControlFlowNode sink, SemanticStackVariable v0
|
||||
) {
|
||||
exists(ControlFlowNode def |
|
||||
actualSourceReaches(source, v, def, v0) and
|
||||
this.actualSourceReaches(source, v, def, v0) and
|
||||
StackVariableReachability.super.reaches(def, v0, sink) and
|
||||
isSinkActual(sink, v0)
|
||||
this.isSinkActual(sink, v0)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate actualSourceReaches(
|
||||
ControlFlowNode source, SemanticStackVariable v, ControlFlowNode def, SemanticStackVariable v0
|
||||
) {
|
||||
isSourceActual(source, v) and def = source and v0 = v
|
||||
this.isSourceActual(source, v) and def = source and v0 = v
|
||||
or
|
||||
exists(ControlFlowNode source1, SemanticStackVariable v1 |
|
||||
actualSourceReaches(source, v, source1, v1)
|
||||
this.actualSourceReaches(source, v, source1, v1)
|
||||
|
|
||||
reassignment(source1, v1, def, v0)
|
||||
this.reassignment(source1, v1, def, v0)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -304,14 +304,14 @@ abstract class StackVariableReachabilityWithReassignment extends StackVariableRe
|
||||
}
|
||||
|
||||
final override predicate isSource(ControlFlowNode node, StackVariable v) {
|
||||
isSourceActual(node, v)
|
||||
this.isSourceActual(node, v)
|
||||
or
|
||||
// Reassignment generates a new (non-actual) source
|
||||
reassignment(_, _, node, v)
|
||||
this.reassignment(_, _, node, v)
|
||||
}
|
||||
|
||||
final override predicate isSink(ControlFlowNode node, StackVariable v) {
|
||||
isSinkActual(node, v)
|
||||
this.isSinkActual(node, v)
|
||||
or
|
||||
// Reassignment generates a new (non-actual) sink
|
||||
exprDefinition(_, node, v.getAnAccess())
|
||||
@@ -342,21 +342,21 @@ abstract class StackVariableReachabilityExt extends string {
|
||||
/** See `StackVariableReachability.reaches`. */
|
||||
predicate reaches(ControlFlowNode source, SemanticStackVariable v, ControlFlowNode sink) {
|
||||
exists(BasicBlock bb, int i |
|
||||
isSource(source, v) and
|
||||
this.isSource(source, v) and
|
||||
bb.getNode(i) = source and
|
||||
not bb.isUnreachable()
|
||||
|
|
||||
exists(int j |
|
||||
j > i and
|
||||
sink = bb.getNode(j) and
|
||||
isSink(sink, v) and
|
||||
not exists(int k | isBarrier(source, bb.getNode(k), bb.getNode(k + 1), v) |
|
||||
this.isSink(sink, v) and
|
||||
not exists(int k | this.isBarrier(source, bb.getNode(k), bb.getNode(k + 1), v) |
|
||||
k in [i .. j - 1]
|
||||
)
|
||||
)
|
||||
or
|
||||
not exists(int k | isBarrier(source, bb.getNode(k), bb.getNode(k + 1), v) | k >= i) and
|
||||
bbSuccessorEntryReaches(source, bb, v, sink, _)
|
||||
not exists(int k | this.isBarrier(source, bb.getNode(k), bb.getNode(k + 1), v) | k >= i) and
|
||||
this.bbSuccessorEntryReaches(source, bb, v, sink, _)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -367,22 +367,22 @@ abstract class StackVariableReachabilityExt extends string {
|
||||
exists(BasicBlock succ, boolean succSkipsFirstLoopAlwaysTrueUponEntry |
|
||||
bbSuccessorEntryReachesLoopInvariant(bb, succ, skipsFirstLoopAlwaysTrueUponEntry,
|
||||
succSkipsFirstLoopAlwaysTrueUponEntry) and
|
||||
not isBarrier(source, bb.getEnd(), succ.getStart(), v)
|
||||
not this.isBarrier(source, bb.getEnd(), succ.getStart(), v)
|
||||
|
|
||||
bbEntryReachesLocally(source, succ, v, node) and
|
||||
this.bbEntryReachesLocally(source, succ, v, node) and
|
||||
succSkipsFirstLoopAlwaysTrueUponEntry = false
|
||||
or
|
||||
not exists(int k | isBarrier(source, succ.getNode(k), succ.getNode(k + 1), v)) and
|
||||
bbSuccessorEntryReaches(source, succ, v, node, succSkipsFirstLoopAlwaysTrueUponEntry)
|
||||
not exists(int k | this.isBarrier(source, succ.getNode(k), succ.getNode(k + 1), v)) and
|
||||
this.bbSuccessorEntryReaches(source, succ, v, node, succSkipsFirstLoopAlwaysTrueUponEntry)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate bbEntryReachesLocally(
|
||||
ControlFlowNode source, BasicBlock bb, SemanticStackVariable v, ControlFlowNode node
|
||||
) {
|
||||
isSource(source, v) and
|
||||
exists(int n | node = bb.getNode(n) and isSink(node, v) |
|
||||
not exists(int m | m < n | isBarrier(source, bb.getNode(m), bb.getNode(m + 1), v))
|
||||
this.isSource(source, v) and
|
||||
exists(int n | node = bb.getNode(n) and this.isSink(node, v) |
|
||||
not exists(int m | m < n | this.isBarrier(source, bb.getNode(m), bb.getNode(m + 1), v))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ class SubBasicBlock extends ControlFlowNodeBase {
|
||||
* returns a 0-based position, while `getRankInBasicBlock` returns a 1-based
|
||||
* position.
|
||||
*/
|
||||
deprecated int getPosInBasicBlock(BasicBlock bb) { result = getRankInBasicBlock(bb) - 1 }
|
||||
deprecated int getPosInBasicBlock(BasicBlock bb) { result = this.getRankInBasicBlock(bb) - 1 }
|
||||
|
||||
pragma[noinline]
|
||||
private int getIndexInBasicBlock(BasicBlock bb) { this = bb.getNode(result) }
|
||||
@@ -102,7 +102,7 @@ class SubBasicBlock extends ControlFlowNodeBase {
|
||||
exists(BasicBlock bb |
|
||||
exists(int outerIndex |
|
||||
result = bb.getNode(outerIndex) and
|
||||
index = outerToInnerIndex(bb, outerIndex)
|
||||
index = this.outerToInnerIndex(bb, outerIndex)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -344,14 +344,13 @@ private int convertIntToType(int val, IntegralType t) {
|
||||
then if val = 0 then result = 0 else result = 1
|
||||
else
|
||||
if t.isUnsigned()
|
||||
then if val >= 0 and val.bitShiftRight(t.getSize() * 8) = 0 then result = val else none()
|
||||
then val >= 0 and val.bitShiftRight(t.getSize() * 8) = 0 and result = val
|
||||
else
|
||||
if val >= 0 and val.bitShiftRight(t.getSize() * 8 - 1) = 0
|
||||
then result = val
|
||||
else
|
||||
if (-(val + 1)).bitShiftRight(t.getSize() * 8 - 1) = 0
|
||||
then result = val
|
||||
else none()
|
||||
else (
|
||||
(-(val + 1)).bitShiftRight(t.getSize() * 8 - 1) = 0 and result = val
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -386,7 +385,7 @@ library class ExprEvaluator extends int {
|
||||
abstract predicate interesting(Expr e);
|
||||
|
||||
/** Gets the value of (interesting) expression `e`, if any. */
|
||||
int getValue(Expr e) { result = getValueInternal(e, e) }
|
||||
int getValue(Expr e) { result = this.getValueInternal(e, e) }
|
||||
|
||||
/**
|
||||
* When evaluating a syntactic subexpression of `e`, we may
|
||||
@@ -426,9 +425,9 @@ library class ExprEvaluator extends int {
|
||||
* calculates the values bottom-up.
|
||||
*/
|
||||
predicate interestingInternal(Expr e, Expr req, boolean sub) {
|
||||
interesting(e) and req = e and sub = true
|
||||
this.interesting(e) and req = e and sub = true
|
||||
or
|
||||
exists(Expr mid | interestingInternal(e, mid, sub) |
|
||||
exists(Expr mid | this.interestingInternal(e, mid, sub) |
|
||||
req = mid.(NotExpr).getOperand() or
|
||||
req = mid.(BinaryLogicalOperation).getAnOperand() or
|
||||
req = mid.(RelationalOperation).getAnOperand() or
|
||||
@@ -443,36 +442,36 @@ library class ExprEvaluator extends int {
|
||||
)
|
||||
or
|
||||
exists(VariableAccess va, Variable v, boolean sub1 |
|
||||
interestingVariableAccess(e, va, v, sub1) and
|
||||
this.interestingVariableAccess(e, va, v, sub1) and
|
||||
req = v.getAnAssignedValue() and
|
||||
(sub1 = true implies not ignoreVariableAssignment(e, v, req)) and
|
||||
(sub1 = true implies not this.ignoreVariableAssignment(e, v, req)) and
|
||||
sub = false
|
||||
)
|
||||
or
|
||||
exists(Function f |
|
||||
interestingFunction(e, f) and
|
||||
this.interestingFunction(e, f) and
|
||||
returnStmt(f, req) and
|
||||
sub = false
|
||||
)
|
||||
}
|
||||
|
||||
private predicate interestingVariableAccess(Expr e, VariableAccess va, Variable v, boolean sub) {
|
||||
interestingInternal(e, va, sub) and
|
||||
this.interestingInternal(e, va, sub) and
|
||||
v = getVariableTarget(va) and
|
||||
(
|
||||
v.hasInitializer()
|
||||
or
|
||||
sub = true and allowVariableWithoutInitializer(e, v)
|
||||
sub = true and this.allowVariableWithoutInitializer(e, v)
|
||||
) and
|
||||
tractableVariable(v) and
|
||||
forall(StmtParent def | nonAnalyzableVariableDefinition(v, def) |
|
||||
sub = true and
|
||||
ignoreNonAnalyzableVariableDefinition(e, v, def)
|
||||
this.ignoreNonAnalyzableVariableDefinition(e, v, def)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate interestingFunction(Expr e, Function f) {
|
||||
exists(FunctionCall fc | interestingInternal(e, fc, _) |
|
||||
exists(FunctionCall fc | this.interestingInternal(e, fc, _) |
|
||||
f = fc.getTarget() and
|
||||
not obviouslyNonConstant(f) and
|
||||
not f.getUnspecifiedType() instanceof VoidType
|
||||
@@ -482,10 +481,10 @@ library class ExprEvaluator extends int {
|
||||
/** Gets the value of subexpressions `req` for expression `e`, if any. */
|
||||
private int getValueInternal(Expr e, Expr req) {
|
||||
(
|
||||
interestingInternal(e, req, true) and
|
||||
this.interestingInternal(e, req, true) and
|
||||
(
|
||||
result = req.(CompileTimeConstantInt).getIntValue() or
|
||||
result = getCompoundValue(e, req.(CompileTimeVariableExpr))
|
||||
result = this.getCompoundValue(e, req.(CompileTimeVariableExpr))
|
||||
) and
|
||||
(
|
||||
req.getUnderlyingType().(IntegralType).isSigned() or
|
||||
@@ -496,109 +495,126 @@ library class ExprEvaluator extends int {
|
||||
|
||||
/** Gets the value of compound subexpressions `val` for expression `e`, if any. */
|
||||
private int getCompoundValue(Expr e, CompileTimeVariableExpr val) {
|
||||
interestingInternal(e, val, true) and
|
||||
this.interestingInternal(e, val, true) and
|
||||
(
|
||||
exists(NotExpr req | req = val |
|
||||
result = 1 and getValueInternal(e, req.getOperand()) = 0
|
||||
result = 1 and this.getValueInternal(e, req.getOperand()) = 0
|
||||
or
|
||||
result = 0 and getValueInternal(e, req.getOperand()) != 0
|
||||
result = 0 and this.getValueInternal(e, req.getOperand()) != 0
|
||||
)
|
||||
or
|
||||
exists(LogicalAndExpr req | req = val |
|
||||
result = 1 and
|
||||
getValueInternal(e, req.getLeftOperand()) != 0 and
|
||||
getValueInternal(e, req.getRightOperand()) != 0
|
||||
this.getValueInternal(e, req.getLeftOperand()) != 0 and
|
||||
this.getValueInternal(e, req.getRightOperand()) != 0
|
||||
or
|
||||
result = 0 and getValueInternal(e, req.getAnOperand()) = 0
|
||||
result = 0 and this.getValueInternal(e, req.getAnOperand()) = 0
|
||||
)
|
||||
or
|
||||
exists(LogicalOrExpr req | req = val |
|
||||
result = 1 and getValueInternal(e, req.getAnOperand()) != 0
|
||||
result = 1 and this.getValueInternal(e, req.getAnOperand()) != 0
|
||||
or
|
||||
result = 0 and
|
||||
getValueInternal(e, req.getLeftOperand()) = 0 and
|
||||
getValueInternal(e, req.getRightOperand()) = 0
|
||||
this.getValueInternal(e, req.getLeftOperand()) = 0 and
|
||||
this.getValueInternal(e, req.getRightOperand()) = 0
|
||||
)
|
||||
or
|
||||
exists(LTExpr req | req = val |
|
||||
result = 1 and
|
||||
getValueInternal(e, req.getLeftOperand()) < getValueInternal(e, req.getRightOperand())
|
||||
this.getValueInternal(e, req.getLeftOperand()) <
|
||||
this.getValueInternal(e, req.getRightOperand())
|
||||
or
|
||||
result = 0 and
|
||||
getValueInternal(e, req.getLeftOperand()) >= getValueInternal(e, req.getRightOperand())
|
||||
this.getValueInternal(e, req.getLeftOperand()) >=
|
||||
this.getValueInternal(e, req.getRightOperand())
|
||||
)
|
||||
or
|
||||
exists(GTExpr req | req = val |
|
||||
result = 1 and
|
||||
getValueInternal(e, req.getLeftOperand()) > getValueInternal(e, req.getRightOperand())
|
||||
this.getValueInternal(e, req.getLeftOperand()) >
|
||||
this.getValueInternal(e, req.getRightOperand())
|
||||
or
|
||||
result = 0 and
|
||||
getValueInternal(e, req.getLeftOperand()) <= getValueInternal(e, req.getRightOperand())
|
||||
this.getValueInternal(e, req.getLeftOperand()) <=
|
||||
this.getValueInternal(e, req.getRightOperand())
|
||||
)
|
||||
or
|
||||
exists(LEExpr req | req = val |
|
||||
result = 1 and
|
||||
getValueInternal(e, req.getLeftOperand()) <= getValueInternal(e, req.getRightOperand())
|
||||
this.getValueInternal(e, req.getLeftOperand()) <=
|
||||
this.getValueInternal(e, req.getRightOperand())
|
||||
or
|
||||
result = 0 and
|
||||
getValueInternal(e, req.getLeftOperand()) > getValueInternal(e, req.getRightOperand())
|
||||
this.getValueInternal(e, req.getLeftOperand()) >
|
||||
this.getValueInternal(e, req.getRightOperand())
|
||||
)
|
||||
or
|
||||
exists(GEExpr req | req = val |
|
||||
result = 1 and
|
||||
getValueInternal(e, req.getLeftOperand()) >= getValueInternal(e, req.getRightOperand())
|
||||
this.getValueInternal(e, req.getLeftOperand()) >=
|
||||
this.getValueInternal(e, req.getRightOperand())
|
||||
or
|
||||
result = 0 and
|
||||
getValueInternal(e, req.getLeftOperand()) < getValueInternal(e, req.getRightOperand())
|
||||
this.getValueInternal(e, req.getLeftOperand()) <
|
||||
this.getValueInternal(e, req.getRightOperand())
|
||||
)
|
||||
or
|
||||
exists(EQExpr req | req = val |
|
||||
result = 1 and
|
||||
getValueInternal(e, req.getLeftOperand()) = getValueInternal(e, req.getRightOperand())
|
||||
this.getValueInternal(e, req.getLeftOperand()) =
|
||||
this.getValueInternal(e, req.getRightOperand())
|
||||
or
|
||||
result = 0 and
|
||||
getValueInternal(e, req.getLeftOperand()) != getValueInternal(e, req.getRightOperand())
|
||||
this.getValueInternal(e, req.getLeftOperand()) !=
|
||||
this.getValueInternal(e, req.getRightOperand())
|
||||
)
|
||||
or
|
||||
exists(NEExpr req | req = val |
|
||||
result = 0 and
|
||||
getValueInternal(e, req.getLeftOperand()) = getValueInternal(e, req.getRightOperand())
|
||||
this.getValueInternal(e, req.getLeftOperand()) =
|
||||
this.getValueInternal(e, req.getRightOperand())
|
||||
or
|
||||
result = 1 and
|
||||
getValueInternal(e, req.getLeftOperand()) != getValueInternal(e, req.getRightOperand())
|
||||
this.getValueInternal(e, req.getLeftOperand()) !=
|
||||
this.getValueInternal(e, req.getRightOperand())
|
||||
)
|
||||
or
|
||||
exists(AddExpr req | req = val |
|
||||
result =
|
||||
getValueInternal(e, req.getLeftOperand()) + getValueInternal(e, req.getRightOperand())
|
||||
this.getValueInternal(e, req.getLeftOperand()) +
|
||||
this.getValueInternal(e, req.getRightOperand())
|
||||
)
|
||||
or
|
||||
exists(SubExpr req | req = val |
|
||||
result =
|
||||
getValueInternal(e, req.getLeftOperand()) - getValueInternal(e, req.getRightOperand())
|
||||
this.getValueInternal(e, req.getLeftOperand()) -
|
||||
this.getValueInternal(e, req.getRightOperand())
|
||||
)
|
||||
or
|
||||
exists(MulExpr req | req = val |
|
||||
result =
|
||||
getValueInternal(e, req.getLeftOperand()) * getValueInternal(e, req.getRightOperand())
|
||||
this.getValueInternal(e, req.getLeftOperand()) *
|
||||
this.getValueInternal(e, req.getRightOperand())
|
||||
)
|
||||
or
|
||||
exists(RemExpr req | req = val |
|
||||
result =
|
||||
getValueInternal(e, req.getLeftOperand()) % getValueInternal(e, req.getRightOperand())
|
||||
this.getValueInternal(e, req.getLeftOperand()) %
|
||||
this.getValueInternal(e, req.getRightOperand())
|
||||
)
|
||||
or
|
||||
exists(DivExpr req | req = val |
|
||||
result =
|
||||
getValueInternal(e, req.getLeftOperand()) / getValueInternal(e, req.getRightOperand())
|
||||
this.getValueInternal(e, req.getLeftOperand()) /
|
||||
this.getValueInternal(e, req.getRightOperand())
|
||||
)
|
||||
or
|
||||
exists(AssignExpr req | req = val | result = getValueInternal(e, req.getRValue()))
|
||||
exists(AssignExpr req | req = val | result = this.getValueInternal(e, req.getRValue()))
|
||||
or
|
||||
result = getVariableValue(e, val.(VariableAccess))
|
||||
result = this.getVariableValue(e, val.(VariableAccess))
|
||||
or
|
||||
exists(FunctionCall call | call = val and not callWithMultipleTargets(call) |
|
||||
result = getFunctionValue(call.getTarget())
|
||||
result = this.getFunctionValue(call.getTarget())
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -606,7 +622,7 @@ library class ExprEvaluator extends int {
|
||||
language[monotonicAggregates]
|
||||
private int getVariableValue(Expr e, VariableAccess va) {
|
||||
exists(Variable v |
|
||||
interestingVariableAccess(e, va, v, true) and
|
||||
this.interestingVariableAccess(e, va, v, true) and
|
||||
// All assignments must have the same int value
|
||||
result =
|
||||
unique(Expr value |
|
||||
@@ -620,14 +636,16 @@ library class ExprEvaluator extends int {
|
||||
/** Holds if the function `f` is considered by the analysis and may return `ret`. */
|
||||
pragma[noinline]
|
||||
private predicate interestingReturnValue(Function f, Expr ret) {
|
||||
interestingFunction(_, f) and
|
||||
this.interestingFunction(_, f) and
|
||||
returnStmt(f, ret)
|
||||
}
|
||||
|
||||
private int getFunctionValue(Function f) {
|
||||
// All returns must have the same int value
|
||||
// And it must have at least one return
|
||||
forex(Expr ret | interestingReturnValue(f, ret) | result = getValueInternalNonSubExpr(ret))
|
||||
forex(Expr ret | this.interestingReturnValue(f, ret) |
|
||||
result = this.getValueInternalNonSubExpr(ret)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -642,10 +660,10 @@ library class ExprEvaluator extends int {
|
||||
* omitted).
|
||||
*/
|
||||
private int getValueInternalNonSubExpr(Expr req) {
|
||||
interestingInternal(_, req, false) and
|
||||
this.interestingInternal(_, req, false) and
|
||||
(
|
||||
result = req.(CompileTimeConstantInt).getIntValue() or
|
||||
result = getCompoundValueNonSubExpr(req.(CompileTimeVariableExpr))
|
||||
result = this.getCompoundValueNonSubExpr(req.(CompileTimeVariableExpr))
|
||||
) and
|
||||
(
|
||||
req.getUnderlyingType().(IntegralType).isSigned() or
|
||||
@@ -656,131 +674,131 @@ library class ExprEvaluator extends int {
|
||||
private int getCompoundValueNonSubExpr(CompileTimeVariableExpr val) {
|
||||
(
|
||||
exists(NotExpr req | req = val |
|
||||
result = 1 and getValueInternalNonSubExpr(req.getOperand()) = 0
|
||||
result = 1 and this.getValueInternalNonSubExpr(req.getOperand()) = 0
|
||||
or
|
||||
result = 0 and getValueInternalNonSubExpr(req.getOperand()) != 0
|
||||
result = 0 and this.getValueInternalNonSubExpr(req.getOperand()) != 0
|
||||
)
|
||||
or
|
||||
exists(LogicalAndExpr req | req = val |
|
||||
result = 1 and
|
||||
getValueInternalNonSubExpr(req.getLeftOperand()) != 0 and
|
||||
getValueInternalNonSubExpr(req.getRightOperand()) != 0
|
||||
this.getValueInternalNonSubExpr(req.getLeftOperand()) != 0 and
|
||||
this.getValueInternalNonSubExpr(req.getRightOperand()) != 0
|
||||
or
|
||||
result = 0 and getValueInternalNonSubExpr(req.getAnOperand()) = 0
|
||||
result = 0 and this.getValueInternalNonSubExpr(req.getAnOperand()) = 0
|
||||
)
|
||||
or
|
||||
exists(LogicalOrExpr req | req = val |
|
||||
result = 1 and getValueInternalNonSubExpr(req.getAnOperand()) != 0
|
||||
result = 1 and this.getValueInternalNonSubExpr(req.getAnOperand()) != 0
|
||||
or
|
||||
result = 0 and
|
||||
getValueInternalNonSubExpr(req.getLeftOperand()) = 0 and
|
||||
getValueInternalNonSubExpr(req.getRightOperand()) = 0
|
||||
this.getValueInternalNonSubExpr(req.getLeftOperand()) = 0 and
|
||||
this.getValueInternalNonSubExpr(req.getRightOperand()) = 0
|
||||
)
|
||||
or
|
||||
exists(LTExpr req | req = val |
|
||||
result = 1 and
|
||||
getValueInternalNonSubExpr(req.getLeftOperand()) <
|
||||
getValueInternalNonSubExpr(req.getRightOperand())
|
||||
this.getValueInternalNonSubExpr(req.getLeftOperand()) <
|
||||
this.getValueInternalNonSubExpr(req.getRightOperand())
|
||||
or
|
||||
result = 0 and
|
||||
getValueInternalNonSubExpr(req.getLeftOperand()) >=
|
||||
getValueInternalNonSubExpr(req.getRightOperand())
|
||||
this.getValueInternalNonSubExpr(req.getLeftOperand()) >=
|
||||
this.getValueInternalNonSubExpr(req.getRightOperand())
|
||||
)
|
||||
or
|
||||
exists(GTExpr req | req = val |
|
||||
result = 1 and
|
||||
getValueInternalNonSubExpr(req.getLeftOperand()) >
|
||||
getValueInternalNonSubExpr(req.getRightOperand())
|
||||
this.getValueInternalNonSubExpr(req.getLeftOperand()) >
|
||||
this.getValueInternalNonSubExpr(req.getRightOperand())
|
||||
or
|
||||
result = 0 and
|
||||
getValueInternalNonSubExpr(req.getLeftOperand()) <=
|
||||
getValueInternalNonSubExpr(req.getRightOperand())
|
||||
this.getValueInternalNonSubExpr(req.getLeftOperand()) <=
|
||||
this.getValueInternalNonSubExpr(req.getRightOperand())
|
||||
)
|
||||
or
|
||||
exists(LEExpr req | req = val |
|
||||
result = 1 and
|
||||
getValueInternalNonSubExpr(req.getLeftOperand()) <=
|
||||
getValueInternalNonSubExpr(req.getRightOperand())
|
||||
this.getValueInternalNonSubExpr(req.getLeftOperand()) <=
|
||||
this.getValueInternalNonSubExpr(req.getRightOperand())
|
||||
or
|
||||
result = 0 and
|
||||
getValueInternalNonSubExpr(req.getLeftOperand()) >
|
||||
getValueInternalNonSubExpr(req.getRightOperand())
|
||||
this.getValueInternalNonSubExpr(req.getLeftOperand()) >
|
||||
this.getValueInternalNonSubExpr(req.getRightOperand())
|
||||
)
|
||||
or
|
||||
exists(GEExpr req | req = val |
|
||||
result = 1 and
|
||||
getValueInternalNonSubExpr(req.getLeftOperand()) >=
|
||||
getValueInternalNonSubExpr(req.getRightOperand())
|
||||
this.getValueInternalNonSubExpr(req.getLeftOperand()) >=
|
||||
this.getValueInternalNonSubExpr(req.getRightOperand())
|
||||
or
|
||||
result = 0 and
|
||||
getValueInternalNonSubExpr(req.getLeftOperand()) <
|
||||
getValueInternalNonSubExpr(req.getRightOperand())
|
||||
this.getValueInternalNonSubExpr(req.getLeftOperand()) <
|
||||
this.getValueInternalNonSubExpr(req.getRightOperand())
|
||||
)
|
||||
or
|
||||
exists(EQExpr req | req = val |
|
||||
result = 1 and
|
||||
getValueInternalNonSubExpr(req.getLeftOperand()) =
|
||||
getValueInternalNonSubExpr(req.getRightOperand())
|
||||
this.getValueInternalNonSubExpr(req.getLeftOperand()) =
|
||||
this.getValueInternalNonSubExpr(req.getRightOperand())
|
||||
or
|
||||
result = 0 and
|
||||
getValueInternalNonSubExpr(req.getLeftOperand()) !=
|
||||
getValueInternalNonSubExpr(req.getRightOperand())
|
||||
this.getValueInternalNonSubExpr(req.getLeftOperand()) !=
|
||||
this.getValueInternalNonSubExpr(req.getRightOperand())
|
||||
)
|
||||
or
|
||||
exists(NEExpr req | req = val |
|
||||
result = 0 and
|
||||
getValueInternalNonSubExpr(req.getLeftOperand()) =
|
||||
getValueInternalNonSubExpr(req.getRightOperand())
|
||||
this.getValueInternalNonSubExpr(req.getLeftOperand()) =
|
||||
this.getValueInternalNonSubExpr(req.getRightOperand())
|
||||
or
|
||||
result = 1 and
|
||||
getValueInternalNonSubExpr(req.getLeftOperand()) !=
|
||||
getValueInternalNonSubExpr(req.getRightOperand())
|
||||
this.getValueInternalNonSubExpr(req.getLeftOperand()) !=
|
||||
this.getValueInternalNonSubExpr(req.getRightOperand())
|
||||
)
|
||||
or
|
||||
exists(AddExpr req | req = val |
|
||||
result =
|
||||
getValueInternalNonSubExpr(req.getLeftOperand()) +
|
||||
getValueInternalNonSubExpr(req.getRightOperand())
|
||||
this.getValueInternalNonSubExpr(req.getLeftOperand()) +
|
||||
this.getValueInternalNonSubExpr(req.getRightOperand())
|
||||
)
|
||||
or
|
||||
exists(SubExpr req | req = val |
|
||||
result =
|
||||
getValueInternalNonSubExpr(req.getLeftOperand()) -
|
||||
getValueInternalNonSubExpr(req.getRightOperand())
|
||||
this.getValueInternalNonSubExpr(req.getLeftOperand()) -
|
||||
this.getValueInternalNonSubExpr(req.getRightOperand())
|
||||
)
|
||||
or
|
||||
exists(MulExpr req | req = val |
|
||||
result =
|
||||
getValueInternalNonSubExpr(req.getLeftOperand()) *
|
||||
getValueInternalNonSubExpr(req.getRightOperand())
|
||||
this.getValueInternalNonSubExpr(req.getLeftOperand()) *
|
||||
this.getValueInternalNonSubExpr(req.getRightOperand())
|
||||
)
|
||||
or
|
||||
exists(RemExpr req | req = val |
|
||||
result =
|
||||
getValueInternalNonSubExpr(req.getLeftOperand()) %
|
||||
getValueInternalNonSubExpr(req.getRightOperand())
|
||||
this.getValueInternalNonSubExpr(req.getLeftOperand()) %
|
||||
this.getValueInternalNonSubExpr(req.getRightOperand())
|
||||
)
|
||||
or
|
||||
exists(DivExpr req | req = val |
|
||||
result =
|
||||
getValueInternalNonSubExpr(req.getLeftOperand()) /
|
||||
getValueInternalNonSubExpr(req.getRightOperand())
|
||||
this.getValueInternalNonSubExpr(req.getLeftOperand()) /
|
||||
this.getValueInternalNonSubExpr(req.getRightOperand())
|
||||
)
|
||||
or
|
||||
exists(AssignExpr req | req = val | result = getValueInternalNonSubExpr(req.getRValue()))
|
||||
exists(AssignExpr req | req = val | result = this.getValueInternalNonSubExpr(req.getRValue()))
|
||||
or
|
||||
result = getVariableValueNonSubExpr(val.(VariableAccess))
|
||||
result = this.getVariableValueNonSubExpr(val.(VariableAccess))
|
||||
or
|
||||
exists(FunctionCall call | call = val and not callWithMultipleTargets(call) |
|
||||
result = getFunctionValue(call.getTarget())
|
||||
result = this.getFunctionValue(call.getTarget())
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private int getVariableValueNonSubExpr(VariableAccess va) {
|
||||
// All assignments must have the same int value
|
||||
result = getMinVariableValueNonSubExpr(va) and
|
||||
result = getMaxVariableValueNonSubExpr(va)
|
||||
result = this.getMinVariableValueNonSubExpr(va) and
|
||||
result = this.getMaxVariableValueNonSubExpr(va)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -791,8 +809,9 @@ library class ExprEvaluator extends int {
|
||||
pragma[noopt]
|
||||
private int getMinVariableValueNonSubExpr(VariableAccess va) {
|
||||
exists(Variable v |
|
||||
interestingVariableAccess(_, va, v, false) and
|
||||
result = min(Expr value | value = v.getAnAssignedValue() | getValueInternalNonSubExpr(value))
|
||||
this.interestingVariableAccess(_, va, v, false) and
|
||||
result =
|
||||
min(Expr value | value = v.getAnAssignedValue() | this.getValueInternalNonSubExpr(value))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -804,8 +823,9 @@ library class ExprEvaluator extends int {
|
||||
pragma[noopt]
|
||||
private int getMaxVariableValueNonSubExpr(VariableAccess va) {
|
||||
exists(Variable v |
|
||||
interestingVariableAccess(_, va, v, false) and
|
||||
result = max(Expr value | value = v.getAnAssignedValue() | getValueInternalNonSubExpr(value))
|
||||
this.interestingVariableAccess(_, va, v, false) and
|
||||
result =
|
||||
max(Expr value | value = v.getAnAssignedValue() | this.getValueInternalNonSubExpr(value))
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -968,9 +988,9 @@ library class LoopEntryConditionEvaluator extends ExprEvaluator {
|
||||
abstract predicate isLoopBody(Expr e, StmtParent s);
|
||||
|
||||
private predicate isLoopBodyDescendant(Expr e, StmtParent s) {
|
||||
isLoopBody(e, s)
|
||||
this.isLoopBody(e, s)
|
||||
or
|
||||
exists(StmtParent mid | isLoopBodyDescendant(e, mid) |
|
||||
exists(StmtParent mid | this.isLoopBodyDescendant(e, mid) |
|
||||
s = mid.(Stmt).getAChild() or
|
||||
s = mid.(Expr).getAChild()
|
||||
)
|
||||
@@ -978,13 +998,13 @@ library class LoopEntryConditionEvaluator extends ExprEvaluator {
|
||||
|
||||
// Same as `interestingInternal(e, sub, true)` but avoids negative recursion
|
||||
private predicate interestingSubExpr(Expr e, Expr sub) {
|
||||
interesting(e) and e = sub
|
||||
this.interesting(e) and e = sub
|
||||
or
|
||||
exists(Expr mid | interestingSubExpr(e, mid) and sub = mid.getAChild())
|
||||
exists(Expr mid | this.interestingSubExpr(e, mid) and sub = mid.getAChild())
|
||||
}
|
||||
|
||||
private predicate maybeInterestingVariable(Expr e, Variable v) {
|
||||
exists(VariableAccess va | interestingSubExpr(e, va) | va.getTarget() = v)
|
||||
exists(VariableAccess va | this.interestingSubExpr(e, va) | va.getTarget() = v)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -996,9 +1016,9 @@ library class LoopEntryConditionEvaluator extends ExprEvaluator {
|
||||
* definition of `v`.
|
||||
*/
|
||||
private predicate reachesLoopEntryFromLoopBody(Expr e, Variable v, StmtParent valueOrDef) {
|
||||
maybeInterestingVariable(e, v) and
|
||||
this.maybeInterestingVariable(e, v) and
|
||||
(valueOrDef = v.getAnAssignedValue() or nonAnalyzableVariableDefinition(v, valueOrDef)) and
|
||||
isLoopBodyDescendant(e, valueOrDef) and
|
||||
this.isLoopBodyDescendant(e, valueOrDef) and
|
||||
/*
|
||||
* Use primitive basic blocks in reachability analysis for better performance.
|
||||
* This is similar to the pattern used in e.g. `DefinitionsAndUses` and
|
||||
@@ -1008,16 +1028,16 @@ library class LoopEntryConditionEvaluator extends ExprEvaluator {
|
||||
exists(PrimitiveBasicBlock bb1, int pos1 | bb1.getNode(pos1) = valueOrDef |
|
||||
// Reaches in same basic block
|
||||
exists(int pos2 |
|
||||
loopEntryAt(bb1, pos2, e) and
|
||||
this.loopEntryAt(bb1, pos2, e) and
|
||||
pos2 > pos1 and
|
||||
not exists(int k | assignmentAt(bb1, k, v) | k in [pos1 + 1 .. pos2 - 1])
|
||||
not exists(int k | this.assignmentAt(bb1, k, v) | k in [pos1 + 1 .. pos2 - 1])
|
||||
)
|
||||
or
|
||||
// Reaches in a successor block
|
||||
exists(PrimitiveBasicBlock bb2 |
|
||||
bb2 = bb1.getASuccessor() and
|
||||
not exists(int pos3 | assignmentAt(bb1, pos3, v) and pos3 > pos1) and
|
||||
bbReachesLoopEntry(bb2, e, v)
|
||||
not exists(int pos3 | this.assignmentAt(bb1, pos3, v) and pos3 > pos1) and
|
||||
this.bbReachesLoopEntry(bb2, e, v)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -1025,12 +1045,12 @@ library class LoopEntryConditionEvaluator extends ExprEvaluator {
|
||||
private predicate loopEntryAt(PrimitiveBasicBlock bb, int pos, Expr e) {
|
||||
exists(Node cfn |
|
||||
bb.getNode(pos) = cfn and
|
||||
isLoopEntry(e, cfn)
|
||||
this.isLoopEntry(e, cfn)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate assignmentAt(PrimitiveBasicBlock bb, int pos, Variable v) {
|
||||
maybeInterestingVariable(_, v) and
|
||||
this.maybeInterestingVariable(_, v) and
|
||||
bb.getNode(pos) = v.getAnAssignedValue()
|
||||
}
|
||||
|
||||
@@ -1039,19 +1059,19 @@ library class LoopEntryConditionEvaluator extends ExprEvaluator {
|
||||
* the loop belonging to `e` without crossing an assignment to `v`.
|
||||
*/
|
||||
private predicate bbReachesLoopEntry(PrimitiveBasicBlock bb, Expr e, Variable v) {
|
||||
bbReachesLoopEntryLocally(bb, e, v)
|
||||
this.bbReachesLoopEntryLocally(bb, e, v)
|
||||
or
|
||||
exists(PrimitiveBasicBlock succ | succ = bb.getASuccessor() |
|
||||
bbReachesLoopEntry(succ, e, v) and
|
||||
not assignmentAt(bb, _, v)
|
||||
this.bbReachesLoopEntry(succ, e, v) and
|
||||
not this.assignmentAt(bb, _, v)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate bbReachesLoopEntryLocally(PrimitiveBasicBlock bb, Expr e, Variable v) {
|
||||
exists(int pos |
|
||||
loopEntryAt(bb, pos, e) and
|
||||
maybeInterestingVariable(e, v) and
|
||||
not exists(int pos1 | assignmentAt(bb, pos1, v) | pos1 < pos)
|
||||
this.loopEntryAt(bb, pos, e) and
|
||||
this.maybeInterestingVariable(e, v) and
|
||||
not exists(int pos1 | this.assignmentAt(bb, pos1, v) | pos1 < pos)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1085,10 +1105,10 @@ library class LoopEntryConditionEvaluator extends ExprEvaluator {
|
||||
* ```
|
||||
*/
|
||||
override predicate ignoreNonAnalyzableVariableDefinition(Expr e, Variable v, StmtParent def) {
|
||||
maybeInterestingVariable(e, v) and
|
||||
this.maybeInterestingVariable(e, v) and
|
||||
nonAnalyzableVariableDefinition(v, def) and
|
||||
isLoopBodyDescendant(e, def) and
|
||||
not reachesLoopEntryFromLoopBody(e, v, def)
|
||||
this.isLoopBodyDescendant(e, def) and
|
||||
not this.reachesLoopEntryFromLoopBody(e, v, def)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1121,10 +1141,10 @@ library class LoopEntryConditionEvaluator extends ExprEvaluator {
|
||||
* ```
|
||||
*/
|
||||
override predicate ignoreVariableAssignment(Expr e, Variable v, Expr value) {
|
||||
maybeInterestingVariable(e, v) and
|
||||
this.maybeInterestingVariable(e, v) and
|
||||
value = v.getAnAssignedValue() and
|
||||
isLoopBodyDescendant(e, value) and
|
||||
not reachesLoopEntryFromLoopBody(e, v, value)
|
||||
this.isLoopBodyDescendant(e, value) and
|
||||
not this.reachesLoopEntryFromLoopBody(e, v, value)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -110,12 +110,12 @@ abstract class Configuration extends string {
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlowTo(Node sink) { hasFlow(_, sink) }
|
||||
predicate hasFlowTo(Node sink) { this.hasFlow(_, sink) }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlowToExpr(DataFlowExpr sink) { hasFlowTo(exprNode(sink)) }
|
||||
predicate hasFlowToExpr(DataFlowExpr sink) { this.hasFlowTo(exprNode(sink)) }
|
||||
|
||||
/**
|
||||
* Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev`
|
||||
@@ -244,6 +244,8 @@ private class ParamNodeEx extends NodeEx {
|
||||
}
|
||||
|
||||
int getPosition() { this.isParameterOf(_, result) }
|
||||
|
||||
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
|
||||
}
|
||||
|
||||
private class RetNodeEx extends NodeEx {
|
||||
@@ -744,8 +746,12 @@ private module Stage1 {
|
||||
returnFlowCallableNodeCand(c, kind, config) and
|
||||
p.getEnclosingCallable() = c and
|
||||
exists(ap) and
|
||||
// we don't expect a parameter to return stored in itself
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -923,28 +929,29 @@ private module Stage2 {
|
||||
|
||||
ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
|
||||
|
||||
class Cc = boolean;
|
||||
class Cc = CallContext;
|
||||
|
||||
class CcCall extends Cc {
|
||||
CcCall() { this = true }
|
||||
class CcCall = CallContextCall;
|
||||
|
||||
/** Holds if this call context may be `call`. */
|
||||
predicate matchesCall(DataFlowCall call) { any() }
|
||||
}
|
||||
class CcNoCall = CallContextNoCall;
|
||||
|
||||
class CcNoCall extends Cc {
|
||||
CcNoCall() { this = false }
|
||||
}
|
||||
|
||||
Cc ccNone() { result = false }
|
||||
Cc ccNone() { result instanceof CallContextAny }
|
||||
|
||||
private class LocalCc = Unit;
|
||||
|
||||
bindingset[call, c, outercc]
|
||||
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
|
||||
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
|
||||
checkCallContextCall(outercc, call, c) and
|
||||
if recordDataFlowCallSiteDispatch(call, c)
|
||||
then result = TSpecificCall(call)
|
||||
else result = TSomeCall()
|
||||
}
|
||||
|
||||
bindingset[call, c, innercc]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
|
||||
checkCallContextReturn(innercc, c, call) and
|
||||
if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
|
||||
}
|
||||
|
||||
bindingset[node, cc, config]
|
||||
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
|
||||
@@ -1172,7 +1179,8 @@ private module Stage2 {
|
||||
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
|
||||
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
|
||||
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
@@ -1392,8 +1400,12 @@ private module Stage2 {
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1860,7 +1872,8 @@ private module Stage3 {
|
||||
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
|
||||
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
|
||||
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
@@ -2080,8 +2093,12 @@ private module Stage3 {
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2117,7 +2134,7 @@ private module Stage3 {
|
||||
private predicate flowCandSummaryCtx(NodeEx node, AccessPathFront argApf, Configuration config) {
|
||||
exists(AccessPathFront apf |
|
||||
Stage3::revFlow(node, true, _, apf, config) and
|
||||
Stage3::fwdFlow(node, true, TAccessPathFrontSome(argApf), apf, config)
|
||||
Stage3::fwdFlow(node, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2136,7 +2153,8 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
|
||||
) and
|
||||
accessPathApproxCostLimits(apLimit, tupleLimit) and
|
||||
apLimit < tails and
|
||||
tupleLimit < (tails - 1) * nodes
|
||||
tupleLimit < (tails - 1) * nodes and
|
||||
not tc.forceHighPrecision()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2618,7 +2636,8 @@ private module Stage4 {
|
||||
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
|
||||
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
|
||||
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
@@ -2838,8 +2857,12 @@ private module Stage4 {
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2912,6 +2935,8 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome {
|
||||
|
||||
int getParameterPos() { p.isParameterOf(_, result) }
|
||||
|
||||
ParamNodeEx getParamNode() { result = p }
|
||||
|
||||
override string toString() { result = p + ": " + ap }
|
||||
|
||||
predicate hasLocationInfo(
|
||||
@@ -2969,12 +2994,15 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) {
|
||||
* expected to be expensive. Holds with `unfold = true` otherwise.
|
||||
*/
|
||||
private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) {
|
||||
exists(int aps, int nodes, int apLimit, int tupleLimit |
|
||||
aps = countPotentialAps(apa, config) and
|
||||
nodes = countNodesUsingAccessPath(apa, config) and
|
||||
accessPathCostLimits(apLimit, tupleLimit) and
|
||||
if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true
|
||||
)
|
||||
if apa.getHead().forceHighPrecision()
|
||||
then unfold = true
|
||||
else
|
||||
exists(int aps, int nodes, int apLimit, int tupleLimit |
|
||||
aps = countPotentialAps(apa, config) and
|
||||
nodes = countNodesUsingAccessPath(apa, config) and
|
||||
accessPathCostLimits(apLimit, tupleLimit) and
|
||||
if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3162,7 +3190,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons {
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "[" + this.toStringImpl(true) + length().toString() + ")]"
|
||||
result = "[" + this.toStringImpl(true) + this.length().toString() + ")]"
|
||||
or
|
||||
result = "[" + this.toStringImpl(false)
|
||||
}
|
||||
@@ -3244,7 +3272,7 @@ class PathNode extends TPathNode {
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
|
||||
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
@@ -3301,9 +3329,11 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">"
|
||||
}
|
||||
|
||||
override string toString() { result = this.getNodeEx().toString() + ppAp() }
|
||||
override string toString() { result = this.getNodeEx().toString() + this.ppAp() }
|
||||
|
||||
override string toStringWithContext() { result = this.getNodeEx().toString() + ppAp() + ppCtx() }
|
||||
override string toStringWithContext() {
|
||||
result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx()
|
||||
}
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
@@ -3320,8 +3350,8 @@ private predicate directReach(PathNode n) {
|
||||
/** Holds if `n` can reach a sink or is used in a subpath. */
|
||||
private predicate reach(PathNode n) { directReach(n) or Subpaths::retReach(n) }
|
||||
|
||||
/** Holds if `n1.getSucc() = n2` and `n2` can reach a sink or is used in a subpath. */
|
||||
private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and reach(n2) }
|
||||
/** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */
|
||||
private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and directReach(n2) }
|
||||
|
||||
private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2)
|
||||
|
||||
@@ -3330,7 +3360,7 @@ private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1
|
||||
*/
|
||||
module PathGraph {
|
||||
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
|
||||
query predicate edges(PathNode a, PathNode b) { pathSucc(a, b) }
|
||||
query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b and reach(b) }
|
||||
|
||||
/** Holds if `n` is a node in the graph of data flow path explanations. */
|
||||
query predicate nodes(PathNode n, string key, string val) {
|
||||
@@ -3371,11 +3401,11 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
|
||||
|
||||
override PathNodeImpl getASuccessorImpl() {
|
||||
// an intermediate step to another intermediate node
|
||||
result = getSuccMid()
|
||||
result = this.getSuccMid()
|
||||
or
|
||||
// a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
|
||||
exists(PathNodeMid mid, PathNodeSink sink |
|
||||
mid = getSuccMid() and
|
||||
mid = this.getSuccMid() and
|
||||
mid.getNodeEx() = sink.getNodeEx() and
|
||||
mid.getAp() instanceof AccessPathNil and
|
||||
sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
|
||||
@@ -3452,7 +3482,7 @@ private predicate pathStep(
|
||||
exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and
|
||||
sc = mid.getSummaryCtx()
|
||||
or
|
||||
pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp()
|
||||
pathIntoCallable(mid, node, _, cc, sc, _, _) and ap = mid.getAp()
|
||||
or
|
||||
pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone
|
||||
or
|
||||
@@ -3529,14 +3559,16 @@ private predicate pathOutOfCallable(PathNodeMid mid, NodeEx out, CallContext cc)
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate pathIntoArg(
|
||||
PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa
|
||||
PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ArgNode arg |
|
||||
arg = mid.getNodeEx().asNode() and
|
||||
cc = mid.getCallContext() and
|
||||
arg.argumentOf(call, i) and
|
||||
ap = mid.getAp() and
|
||||
apa = ap.getApprox()
|
||||
apa = ap.getApprox() and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3553,12 +3585,14 @@ private predicate parameterCand(
|
||||
pragma[nomagic]
|
||||
private predicate pathIntoCallable0(
|
||||
PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call,
|
||||
AccessPath ap
|
||||
AccessPath ap, Configuration config
|
||||
) {
|
||||
exists(AccessPathApprox apa |
|
||||
pathIntoArg(mid, i, outercc, call, ap, apa) and
|
||||
pathIntoArg(mid, pragma[only_bind_into](i), outercc, call, ap, pragma[only_bind_into](apa),
|
||||
pragma[only_bind_into](config)) and
|
||||
callable = resolveCall(call, outercc) and
|
||||
parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration())
|
||||
parameterCand(callable, pragma[only_bind_into](i), pragma[only_bind_into](apa),
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3567,12 +3601,13 @@ private predicate pathIntoCallable0(
|
||||
* before and after entering the callable are `outercc` and `innercc`,
|
||||
* respectively.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate pathIntoCallable(
|
||||
PathNodeMid mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc, SummaryCtx sc,
|
||||
DataFlowCall call
|
||||
DataFlowCall call, Configuration config
|
||||
) {
|
||||
exists(int i, DataFlowCallable callable, AccessPath ap |
|
||||
pathIntoCallable0(mid, callable, i, outercc, call, ap) and
|
||||
pathIntoCallable0(mid, callable, i, outercc, call, ap, config) and
|
||||
p.isParameterOf(callable, i) and
|
||||
(
|
||||
sc = TSummaryCtxSome(p, ap)
|
||||
@@ -3602,18 +3637,23 @@ private predicate paramFlowsThrough(
|
||||
ap = mid.getAp() and
|
||||
apa = ap.getApprox() and
|
||||
pos = sc.getParameterPos() and
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
sc.getParamNode().allowParameterReturnInSelf()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate pathThroughCallable0(
|
||||
DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap,
|
||||
AccessPathApprox apa
|
||||
AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(CallContext innercc, SummaryCtx sc |
|
||||
pathIntoCallable(mid, _, cc, innercc, sc, call) and
|
||||
paramFlowsThrough(kind, innercc, sc, ap, apa, unbindConf(mid.getConfiguration()))
|
||||
pathIntoCallable(mid, _, cc, innercc, sc, call, config) and
|
||||
paramFlowsThrough(kind, innercc, sc, ap, apa, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3623,9 +3663,9 @@ private predicate pathThroughCallable0(
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext cc, AccessPath ap) {
|
||||
exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa |
|
||||
pathThroughCallable0(call, mid, kind, cc, ap, apa) and
|
||||
out = getAnOutNodeFlow(kind, call, apa, unbindConf(mid.getConfiguration()))
|
||||
exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa, Configuration config |
|
||||
pathThroughCallable0(call, mid, kind, cc, ap, apa, config) and
|
||||
out = getAnOutNodeFlow(kind, call, apa, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3639,9 +3679,11 @@ private module Subpaths {
|
||||
PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
|
||||
NodeEx out, AccessPath apout
|
||||
) {
|
||||
pathThroughCallable(arg, out, _, apout) and
|
||||
pathIntoCallable(arg, par, _, innercc, sc, _) and
|
||||
paramFlowsThrough(kind, innercc, sc, apout, _, unbindConf(arg.getConfiguration()))
|
||||
exists(Configuration config |
|
||||
pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and
|
||||
pathIntoCallable(arg, par, _, innercc, sc, _, config) and
|
||||
paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, unbindConf(config))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3686,8 +3728,8 @@ private module Subpaths {
|
||||
*/
|
||||
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeMid ret, PathNodeMid out) {
|
||||
exists(ParamNodeEx p, NodeEx o, AccessPath apout |
|
||||
arg.getASuccessor() = par and
|
||||
arg.getASuccessor() = out and
|
||||
pragma[only_bind_into](arg).getASuccessor() = par and
|
||||
pragma[only_bind_into](arg).getASuccessor() = out and
|
||||
subpaths03(arg, p, ret, o, apout) and
|
||||
par.getNodeEx() = p and
|
||||
out.getNodeEx() = o and
|
||||
@@ -4028,7 +4070,7 @@ private module FlowExploration {
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
|
||||
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
|
||||
@@ -110,12 +110,12 @@ abstract class Configuration extends string {
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlowTo(Node sink) { hasFlow(_, sink) }
|
||||
predicate hasFlowTo(Node sink) { this.hasFlow(_, sink) }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlowToExpr(DataFlowExpr sink) { hasFlowTo(exprNode(sink)) }
|
||||
predicate hasFlowToExpr(DataFlowExpr sink) { this.hasFlowTo(exprNode(sink)) }
|
||||
|
||||
/**
|
||||
* Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev`
|
||||
@@ -244,6 +244,8 @@ private class ParamNodeEx extends NodeEx {
|
||||
}
|
||||
|
||||
int getPosition() { this.isParameterOf(_, result) }
|
||||
|
||||
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
|
||||
}
|
||||
|
||||
private class RetNodeEx extends NodeEx {
|
||||
@@ -744,8 +746,12 @@ private module Stage1 {
|
||||
returnFlowCallableNodeCand(c, kind, config) and
|
||||
p.getEnclosingCallable() = c and
|
||||
exists(ap) and
|
||||
// we don't expect a parameter to return stored in itself
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -923,28 +929,29 @@ private module Stage2 {
|
||||
|
||||
ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
|
||||
|
||||
class Cc = boolean;
|
||||
class Cc = CallContext;
|
||||
|
||||
class CcCall extends Cc {
|
||||
CcCall() { this = true }
|
||||
class CcCall = CallContextCall;
|
||||
|
||||
/** Holds if this call context may be `call`. */
|
||||
predicate matchesCall(DataFlowCall call) { any() }
|
||||
}
|
||||
class CcNoCall = CallContextNoCall;
|
||||
|
||||
class CcNoCall extends Cc {
|
||||
CcNoCall() { this = false }
|
||||
}
|
||||
|
||||
Cc ccNone() { result = false }
|
||||
Cc ccNone() { result instanceof CallContextAny }
|
||||
|
||||
private class LocalCc = Unit;
|
||||
|
||||
bindingset[call, c, outercc]
|
||||
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
|
||||
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
|
||||
checkCallContextCall(outercc, call, c) and
|
||||
if recordDataFlowCallSiteDispatch(call, c)
|
||||
then result = TSpecificCall(call)
|
||||
else result = TSomeCall()
|
||||
}
|
||||
|
||||
bindingset[call, c, innercc]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
|
||||
checkCallContextReturn(innercc, c, call) and
|
||||
if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
|
||||
}
|
||||
|
||||
bindingset[node, cc, config]
|
||||
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
|
||||
@@ -1172,7 +1179,8 @@ private module Stage2 {
|
||||
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
|
||||
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
|
||||
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
@@ -1392,8 +1400,12 @@ private module Stage2 {
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1860,7 +1872,8 @@ private module Stage3 {
|
||||
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
|
||||
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
|
||||
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
@@ -2080,8 +2093,12 @@ private module Stage3 {
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2117,7 +2134,7 @@ private module Stage3 {
|
||||
private predicate flowCandSummaryCtx(NodeEx node, AccessPathFront argApf, Configuration config) {
|
||||
exists(AccessPathFront apf |
|
||||
Stage3::revFlow(node, true, _, apf, config) and
|
||||
Stage3::fwdFlow(node, true, TAccessPathFrontSome(argApf), apf, config)
|
||||
Stage3::fwdFlow(node, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2136,7 +2153,8 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
|
||||
) and
|
||||
accessPathApproxCostLimits(apLimit, tupleLimit) and
|
||||
apLimit < tails and
|
||||
tupleLimit < (tails - 1) * nodes
|
||||
tupleLimit < (tails - 1) * nodes and
|
||||
not tc.forceHighPrecision()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2618,7 +2636,8 @@ private module Stage4 {
|
||||
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
|
||||
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
|
||||
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
@@ -2838,8 +2857,12 @@ private module Stage4 {
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2912,6 +2935,8 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome {
|
||||
|
||||
int getParameterPos() { p.isParameterOf(_, result) }
|
||||
|
||||
ParamNodeEx getParamNode() { result = p }
|
||||
|
||||
override string toString() { result = p + ": " + ap }
|
||||
|
||||
predicate hasLocationInfo(
|
||||
@@ -2969,12 +2994,15 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) {
|
||||
* expected to be expensive. Holds with `unfold = true` otherwise.
|
||||
*/
|
||||
private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) {
|
||||
exists(int aps, int nodes, int apLimit, int tupleLimit |
|
||||
aps = countPotentialAps(apa, config) and
|
||||
nodes = countNodesUsingAccessPath(apa, config) and
|
||||
accessPathCostLimits(apLimit, tupleLimit) and
|
||||
if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true
|
||||
)
|
||||
if apa.getHead().forceHighPrecision()
|
||||
then unfold = true
|
||||
else
|
||||
exists(int aps, int nodes, int apLimit, int tupleLimit |
|
||||
aps = countPotentialAps(apa, config) and
|
||||
nodes = countNodesUsingAccessPath(apa, config) and
|
||||
accessPathCostLimits(apLimit, tupleLimit) and
|
||||
if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3162,7 +3190,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons {
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "[" + this.toStringImpl(true) + length().toString() + ")]"
|
||||
result = "[" + this.toStringImpl(true) + this.length().toString() + ")]"
|
||||
or
|
||||
result = "[" + this.toStringImpl(false)
|
||||
}
|
||||
@@ -3244,7 +3272,7 @@ class PathNode extends TPathNode {
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
|
||||
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
@@ -3301,9 +3329,11 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">"
|
||||
}
|
||||
|
||||
override string toString() { result = this.getNodeEx().toString() + ppAp() }
|
||||
override string toString() { result = this.getNodeEx().toString() + this.ppAp() }
|
||||
|
||||
override string toStringWithContext() { result = this.getNodeEx().toString() + ppAp() + ppCtx() }
|
||||
override string toStringWithContext() {
|
||||
result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx()
|
||||
}
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
@@ -3320,8 +3350,8 @@ private predicate directReach(PathNode n) {
|
||||
/** Holds if `n` can reach a sink or is used in a subpath. */
|
||||
private predicate reach(PathNode n) { directReach(n) or Subpaths::retReach(n) }
|
||||
|
||||
/** Holds if `n1.getSucc() = n2` and `n2` can reach a sink or is used in a subpath. */
|
||||
private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and reach(n2) }
|
||||
/** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */
|
||||
private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and directReach(n2) }
|
||||
|
||||
private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2)
|
||||
|
||||
@@ -3330,7 +3360,7 @@ private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1
|
||||
*/
|
||||
module PathGraph {
|
||||
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
|
||||
query predicate edges(PathNode a, PathNode b) { pathSucc(a, b) }
|
||||
query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b and reach(b) }
|
||||
|
||||
/** Holds if `n` is a node in the graph of data flow path explanations. */
|
||||
query predicate nodes(PathNode n, string key, string val) {
|
||||
@@ -3371,11 +3401,11 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
|
||||
|
||||
override PathNodeImpl getASuccessorImpl() {
|
||||
// an intermediate step to another intermediate node
|
||||
result = getSuccMid()
|
||||
result = this.getSuccMid()
|
||||
or
|
||||
// a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
|
||||
exists(PathNodeMid mid, PathNodeSink sink |
|
||||
mid = getSuccMid() and
|
||||
mid = this.getSuccMid() and
|
||||
mid.getNodeEx() = sink.getNodeEx() and
|
||||
mid.getAp() instanceof AccessPathNil and
|
||||
sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
|
||||
@@ -3452,7 +3482,7 @@ private predicate pathStep(
|
||||
exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and
|
||||
sc = mid.getSummaryCtx()
|
||||
or
|
||||
pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp()
|
||||
pathIntoCallable(mid, node, _, cc, sc, _, _) and ap = mid.getAp()
|
||||
or
|
||||
pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone
|
||||
or
|
||||
@@ -3529,14 +3559,16 @@ private predicate pathOutOfCallable(PathNodeMid mid, NodeEx out, CallContext cc)
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate pathIntoArg(
|
||||
PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa
|
||||
PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ArgNode arg |
|
||||
arg = mid.getNodeEx().asNode() and
|
||||
cc = mid.getCallContext() and
|
||||
arg.argumentOf(call, i) and
|
||||
ap = mid.getAp() and
|
||||
apa = ap.getApprox()
|
||||
apa = ap.getApprox() and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3553,12 +3585,14 @@ private predicate parameterCand(
|
||||
pragma[nomagic]
|
||||
private predicate pathIntoCallable0(
|
||||
PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call,
|
||||
AccessPath ap
|
||||
AccessPath ap, Configuration config
|
||||
) {
|
||||
exists(AccessPathApprox apa |
|
||||
pathIntoArg(mid, i, outercc, call, ap, apa) and
|
||||
pathIntoArg(mid, pragma[only_bind_into](i), outercc, call, ap, pragma[only_bind_into](apa),
|
||||
pragma[only_bind_into](config)) and
|
||||
callable = resolveCall(call, outercc) and
|
||||
parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration())
|
||||
parameterCand(callable, pragma[only_bind_into](i), pragma[only_bind_into](apa),
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3567,12 +3601,13 @@ private predicate pathIntoCallable0(
|
||||
* before and after entering the callable are `outercc` and `innercc`,
|
||||
* respectively.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate pathIntoCallable(
|
||||
PathNodeMid mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc, SummaryCtx sc,
|
||||
DataFlowCall call
|
||||
DataFlowCall call, Configuration config
|
||||
) {
|
||||
exists(int i, DataFlowCallable callable, AccessPath ap |
|
||||
pathIntoCallable0(mid, callable, i, outercc, call, ap) and
|
||||
pathIntoCallable0(mid, callable, i, outercc, call, ap, config) and
|
||||
p.isParameterOf(callable, i) and
|
||||
(
|
||||
sc = TSummaryCtxSome(p, ap)
|
||||
@@ -3602,18 +3637,23 @@ private predicate paramFlowsThrough(
|
||||
ap = mid.getAp() and
|
||||
apa = ap.getApprox() and
|
||||
pos = sc.getParameterPos() and
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
sc.getParamNode().allowParameterReturnInSelf()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate pathThroughCallable0(
|
||||
DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap,
|
||||
AccessPathApprox apa
|
||||
AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(CallContext innercc, SummaryCtx sc |
|
||||
pathIntoCallable(mid, _, cc, innercc, sc, call) and
|
||||
paramFlowsThrough(kind, innercc, sc, ap, apa, unbindConf(mid.getConfiguration()))
|
||||
pathIntoCallable(mid, _, cc, innercc, sc, call, config) and
|
||||
paramFlowsThrough(kind, innercc, sc, ap, apa, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3623,9 +3663,9 @@ private predicate pathThroughCallable0(
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext cc, AccessPath ap) {
|
||||
exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa |
|
||||
pathThroughCallable0(call, mid, kind, cc, ap, apa) and
|
||||
out = getAnOutNodeFlow(kind, call, apa, unbindConf(mid.getConfiguration()))
|
||||
exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa, Configuration config |
|
||||
pathThroughCallable0(call, mid, kind, cc, ap, apa, config) and
|
||||
out = getAnOutNodeFlow(kind, call, apa, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3639,9 +3679,11 @@ private module Subpaths {
|
||||
PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
|
||||
NodeEx out, AccessPath apout
|
||||
) {
|
||||
pathThroughCallable(arg, out, _, apout) and
|
||||
pathIntoCallable(arg, par, _, innercc, sc, _) and
|
||||
paramFlowsThrough(kind, innercc, sc, apout, _, unbindConf(arg.getConfiguration()))
|
||||
exists(Configuration config |
|
||||
pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and
|
||||
pathIntoCallable(arg, par, _, innercc, sc, _, config) and
|
||||
paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, unbindConf(config))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3686,8 +3728,8 @@ private module Subpaths {
|
||||
*/
|
||||
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeMid ret, PathNodeMid out) {
|
||||
exists(ParamNodeEx p, NodeEx o, AccessPath apout |
|
||||
arg.getASuccessor() = par and
|
||||
arg.getASuccessor() = out and
|
||||
pragma[only_bind_into](arg).getASuccessor() = par and
|
||||
pragma[only_bind_into](arg).getASuccessor() = out and
|
||||
subpaths03(arg, p, ret, o, apout) and
|
||||
par.getNodeEx() = p and
|
||||
out.getNodeEx() = o and
|
||||
@@ -4028,7 +4070,7 @@ private module FlowExploration {
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
|
||||
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
|
||||
@@ -110,12 +110,12 @@ abstract class Configuration extends string {
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlowTo(Node sink) { hasFlow(_, sink) }
|
||||
predicate hasFlowTo(Node sink) { this.hasFlow(_, sink) }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlowToExpr(DataFlowExpr sink) { hasFlowTo(exprNode(sink)) }
|
||||
predicate hasFlowToExpr(DataFlowExpr sink) { this.hasFlowTo(exprNode(sink)) }
|
||||
|
||||
/**
|
||||
* Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev`
|
||||
@@ -244,6 +244,8 @@ private class ParamNodeEx extends NodeEx {
|
||||
}
|
||||
|
||||
int getPosition() { this.isParameterOf(_, result) }
|
||||
|
||||
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
|
||||
}
|
||||
|
||||
private class RetNodeEx extends NodeEx {
|
||||
@@ -744,8 +746,12 @@ private module Stage1 {
|
||||
returnFlowCallableNodeCand(c, kind, config) and
|
||||
p.getEnclosingCallable() = c and
|
||||
exists(ap) and
|
||||
// we don't expect a parameter to return stored in itself
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -923,28 +929,29 @@ private module Stage2 {
|
||||
|
||||
ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
|
||||
|
||||
class Cc = boolean;
|
||||
class Cc = CallContext;
|
||||
|
||||
class CcCall extends Cc {
|
||||
CcCall() { this = true }
|
||||
class CcCall = CallContextCall;
|
||||
|
||||
/** Holds if this call context may be `call`. */
|
||||
predicate matchesCall(DataFlowCall call) { any() }
|
||||
}
|
||||
class CcNoCall = CallContextNoCall;
|
||||
|
||||
class CcNoCall extends Cc {
|
||||
CcNoCall() { this = false }
|
||||
}
|
||||
|
||||
Cc ccNone() { result = false }
|
||||
Cc ccNone() { result instanceof CallContextAny }
|
||||
|
||||
private class LocalCc = Unit;
|
||||
|
||||
bindingset[call, c, outercc]
|
||||
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
|
||||
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
|
||||
checkCallContextCall(outercc, call, c) and
|
||||
if recordDataFlowCallSiteDispatch(call, c)
|
||||
then result = TSpecificCall(call)
|
||||
else result = TSomeCall()
|
||||
}
|
||||
|
||||
bindingset[call, c, innercc]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
|
||||
checkCallContextReturn(innercc, c, call) and
|
||||
if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
|
||||
}
|
||||
|
||||
bindingset[node, cc, config]
|
||||
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
|
||||
@@ -1172,7 +1179,8 @@ private module Stage2 {
|
||||
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
|
||||
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
|
||||
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
@@ -1392,8 +1400,12 @@ private module Stage2 {
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1860,7 +1872,8 @@ private module Stage3 {
|
||||
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
|
||||
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
|
||||
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
@@ -2080,8 +2093,12 @@ private module Stage3 {
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2117,7 +2134,7 @@ private module Stage3 {
|
||||
private predicate flowCandSummaryCtx(NodeEx node, AccessPathFront argApf, Configuration config) {
|
||||
exists(AccessPathFront apf |
|
||||
Stage3::revFlow(node, true, _, apf, config) and
|
||||
Stage3::fwdFlow(node, true, TAccessPathFrontSome(argApf), apf, config)
|
||||
Stage3::fwdFlow(node, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2136,7 +2153,8 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
|
||||
) and
|
||||
accessPathApproxCostLimits(apLimit, tupleLimit) and
|
||||
apLimit < tails and
|
||||
tupleLimit < (tails - 1) * nodes
|
||||
tupleLimit < (tails - 1) * nodes and
|
||||
not tc.forceHighPrecision()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2618,7 +2636,8 @@ private module Stage4 {
|
||||
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
|
||||
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
|
||||
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
@@ -2838,8 +2857,12 @@ private module Stage4 {
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2912,6 +2935,8 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome {
|
||||
|
||||
int getParameterPos() { p.isParameterOf(_, result) }
|
||||
|
||||
ParamNodeEx getParamNode() { result = p }
|
||||
|
||||
override string toString() { result = p + ": " + ap }
|
||||
|
||||
predicate hasLocationInfo(
|
||||
@@ -2969,12 +2994,15 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) {
|
||||
* expected to be expensive. Holds with `unfold = true` otherwise.
|
||||
*/
|
||||
private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) {
|
||||
exists(int aps, int nodes, int apLimit, int tupleLimit |
|
||||
aps = countPotentialAps(apa, config) and
|
||||
nodes = countNodesUsingAccessPath(apa, config) and
|
||||
accessPathCostLimits(apLimit, tupleLimit) and
|
||||
if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true
|
||||
)
|
||||
if apa.getHead().forceHighPrecision()
|
||||
then unfold = true
|
||||
else
|
||||
exists(int aps, int nodes, int apLimit, int tupleLimit |
|
||||
aps = countPotentialAps(apa, config) and
|
||||
nodes = countNodesUsingAccessPath(apa, config) and
|
||||
accessPathCostLimits(apLimit, tupleLimit) and
|
||||
if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3162,7 +3190,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons {
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "[" + this.toStringImpl(true) + length().toString() + ")]"
|
||||
result = "[" + this.toStringImpl(true) + this.length().toString() + ")]"
|
||||
or
|
||||
result = "[" + this.toStringImpl(false)
|
||||
}
|
||||
@@ -3244,7 +3272,7 @@ class PathNode extends TPathNode {
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
|
||||
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
@@ -3301,9 +3329,11 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">"
|
||||
}
|
||||
|
||||
override string toString() { result = this.getNodeEx().toString() + ppAp() }
|
||||
override string toString() { result = this.getNodeEx().toString() + this.ppAp() }
|
||||
|
||||
override string toStringWithContext() { result = this.getNodeEx().toString() + ppAp() + ppCtx() }
|
||||
override string toStringWithContext() {
|
||||
result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx()
|
||||
}
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
@@ -3320,8 +3350,8 @@ private predicate directReach(PathNode n) {
|
||||
/** Holds if `n` can reach a sink or is used in a subpath. */
|
||||
private predicate reach(PathNode n) { directReach(n) or Subpaths::retReach(n) }
|
||||
|
||||
/** Holds if `n1.getSucc() = n2` and `n2` can reach a sink or is used in a subpath. */
|
||||
private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and reach(n2) }
|
||||
/** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */
|
||||
private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and directReach(n2) }
|
||||
|
||||
private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2)
|
||||
|
||||
@@ -3330,7 +3360,7 @@ private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1
|
||||
*/
|
||||
module PathGraph {
|
||||
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
|
||||
query predicate edges(PathNode a, PathNode b) { pathSucc(a, b) }
|
||||
query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b and reach(b) }
|
||||
|
||||
/** Holds if `n` is a node in the graph of data flow path explanations. */
|
||||
query predicate nodes(PathNode n, string key, string val) {
|
||||
@@ -3371,11 +3401,11 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
|
||||
|
||||
override PathNodeImpl getASuccessorImpl() {
|
||||
// an intermediate step to another intermediate node
|
||||
result = getSuccMid()
|
||||
result = this.getSuccMid()
|
||||
or
|
||||
// a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
|
||||
exists(PathNodeMid mid, PathNodeSink sink |
|
||||
mid = getSuccMid() and
|
||||
mid = this.getSuccMid() and
|
||||
mid.getNodeEx() = sink.getNodeEx() and
|
||||
mid.getAp() instanceof AccessPathNil and
|
||||
sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
|
||||
@@ -3452,7 +3482,7 @@ private predicate pathStep(
|
||||
exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and
|
||||
sc = mid.getSummaryCtx()
|
||||
or
|
||||
pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp()
|
||||
pathIntoCallable(mid, node, _, cc, sc, _, _) and ap = mid.getAp()
|
||||
or
|
||||
pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone
|
||||
or
|
||||
@@ -3529,14 +3559,16 @@ private predicate pathOutOfCallable(PathNodeMid mid, NodeEx out, CallContext cc)
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate pathIntoArg(
|
||||
PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa
|
||||
PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ArgNode arg |
|
||||
arg = mid.getNodeEx().asNode() and
|
||||
cc = mid.getCallContext() and
|
||||
arg.argumentOf(call, i) and
|
||||
ap = mid.getAp() and
|
||||
apa = ap.getApprox()
|
||||
apa = ap.getApprox() and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3553,12 +3585,14 @@ private predicate parameterCand(
|
||||
pragma[nomagic]
|
||||
private predicate pathIntoCallable0(
|
||||
PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call,
|
||||
AccessPath ap
|
||||
AccessPath ap, Configuration config
|
||||
) {
|
||||
exists(AccessPathApprox apa |
|
||||
pathIntoArg(mid, i, outercc, call, ap, apa) and
|
||||
pathIntoArg(mid, pragma[only_bind_into](i), outercc, call, ap, pragma[only_bind_into](apa),
|
||||
pragma[only_bind_into](config)) and
|
||||
callable = resolveCall(call, outercc) and
|
||||
parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration())
|
||||
parameterCand(callable, pragma[only_bind_into](i), pragma[only_bind_into](apa),
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3567,12 +3601,13 @@ private predicate pathIntoCallable0(
|
||||
* before and after entering the callable are `outercc` and `innercc`,
|
||||
* respectively.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate pathIntoCallable(
|
||||
PathNodeMid mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc, SummaryCtx sc,
|
||||
DataFlowCall call
|
||||
DataFlowCall call, Configuration config
|
||||
) {
|
||||
exists(int i, DataFlowCallable callable, AccessPath ap |
|
||||
pathIntoCallable0(mid, callable, i, outercc, call, ap) and
|
||||
pathIntoCallable0(mid, callable, i, outercc, call, ap, config) and
|
||||
p.isParameterOf(callable, i) and
|
||||
(
|
||||
sc = TSummaryCtxSome(p, ap)
|
||||
@@ -3602,18 +3637,23 @@ private predicate paramFlowsThrough(
|
||||
ap = mid.getAp() and
|
||||
apa = ap.getApprox() and
|
||||
pos = sc.getParameterPos() and
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
sc.getParamNode().allowParameterReturnInSelf()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate pathThroughCallable0(
|
||||
DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap,
|
||||
AccessPathApprox apa
|
||||
AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(CallContext innercc, SummaryCtx sc |
|
||||
pathIntoCallable(mid, _, cc, innercc, sc, call) and
|
||||
paramFlowsThrough(kind, innercc, sc, ap, apa, unbindConf(mid.getConfiguration()))
|
||||
pathIntoCallable(mid, _, cc, innercc, sc, call, config) and
|
||||
paramFlowsThrough(kind, innercc, sc, ap, apa, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3623,9 +3663,9 @@ private predicate pathThroughCallable0(
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext cc, AccessPath ap) {
|
||||
exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa |
|
||||
pathThroughCallable0(call, mid, kind, cc, ap, apa) and
|
||||
out = getAnOutNodeFlow(kind, call, apa, unbindConf(mid.getConfiguration()))
|
||||
exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa, Configuration config |
|
||||
pathThroughCallable0(call, mid, kind, cc, ap, apa, config) and
|
||||
out = getAnOutNodeFlow(kind, call, apa, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3639,9 +3679,11 @@ private module Subpaths {
|
||||
PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
|
||||
NodeEx out, AccessPath apout
|
||||
) {
|
||||
pathThroughCallable(arg, out, _, apout) and
|
||||
pathIntoCallable(arg, par, _, innercc, sc, _) and
|
||||
paramFlowsThrough(kind, innercc, sc, apout, _, unbindConf(arg.getConfiguration()))
|
||||
exists(Configuration config |
|
||||
pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and
|
||||
pathIntoCallable(arg, par, _, innercc, sc, _, config) and
|
||||
paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, unbindConf(config))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3686,8 +3728,8 @@ private module Subpaths {
|
||||
*/
|
||||
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeMid ret, PathNodeMid out) {
|
||||
exists(ParamNodeEx p, NodeEx o, AccessPath apout |
|
||||
arg.getASuccessor() = par and
|
||||
arg.getASuccessor() = out and
|
||||
pragma[only_bind_into](arg).getASuccessor() = par and
|
||||
pragma[only_bind_into](arg).getASuccessor() = out and
|
||||
subpaths03(arg, p, ret, o, apout) and
|
||||
par.getNodeEx() = p and
|
||||
out.getNodeEx() = o and
|
||||
@@ -4028,7 +4070,7 @@ private module FlowExploration {
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
|
||||
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
|
||||
@@ -110,12 +110,12 @@ abstract class Configuration extends string {
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlowTo(Node sink) { hasFlow(_, sink) }
|
||||
predicate hasFlowTo(Node sink) { this.hasFlow(_, sink) }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlowToExpr(DataFlowExpr sink) { hasFlowTo(exprNode(sink)) }
|
||||
predicate hasFlowToExpr(DataFlowExpr sink) { this.hasFlowTo(exprNode(sink)) }
|
||||
|
||||
/**
|
||||
* Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev`
|
||||
@@ -244,6 +244,8 @@ private class ParamNodeEx extends NodeEx {
|
||||
}
|
||||
|
||||
int getPosition() { this.isParameterOf(_, result) }
|
||||
|
||||
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
|
||||
}
|
||||
|
||||
private class RetNodeEx extends NodeEx {
|
||||
@@ -744,8 +746,12 @@ private module Stage1 {
|
||||
returnFlowCallableNodeCand(c, kind, config) and
|
||||
p.getEnclosingCallable() = c and
|
||||
exists(ap) and
|
||||
// we don't expect a parameter to return stored in itself
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -923,28 +929,29 @@ private module Stage2 {
|
||||
|
||||
ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
|
||||
|
||||
class Cc = boolean;
|
||||
class Cc = CallContext;
|
||||
|
||||
class CcCall extends Cc {
|
||||
CcCall() { this = true }
|
||||
class CcCall = CallContextCall;
|
||||
|
||||
/** Holds if this call context may be `call`. */
|
||||
predicate matchesCall(DataFlowCall call) { any() }
|
||||
}
|
||||
class CcNoCall = CallContextNoCall;
|
||||
|
||||
class CcNoCall extends Cc {
|
||||
CcNoCall() { this = false }
|
||||
}
|
||||
|
||||
Cc ccNone() { result = false }
|
||||
Cc ccNone() { result instanceof CallContextAny }
|
||||
|
||||
private class LocalCc = Unit;
|
||||
|
||||
bindingset[call, c, outercc]
|
||||
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
|
||||
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
|
||||
checkCallContextCall(outercc, call, c) and
|
||||
if recordDataFlowCallSiteDispatch(call, c)
|
||||
then result = TSpecificCall(call)
|
||||
else result = TSomeCall()
|
||||
}
|
||||
|
||||
bindingset[call, c, innercc]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
|
||||
checkCallContextReturn(innercc, c, call) and
|
||||
if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
|
||||
}
|
||||
|
||||
bindingset[node, cc, config]
|
||||
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
|
||||
@@ -1172,7 +1179,8 @@ private module Stage2 {
|
||||
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
|
||||
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
|
||||
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
@@ -1392,8 +1400,12 @@ private module Stage2 {
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1860,7 +1872,8 @@ private module Stage3 {
|
||||
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
|
||||
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
|
||||
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
@@ -2080,8 +2093,12 @@ private module Stage3 {
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2117,7 +2134,7 @@ private module Stage3 {
|
||||
private predicate flowCandSummaryCtx(NodeEx node, AccessPathFront argApf, Configuration config) {
|
||||
exists(AccessPathFront apf |
|
||||
Stage3::revFlow(node, true, _, apf, config) and
|
||||
Stage3::fwdFlow(node, true, TAccessPathFrontSome(argApf), apf, config)
|
||||
Stage3::fwdFlow(node, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2136,7 +2153,8 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
|
||||
) and
|
||||
accessPathApproxCostLimits(apLimit, tupleLimit) and
|
||||
apLimit < tails and
|
||||
tupleLimit < (tails - 1) * nodes
|
||||
tupleLimit < (tails - 1) * nodes and
|
||||
not tc.forceHighPrecision()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2618,7 +2636,8 @@ private module Stage4 {
|
||||
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
|
||||
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
|
||||
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
@@ -2838,8 +2857,12 @@ private module Stage4 {
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2912,6 +2935,8 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome {
|
||||
|
||||
int getParameterPos() { p.isParameterOf(_, result) }
|
||||
|
||||
ParamNodeEx getParamNode() { result = p }
|
||||
|
||||
override string toString() { result = p + ": " + ap }
|
||||
|
||||
predicate hasLocationInfo(
|
||||
@@ -2969,12 +2994,15 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) {
|
||||
* expected to be expensive. Holds with `unfold = true` otherwise.
|
||||
*/
|
||||
private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) {
|
||||
exists(int aps, int nodes, int apLimit, int tupleLimit |
|
||||
aps = countPotentialAps(apa, config) and
|
||||
nodes = countNodesUsingAccessPath(apa, config) and
|
||||
accessPathCostLimits(apLimit, tupleLimit) and
|
||||
if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true
|
||||
)
|
||||
if apa.getHead().forceHighPrecision()
|
||||
then unfold = true
|
||||
else
|
||||
exists(int aps, int nodes, int apLimit, int tupleLimit |
|
||||
aps = countPotentialAps(apa, config) and
|
||||
nodes = countNodesUsingAccessPath(apa, config) and
|
||||
accessPathCostLimits(apLimit, tupleLimit) and
|
||||
if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3162,7 +3190,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons {
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "[" + this.toStringImpl(true) + length().toString() + ")]"
|
||||
result = "[" + this.toStringImpl(true) + this.length().toString() + ")]"
|
||||
or
|
||||
result = "[" + this.toStringImpl(false)
|
||||
}
|
||||
@@ -3244,7 +3272,7 @@ class PathNode extends TPathNode {
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
|
||||
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
@@ -3301,9 +3329,11 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">"
|
||||
}
|
||||
|
||||
override string toString() { result = this.getNodeEx().toString() + ppAp() }
|
||||
override string toString() { result = this.getNodeEx().toString() + this.ppAp() }
|
||||
|
||||
override string toStringWithContext() { result = this.getNodeEx().toString() + ppAp() + ppCtx() }
|
||||
override string toStringWithContext() {
|
||||
result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx()
|
||||
}
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
@@ -3320,8 +3350,8 @@ private predicate directReach(PathNode n) {
|
||||
/** Holds if `n` can reach a sink or is used in a subpath. */
|
||||
private predicate reach(PathNode n) { directReach(n) or Subpaths::retReach(n) }
|
||||
|
||||
/** Holds if `n1.getSucc() = n2` and `n2` can reach a sink or is used in a subpath. */
|
||||
private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and reach(n2) }
|
||||
/** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */
|
||||
private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and directReach(n2) }
|
||||
|
||||
private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2)
|
||||
|
||||
@@ -3330,7 +3360,7 @@ private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1
|
||||
*/
|
||||
module PathGraph {
|
||||
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
|
||||
query predicate edges(PathNode a, PathNode b) { pathSucc(a, b) }
|
||||
query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b and reach(b) }
|
||||
|
||||
/** Holds if `n` is a node in the graph of data flow path explanations. */
|
||||
query predicate nodes(PathNode n, string key, string val) {
|
||||
@@ -3371,11 +3401,11 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
|
||||
|
||||
override PathNodeImpl getASuccessorImpl() {
|
||||
// an intermediate step to another intermediate node
|
||||
result = getSuccMid()
|
||||
result = this.getSuccMid()
|
||||
or
|
||||
// a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
|
||||
exists(PathNodeMid mid, PathNodeSink sink |
|
||||
mid = getSuccMid() and
|
||||
mid = this.getSuccMid() and
|
||||
mid.getNodeEx() = sink.getNodeEx() and
|
||||
mid.getAp() instanceof AccessPathNil and
|
||||
sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
|
||||
@@ -3452,7 +3482,7 @@ private predicate pathStep(
|
||||
exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and
|
||||
sc = mid.getSummaryCtx()
|
||||
or
|
||||
pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp()
|
||||
pathIntoCallable(mid, node, _, cc, sc, _, _) and ap = mid.getAp()
|
||||
or
|
||||
pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone
|
||||
or
|
||||
@@ -3529,14 +3559,16 @@ private predicate pathOutOfCallable(PathNodeMid mid, NodeEx out, CallContext cc)
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate pathIntoArg(
|
||||
PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa
|
||||
PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ArgNode arg |
|
||||
arg = mid.getNodeEx().asNode() and
|
||||
cc = mid.getCallContext() and
|
||||
arg.argumentOf(call, i) and
|
||||
ap = mid.getAp() and
|
||||
apa = ap.getApprox()
|
||||
apa = ap.getApprox() and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3553,12 +3585,14 @@ private predicate parameterCand(
|
||||
pragma[nomagic]
|
||||
private predicate pathIntoCallable0(
|
||||
PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call,
|
||||
AccessPath ap
|
||||
AccessPath ap, Configuration config
|
||||
) {
|
||||
exists(AccessPathApprox apa |
|
||||
pathIntoArg(mid, i, outercc, call, ap, apa) and
|
||||
pathIntoArg(mid, pragma[only_bind_into](i), outercc, call, ap, pragma[only_bind_into](apa),
|
||||
pragma[only_bind_into](config)) and
|
||||
callable = resolveCall(call, outercc) and
|
||||
parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration())
|
||||
parameterCand(callable, pragma[only_bind_into](i), pragma[only_bind_into](apa),
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3567,12 +3601,13 @@ private predicate pathIntoCallable0(
|
||||
* before and after entering the callable are `outercc` and `innercc`,
|
||||
* respectively.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate pathIntoCallable(
|
||||
PathNodeMid mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc, SummaryCtx sc,
|
||||
DataFlowCall call
|
||||
DataFlowCall call, Configuration config
|
||||
) {
|
||||
exists(int i, DataFlowCallable callable, AccessPath ap |
|
||||
pathIntoCallable0(mid, callable, i, outercc, call, ap) and
|
||||
pathIntoCallable0(mid, callable, i, outercc, call, ap, config) and
|
||||
p.isParameterOf(callable, i) and
|
||||
(
|
||||
sc = TSummaryCtxSome(p, ap)
|
||||
@@ -3602,18 +3637,23 @@ private predicate paramFlowsThrough(
|
||||
ap = mid.getAp() and
|
||||
apa = ap.getApprox() and
|
||||
pos = sc.getParameterPos() and
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
sc.getParamNode().allowParameterReturnInSelf()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate pathThroughCallable0(
|
||||
DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap,
|
||||
AccessPathApprox apa
|
||||
AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(CallContext innercc, SummaryCtx sc |
|
||||
pathIntoCallable(mid, _, cc, innercc, sc, call) and
|
||||
paramFlowsThrough(kind, innercc, sc, ap, apa, unbindConf(mid.getConfiguration()))
|
||||
pathIntoCallable(mid, _, cc, innercc, sc, call, config) and
|
||||
paramFlowsThrough(kind, innercc, sc, ap, apa, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3623,9 +3663,9 @@ private predicate pathThroughCallable0(
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext cc, AccessPath ap) {
|
||||
exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa |
|
||||
pathThroughCallable0(call, mid, kind, cc, ap, apa) and
|
||||
out = getAnOutNodeFlow(kind, call, apa, unbindConf(mid.getConfiguration()))
|
||||
exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa, Configuration config |
|
||||
pathThroughCallable0(call, mid, kind, cc, ap, apa, config) and
|
||||
out = getAnOutNodeFlow(kind, call, apa, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3639,9 +3679,11 @@ private module Subpaths {
|
||||
PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
|
||||
NodeEx out, AccessPath apout
|
||||
) {
|
||||
pathThroughCallable(arg, out, _, apout) and
|
||||
pathIntoCallable(arg, par, _, innercc, sc, _) and
|
||||
paramFlowsThrough(kind, innercc, sc, apout, _, unbindConf(arg.getConfiguration()))
|
||||
exists(Configuration config |
|
||||
pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and
|
||||
pathIntoCallable(arg, par, _, innercc, sc, _, config) and
|
||||
paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, unbindConf(config))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3686,8 +3728,8 @@ private module Subpaths {
|
||||
*/
|
||||
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeMid ret, PathNodeMid out) {
|
||||
exists(ParamNodeEx p, NodeEx o, AccessPath apout |
|
||||
arg.getASuccessor() = par and
|
||||
arg.getASuccessor() = out and
|
||||
pragma[only_bind_into](arg).getASuccessor() = par and
|
||||
pragma[only_bind_into](arg).getASuccessor() = out and
|
||||
subpaths03(arg, p, ret, o, apout) and
|
||||
par.getNodeEx() = p and
|
||||
out.getNodeEx() = o and
|
||||
@@ -4028,7 +4070,7 @@ private module FlowExploration {
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
|
||||
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
|
||||
@@ -786,16 +786,24 @@ private module Cached {
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the call context `call` either improves virtual dispatch in
|
||||
* `callable` or if it allows us to prune unreachable nodes in `callable`.
|
||||
* Holds if the call context `call` improves virtual dispatch in `callable`.
|
||||
*/
|
||||
cached
|
||||
predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) {
|
||||
predicate recordDataFlowCallSiteDispatch(DataFlowCall call, DataFlowCallable callable) {
|
||||
reducedViableImplInCallContext(_, callable, call)
|
||||
or
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the call context `call` allows us to prune unreachable nodes in `callable`.
|
||||
*/
|
||||
cached
|
||||
predicate recordDataFlowCallSiteUnreachable(DataFlowCall call, DataFlowCallable callable) {
|
||||
exists(Node n | getNodeEnclosingCallable(n) = callable | isUnreachableInCallCached(n, call))
|
||||
}
|
||||
|
||||
cached
|
||||
predicate allowParameterReturnInSelfCached(ParamNode p) { allowParameterReturnInSelf(p) }
|
||||
|
||||
cached
|
||||
newtype TCallContext =
|
||||
TAnyCallContext() or
|
||||
@@ -846,6 +854,15 @@ private module Cached {
|
||||
TAccessPathFrontSome(AccessPathFront apf)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the call context `call` either improves virtual dispatch in
|
||||
* `callable` or if it allows us to prune unreachable nodes in `callable`.
|
||||
*/
|
||||
predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) {
|
||||
recordDataFlowCallSiteDispatch(call, callable) or
|
||||
recordDataFlowCallSiteUnreachable(call, callable)
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Node` at which a cast can occur such that the type should be checked.
|
||||
*/
|
||||
@@ -923,7 +940,7 @@ class CallContextSpecificCall extends CallContextCall, TSpecificCall {
|
||||
}
|
||||
|
||||
override predicate relevantFor(DataFlowCallable callable) {
|
||||
recordDataFlowCallSite(getCall(), callable)
|
||||
recordDataFlowCallSite(this.getCall(), callable)
|
||||
}
|
||||
|
||||
override predicate matchesCall(DataFlowCall call) { call = this.getCall() }
|
||||
@@ -1222,6 +1239,13 @@ class TypedContent extends MkTypedContent {
|
||||
|
||||
/** Gets a textual representation of this content. */
|
||||
string toString() { result = c.toString() }
|
||||
|
||||
/**
|
||||
* Holds if access paths with this `TypedContent` at their head always should
|
||||
* be tracked at high precision. This disables adaptive access path precision
|
||||
* for such access paths.
|
||||
*/
|
||||
predicate forceHighPrecision() { forceHighPrecision(c) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1236,7 +1260,7 @@ abstract class AccessPathFront extends TAccessPathFront {
|
||||
|
||||
TypedContent getHead() { this = TFrontHead(result) }
|
||||
|
||||
predicate isClearedAt(Node n) { clearsContentCached(n, getHead().getContent()) }
|
||||
predicate isClearedAt(Node n) { clearsContentCached(n, this.getHead().getContent()) }
|
||||
}
|
||||
|
||||
class AccessPathFrontNil extends AccessPathFront, TFrontNil {
|
||||
|
||||
@@ -175,6 +175,7 @@ module Consistency {
|
||||
|
||||
query predicate postWithInFlow(Node n, string msg) {
|
||||
isPostUpdateNode(n) and
|
||||
not clearsContent(n, _) and
|
||||
simpleLocalFlowStep(_, n) and
|
||||
msg = "PostUpdateNode should not be the target of local flow."
|
||||
}
|
||||
|
||||
@@ -110,12 +110,12 @@ abstract class Configuration extends string {
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlowTo(Node sink) { hasFlow(_, sink) }
|
||||
predicate hasFlowTo(Node sink) { this.hasFlow(_, sink) }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlowToExpr(DataFlowExpr sink) { hasFlowTo(exprNode(sink)) }
|
||||
predicate hasFlowToExpr(DataFlowExpr sink) { this.hasFlowTo(exprNode(sink)) }
|
||||
|
||||
/**
|
||||
* Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev`
|
||||
@@ -244,6 +244,8 @@ private class ParamNodeEx extends NodeEx {
|
||||
}
|
||||
|
||||
int getPosition() { this.isParameterOf(_, result) }
|
||||
|
||||
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
|
||||
}
|
||||
|
||||
private class RetNodeEx extends NodeEx {
|
||||
@@ -744,8 +746,12 @@ private module Stage1 {
|
||||
returnFlowCallableNodeCand(c, kind, config) and
|
||||
p.getEnclosingCallable() = c and
|
||||
exists(ap) and
|
||||
// we don't expect a parameter to return stored in itself
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -923,28 +929,29 @@ private module Stage2 {
|
||||
|
||||
ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
|
||||
|
||||
class Cc = boolean;
|
||||
class Cc = CallContext;
|
||||
|
||||
class CcCall extends Cc {
|
||||
CcCall() { this = true }
|
||||
class CcCall = CallContextCall;
|
||||
|
||||
/** Holds if this call context may be `call`. */
|
||||
predicate matchesCall(DataFlowCall call) { any() }
|
||||
}
|
||||
class CcNoCall = CallContextNoCall;
|
||||
|
||||
class CcNoCall extends Cc {
|
||||
CcNoCall() { this = false }
|
||||
}
|
||||
|
||||
Cc ccNone() { result = false }
|
||||
Cc ccNone() { result instanceof CallContextAny }
|
||||
|
||||
private class LocalCc = Unit;
|
||||
|
||||
bindingset[call, c, outercc]
|
||||
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
|
||||
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
|
||||
checkCallContextCall(outercc, call, c) and
|
||||
if recordDataFlowCallSiteDispatch(call, c)
|
||||
then result = TSpecificCall(call)
|
||||
else result = TSomeCall()
|
||||
}
|
||||
|
||||
bindingset[call, c, innercc]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
|
||||
checkCallContextReturn(innercc, c, call) and
|
||||
if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
|
||||
}
|
||||
|
||||
bindingset[node, cc, config]
|
||||
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
|
||||
@@ -1172,7 +1179,8 @@ private module Stage2 {
|
||||
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
|
||||
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
|
||||
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
@@ -1392,8 +1400,12 @@ private module Stage2 {
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1860,7 +1872,8 @@ private module Stage3 {
|
||||
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
|
||||
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
|
||||
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
@@ -2080,8 +2093,12 @@ private module Stage3 {
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2117,7 +2134,7 @@ private module Stage3 {
|
||||
private predicate flowCandSummaryCtx(NodeEx node, AccessPathFront argApf, Configuration config) {
|
||||
exists(AccessPathFront apf |
|
||||
Stage3::revFlow(node, true, _, apf, config) and
|
||||
Stage3::fwdFlow(node, true, TAccessPathFrontSome(argApf), apf, config)
|
||||
Stage3::fwdFlow(node, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2136,7 +2153,8 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
|
||||
) and
|
||||
accessPathApproxCostLimits(apLimit, tupleLimit) and
|
||||
apLimit < tails and
|
||||
tupleLimit < (tails - 1) * nodes
|
||||
tupleLimit < (tails - 1) * nodes and
|
||||
not tc.forceHighPrecision()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2618,7 +2636,8 @@ private module Stage4 {
|
||||
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
|
||||
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
|
||||
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
@@ -2838,8 +2857,12 @@ private module Stage4 {
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2912,6 +2935,8 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome {
|
||||
|
||||
int getParameterPos() { p.isParameterOf(_, result) }
|
||||
|
||||
ParamNodeEx getParamNode() { result = p }
|
||||
|
||||
override string toString() { result = p + ": " + ap }
|
||||
|
||||
predicate hasLocationInfo(
|
||||
@@ -2969,12 +2994,15 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) {
|
||||
* expected to be expensive. Holds with `unfold = true` otherwise.
|
||||
*/
|
||||
private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) {
|
||||
exists(int aps, int nodes, int apLimit, int tupleLimit |
|
||||
aps = countPotentialAps(apa, config) and
|
||||
nodes = countNodesUsingAccessPath(apa, config) and
|
||||
accessPathCostLimits(apLimit, tupleLimit) and
|
||||
if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true
|
||||
)
|
||||
if apa.getHead().forceHighPrecision()
|
||||
then unfold = true
|
||||
else
|
||||
exists(int aps, int nodes, int apLimit, int tupleLimit |
|
||||
aps = countPotentialAps(apa, config) and
|
||||
nodes = countNodesUsingAccessPath(apa, config) and
|
||||
accessPathCostLimits(apLimit, tupleLimit) and
|
||||
if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3162,7 +3190,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons {
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "[" + this.toStringImpl(true) + length().toString() + ")]"
|
||||
result = "[" + this.toStringImpl(true) + this.length().toString() + ")]"
|
||||
or
|
||||
result = "[" + this.toStringImpl(false)
|
||||
}
|
||||
@@ -3244,7 +3272,7 @@ class PathNode extends TPathNode {
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
|
||||
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
@@ -3301,9 +3329,11 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">"
|
||||
}
|
||||
|
||||
override string toString() { result = this.getNodeEx().toString() + ppAp() }
|
||||
override string toString() { result = this.getNodeEx().toString() + this.ppAp() }
|
||||
|
||||
override string toStringWithContext() { result = this.getNodeEx().toString() + ppAp() + ppCtx() }
|
||||
override string toStringWithContext() {
|
||||
result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx()
|
||||
}
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
@@ -3320,8 +3350,8 @@ private predicate directReach(PathNode n) {
|
||||
/** Holds if `n` can reach a sink or is used in a subpath. */
|
||||
private predicate reach(PathNode n) { directReach(n) or Subpaths::retReach(n) }
|
||||
|
||||
/** Holds if `n1.getSucc() = n2` and `n2` can reach a sink or is used in a subpath. */
|
||||
private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and reach(n2) }
|
||||
/** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */
|
||||
private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and directReach(n2) }
|
||||
|
||||
private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2)
|
||||
|
||||
@@ -3330,7 +3360,7 @@ private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1
|
||||
*/
|
||||
module PathGraph {
|
||||
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
|
||||
query predicate edges(PathNode a, PathNode b) { pathSucc(a, b) }
|
||||
query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b and reach(b) }
|
||||
|
||||
/** Holds if `n` is a node in the graph of data flow path explanations. */
|
||||
query predicate nodes(PathNode n, string key, string val) {
|
||||
@@ -3371,11 +3401,11 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
|
||||
|
||||
override PathNodeImpl getASuccessorImpl() {
|
||||
// an intermediate step to another intermediate node
|
||||
result = getSuccMid()
|
||||
result = this.getSuccMid()
|
||||
or
|
||||
// a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
|
||||
exists(PathNodeMid mid, PathNodeSink sink |
|
||||
mid = getSuccMid() and
|
||||
mid = this.getSuccMid() and
|
||||
mid.getNodeEx() = sink.getNodeEx() and
|
||||
mid.getAp() instanceof AccessPathNil and
|
||||
sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
|
||||
@@ -3452,7 +3482,7 @@ private predicate pathStep(
|
||||
exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and
|
||||
sc = mid.getSummaryCtx()
|
||||
or
|
||||
pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp()
|
||||
pathIntoCallable(mid, node, _, cc, sc, _, _) and ap = mid.getAp()
|
||||
or
|
||||
pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone
|
||||
or
|
||||
@@ -3529,14 +3559,16 @@ private predicate pathOutOfCallable(PathNodeMid mid, NodeEx out, CallContext cc)
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate pathIntoArg(
|
||||
PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa
|
||||
PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ArgNode arg |
|
||||
arg = mid.getNodeEx().asNode() and
|
||||
cc = mid.getCallContext() and
|
||||
arg.argumentOf(call, i) and
|
||||
ap = mid.getAp() and
|
||||
apa = ap.getApprox()
|
||||
apa = ap.getApprox() and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3553,12 +3585,14 @@ private predicate parameterCand(
|
||||
pragma[nomagic]
|
||||
private predicate pathIntoCallable0(
|
||||
PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call,
|
||||
AccessPath ap
|
||||
AccessPath ap, Configuration config
|
||||
) {
|
||||
exists(AccessPathApprox apa |
|
||||
pathIntoArg(mid, i, outercc, call, ap, apa) and
|
||||
pathIntoArg(mid, pragma[only_bind_into](i), outercc, call, ap, pragma[only_bind_into](apa),
|
||||
pragma[only_bind_into](config)) and
|
||||
callable = resolveCall(call, outercc) and
|
||||
parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration())
|
||||
parameterCand(callable, pragma[only_bind_into](i), pragma[only_bind_into](apa),
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3567,12 +3601,13 @@ private predicate pathIntoCallable0(
|
||||
* before and after entering the callable are `outercc` and `innercc`,
|
||||
* respectively.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate pathIntoCallable(
|
||||
PathNodeMid mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc, SummaryCtx sc,
|
||||
DataFlowCall call
|
||||
DataFlowCall call, Configuration config
|
||||
) {
|
||||
exists(int i, DataFlowCallable callable, AccessPath ap |
|
||||
pathIntoCallable0(mid, callable, i, outercc, call, ap) and
|
||||
pathIntoCallable0(mid, callable, i, outercc, call, ap, config) and
|
||||
p.isParameterOf(callable, i) and
|
||||
(
|
||||
sc = TSummaryCtxSome(p, ap)
|
||||
@@ -3602,18 +3637,23 @@ private predicate paramFlowsThrough(
|
||||
ap = mid.getAp() and
|
||||
apa = ap.getApprox() and
|
||||
pos = sc.getParameterPos() and
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
sc.getParamNode().allowParameterReturnInSelf()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate pathThroughCallable0(
|
||||
DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap,
|
||||
AccessPathApprox apa
|
||||
AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(CallContext innercc, SummaryCtx sc |
|
||||
pathIntoCallable(mid, _, cc, innercc, sc, call) and
|
||||
paramFlowsThrough(kind, innercc, sc, ap, apa, unbindConf(mid.getConfiguration()))
|
||||
pathIntoCallable(mid, _, cc, innercc, sc, call, config) and
|
||||
paramFlowsThrough(kind, innercc, sc, ap, apa, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3623,9 +3663,9 @@ private predicate pathThroughCallable0(
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext cc, AccessPath ap) {
|
||||
exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa |
|
||||
pathThroughCallable0(call, mid, kind, cc, ap, apa) and
|
||||
out = getAnOutNodeFlow(kind, call, apa, unbindConf(mid.getConfiguration()))
|
||||
exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa, Configuration config |
|
||||
pathThroughCallable0(call, mid, kind, cc, ap, apa, config) and
|
||||
out = getAnOutNodeFlow(kind, call, apa, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3639,9 +3679,11 @@ private module Subpaths {
|
||||
PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
|
||||
NodeEx out, AccessPath apout
|
||||
) {
|
||||
pathThroughCallable(arg, out, _, apout) and
|
||||
pathIntoCallable(arg, par, _, innercc, sc, _) and
|
||||
paramFlowsThrough(kind, innercc, sc, apout, _, unbindConf(arg.getConfiguration()))
|
||||
exists(Configuration config |
|
||||
pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and
|
||||
pathIntoCallable(arg, par, _, innercc, sc, _, config) and
|
||||
paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, unbindConf(config))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3686,8 +3728,8 @@ private module Subpaths {
|
||||
*/
|
||||
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeMid ret, PathNodeMid out) {
|
||||
exists(ParamNodeEx p, NodeEx o, AccessPath apout |
|
||||
arg.getASuccessor() = par and
|
||||
arg.getASuccessor() = out and
|
||||
pragma[only_bind_into](arg).getASuccessor() = par and
|
||||
pragma[only_bind_into](arg).getASuccessor() = out and
|
||||
subpaths03(arg, p, ret, o, apout) and
|
||||
par.getNodeEx() = p and
|
||||
out.getNodeEx() = o and
|
||||
@@ -4028,7 +4070,7 @@ private module FlowExploration {
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
|
||||
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
|
||||
@@ -219,15 +219,13 @@ class DataFlowExpr = Expr;
|
||||
class DataFlowType = Type;
|
||||
|
||||
/** A function call relevant for data flow. */
|
||||
class DataFlowCall extends Expr {
|
||||
DataFlowCall() { this instanceof Call }
|
||||
|
||||
class DataFlowCall extends Expr instanceof Call {
|
||||
/**
|
||||
* Gets the nth argument for this call.
|
||||
*
|
||||
* The range of `n` is from `0` to `getNumberOfArguments() - 1`.
|
||||
*/
|
||||
Expr getArgument(int n) { result = this.(Call).getArgument(n) }
|
||||
Expr getArgument(int n) { result = super.getArgument(n) }
|
||||
|
||||
/** Gets the data flow node corresponding to this call. */
|
||||
ExprNode getNode() { result.getExpr() = this }
|
||||
@@ -240,6 +238,12 @@ predicate isUnreachableInCall(Node n, DataFlowCall call) { none() } // stub impl
|
||||
|
||||
int accessPathLimit() { result = 5 }
|
||||
|
||||
/**
|
||||
* Holds if access paths with `c` at their head always should be tracked at high
|
||||
* precision. This disables adaptive access path precision for such access paths.
|
||||
*/
|
||||
predicate forceHighPrecision(Content c) { none() }
|
||||
|
||||
/** The unit type. */
|
||||
private newtype TUnit = TMkUnit()
|
||||
|
||||
@@ -283,3 +287,12 @@ predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) { no
|
||||
|
||||
/** Extra data-flow steps needed for lambda flow analysis. */
|
||||
predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preservesValue) { none() }
|
||||
|
||||
/**
|
||||
* Holds if flow is allowed to pass from parameter `p` and back to itself as a
|
||||
* side-effect, resulting in a summary from `p` to itself.
|
||||
*
|
||||
* One example would be to allow flow like `p.foo = p.bar;`, which is disallowed
|
||||
* by default as a heuristic.
|
||||
*/
|
||||
predicate allowParameterReturnInSelf(ParameterNode p) { none() }
|
||||
|
||||
@@ -101,18 +101,18 @@ class Node extends TNode {
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
|
||||
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an upper bound on the type of this node.
|
||||
*/
|
||||
Type getTypeBound() { result = getType() }
|
||||
Type getTypeBound() { result = this.getType() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -293,11 +293,11 @@ abstract class PostUpdateNode extends Node {
|
||||
*/
|
||||
abstract Node getPreUpdateNode();
|
||||
|
||||
override Function getFunction() { result = getPreUpdateNode().getFunction() }
|
||||
override Function getFunction() { result = this.getPreUpdateNode().getFunction() }
|
||||
|
||||
override Type getType() { result = getPreUpdateNode().getType() }
|
||||
override Type getType() { result = this.getPreUpdateNode().getType() }
|
||||
|
||||
override Location getLocation() { result = getPreUpdateNode().getLocation() }
|
||||
override Location getLocation() { result = this.getPreUpdateNode().getLocation() }
|
||||
}
|
||||
|
||||
abstract private class PartialDefinitionNode extends PostUpdateNode, TPartialDefinitionNode {
|
||||
@@ -309,7 +309,7 @@ abstract private class PartialDefinitionNode extends PostUpdateNode, TPartialDef
|
||||
|
||||
PartialDefinition getPartialDefinition() { result = pd }
|
||||
|
||||
override string toString() { result = getPreUpdateNode().toString() + " [post update]" }
|
||||
override string toString() { result = this.getPreUpdateNode().toString() + " [post update]" }
|
||||
}
|
||||
|
||||
private class VariablePartialDefinitionNode extends PartialDefinitionNode {
|
||||
@@ -380,13 +380,13 @@ private class ObjectInitializerNode extends PostUpdateNode, TExprNode {
|
||||
class PreObjectInitializerNode extends Node, TPreObjectInitializerNode {
|
||||
Expr getExpr() { this = TPreObjectInitializerNode(result) }
|
||||
|
||||
override Function getFunction() { result = getExpr().getEnclosingFunction() }
|
||||
override Function getFunction() { result = this.getExpr().getEnclosingFunction() }
|
||||
|
||||
override Type getType() { result = getExpr().getType() }
|
||||
override Type getType() { result = this.getExpr().getType() }
|
||||
|
||||
override Location getLocation() { result = getExpr().getLocation() }
|
||||
override Location getLocation() { result = this.getExpr().getLocation() }
|
||||
|
||||
override string toString() { result = getExpr().toString() + " [pre init]" }
|
||||
override string toString() { result = this.getExpr().toString() + " [pre init]" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -401,7 +401,7 @@ private class PostConstructorInitThis extends PostUpdateNode, TPostConstructorIn
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = getPreUpdateNode().getConstructorFieldInit().toString() + " [post-this]"
|
||||
result = this.getPreUpdateNode().getConstructorFieldInit().toString() + " [post-this]"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -416,15 +416,17 @@ private class PostConstructorInitThis extends PostUpdateNode, TPostConstructorIn
|
||||
class PreConstructorInitThis extends Node, TPreConstructorInitThis {
|
||||
ConstructorFieldInit getConstructorFieldInit() { this = TPreConstructorInitThis(result) }
|
||||
|
||||
override Constructor getFunction() { result = getConstructorFieldInit().getEnclosingFunction() }
|
||||
|
||||
override PointerType getType() {
|
||||
result.getBaseType() = getConstructorFieldInit().getEnclosingFunction().getDeclaringType()
|
||||
override Constructor getFunction() {
|
||||
result = this.getConstructorFieldInit().getEnclosingFunction()
|
||||
}
|
||||
|
||||
override Location getLocation() { result = getConstructorFieldInit().getLocation() }
|
||||
override PointerType getType() {
|
||||
result.getBaseType() = this.getConstructorFieldInit().getEnclosingFunction().getDeclaringType()
|
||||
}
|
||||
|
||||
override string toString() { result = getConstructorFieldInit().toString() + " [pre-this]" }
|
||||
override Location getLocation() { result = this.getConstructorFieldInit().getLocation() }
|
||||
|
||||
override string toString() { result = this.getConstructorFieldInit().toString() + " [pre-this]" }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -354,7 +354,7 @@ module FlowVar_internal {
|
||||
result = def.getAUse(v)
|
||||
or
|
||||
exists(SsaDefinition descendentDef |
|
||||
getASuccessorSsaVar+() = TSsaVar(descendentDef, _) and
|
||||
this.getASuccessorSsaVar+() = TSsaVar(descendentDef, _) and
|
||||
result = descendentDef.getAUse(v)
|
||||
)
|
||||
)
|
||||
@@ -515,7 +515,7 @@ module FlowVar_internal {
|
||||
this.bbInLoopCondition(bbInside) and
|
||||
not this.bbInLoop(bbOutside) and
|
||||
bbOutside = bbInside.getASuccessor() and
|
||||
not reachesWithoutAssignment(bbInside, v)
|
||||
not this.reachesWithoutAssignment(bbInside, v)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -546,7 +546,7 @@ module FlowVar_internal {
|
||||
private predicate bbInLoop(BasicBlock bb) {
|
||||
bbDominates(this.(Loop).getStmt(), bb)
|
||||
or
|
||||
bbInLoopCondition(bb)
|
||||
this.bbInLoopCondition(bb)
|
||||
}
|
||||
|
||||
/** Holds if `sbb` is inside this loop. */
|
||||
@@ -563,7 +563,7 @@ module FlowVar_internal {
|
||||
bb = this.(Loop).getStmt() and
|
||||
v = this.getARelevantVariable()
|
||||
or
|
||||
reachesWithoutAssignment(bb.getAPredecessor(), v) and
|
||||
this.reachesWithoutAssignment(bb.getAPredecessor(), v) and
|
||||
this.bbInLoop(bb)
|
||||
) and
|
||||
not assignsToVar(bb, v)
|
||||
|
||||
@@ -80,7 +80,7 @@ class SubBasicBlock extends ControlFlowNodeBase {
|
||||
* returns a 0-based position, while `getRankInBasicBlock` returns a 1-based
|
||||
* position.
|
||||
*/
|
||||
deprecated int getPosInBasicBlock(BasicBlock bb) { result = getRankInBasicBlock(bb) - 1 }
|
||||
deprecated int getPosInBasicBlock(BasicBlock bb) { result = this.getRankInBasicBlock(bb) - 1 }
|
||||
|
||||
pragma[noinline]
|
||||
private int getIndexInBasicBlock(BasicBlock bb) { this = bb.getNode(result) }
|
||||
@@ -102,7 +102,7 @@ class SubBasicBlock extends ControlFlowNodeBase {
|
||||
exists(BasicBlock bb |
|
||||
exists(int outerIndex |
|
||||
result = bb.getNode(outerIndex) and
|
||||
index = outerToInnerIndex(bb, outerIndex)
|
||||
index = this.outerToInnerIndex(bb, outerIndex)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -75,24 +75,26 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
predicate isSanitizer(DataFlow::Node node) { none() }
|
||||
|
||||
final override predicate isBarrier(DataFlow::Node node) {
|
||||
isSanitizer(node) or
|
||||
this.isSanitizer(node) or
|
||||
defaultTaintSanitizer(node)
|
||||
}
|
||||
|
||||
/** Holds if taint propagation into `node` is prohibited. */
|
||||
predicate isSanitizerIn(DataFlow::Node node) { none() }
|
||||
|
||||
final override predicate isBarrierIn(DataFlow::Node node) { isSanitizerIn(node) }
|
||||
final override predicate isBarrierIn(DataFlow::Node node) { this.isSanitizerIn(node) }
|
||||
|
||||
/** Holds if taint propagation out of `node` is prohibited. */
|
||||
predicate isSanitizerOut(DataFlow::Node node) { none() }
|
||||
|
||||
final override predicate isBarrierOut(DataFlow::Node node) { isSanitizerOut(node) }
|
||||
final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) }
|
||||
|
||||
/** Holds if taint propagation through nodes guarded by `guard` is prohibited. */
|
||||
predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
|
||||
|
||||
final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { isSanitizerGuard(guard) }
|
||||
final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
|
||||
this.isSanitizerGuard(guard)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the additional taint propagation step from `node1` to `node2`
|
||||
@@ -101,7 +103,7 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { none() }
|
||||
|
||||
final override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
isAdditionalTaintStep(node1, node2) or
|
||||
this.isAdditionalTaintStep(node1, node2) or
|
||||
defaultAdditionalTaintStep(node1, node2)
|
||||
}
|
||||
|
||||
|
||||
@@ -75,24 +75,26 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
predicate isSanitizer(DataFlow::Node node) { none() }
|
||||
|
||||
final override predicate isBarrier(DataFlow::Node node) {
|
||||
isSanitizer(node) or
|
||||
this.isSanitizer(node) or
|
||||
defaultTaintSanitizer(node)
|
||||
}
|
||||
|
||||
/** Holds if taint propagation into `node` is prohibited. */
|
||||
predicate isSanitizerIn(DataFlow::Node node) { none() }
|
||||
|
||||
final override predicate isBarrierIn(DataFlow::Node node) { isSanitizerIn(node) }
|
||||
final override predicate isBarrierIn(DataFlow::Node node) { this.isSanitizerIn(node) }
|
||||
|
||||
/** Holds if taint propagation out of `node` is prohibited. */
|
||||
predicate isSanitizerOut(DataFlow::Node node) { none() }
|
||||
|
||||
final override predicate isBarrierOut(DataFlow::Node node) { isSanitizerOut(node) }
|
||||
final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) }
|
||||
|
||||
/** Holds if taint propagation through nodes guarded by `guard` is prohibited. */
|
||||
predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
|
||||
|
||||
final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { isSanitizerGuard(guard) }
|
||||
final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
|
||||
this.isSanitizerGuard(guard)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the additional taint propagation step from `node1` to `node2`
|
||||
@@ -101,7 +103,7 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { none() }
|
||||
|
||||
final override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
isAdditionalTaintStep(node1, node2) or
|
||||
this.isAdditionalTaintStep(node1, node2) or
|
||||
defaultAdditionalTaintStep(node1, node2)
|
||||
}
|
||||
|
||||
|
||||
@@ -203,7 +203,7 @@ class PointerFieldAccess extends FieldAccess {
|
||||
|
||||
PointerFieldAccess() {
|
||||
exists(PointerType t |
|
||||
t = getQualifier().getFullyConverted().getUnspecifiedType() and
|
||||
t = this.getQualifier().getFullyConverted().getUnspecifiedType() and
|
||||
t.getBaseType() instanceof Class
|
||||
)
|
||||
}
|
||||
@@ -218,7 +218,9 @@ class PointerFieldAccess extends FieldAccess {
|
||||
class DotFieldAccess extends FieldAccess {
|
||||
override string getAPrimaryQlClass() { result = "DotFieldAccess" }
|
||||
|
||||
DotFieldAccess() { exists(Class c | c = getQualifier().getFullyConverted().getUnspecifiedType()) }
|
||||
DotFieldAccess() {
|
||||
exists(Class c | c = this.getQualifier().getFullyConverted().getUnspecifiedType())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -148,7 +148,7 @@ class PostfixIncrExpr extends IncrementOperation, PostfixCrementOperation, @post
|
||||
|
||||
override int getPrecedence() { result = 17 }
|
||||
|
||||
override string toString() { result = "... " + getOperator() }
|
||||
override string toString() { result = "... " + this.getOperator() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -166,7 +166,7 @@ class PostfixDecrExpr extends DecrementOperation, PostfixCrementOperation, @post
|
||||
|
||||
override int getPrecedence() { result = 17 }
|
||||
|
||||
override string toString() { result = "... " + getOperator() }
|
||||
override string toString() { result = "... " + this.getOperator() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -35,12 +35,12 @@ class BuiltInVarArgsStart extends VarArgsExpr, @vastartexpr {
|
||||
/**
|
||||
* Gets the `va_list` argument.
|
||||
*/
|
||||
final Expr getVAList() { result = getChild(0) }
|
||||
final Expr getVAList() { result = this.getChild(0) }
|
||||
|
||||
/**
|
||||
* Gets the argument that specifies the last named parameter before the ellipsis.
|
||||
*/
|
||||
final VariableAccess getLastNamedParameter() { result = getChild(1) }
|
||||
final VariableAccess getLastNamedParameter() { result = this.getChild(1) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -60,7 +60,7 @@ class BuiltInVarArgsEnd extends VarArgsExpr, @vaendexpr {
|
||||
/**
|
||||
* Gets the `va_list` argument.
|
||||
*/
|
||||
final Expr getVAList() { result = getChild(0) }
|
||||
final Expr getVAList() { result = this.getChild(0) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -78,7 +78,7 @@ class BuiltInVarArg extends VarArgsExpr, @vaargexpr {
|
||||
/**
|
||||
* Gets the `va_list` argument.
|
||||
*/
|
||||
final Expr getVAList() { result = getChild(0) }
|
||||
final Expr getVAList() { result = this.getChild(0) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -98,12 +98,12 @@ class BuiltInVarArgCopy extends VarArgsExpr, @vacopyexpr {
|
||||
/**
|
||||
* Gets the destination `va_list` argument.
|
||||
*/
|
||||
final Expr getDestinationVAList() { result = getChild(0) }
|
||||
final Expr getDestinationVAList() { result = this.getChild(0) }
|
||||
|
||||
/**
|
||||
* Gets the the source `va_list` argument.
|
||||
*/
|
||||
final Expr getSourceVAList() { result = getChild(1) }
|
||||
final Expr getSourceVAList() { result = this.getChild(1) }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -71,10 +71,10 @@ class Call extends Expr, NameQualifiableElement, TCall {
|
||||
* at index 2, respectively.
|
||||
*/
|
||||
Expr getAnArgumentSubExpr(int index) {
|
||||
result = getArgument(index)
|
||||
result = this.getArgument(index)
|
||||
or
|
||||
exists(Expr mid |
|
||||
mid = getAnArgumentSubExpr(index) and
|
||||
mid = this.getAnArgumentSubExpr(index) and
|
||||
not mid instanceof Call and
|
||||
not mid instanceof SizeofOperator and
|
||||
result = mid.getAChild()
|
||||
@@ -167,27 +167,27 @@ class FunctionCall extends Call, @funbindexpr {
|
||||
override string getAPrimaryQlClass() { result = "FunctionCall" }
|
||||
|
||||
/** Gets an explicit template argument for this call. */
|
||||
Locatable getAnExplicitTemplateArgument() { result = getExplicitTemplateArgument(_) }
|
||||
Locatable getAnExplicitTemplateArgument() { result = this.getExplicitTemplateArgument(_) }
|
||||
|
||||
/** Gets an explicit template argument value for this call. */
|
||||
Locatable getAnExplicitTemplateArgumentKind() { result = getExplicitTemplateArgumentKind(_) }
|
||||
Locatable getAnExplicitTemplateArgumentKind() { result = this.getExplicitTemplateArgumentKind(_) }
|
||||
|
||||
/** Gets a template argument for this call. */
|
||||
Locatable getATemplateArgument() { result = getTarget().getATemplateArgument() }
|
||||
Locatable getATemplateArgument() { result = this.getTarget().getATemplateArgument() }
|
||||
|
||||
/** Gets a template argument value for this call. */
|
||||
Locatable getATemplateArgumentKind() { result = getTarget().getATemplateArgumentKind() }
|
||||
Locatable getATemplateArgumentKind() { result = this.getTarget().getATemplateArgumentKind() }
|
||||
|
||||
/** Gets the nth explicit template argument for this call. */
|
||||
Locatable getExplicitTemplateArgument(int n) {
|
||||
n < getNumberOfExplicitTemplateArguments() and
|
||||
result = getTemplateArgument(n)
|
||||
n < this.getNumberOfExplicitTemplateArguments() and
|
||||
result = this.getTemplateArgument(n)
|
||||
}
|
||||
|
||||
/** Gets the nth explicit template argument value for this call. */
|
||||
Locatable getExplicitTemplateArgumentKind(int n) {
|
||||
n < getNumberOfExplicitTemplateArguments() and
|
||||
result = getTemplateArgumentKind(n)
|
||||
n < this.getNumberOfExplicitTemplateArguments() and
|
||||
result = this.getTemplateArgumentKind(n)
|
||||
}
|
||||
|
||||
/** Gets the number of explicit template arguments for this call. */
|
||||
@@ -198,19 +198,19 @@ class FunctionCall extends Call, @funbindexpr {
|
||||
}
|
||||
|
||||
/** Gets the number of template arguments for this call. */
|
||||
int getNumberOfTemplateArguments() { result = count(int i | exists(getTemplateArgument(i))) }
|
||||
int getNumberOfTemplateArguments() { result = count(int i | exists(this.getTemplateArgument(i))) }
|
||||
|
||||
/** Gets the nth template argument for this call (indexed from 0). */
|
||||
Locatable getTemplateArgument(int n) { result = getTarget().getTemplateArgument(n) }
|
||||
Locatable getTemplateArgument(int n) { result = this.getTarget().getTemplateArgument(n) }
|
||||
|
||||
/** Gets the nth template argument value for this call (indexed from 0). */
|
||||
Locatable getTemplateArgumentKind(int n) { result = getTarget().getTemplateArgumentKind(n) }
|
||||
Locatable getTemplateArgumentKind(int n) { result = this.getTarget().getTemplateArgumentKind(n) }
|
||||
|
||||
/** Holds if any template arguments for this call are implicit / deduced. */
|
||||
predicate hasImplicitTemplateArguments() {
|
||||
exists(int i |
|
||||
exists(getTemplateArgument(i)) and
|
||||
not exists(getExplicitTemplateArgument(i))
|
||||
exists(this.getTemplateArgument(i)) and
|
||||
not exists(this.getExplicitTemplateArgument(i))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -233,9 +233,9 @@ class FunctionCall extends Call, @funbindexpr {
|
||||
* visible at the call site.
|
||||
*/
|
||||
Type getExpectedReturnType() {
|
||||
if getTargetType() instanceof RoutineType
|
||||
then result = getTargetType().(RoutineType).getReturnType()
|
||||
else result = getTarget().getType()
|
||||
if this.getTargetType() instanceof RoutineType
|
||||
then result = this.getTargetType().(RoutineType).getReturnType()
|
||||
else result = this.getTarget().getType()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -247,9 +247,9 @@ class FunctionCall extends Call, @funbindexpr {
|
||||
* was visible at the call site.
|
||||
*/
|
||||
Type getExpectedParameterType(int n) {
|
||||
if getTargetType() instanceof RoutineType
|
||||
then result = getTargetType().(RoutineType).getParameterType(n)
|
||||
else result = getTarget().getParameter(n).getType()
|
||||
if this.getTargetType() instanceof RoutineType
|
||||
then result = this.getTargetType().(RoutineType).getParameterType(n)
|
||||
else result = this.getTarget().getParameter(n).getType()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -263,7 +263,7 @@ class FunctionCall extends Call, @funbindexpr {
|
||||
/**
|
||||
* Gets the type of this expression, that is, the return type of the function being called.
|
||||
*/
|
||||
override Type getType() { result = getExpectedReturnType() }
|
||||
override Type getType() { result = this.getExpectedReturnType() }
|
||||
|
||||
/**
|
||||
* Holds if this is a call to a virtual function.
|
||||
@@ -280,7 +280,7 @@ class FunctionCall extends Call, @funbindexpr {
|
||||
|
||||
/** Gets a textual representation of this function call. */
|
||||
override string toString() {
|
||||
if exists(getTarget())
|
||||
if exists(this.getTarget())
|
||||
then result = "call to " + this.getTarget().getName()
|
||||
else result = "call to unknown function"
|
||||
}
|
||||
@@ -288,15 +288,15 @@ class FunctionCall extends Call, @funbindexpr {
|
||||
override predicate mayBeImpure() {
|
||||
this.getChild(_).mayBeImpure() or
|
||||
this.getTarget().mayHaveSideEffects() or
|
||||
isVirtual() or
|
||||
getTarget().getAnAttribute().getName() = "weak"
|
||||
this.isVirtual() or
|
||||
this.getTarget().getAnAttribute().getName() = "weak"
|
||||
}
|
||||
|
||||
override predicate mayBeGloballyImpure() {
|
||||
this.getChild(_).mayBeGloballyImpure() or
|
||||
this.getTarget().mayHaveSideEffects() or
|
||||
isVirtual() or
|
||||
getTarget().getAnAttribute().getName() = "weak"
|
||||
this.isVirtual() or
|
||||
this.getTarget().getAnAttribute().getName() = "weak"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -367,7 +367,7 @@ class OverloadedPointerDereferenceExpr extends FunctionCall {
|
||||
* ```
|
||||
*/
|
||||
class OverloadedArrayExpr extends FunctionCall {
|
||||
OverloadedArrayExpr() { getTarget().hasName("operator[]") }
|
||||
OverloadedArrayExpr() { this.getTarget().hasName("operator[]") }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "OverloadedArrayExpr" }
|
||||
|
||||
@@ -585,7 +585,7 @@ class ConstructorFieldInit extends ConstructorInit, @ctorfieldinit {
|
||||
*/
|
||||
Expr getExpr() { result = this.getChild(0) }
|
||||
|
||||
override string toString() { result = "constructor init of field " + getTarget().getName() }
|
||||
override string toString() { result = "constructor init of field " + this.getTarget().getName() }
|
||||
|
||||
override predicate mayBeImpure() { this.getExpr().mayBeImpure() }
|
||||
|
||||
|
||||
@@ -188,8 +188,8 @@ private predicate isPointerToMemberOrNullPointer(Type type) {
|
||||
class ArithmeticConversion extends Cast {
|
||||
ArithmeticConversion() {
|
||||
conversionkinds(underlyingElement(this), 0) and
|
||||
isArithmeticOrEnum(getUnspecifiedType()) and
|
||||
isArithmeticOrEnum(getExpr().getUnspecifiedType())
|
||||
isArithmeticOrEnum(this.getUnspecifiedType()) and
|
||||
isArithmeticOrEnum(this.getExpr().getUnspecifiedType())
|
||||
}
|
||||
|
||||
override string getSemanticConversionString() { result = "arithmetic conversion" }
|
||||
@@ -204,8 +204,8 @@ class ArithmeticConversion extends Cast {
|
||||
*/
|
||||
class IntegralConversion extends ArithmeticConversion {
|
||||
IntegralConversion() {
|
||||
isIntegralOrEnum(getUnspecifiedType()) and
|
||||
isIntegralOrEnum(getExpr().getUnspecifiedType())
|
||||
isIntegralOrEnum(this.getUnspecifiedType()) and
|
||||
isIntegralOrEnum(this.getExpr().getUnspecifiedType())
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() {
|
||||
@@ -224,8 +224,8 @@ class IntegralConversion extends ArithmeticConversion {
|
||||
*/
|
||||
class FloatingPointConversion extends ArithmeticConversion {
|
||||
FloatingPointConversion() {
|
||||
getUnspecifiedType() instanceof FloatingPointType and
|
||||
getExpr().getUnspecifiedType() instanceof FloatingPointType
|
||||
this.getUnspecifiedType() instanceof FloatingPointType and
|
||||
this.getExpr().getUnspecifiedType() instanceof FloatingPointType
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() {
|
||||
@@ -244,8 +244,8 @@ class FloatingPointConversion extends ArithmeticConversion {
|
||||
*/
|
||||
class FloatingPointToIntegralConversion extends ArithmeticConversion {
|
||||
FloatingPointToIntegralConversion() {
|
||||
isIntegralOrEnum(getUnspecifiedType()) and
|
||||
getExpr().getUnspecifiedType() instanceof FloatingPointType
|
||||
isIntegralOrEnum(this.getUnspecifiedType()) and
|
||||
this.getExpr().getUnspecifiedType() instanceof FloatingPointType
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() {
|
||||
@@ -264,8 +264,8 @@ class FloatingPointToIntegralConversion extends ArithmeticConversion {
|
||||
*/
|
||||
class IntegralToFloatingPointConversion extends ArithmeticConversion {
|
||||
IntegralToFloatingPointConversion() {
|
||||
getUnspecifiedType() instanceof FloatingPointType and
|
||||
isIntegralOrEnum(getExpr().getUnspecifiedType())
|
||||
this.getUnspecifiedType() instanceof FloatingPointType and
|
||||
isIntegralOrEnum(this.getExpr().getUnspecifiedType())
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() {
|
||||
@@ -290,8 +290,8 @@ class IntegralToFloatingPointConversion extends ArithmeticConversion {
|
||||
class PointerConversion extends Cast {
|
||||
PointerConversion() {
|
||||
conversionkinds(underlyingElement(this), 0) and
|
||||
isPointerOrNullPointer(getUnspecifiedType()) and
|
||||
isPointerOrNullPointer(getExpr().getUnspecifiedType())
|
||||
isPointerOrNullPointer(this.getUnspecifiedType()) and
|
||||
isPointerOrNullPointer(this.getExpr().getUnspecifiedType())
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() { not exists(qlCast(this)) and result = "PointerConversion" }
|
||||
@@ -315,8 +315,8 @@ class PointerToMemberConversion extends Cast {
|
||||
PointerToMemberConversion() {
|
||||
conversionkinds(underlyingElement(this), 0) and
|
||||
exists(Type fromType, Type toType |
|
||||
fromType = getExpr().getUnspecifiedType() and
|
||||
toType = getUnspecifiedType() and
|
||||
fromType = this.getExpr().getUnspecifiedType() and
|
||||
toType = this.getUnspecifiedType() and
|
||||
isPointerToMemberOrNullPointer(fromType) and
|
||||
isPointerToMemberOrNullPointer(toType) and
|
||||
// A conversion from nullptr to nullptr is a `PointerConversion`, not a
|
||||
@@ -345,8 +345,8 @@ class PointerToMemberConversion extends Cast {
|
||||
class PointerToIntegralConversion extends Cast {
|
||||
PointerToIntegralConversion() {
|
||||
conversionkinds(underlyingElement(this), 0) and
|
||||
isIntegralOrEnum(getUnspecifiedType()) and
|
||||
isPointerOrNullPointer(getExpr().getUnspecifiedType())
|
||||
isIntegralOrEnum(this.getUnspecifiedType()) and
|
||||
isPointerOrNullPointer(this.getExpr().getUnspecifiedType())
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() {
|
||||
@@ -366,8 +366,8 @@ class PointerToIntegralConversion extends Cast {
|
||||
class IntegralToPointerConversion extends Cast {
|
||||
IntegralToPointerConversion() {
|
||||
conversionkinds(underlyingElement(this), 0) and
|
||||
isPointerOrNullPointer(getUnspecifiedType()) and
|
||||
isIntegralOrEnum(getExpr().getUnspecifiedType())
|
||||
isPointerOrNullPointer(this.getUnspecifiedType()) and
|
||||
isIntegralOrEnum(this.getExpr().getUnspecifiedType())
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() {
|
||||
@@ -403,7 +403,7 @@ class BoolConversion extends Cast {
|
||||
class VoidConversion extends Cast {
|
||||
VoidConversion() {
|
||||
conversionkinds(underlyingElement(this), 0) and
|
||||
getUnspecifiedType() instanceof VoidType
|
||||
this.getUnspecifiedType() instanceof VoidType
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() { not exists(qlCast(this)) and result = "VoidConversion" }
|
||||
@@ -434,8 +434,8 @@ class InheritanceConversion extends Cast {
|
||||
* conversion is to an indirect virtual base class.
|
||||
*/
|
||||
final ClassDerivation getDerivation() {
|
||||
result.getBaseClass() = getBaseClass() and
|
||||
result.getDerivedClass() = getDerivedClass()
|
||||
result.getBaseClass() = this.getBaseClass() and
|
||||
result.getDerivedClass() = this.getDerivedClass()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -490,12 +490,12 @@ class BaseClassConversion extends InheritanceConversion {
|
||||
|
||||
override Class getBaseClass() { result = getConversionClass(this) }
|
||||
|
||||
override Class getDerivedClass() { result = getConversionClass(getExpr()) }
|
||||
override Class getDerivedClass() { result = getConversionClass(this.getExpr()) }
|
||||
|
||||
/**
|
||||
* Holds if this conversion is to a virtual base class.
|
||||
*/
|
||||
predicate isVirtual() { getDerivation().isVirtual() or not exists(getDerivation()) }
|
||||
predicate isVirtual() { this.getDerivation().isVirtual() or not exists(this.getDerivation()) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -515,7 +515,7 @@ class DerivedClassConversion extends InheritanceConversion {
|
||||
|
||||
override string getSemanticConversionString() { result = "derived class conversion" }
|
||||
|
||||
override Class getBaseClass() { result = getConversionClass(getExpr()) }
|
||||
override Class getBaseClass() { result = getConversionClass(this.getExpr()) }
|
||||
|
||||
override Class getDerivedClass() { result = getConversionClass(this) }
|
||||
}
|
||||
@@ -637,8 +637,8 @@ class DynamicCast extends Cast, @dynamic_cast {
|
||||
*/
|
||||
class UuidofOperator extends Expr, @uuidof {
|
||||
override string toString() {
|
||||
if exists(getTypeOperand())
|
||||
then result = "__uuidof(" + getTypeOperand().getName() + ")"
|
||||
if exists(this.getTypeOperand())
|
||||
then result = "__uuidof(" + this.getTypeOperand().getName() + ")"
|
||||
else result = "__uuidof(0)"
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ class Expr extends StmtParent, @expr {
|
||||
Function getEnclosingFunction() { result = exprEnclosingElement(this) }
|
||||
|
||||
/** Gets the nearest enclosing set of curly braces around this expression in the source, if any. */
|
||||
BlockStmt getEnclosingBlock() { result = getEnclosingStmt().getEnclosingBlock() }
|
||||
BlockStmt getEnclosingBlock() { result = this.getEnclosingStmt().getEnclosingBlock() }
|
||||
|
||||
override Stmt getEnclosingStmt() {
|
||||
result = this.getParent().(Expr).getEnclosingStmt()
|
||||
@@ -219,13 +219,13 @@ class Expr extends StmtParent, @expr {
|
||||
* Holds if this expression is a _glvalue_. A _glvalue_ is either an _lvalue_ or an
|
||||
* _xvalue_.
|
||||
*/
|
||||
predicate isGLValueCategory() { isLValueCategory() or isXValueCategory() }
|
||||
predicate isGLValueCategory() { this.isLValueCategory() or this.isXValueCategory() }
|
||||
|
||||
/**
|
||||
* Holds if this expression is an _rvalue_. An _rvalue_ is either a _prvalue_ or an
|
||||
* _xvalue_.
|
||||
*/
|
||||
predicate isRValueCategory() { isPRValueCategory() or isXValueCategory() }
|
||||
predicate isRValueCategory() { this.isPRValueCategory() or this.isXValueCategory() }
|
||||
|
||||
/**
|
||||
* Gets a string representation of the value category of the expression.
|
||||
@@ -240,15 +240,15 @@ class Expr extends StmtParent, @expr {
|
||||
* `hasLValueToRvalueConversion()` holds.
|
||||
*/
|
||||
string getValueCategoryString() {
|
||||
isLValueCategory() and
|
||||
this.isLValueCategory() and
|
||||
result = "lvalue"
|
||||
or
|
||||
isXValueCategory() and
|
||||
this.isXValueCategory() and
|
||||
result = "xvalue"
|
||||
or
|
||||
(
|
||||
isPRValueCategory() and
|
||||
if hasLValueToRValueConversion() then result = "prvalue(load)" else result = "prvalue"
|
||||
this.isPRValueCategory() and
|
||||
if this.hasLValueToRValueConversion() then result = "prvalue(load)" else result = "prvalue"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -263,7 +263,7 @@ class Expr extends StmtParent, @expr {
|
||||
* such as an expression inside a sizeof.
|
||||
*/
|
||||
predicate isUnevaluated() {
|
||||
exists(Element e | e = getParentWithConversions+() |
|
||||
exists(Element e | e = this.getParentWithConversions+() |
|
||||
e instanceof SizeofOperator
|
||||
or
|
||||
exists(Expr e2 |
|
||||
@@ -279,7 +279,7 @@ class Expr extends StmtParent, @expr {
|
||||
e instanceof AlignofOperator
|
||||
)
|
||||
or
|
||||
exists(Decltype d | d.getExpr() = getParentWithConversions*())
|
||||
exists(Decltype d | d.getExpr() = this.getParentWithConversions*())
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -725,7 +725,7 @@ class PointerDereferenceExpr extends UnaryOperation, @indirect {
|
||||
*
|
||||
* Gets the expression that is being dereferenced.
|
||||
*/
|
||||
deprecated Expr getExpr() { result = getOperand() }
|
||||
deprecated Expr getExpr() { result = this.getOperand() }
|
||||
|
||||
override string getOperator() { result = "*" }
|
||||
|
||||
@@ -780,15 +780,15 @@ class NewOrNewArrayExpr extends Expr, @any_new_expr {
|
||||
* Gets the alignment argument passed to the allocation function, if any.
|
||||
*/
|
||||
Expr getAlignmentArgument() {
|
||||
hasAlignedAllocation() and
|
||||
this.hasAlignedAllocation() and
|
||||
(
|
||||
// If we have an allocator call, the alignment is the second argument to
|
||||
// that call.
|
||||
result = getAllocatorCall().getArgument(1)
|
||||
result = this.getAllocatorCall().getArgument(1)
|
||||
or
|
||||
// Otherwise, the alignment winds up as child number 3 of the `new`
|
||||
// itself.
|
||||
result = getChild(3)
|
||||
result = this.getChild(3)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -916,7 +916,7 @@ class NewArrayExpr extends NewOrNewArrayExpr, @new_array_expr {
|
||||
* Gets the element type of the array being allocated.
|
||||
*/
|
||||
Type getAllocatedElementType() {
|
||||
result = getType().getUnderlyingType().(PointerType).getBaseType()
|
||||
result = this.getType().getUnderlyingType().(PointerType).getBaseType()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -946,7 +946,12 @@ class DeleteExpr extends Expr, @delete_expr {
|
||||
*/
|
||||
Type getDeletedObjectType() {
|
||||
result =
|
||||
getExpr().getFullyConverted().getType().stripTopLevelSpecifiers().(PointerType).getBaseType()
|
||||
this.getExpr()
|
||||
.getFullyConverted()
|
||||
.getType()
|
||||
.stripTopLevelSpecifiers()
|
||||
.(PointerType)
|
||||
.getBaseType()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -957,7 +962,7 @@ class DeleteExpr extends Expr, @delete_expr {
|
||||
/**
|
||||
* Gets the destructor to be called to destroy the object, if any.
|
||||
*/
|
||||
Destructor getDestructor() { result = getDestructorCall().getTarget() }
|
||||
Destructor getDestructor() { result = this.getDestructorCall().getTarget() }
|
||||
|
||||
/**
|
||||
* Gets the `operator delete` that deallocates storage. Does not hold
|
||||
@@ -1020,7 +1025,12 @@ class DeleteArrayExpr extends Expr, @delete_array_expr {
|
||||
*/
|
||||
Type getDeletedElementType() {
|
||||
result =
|
||||
getExpr().getFullyConverted().getType().stripTopLevelSpecifiers().(PointerType).getBaseType()
|
||||
this.getExpr()
|
||||
.getFullyConverted()
|
||||
.getType()
|
||||
.stripTopLevelSpecifiers()
|
||||
.(PointerType)
|
||||
.getBaseType()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1034,7 +1044,7 @@ class DeleteArrayExpr extends Expr, @delete_array_expr {
|
||||
/**
|
||||
* Gets the destructor to be called to destroy each element in the array, if any.
|
||||
*/
|
||||
Destructor getDestructor() { result = getDestructorCall().getTarget() }
|
||||
Destructor getDestructor() { result = this.getDestructorCall().getTarget() }
|
||||
|
||||
/**
|
||||
* Gets the `operator delete[]` that deallocates storage.
|
||||
@@ -1101,7 +1111,7 @@ class StmtExpr extends Expr, @expr_stmt {
|
||||
* x = ({ dosomething(); a+b; });
|
||||
* ```
|
||||
*/
|
||||
Expr getResultExpr() { result = getStmtResultExpr(getStmt()) }
|
||||
Expr getResultExpr() { result = getStmtResultExpr(this.getStmt()) }
|
||||
}
|
||||
|
||||
/** Get the result expression of a statement. (Helper function for StmtExpr.) */
|
||||
@@ -1230,20 +1240,20 @@ class FoldExpr extends Expr, @foldexpr {
|
||||
predicate isRightFold() { fold(underlyingElement(this), _, false) }
|
||||
|
||||
/** Holds if this is a unary fold expression. */
|
||||
predicate isUnaryFold() { getNumChild() = 1 }
|
||||
predicate isUnaryFold() { this.getNumChild() = 1 }
|
||||
|
||||
/** Holds if this is a binary fold expression. */
|
||||
predicate isBinaryFold() { getNumChild() = 2 }
|
||||
predicate isBinaryFold() { this.getNumChild() = 2 }
|
||||
|
||||
/**
|
||||
* Gets the child expression containing the unexpanded parameter pack.
|
||||
*/
|
||||
Expr getPackExpr() {
|
||||
this.isUnaryFold() and
|
||||
result = getChild(0)
|
||||
result = this.getChild(0)
|
||||
or
|
||||
this.isBinaryFold() and
|
||||
if this.isRightFold() then result = getChild(0) else result = getChild(1)
|
||||
if this.isRightFold() then result = this.getChild(0) else result = this.getChild(1)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1251,7 +1261,7 @@ class FoldExpr extends Expr, @foldexpr {
|
||||
*/
|
||||
Expr getInitExpr() {
|
||||
this.isBinaryFold() and
|
||||
if this.isRightFold() then result = getChild(1) else result = getChild(0)
|
||||
if this.isRightFold() then result = this.getChild(1) else result = this.getChild(0)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ class LambdaExpression extends Expr, @lambdaexpr {
|
||||
/**
|
||||
* Gets an implicitly or explicitly captured value of this lambda expression.
|
||||
*/
|
||||
LambdaCapture getACapture() { result = getCapture(_) }
|
||||
LambdaCapture getACapture() { result = this.getCapture(_) }
|
||||
|
||||
/**
|
||||
* Gets the nth implicitly or explicitly captured value of this lambda expression.
|
||||
@@ -58,13 +58,13 @@ class LambdaExpression extends Expr, @lambdaexpr {
|
||||
* - The return type.
|
||||
* - The statements comprising the lambda body.
|
||||
*/
|
||||
Operator getLambdaFunction() { result = getType().(Closure).getLambdaFunction() }
|
||||
Operator getLambdaFunction() { result = this.getType().(Closure).getLambdaFunction() }
|
||||
|
||||
/**
|
||||
* Gets the initializer that initializes the captured variables in the closure, if any.
|
||||
* A lambda that does not capture any variables will not have an initializer.
|
||||
*/
|
||||
ClassAggregateLiteral getInitializer() { result = getChild(0) }
|
||||
ClassAggregateLiteral getInitializer() { result = this.getChild(0) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -103,7 +103,7 @@ class Closure extends Class {
|
||||
* ```
|
||||
*/
|
||||
class LambdaCapture extends Locatable, @lambdacapture {
|
||||
override string toString() { result = getField().getName() }
|
||||
override string toString() { result = this.getField().getName() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "LambdaCapture" }
|
||||
|
||||
|
||||
@@ -60,12 +60,12 @@ class TextLiteral extends Literal {
|
||||
|
||||
/** Gets a hex escape sequence that appears in the character or string literal (see [lex.ccon] in the C++ Standard). */
|
||||
string getAHexEscapeSequence(int occurrence, int offset) {
|
||||
result = getValueText().regexpFind("(?<!\\\\)\\\\x[0-9a-fA-F]+", occurrence, offset)
|
||||
result = this.getValueText().regexpFind("(?<!\\\\)\\\\x[0-9a-fA-F]+", occurrence, offset)
|
||||
}
|
||||
|
||||
/** Gets an octal escape sequence that appears in the character or string literal (see [lex.ccon] in the C++ Standard). */
|
||||
string getAnOctalEscapeSequence(int occurrence, int offset) {
|
||||
result = getValueText().regexpFind("(?<!\\\\)\\\\[0-7]{1,3}", occurrence, offset)
|
||||
result = this.getValueText().regexpFind("(?<!\\\\)\\\\[0-7]{1,3}", occurrence, offset)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -75,27 +75,27 @@ class TextLiteral extends Literal {
|
||||
string getANonStandardEscapeSequence(int occurrence, int offset) {
|
||||
// Find all single character escape sequences (ignoring the start of octal escape sequences),
|
||||
// together with anything starting like a hex escape sequence but not followed by a hex digit.
|
||||
result = getValueText().regexpFind("\\\\[^x0-7\\s]|\\\\x[^0-9a-fA-F]", occurrence, offset) and
|
||||
result = this.getValueText().regexpFind("\\\\[^x0-7\\s]|\\\\x[^0-9a-fA-F]", occurrence, offset) and
|
||||
// From these, exclude all standard escape sequences.
|
||||
not result = getAStandardEscapeSequence(_, _)
|
||||
not result = this.getAStandardEscapeSequence(_, _)
|
||||
}
|
||||
|
||||
/** Gets a simple escape sequence that appears in the char or string literal (see [lex.ccon] in the C++ Standard). */
|
||||
string getASimpleEscapeSequence(int occurrence, int offset) {
|
||||
result = getValueText().regexpFind("\\\\['\"?\\\\abfnrtv]", occurrence, offset)
|
||||
result = this.getValueText().regexpFind("\\\\['\"?\\\\abfnrtv]", occurrence, offset)
|
||||
}
|
||||
|
||||
/** Gets a standard escape sequence that appears in the char or string literal (see [lex.ccon] in the C++ Standard). */
|
||||
string getAStandardEscapeSequence(int occurrence, int offset) {
|
||||
result = getASimpleEscapeSequence(occurrence, offset) or
|
||||
result = getAnOctalEscapeSequence(occurrence, offset) or
|
||||
result = getAHexEscapeSequence(occurrence, offset)
|
||||
result = this.getASimpleEscapeSequence(occurrence, offset) or
|
||||
result = this.getAnOctalEscapeSequence(occurrence, offset) or
|
||||
result = this.getAHexEscapeSequence(occurrence, offset)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the length of the string literal (including null) before escape sequences added by the extractor.
|
||||
*/
|
||||
int getOriginalLength() { result = getValue().length() + 1 }
|
||||
int getOriginalLength() { result = this.getValue().length() + 1 }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -216,7 +216,7 @@ class ClassAggregateLiteral extends AggregateLiteral {
|
||||
(
|
||||
// If the field has an explicit initializer expression, then the field is
|
||||
// initialized.
|
||||
exists(getFieldExpr(field))
|
||||
exists(this.getFieldExpr(field))
|
||||
or
|
||||
// If the type is not a union, all fields without initializers are value
|
||||
// initialized.
|
||||
@@ -224,7 +224,7 @@ class ClassAggregateLiteral extends AggregateLiteral {
|
||||
or
|
||||
// If the type is a union, and there are no explicit initializers, then
|
||||
// the first declared field is value initialized.
|
||||
not exists(getAChild()) and
|
||||
not exists(this.getAChild()) and
|
||||
field.getInitializationOrder() = 0
|
||||
)
|
||||
}
|
||||
@@ -239,8 +239,8 @@ class ClassAggregateLiteral extends AggregateLiteral {
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate isValueInitialized(Field field) {
|
||||
isInitialized(field) and
|
||||
not exists(getFieldExpr(field))
|
||||
this.isInitialized(field) and
|
||||
not exists(this.getFieldExpr(field))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -285,7 +285,7 @@ class ArrayOrVectorAggregateLiteral extends AggregateLiteral {
|
||||
bindingset[elementIndex]
|
||||
predicate isInitialized(int elementIndex) {
|
||||
elementIndex >= 0 and
|
||||
elementIndex < getArraySize()
|
||||
elementIndex < this.getArraySize()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -298,8 +298,8 @@ class ArrayOrVectorAggregateLiteral extends AggregateLiteral {
|
||||
*/
|
||||
bindingset[elementIndex]
|
||||
predicate isValueInitialized(int elementIndex) {
|
||||
isInitialized(elementIndex) and
|
||||
not exists(getElementExpr(elementIndex))
|
||||
this.isInitialized(elementIndex) and
|
||||
not exists(this.getElementExpr(elementIndex))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -173,7 +173,7 @@ class LocalVariable extends LocalScopeVariable, @localvariable { }
|
||||
class VariableDeclarationEntry extends @var_decl {
|
||||
string toString() { result = "QualifiedName DeclarationEntry" }
|
||||
|
||||
Variable getDeclaration() { result = getVariable() }
|
||||
Variable getDeclaration() { result = this.getVariable() }
|
||||
|
||||
/**
|
||||
* Gets the variable which is being declared or defined.
|
||||
|
||||
@@ -4,7 +4,7 @@ private import semmle.code.cpp.ir.dataflow.DataFlow
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
|
||||
private import semmle.code.cpp.ir.dataflow.DataFlow3
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowDispatch as Dispatch
|
||||
private import semmle.code.cpp.ir.dataflow.ResolveCall
|
||||
private import semmle.code.cpp.controlflow.IRGuards
|
||||
private import semmle.code.cpp.models.interfaces.Taint
|
||||
private import semmle.code.cpp.models.interfaces.DataFlow
|
||||
@@ -355,20 +355,6 @@ predicate taintedIncludingGlobalVars(Expr source, Element tainted, string global
|
||||
*/
|
||||
GlobalOrNamespaceVariable globalVarFromId(string id) { id = result.getQualifiedName() }
|
||||
|
||||
/**
|
||||
* Resolve potential target function(s) for `call`.
|
||||
*
|
||||
* If `call` is a call through a function pointer (`ExprCall`) or
|
||||
* targets a virtual method, simple data flow analysis is performed
|
||||
* in order to identify target(s).
|
||||
*/
|
||||
Function resolveCall(Call call) {
|
||||
exists(CallInstruction callInstruction |
|
||||
callInstruction.getAST() = call and
|
||||
result = Dispatch::viableCallable(callInstruction)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides definitions for augmenting source/sink pairs with data-flow paths
|
||||
* between them. From a `@kind path-problem` query, import this module in the
|
||||
@@ -479,7 +465,7 @@ module TaintedWithPath {
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
|
||||
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
@@ -550,6 +536,39 @@ module TaintedWithPath {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there is flow from `arg` to `out` across a call that can by summarized by the flow
|
||||
* from `par` to `ret` within it, in the graph of data flow path explanations.
|
||||
*/
|
||||
query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) {
|
||||
DataFlow3::PathGraph::subpaths(arg.(WrapPathNode).inner(), par.(WrapPathNode).inner(),
|
||||
ret.(WrapPathNode).inner(), out.(WrapPathNode).inner())
|
||||
or
|
||||
// To avoid showing trivial-looking steps, we _replace_ the last node instead
|
||||
// of adding an edge out of it.
|
||||
exists(WrapPathNode sinkNode |
|
||||
DataFlow3::PathGraph::subpaths(arg.(WrapPathNode).inner(), par.(WrapPathNode).inner(),
|
||||
ret.(WrapPathNode).inner(), sinkNode.inner()) and
|
||||
out.(FinalPathNode).inner() = adjustedSink(sinkNode.inner().getNode())
|
||||
)
|
||||
or
|
||||
// Same for the first node
|
||||
exists(WrapPathNode sourceNode |
|
||||
DataFlow3::PathGraph::subpaths(sourceNode.inner(), par.(WrapPathNode).inner(),
|
||||
ret.(WrapPathNode).inner(), out.(WrapPathNode).inner()) and
|
||||
sourceNode.inner().getNode() = getNodeForExpr(arg.(InitialPathNode).inner())
|
||||
)
|
||||
or
|
||||
// Finally, handle the case where the path goes directly from a source to a
|
||||
// sink, meaning that they both need to be translated.
|
||||
exists(WrapPathNode sinkNode, WrapPathNode sourceNode |
|
||||
DataFlow3::PathGraph::subpaths(sourceNode.inner(), par.(WrapPathNode).inner(),
|
||||
ret.(WrapPathNode).inner(), sinkNode.inner()) and
|
||||
sourceNode.inner().getNode() = getNodeForExpr(arg.(InitialPathNode).inner()) and
|
||||
out.(FinalPathNode).inner() = adjustedSink(sinkNode.inner().getNode())
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `n` is a node in the graph of data flow path explanations. */
|
||||
query predicate nodes(PathNode n, string key, string val) {
|
||||
key = "semmle.label" and val = n.toString()
|
||||
|
||||
23
cpp/ql/lib/semmle/code/cpp/ir/dataflow/ResolveCall.qll
Normal file
23
cpp/ql/lib/semmle/code/cpp/ir/dataflow/ResolveCall.qll
Normal file
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* Provides a predicate for non-contextual virtual dispatch and function
|
||||
* pointer resolution.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
private import semmle.code.cpp.ir.ValueNumbering
|
||||
private import internal.DataFlowDispatch
|
||||
private import semmle.code.cpp.ir.IR
|
||||
|
||||
/**
|
||||
* Resolve potential target function(s) for `call`.
|
||||
*
|
||||
* If `call` is a call through a function pointer (`ExprCall`) or its target is
|
||||
* a virtual member function, simple data flow analysis is performed in order
|
||||
* to identify the possible target(s).
|
||||
*/
|
||||
Function resolveCall(Call call) {
|
||||
exists(CallInstruction callInstruction |
|
||||
callInstruction.getAST() = call and
|
||||
result = viableCallable(callInstruction)
|
||||
)
|
||||
}
|
||||
@@ -63,8 +63,10 @@ private module VirtualDispatch {
|
||||
|
|
||||
// Call argument
|
||||
exists(DataFlowCall call, int i |
|
||||
other.(DataFlow::ParameterNode).isParameterOf(call.getStaticCallTarget(), i) and
|
||||
src.(ArgumentNode).argumentOf(call, i)
|
||||
other
|
||||
.(DataFlow::ParameterNode)
|
||||
.isParameterOf(pragma[only_bind_into](call).getStaticCallTarget(), i) and
|
||||
src.(ArgumentNode).argumentOf(call, pragma[only_bind_into](pragma[only_bind_out](i)))
|
||||
) and
|
||||
allowOtherFromArg = true and
|
||||
allowFromArg = true
|
||||
@@ -128,6 +130,7 @@ private module VirtualDispatch {
|
||||
*
|
||||
* Used to fix a join ordering issue in flowsFrom.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate returnNodeWithKindAndEnclosingCallable(
|
||||
ReturnNode node, ReturnKind kind, DataFlowCallable callable
|
||||
) {
|
||||
|
||||
@@ -110,12 +110,12 @@ abstract class Configuration extends string {
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlowTo(Node sink) { hasFlow(_, sink) }
|
||||
predicate hasFlowTo(Node sink) { this.hasFlow(_, sink) }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlowToExpr(DataFlowExpr sink) { hasFlowTo(exprNode(sink)) }
|
||||
predicate hasFlowToExpr(DataFlowExpr sink) { this.hasFlowTo(exprNode(sink)) }
|
||||
|
||||
/**
|
||||
* Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev`
|
||||
@@ -244,6 +244,8 @@ private class ParamNodeEx extends NodeEx {
|
||||
}
|
||||
|
||||
int getPosition() { this.isParameterOf(_, result) }
|
||||
|
||||
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
|
||||
}
|
||||
|
||||
private class RetNodeEx extends NodeEx {
|
||||
@@ -744,8 +746,12 @@ private module Stage1 {
|
||||
returnFlowCallableNodeCand(c, kind, config) and
|
||||
p.getEnclosingCallable() = c and
|
||||
exists(ap) and
|
||||
// we don't expect a parameter to return stored in itself
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -923,28 +929,29 @@ private module Stage2 {
|
||||
|
||||
ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
|
||||
|
||||
class Cc = boolean;
|
||||
class Cc = CallContext;
|
||||
|
||||
class CcCall extends Cc {
|
||||
CcCall() { this = true }
|
||||
class CcCall = CallContextCall;
|
||||
|
||||
/** Holds if this call context may be `call`. */
|
||||
predicate matchesCall(DataFlowCall call) { any() }
|
||||
}
|
||||
class CcNoCall = CallContextNoCall;
|
||||
|
||||
class CcNoCall extends Cc {
|
||||
CcNoCall() { this = false }
|
||||
}
|
||||
|
||||
Cc ccNone() { result = false }
|
||||
Cc ccNone() { result instanceof CallContextAny }
|
||||
|
||||
private class LocalCc = Unit;
|
||||
|
||||
bindingset[call, c, outercc]
|
||||
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
|
||||
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
|
||||
checkCallContextCall(outercc, call, c) and
|
||||
if recordDataFlowCallSiteDispatch(call, c)
|
||||
then result = TSpecificCall(call)
|
||||
else result = TSomeCall()
|
||||
}
|
||||
|
||||
bindingset[call, c, innercc]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
|
||||
checkCallContextReturn(innercc, c, call) and
|
||||
if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
|
||||
}
|
||||
|
||||
bindingset[node, cc, config]
|
||||
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
|
||||
@@ -1172,7 +1179,8 @@ private module Stage2 {
|
||||
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
|
||||
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
|
||||
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
@@ -1392,8 +1400,12 @@ private module Stage2 {
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1860,7 +1872,8 @@ private module Stage3 {
|
||||
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
|
||||
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
|
||||
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
@@ -2080,8 +2093,12 @@ private module Stage3 {
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2117,7 +2134,7 @@ private module Stage3 {
|
||||
private predicate flowCandSummaryCtx(NodeEx node, AccessPathFront argApf, Configuration config) {
|
||||
exists(AccessPathFront apf |
|
||||
Stage3::revFlow(node, true, _, apf, config) and
|
||||
Stage3::fwdFlow(node, true, TAccessPathFrontSome(argApf), apf, config)
|
||||
Stage3::fwdFlow(node, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2136,7 +2153,8 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
|
||||
) and
|
||||
accessPathApproxCostLimits(apLimit, tupleLimit) and
|
||||
apLimit < tails and
|
||||
tupleLimit < (tails - 1) * nodes
|
||||
tupleLimit < (tails - 1) * nodes and
|
||||
not tc.forceHighPrecision()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2618,7 +2636,8 @@ private module Stage4 {
|
||||
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
|
||||
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
|
||||
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
@@ -2838,8 +2857,12 @@ private module Stage4 {
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2912,6 +2935,8 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome {
|
||||
|
||||
int getParameterPos() { p.isParameterOf(_, result) }
|
||||
|
||||
ParamNodeEx getParamNode() { result = p }
|
||||
|
||||
override string toString() { result = p + ": " + ap }
|
||||
|
||||
predicate hasLocationInfo(
|
||||
@@ -2969,12 +2994,15 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) {
|
||||
* expected to be expensive. Holds with `unfold = true` otherwise.
|
||||
*/
|
||||
private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) {
|
||||
exists(int aps, int nodes, int apLimit, int tupleLimit |
|
||||
aps = countPotentialAps(apa, config) and
|
||||
nodes = countNodesUsingAccessPath(apa, config) and
|
||||
accessPathCostLimits(apLimit, tupleLimit) and
|
||||
if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true
|
||||
)
|
||||
if apa.getHead().forceHighPrecision()
|
||||
then unfold = true
|
||||
else
|
||||
exists(int aps, int nodes, int apLimit, int tupleLimit |
|
||||
aps = countPotentialAps(apa, config) and
|
||||
nodes = countNodesUsingAccessPath(apa, config) and
|
||||
accessPathCostLimits(apLimit, tupleLimit) and
|
||||
if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3162,7 +3190,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons {
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "[" + this.toStringImpl(true) + length().toString() + ")]"
|
||||
result = "[" + this.toStringImpl(true) + this.length().toString() + ")]"
|
||||
or
|
||||
result = "[" + this.toStringImpl(false)
|
||||
}
|
||||
@@ -3244,7 +3272,7 @@ class PathNode extends TPathNode {
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
|
||||
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
@@ -3301,9 +3329,11 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">"
|
||||
}
|
||||
|
||||
override string toString() { result = this.getNodeEx().toString() + ppAp() }
|
||||
override string toString() { result = this.getNodeEx().toString() + this.ppAp() }
|
||||
|
||||
override string toStringWithContext() { result = this.getNodeEx().toString() + ppAp() + ppCtx() }
|
||||
override string toStringWithContext() {
|
||||
result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx()
|
||||
}
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
@@ -3320,8 +3350,8 @@ private predicate directReach(PathNode n) {
|
||||
/** Holds if `n` can reach a sink or is used in a subpath. */
|
||||
private predicate reach(PathNode n) { directReach(n) or Subpaths::retReach(n) }
|
||||
|
||||
/** Holds if `n1.getSucc() = n2` and `n2` can reach a sink or is used in a subpath. */
|
||||
private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and reach(n2) }
|
||||
/** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */
|
||||
private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and directReach(n2) }
|
||||
|
||||
private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2)
|
||||
|
||||
@@ -3330,7 +3360,7 @@ private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1
|
||||
*/
|
||||
module PathGraph {
|
||||
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
|
||||
query predicate edges(PathNode a, PathNode b) { pathSucc(a, b) }
|
||||
query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b and reach(b) }
|
||||
|
||||
/** Holds if `n` is a node in the graph of data flow path explanations. */
|
||||
query predicate nodes(PathNode n, string key, string val) {
|
||||
@@ -3371,11 +3401,11 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
|
||||
|
||||
override PathNodeImpl getASuccessorImpl() {
|
||||
// an intermediate step to another intermediate node
|
||||
result = getSuccMid()
|
||||
result = this.getSuccMid()
|
||||
or
|
||||
// a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
|
||||
exists(PathNodeMid mid, PathNodeSink sink |
|
||||
mid = getSuccMid() and
|
||||
mid = this.getSuccMid() and
|
||||
mid.getNodeEx() = sink.getNodeEx() and
|
||||
mid.getAp() instanceof AccessPathNil and
|
||||
sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
|
||||
@@ -3452,7 +3482,7 @@ private predicate pathStep(
|
||||
exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and
|
||||
sc = mid.getSummaryCtx()
|
||||
or
|
||||
pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp()
|
||||
pathIntoCallable(mid, node, _, cc, sc, _, _) and ap = mid.getAp()
|
||||
or
|
||||
pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone
|
||||
or
|
||||
@@ -3529,14 +3559,16 @@ private predicate pathOutOfCallable(PathNodeMid mid, NodeEx out, CallContext cc)
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate pathIntoArg(
|
||||
PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa
|
||||
PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ArgNode arg |
|
||||
arg = mid.getNodeEx().asNode() and
|
||||
cc = mid.getCallContext() and
|
||||
arg.argumentOf(call, i) and
|
||||
ap = mid.getAp() and
|
||||
apa = ap.getApprox()
|
||||
apa = ap.getApprox() and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3553,12 +3585,14 @@ private predicate parameterCand(
|
||||
pragma[nomagic]
|
||||
private predicate pathIntoCallable0(
|
||||
PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call,
|
||||
AccessPath ap
|
||||
AccessPath ap, Configuration config
|
||||
) {
|
||||
exists(AccessPathApprox apa |
|
||||
pathIntoArg(mid, i, outercc, call, ap, apa) and
|
||||
pathIntoArg(mid, pragma[only_bind_into](i), outercc, call, ap, pragma[only_bind_into](apa),
|
||||
pragma[only_bind_into](config)) and
|
||||
callable = resolveCall(call, outercc) and
|
||||
parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration())
|
||||
parameterCand(callable, pragma[only_bind_into](i), pragma[only_bind_into](apa),
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3567,12 +3601,13 @@ private predicate pathIntoCallable0(
|
||||
* before and after entering the callable are `outercc` and `innercc`,
|
||||
* respectively.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate pathIntoCallable(
|
||||
PathNodeMid mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc, SummaryCtx sc,
|
||||
DataFlowCall call
|
||||
DataFlowCall call, Configuration config
|
||||
) {
|
||||
exists(int i, DataFlowCallable callable, AccessPath ap |
|
||||
pathIntoCallable0(mid, callable, i, outercc, call, ap) and
|
||||
pathIntoCallable0(mid, callable, i, outercc, call, ap, config) and
|
||||
p.isParameterOf(callable, i) and
|
||||
(
|
||||
sc = TSummaryCtxSome(p, ap)
|
||||
@@ -3602,18 +3637,23 @@ private predicate paramFlowsThrough(
|
||||
ap = mid.getAp() and
|
||||
apa = ap.getApprox() and
|
||||
pos = sc.getParameterPos() and
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
sc.getParamNode().allowParameterReturnInSelf()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate pathThroughCallable0(
|
||||
DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap,
|
||||
AccessPathApprox apa
|
||||
AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(CallContext innercc, SummaryCtx sc |
|
||||
pathIntoCallable(mid, _, cc, innercc, sc, call) and
|
||||
paramFlowsThrough(kind, innercc, sc, ap, apa, unbindConf(mid.getConfiguration()))
|
||||
pathIntoCallable(mid, _, cc, innercc, sc, call, config) and
|
||||
paramFlowsThrough(kind, innercc, sc, ap, apa, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3623,9 +3663,9 @@ private predicate pathThroughCallable0(
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext cc, AccessPath ap) {
|
||||
exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa |
|
||||
pathThroughCallable0(call, mid, kind, cc, ap, apa) and
|
||||
out = getAnOutNodeFlow(kind, call, apa, unbindConf(mid.getConfiguration()))
|
||||
exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa, Configuration config |
|
||||
pathThroughCallable0(call, mid, kind, cc, ap, apa, config) and
|
||||
out = getAnOutNodeFlow(kind, call, apa, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3639,9 +3679,11 @@ private module Subpaths {
|
||||
PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
|
||||
NodeEx out, AccessPath apout
|
||||
) {
|
||||
pathThroughCallable(arg, out, _, apout) and
|
||||
pathIntoCallable(arg, par, _, innercc, sc, _) and
|
||||
paramFlowsThrough(kind, innercc, sc, apout, _, unbindConf(arg.getConfiguration()))
|
||||
exists(Configuration config |
|
||||
pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and
|
||||
pathIntoCallable(arg, par, _, innercc, sc, _, config) and
|
||||
paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, unbindConf(config))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3686,8 +3728,8 @@ private module Subpaths {
|
||||
*/
|
||||
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeMid ret, PathNodeMid out) {
|
||||
exists(ParamNodeEx p, NodeEx o, AccessPath apout |
|
||||
arg.getASuccessor() = par and
|
||||
arg.getASuccessor() = out and
|
||||
pragma[only_bind_into](arg).getASuccessor() = par and
|
||||
pragma[only_bind_into](arg).getASuccessor() = out and
|
||||
subpaths03(arg, p, ret, o, apout) and
|
||||
par.getNodeEx() = p and
|
||||
out.getNodeEx() = o and
|
||||
@@ -4028,7 +4070,7 @@ private module FlowExploration {
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
|
||||
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
|
||||
@@ -110,12 +110,12 @@ abstract class Configuration extends string {
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlowTo(Node sink) { hasFlow(_, sink) }
|
||||
predicate hasFlowTo(Node sink) { this.hasFlow(_, sink) }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlowToExpr(DataFlowExpr sink) { hasFlowTo(exprNode(sink)) }
|
||||
predicate hasFlowToExpr(DataFlowExpr sink) { this.hasFlowTo(exprNode(sink)) }
|
||||
|
||||
/**
|
||||
* Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev`
|
||||
@@ -244,6 +244,8 @@ private class ParamNodeEx extends NodeEx {
|
||||
}
|
||||
|
||||
int getPosition() { this.isParameterOf(_, result) }
|
||||
|
||||
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
|
||||
}
|
||||
|
||||
private class RetNodeEx extends NodeEx {
|
||||
@@ -744,8 +746,12 @@ private module Stage1 {
|
||||
returnFlowCallableNodeCand(c, kind, config) and
|
||||
p.getEnclosingCallable() = c and
|
||||
exists(ap) and
|
||||
// we don't expect a parameter to return stored in itself
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -923,28 +929,29 @@ private module Stage2 {
|
||||
|
||||
ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
|
||||
|
||||
class Cc = boolean;
|
||||
class Cc = CallContext;
|
||||
|
||||
class CcCall extends Cc {
|
||||
CcCall() { this = true }
|
||||
class CcCall = CallContextCall;
|
||||
|
||||
/** Holds if this call context may be `call`. */
|
||||
predicate matchesCall(DataFlowCall call) { any() }
|
||||
}
|
||||
class CcNoCall = CallContextNoCall;
|
||||
|
||||
class CcNoCall extends Cc {
|
||||
CcNoCall() { this = false }
|
||||
}
|
||||
|
||||
Cc ccNone() { result = false }
|
||||
Cc ccNone() { result instanceof CallContextAny }
|
||||
|
||||
private class LocalCc = Unit;
|
||||
|
||||
bindingset[call, c, outercc]
|
||||
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
|
||||
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
|
||||
checkCallContextCall(outercc, call, c) and
|
||||
if recordDataFlowCallSiteDispatch(call, c)
|
||||
then result = TSpecificCall(call)
|
||||
else result = TSomeCall()
|
||||
}
|
||||
|
||||
bindingset[call, c, innercc]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
|
||||
checkCallContextReturn(innercc, c, call) and
|
||||
if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
|
||||
}
|
||||
|
||||
bindingset[node, cc, config]
|
||||
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
|
||||
@@ -1172,7 +1179,8 @@ private module Stage2 {
|
||||
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
|
||||
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
|
||||
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
@@ -1392,8 +1400,12 @@ private module Stage2 {
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1860,7 +1872,8 @@ private module Stage3 {
|
||||
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
|
||||
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
|
||||
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
@@ -2080,8 +2093,12 @@ private module Stage3 {
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2117,7 +2134,7 @@ private module Stage3 {
|
||||
private predicate flowCandSummaryCtx(NodeEx node, AccessPathFront argApf, Configuration config) {
|
||||
exists(AccessPathFront apf |
|
||||
Stage3::revFlow(node, true, _, apf, config) and
|
||||
Stage3::fwdFlow(node, true, TAccessPathFrontSome(argApf), apf, config)
|
||||
Stage3::fwdFlow(node, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2136,7 +2153,8 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
|
||||
) and
|
||||
accessPathApproxCostLimits(apLimit, tupleLimit) and
|
||||
apLimit < tails and
|
||||
tupleLimit < (tails - 1) * nodes
|
||||
tupleLimit < (tails - 1) * nodes and
|
||||
not tc.forceHighPrecision()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2618,7 +2636,8 @@ private module Stage4 {
|
||||
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
|
||||
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
|
||||
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
@@ -2838,8 +2857,12 @@ private module Stage4 {
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2912,6 +2935,8 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome {
|
||||
|
||||
int getParameterPos() { p.isParameterOf(_, result) }
|
||||
|
||||
ParamNodeEx getParamNode() { result = p }
|
||||
|
||||
override string toString() { result = p + ": " + ap }
|
||||
|
||||
predicate hasLocationInfo(
|
||||
@@ -2969,12 +2994,15 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) {
|
||||
* expected to be expensive. Holds with `unfold = true` otherwise.
|
||||
*/
|
||||
private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) {
|
||||
exists(int aps, int nodes, int apLimit, int tupleLimit |
|
||||
aps = countPotentialAps(apa, config) and
|
||||
nodes = countNodesUsingAccessPath(apa, config) and
|
||||
accessPathCostLimits(apLimit, tupleLimit) and
|
||||
if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true
|
||||
)
|
||||
if apa.getHead().forceHighPrecision()
|
||||
then unfold = true
|
||||
else
|
||||
exists(int aps, int nodes, int apLimit, int tupleLimit |
|
||||
aps = countPotentialAps(apa, config) and
|
||||
nodes = countNodesUsingAccessPath(apa, config) and
|
||||
accessPathCostLimits(apLimit, tupleLimit) and
|
||||
if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3162,7 +3190,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons {
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "[" + this.toStringImpl(true) + length().toString() + ")]"
|
||||
result = "[" + this.toStringImpl(true) + this.length().toString() + ")]"
|
||||
or
|
||||
result = "[" + this.toStringImpl(false)
|
||||
}
|
||||
@@ -3244,7 +3272,7 @@ class PathNode extends TPathNode {
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
|
||||
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
@@ -3301,9 +3329,11 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">"
|
||||
}
|
||||
|
||||
override string toString() { result = this.getNodeEx().toString() + ppAp() }
|
||||
override string toString() { result = this.getNodeEx().toString() + this.ppAp() }
|
||||
|
||||
override string toStringWithContext() { result = this.getNodeEx().toString() + ppAp() + ppCtx() }
|
||||
override string toStringWithContext() {
|
||||
result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx()
|
||||
}
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
@@ -3320,8 +3350,8 @@ private predicate directReach(PathNode n) {
|
||||
/** Holds if `n` can reach a sink or is used in a subpath. */
|
||||
private predicate reach(PathNode n) { directReach(n) or Subpaths::retReach(n) }
|
||||
|
||||
/** Holds if `n1.getSucc() = n2` and `n2` can reach a sink or is used in a subpath. */
|
||||
private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and reach(n2) }
|
||||
/** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */
|
||||
private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and directReach(n2) }
|
||||
|
||||
private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2)
|
||||
|
||||
@@ -3330,7 +3360,7 @@ private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1
|
||||
*/
|
||||
module PathGraph {
|
||||
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
|
||||
query predicate edges(PathNode a, PathNode b) { pathSucc(a, b) }
|
||||
query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b and reach(b) }
|
||||
|
||||
/** Holds if `n` is a node in the graph of data flow path explanations. */
|
||||
query predicate nodes(PathNode n, string key, string val) {
|
||||
@@ -3371,11 +3401,11 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
|
||||
|
||||
override PathNodeImpl getASuccessorImpl() {
|
||||
// an intermediate step to another intermediate node
|
||||
result = getSuccMid()
|
||||
result = this.getSuccMid()
|
||||
or
|
||||
// a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
|
||||
exists(PathNodeMid mid, PathNodeSink sink |
|
||||
mid = getSuccMid() and
|
||||
mid = this.getSuccMid() and
|
||||
mid.getNodeEx() = sink.getNodeEx() and
|
||||
mid.getAp() instanceof AccessPathNil and
|
||||
sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
|
||||
@@ -3452,7 +3482,7 @@ private predicate pathStep(
|
||||
exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and
|
||||
sc = mid.getSummaryCtx()
|
||||
or
|
||||
pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp()
|
||||
pathIntoCallable(mid, node, _, cc, sc, _, _) and ap = mid.getAp()
|
||||
or
|
||||
pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone
|
||||
or
|
||||
@@ -3529,14 +3559,16 @@ private predicate pathOutOfCallable(PathNodeMid mid, NodeEx out, CallContext cc)
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate pathIntoArg(
|
||||
PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa
|
||||
PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ArgNode arg |
|
||||
arg = mid.getNodeEx().asNode() and
|
||||
cc = mid.getCallContext() and
|
||||
arg.argumentOf(call, i) and
|
||||
ap = mid.getAp() and
|
||||
apa = ap.getApprox()
|
||||
apa = ap.getApprox() and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3553,12 +3585,14 @@ private predicate parameterCand(
|
||||
pragma[nomagic]
|
||||
private predicate pathIntoCallable0(
|
||||
PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call,
|
||||
AccessPath ap
|
||||
AccessPath ap, Configuration config
|
||||
) {
|
||||
exists(AccessPathApprox apa |
|
||||
pathIntoArg(mid, i, outercc, call, ap, apa) and
|
||||
pathIntoArg(mid, pragma[only_bind_into](i), outercc, call, ap, pragma[only_bind_into](apa),
|
||||
pragma[only_bind_into](config)) and
|
||||
callable = resolveCall(call, outercc) and
|
||||
parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration())
|
||||
parameterCand(callable, pragma[only_bind_into](i), pragma[only_bind_into](apa),
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3567,12 +3601,13 @@ private predicate pathIntoCallable0(
|
||||
* before and after entering the callable are `outercc` and `innercc`,
|
||||
* respectively.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate pathIntoCallable(
|
||||
PathNodeMid mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc, SummaryCtx sc,
|
||||
DataFlowCall call
|
||||
DataFlowCall call, Configuration config
|
||||
) {
|
||||
exists(int i, DataFlowCallable callable, AccessPath ap |
|
||||
pathIntoCallable0(mid, callable, i, outercc, call, ap) and
|
||||
pathIntoCallable0(mid, callable, i, outercc, call, ap, config) and
|
||||
p.isParameterOf(callable, i) and
|
||||
(
|
||||
sc = TSummaryCtxSome(p, ap)
|
||||
@@ -3602,18 +3637,23 @@ private predicate paramFlowsThrough(
|
||||
ap = mid.getAp() and
|
||||
apa = ap.getApprox() and
|
||||
pos = sc.getParameterPos() and
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
sc.getParamNode().allowParameterReturnInSelf()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate pathThroughCallable0(
|
||||
DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap,
|
||||
AccessPathApprox apa
|
||||
AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(CallContext innercc, SummaryCtx sc |
|
||||
pathIntoCallable(mid, _, cc, innercc, sc, call) and
|
||||
paramFlowsThrough(kind, innercc, sc, ap, apa, unbindConf(mid.getConfiguration()))
|
||||
pathIntoCallable(mid, _, cc, innercc, sc, call, config) and
|
||||
paramFlowsThrough(kind, innercc, sc, ap, apa, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3623,9 +3663,9 @@ private predicate pathThroughCallable0(
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext cc, AccessPath ap) {
|
||||
exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa |
|
||||
pathThroughCallable0(call, mid, kind, cc, ap, apa) and
|
||||
out = getAnOutNodeFlow(kind, call, apa, unbindConf(mid.getConfiguration()))
|
||||
exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa, Configuration config |
|
||||
pathThroughCallable0(call, mid, kind, cc, ap, apa, config) and
|
||||
out = getAnOutNodeFlow(kind, call, apa, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3639,9 +3679,11 @@ private module Subpaths {
|
||||
PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
|
||||
NodeEx out, AccessPath apout
|
||||
) {
|
||||
pathThroughCallable(arg, out, _, apout) and
|
||||
pathIntoCallable(arg, par, _, innercc, sc, _) and
|
||||
paramFlowsThrough(kind, innercc, sc, apout, _, unbindConf(arg.getConfiguration()))
|
||||
exists(Configuration config |
|
||||
pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and
|
||||
pathIntoCallable(arg, par, _, innercc, sc, _, config) and
|
||||
paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, unbindConf(config))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3686,8 +3728,8 @@ private module Subpaths {
|
||||
*/
|
||||
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeMid ret, PathNodeMid out) {
|
||||
exists(ParamNodeEx p, NodeEx o, AccessPath apout |
|
||||
arg.getASuccessor() = par and
|
||||
arg.getASuccessor() = out and
|
||||
pragma[only_bind_into](arg).getASuccessor() = par and
|
||||
pragma[only_bind_into](arg).getASuccessor() = out and
|
||||
subpaths03(arg, p, ret, o, apout) and
|
||||
par.getNodeEx() = p and
|
||||
out.getNodeEx() = o and
|
||||
@@ -4028,7 +4070,7 @@ private module FlowExploration {
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
|
||||
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
|
||||
@@ -110,12 +110,12 @@ abstract class Configuration extends string {
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlowTo(Node sink) { hasFlow(_, sink) }
|
||||
predicate hasFlowTo(Node sink) { this.hasFlow(_, sink) }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlowToExpr(DataFlowExpr sink) { hasFlowTo(exprNode(sink)) }
|
||||
predicate hasFlowToExpr(DataFlowExpr sink) { this.hasFlowTo(exprNode(sink)) }
|
||||
|
||||
/**
|
||||
* Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev`
|
||||
@@ -244,6 +244,8 @@ private class ParamNodeEx extends NodeEx {
|
||||
}
|
||||
|
||||
int getPosition() { this.isParameterOf(_, result) }
|
||||
|
||||
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
|
||||
}
|
||||
|
||||
private class RetNodeEx extends NodeEx {
|
||||
@@ -744,8 +746,12 @@ private module Stage1 {
|
||||
returnFlowCallableNodeCand(c, kind, config) and
|
||||
p.getEnclosingCallable() = c and
|
||||
exists(ap) and
|
||||
// we don't expect a parameter to return stored in itself
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -923,28 +929,29 @@ private module Stage2 {
|
||||
|
||||
ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
|
||||
|
||||
class Cc = boolean;
|
||||
class Cc = CallContext;
|
||||
|
||||
class CcCall extends Cc {
|
||||
CcCall() { this = true }
|
||||
class CcCall = CallContextCall;
|
||||
|
||||
/** Holds if this call context may be `call`. */
|
||||
predicate matchesCall(DataFlowCall call) { any() }
|
||||
}
|
||||
class CcNoCall = CallContextNoCall;
|
||||
|
||||
class CcNoCall extends Cc {
|
||||
CcNoCall() { this = false }
|
||||
}
|
||||
|
||||
Cc ccNone() { result = false }
|
||||
Cc ccNone() { result instanceof CallContextAny }
|
||||
|
||||
private class LocalCc = Unit;
|
||||
|
||||
bindingset[call, c, outercc]
|
||||
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
|
||||
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
|
||||
checkCallContextCall(outercc, call, c) and
|
||||
if recordDataFlowCallSiteDispatch(call, c)
|
||||
then result = TSpecificCall(call)
|
||||
else result = TSomeCall()
|
||||
}
|
||||
|
||||
bindingset[call, c, innercc]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
|
||||
checkCallContextReturn(innercc, c, call) and
|
||||
if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
|
||||
}
|
||||
|
||||
bindingset[node, cc, config]
|
||||
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
|
||||
@@ -1172,7 +1179,8 @@ private module Stage2 {
|
||||
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
|
||||
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
|
||||
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
@@ -1392,8 +1400,12 @@ private module Stage2 {
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1860,7 +1872,8 @@ private module Stage3 {
|
||||
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
|
||||
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
|
||||
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
@@ -2080,8 +2093,12 @@ private module Stage3 {
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2117,7 +2134,7 @@ private module Stage3 {
|
||||
private predicate flowCandSummaryCtx(NodeEx node, AccessPathFront argApf, Configuration config) {
|
||||
exists(AccessPathFront apf |
|
||||
Stage3::revFlow(node, true, _, apf, config) and
|
||||
Stage3::fwdFlow(node, true, TAccessPathFrontSome(argApf), apf, config)
|
||||
Stage3::fwdFlow(node, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2136,7 +2153,8 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
|
||||
) and
|
||||
accessPathApproxCostLimits(apLimit, tupleLimit) and
|
||||
apLimit < tails and
|
||||
tupleLimit < (tails - 1) * nodes
|
||||
tupleLimit < (tails - 1) * nodes and
|
||||
not tc.forceHighPrecision()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2618,7 +2636,8 @@ private module Stage4 {
|
||||
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
|
||||
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
|
||||
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
@@ -2838,8 +2857,12 @@ private module Stage4 {
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2912,6 +2935,8 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome {
|
||||
|
||||
int getParameterPos() { p.isParameterOf(_, result) }
|
||||
|
||||
ParamNodeEx getParamNode() { result = p }
|
||||
|
||||
override string toString() { result = p + ": " + ap }
|
||||
|
||||
predicate hasLocationInfo(
|
||||
@@ -2969,12 +2994,15 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) {
|
||||
* expected to be expensive. Holds with `unfold = true` otherwise.
|
||||
*/
|
||||
private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) {
|
||||
exists(int aps, int nodes, int apLimit, int tupleLimit |
|
||||
aps = countPotentialAps(apa, config) and
|
||||
nodes = countNodesUsingAccessPath(apa, config) and
|
||||
accessPathCostLimits(apLimit, tupleLimit) and
|
||||
if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true
|
||||
)
|
||||
if apa.getHead().forceHighPrecision()
|
||||
then unfold = true
|
||||
else
|
||||
exists(int aps, int nodes, int apLimit, int tupleLimit |
|
||||
aps = countPotentialAps(apa, config) and
|
||||
nodes = countNodesUsingAccessPath(apa, config) and
|
||||
accessPathCostLimits(apLimit, tupleLimit) and
|
||||
if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3162,7 +3190,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons {
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "[" + this.toStringImpl(true) + length().toString() + ")]"
|
||||
result = "[" + this.toStringImpl(true) + this.length().toString() + ")]"
|
||||
or
|
||||
result = "[" + this.toStringImpl(false)
|
||||
}
|
||||
@@ -3244,7 +3272,7 @@ class PathNode extends TPathNode {
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
|
||||
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
@@ -3301,9 +3329,11 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">"
|
||||
}
|
||||
|
||||
override string toString() { result = this.getNodeEx().toString() + ppAp() }
|
||||
override string toString() { result = this.getNodeEx().toString() + this.ppAp() }
|
||||
|
||||
override string toStringWithContext() { result = this.getNodeEx().toString() + ppAp() + ppCtx() }
|
||||
override string toStringWithContext() {
|
||||
result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx()
|
||||
}
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
@@ -3320,8 +3350,8 @@ private predicate directReach(PathNode n) {
|
||||
/** Holds if `n` can reach a sink or is used in a subpath. */
|
||||
private predicate reach(PathNode n) { directReach(n) or Subpaths::retReach(n) }
|
||||
|
||||
/** Holds if `n1.getSucc() = n2` and `n2` can reach a sink or is used in a subpath. */
|
||||
private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and reach(n2) }
|
||||
/** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */
|
||||
private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and directReach(n2) }
|
||||
|
||||
private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2)
|
||||
|
||||
@@ -3330,7 +3360,7 @@ private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1
|
||||
*/
|
||||
module PathGraph {
|
||||
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
|
||||
query predicate edges(PathNode a, PathNode b) { pathSucc(a, b) }
|
||||
query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b and reach(b) }
|
||||
|
||||
/** Holds if `n` is a node in the graph of data flow path explanations. */
|
||||
query predicate nodes(PathNode n, string key, string val) {
|
||||
@@ -3371,11 +3401,11 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
|
||||
|
||||
override PathNodeImpl getASuccessorImpl() {
|
||||
// an intermediate step to another intermediate node
|
||||
result = getSuccMid()
|
||||
result = this.getSuccMid()
|
||||
or
|
||||
// a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
|
||||
exists(PathNodeMid mid, PathNodeSink sink |
|
||||
mid = getSuccMid() and
|
||||
mid = this.getSuccMid() and
|
||||
mid.getNodeEx() = sink.getNodeEx() and
|
||||
mid.getAp() instanceof AccessPathNil and
|
||||
sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
|
||||
@@ -3452,7 +3482,7 @@ private predicate pathStep(
|
||||
exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and
|
||||
sc = mid.getSummaryCtx()
|
||||
or
|
||||
pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp()
|
||||
pathIntoCallable(mid, node, _, cc, sc, _, _) and ap = mid.getAp()
|
||||
or
|
||||
pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone
|
||||
or
|
||||
@@ -3529,14 +3559,16 @@ private predicate pathOutOfCallable(PathNodeMid mid, NodeEx out, CallContext cc)
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate pathIntoArg(
|
||||
PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa
|
||||
PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ArgNode arg |
|
||||
arg = mid.getNodeEx().asNode() and
|
||||
cc = mid.getCallContext() and
|
||||
arg.argumentOf(call, i) and
|
||||
ap = mid.getAp() and
|
||||
apa = ap.getApprox()
|
||||
apa = ap.getApprox() and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3553,12 +3585,14 @@ private predicate parameterCand(
|
||||
pragma[nomagic]
|
||||
private predicate pathIntoCallable0(
|
||||
PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call,
|
||||
AccessPath ap
|
||||
AccessPath ap, Configuration config
|
||||
) {
|
||||
exists(AccessPathApprox apa |
|
||||
pathIntoArg(mid, i, outercc, call, ap, apa) and
|
||||
pathIntoArg(mid, pragma[only_bind_into](i), outercc, call, ap, pragma[only_bind_into](apa),
|
||||
pragma[only_bind_into](config)) and
|
||||
callable = resolveCall(call, outercc) and
|
||||
parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration())
|
||||
parameterCand(callable, pragma[only_bind_into](i), pragma[only_bind_into](apa),
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3567,12 +3601,13 @@ private predicate pathIntoCallable0(
|
||||
* before and after entering the callable are `outercc` and `innercc`,
|
||||
* respectively.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate pathIntoCallable(
|
||||
PathNodeMid mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc, SummaryCtx sc,
|
||||
DataFlowCall call
|
||||
DataFlowCall call, Configuration config
|
||||
) {
|
||||
exists(int i, DataFlowCallable callable, AccessPath ap |
|
||||
pathIntoCallable0(mid, callable, i, outercc, call, ap) and
|
||||
pathIntoCallable0(mid, callable, i, outercc, call, ap, config) and
|
||||
p.isParameterOf(callable, i) and
|
||||
(
|
||||
sc = TSummaryCtxSome(p, ap)
|
||||
@@ -3602,18 +3637,23 @@ private predicate paramFlowsThrough(
|
||||
ap = mid.getAp() and
|
||||
apa = ap.getApprox() and
|
||||
pos = sc.getParameterPos() and
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
sc.getParamNode().allowParameterReturnInSelf()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate pathThroughCallable0(
|
||||
DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap,
|
||||
AccessPathApprox apa
|
||||
AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(CallContext innercc, SummaryCtx sc |
|
||||
pathIntoCallable(mid, _, cc, innercc, sc, call) and
|
||||
paramFlowsThrough(kind, innercc, sc, ap, apa, unbindConf(mid.getConfiguration()))
|
||||
pathIntoCallable(mid, _, cc, innercc, sc, call, config) and
|
||||
paramFlowsThrough(kind, innercc, sc, ap, apa, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3623,9 +3663,9 @@ private predicate pathThroughCallable0(
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext cc, AccessPath ap) {
|
||||
exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa |
|
||||
pathThroughCallable0(call, mid, kind, cc, ap, apa) and
|
||||
out = getAnOutNodeFlow(kind, call, apa, unbindConf(mid.getConfiguration()))
|
||||
exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa, Configuration config |
|
||||
pathThroughCallable0(call, mid, kind, cc, ap, apa, config) and
|
||||
out = getAnOutNodeFlow(kind, call, apa, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3639,9 +3679,11 @@ private module Subpaths {
|
||||
PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
|
||||
NodeEx out, AccessPath apout
|
||||
) {
|
||||
pathThroughCallable(arg, out, _, apout) and
|
||||
pathIntoCallable(arg, par, _, innercc, sc, _) and
|
||||
paramFlowsThrough(kind, innercc, sc, apout, _, unbindConf(arg.getConfiguration()))
|
||||
exists(Configuration config |
|
||||
pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and
|
||||
pathIntoCallable(arg, par, _, innercc, sc, _, config) and
|
||||
paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, unbindConf(config))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3686,8 +3728,8 @@ private module Subpaths {
|
||||
*/
|
||||
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeMid ret, PathNodeMid out) {
|
||||
exists(ParamNodeEx p, NodeEx o, AccessPath apout |
|
||||
arg.getASuccessor() = par and
|
||||
arg.getASuccessor() = out and
|
||||
pragma[only_bind_into](arg).getASuccessor() = par and
|
||||
pragma[only_bind_into](arg).getASuccessor() = out and
|
||||
subpaths03(arg, p, ret, o, apout) and
|
||||
par.getNodeEx() = p and
|
||||
out.getNodeEx() = o and
|
||||
@@ -4028,7 +4070,7 @@ private module FlowExploration {
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
|
||||
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
|
||||
@@ -110,12 +110,12 @@ abstract class Configuration extends string {
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlowTo(Node sink) { hasFlow(_, sink) }
|
||||
predicate hasFlowTo(Node sink) { this.hasFlow(_, sink) }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlowToExpr(DataFlowExpr sink) { hasFlowTo(exprNode(sink)) }
|
||||
predicate hasFlowToExpr(DataFlowExpr sink) { this.hasFlowTo(exprNode(sink)) }
|
||||
|
||||
/**
|
||||
* Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev`
|
||||
@@ -244,6 +244,8 @@ private class ParamNodeEx extends NodeEx {
|
||||
}
|
||||
|
||||
int getPosition() { this.isParameterOf(_, result) }
|
||||
|
||||
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
|
||||
}
|
||||
|
||||
private class RetNodeEx extends NodeEx {
|
||||
@@ -744,8 +746,12 @@ private module Stage1 {
|
||||
returnFlowCallableNodeCand(c, kind, config) and
|
||||
p.getEnclosingCallable() = c and
|
||||
exists(ap) and
|
||||
// we don't expect a parameter to return stored in itself
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -923,28 +929,29 @@ private module Stage2 {
|
||||
|
||||
ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
|
||||
|
||||
class Cc = boolean;
|
||||
class Cc = CallContext;
|
||||
|
||||
class CcCall extends Cc {
|
||||
CcCall() { this = true }
|
||||
class CcCall = CallContextCall;
|
||||
|
||||
/** Holds if this call context may be `call`. */
|
||||
predicate matchesCall(DataFlowCall call) { any() }
|
||||
}
|
||||
class CcNoCall = CallContextNoCall;
|
||||
|
||||
class CcNoCall extends Cc {
|
||||
CcNoCall() { this = false }
|
||||
}
|
||||
|
||||
Cc ccNone() { result = false }
|
||||
Cc ccNone() { result instanceof CallContextAny }
|
||||
|
||||
private class LocalCc = Unit;
|
||||
|
||||
bindingset[call, c, outercc]
|
||||
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
|
||||
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
|
||||
checkCallContextCall(outercc, call, c) and
|
||||
if recordDataFlowCallSiteDispatch(call, c)
|
||||
then result = TSpecificCall(call)
|
||||
else result = TSomeCall()
|
||||
}
|
||||
|
||||
bindingset[call, c, innercc]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
|
||||
checkCallContextReturn(innercc, c, call) and
|
||||
if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
|
||||
}
|
||||
|
||||
bindingset[node, cc, config]
|
||||
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
|
||||
@@ -1172,7 +1179,8 @@ private module Stage2 {
|
||||
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
|
||||
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
|
||||
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
@@ -1392,8 +1400,12 @@ private module Stage2 {
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1860,7 +1872,8 @@ private module Stage3 {
|
||||
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
|
||||
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
|
||||
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
@@ -2080,8 +2093,12 @@ private module Stage3 {
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2117,7 +2134,7 @@ private module Stage3 {
|
||||
private predicate flowCandSummaryCtx(NodeEx node, AccessPathFront argApf, Configuration config) {
|
||||
exists(AccessPathFront apf |
|
||||
Stage3::revFlow(node, true, _, apf, config) and
|
||||
Stage3::fwdFlow(node, true, TAccessPathFrontSome(argApf), apf, config)
|
||||
Stage3::fwdFlow(node, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2136,7 +2153,8 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
|
||||
) and
|
||||
accessPathApproxCostLimits(apLimit, tupleLimit) and
|
||||
apLimit < tails and
|
||||
tupleLimit < (tails - 1) * nodes
|
||||
tupleLimit < (tails - 1) * nodes and
|
||||
not tc.forceHighPrecision()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2618,7 +2636,8 @@ private module Stage4 {
|
||||
fwdFlow(out, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlowOutFromArg(call, out, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), argAp0,
|
||||
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
|
||||
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
@@ -2838,8 +2857,12 @@ private module Stage4 {
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2912,6 +2935,8 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome {
|
||||
|
||||
int getParameterPos() { p.isParameterOf(_, result) }
|
||||
|
||||
ParamNodeEx getParamNode() { result = p }
|
||||
|
||||
override string toString() { result = p + ": " + ap }
|
||||
|
||||
predicate hasLocationInfo(
|
||||
@@ -2969,12 +2994,15 @@ private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) {
|
||||
* expected to be expensive. Holds with `unfold = true` otherwise.
|
||||
*/
|
||||
private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) {
|
||||
exists(int aps, int nodes, int apLimit, int tupleLimit |
|
||||
aps = countPotentialAps(apa, config) and
|
||||
nodes = countNodesUsingAccessPath(apa, config) and
|
||||
accessPathCostLimits(apLimit, tupleLimit) and
|
||||
if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true
|
||||
)
|
||||
if apa.getHead().forceHighPrecision()
|
||||
then unfold = true
|
||||
else
|
||||
exists(int aps, int nodes, int apLimit, int tupleLimit |
|
||||
aps = countPotentialAps(apa, config) and
|
||||
nodes = countNodesUsingAccessPath(apa, config) and
|
||||
accessPathCostLimits(apLimit, tupleLimit) and
|
||||
if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3162,7 +3190,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons {
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "[" + this.toStringImpl(true) + length().toString() + ")]"
|
||||
result = "[" + this.toStringImpl(true) + this.length().toString() + ")]"
|
||||
or
|
||||
result = "[" + this.toStringImpl(false)
|
||||
}
|
||||
@@ -3244,7 +3272,7 @@ class PathNode extends TPathNode {
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
|
||||
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
@@ -3301,9 +3329,11 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">"
|
||||
}
|
||||
|
||||
override string toString() { result = this.getNodeEx().toString() + ppAp() }
|
||||
override string toString() { result = this.getNodeEx().toString() + this.ppAp() }
|
||||
|
||||
override string toStringWithContext() { result = this.getNodeEx().toString() + ppAp() + ppCtx() }
|
||||
override string toStringWithContext() {
|
||||
result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx()
|
||||
}
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
@@ -3320,8 +3350,8 @@ private predicate directReach(PathNode n) {
|
||||
/** Holds if `n` can reach a sink or is used in a subpath. */
|
||||
private predicate reach(PathNode n) { directReach(n) or Subpaths::retReach(n) }
|
||||
|
||||
/** Holds if `n1.getSucc() = n2` and `n2` can reach a sink or is used in a subpath. */
|
||||
private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and reach(n2) }
|
||||
/** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */
|
||||
private predicate pathSucc(PathNode n1, PathNode n2) { n1.getASuccessor() = n2 and directReach(n2) }
|
||||
|
||||
private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2)
|
||||
|
||||
@@ -3330,7 +3360,7 @@ private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1
|
||||
*/
|
||||
module PathGraph {
|
||||
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
|
||||
query predicate edges(PathNode a, PathNode b) { pathSucc(a, b) }
|
||||
query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b and reach(b) }
|
||||
|
||||
/** Holds if `n` is a node in the graph of data flow path explanations. */
|
||||
query predicate nodes(PathNode n, string key, string val) {
|
||||
@@ -3371,11 +3401,11 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
|
||||
|
||||
override PathNodeImpl getASuccessorImpl() {
|
||||
// an intermediate step to another intermediate node
|
||||
result = getSuccMid()
|
||||
result = this.getSuccMid()
|
||||
or
|
||||
// a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
|
||||
exists(PathNodeMid mid, PathNodeSink sink |
|
||||
mid = getSuccMid() and
|
||||
mid = this.getSuccMid() and
|
||||
mid.getNodeEx() = sink.getNodeEx() and
|
||||
mid.getAp() instanceof AccessPathNil and
|
||||
sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
|
||||
@@ -3452,7 +3482,7 @@ private predicate pathStep(
|
||||
exists(TypedContent tc | pathReadStep(mid, node, ap.push(tc), tc, cc)) and
|
||||
sc = mid.getSummaryCtx()
|
||||
or
|
||||
pathIntoCallable(mid, node, _, cc, sc, _) and ap = mid.getAp()
|
||||
pathIntoCallable(mid, node, _, cc, sc, _, _) and ap = mid.getAp()
|
||||
or
|
||||
pathOutOfCallable(mid, node, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone
|
||||
or
|
||||
@@ -3529,14 +3559,16 @@ private predicate pathOutOfCallable(PathNodeMid mid, NodeEx out, CallContext cc)
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate pathIntoArg(
|
||||
PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa
|
||||
PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ArgNode arg |
|
||||
arg = mid.getNodeEx().asNode() and
|
||||
cc = mid.getCallContext() and
|
||||
arg.argumentOf(call, i) and
|
||||
ap = mid.getAp() and
|
||||
apa = ap.getApprox()
|
||||
apa = ap.getApprox() and
|
||||
config = mid.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3553,12 +3585,14 @@ private predicate parameterCand(
|
||||
pragma[nomagic]
|
||||
private predicate pathIntoCallable0(
|
||||
PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call,
|
||||
AccessPath ap
|
||||
AccessPath ap, Configuration config
|
||||
) {
|
||||
exists(AccessPathApprox apa |
|
||||
pathIntoArg(mid, i, outercc, call, ap, apa) and
|
||||
pathIntoArg(mid, pragma[only_bind_into](i), outercc, call, ap, pragma[only_bind_into](apa),
|
||||
pragma[only_bind_into](config)) and
|
||||
callable = resolveCall(call, outercc) and
|
||||
parameterCand(callable, any(int j | j <= i and j >= i), apa, mid.getConfiguration())
|
||||
parameterCand(callable, pragma[only_bind_into](i), pragma[only_bind_into](apa),
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3567,12 +3601,13 @@ private predicate pathIntoCallable0(
|
||||
* before and after entering the callable are `outercc` and `innercc`,
|
||||
* respectively.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate pathIntoCallable(
|
||||
PathNodeMid mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc, SummaryCtx sc,
|
||||
DataFlowCall call
|
||||
DataFlowCall call, Configuration config
|
||||
) {
|
||||
exists(int i, DataFlowCallable callable, AccessPath ap |
|
||||
pathIntoCallable0(mid, callable, i, outercc, call, ap) and
|
||||
pathIntoCallable0(mid, callable, i, outercc, call, ap, config) and
|
||||
p.isParameterOf(callable, i) and
|
||||
(
|
||||
sc = TSummaryCtxSome(p, ap)
|
||||
@@ -3602,18 +3637,23 @@ private predicate paramFlowsThrough(
|
||||
ap = mid.getAp() and
|
||||
apa = ap.getApprox() and
|
||||
pos = sc.getParameterPos() and
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
sc.getParamNode().allowParameterReturnInSelf()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate pathThroughCallable0(
|
||||
DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, CallContext cc, AccessPath ap,
|
||||
AccessPathApprox apa
|
||||
AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(CallContext innercc, SummaryCtx sc |
|
||||
pathIntoCallable(mid, _, cc, innercc, sc, call) and
|
||||
paramFlowsThrough(kind, innercc, sc, ap, apa, unbindConf(mid.getConfiguration()))
|
||||
pathIntoCallable(mid, _, cc, innercc, sc, call, config) and
|
||||
paramFlowsThrough(kind, innercc, sc, ap, apa, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3623,9 +3663,9 @@ private predicate pathThroughCallable0(
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate pathThroughCallable(PathNodeMid mid, NodeEx out, CallContext cc, AccessPath ap) {
|
||||
exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa |
|
||||
pathThroughCallable0(call, mid, kind, cc, ap, apa) and
|
||||
out = getAnOutNodeFlow(kind, call, apa, unbindConf(mid.getConfiguration()))
|
||||
exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa, Configuration config |
|
||||
pathThroughCallable0(call, mid, kind, cc, ap, apa, config) and
|
||||
out = getAnOutNodeFlow(kind, call, apa, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3639,9 +3679,11 @@ private module Subpaths {
|
||||
PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
|
||||
NodeEx out, AccessPath apout
|
||||
) {
|
||||
pathThroughCallable(arg, out, _, apout) and
|
||||
pathIntoCallable(arg, par, _, innercc, sc, _) and
|
||||
paramFlowsThrough(kind, innercc, sc, apout, _, unbindConf(arg.getConfiguration()))
|
||||
exists(Configuration config |
|
||||
pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and
|
||||
pathIntoCallable(arg, par, _, innercc, sc, _, config) and
|
||||
paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, unbindConf(config))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3686,8 +3728,8 @@ private module Subpaths {
|
||||
*/
|
||||
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeMid ret, PathNodeMid out) {
|
||||
exists(ParamNodeEx p, NodeEx o, AccessPath apout |
|
||||
arg.getASuccessor() = par and
|
||||
arg.getASuccessor() = out and
|
||||
pragma[only_bind_into](arg).getASuccessor() = par and
|
||||
pragma[only_bind_into](arg).getASuccessor() = out and
|
||||
subpaths03(arg, p, ret, o, apout) and
|
||||
par.getNodeEx() = p and
|
||||
out.getNodeEx() = o and
|
||||
@@ -4028,7 +4070,7 @@ private module FlowExploration {
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
|
||||
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
|
||||
@@ -786,16 +786,24 @@ private module Cached {
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the call context `call` either improves virtual dispatch in
|
||||
* `callable` or if it allows us to prune unreachable nodes in `callable`.
|
||||
* Holds if the call context `call` improves virtual dispatch in `callable`.
|
||||
*/
|
||||
cached
|
||||
predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) {
|
||||
predicate recordDataFlowCallSiteDispatch(DataFlowCall call, DataFlowCallable callable) {
|
||||
reducedViableImplInCallContext(_, callable, call)
|
||||
or
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the call context `call` allows us to prune unreachable nodes in `callable`.
|
||||
*/
|
||||
cached
|
||||
predicate recordDataFlowCallSiteUnreachable(DataFlowCall call, DataFlowCallable callable) {
|
||||
exists(Node n | getNodeEnclosingCallable(n) = callable | isUnreachableInCallCached(n, call))
|
||||
}
|
||||
|
||||
cached
|
||||
predicate allowParameterReturnInSelfCached(ParamNode p) { allowParameterReturnInSelf(p) }
|
||||
|
||||
cached
|
||||
newtype TCallContext =
|
||||
TAnyCallContext() or
|
||||
@@ -846,6 +854,15 @@ private module Cached {
|
||||
TAccessPathFrontSome(AccessPathFront apf)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the call context `call` either improves virtual dispatch in
|
||||
* `callable` or if it allows us to prune unreachable nodes in `callable`.
|
||||
*/
|
||||
predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) {
|
||||
recordDataFlowCallSiteDispatch(call, callable) or
|
||||
recordDataFlowCallSiteUnreachable(call, callable)
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Node` at which a cast can occur such that the type should be checked.
|
||||
*/
|
||||
@@ -923,7 +940,7 @@ class CallContextSpecificCall extends CallContextCall, TSpecificCall {
|
||||
}
|
||||
|
||||
override predicate relevantFor(DataFlowCallable callable) {
|
||||
recordDataFlowCallSite(getCall(), callable)
|
||||
recordDataFlowCallSite(this.getCall(), callable)
|
||||
}
|
||||
|
||||
override predicate matchesCall(DataFlowCall call) { call = this.getCall() }
|
||||
@@ -1222,6 +1239,13 @@ class TypedContent extends MkTypedContent {
|
||||
|
||||
/** Gets a textual representation of this content. */
|
||||
string toString() { result = c.toString() }
|
||||
|
||||
/**
|
||||
* Holds if access paths with this `TypedContent` at their head always should
|
||||
* be tracked at high precision. This disables adaptive access path precision
|
||||
* for such access paths.
|
||||
*/
|
||||
predicate forceHighPrecision() { forceHighPrecision(c) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1236,7 +1260,7 @@ abstract class AccessPathFront extends TAccessPathFront {
|
||||
|
||||
TypedContent getHead() { this = TFrontHead(result) }
|
||||
|
||||
predicate isClearedAt(Node n) { clearsContentCached(n, getHead().getContent()) }
|
||||
predicate isClearedAt(Node n) { clearsContentCached(n, this.getHead().getContent()) }
|
||||
}
|
||||
|
||||
class AccessPathFrontNil extends AccessPathFront, TFrontNil {
|
||||
|
||||
@@ -175,6 +175,7 @@ module Consistency {
|
||||
|
||||
query predicate postWithInFlow(Node n, string msg) {
|
||||
isPostUpdateNode(n) and
|
||||
not clearsContent(n, _) and
|
||||
simpleLocalFlowStep(_, n) and
|
||||
msg = "PostUpdateNode should not be the target of local flow."
|
||||
}
|
||||
|
||||
@@ -106,11 +106,9 @@ class ReturnNode extends InstructionNode {
|
||||
Instruction primary;
|
||||
|
||||
ReturnNode() {
|
||||
exists(ReturnValueInstruction ret | instr = ret.getReturnValue() and primary = ret)
|
||||
exists(ReturnValueInstruction ret | instr = ret and primary = ret)
|
||||
or
|
||||
exists(ReturnIndirectionInstruction rii |
|
||||
instr = rii.getSideEffectOperand().getAnyDef() and primary = rii
|
||||
)
|
||||
exists(ReturnIndirectionInstruction rii | instr = rii and primary = rii)
|
||||
}
|
||||
|
||||
/** Gets the kind of this returned value. */
|
||||
@@ -184,108 +182,16 @@ OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) {
|
||||
*/
|
||||
predicate jumpStep(Node n1, Node n2) { none() }
|
||||
|
||||
private predicate fieldStoreStepNoChi(Node node1, FieldContent f, PostUpdateNode node2) {
|
||||
exists(StoreInstruction store, Class c |
|
||||
store = node2.asInstruction() and
|
||||
store.getSourceValueOperand() = node1.asOperand() and
|
||||
getWrittenField(store, f.(FieldContent).getAField(), c) and
|
||||
f.hasOffset(c, _, _)
|
||||
)
|
||||
}
|
||||
|
||||
private FieldAddressInstruction getFieldInstruction(Instruction instr) {
|
||||
result = instr or
|
||||
result = instr.(CopyValueInstruction).getUnary()
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate getWrittenField(Instruction instr, Field f, Class c) {
|
||||
exists(FieldAddressInstruction fa |
|
||||
fa =
|
||||
getFieldInstruction([
|
||||
instr.(StoreInstruction).getDestinationAddress(),
|
||||
instr.(WriteSideEffectInstruction).getDestinationAddress()
|
||||
]) and
|
||||
f = fa.getField() and
|
||||
c = f.getDeclaringType()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate fieldStoreStepChi(Node node1, FieldContent f, PostUpdateNode node2) {
|
||||
exists(ChiPartialOperand operand, ChiInstruction chi |
|
||||
chi.getPartialOperand() = operand and
|
||||
node1.asOperand() = operand and
|
||||
node2.asInstruction() = chi and
|
||||
exists(Class c |
|
||||
c = chi.getResultType() and
|
||||
exists(int startBit, int endBit |
|
||||
chi.getUpdatedInterval(startBit, endBit) and
|
||||
f.hasOffset(c, startBit, endBit)
|
||||
)
|
||||
or
|
||||
getWrittenField(operand.getDef(), f.getAField(), c) and
|
||||
f.hasOffset(c, _, _)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate arrayStoreStepChi(Node node1, ArrayContent a, PostUpdateNode node2) {
|
||||
exists(a) and
|
||||
exists(ChiPartialOperand operand, ChiInstruction chi, StoreInstruction store |
|
||||
chi.getPartialOperand() = operand and
|
||||
store = operand.getDef() and
|
||||
node1.asOperand() = operand and
|
||||
// This `ChiInstruction` will always have a non-conflated result because both `ArrayStoreNode`
|
||||
// and `PointerStoreNode` require it in their characteristic predicates.
|
||||
node2.asInstruction() = chi and
|
||||
(
|
||||
// `x[i] = taint()`
|
||||
// This matches the characteristic predicate in `ArrayStoreNode`.
|
||||
store.getDestinationAddress() instanceof PointerAddInstruction
|
||||
or
|
||||
// `*p = taint()`
|
||||
// This matches the characteristic predicate in `PointerStoreNode`.
|
||||
store.getDestinationAddress().(CopyValueInstruction).getUnary() instanceof LoadInstruction
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` via an assignment to `f`.
|
||||
* Thus, `node2` references an object with a field `f` that contains the
|
||||
* value of `node1`.
|
||||
*/
|
||||
predicate storeStep(Node node1, Content f, PostUpdateNode node2) {
|
||||
fieldStoreStepNoChi(node1, f, node2) or
|
||||
fieldStoreStepChi(node1, f, node2) or
|
||||
arrayStoreStepChi(node1, f, node2) or
|
||||
fieldStoreStepAfterArraySuppression(node1, f, node2)
|
||||
}
|
||||
|
||||
// This predicate pushes the correct `FieldContent` onto the access path when the
|
||||
// `suppressArrayRead` predicate has popped off an `ArrayContent`.
|
||||
private predicate fieldStoreStepAfterArraySuppression(
|
||||
Node node1, FieldContent f, PostUpdateNode node2
|
||||
) {
|
||||
exists(WriteSideEffectInstruction write, ChiInstruction chi, Class c |
|
||||
not chi.isResultConflated() and
|
||||
node1.asInstruction() = chi and
|
||||
node2.asInstruction() = chi and
|
||||
chi.getPartial() = write and
|
||||
getWrittenField(write, f.getAField(), c) and
|
||||
f.hasOffset(c, _, _)
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[result, i]
|
||||
private int unbindInt(int i) { i <= result and i >= result }
|
||||
|
||||
pragma[noinline]
|
||||
private predicate getLoadedField(LoadInstruction load, Field f, Class c) {
|
||||
exists(FieldAddressInstruction fa |
|
||||
fa = load.getSourceAddress() and
|
||||
f = fa.getField() and
|
||||
c = f.getDeclaringType()
|
||||
predicate storeStep(StoreNode node1, FieldContent f, StoreNode node2) {
|
||||
exists(FieldAddressInstruction fai |
|
||||
node1.getInstruction() = fai and
|
||||
node2.getInstruction() = fai.getObjectAddress() and
|
||||
f.getField() = fai.getField()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -294,122 +200,14 @@ private predicate getLoadedField(LoadInstruction load, Field f, Class c) {
|
||||
* Thus, `node1` references an object with a field `f` whose value ends up in
|
||||
* `node2`.
|
||||
*/
|
||||
private predicate fieldReadStep(Node node1, FieldContent f, Node node2) {
|
||||
exists(LoadOperand operand |
|
||||
node2.asOperand() = operand and
|
||||
node1.asInstruction() = operand.getAnyDef() and
|
||||
exists(Class c |
|
||||
c = operand.getAnyDef().getResultType() and
|
||||
exists(int startBit, int endBit |
|
||||
operand.getUsedInterval(unbindInt(startBit), unbindInt(endBit)) and
|
||||
f.hasOffset(c, startBit, endBit)
|
||||
)
|
||||
or
|
||||
getLoadedField(operand.getUse(), f.getAField(), c) and
|
||||
f.hasOffset(c, _, _)
|
||||
)
|
||||
predicate readStep(ReadNode node1, FieldContent f, ReadNode node2) {
|
||||
exists(FieldAddressInstruction fai |
|
||||
node1.getInstruction() = fai.getObjectAddress() and
|
||||
node2.getInstruction() = fai and
|
||||
f.getField() = fai.getField()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* When a store step happens in a function that looks like an array write such as:
|
||||
* ```cpp
|
||||
* void f(int* pa) {
|
||||
* pa = source();
|
||||
* }
|
||||
* ```
|
||||
* it can be a write to an array, but it can also happen that `f` is called as `f(&a.x)`. If that is
|
||||
* the case, the `ArrayContent` that was written by the call to `f` should be popped off the access
|
||||
* path, and a `FieldContent` containing `x` should be pushed instead.
|
||||
* So this case pops `ArrayContent` off the access path, and the `fieldStoreStepAfterArraySuppression`
|
||||
* predicate in `storeStep` ensures that we push the right `FieldContent` onto the access path.
|
||||
*/
|
||||
predicate suppressArrayRead(Node node1, ArrayContent a, Node node2) {
|
||||
exists(a) and
|
||||
exists(WriteSideEffectInstruction write, ChiInstruction chi |
|
||||
node1.asInstruction() = write and
|
||||
node2.asInstruction() = chi and
|
||||
chi.getPartial() = write and
|
||||
getWrittenField(write, _, _)
|
||||
)
|
||||
}
|
||||
|
||||
private class ArrayToPointerConvertInstruction extends ConvertInstruction {
|
||||
ArrayToPointerConvertInstruction() {
|
||||
this.getUnary().getResultType() instanceof ArrayType and
|
||||
this.getResultType() instanceof PointerType
|
||||
}
|
||||
}
|
||||
|
||||
private Instruction skipOneCopyValueInstructionRec(CopyValueInstruction copy) {
|
||||
copy.getUnary() = result and not result instanceof CopyValueInstruction
|
||||
or
|
||||
result = skipOneCopyValueInstructionRec(copy.getUnary())
|
||||
}
|
||||
|
||||
private Instruction skipCopyValueInstructions(Operand op) {
|
||||
not result instanceof CopyValueInstruction and result = op.getDef()
|
||||
or
|
||||
result = skipOneCopyValueInstructionRec(op.getDef())
|
||||
}
|
||||
|
||||
private predicate arrayReadStep(Node node1, ArrayContent a, Node node2) {
|
||||
exists(a) and
|
||||
// Explicit dereferences such as `*p` or `p[i]` where `p` is a pointer or array.
|
||||
exists(LoadOperand operand, Instruction address |
|
||||
operand.isDefinitionInexact() and
|
||||
node1.asInstruction() = operand.getAnyDef() and
|
||||
operand = node2.asOperand() and
|
||||
address = skipCopyValueInstructions(operand.getAddressOperand()) and
|
||||
(
|
||||
address instanceof LoadInstruction or
|
||||
address instanceof ArrayToPointerConvertInstruction or
|
||||
address instanceof PointerOffsetInstruction
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* In cases such as:
|
||||
* ```cpp
|
||||
* void f(int* pa) {
|
||||
* *pa = source();
|
||||
* }
|
||||
* ...
|
||||
* int x;
|
||||
* f(&x);
|
||||
* use(x);
|
||||
* ```
|
||||
* the load on `x` in `use(x)` will exactly overlap with its definition (in this case the definition
|
||||
* is a `WriteSideEffect`). This predicate pops the `ArrayContent` (pushed by the store in `f`)
|
||||
* from the access path.
|
||||
*/
|
||||
private predicate exactReadStep(Node node1, ArrayContent a, Node node2) {
|
||||
exists(a) and
|
||||
exists(WriteSideEffectInstruction write, ChiInstruction chi |
|
||||
not chi.isResultConflated() and
|
||||
chi.getPartial() = write and
|
||||
node1.asInstruction() = write and
|
||||
node2.asInstruction() = chi and
|
||||
// To distinquish this case from the `arrayReadStep` case we require that the entire variable was
|
||||
// overwritten by the `WriteSideEffectInstruction` (i.e., there is a load that reads the
|
||||
// entire variable).
|
||||
exists(LoadInstruction load | load.getSourceValue() = chi)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` via a read of `f`.
|
||||
* Thus, `node1` references an object with a field `f` whose value ends up in
|
||||
* `node2`.
|
||||
*/
|
||||
predicate readStep(Node node1, Content f, Node node2) {
|
||||
fieldReadStep(node1, f, node2) or
|
||||
arrayReadStep(node1, f, node2) or
|
||||
exactReadStep(node1, f, node2) or
|
||||
suppressArrayRead(node1, f, node2)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if values stored inside content `c` are cleared at node `n`.
|
||||
*/
|
||||
@@ -441,7 +239,7 @@ private predicate suppressUnusedNode(Node n) { any() }
|
||||
// Java QL library compatibility wrappers
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/** A node that performs a type cast. */
|
||||
class CastNode extends InstructionNode {
|
||||
class CastNode extends Node {
|
||||
CastNode() { none() } // stub implementation
|
||||
}
|
||||
|
||||
@@ -466,6 +264,12 @@ predicate isUnreachableInCall(Node n, DataFlowCall call) { none() } // stub impl
|
||||
|
||||
int accessPathLimit() { result = 5 }
|
||||
|
||||
/**
|
||||
* Holds if access paths with `c` at their head always should be tracked at high
|
||||
* precision. This disables adaptive access path precision for such access paths.
|
||||
*/
|
||||
predicate forceHighPrecision(Content c) { none() }
|
||||
|
||||
/** The unit type. */
|
||||
private newtype TUnit = TMkUnit()
|
||||
|
||||
@@ -501,3 +305,12 @@ predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) { no
|
||||
|
||||
/** Extra data-flow steps needed for lambda flow analysis. */
|
||||
predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preservesValue) { none() }
|
||||
|
||||
/**
|
||||
* Holds if flow is allowed to pass from parameter `p` and back to itself as a
|
||||
* side-effect, resulting in a summary from `p` to itself.
|
||||
*
|
||||
* One example would be to allow flow like `p.foo = p.bar;`, which is disallowed
|
||||
* by default as a heuristic.
|
||||
*/
|
||||
predicate allowParameterReturnInSelf(ParameterNode p) { none() }
|
||||
|
||||
@@ -10,6 +10,8 @@ private import semmle.code.cpp.ir.ValueNumbering
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import semmle.code.cpp.controlflow.IRGuards
|
||||
private import semmle.code.cpp.models.interfaces.DataFlow
|
||||
private import DataFlowPrivate
|
||||
private import Ssa as Ssa
|
||||
|
||||
cached
|
||||
private module Cached {
|
||||
@@ -17,12 +19,30 @@ private module Cached {
|
||||
newtype TIRDataFlowNode =
|
||||
TInstructionNode(Instruction i) or
|
||||
TOperandNode(Operand op) or
|
||||
TVariableNode(Variable var)
|
||||
TVariableNode(Variable var) or
|
||||
TStoreNodeInstr(Instruction i) { Ssa::explicitWrite(_, _, i) } or
|
||||
TStoreNodeOperand(ArgumentOperand op) { Ssa::explicitWrite(_, _, op.getDef()) } or
|
||||
TReadNode(Instruction i) { needsPostReadNode(i) } or
|
||||
TSsaPhiNode(Ssa::PhiNode phi)
|
||||
|
||||
cached
|
||||
predicate localFlowStepCached(Node nodeFrom, Node nodeTo) {
|
||||
simpleLocalFlowStep(nodeFrom, nodeTo)
|
||||
}
|
||||
|
||||
private predicate needsPostReadNode(Instruction iFrom) {
|
||||
// If the instruction generates an address that flows to a load.
|
||||
Ssa::addressFlowTC(iFrom, Ssa::getSourceAddress(_)) and
|
||||
(
|
||||
// And it is either a field address
|
||||
iFrom instanceof FieldAddressInstruction
|
||||
or
|
||||
// Or it is instruction that either uses or is used for an address that needs a post read node.
|
||||
exists(Instruction mid | needsPostReadNode(mid) |
|
||||
Ssa::addressFlow(mid, iFrom) or Ssa::addressFlow(iFrom, mid)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private import Cached
|
||||
@@ -110,7 +130,7 @@ class Node extends TIRDataFlowNode {
|
||||
/**
|
||||
* Gets an upper bound on the type of this node.
|
||||
*/
|
||||
IRType getTypeBound() { result = getType() }
|
||||
IRType getTypeBound() { result = this.getType() }
|
||||
|
||||
/** Gets the location of this element. */
|
||||
Location getLocation() { none() } // overridden by subclasses
|
||||
@@ -120,7 +140,7 @@ class Node extends TIRDataFlowNode {
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
|
||||
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
@@ -180,6 +200,186 @@ class OperandNode extends Node, TOperandNode {
|
||||
override string toString() { result = this.getOperand().toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL: do not use.
|
||||
*
|
||||
* A `StoreNode` is a node that has been (or is about to be) the
|
||||
* source or target of a `storeStep`.
|
||||
*/
|
||||
abstract class StoreNode extends Node {
|
||||
/** Gets the underlying instruction, if any. */
|
||||
Instruction getInstruction() { none() }
|
||||
|
||||
/** Gets the underlying operand, if any. */
|
||||
Operand getOperand() { none() }
|
||||
|
||||
/** Holds if this node should receive flow from `addr`. */
|
||||
abstract predicate flowInto(Instruction addr);
|
||||
|
||||
override Declaration getEnclosingCallable() { result = this.getFunction() }
|
||||
|
||||
/** Holds if this `StoreNode` is the root of the address computation used by a store operation. */
|
||||
predicate isTerminal() {
|
||||
not exists(this.getAPredecessor()) and
|
||||
not storeStep(this, _, _)
|
||||
}
|
||||
|
||||
/** Gets the store operation that uses the address computed by this `StoreNode`. */
|
||||
abstract Instruction getStoreInstruction();
|
||||
|
||||
/** Holds if the store operation associated with this `StoreNode` overwrites the entire variable. */
|
||||
final predicate isCertain() { Ssa::explicitWrite(true, this.getStoreInstruction(), _) }
|
||||
|
||||
/**
|
||||
* Gets the `StoreNode` that computes the address used by this `StoreNode`.
|
||||
* The boolean `readEffect` is `true` if the predecessor is accessed through the
|
||||
* address of a `ReadSideEffectInstruction`.
|
||||
*/
|
||||
abstract StoreNode getAPredecessor();
|
||||
|
||||
/** The inverse of `StoreNode.getAPredecessor`. */
|
||||
final StoreNode getASuccessor() { result.getAPredecessor() = this }
|
||||
}
|
||||
|
||||
private class StoreNodeInstr extends StoreNode, TStoreNodeInstr {
|
||||
Instruction instr;
|
||||
|
||||
StoreNodeInstr() { this = TStoreNodeInstr(instr) }
|
||||
|
||||
override predicate flowInto(Instruction addr) { this.getInstruction() = addr }
|
||||
|
||||
override Instruction getInstruction() { result = instr }
|
||||
|
||||
override Function getFunction() { result = this.getInstruction().getEnclosingFunction() }
|
||||
|
||||
override IRType getType() { result = this.getInstruction().getResultIRType() }
|
||||
|
||||
override Location getLocation() { result = this.getInstruction().getLocation() }
|
||||
|
||||
override string toString() {
|
||||
result = instructionNode(this.getInstruction()).toString() + " [store]"
|
||||
}
|
||||
|
||||
override Instruction getStoreInstruction() {
|
||||
Ssa::explicitWrite(_, result, this.getInstruction())
|
||||
}
|
||||
|
||||
override StoreNode getAPredecessor() {
|
||||
Ssa::addressFlow(result.getInstruction(), this.getInstruction())
|
||||
}
|
||||
}
|
||||
|
||||
private class StoreNodeOperand extends StoreNode, TStoreNodeOperand {
|
||||
ArgumentOperand operand;
|
||||
|
||||
StoreNodeOperand() { this = TStoreNodeOperand(operand) }
|
||||
|
||||
override predicate flowInto(Instruction addr) { this.getOperand().getDef() = addr }
|
||||
|
||||
override Operand getOperand() { result = operand }
|
||||
|
||||
override Function getFunction() { result = operand.getDef().getEnclosingFunction() }
|
||||
|
||||
override IRType getType() { result = operand.getIRType() }
|
||||
|
||||
override Location getLocation() { result = operand.getLocation() }
|
||||
|
||||
override string toString() { result = operandNode(this.getOperand()).toString() + " [store]" }
|
||||
|
||||
override WriteSideEffectInstruction getStoreInstruction() {
|
||||
Ssa::explicitWrite(_, result, operand.getDef())
|
||||
}
|
||||
|
||||
override StoreNode getAPredecessor() { operand.getDef() = result.getInstruction() }
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL: do not use.
|
||||
*
|
||||
* A `ReadNode` is a node that has been (or is about to be) the
|
||||
* source or target of a `readStep`.
|
||||
*/
|
||||
class ReadNode extends Node, TReadNode {
|
||||
Instruction i;
|
||||
|
||||
ReadNode() { this = TReadNode(i) }
|
||||
|
||||
/** Gets the underlying instruction. */
|
||||
Instruction getInstruction() { result = i }
|
||||
|
||||
override Declaration getEnclosingCallable() { result = this.getFunction() }
|
||||
|
||||
override Function getFunction() { result = this.getInstruction().getEnclosingFunction() }
|
||||
|
||||
override IRType getType() { result = this.getInstruction().getResultIRType() }
|
||||
|
||||
override Location getLocation() { result = this.getInstruction().getLocation() }
|
||||
|
||||
override string toString() {
|
||||
result = instructionNode(this.getInstruction()).toString() + " [read]"
|
||||
}
|
||||
|
||||
/** Gets a load instruction that uses the address computed by this read node. */
|
||||
final Instruction getALoadInstruction() {
|
||||
Ssa::addressFlowTC(this.getInstruction(), Ssa::getSourceAddress(result))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a read node with an underlying instruction that is used by this
|
||||
* underlying instruction to compute an address of a load instruction.
|
||||
*/
|
||||
final ReadNode getAPredecessor() {
|
||||
Ssa::addressFlow(result.getInstruction(), this.getInstruction())
|
||||
}
|
||||
|
||||
/** The inverse of `ReadNode.getAPredecessor`. */
|
||||
final ReadNode getASuccessor() { result.getAPredecessor() = this }
|
||||
|
||||
/** Holds if this read node computes a value that will not be used for any future read nodes. */
|
||||
final predicate isTerminal() {
|
||||
not exists(this.getASuccessor()) and
|
||||
not readStep(this, _, _)
|
||||
}
|
||||
|
||||
/** Holds if this read node computes a value that has not yet been used for any read operations. */
|
||||
final predicate isInitial() {
|
||||
not exists(this.getAPredecessor()) and
|
||||
not readStep(_, _, this)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL: do not use.
|
||||
*
|
||||
* A phi node produced by the shared SSA library, viewed as a node in a data flow graph.
|
||||
*/
|
||||
class SsaPhiNode extends Node, TSsaPhiNode {
|
||||
Ssa::PhiNode phi;
|
||||
|
||||
SsaPhiNode() { this = TSsaPhiNode(phi) }
|
||||
|
||||
/* Get the phi node associated with this node. */
|
||||
Ssa::PhiNode getPhiNode() { result = phi }
|
||||
|
||||
override Declaration getEnclosingCallable() { result = this.getFunction() }
|
||||
|
||||
override Function getFunction() { result = phi.getBasicBlock().getEnclosingFunction() }
|
||||
|
||||
override IRType getType() { result instanceof IRVoidType }
|
||||
|
||||
override Location getLocation() { result = phi.getBasicBlock().getLocation() }
|
||||
|
||||
/** Holds if this phi node has input from the `rnk`'th write operation in block `block`. */
|
||||
cached
|
||||
final predicate hasInputAtRankInBlock(IRBlock block, int rnk) {
|
||||
exists(Ssa::Definition input |
|
||||
Ssa::phiHasInputFromBlock(phi, input, _) and input.definesAt(_, block, rnk)
|
||||
)
|
||||
}
|
||||
|
||||
override string toString() { result = "Phi" }
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression, viewed as a node in a data flow graph.
|
||||
*/
|
||||
@@ -313,15 +513,14 @@ deprecated class UninitializedNode extends Node {
|
||||
* Nodes corresponding to AST elements, for example `ExprNode`, usually refer
|
||||
* to the value before the update with the exception of `ClassInstanceExpr`,
|
||||
* which represents the value after the constructor has run.
|
||||
*
|
||||
* This class exists to match the interface used by Java. There are currently no non-abstract
|
||||
* classes that extend it. When we implement field flow, we can revisit this.
|
||||
*/
|
||||
abstract class PostUpdateNode extends InstructionNode {
|
||||
abstract class PostUpdateNode extends Node {
|
||||
/**
|
||||
* Gets the node before the state update.
|
||||
*/
|
||||
abstract Node getPreUpdateNode();
|
||||
|
||||
override string toString() { result = this.getPreUpdateNode() + " [post update]" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -332,7 +531,7 @@ abstract class PostUpdateNode extends InstructionNode {
|
||||
* value, but does not necessarily replace it entirely. For example:
|
||||
* ```
|
||||
* x.y = 1; // a partial definition of the object `x`.
|
||||
* x.y.z = 1; // a partial definition of the object `x.y`.
|
||||
* x.y.z = 1; // a partial definition of the object `x.y` and `x`.
|
||||
* x.setY(1); // a partial definition of the object `x`.
|
||||
* setY(&x); // a partial definition of the object `x`.
|
||||
* ```
|
||||
@@ -341,135 +540,34 @@ abstract private class PartialDefinitionNode extends PostUpdateNode {
|
||||
abstract Expr getDefinedExpr();
|
||||
}
|
||||
|
||||
private class ExplicitFieldStoreQualifierNode extends PartialDefinitionNode {
|
||||
override ChiInstruction instr;
|
||||
StoreInstruction store;
|
||||
|
||||
ExplicitFieldStoreQualifierNode() {
|
||||
not instr.isResultConflated() and
|
||||
instr.getPartial() = store and
|
||||
(
|
||||
instr.getUpdatedInterval(_, _) or
|
||||
store.getDestinationAddress() instanceof FieldAddressInstruction
|
||||
)
|
||||
private class FieldPartialDefinitionNode extends PartialDefinitionNode, StoreNodeInstr {
|
||||
FieldPartialDefinitionNode() {
|
||||
this.getInstruction() = any(FieldAddressInstruction fai).getObjectAddress()
|
||||
}
|
||||
|
||||
// By using an operand as the result of this predicate we avoid the dataflow inconsistency errors
|
||||
// caused by having multiple nodes sharing the same pre update node. This inconsistency error can cause
|
||||
// a tuple explosion in the big step dataflow relation since it can make many nodes be the entry node
|
||||
// into a big step.
|
||||
override Node getPreUpdateNode() { result.asOperand() = instr.getTotalOperand() }
|
||||
override Node getPreUpdateNode() { result.asInstruction() = this.getInstruction() }
|
||||
|
||||
override Expr getDefinedExpr() { result = this.getInstruction().getUnconvertedResultExpression() }
|
||||
|
||||
override string toString() { result = PartialDefinitionNode.super.toString() }
|
||||
}
|
||||
|
||||
private class NonPartialDefinitionPostUpdate extends PostUpdateNode, StoreNodeInstr {
|
||||
NonPartialDefinitionPostUpdate() { not this instanceof PartialDefinitionNode }
|
||||
|
||||
override Node getPreUpdateNode() { result.asInstruction() = this.getInstruction() }
|
||||
|
||||
override string toString() { result = PostUpdateNode.super.toString() }
|
||||
}
|
||||
|
||||
private class ArgumentPostUpdateNode extends PartialDefinitionNode, StoreNodeOperand {
|
||||
override ArgumentNode getPreUpdateNode() { result.asOperand() = operand }
|
||||
|
||||
override Expr getDefinedExpr() {
|
||||
result =
|
||||
store
|
||||
.getDestinationAddress()
|
||||
.(FieldAddressInstruction)
|
||||
.getObjectAddress()
|
||||
.getUnconvertedResultExpression()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Not every store instruction generates a chi instruction that we can attach a PostUpdateNode to.
|
||||
* For instance, an update to a field of a struct containing only one field. Even if the store does
|
||||
* have a chi instruction, a subsequent use of the result of the store may be linked directly to the
|
||||
* result of the store as an inexact definition if the store totally overlaps the use. For these
|
||||
* cases we attach the PostUpdateNode to the store instruction. There's no obvious pre update node
|
||||
* for this case (as the entire memory is updated), so `getPreUpdateNode` is implemented as
|
||||
* `none()`.
|
||||
*/
|
||||
private class ExplicitSingleFieldStoreQualifierNode extends PartialDefinitionNode {
|
||||
override StoreInstruction instr;
|
||||
|
||||
ExplicitSingleFieldStoreQualifierNode() {
|
||||
(
|
||||
instr.getAUse().isDefinitionInexact()
|
||||
or
|
||||
not exists(ChiInstruction chi | chi.getPartial() = instr)
|
||||
) and
|
||||
// Without this condition any store would create a `PostUpdateNode`.
|
||||
instr.getDestinationAddress() instanceof FieldAddressInstruction
|
||||
result = this.getOperand().getDef().getUnconvertedResultExpression()
|
||||
}
|
||||
|
||||
override Node getPreUpdateNode() { none() }
|
||||
|
||||
override Expr getDefinedExpr() {
|
||||
result =
|
||||
instr
|
||||
.getDestinationAddress()
|
||||
.(FieldAddressInstruction)
|
||||
.getObjectAddress()
|
||||
.getUnconvertedResultExpression()
|
||||
}
|
||||
}
|
||||
|
||||
private FieldAddressInstruction getFieldInstruction(Instruction instr) {
|
||||
result = instr or
|
||||
result = instr.(CopyValueInstruction).getUnary()
|
||||
}
|
||||
|
||||
/**
|
||||
* The target of a `fieldStoreStepAfterArraySuppression` store step, which is used to convert
|
||||
* an `ArrayContent` to a `FieldContent` when the `WriteSideEffect` instruction stores
|
||||
* into a field. See the QLDoc for `suppressArrayRead` for an example of where such a conversion
|
||||
* is inserted.
|
||||
*/
|
||||
private class WriteSideEffectFieldStoreQualifierNode extends PartialDefinitionNode {
|
||||
override ChiInstruction instr;
|
||||
WriteSideEffectInstruction write;
|
||||
FieldAddressInstruction field;
|
||||
|
||||
WriteSideEffectFieldStoreQualifierNode() {
|
||||
not instr.isResultConflated() and
|
||||
instr.getPartial() = write and
|
||||
field = getFieldInstruction(write.getDestinationAddress())
|
||||
}
|
||||
|
||||
override Node getPreUpdateNode() { result.asOperand() = instr.getTotalOperand() }
|
||||
|
||||
override Expr getDefinedExpr() {
|
||||
result = field.getObjectAddress().getUnconvertedResultExpression()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The `PostUpdateNode` that is the target of a `arrayStoreStepChi` store step. The overriden
|
||||
* `ChiInstruction` corresponds to the instruction represented by `node2` in `arrayStoreStepChi`.
|
||||
*/
|
||||
private class ArrayStoreNode extends PartialDefinitionNode {
|
||||
override ChiInstruction instr;
|
||||
PointerAddInstruction add;
|
||||
|
||||
ArrayStoreNode() {
|
||||
not instr.isResultConflated() and
|
||||
exists(StoreInstruction store |
|
||||
instr.getPartial() = store and
|
||||
add = store.getDestinationAddress()
|
||||
)
|
||||
}
|
||||
|
||||
override Node getPreUpdateNode() { result.asOperand() = instr.getTotalOperand() }
|
||||
|
||||
override Expr getDefinedExpr() { result = add.getLeft().getUnconvertedResultExpression() }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `PostUpdateNode` that is the target of a `arrayStoreStepChi` store step. The overriden
|
||||
* `ChiInstruction` corresponds to the instruction represented by `node2` in `arrayStoreStepChi`.
|
||||
*/
|
||||
private class PointerStoreNode extends PostUpdateNode {
|
||||
override ChiInstruction instr;
|
||||
|
||||
PointerStoreNode() {
|
||||
not instr.isResultConflated() and
|
||||
exists(StoreInstruction store |
|
||||
instr.getPartial() = store and
|
||||
store.getDestinationAddress().(CopyValueInstruction).getUnary() instanceof LoadInstruction
|
||||
)
|
||||
}
|
||||
|
||||
override Node getPreUpdateNode() { result.asOperand() = instr.getTotalOperand() }
|
||||
override string toString() { result = PartialDefinitionNode.super.toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -548,6 +646,11 @@ class VariableNode extends Node, TVariableNode {
|
||||
*/
|
||||
InstructionNode instructionNode(Instruction instr) { result.getInstruction() = instr }
|
||||
|
||||
/**
|
||||
* Gets the node corresponding to `operand`.
|
||||
*/
|
||||
OperandNode operandNode(Operand operand) { result.getOperand() = operand }
|
||||
|
||||
/**
|
||||
* DEPRECATED: use `definitionByReferenceNodeFromArgument` instead.
|
||||
*
|
||||
@@ -614,61 +717,182 @@ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
|
||||
or
|
||||
// Instruction -> Operand flow
|
||||
simpleOperandLocalFlowStep(nodeFrom.asInstruction(), nodeTo.asOperand())
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate getFieldSizeOfClass(Class c, Type type, int size) {
|
||||
exists(Field f |
|
||||
f.getDeclaringType() = c and
|
||||
f.getUnderlyingType() = type and
|
||||
type.getSize() = size
|
||||
or
|
||||
// Flow into, through, and out of store nodes
|
||||
StoreNodeFlow::flowInto(nodeFrom, nodeTo)
|
||||
or
|
||||
StoreNodeFlow::flowThrough(nodeFrom, nodeTo)
|
||||
or
|
||||
StoreNodeFlow::flowOutOf(nodeFrom, nodeTo)
|
||||
or
|
||||
// Flow into, through, and out of read nodes
|
||||
ReadNodeFlow::flowInto(nodeFrom, nodeTo)
|
||||
or
|
||||
ReadNodeFlow::flowThrough(nodeFrom, nodeTo)
|
||||
or
|
||||
ReadNodeFlow::flowOutOf(nodeFrom, nodeTo)
|
||||
or
|
||||
// Adjacent-def-use and adjacent-use-use flow
|
||||
adjacentDefUseFlow(nodeFrom, nodeTo)
|
||||
or
|
||||
// When we want to transfer flow out of a `StoreNode` we perform two steps:
|
||||
// 1. Find the next use of the address being stored to
|
||||
// 2. Find the `LoadInstruction` that loads the address
|
||||
// When the address being stored into doesn't have a `LoadInstruction` associated with it because it's
|
||||
// passed into a `CallInstruction` we transfer flow to the `ReadSideEffect`, which will then flow into
|
||||
// the callee. We then pickup the flow from the `InitializeIndirectionInstruction` and use the shared
|
||||
// SSA library to determine where the next use of the address that received the flow is.
|
||||
exists(Node init |
|
||||
nodeFrom.asInstruction().(InitializeIndirectionInstruction).getIRVariable() =
|
||||
init.asInstruction().(InitializeParameterInstruction).getIRVariable() and
|
||||
// No need for the flow if the next use is the instruction that returns the flow out of the callee.
|
||||
not nodeTo.asInstruction() instanceof ReturnIndirectionInstruction and
|
||||
Ssa::ssaFlow(init, nodeTo)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate isSingleFieldClass(Type type, Operand op) {
|
||||
exists(int size, Class c |
|
||||
c = op.getType().getUnderlyingType() and
|
||||
c.getSize() = size and
|
||||
getFieldSizeOfClass(c, type, size)
|
||||
private predicate adjacentDefUseFlow(Node nodeFrom, Node nodeTo) {
|
||||
// Flow that isn't already covered by field flow out of store/read nodes.
|
||||
not nodeFrom.asInstruction() = any(StoreNode pun).getStoreInstruction() and
|
||||
not nodeFrom.asInstruction() = any(ReadNode pun).getALoadInstruction() and
|
||||
(
|
||||
//Def-use flow
|
||||
Ssa::ssaFlow(nodeFrom, nodeTo)
|
||||
or
|
||||
exists(Instruction loadAddress | loadAddress = Ssa::getSourceAddressFromNode(nodeFrom) |
|
||||
// Use-use flow through reads
|
||||
exists(Node address |
|
||||
Ssa::addressFlowTC(address.asInstruction(), loadAddress) and
|
||||
Ssa::ssaFlow(address, nodeTo)
|
||||
)
|
||||
or
|
||||
// Use-use flow through stores.
|
||||
exists(Node store |
|
||||
Ssa::explicitWrite(_, store.asInstruction(), loadAddress) and
|
||||
Ssa::ssaFlow(store, nodeTo)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private module ReadNodeFlow {
|
||||
/** Holds if the read node `nodeTo` should receive flow from `nodeFrom`. */
|
||||
predicate flowInto(Node nodeFrom, ReadNode nodeTo) {
|
||||
nodeTo.isInitial() and
|
||||
(
|
||||
// If we entered through an address operand.
|
||||
nodeFrom.asOperand().getDef() = nodeTo.getInstruction()
|
||||
or
|
||||
// If we entered flow through a memory-producing instruction.
|
||||
// This can happen if we have flow to an `InitializeParameterIndirection` through
|
||||
// a `ReadSideEffectInstruction`.
|
||||
exists(Instruction load, Instruction def |
|
||||
def = nodeFrom.asInstruction() and
|
||||
def = Ssa::getSourceValueOperand(load).getAnyDef() and
|
||||
not def = any(StoreNode store).getStoreInstruction() and
|
||||
pragma[only_bind_into](nodeTo).getALoadInstruction() = load
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if the read node `nodeTo` should receive flow from the read node `nodeFrom`. */
|
||||
predicate flowThrough(ReadNode nodeFrom, ReadNode nodeTo) {
|
||||
not readStep(nodeFrom, _, _) and
|
||||
nodeFrom.getASuccessor() = nodeTo
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if flow should leave the read node `nFrom` and enter the node `nodeTo`.
|
||||
* This happens either because there is use-use flow from one of the variables used in
|
||||
* the read operation, or because we have traversed all the field dereferences in the
|
||||
* read operation.
|
||||
*/
|
||||
predicate flowOutOf(ReadNode nFrom, Node nodeTo) {
|
||||
// Use-use flow to another use of the same variable instruction
|
||||
Ssa::ssaFlow(nFrom, nodeTo)
|
||||
or
|
||||
not exists(nFrom.getAPredecessor()) and
|
||||
exists(Node store |
|
||||
Ssa::explicitWrite(_, store.asInstruction(), nFrom.getInstruction()) and
|
||||
Ssa::ssaFlow(store, nodeTo)
|
||||
)
|
||||
or
|
||||
// Flow out of read nodes and into memory instructions if we cannot move any further through
|
||||
// read nodes.
|
||||
nFrom.isTerminal() and
|
||||
(
|
||||
exists(Instruction load |
|
||||
load = nodeTo.asInstruction() and
|
||||
Ssa::getSourceAddress(load) = nFrom.getInstruction()
|
||||
)
|
||||
or
|
||||
exists(CallInstruction call, int i |
|
||||
call.getArgument(i) = nodeTo.asInstruction() and
|
||||
call.getArgument(i) = nFrom.getInstruction()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private module StoreNodeFlow {
|
||||
/** Holds if the store node `nodeTo` should receive flow from `nodeFrom`. */
|
||||
predicate flowInto(Node nodeFrom, StoreNode nodeTo) {
|
||||
nodeTo.flowInto(Ssa::getDestinationAddress(nodeFrom.asInstruction()))
|
||||
}
|
||||
|
||||
/** Holds if the store node `nodeTo` should receive flow from `nodeFom`. */
|
||||
predicate flowThrough(StoreNode nFrom, StoreNode nodeTo) {
|
||||
// Flow through a post update node that doesn't need a store step.
|
||||
not storeStep(nFrom, _, _) and
|
||||
nodeTo.getASuccessor() = nFrom
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if flow should leave the store node `nodeFrom` and enter the node `nodeTo`.
|
||||
* This happens because we have traversed an entire chain of field dereferences
|
||||
* after a store operation.
|
||||
*/
|
||||
predicate flowOutOf(StoreNode nFrom, Node nodeTo) {
|
||||
nFrom.isTerminal() and
|
||||
Ssa::ssaFlow(nFrom, nodeTo)
|
||||
}
|
||||
}
|
||||
|
||||
private predicate simpleOperandLocalFlowStep(Instruction iFrom, Operand opTo) {
|
||||
// Propagate flow from an instruction to its exact uses.
|
||||
// We do this for all instruction/operand pairs, except when the operand is the
|
||||
// side effect operand of a ReturnIndirectionInstruction, or the load operand of a LoadInstruction.
|
||||
// This is because we get these flows through the shared SSA library already, and including this
|
||||
// flow here will create multiple dataflow paths which creates a blowup in stage 3 of dataflow.
|
||||
(
|
||||
not any(ReturnIndirectionInstruction ret).getSideEffectOperand() = opTo and
|
||||
not any(LoadInstruction load).getSourceValueOperand() = opTo and
|
||||
not any(ReturnValueInstruction ret).getReturnValueOperand() = opTo
|
||||
) and
|
||||
opTo.getDef() = iFrom
|
||||
or
|
||||
opTo = any(ReadSideEffectInstruction read).getSideEffectOperand() and
|
||||
not iFrom.isResultConflated() and
|
||||
iFrom = opTo.getAnyDef()
|
||||
or
|
||||
// Loading a single `int` from an `int *` parameter is not an exact load since
|
||||
// the parameter may point to an entire array rather than a single `int`. The
|
||||
// following rule ensures that any flow going into the
|
||||
// `InitializeIndirectionInstruction`, even if it's for a different array
|
||||
// element, will propagate to a load of the first element.
|
||||
//
|
||||
// Since we're linking `InitializeIndirectionInstruction` and
|
||||
// `LoadInstruction` together directly, this rule will break if there's any
|
||||
// reassignment of the parameter indirection, including a conditional one that
|
||||
// leads to a phi node.
|
||||
exists(InitializeIndirectionInstruction init |
|
||||
iFrom = init and
|
||||
opTo.(LoadOperand).getAnyDef() = init and
|
||||
// Check that the types match. Otherwise we can get flow from an object to
|
||||
// its fields, which leads to field conflation when there's flow from other
|
||||
// fields to the object elsewhere.
|
||||
init.getParameter().getType().getUnspecifiedType().(DerivedType).getBaseType() =
|
||||
opTo.getType().getUnspecifiedType()
|
||||
)
|
||||
or
|
||||
// Flow from stores to structs with a single field to a load of that field.
|
||||
exists(LoadInstruction load |
|
||||
load.getSourceValueOperand() = opTo and
|
||||
opTo.getAnyDef() = iFrom and
|
||||
isSingleFieldClass(pragma[only_bind_out](pragma[only_bind_out](iFrom).getResultType()), opTo)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate getAddressType(LoadInstruction load, Type t) {
|
||||
exists(Instruction address |
|
||||
address = load.getSourceAddress() and
|
||||
t = address.getResultType()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Like the AST dataflow library, we want to conflate the address and value of a reference. This class
|
||||
* represents the `LoadInstruction` that is generated from a reference dereference.
|
||||
*/
|
||||
private class ReferenceDereferenceInstruction extends LoadInstruction {
|
||||
ReferenceDereferenceInstruction() {
|
||||
exists(ReferenceType ref |
|
||||
getAddressType(this, ref) and
|
||||
this.getResultType() = ref.getBaseType()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private predicate simpleInstructionLocalFlowStep(Operand opFrom, Instruction iTo) {
|
||||
iTo.(CopyInstruction).getSourceValueOperand() = opFrom
|
||||
or
|
||||
@@ -681,40 +905,8 @@ private predicate simpleInstructionLocalFlowStep(Operand opFrom, Instruction iTo
|
||||
or
|
||||
iTo.(InheritanceConversionInstruction).getUnaryOperand() = opFrom
|
||||
or
|
||||
// A chi instruction represents a point where a new value (the _partial_
|
||||
// operand) may overwrite an old value (the _total_ operand), but the alias
|
||||
// analysis couldn't determine that it surely will overwrite every bit of it or
|
||||
// that it surely will overwrite no bit of it.
|
||||
//
|
||||
// By allowing flow through the total operand, we ensure that flow is not lost
|
||||
// due to shortcomings of the alias analysis. We may get false flow in cases
|
||||
// where the data is indeed overwritten.
|
||||
//
|
||||
// Flow through the partial operand belongs in the taint-tracking libraries
|
||||
// for now.
|
||||
iTo.getAnOperand().(ChiTotalOperand) = opFrom
|
||||
or
|
||||
// Add flow from write side-effects to non-conflated chi instructions through their
|
||||
// partial operands. From there, a `readStep` will find subsequent reads of that field.
|
||||
// Consider the following example:
|
||||
// ```
|
||||
// void setX(Point* p, int new_x) {
|
||||
// p->x = new_x;
|
||||
// }
|
||||
// ...
|
||||
// setX(&p, taint());
|
||||
// ```
|
||||
// Here, a `WriteSideEffectInstruction` will provide a new definition for `p->x` after the call to
|
||||
// `setX`, which will be melded into `p` through a chi instruction.
|
||||
exists(ChiInstruction chi | chi = iTo |
|
||||
opFrom.getAnyDef() instanceof WriteSideEffectInstruction and
|
||||
chi.getPartialOperand() = opFrom and
|
||||
not chi.isResultConflated() and
|
||||
// In a call such as `set_value(&x->val);` we don't want the memory representing `x` to receive
|
||||
// dataflow by a simple step. Instead, this is handled by field flow. If we add a simple step here
|
||||
// we can get field-to-object flow.
|
||||
not chi.isPartialUpdate()
|
||||
)
|
||||
// Conflate references and values like in AST dataflow.
|
||||
iTo.(ReferenceDereferenceInstruction).getSourceAddressOperand() = opFrom
|
||||
or
|
||||
// Flow through modeled functions
|
||||
modelFlow(opFrom, iTo)
|
||||
@@ -788,25 +980,10 @@ predicate localInstructionFlow(Instruction e1, Instruction e2) {
|
||||
*/
|
||||
predicate localExprFlow(Expr e1, Expr e2) { localFlow(exprNode(e1), exprNode(e2)) }
|
||||
|
||||
/**
|
||||
* Gets a field corresponding to the bit range `[startBit..endBit)` of class `c`, if any.
|
||||
*/
|
||||
private Field getAField(Class c, int startBit, int endBit) {
|
||||
result.getDeclaringType() = c and
|
||||
startBit = 8 * result.getByteOffset() and
|
||||
endBit = 8 * result.getType().getSize() + startBit
|
||||
or
|
||||
exists(Field f, Class cInner |
|
||||
f = c.getAField() and
|
||||
cInner = f.getUnderlyingType() and
|
||||
result = getAField(cInner, startBit - 8 * f.getByteOffset(), endBit - 8 * f.getByteOffset())
|
||||
)
|
||||
}
|
||||
|
||||
private newtype TContent =
|
||||
TFieldContent(Class c, int startBit, int endBit) { exists(getAField(c, startBit, endBit)) } or
|
||||
TCollectionContent() or
|
||||
TArrayContent()
|
||||
TFieldContent(Field f) or
|
||||
TCollectionContent() or // Not used in C/C++
|
||||
TArrayContent() // Not used in C/C++.
|
||||
|
||||
/**
|
||||
* A description of the way data may be stored inside an object. Examples
|
||||
@@ -824,18 +1001,13 @@ class Content extends TContent {
|
||||
|
||||
/** A reference through an instance field. */
|
||||
class FieldContent extends Content, TFieldContent {
|
||||
Class c;
|
||||
int startBit;
|
||||
int endBit;
|
||||
Field f;
|
||||
|
||||
FieldContent() { this = TFieldContent(c, startBit, endBit) }
|
||||
FieldContent() { this = TFieldContent(f) }
|
||||
|
||||
// Ensure that there's just 1 result for `toString`.
|
||||
override string toString() { result = min(Field f | f = getAField() | f.toString()) }
|
||||
override string toString() { result = f.toString() }
|
||||
|
||||
predicate hasOffset(Class cl, int start, int end) { cl = c and start = startBit and end = endBit }
|
||||
|
||||
Field getAField() { result = getAField(c, startBit, endBit) }
|
||||
Field getField() { result = f }
|
||||
}
|
||||
|
||||
/** A reference through an array. */
|
||||
|
||||
@@ -38,5 +38,8 @@ Instruction callOutput(CallInstruction call, FunctionOutput output) {
|
||||
effect.getPrimaryInstruction() = call and
|
||||
output.isParameterDerefOrQualifierObject(effect.getIndex())
|
||||
)
|
||||
// TODO: return value dereference
|
||||
or
|
||||
// TODO: modify this when we get return value dereferences
|
||||
result = call and
|
||||
output.isReturnValueDeref()
|
||||
}
|
||||
|
||||
489
cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/Ssa.qll
Normal file
489
cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/Ssa.qll
Normal file
@@ -0,0 +1,489 @@
|
||||
import SsaImplCommon
|
||||
import SsaImplSpecific
|
||||
private import cpp as Cpp
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import DataFlowUtil
|
||||
private import DataFlowPrivate
|
||||
private import semmle.code.cpp.models.interfaces.Allocation as Alloc
|
||||
private import semmle.code.cpp.models.interfaces.DataFlow as DataFlow
|
||||
|
||||
cached
|
||||
private newtype TDefOrUse =
|
||||
TExplicitDef(Instruction store) { explicitWrite(_, store, _) } or
|
||||
TInitializeParam(Instruction instr) {
|
||||
instr instanceof InitializeParameterInstruction
|
||||
or
|
||||
instr instanceof InitializeIndirectionInstruction
|
||||
} or
|
||||
TExplicitUse(Operand op) { isExplicitUse(op) } or
|
||||
TReturnParamIndirection(Operand op) { returnParameterIndirection(op, _) }
|
||||
|
||||
pragma[nomagic]
|
||||
private int getRank(DefOrUse defOrUse, IRBlock block) {
|
||||
defOrUse =
|
||||
rank[result](int i, DefOrUse cand |
|
||||
block.getInstruction(i) = toInstruction(cand)
|
||||
|
|
||||
cand order by i
|
||||
)
|
||||
}
|
||||
|
||||
private class DefOrUse extends TDefOrUse {
|
||||
/** Gets the instruction associated with this definition, if any. */
|
||||
Instruction asDef() { none() }
|
||||
|
||||
/** Gets the operand associated with this use, if any. */
|
||||
Operand asUse() { none() }
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
abstract string toString();
|
||||
|
||||
/** Gets the block of this definition or use. */
|
||||
abstract IRBlock getBlock();
|
||||
|
||||
/** Holds if this definition or use has rank `rank` in block `block`. */
|
||||
cached
|
||||
final predicate hasRankInBlock(IRBlock block, int rnk) {
|
||||
block = getBlock() and
|
||||
rnk = getRank(this, block)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||
*/
|
||||
abstract predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
);
|
||||
}
|
||||
|
||||
private Instruction toInstruction(DefOrUse defOrUse) {
|
||||
result = defOrUse.asDef()
|
||||
or
|
||||
result = defOrUse.asUse().getUse()
|
||||
}
|
||||
|
||||
abstract class Def extends DefOrUse {
|
||||
Instruction store;
|
||||
|
||||
/** Gets the instruction of this definition. */
|
||||
Instruction getInstruction() { result = store }
|
||||
|
||||
/** Gets the variable that is defined by this definition. */
|
||||
abstract SourceVariable getVariable();
|
||||
|
||||
/** Holds if this definition is guaranteed to happen. */
|
||||
abstract predicate isCertain();
|
||||
|
||||
override Instruction asDef() { result = this.getInstruction() }
|
||||
|
||||
override string toString() { result = "Def(" + store.getDumpString() + ")" }
|
||||
|
||||
override IRBlock getBlock() { result = this.getInstruction().getBlock() }
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
store.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
}
|
||||
|
||||
private class ExplicitDef extends Def, TExplicitDef {
|
||||
ExplicitDef() { this = TExplicitDef(store) }
|
||||
|
||||
override SourceVariable getVariable() {
|
||||
exists(VariableInstruction var |
|
||||
explicitWrite(_, this.getInstruction(), var) and
|
||||
result.getVariable() = var.getIRVariable() and
|
||||
not result.isIndirection()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isCertain() { explicitWrite(true, this.getInstruction(), _) }
|
||||
}
|
||||
|
||||
private class ParameterDef extends Def, TInitializeParam {
|
||||
ParameterDef() { this = TInitializeParam(store) }
|
||||
|
||||
override SourceVariable getVariable() {
|
||||
result.getVariable() = store.(InitializeParameterInstruction).getIRVariable() and
|
||||
not result.isIndirection()
|
||||
or
|
||||
result.getVariable() = store.(InitializeIndirectionInstruction).getIRVariable() and
|
||||
result.isIndirection()
|
||||
}
|
||||
|
||||
override predicate isCertain() { any() }
|
||||
}
|
||||
|
||||
abstract class Use extends DefOrUse {
|
||||
Operand use;
|
||||
|
||||
override Operand asUse() { result = use }
|
||||
|
||||
/** Gets the underlying operand of this use. */
|
||||
Operand getOperand() { result = use }
|
||||
|
||||
override string toString() { result = "Use(" + use.getDumpString() + ")" }
|
||||
|
||||
/** Gets the variable that is used by this use. */
|
||||
abstract SourceVariable getVariable();
|
||||
|
||||
override IRBlock getBlock() { result = use.getUse().getBlock() }
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
use.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
}
|
||||
|
||||
private class ExplicitUse extends Use, TExplicitUse {
|
||||
ExplicitUse() { this = TExplicitUse(use) }
|
||||
|
||||
override SourceVariable getVariable() {
|
||||
exists(VariableInstruction var |
|
||||
use.getDef() = var and
|
||||
result.getVariable() = var.getIRVariable() and
|
||||
(
|
||||
if use.getUse() instanceof ReadSideEffectInstruction
|
||||
then result.isIndirection()
|
||||
else not result.isIndirection()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class ReturnParameterIndirection extends Use, TReturnParamIndirection {
|
||||
ReturnParameterIndirection() { this = TReturnParamIndirection(use) }
|
||||
|
||||
override SourceVariable getVariable() {
|
||||
exists(ReturnIndirectionInstruction ret |
|
||||
returnParameterIndirection(use, ret) and
|
||||
result.getVariable() = ret.getIRVariable() and
|
||||
result.isIndirection()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private predicate isExplicitUse(Operand op) {
|
||||
op.getDef() instanceof VariableAddressInstruction and
|
||||
not exists(LoadInstruction load |
|
||||
load.getSourceAddressOperand() = op and
|
||||
load.getAUse().getUse() instanceof InitializeIndirectionInstruction
|
||||
)
|
||||
}
|
||||
|
||||
private predicate returnParameterIndirection(Operand op, ReturnIndirectionInstruction ret) {
|
||||
ret.getSourceAddressOperand() = op
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `iFrom` computes an address that is used by `iTo`.
|
||||
*/
|
||||
predicate addressFlow(Instruction iFrom, Instruction iTo) {
|
||||
iTo.(CopyValueInstruction).getSourceValue() = iFrom
|
||||
or
|
||||
iTo.(ConvertInstruction).getUnary() = iFrom
|
||||
or
|
||||
iTo.(CheckedConvertOrNullInstruction).getUnary() = iFrom
|
||||
or
|
||||
iTo.(InheritanceConversionInstruction).getUnary() = iFrom
|
||||
or
|
||||
iTo.(PointerArithmeticInstruction).getLeft() = iFrom
|
||||
or
|
||||
iTo.(FieldAddressInstruction).getObjectAddress() = iFrom
|
||||
or
|
||||
iTo.(LoadInstruction).getSourceAddress() = iFrom
|
||||
or
|
||||
exists(WriteSideEffectInstruction write |
|
||||
write.getPrimaryInstruction() = iTo and
|
||||
write.getDestinationAddress() = iFrom
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* The reflexive, transitive closure of `addressFlow` that ends as the address of a
|
||||
* store or read operation.
|
||||
*/
|
||||
cached
|
||||
predicate addressFlowTC(Instruction iFrom, Instruction iTo) {
|
||||
iTo = [getDestinationAddress(_), getSourceAddress(_)] and
|
||||
addressFlow*(iFrom, iTo)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the destination address of `instr` if it is a `StoreInstruction` or
|
||||
* a `WriteSideEffectInstruction`. The destination address of a `WriteSideEffectInstruction` is adjusted
|
||||
* in the case of calls to operator `new` to give the destination address of a subsequent store (if any).
|
||||
*/
|
||||
Instruction getDestinationAddress(Instruction instr) {
|
||||
result =
|
||||
[
|
||||
instr.(StoreInstruction).getDestinationAddress(),
|
||||
instr.(WriteSideEffectInstruction).getDestinationAddress()
|
||||
]
|
||||
}
|
||||
|
||||
class ReferenceToInstruction extends CopyValueInstruction {
|
||||
ReferenceToInstruction() {
|
||||
this.getResultType() instanceof Cpp::ReferenceType and
|
||||
not this.getUnary().getResultType() instanceof Cpp::ReferenceType
|
||||
}
|
||||
|
||||
Instruction getSourceAddress() { result = getSourceAddressOperand().getDef() }
|
||||
|
||||
Operand getSourceAddressOperand() { result = this.getUnaryOperand() }
|
||||
}
|
||||
|
||||
/** Gets the source address of `instr` if it is an instruction that behaves like a `LoadInstruction`. */
|
||||
Instruction getSourceAddress(Instruction instr) { result = getSourceAddressOperand(instr).getDef() }
|
||||
|
||||
/**
|
||||
* Gets the operand that represents the source address of `instr` if it is an
|
||||
* instruction that behaves like a `LoadInstruction`.
|
||||
*/
|
||||
Operand getSourceAddressOperand(Instruction instr) {
|
||||
result =
|
||||
[
|
||||
instr.(LoadInstruction).getSourceAddressOperand(),
|
||||
instr.(ReadSideEffectInstruction).getArgumentOperand(),
|
||||
instr.(ReferenceToInstruction).getSourceAddressOperand()
|
||||
]
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the source address of `node` if it's an instruction or operand that
|
||||
* behaves like a `LoadInstruction`.
|
||||
*/
|
||||
Instruction getSourceAddressFromNode(Node node) {
|
||||
result = getSourceAddress(node.asInstruction())
|
||||
or
|
||||
result = getSourceAddress(node.asOperand().(SideEffectOperand).getUse())
|
||||
}
|
||||
|
||||
/** Gets the source value of `instr` if it's an instruction that behaves like a `LoadInstruction`. */
|
||||
Instruction getSourceValue(Instruction instr) { result = getSourceValueOperand(instr).getDef() }
|
||||
|
||||
/**
|
||||
* Gets the operand that represents the source value of `instr` if it's an instruction
|
||||
* that behaves like a `LoadInstruction`.
|
||||
*/
|
||||
Operand getSourceValueOperand(Instruction instr) {
|
||||
result = instr.(LoadInstruction).getSourceValueOperand()
|
||||
or
|
||||
result = instr.(ReadSideEffectInstruction).getSideEffectOperand()
|
||||
or
|
||||
result = instr.(ReferenceToInstruction).getSourceValueOperand()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` is a `StoreInstruction` or a `WriteSideEffectInstruction` that writes to an address.
|
||||
* The addresses is computed using `address`, and `certain` is `true` if the write is guaranteed to overwrite
|
||||
* the entire variable.
|
||||
*/
|
||||
cached
|
||||
predicate explicitWrite(boolean certain, Instruction instr, Instruction address) {
|
||||
exists(StoreInstruction store |
|
||||
store = instr and addressFlowTC(address, store.getDestinationAddress())
|
||||
|
|
||||
if
|
||||
addressFlowTC(any(Instruction i |
|
||||
i instanceof FieldAddressInstruction or i instanceof PointerArithmeticInstruction
|
||||
), store.getDestinationAddress())
|
||||
then certain = false
|
||||
else certain = true
|
||||
)
|
||||
or
|
||||
addressFlowTC(address, instr.(WriteSideEffectInstruction).getDestinationAddress()) and
|
||||
certain = false
|
||||
}
|
||||
|
||||
cached
|
||||
private module Cached {
|
||||
/**
|
||||
* Holds if `nodeFrom` is a read or write, and `nTo` is the next subsequent read of the variable
|
||||
* written (or read) by `storeOrRead`.
|
||||
*/
|
||||
cached
|
||||
predicate ssaFlow(Node nodeFrom, Node nodeTo) {
|
||||
// Def-use/use-use flow from an `InstructionNode` to an `OperandNode`.
|
||||
exists(IRBlock bb1, int i1, IRBlock bb2, int i2, DefOrUse defOrUse, Use use, SourceVariable v |
|
||||
defOrUse.hasRankInBlock(bb1, i1) and
|
||||
use.hasRankInBlock(bb2, i2) and
|
||||
use.getVariable() = v and
|
||||
adjacentDefRead(_, bb1, i1, bb2, i2) and
|
||||
nodeFrom.asInstruction() = toInstruction(defOrUse) and
|
||||
flowOutOfAddressStep(use.getOperand(), nodeTo)
|
||||
)
|
||||
or
|
||||
// Use-use flow from a `ReadNode` to an `OperandNode`.
|
||||
exists(ReadNode read, IRBlock bb1, int i1, IRBlock bb2, int i2, Use use1, Use use2 |
|
||||
read = nodeFrom and
|
||||
use1.hasRankInBlock(bb1, i1) and
|
||||
use2.hasRankInBlock(bb2, i2) and
|
||||
use1.getOperand().getDef() = read.getInstruction() and
|
||||
adjacentDefRead(_, bb1, i1, bb2, i2) and
|
||||
flowOutOfAddressStep(use2.getOperand(), nodeTo)
|
||||
)
|
||||
or
|
||||
// Flow from phi nodes
|
||||
exists(PhiNode phi, Use use, IRBlock block, int rnk |
|
||||
phi = nodeFrom.(SsaPhiNode).getPhiNode() and
|
||||
use.hasRankInBlock(block, rnk) and
|
||||
phi.getSourceVariable() = use.getVariable() and
|
||||
flowOutOfAddressStep(use.getOperand(), nodeTo) and
|
||||
adjacentDefRead(_, phi.getBasicBlock(), -1, block, rnk)
|
||||
)
|
||||
or
|
||||
// Flow to phi nodes
|
||||
exists(Def def, StoreNode store, IRBlock block, int rnk |
|
||||
store = nodeFrom and
|
||||
store.isTerminal() and
|
||||
def.getInstruction() = store.getStoreInstruction() and
|
||||
def.hasRankInBlock(block, rnk) and
|
||||
nodeTo.(SsaPhiNode).hasInputAtRankInBlock(block, rnk)
|
||||
)
|
||||
or
|
||||
// Def-use flow from a `StoreNode` to an `OperandNode`.
|
||||
exists(
|
||||
StoreNode store, IRBlock bb1, int i1, IRBlock bb2, int i2, Def def, Use use, Definition ssaDef
|
||||
|
|
||||
store = nodeFrom and
|
||||
store.isTerminal() and
|
||||
def.getInstruction() = store.getStoreInstruction() and
|
||||
def.hasRankInBlock(bb1, i1) and
|
||||
adjacentDefRead(ssaDef, bb1, i1, bb2, i2) and
|
||||
use.hasRankInBlock(bb2, i2) and
|
||||
flowOutOfAddressStep(use.getOperand(), nodeTo)
|
||||
)
|
||||
or
|
||||
// This final case is a bit annoying. The write side effect on an expression like `a = new A;` writes
|
||||
// to a fresh address returned by `operator new`, and there's no easy way to use the shared SSA
|
||||
// library to hook that up to the assignment to `a`. So instead we flow to the _first_ use of the
|
||||
// value computed by `operator new` that occurs after `nodeFrom` (to avoid a loop in the
|
||||
// dataflow graph).
|
||||
exists(
|
||||
StoreNode store, WriteSideEffectInstruction write, IRBlock bb, int i1, int i2, Operand op
|
||||
|
|
||||
store = nodeFrom and
|
||||
store.getInstruction().(CallInstruction).getStaticCallTarget() instanceof
|
||||
Alloc::OperatorNewAllocationFunction and
|
||||
write = store.getStoreInstruction() and
|
||||
bb.getInstruction(i1) = write and
|
||||
bb.getInstruction(i2) = op.getUse() and
|
||||
// Flow to an instruction that occurs later in the block.
|
||||
valueFlow*(store.getInstruction(), op.getDef()) and
|
||||
nodeTo.asOperand() = op and
|
||||
i2 > i1 and
|
||||
// There is no previous instruction that also occurs after `nodeFrom`.
|
||||
not exists(Instruction instr, int i |
|
||||
bb.getInstruction(i) = instr and
|
||||
valueFlow(instr, op.getDef()) and
|
||||
i1 < i and
|
||||
i < i2
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate valueFlow(Instruction iFrom, Instruction iTo) {
|
||||
iTo.(CopyValueInstruction).getSourceValue() = iFrom
|
||||
or
|
||||
iTo.(ConvertInstruction).getUnary() = iFrom
|
||||
or
|
||||
iTo.(CheckedConvertOrNullInstruction).getUnary() = iFrom
|
||||
or
|
||||
iTo.(InheritanceConversionInstruction).getUnary() = iFrom
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate callTargetHasInputOutput(
|
||||
CallInstruction call, DataFlow::FunctionInput input, DataFlow::FunctionOutput output
|
||||
) {
|
||||
exists(DataFlow::DataFlowFunction func |
|
||||
call.getStaticCallTarget() = func and
|
||||
func.hasDataFlow(input, output)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate flowOutOfAddressStep(Operand operand, Node nTo) {
|
||||
// Flow into a read node
|
||||
exists(ReadNode readNode | readNode = nTo |
|
||||
readNode.isInitial() and
|
||||
operand.getDef() = readNode.getInstruction()
|
||||
)
|
||||
or
|
||||
exists(StoreNode storeNode, Instruction def |
|
||||
storeNode = nTo and
|
||||
def = operand.getDef()
|
||||
|
|
||||
storeNode.isTerminal() and
|
||||
not addressFlow(def, _) and
|
||||
// Only transfer flow to a store node if it doesn't immediately overwrite the address
|
||||
// we've just written to.
|
||||
explicitWrite(false, storeNode.getStoreInstruction(), def)
|
||||
)
|
||||
or
|
||||
operand = getSourceAddressOperand(nTo.asInstruction())
|
||||
or
|
||||
exists(ReturnIndirectionInstruction ret |
|
||||
ret.getSourceAddressOperand() = operand and
|
||||
ret = nTo.asInstruction()
|
||||
)
|
||||
or
|
||||
exists(ReturnValueInstruction ret |
|
||||
ret.getReturnAddressOperand() = operand and
|
||||
nTo.asInstruction() = ret
|
||||
)
|
||||
or
|
||||
exists(CallInstruction call, int index, ReadSideEffectInstruction read |
|
||||
call.getArgumentOperand(index) = operand and
|
||||
read = getSideEffectFor(call, index) and
|
||||
nTo.asOperand() = read.getSideEffectOperand()
|
||||
)
|
||||
or
|
||||
exists(CopyInstruction copy |
|
||||
not exists(getSourceAddressOperand(copy)) and
|
||||
copy.getSourceValueOperand() = operand and
|
||||
flowOutOfAddressStep(copy.getAUse(), nTo)
|
||||
)
|
||||
or
|
||||
exists(ConvertInstruction convert |
|
||||
convert.getUnaryOperand() = operand and
|
||||
flowOutOfAddressStep(convert.getAUse(), nTo)
|
||||
)
|
||||
or
|
||||
exists(CheckedConvertOrNullInstruction convert |
|
||||
convert.getUnaryOperand() = operand and
|
||||
flowOutOfAddressStep(convert.getAUse(), nTo)
|
||||
)
|
||||
or
|
||||
exists(InheritanceConversionInstruction convert |
|
||||
convert.getUnaryOperand() = operand and
|
||||
flowOutOfAddressStep(convert.getAUse(), nTo)
|
||||
)
|
||||
or
|
||||
exists(PointerArithmeticInstruction arith |
|
||||
arith.getLeftOperand() = operand and
|
||||
flowOutOfAddressStep(arith.getAUse(), nTo)
|
||||
)
|
||||
or
|
||||
// Flow through a modelled function that has parameter -> return value flow.
|
||||
exists(
|
||||
CallInstruction call, int index, DataFlow::FunctionInput input,
|
||||
DataFlow::FunctionOutput output
|
||||
|
|
||||
callTargetHasInputOutput(call, input, output) and
|
||||
call.getArgumentOperand(index) = operand and
|
||||
not getSideEffectFor(call, index) instanceof ReadSideEffectInstruction and
|
||||
input.isParameter(index) and
|
||||
output.isReturnValue() and
|
||||
flowOutOfAddressStep(call.getAUse(), nTo)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
import Cached
|
||||
@@ -0,0 +1,637 @@
|
||||
/**
|
||||
* Provides a language-independent implementation of static single assignment
|
||||
* (SSA) form.
|
||||
*/
|
||||
|
||||
private import SsaImplSpecific
|
||||
|
||||
private BasicBlock getABasicBlockPredecessor(BasicBlock bb) { getABasicBlockSuccessor(result) = bb }
|
||||
|
||||
/**
|
||||
* Liveness analysis (based on source variables) to restrict the size of the
|
||||
* SSA representation.
|
||||
*/
|
||||
private module Liveness {
|
||||
/**
|
||||
* A classification of variable references into reads (of a given kind) and
|
||||
* (certain or uncertain) writes.
|
||||
*/
|
||||
private newtype TRefKind =
|
||||
Read(boolean certain) { certain in [false, true] } or
|
||||
Write(boolean certain) { certain in [false, true] }
|
||||
|
||||
private class RefKind extends TRefKind {
|
||||
string toString() {
|
||||
exists(boolean certain | this = Read(certain) and result = "read (" + certain + ")")
|
||||
or
|
||||
exists(boolean certain | this = Write(certain) and result = "write (" + certain + ")")
|
||||
}
|
||||
|
||||
int getOrder() {
|
||||
this = Read(_) and
|
||||
result = 0
|
||||
or
|
||||
this = Write(_) and
|
||||
result = 1
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the `i`th node of basic block `bb` is a reference to `v` of kind `k`.
|
||||
*/
|
||||
private predicate ref(BasicBlock bb, int i, SourceVariable v, RefKind k) {
|
||||
exists(boolean certain | variableRead(bb, i, v, certain) | k = Read(certain))
|
||||
or
|
||||
exists(boolean certain | variableWrite(bb, i, v, certain) | k = Write(certain))
|
||||
}
|
||||
|
||||
private newtype OrderedRefIndex =
|
||||
MkOrderedRefIndex(int i, int tag) {
|
||||
exists(RefKind rk | ref(_, i, _, rk) | tag = rk.getOrder())
|
||||
}
|
||||
|
||||
private OrderedRefIndex refOrd(BasicBlock bb, int i, SourceVariable v, RefKind k, int ord) {
|
||||
ref(bb, i, v, k) and
|
||||
result = MkOrderedRefIndex(i, ord) and
|
||||
ord = k.getOrder()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the (1-based) rank of the reference to `v` at the `i`th node of
|
||||
* basic block `bb`, which has the given reference kind `k`.
|
||||
*
|
||||
* Reads are considered before writes when they happen at the same index.
|
||||
*/
|
||||
private int refRank(BasicBlock bb, int i, SourceVariable v, RefKind k) {
|
||||
refOrd(bb, i, v, k, _) =
|
||||
rank[result](int j, int ord, OrderedRefIndex res |
|
||||
res = refOrd(bb, j, v, _, ord)
|
||||
|
|
||||
res order by j, ord
|
||||
)
|
||||
}
|
||||
|
||||
private int maxRefRank(BasicBlock bb, SourceVariable v) {
|
||||
result = refRank(bb, _, v, _) and
|
||||
not result + 1 = refRank(bb, _, v, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the (1-based) rank of the first reference to `v` inside basic block `bb`
|
||||
* that is either a read or a certain write.
|
||||
*/
|
||||
private int firstReadOrCertainWrite(BasicBlock bb, SourceVariable v) {
|
||||
result =
|
||||
min(int r, RefKind k |
|
||||
r = refRank(bb, _, v, k) and
|
||||
k != Write(false)
|
||||
|
|
||||
r
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if source variable `v` is live at the beginning of basic block `bb`.
|
||||
*/
|
||||
predicate liveAtEntry(BasicBlock bb, SourceVariable v) {
|
||||
// The first read or certain write to `v` inside `bb` is a read
|
||||
refRank(bb, _, v, Read(_)) = firstReadOrCertainWrite(bb, v)
|
||||
or
|
||||
// There is no certain write to `v` inside `bb`, but `v` is live at entry
|
||||
// to a successor basic block of `bb`
|
||||
not exists(firstReadOrCertainWrite(bb, v)) and
|
||||
liveAtExit(bb, v)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if source variable `v` is live at the end of basic block `bb`.
|
||||
*/
|
||||
predicate liveAtExit(BasicBlock bb, SourceVariable v) {
|
||||
liveAtEntry(getABasicBlockSuccessor(bb), v)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if variable `v` is live in basic block `bb` at index `i`.
|
||||
* The rank of `i` is `rnk` as defined by `refRank()`.
|
||||
*/
|
||||
private predicate liveAtRank(BasicBlock bb, int i, SourceVariable v, int rnk) {
|
||||
exists(RefKind kind | rnk = refRank(bb, i, v, kind) |
|
||||
rnk = maxRefRank(bb, v) and
|
||||
liveAtExit(bb, v)
|
||||
or
|
||||
ref(bb, i, v, kind) and
|
||||
kind = Read(_)
|
||||
or
|
||||
exists(RefKind nextKind |
|
||||
liveAtRank(bb, _, v, rnk + 1) and
|
||||
rnk + 1 = refRank(bb, _, v, nextKind) and
|
||||
nextKind != Write(true)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if variable `v` is live after the (certain or uncertain) write at
|
||||
* index `i` inside basic block `bb`.
|
||||
*/
|
||||
predicate liveAfterWrite(BasicBlock bb, int i, SourceVariable v) {
|
||||
exists(int rnk | rnk = refRank(bb, i, v, Write(_)) | liveAtRank(bb, i, v, rnk))
|
||||
}
|
||||
}
|
||||
|
||||
private import Liveness
|
||||
|
||||
/** Holds if `bb1` strictly dominates `bb2`. */
|
||||
private predicate strictlyDominates(BasicBlock bb1, BasicBlock bb2) {
|
||||
bb1 = getImmediateBasicBlockDominator+(bb2)
|
||||
}
|
||||
|
||||
/** Holds if `bb1` dominates a predecessor of `bb2`. */
|
||||
private predicate dominatesPredecessor(BasicBlock bb1, BasicBlock bb2) {
|
||||
exists(BasicBlock pred | pred = getABasicBlockPredecessor(bb2) |
|
||||
bb1 = pred
|
||||
or
|
||||
strictlyDominates(bb1, pred)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `df` is in the dominance frontier of `bb`. */
|
||||
private predicate inDominanceFrontier(BasicBlock bb, BasicBlock df) {
|
||||
dominatesPredecessor(bb, df) and
|
||||
not strictlyDominates(bb, df)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `bb` is in the dominance frontier of a block containing a
|
||||
* definition of `v`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate inDefDominanceFrontier(BasicBlock bb, SourceVariable v) {
|
||||
exists(BasicBlock defbb, Definition def |
|
||||
def.definesAt(v, defbb, _) and
|
||||
inDominanceFrontier(defbb, bb)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
newtype TDefinition =
|
||||
TWriteDef(SourceVariable v, BasicBlock bb, int i) {
|
||||
variableWrite(bb, i, v, _) and
|
||||
liveAfterWrite(bb, i, v)
|
||||
} or
|
||||
TPhiNode(SourceVariable v, BasicBlock bb) {
|
||||
inDefDominanceFrontier(bb, v) and
|
||||
liveAtEntry(bb, v)
|
||||
}
|
||||
|
||||
private module SsaDefReaches {
|
||||
newtype TSsaRefKind =
|
||||
SsaRead() or
|
||||
SsaDef()
|
||||
|
||||
/**
|
||||
* A classification of SSA variable references into reads and definitions.
|
||||
*/
|
||||
class SsaRefKind extends TSsaRefKind {
|
||||
string toString() {
|
||||
this = SsaRead() and
|
||||
result = "SsaRead"
|
||||
or
|
||||
this = SsaDef() and
|
||||
result = "SsaDef"
|
||||
}
|
||||
|
||||
int getOrder() {
|
||||
this = SsaRead() and
|
||||
result = 0
|
||||
or
|
||||
this = SsaDef() and
|
||||
result = 1
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the `i`th node of basic block `bb` is a reference to `v`,
|
||||
* either a read (when `k` is `SsaRead()`) or an SSA definition (when `k`
|
||||
* is `SsaDef()`).
|
||||
*
|
||||
* Unlike `Liveness::ref`, this includes `phi` nodes.
|
||||
*/
|
||||
predicate ssaRef(BasicBlock bb, int i, SourceVariable v, SsaRefKind k) {
|
||||
variableRead(bb, i, v, _) and
|
||||
k = SsaRead()
|
||||
or
|
||||
exists(Definition def | def.definesAt(v, bb, i)) and
|
||||
k = SsaDef()
|
||||
}
|
||||
|
||||
private newtype OrderedSsaRefIndex =
|
||||
MkOrderedSsaRefIndex(int i, SsaRefKind k) { ssaRef(_, i, _, k) }
|
||||
|
||||
private OrderedSsaRefIndex ssaRefOrd(BasicBlock bb, int i, SourceVariable v, SsaRefKind k, int ord) {
|
||||
ssaRef(bb, i, v, k) and
|
||||
result = MkOrderedSsaRefIndex(i, k) and
|
||||
ord = k.getOrder()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the (1-based) rank of the reference to `v` at the `i`th node of basic
|
||||
* block `bb`, which has the given reference kind `k`.
|
||||
*
|
||||
* For example, if `bb` is a basic block with a phi node for `v` (considered
|
||||
* to be at index -1), reads `v` at node 2, and defines it at node 5, we have:
|
||||
*
|
||||
* ```ql
|
||||
* ssaRefRank(bb, -1, v, SsaDef()) = 1 // phi node
|
||||
* ssaRefRank(bb, 2, v, Read()) = 2 // read at node 2
|
||||
* ssaRefRank(bb, 5, v, SsaDef()) = 3 // definition at node 5
|
||||
* ```
|
||||
*
|
||||
* Reads are considered before writes when they happen at the same index.
|
||||
*/
|
||||
int ssaRefRank(BasicBlock bb, int i, SourceVariable v, SsaRefKind k) {
|
||||
ssaRefOrd(bb, i, v, k, _) =
|
||||
rank[result](int j, int ord, OrderedSsaRefIndex res |
|
||||
res = ssaRefOrd(bb, j, v, _, ord)
|
||||
|
|
||||
res order by j, ord
|
||||
)
|
||||
}
|
||||
|
||||
int maxSsaRefRank(BasicBlock bb, SourceVariable v) {
|
||||
result = ssaRefRank(bb, _, v, _) and
|
||||
not result + 1 = ssaRefRank(bb, _, v, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the SSA definition `def` reaches rank index `rnk` in its own
|
||||
* basic block `bb`.
|
||||
*/
|
||||
predicate ssaDefReachesRank(BasicBlock bb, Definition def, int rnk, SourceVariable v) {
|
||||
exists(int i |
|
||||
rnk = ssaRefRank(bb, i, v, SsaDef()) and
|
||||
def.definesAt(v, bb, i)
|
||||
)
|
||||
or
|
||||
ssaDefReachesRank(bb, def, rnk - 1, v) and
|
||||
rnk = ssaRefRank(bb, _, v, SsaRead())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the SSA definition of `v` at `def` reaches index `i` in the same
|
||||
* basic block `bb`, without crossing another SSA definition of `v`.
|
||||
*/
|
||||
predicate ssaDefReachesReadWithinBlock(SourceVariable v, Definition def, BasicBlock bb, int i) {
|
||||
exists(int rnk |
|
||||
ssaDefReachesRank(bb, def, rnk, v) and
|
||||
rnk = ssaRefRank(bb, i, v, SsaRead())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the SSA definition of `v` at `def` reaches uncertain SSA definition
|
||||
* `redef` in the same basic block, without crossing another SSA definition of `v`.
|
||||
*/
|
||||
predicate ssaDefReachesUncertainDefWithinBlock(
|
||||
SourceVariable v, Definition def, UncertainWriteDefinition redef
|
||||
) {
|
||||
exists(BasicBlock bb, int rnk, int i |
|
||||
ssaDefReachesRank(bb, def, rnk, v) and
|
||||
rnk = ssaRefRank(bb, i, v, SsaDef()) - 1 and
|
||||
redef.definesAt(v, bb, i)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as `ssaRefRank()`, but restricted to a particular SSA definition `def`.
|
||||
*/
|
||||
int ssaDefRank(Definition def, SourceVariable v, BasicBlock bb, int i, SsaRefKind k) {
|
||||
v = def.getSourceVariable() and
|
||||
result = ssaRefRank(bb, i, v, k) and
|
||||
(
|
||||
ssaDefReachesRead(_, def, bb, i)
|
||||
or
|
||||
def.definesAt(_, bb, i)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the reference to `def` at index `i` in basic block `bb` is the
|
||||
* last reference to `v` inside `bb`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
predicate lastSsaRef(Definition def, SourceVariable v, BasicBlock bb, int i) {
|
||||
ssaDefRank(def, v, bb, i, _) = maxSsaRefRank(bb, v)
|
||||
}
|
||||
|
||||
predicate defOccursInBlock(Definition def, BasicBlock bb, SourceVariable v) {
|
||||
exists(ssaDefRank(def, v, bb, _, _))
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate ssaDefReachesThroughBlock(Definition def, BasicBlock bb) {
|
||||
ssaDefReachesEndOfBlock(bb, def, _) and
|
||||
not defOccursInBlock(_, bb, def.getSourceVariable())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `def` is accessed in basic block `bb1` (either a read or a write),
|
||||
* `bb2` is a transitive successor of `bb1`, `def` is live at the end of `bb1`,
|
||||
* and the underlying variable for `def` is neither read nor written in any block
|
||||
* on the path between `bb1` and `bb2`.
|
||||
*/
|
||||
predicate varBlockReaches(Definition def, BasicBlock bb1, BasicBlock bb2) {
|
||||
defOccursInBlock(def, bb1, _) and
|
||||
bb2 = getABasicBlockSuccessor(bb1)
|
||||
or
|
||||
exists(BasicBlock mid |
|
||||
varBlockReaches(def, bb1, mid) and
|
||||
ssaDefReachesThroughBlock(def, mid) and
|
||||
bb2 = getABasicBlockSuccessor(mid)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `def` is accessed in basic block `bb1` (either a read or a write),
|
||||
* `def` is read at index `i2` in basic block `bb2`, `bb2` is in a transitive
|
||||
* successor block of `bb1`, and `def` is neither read nor written in any block
|
||||
* on a path between `bb1` and `bb2`.
|
||||
*/
|
||||
predicate defAdjacentRead(Definition def, BasicBlock bb1, BasicBlock bb2, int i2) {
|
||||
varBlockReaches(def, bb1, bb2) and
|
||||
ssaRefRank(bb2, i2, def.getSourceVariable(), SsaRead()) = 1
|
||||
}
|
||||
}
|
||||
|
||||
private import SsaDefReaches
|
||||
|
||||
pragma[nomagic]
|
||||
predicate liveThrough(BasicBlock bb, SourceVariable v) {
|
||||
liveAtExit(bb, v) and
|
||||
not ssaRef(bb, _, v, SsaDef())
|
||||
}
|
||||
|
||||
/**
|
||||
* NB: If this predicate is exposed, it should be cached.
|
||||
*
|
||||
* Holds if the SSA definition of `v` at `def` reaches the end of basic
|
||||
* block `bb`, at which point it is still live, without crossing another
|
||||
* SSA definition of `v`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate ssaDefReachesEndOfBlock(BasicBlock bb, Definition def, SourceVariable v) {
|
||||
exists(int last | last = maxSsaRefRank(bb, v) |
|
||||
ssaDefReachesRank(bb, def, last, v) and
|
||||
liveAtExit(bb, v)
|
||||
)
|
||||
or
|
||||
// The construction of SSA form ensures that each read of a variable is
|
||||
// dominated by its definition. An SSA definition therefore reaches a
|
||||
// control flow node if it is the _closest_ SSA definition that dominates
|
||||
// the node. If two definitions dominate a node then one must dominate the
|
||||
// other, so therefore the definition of _closest_ is given by the dominator
|
||||
// tree. Thus, reaching definitions can be calculated in terms of dominance.
|
||||
ssaDefReachesEndOfBlock(getImmediateBasicBlockDominator(bb), def, pragma[only_bind_into](v)) and
|
||||
liveThrough(bb, pragma[only_bind_into](v))
|
||||
}
|
||||
|
||||
/**
|
||||
* NB: If this predicate is exposed, it should be cached.
|
||||
*
|
||||
* Holds if `inp` is an input to the phi node `phi` along the edge originating in `bb`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate phiHasInputFromBlock(PhiNode phi, Definition inp, BasicBlock bb) {
|
||||
exists(SourceVariable v, BasicBlock bbDef |
|
||||
phi.definesAt(v, bbDef, _) and
|
||||
getABasicBlockPredecessor(bbDef) = bb and
|
||||
ssaDefReachesEndOfBlock(bb, inp, v)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* NB: If this predicate is exposed, it should be cached.
|
||||
*
|
||||
* Holds if the SSA definition of `v` at `def` reaches a read at index `i` in
|
||||
* basic block `bb`, without crossing another SSA definition of `v`. The read
|
||||
* is of kind `rk`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate ssaDefReachesRead(SourceVariable v, Definition def, BasicBlock bb, int i) {
|
||||
ssaDefReachesReadWithinBlock(v, def, bb, i)
|
||||
or
|
||||
variableRead(bb, i, v, _) and
|
||||
ssaDefReachesEndOfBlock(getABasicBlockPredecessor(bb), def, v) and
|
||||
not ssaDefReachesReadWithinBlock(v, _, bb, i)
|
||||
}
|
||||
|
||||
/**
|
||||
* NB: If this predicate is exposed, it should be cached.
|
||||
*
|
||||
* Holds if `def` is accessed at index `i1` in basic block `bb1` (either a read
|
||||
* or a write), `def` is read at index `i2` in basic block `bb2`, and there is a
|
||||
* path between them without any read of `def`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate adjacentDefRead(Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2) {
|
||||
exists(int rnk |
|
||||
rnk = ssaDefRank(def, _, bb1, i1, _) and
|
||||
rnk + 1 = ssaDefRank(def, _, bb1, i2, SsaRead()) and
|
||||
variableRead(bb1, i2, _, _) and
|
||||
bb2 = bb1
|
||||
)
|
||||
or
|
||||
lastSsaRef(def, _, bb1, i1) and
|
||||
defAdjacentRead(def, bb1, bb2, i2)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate adjacentDefRead(
|
||||
Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2, SourceVariable v
|
||||
) {
|
||||
adjacentDefRead(def, bb1, i1, bb2, i2) and
|
||||
v = def.getSourceVariable()
|
||||
}
|
||||
|
||||
private predicate adjacentDefReachesRead(
|
||||
Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2
|
||||
) {
|
||||
exists(SourceVariable v | adjacentDefRead(def, bb1, i1, bb2, i2, v) |
|
||||
ssaRef(bb1, i1, v, SsaDef())
|
||||
or
|
||||
variableRead(bb1, i1, v, true)
|
||||
)
|
||||
or
|
||||
exists(BasicBlock bb3, int i3 |
|
||||
adjacentDefReachesRead(def, bb1, i1, bb3, i3) and
|
||||
variableRead(bb3, i3, _, false) and
|
||||
adjacentDefRead(def, bb3, i3, bb2, i2)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* NB: If this predicate is exposed, it should be cached.
|
||||
*
|
||||
* Same as `adjacentDefRead`, but ignores uncertain reads.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate adjacentDefNoUncertainReads(Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2) {
|
||||
adjacentDefReachesRead(def, bb1, i1, bb2, i2) and
|
||||
variableRead(bb2, i2, _, true)
|
||||
}
|
||||
|
||||
/**
|
||||
* NB: If this predicate is exposed, it should be cached.
|
||||
*
|
||||
* Holds if the node at index `i` in `bb` is a last reference to SSA definition
|
||||
* `def`. The reference is last because it can reach another write `next`,
|
||||
* without passing through another read or write.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate lastRefRedef(Definition def, BasicBlock bb, int i, Definition next) {
|
||||
exists(SourceVariable v |
|
||||
// Next reference to `v` inside `bb` is a write
|
||||
exists(int rnk, int j |
|
||||
rnk = ssaDefRank(def, v, bb, i, _) and
|
||||
next.definesAt(v, bb, j) and
|
||||
rnk + 1 = ssaRefRank(bb, j, v, SsaDef())
|
||||
)
|
||||
or
|
||||
// Can reach a write using one or more steps
|
||||
lastSsaRef(def, v, bb, i) and
|
||||
exists(BasicBlock bb2 |
|
||||
varBlockReaches(def, bb, bb2) and
|
||||
1 = ssaDefRank(next, v, bb2, _, SsaDef())
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* NB: If this predicate is exposed, it should be cached.
|
||||
*
|
||||
* Holds if `inp` is an immediately preceding definition of uncertain definition
|
||||
* `def`. Since `def` is uncertain, the value from the preceding definition might
|
||||
* still be valid.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate uncertainWriteDefinitionInput(UncertainWriteDefinition def, Definition inp) {
|
||||
lastRefRedef(inp, _, _, def)
|
||||
}
|
||||
|
||||
private predicate adjacentDefReachesUncertainRead(
|
||||
Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2
|
||||
) {
|
||||
adjacentDefReachesRead(def, bb1, i1, bb2, i2) and
|
||||
variableRead(bb2, i2, _, false)
|
||||
}
|
||||
|
||||
/**
|
||||
* NB: If this predicate is exposed, it should be cached.
|
||||
*
|
||||
* Same as `lastRefRedef`, but ignores uncertain reads.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate lastRefRedefNoUncertainReads(Definition def, BasicBlock bb, int i, Definition next) {
|
||||
lastRefRedef(def, bb, i, next) and
|
||||
not variableRead(bb, i, def.getSourceVariable(), false)
|
||||
or
|
||||
exists(BasicBlock bb0, int i0 |
|
||||
lastRefRedef(def, bb0, i0, next) and
|
||||
adjacentDefReachesUncertainRead(def, bb, i, bb0, i0)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* NB: If this predicate is exposed, it should be cached.
|
||||
*
|
||||
* Holds if the node at index `i` in `bb` is a last reference to SSA
|
||||
* definition `def`.
|
||||
*
|
||||
* That is, the node can reach the end of the enclosing callable, or another
|
||||
* SSA definition for the underlying source variable, without passing through
|
||||
* another read.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate lastRef(Definition def, BasicBlock bb, int i) {
|
||||
lastRefRedef(def, bb, i, _)
|
||||
or
|
||||
lastSsaRef(def, _, bb, i) and
|
||||
(
|
||||
// Can reach exit directly
|
||||
bb instanceof ExitBasicBlock
|
||||
or
|
||||
// Can reach a block using one or more steps, where `def` is no longer live
|
||||
exists(BasicBlock bb2 | varBlockReaches(def, bb, bb2) |
|
||||
not defOccursInBlock(def, bb2, _) and
|
||||
not ssaDefReachesEndOfBlock(bb2, def, _)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* NB: If this predicate is exposed, it should be cached.
|
||||
*
|
||||
* Same as `lastRefRedef`, but ignores uncertain reads.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate lastRefNoUncertainReads(Definition def, BasicBlock bb, int i) {
|
||||
lastRef(def, bb, i) and
|
||||
not variableRead(bb, i, def.getSourceVariable(), false)
|
||||
or
|
||||
exists(BasicBlock bb0, int i0 |
|
||||
lastRef(def, bb0, i0) and
|
||||
adjacentDefReachesUncertainRead(def, bb, i, bb0, i0)
|
||||
)
|
||||
}
|
||||
|
||||
/** A static single assignment (SSA) definition. */
|
||||
class Definition extends TDefinition {
|
||||
/** Gets the source variable underlying this SSA definition. */
|
||||
SourceVariable getSourceVariable() { this.definesAt(result, _, _) }
|
||||
|
||||
/**
|
||||
* Holds if this SSA definition defines `v` at index `i` in basic block `bb`.
|
||||
* Phi nodes are considered to be at index `-1`, while normal variable writes
|
||||
* are at the index of the control flow node they wrap.
|
||||
*/
|
||||
final predicate definesAt(SourceVariable v, BasicBlock bb, int i) {
|
||||
this = TWriteDef(v, bb, i)
|
||||
or
|
||||
this = TPhiNode(v, bb) and i = -1
|
||||
}
|
||||
|
||||
/** Gets the basic block to which this SSA definition belongs. */
|
||||
final BasicBlock getBasicBlock() { this.definesAt(_, result, _) }
|
||||
|
||||
/** Gets a textual representation of this SSA definition. */
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
/** An SSA definition that corresponds to a write. */
|
||||
class WriteDefinition extends Definition, TWriteDef {
|
||||
private SourceVariable v;
|
||||
private BasicBlock bb;
|
||||
private int i;
|
||||
|
||||
WriteDefinition() { this = TWriteDef(v, bb, i) }
|
||||
|
||||
override string toString() { result = "WriteDef" }
|
||||
}
|
||||
|
||||
/** A phi node. */
|
||||
class PhiNode extends Definition, TPhiNode {
|
||||
override string toString() { result = "Phi" }
|
||||
}
|
||||
|
||||
/**
|
||||
* An SSA definition that represents an uncertain update of the underlying
|
||||
* source variable.
|
||||
*/
|
||||
class UncertainWriteDefinition extends WriteDefinition {
|
||||
UncertainWriteDefinition() {
|
||||
exists(SourceVariable v, BasicBlock bb, int i |
|
||||
this.definesAt(v, bb, i) and
|
||||
variableWrite(bb, i, v, false)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import DataFlowUtil
|
||||
private import DataFlowPrivate
|
||||
private import DataFlowImplCommon as DataFlowImplCommon
|
||||
private import Ssa as Ssa
|
||||
|
||||
class BasicBlock = IRBlock;
|
||||
|
||||
BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) { result.immediatelyDominates(bb) }
|
||||
|
||||
BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.getASuccessor() }
|
||||
|
||||
class ExitBasicBlock extends IRBlock {
|
||||
ExitBasicBlock() { this.getLastInstruction() instanceof ExitFunctionInstruction }
|
||||
}
|
||||
|
||||
private newtype TSourceVariable =
|
||||
TSourceIRVariable(IRVariable var) or
|
||||
TSourceIRVariableIndirection(InitializeIndirectionInstruction init)
|
||||
|
||||
abstract class SourceVariable extends TSourceVariable {
|
||||
IRVariable var;
|
||||
|
||||
IRVariable getVariable() { result = var }
|
||||
|
||||
abstract string toString();
|
||||
|
||||
predicate isIndirection() { none() }
|
||||
}
|
||||
|
||||
class SourceIRVariable extends SourceVariable, TSourceIRVariable {
|
||||
SourceIRVariable() { this = TSourceIRVariable(var) }
|
||||
|
||||
override string toString() { result = this.getVariable().toString() }
|
||||
}
|
||||
|
||||
class SourceIRVariableIndirection extends SourceVariable, TSourceIRVariableIndirection {
|
||||
InitializeIndirectionInstruction init;
|
||||
|
||||
SourceIRVariableIndirection() {
|
||||
this = TSourceIRVariableIndirection(init) and var = init.getIRVariable()
|
||||
}
|
||||
|
||||
override string toString() { result = "*" + this.getVariable().toString() }
|
||||
|
||||
override predicate isIndirection() { any() }
|
||||
}
|
||||
|
||||
predicate variableWrite(BasicBlock bb, int i, SourceVariable v, boolean certain) {
|
||||
DataFlowImplCommon::forceCachingInSameStage() and
|
||||
exists(Ssa::Def def |
|
||||
def.hasRankInBlock(bb, i) and
|
||||
v = def.getVariable() and
|
||||
(if def.isCertain() then certain = true else certain = false)
|
||||
)
|
||||
}
|
||||
|
||||
predicate variableRead(BasicBlock bb, int i, SourceVariable v, boolean certain) {
|
||||
exists(Ssa::Use use |
|
||||
use.hasRankInBlock(bb, i) and
|
||||
v = use.getVariable() and
|
||||
certain = true
|
||||
)
|
||||
}
|
||||
@@ -44,8 +44,6 @@ private predicate instructionToOperandTaintStep(Instruction fromInstr, Operand t
|
||||
fromInstr = readInstr.getArgumentDef() and
|
||||
toOperand = readInstr.getSideEffectOperand()
|
||||
)
|
||||
or
|
||||
toOperand.(LoadOperand).getAnyDef() = fromInstr
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -84,8 +82,6 @@ private predicate operandToInstructionTaintStep(Operand opFrom, Instruction inst
|
||||
instrTo.(FieldAddressInstruction).getField().getDeclaringType() instanceof Union
|
||||
)
|
||||
or
|
||||
instrTo.(LoadInstruction).getSourceAddressOperand() = opFrom
|
||||
or
|
||||
// Flow from an element to an array or union that contains it.
|
||||
instrTo.(ChiInstruction).getPartialOperand() = opFrom and
|
||||
not instrTo.isResultConflated() and
|
||||
|
||||
@@ -75,24 +75,26 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
predicate isSanitizer(DataFlow::Node node) { none() }
|
||||
|
||||
final override predicate isBarrier(DataFlow::Node node) {
|
||||
isSanitizer(node) or
|
||||
this.isSanitizer(node) or
|
||||
defaultTaintSanitizer(node)
|
||||
}
|
||||
|
||||
/** Holds if taint propagation into `node` is prohibited. */
|
||||
predicate isSanitizerIn(DataFlow::Node node) { none() }
|
||||
|
||||
final override predicate isBarrierIn(DataFlow::Node node) { isSanitizerIn(node) }
|
||||
final override predicate isBarrierIn(DataFlow::Node node) { this.isSanitizerIn(node) }
|
||||
|
||||
/** Holds if taint propagation out of `node` is prohibited. */
|
||||
predicate isSanitizerOut(DataFlow::Node node) { none() }
|
||||
|
||||
final override predicate isBarrierOut(DataFlow::Node node) { isSanitizerOut(node) }
|
||||
final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) }
|
||||
|
||||
/** Holds if taint propagation through nodes guarded by `guard` is prohibited. */
|
||||
predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
|
||||
|
||||
final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { isSanitizerGuard(guard) }
|
||||
final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
|
||||
this.isSanitizerGuard(guard)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the additional taint propagation step from `node1` to `node2`
|
||||
@@ -101,7 +103,7 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { none() }
|
||||
|
||||
final override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
isAdditionalTaintStep(node1, node2) or
|
||||
this.isAdditionalTaintStep(node1, node2) or
|
||||
defaultAdditionalTaintStep(node1, node2)
|
||||
}
|
||||
|
||||
|
||||
@@ -75,24 +75,26 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
predicate isSanitizer(DataFlow::Node node) { none() }
|
||||
|
||||
final override predicate isBarrier(DataFlow::Node node) {
|
||||
isSanitizer(node) or
|
||||
this.isSanitizer(node) or
|
||||
defaultTaintSanitizer(node)
|
||||
}
|
||||
|
||||
/** Holds if taint propagation into `node` is prohibited. */
|
||||
predicate isSanitizerIn(DataFlow::Node node) { none() }
|
||||
|
||||
final override predicate isBarrierIn(DataFlow::Node node) { isSanitizerIn(node) }
|
||||
final override predicate isBarrierIn(DataFlow::Node node) { this.isSanitizerIn(node) }
|
||||
|
||||
/** Holds if taint propagation out of `node` is prohibited. */
|
||||
predicate isSanitizerOut(DataFlow::Node node) { none() }
|
||||
|
||||
final override predicate isBarrierOut(DataFlow::Node node) { isSanitizerOut(node) }
|
||||
final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) }
|
||||
|
||||
/** Holds if taint propagation through nodes guarded by `guard` is prohibited. */
|
||||
predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
|
||||
|
||||
final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { isSanitizerGuard(guard) }
|
||||
final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
|
||||
this.isSanitizerGuard(guard)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the additional taint propagation step from `node1` to `node2`
|
||||
@@ -101,7 +103,7 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { none() }
|
||||
|
||||
final override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
isAdditionalTaintStep(node1, node2) or
|
||||
this.isAdditionalTaintStep(node1, node2) or
|
||||
defaultAdditionalTaintStep(node1, node2)
|
||||
}
|
||||
|
||||
|
||||
@@ -75,24 +75,26 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
predicate isSanitizer(DataFlow::Node node) { none() }
|
||||
|
||||
final override predicate isBarrier(DataFlow::Node node) {
|
||||
isSanitizer(node) or
|
||||
this.isSanitizer(node) or
|
||||
defaultTaintSanitizer(node)
|
||||
}
|
||||
|
||||
/** Holds if taint propagation into `node` is prohibited. */
|
||||
predicate isSanitizerIn(DataFlow::Node node) { none() }
|
||||
|
||||
final override predicate isBarrierIn(DataFlow::Node node) { isSanitizerIn(node) }
|
||||
final override predicate isBarrierIn(DataFlow::Node node) { this.isSanitizerIn(node) }
|
||||
|
||||
/** Holds if taint propagation out of `node` is prohibited. */
|
||||
predicate isSanitizerOut(DataFlow::Node node) { none() }
|
||||
|
||||
final override predicate isBarrierOut(DataFlow::Node node) { isSanitizerOut(node) }
|
||||
final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) }
|
||||
|
||||
/** Holds if taint propagation through nodes guarded by `guard` is prohibited. */
|
||||
predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
|
||||
|
||||
final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { isSanitizerGuard(guard) }
|
||||
final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
|
||||
this.isSanitizerGuard(guard)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the additional taint propagation step from `node1` to `node2`
|
||||
@@ -101,7 +103,7 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { none() }
|
||||
|
||||
final override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
isAdditionalTaintStep(node1, node2) or
|
||||
this.isAdditionalTaintStep(node1, node2) or
|
||||
defaultAdditionalTaintStep(node1, node2)
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ class IRBlockBase extends TIRBlock {
|
||||
final string toString() { result = getFirstInstruction(this).toString() }
|
||||
|
||||
/** Gets the source location of the first non-`Phi` instruction in this block. */
|
||||
final Language::Location getLocation() { result = getFirstInstruction().getLocation() }
|
||||
final Language::Location getLocation() { result = this.getFirstInstruction().getLocation() }
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
@@ -39,7 +39,7 @@ class IRBlockBase extends TIRBlock {
|
||||
) and
|
||||
this =
|
||||
rank[result + 1](IRBlock funcBlock, int sortOverride, int sortKey1, int sortKey2 |
|
||||
funcBlock.getEnclosingFunction() = getEnclosingFunction() and
|
||||
funcBlock.getEnclosingFunction() = this.getEnclosingFunction() and
|
||||
funcBlock.getFirstInstruction().hasSortKeys(sortKey1, sortKey2) and
|
||||
// Ensure that the block containing `EnterFunction` always comes first.
|
||||
if funcBlock.getFirstInstruction() instanceof EnterFunctionInstruction
|
||||
@@ -59,15 +59,15 @@ class IRBlockBase extends TIRBlock {
|
||||
* Get the `Phi` instructions that appear at the start of this block.
|
||||
*/
|
||||
final PhiInstruction getAPhiInstruction() {
|
||||
Construction::getPhiInstructionBlockStart(result) = getFirstInstruction()
|
||||
Construction::getPhiInstructionBlockStart(result) = this.getFirstInstruction()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an instruction in this block. This includes `Phi` instructions.
|
||||
*/
|
||||
final Instruction getAnInstruction() {
|
||||
result = getInstruction(_) or
|
||||
result = getAPhiInstruction()
|
||||
result = this.getInstruction(_) or
|
||||
result = this.getAPhiInstruction()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -78,7 +78,9 @@ class IRBlockBase extends TIRBlock {
|
||||
/**
|
||||
* Gets the last instruction in this block.
|
||||
*/
|
||||
final Instruction getLastInstruction() { result = getInstruction(getInstructionCount() - 1) }
|
||||
final Instruction getLastInstruction() {
|
||||
result = this.getInstruction(this.getInstructionCount() - 1)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of non-`Phi` instructions in this block.
|
||||
@@ -149,7 +151,7 @@ class IRBlock extends IRBlockBase {
|
||||
* Block `A` dominates block `B` if any control flow path from the entry block of the function to
|
||||
* block `B` must pass through block `A`. A block always dominates itself.
|
||||
*/
|
||||
final predicate dominates(IRBlock block) { strictlyDominates(block) or this = block }
|
||||
final predicate dominates(IRBlock block) { this.strictlyDominates(block) or this = block }
|
||||
|
||||
/**
|
||||
* Gets a block on the dominance frontier of this block.
|
||||
@@ -159,8 +161,8 @@ class IRBlock extends IRBlockBase {
|
||||
*/
|
||||
pragma[noinline]
|
||||
final IRBlock dominanceFrontier() {
|
||||
dominates(result.getAPredecessor()) and
|
||||
not strictlyDominates(result)
|
||||
this.dominates(result.getAPredecessor()) and
|
||||
not this.strictlyDominates(result)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -189,7 +191,7 @@ class IRBlock extends IRBlockBase {
|
||||
* Block `A` post-dominates block `B` if any control flow path from `B` to the exit block of the
|
||||
* function must pass through block `A`. A block always post-dominates itself.
|
||||
*/
|
||||
final predicate postDominates(IRBlock block) { strictlyPostDominates(block) or this = block }
|
||||
final predicate postDominates(IRBlock block) { this.strictlyPostDominates(block) or this = block }
|
||||
|
||||
/**
|
||||
* Gets a block on the post-dominance frontier of this block.
|
||||
@@ -199,16 +201,16 @@ class IRBlock extends IRBlockBase {
|
||||
*/
|
||||
pragma[noinline]
|
||||
final IRBlock postPominanceFrontier() {
|
||||
postDominates(result.getASuccessor()) and
|
||||
not strictlyPostDominates(result)
|
||||
this.postDominates(result.getASuccessor()) and
|
||||
not this.strictlyPostDominates(result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this block is reachable from the entry block of its function.
|
||||
*/
|
||||
final predicate isReachableFromFunctionEntry() {
|
||||
this = getEnclosingIRFunction().getEntryBlock() or
|
||||
getAPredecessor().isReachableFromFunctionEntry()
|
||||
this = this.getEnclosingIRFunction().getEntryBlock() or
|
||||
this.getAPredecessor().isReachableFromFunctionEntry()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user