mirror of
https://github.com/github/codeql.git
synced 2026-07-06 03:55:30 +02:00
Compare commits
722 Commits
codeql-cli
...
alexet/jav
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
72a51af905 | ||
|
|
10afb1cd93 | ||
|
|
ffbe3e6ed4 | ||
|
|
f2db9ce312 | ||
|
|
d6374f65e4 | ||
|
|
ac6c4add14 | ||
|
|
d8686e02a8 | ||
|
|
821f399193 | ||
|
|
f4b3bae88b | ||
|
|
4832dc51ed | ||
|
|
63a04c056a | ||
|
|
45ce988943 | ||
|
|
034ed17227 | ||
|
|
d9b0a5918c | ||
|
|
453cdfa513 | ||
|
|
1785086ccb | ||
|
|
bc9396e0e6 | ||
|
|
f48e295f4a | ||
|
|
7d968184fd | ||
|
|
01475fd8ba | ||
|
|
4e4cd52f63 | ||
|
|
d62d68a40b | ||
|
|
69f6e1e263 | ||
|
|
adfb3c3d50 | ||
|
|
fb4ed39d89 | ||
|
|
a770bddff0 | ||
|
|
3f0ce98ccb | ||
|
|
205d6a3bc5 | ||
|
|
fa7f437e71 | ||
|
|
eca6c00003 | ||
|
|
c7a746e3dc | ||
|
|
3e3eceea5f | ||
|
|
e2c2d574f8 | ||
|
|
46a9bb9804 | ||
|
|
08611f0c9c | ||
|
|
e14e47cd73 | ||
|
|
d6f085373e | ||
|
|
2a73677fd2 | ||
|
|
a698684fec | ||
|
|
3e29a8d2a1 | ||
|
|
d387e6d068 | ||
|
|
9232fafde1 | ||
|
|
a8c5e4e0f2 | ||
|
|
bb9b0eabf2 | ||
|
|
a5b801b31f | ||
|
|
c219b1a3c7 | ||
|
|
93f940aa9c | ||
|
|
98de4e209b | ||
|
|
9cc287dff1 | ||
|
|
be245dd4b2 | ||
|
|
bfa8515b28 | ||
|
|
79094e4b89 | ||
|
|
1b3605754f | ||
|
|
0aa7de295b | ||
|
|
33fe5abf94 | ||
|
|
4e3a6e2140 | ||
|
|
cff63ad5d5 | ||
|
|
ccc5a80d93 | ||
|
|
2aa5ae41fb | ||
|
|
4c4ebd907e | ||
|
|
a3ae304dfe | ||
|
|
33c524d9fd | ||
|
|
9d655520cc | ||
|
|
dc5ee7c8b4 | ||
|
|
7fb64268bd | ||
|
|
d0c09f43a9 | ||
|
|
eb7f07dc3f | ||
|
|
c0f80ecd2f | ||
|
|
54a1c25276 | ||
|
|
9da8cb1165 | ||
|
|
06313b9bb6 | ||
|
|
84646cd795 | ||
|
|
6619be3137 | ||
|
|
ec071cde51 | ||
|
|
9300b04def | ||
|
|
a76832f4e0 | ||
|
|
8f56edea80 | ||
|
|
0ef8c7d87f | ||
|
|
1330c885c8 | ||
|
|
f31bb1391d | ||
|
|
5476f42d2c | ||
|
|
e3be2057d3 | ||
|
|
96cd259eda | ||
|
|
14aff5c94c | ||
|
|
0fe3072a65 | ||
|
|
969676975d | ||
|
|
616015fddf | ||
|
|
79dc7fcc04 | ||
|
|
1d38ca371b | ||
|
|
648543f2fb | ||
|
|
1fd11928eb | ||
|
|
9086ec8b13 | ||
|
|
c71ba0361e | ||
|
|
c9dbb7c5a9 | ||
|
|
06134467e9 | ||
|
|
3fa7532b43 | ||
|
|
8fbd743e9e | ||
|
|
7ff2998c88 | ||
|
|
3a7b80da47 | ||
|
|
9179f0bda6 | ||
|
|
1d956e1039 | ||
|
|
3ee965f2b9 | ||
|
|
0bb86fc087 | ||
|
|
10efcc2bb4 | ||
|
|
f6f7073520 | ||
|
|
97aa301ac9 | ||
|
|
88bfb81b1f | ||
|
|
d12ac1e7ce | ||
|
|
38169a981d | ||
|
|
6f388acdd8 | ||
|
|
04c9ed37a7 | ||
|
|
cac5a8236e | ||
|
|
0550c46766 | ||
|
|
5fed8bc57b | ||
|
|
016aedab0a | ||
|
|
cdc879ee89 | ||
|
|
1411ee5b26 | ||
|
|
a78080cc0e | ||
|
|
8b85735cdc | ||
|
|
bf46fa27d6 | ||
|
|
aeb667c6ca | ||
|
|
6bf1611f10 | ||
|
|
357a2ba733 | ||
|
|
458ee13345 | ||
|
|
ee411cc53a | ||
|
|
d3aa2eed64 | ||
|
|
f5fed84bb2 | ||
|
|
b8608a1b1c | ||
|
|
c640bd67e9 | ||
|
|
6ce3f35ef5 | ||
|
|
311ba8ea1b | ||
|
|
219cd4e415 | ||
|
|
f8124222cc | ||
|
|
7e479e3c8e | ||
|
|
ee3e38f0eb | ||
|
|
7371f5e508 | ||
|
|
597f0082e7 | ||
|
|
70c6744944 | ||
|
|
d24f032d97 | ||
|
|
b39842501a | ||
|
|
e32902ad47 | ||
|
|
5b37ee4ec7 | ||
|
|
54262a53c3 | ||
|
|
a88d8b260d | ||
|
|
a97891cbc1 | ||
|
|
f36b48346e | ||
|
|
df1845379a | ||
|
|
90db9b330f | ||
|
|
42c5066cae | ||
|
|
22ddf2129b | ||
|
|
31e04631d1 | ||
|
|
865026f22b | ||
|
|
72ff494739 | ||
|
|
1af1ba48a9 | ||
|
|
4d3076ae7e | ||
|
|
e16e1c7e83 | ||
|
|
06dbb4e9ef | ||
|
|
d7afd7b2e1 | ||
|
|
350b239ed6 | ||
|
|
d47e2690b8 | ||
|
|
fd49871b9a | ||
|
|
cee6f003fd | ||
|
|
0c3d9f75f4 | ||
|
|
f025430431 | ||
|
|
d78efdb67b | ||
|
|
dde148ee7e | ||
|
|
32b80f8cb1 | ||
|
|
5ab1047b14 | ||
|
|
9a8ec36a4f | ||
|
|
aa1d5c5e41 | ||
|
|
a3da6c886b | ||
|
|
44ab36f238 | ||
|
|
fc55567d90 | ||
|
|
8899d66132 | ||
|
|
187a68bf76 | ||
|
|
e895f96a3a | ||
|
|
b66b8785c4 | ||
|
|
35a309fd40 | ||
|
|
764e99bda7 | ||
|
|
e10ce8875f | ||
|
|
9c2b2160ce | ||
|
|
40dbc6fdd9 | ||
|
|
032678a367 | ||
|
|
dbd47b387a | ||
|
|
decede51dc | ||
|
|
44045d3eed | ||
|
|
a21eea4ee0 | ||
|
|
e812682991 | ||
|
|
b944f3b411 | ||
|
|
e373341f62 | ||
|
|
af7b1bc425 | ||
|
|
668239f355 | ||
|
|
bd0969b87b | ||
|
|
4a55b6fbdf | ||
|
|
80ae017aa1 | ||
|
|
0cecbf5239 | ||
|
|
4177c38ed4 | ||
|
|
322fb6c507 | ||
|
|
7a3b8ebb3a | ||
|
|
40089e8088 | ||
|
|
e53357d376 | ||
|
|
d7c9bfa08b | ||
|
|
1af8167354 | ||
|
|
d749335f54 | ||
|
|
d83500de5d | ||
|
|
e8e1dc0390 | ||
|
|
aebe9f6992 | ||
|
|
c82f5dad56 | ||
|
|
28c3d35e9b | ||
|
|
f9309cec0b | ||
|
|
7b6accd33a | ||
|
|
457d71d7bc | ||
|
|
dbf16827bf | ||
|
|
7429fa7b96 | ||
|
|
f63c9fa07f | ||
|
|
d2bb951ecd | ||
|
|
7fb05f4a76 | ||
|
|
575af1a5f9 | ||
|
|
1d12e0c9d9 | ||
|
|
e0476b555d | ||
|
|
0be329dbdc | ||
|
|
51db2b0bc4 | ||
|
|
881c426631 | ||
|
|
3a8d468983 | ||
|
|
a810165e35 | ||
|
|
a13391bda1 | ||
|
|
2a0c451d2d | ||
|
|
45c65b48aa | ||
|
|
754d4cd959 | ||
|
|
23a58a0835 | ||
|
|
fc367042ef | ||
|
|
a53d5d832d | ||
|
|
c4c843968e | ||
|
|
57d17d85f2 | ||
|
|
6c0885c24d | ||
|
|
5580daf60e | ||
|
|
1d2b31f0be | ||
|
|
5552fe3c34 | ||
|
|
28f98d0344 | ||
|
|
354cdf44aa | ||
|
|
f4542f6160 | ||
|
|
e23e3d7fb4 | ||
|
|
a8718f99a1 | ||
|
|
3a8db49573 | ||
|
|
f4f417c3f9 | ||
|
|
b5e59492bf | ||
|
|
fb218150e1 | ||
|
|
07ebbb0591 | ||
|
|
34decd3cf1 | ||
|
|
f4eb5f5a2d | ||
|
|
b7292fbc67 | ||
|
|
2af68d37d0 | ||
|
|
704f1fad46 | ||
|
|
daee22d38c | ||
|
|
8ae64e992c | ||
|
|
8c5fff2d11 | ||
|
|
23cf99734a | ||
|
|
f7c29e6bfb | ||
|
|
cfbc3f73ec | ||
|
|
3acdd3382c | ||
|
|
26a16b7857 | ||
|
|
a717bf1b9d | ||
|
|
af19a0342e | ||
|
|
085d803b14 | ||
|
|
31dc542111 | ||
|
|
70ebc58b4c | ||
|
|
3d8868a6c3 | ||
|
|
658b88e62f | ||
|
|
1f767b887e | ||
|
|
5cc9170249 | ||
|
|
342465057c | ||
|
|
19c9ea7e20 | ||
|
|
a51fe4a00e | ||
|
|
d3e0a90ae5 | ||
|
|
693c28a821 | ||
|
|
8e52483beb | ||
|
|
80649786c3 | ||
|
|
e7b00a7b42 | ||
|
|
171ff4d161 | ||
|
|
71cf948650 | ||
|
|
7f05743212 | ||
|
|
f464f1b94e | ||
|
|
b4ed77343b | ||
|
|
3e61be1b6a | ||
|
|
5333c75919 | ||
|
|
8c31b612ca | ||
|
|
6dddae0154 | ||
|
|
ee3efbadae | ||
|
|
945121de1b | ||
|
|
2280469564 | ||
|
|
7fdea27d33 | ||
|
|
2bd08838d4 | ||
|
|
5b734c76b6 | ||
|
|
20691e409c | ||
|
|
9aefdca7a7 | ||
|
|
30d906d42a | ||
|
|
dacf7d73d9 | ||
|
|
87b2dcc892 | ||
|
|
a24432bacc | ||
|
|
c375497fa5 | ||
|
|
d8c0ab8e1f | ||
|
|
9d44045e6f | ||
|
|
eecab9122a | ||
|
|
5b88b8a3ed | ||
|
|
36f6a6fb10 | ||
|
|
b5f349bd2c | ||
|
|
b2e0485c94 | ||
|
|
a839c92979 | ||
|
|
2fc0922b29 | ||
|
|
d1c253b519 | ||
|
|
61597f5ac7 | ||
|
|
ef8368cfc4 | ||
|
|
866a3934d4 | ||
|
|
54fa8181da | ||
|
|
67b3670d06 | ||
|
|
039bea1625 | ||
|
|
b638d4d0ba | ||
|
|
bd121b98ae | ||
|
|
6c0ed28e6b | ||
|
|
02ae2d1520 | ||
|
|
e4a4c18166 | ||
|
|
560b355e0c | ||
|
|
53502a8662 | ||
|
|
8d5eab401d | ||
|
|
16cef92106 | ||
|
|
8d504d8b32 | ||
|
|
465c3c18e3 | ||
|
|
c5a02dae2b | ||
|
|
3ea39a2553 | ||
|
|
bcd36b1994 | ||
|
|
adefdfd59f | ||
|
|
129286aa1c | ||
|
|
3ef1ab49ea | ||
|
|
013ed7adb3 | ||
|
|
b5c0fbb827 | ||
|
|
4085c8ec8f | ||
|
|
dd5eb982ec | ||
|
|
2fd2b4c874 | ||
|
|
cff2cdb9e4 | ||
|
|
c190dd21db | ||
|
|
d54489931c | ||
|
|
863e3f79e5 | ||
|
|
0e94aa0eb5 | ||
|
|
8d1ee10981 | ||
|
|
be2ce17376 | ||
|
|
ab6e2f9364 | ||
|
|
f613823047 | ||
|
|
35aae0a981 | ||
|
|
b53ae77c56 | ||
|
|
51f5740707 | ||
|
|
6a563c161e | ||
|
|
179a7d500e | ||
|
|
b07b0762f2 | ||
|
|
74876ff49b | ||
|
|
52f71e4553 | ||
|
|
c2aa334465 | ||
|
|
695e728ed5 | ||
|
|
dddba3228b | ||
|
|
6be0ed1dc3 | ||
|
|
d7790faece | ||
|
|
95a5ec7f27 | ||
|
|
50851210ea | ||
|
|
1a42e55095 | ||
|
|
0e0b73a5e6 | ||
|
|
eb62c033aa | ||
|
|
f59aaf1d75 | ||
|
|
2e5155d1f8 | ||
|
|
af06202241 | ||
|
|
bf27f203d5 | ||
|
|
1633673cc2 | ||
|
|
7745c2c2b7 | ||
|
|
8d767862dc | ||
|
|
1c57e99686 | ||
|
|
257686eb9a | ||
|
|
4291290277 | ||
|
|
e82e3180f0 | ||
|
|
9854ed4b89 | ||
|
|
2345907a52 | ||
|
|
f97b6e2848 | ||
|
|
32e532ff3c | ||
|
|
bc745dfd5e | ||
|
|
9f410eb2d6 | ||
|
|
9c51514bd9 | ||
|
|
c325ff8a23 | ||
|
|
d73f43477f | ||
|
|
04436208ab | ||
|
|
5749fdbc46 | ||
|
|
ff2d78d2c8 | ||
|
|
76aeee2820 | ||
|
|
61dbe26858 | ||
|
|
7b0df57d7a | ||
|
|
f571ebdaf4 | ||
|
|
800351c7b7 | ||
|
|
e6e6a4e9c8 | ||
|
|
adbcbefaa9 | ||
|
|
da66281fef | ||
|
|
7a39f077d9 | ||
|
|
4ac8dd72a7 | ||
|
|
73c4fe08fa | ||
|
|
00c228540e | ||
|
|
35a8e7cbf0 | ||
|
|
42acd9c22c | ||
|
|
6485dcc0fc | ||
|
|
a09eb9f4c5 | ||
|
|
7fe378e831 | ||
|
|
3fdc7e95df | ||
|
|
8187b00562 | ||
|
|
d300736c7e | ||
|
|
58f2777532 | ||
|
|
589a34241c | ||
|
|
820c14577a | ||
|
|
e7852f520f | ||
|
|
5440dbf70d | ||
|
|
43df6a2c07 | ||
|
|
66130d208e | ||
|
|
dbd33d1cf0 | ||
|
|
9b5cfc9026 | ||
|
|
3601773856 | ||
|
|
adf5a4b1e4 | ||
|
|
9ee2314ef6 | ||
|
|
7c35309732 | ||
|
|
87b6592dbc | ||
|
|
8fe483d9d8 | ||
|
|
245cd5c0b5 | ||
|
|
ac4601cb8f | ||
|
|
a9bab18804 | ||
|
|
36a775502f | ||
|
|
7c46e9fcf4 | ||
|
|
33eb69164c | ||
|
|
6d8d106d91 | ||
|
|
546b0a9a89 | ||
|
|
fc5b9e2796 | ||
|
|
24e35f6f3d | ||
|
|
63bb772ef9 | ||
|
|
e793a1e9fe | ||
|
|
2896bfbd9f | ||
|
|
7f950d8e0d | ||
|
|
7dd175d938 | ||
|
|
761f6d3a7e | ||
|
|
feb1ca29cc | ||
|
|
e4f680d476 | ||
|
|
a548316747 | ||
|
|
4f9bdca4f0 | ||
|
|
2321eecb9e | ||
|
|
cedbfbe7ea | ||
|
|
e74606eba3 | ||
|
|
79c5ad93b0 | ||
|
|
f2e467d8ea | ||
|
|
5b48bc4a3e | ||
|
|
48fcec82d6 | ||
|
|
608a3f907c | ||
|
|
1f64f5f8c9 | ||
|
|
f8c805de6b | ||
|
|
ec6e17360d | ||
|
|
a3f6bfe1df | ||
|
|
51afe12ae1 | ||
|
|
b0eb0e1f1e | ||
|
|
85782ff1d4 | ||
|
|
9e77b89885 | ||
|
|
1fa151db7c | ||
|
|
76564edc93 | ||
|
|
92b086d36f | ||
|
|
c9474050c8 | ||
|
|
ea38bf5ebc | ||
|
|
138ce42cf6 | ||
|
|
7d74125508 | ||
|
|
5202792163 | ||
|
|
b71b43a2fb | ||
|
|
4b0689b6ba | ||
|
|
6dec323cfc | ||
|
|
4dd8f6e618 | ||
|
|
81b04863b2 | ||
|
|
22b168beee | ||
|
|
67612e60b5 | ||
|
|
7bbc18f114 | ||
|
|
c7295a09cd | ||
|
|
f3a381f10d | ||
|
|
dae20ca50c | ||
|
|
6d5fd3c9de | ||
|
|
73040bd30f | ||
|
|
990dec67d0 | ||
|
|
43ee62ad64 | ||
|
|
eda345a5b8 | ||
|
|
e3380aa545 | ||
|
|
7f6c84dda8 | ||
|
|
35b93063f9 | ||
|
|
16375b0fe7 | ||
|
|
37677142b9 | ||
|
|
58a1353ddc | ||
|
|
1638183d18 | ||
|
|
6178acc070 | ||
|
|
81ce8dc02d | ||
|
|
92447dc743 | ||
|
|
7ba25b23a5 | ||
|
|
eb5cb2a7bf | ||
|
|
215808d7e9 | ||
|
|
cdf3d47031 | ||
|
|
c606ab09a5 | ||
|
|
d7dc73e18b | ||
|
|
b876117ecc | ||
|
|
01fc7432cb | ||
|
|
bf84f3a936 | ||
|
|
3295d5cb9f | ||
|
|
fc9919a5b6 | ||
|
|
c2db5f490e | ||
|
|
cd57cd0d8a | ||
|
|
cf4c8eb517 | ||
|
|
6dc0fa515d | ||
|
|
8ae6fa5366 | ||
|
|
f0a5183a3f | ||
|
|
4c9876b008 | ||
|
|
cf162aa412 | ||
|
|
84797b9091 | ||
|
|
cc754858c6 | ||
|
|
a60afef923 | ||
|
|
e58b6e86b2 | ||
|
|
0edfafeb06 | ||
|
|
a87df5459f | ||
|
|
527041348e | ||
|
|
e065390185 | ||
|
|
27ba51cf9d | ||
|
|
94a941115f | ||
|
|
ca55b92281 | ||
|
|
0ebe045cd8 | ||
|
|
c4f2bbda2a | ||
|
|
34308eee8d | ||
|
|
4e5a6d770a | ||
|
|
f1115af146 | ||
|
|
f4c2e65614 | ||
|
|
a54a73c9a2 | ||
|
|
caa45058ae | ||
|
|
55e6255e05 | ||
|
|
316273c7f3 | ||
|
|
66d2a8499d | ||
|
|
02bab4c15a | ||
|
|
0dbe8c3d8a | ||
|
|
d13ea0b6c9 | ||
|
|
6972f9b31d | ||
|
|
350dab4621 | ||
|
|
6ba6b12b9f | ||
|
|
b71074f9c4 | ||
|
|
fbbd57b34f | ||
|
|
f400228037 | ||
|
|
dcc6f83d3b | ||
|
|
7027b7fe82 | ||
|
|
b4fdd4e222 | ||
|
|
e78e71c875 | ||
|
|
edd383afc1 | ||
|
|
a264ea23c6 | ||
|
|
2b99b83857 | ||
|
|
8e43c5c683 | ||
|
|
4dde1fb117 | ||
|
|
148241183a | ||
|
|
91cb2a37fd | ||
|
|
bd7b2c4cc6 | ||
|
|
d5c34264ad | ||
|
|
179aaa1342 | ||
|
|
5be75e91fc | ||
|
|
a78e04eb34 | ||
|
|
05aaf2bb67 | ||
|
|
84f3771cdd | ||
|
|
28e1e3a13a | ||
|
|
6e09dcc16a | ||
|
|
22e6c676c3 | ||
|
|
b7d2e54bbd | ||
|
|
00ab1a3129 | ||
|
|
a92e394a7c | ||
|
|
7286f56718 | ||
|
|
97275157e6 | ||
|
|
c0974934bc | ||
|
|
39a802fb98 | ||
|
|
6a1bb9bfb0 | ||
|
|
357638baa8 | ||
|
|
0bf0c069c6 | ||
|
|
fc12537699 | ||
|
|
910725939f | ||
|
|
50ad45944c | ||
|
|
5def2887e7 | ||
|
|
31687afd5d | ||
|
|
4ab7acedb6 | ||
|
|
72e6853792 | ||
|
|
7072ab9364 | ||
|
|
f5d014baa5 | ||
|
|
59b14f6a69 | ||
|
|
45e1be875c | ||
|
|
4dc605354c | ||
|
|
1ba3efb111 | ||
|
|
e84a509976 | ||
|
|
c3671c7625 | ||
|
|
be3c1ed0be | ||
|
|
c95abd47ce | ||
|
|
cb1c68260e | ||
|
|
c663809cc7 | ||
|
|
0e24ed14da | ||
|
|
5a1c0f60e6 | ||
|
|
4b1626c83a | ||
|
|
65b30c1dff | ||
|
|
a08b292099 | ||
|
|
7c60562132 | ||
|
|
7e3e5db3db | ||
|
|
bcd5c08ebd | ||
|
|
5d212514c6 | ||
|
|
1658a1cb80 | ||
|
|
bff95c4c1b | ||
|
|
1fece75f15 | ||
|
|
ff5f794750 | ||
|
|
bbe8c6dcaa | ||
|
|
9f01ea68f7 | ||
|
|
a499919239 | ||
|
|
f834768720 | ||
|
|
9d2dc7a3cc | ||
|
|
88e3bc6865 | ||
|
|
445b82b4e1 | ||
|
|
70465b22c7 | ||
|
|
98289b52d6 | ||
|
|
8151f3024d | ||
|
|
13e3a5158e | ||
|
|
6a0adff1dc | ||
|
|
eeaa2bcc55 | ||
|
|
853397361f | ||
|
|
052a8e7f81 | ||
|
|
f384afbaf6 | ||
|
|
3ad83cc098 | ||
|
|
7cd84c8f0a | ||
|
|
6b310bb825 | ||
|
|
2a70437a12 | ||
|
|
4b9340816c | ||
|
|
9ab3837cdc | ||
|
|
d0e7fbc871 | ||
|
|
b4b5ae2a2c | ||
|
|
3eb9491cb4 | ||
|
|
f7b8e8af41 | ||
|
|
8bed3fbed4 | ||
|
|
9d13a1ff51 | ||
|
|
d1847566b6 | ||
|
|
beef9965cc | ||
|
|
a03c06802e | ||
|
|
0056067a17 | ||
|
|
083f56921c | ||
|
|
573763a4b3 | ||
|
|
f1392712ee | ||
|
|
5125973f9b | ||
|
|
1927530517 | ||
|
|
376c6ea09a | ||
|
|
7b85bb4c95 | ||
|
|
4367b7813c | ||
|
|
d1c0294551 | ||
|
|
5e401abccb | ||
|
|
2f1d4b923e | ||
|
|
4c769f2b09 | ||
|
|
3307457deb | ||
|
|
13b0a9a842 | ||
|
|
905420143b | ||
|
|
580e68d5de | ||
|
|
45bb4a0ee5 | ||
|
|
3b1751dc8a | ||
|
|
f433039a25 | ||
|
|
f275531542 | ||
|
|
a47c702171 | ||
|
|
65ac94320e | ||
|
|
073f65d586 | ||
|
|
3c79faf37a | ||
|
|
2fe10942da | ||
|
|
3fcb0ee228 | ||
|
|
b2edf6cf3e | ||
|
|
a1c384c57b | ||
|
|
0efb00724d | ||
|
|
22cb9ed063 | ||
|
|
79edc5c261 | ||
|
|
c90fcd19cc | ||
|
|
8d8126f849 | ||
|
|
a72bd7efcc | ||
|
|
87b1028aab | ||
|
|
ac465b9234 | ||
|
|
7121282b27 | ||
|
|
572777f11b | ||
|
|
59195cccdd | ||
|
|
bb5017121f | ||
|
|
737f3e8899 | ||
|
|
2cb0afee73 | ||
|
|
1aa4494dbc | ||
|
|
fabde6e0ff | ||
|
|
75e01d3648 | ||
|
|
b8c800608e | ||
|
|
7af4b8de7b | ||
|
|
e421c49440 | ||
|
|
bd1ee9b937 | ||
|
|
1fc18bee5d | ||
|
|
a1a49bdddf | ||
|
|
761aede2df | ||
|
|
ac5e9c75e1 | ||
|
|
7d60f03131 | ||
|
|
8e5be13466 | ||
|
|
46616d6b42 | ||
|
|
c630300374 | ||
|
|
d63b33bb08 | ||
|
|
14d1e08051 | ||
|
|
13577f71d6 | ||
|
|
21f477a674 | ||
|
|
d4b2ca5cee | ||
|
|
31cae204f6 | ||
|
|
00d1b11b0b | ||
|
|
d99c0a2382 | ||
|
|
9245e1c2a4 | ||
|
|
2961b79fb9 | ||
|
|
2e7529cc70 | ||
|
|
e1d5c9d45b | ||
|
|
5a3a8d781a | ||
|
|
08f78a2df4 | ||
|
|
0f540f4c25 | ||
|
|
f6f99fb459 | ||
|
|
c76d0d364d | ||
|
|
f79bd2a071 | ||
|
|
7ce825c5ea | ||
|
|
ab7e797fff | ||
|
|
26f1091d5f | ||
|
|
56d0254d2b | ||
|
|
4ee54738fa | ||
|
|
260c111932 | ||
|
|
1b598c8683 | ||
|
|
f1918fb4e0 | ||
|
|
fbfc959f82 |
@@ -1 +1 @@
|
|||||||
7.0.2
|
7.1.0
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ provide:
|
|||||||
- "*/ql/consistency-queries/qlpack.yml"
|
- "*/ql/consistency-queries/qlpack.yml"
|
||||||
- "*/ql/automodel/src/qlpack.yml"
|
- "*/ql/automodel/src/qlpack.yml"
|
||||||
- "*/ql/automodel/test/qlpack.yml"
|
- "*/ql/automodel/test/qlpack.yml"
|
||||||
|
- "python/extractor/qlpack.yml"
|
||||||
- "shared/**/qlpack.yml"
|
- "shared/**/qlpack.yml"
|
||||||
- "cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/qlpack.yml"
|
- "cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/qlpack.yml"
|
||||||
- "go/ql/config/legacy-support/qlpack.yml"
|
- "go/ql/config/legacy-support/qlpack.yml"
|
||||||
|
|||||||
@@ -88,123 +88,46 @@
|
|||||||
"IR Instruction": [
|
"IR Instruction": [
|
||||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/Instruction.qll",
|
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/Instruction.qll",
|
||||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll",
|
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll",
|
||||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll",
|
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll"
|
||||||
"csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll",
|
|
||||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll"
|
|
||||||
],
|
],
|
||||||
"IR IRBlock": [
|
"IR IRBlock": [
|
||||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IRBlock.qll",
|
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IRBlock.qll",
|
||||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll",
|
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll",
|
||||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll",
|
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll"
|
||||||
"csharp/ql/src/experimental/ir/implementation/raw/IRBlock.qll",
|
|
||||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRBlock.qll"
|
|
||||||
],
|
],
|
||||||
"IR IRVariable": [
|
"IR IRVariable": [
|
||||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IRVariable.qll",
|
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IRVariable.qll",
|
||||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll",
|
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll",
|
||||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll",
|
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll"
|
||||||
"csharp/ql/src/experimental/ir/implementation/raw/IRVariable.qll",
|
|
||||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRVariable.qll"
|
|
||||||
],
|
],
|
||||||
"IR IRFunction": [
|
"IR IRFunction": [
|
||||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IRFunction.qll",
|
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IRFunction.qll",
|
||||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IRFunction.qll",
|
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IRFunction.qll",
|
||||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRFunction.qll",
|
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRFunction.qll"
|
||||||
"csharp/ql/src/experimental/ir/implementation/raw/IRFunction.qll",
|
|
||||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRFunction.qll"
|
|
||||||
],
|
],
|
||||||
"IR Operand": [
|
"IR Operand": [
|
||||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/Operand.qll",
|
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/Operand.qll",
|
||||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll",
|
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll",
|
||||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll",
|
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll"
|
||||||
"csharp/ql/src/experimental/ir/implementation/raw/Operand.qll",
|
|
||||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Operand.qll"
|
|
||||||
],
|
|
||||||
"IR IRType": [
|
|
||||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/IRType.qll",
|
|
||||||
"csharp/ql/src/experimental/ir/implementation/IRType.qll"
|
|
||||||
],
|
|
||||||
"IR IRConfiguration": [
|
|
||||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/IRConfiguration.qll",
|
|
||||||
"csharp/ql/src/experimental/ir/implementation/IRConfiguration.qll"
|
|
||||||
],
|
|
||||||
"IR UseSoundEscapeAnalysis": [
|
|
||||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/UseSoundEscapeAnalysis.qll",
|
|
||||||
"csharp/ql/src/experimental/ir/implementation/UseSoundEscapeAnalysis.qll"
|
|
||||||
],
|
|
||||||
"IR IRFunctionBase": [
|
|
||||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/internal/IRFunctionBase.qll",
|
|
||||||
"csharp/ql/src/experimental/ir/implementation/internal/IRFunctionBase.qll"
|
|
||||||
],
|
|
||||||
"IR Operand Tag": [
|
|
||||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/internal/OperandTag.qll",
|
|
||||||
"csharp/ql/src/experimental/ir/implementation/internal/OperandTag.qll"
|
|
||||||
],
|
|
||||||
"IR TInstruction": [
|
|
||||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/internal/TInstruction.qll",
|
|
||||||
"csharp/ql/src/experimental/ir/implementation/internal/TInstruction.qll"
|
|
||||||
],
|
|
||||||
"IR TIRVariable": [
|
|
||||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/internal/TIRVariable.qll",
|
|
||||||
"csharp/ql/src/experimental/ir/implementation/internal/TIRVariable.qll"
|
|
||||||
],
|
],
|
||||||
"IR IR": [
|
"IR IR": [
|
||||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IR.qll",
|
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IR.qll",
|
||||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IR.qll",
|
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IR.qll",
|
||||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IR.qll",
|
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IR.qll"
|
||||||
"csharp/ql/src/experimental/ir/implementation/raw/IR.qll",
|
|
||||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IR.qll"
|
|
||||||
],
|
],
|
||||||
"IR IRConsistency": [
|
"IR IRConsistency": [
|
||||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IRConsistency.qll",
|
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IRConsistency.qll",
|
||||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IRConsistency.qll",
|
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IRConsistency.qll",
|
||||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRConsistency.qll",
|
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRConsistency.qll"
|
||||||
"csharp/ql/src/experimental/ir/implementation/raw/IRConsistency.qll",
|
|
||||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRConsistency.qll"
|
|
||||||
],
|
],
|
||||||
"IR PrintIR": [
|
"IR PrintIR": [
|
||||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/PrintIR.qll",
|
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/PrintIR.qll",
|
||||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/PrintIR.qll",
|
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/PrintIR.qll",
|
||||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll",
|
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll"
|
||||||
"csharp/ql/src/experimental/ir/implementation/raw/PrintIR.qll",
|
|
||||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/PrintIR.qll"
|
|
||||||
],
|
|
||||||
"IR IntegerConstant": [
|
|
||||||
"cpp/ql/lib/semmle/code/cpp/ir/internal/IntegerConstant.qll",
|
|
||||||
"csharp/ql/src/experimental/ir/internal/IntegerConstant.qll"
|
|
||||||
],
|
|
||||||
"IR IntegerInteval": [
|
|
||||||
"cpp/ql/lib/semmle/code/cpp/ir/internal/IntegerInterval.qll",
|
|
||||||
"csharp/ql/src/experimental/ir/internal/IntegerInterval.qll"
|
|
||||||
],
|
|
||||||
"IR IntegerPartial": [
|
|
||||||
"cpp/ql/lib/semmle/code/cpp/ir/internal/IntegerPartial.qll",
|
|
||||||
"csharp/ql/src/experimental/ir/internal/IntegerPartial.qll"
|
|
||||||
],
|
|
||||||
"IR Overlap": [
|
|
||||||
"cpp/ql/lib/semmle/code/cpp/ir/internal/Overlap.qll",
|
|
||||||
"csharp/ql/src/experimental/ir/internal/Overlap.qll"
|
|
||||||
],
|
|
||||||
"IR EdgeKind": [
|
|
||||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/EdgeKind.qll",
|
|
||||||
"csharp/ql/src/experimental/ir/implementation/EdgeKind.qll"
|
|
||||||
],
|
|
||||||
"IR MemoryAccessKind": [
|
|
||||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/MemoryAccessKind.qll",
|
|
||||||
"csharp/ql/src/experimental/ir/implementation/MemoryAccessKind.qll"
|
|
||||||
],
|
|
||||||
"IR TempVariableTag": [
|
|
||||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/TempVariableTag.qll",
|
|
||||||
"csharp/ql/src/experimental/ir/implementation/TempVariableTag.qll"
|
|
||||||
],
|
|
||||||
"IR Opcode": [
|
|
||||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/Opcode.qll",
|
|
||||||
"csharp/ql/src/experimental/ir/implementation/Opcode.qll"
|
|
||||||
],
|
],
|
||||||
"IR SSAConsistency": [
|
"IR SSAConsistency": [
|
||||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConsistency.qll",
|
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConsistency.qll",
|
||||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConsistency.qll",
|
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConsistency.qll"
|
||||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConsistency.qll"
|
|
||||||
],
|
],
|
||||||
"C++ IR InstructionImports": [
|
"C++ IR InstructionImports": [
|
||||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/InstructionImports.qll",
|
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/InstructionImports.qll",
|
||||||
@@ -252,8 +175,7 @@
|
|||||||
],
|
],
|
||||||
"SSA AliasAnalysis": [
|
"SSA AliasAnalysis": [
|
||||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll",
|
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll",
|
||||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll",
|
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll"
|
||||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll"
|
|
||||||
],
|
],
|
||||||
"SSA PrintAliasAnalysis": [
|
"SSA PrintAliasAnalysis": [
|
||||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/PrintAliasAnalysis.qll",
|
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/PrintAliasAnalysis.qll",
|
||||||
@@ -268,44 +190,28 @@
|
|||||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingImports.qll",
|
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingImports.qll",
|
||||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/internal/ValueNumberingImports.qll"
|
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/internal/ValueNumberingImports.qll"
|
||||||
],
|
],
|
||||||
"IR SSA SimpleSSA": [
|
|
||||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll",
|
|
||||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll"
|
|
||||||
],
|
|
||||||
"IR AliasConfiguration (unaliased_ssa)": [
|
|
||||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll",
|
|
||||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll"
|
|
||||||
],
|
|
||||||
"IR SSA SSAConstruction": [
|
"IR SSA SSAConstruction": [
|
||||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll",
|
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll",
|
||||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll",
|
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll"
|
||||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll"
|
|
||||||
],
|
],
|
||||||
"IR SSA PrintSSA": [
|
"IR SSA PrintSSA": [
|
||||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/PrintSSA.qll",
|
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/PrintSSA.qll",
|
||||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintSSA.qll",
|
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintSSA.qll"
|
||||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/PrintSSA.qll"
|
|
||||||
],
|
],
|
||||||
"IR ValueNumberInternal": [
|
"IR ValueNumberInternal": [
|
||||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/gvn/internal/ValueNumberingInternal.qll",
|
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/gvn/internal/ValueNumberingInternal.qll",
|
||||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingInternal.qll",
|
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingInternal.qll",
|
||||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/internal/ValueNumberingInternal.qll",
|
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/internal/ValueNumberingInternal.qll"
|
||||||
"csharp/ql/src/experimental/ir/implementation/raw/gvn/internal/ValueNumberingInternal.qll",
|
|
||||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingInternal.qll"
|
|
||||||
],
|
],
|
||||||
"C++ IR ValueNumber": [
|
"C++ IR ValueNumber": [
|
||||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/gvn/ValueNumbering.qll",
|
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/gvn/ValueNumbering.qll",
|
||||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/ValueNumbering.qll",
|
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/ValueNumbering.qll",
|
||||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/ValueNumbering.qll",
|
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/ValueNumbering.qll"
|
||||||
"csharp/ql/src/experimental/ir/implementation/raw/gvn/ValueNumbering.qll",
|
|
||||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/gvn/ValueNumbering.qll"
|
|
||||||
],
|
],
|
||||||
"C++ IR PrintValueNumbering": [
|
"C++ IR PrintValueNumbering": [
|
||||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/gvn/PrintValueNumbering.qll",
|
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/gvn/PrintValueNumbering.qll",
|
||||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/PrintValueNumbering.qll",
|
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/PrintValueNumbering.qll",
|
||||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/PrintValueNumbering.qll",
|
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/PrintValueNumbering.qll"
|
||||||
"csharp/ql/src/experimental/ir/implementation/raw/gvn/PrintValueNumbering.qll",
|
|
||||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/gvn/PrintValueNumbering.qll"
|
|
||||||
],
|
],
|
||||||
"C++ IR ConstantAnalysis": [
|
"C++ IR ConstantAnalysis": [
|
||||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/constant/ConstantAnalysis.qll",
|
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/constant/ConstantAnalysis.qll",
|
||||||
@@ -333,38 +239,6 @@
|
|||||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/reachability/PrintDominance.qll",
|
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/reachability/PrintDominance.qll",
|
||||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/PrintDominance.qll"
|
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/PrintDominance.qll"
|
||||||
],
|
],
|
||||||
"C# IR InstructionImports": [
|
|
||||||
"csharp/ql/src/experimental/ir/implementation/raw/internal/InstructionImports.qll",
|
|
||||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/InstructionImports.qll"
|
|
||||||
],
|
|
||||||
"C# IR IRImports": [
|
|
||||||
"csharp/ql/src/experimental/ir/implementation/raw/internal/IRImports.qll",
|
|
||||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/IRImports.qll"
|
|
||||||
],
|
|
||||||
"C# IR IRBlockImports": [
|
|
||||||
"csharp/ql/src/experimental/ir/implementation/raw/internal/IRBlockImports.qll",
|
|
||||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/IRBlockImports.qll"
|
|
||||||
],
|
|
||||||
"C# IR IRFunctionImports": [
|
|
||||||
"csharp/ql/src/experimental/ir/implementation/raw/internal/IRFunctionImports.qll",
|
|
||||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/IRFunctionImports.qll"
|
|
||||||
],
|
|
||||||
"C# IR IRVariableImports": [
|
|
||||||
"csharp/ql/src/experimental/ir/implementation/raw/internal/IRVariableImports.qll",
|
|
||||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/IRVariableImports.qll"
|
|
||||||
],
|
|
||||||
"C# IR OperandImports": [
|
|
||||||
"csharp/ql/src/experimental/ir/implementation/raw/internal/OperandImports.qll",
|
|
||||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/OperandImports.qll"
|
|
||||||
],
|
|
||||||
"C# IR PrintIRImports": [
|
|
||||||
"csharp/ql/src/experimental/ir/implementation/raw/internal/PrintIRImports.qll",
|
|
||||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/PrintIRImports.qll"
|
|
||||||
],
|
|
||||||
"C# IR ValueNumberingImports": [
|
|
||||||
"csharp/ql/src/experimental/ir/implementation/raw/gvn/internal/ValueNumberingImports.qll",
|
|
||||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingImports.qll"
|
|
||||||
],
|
|
||||||
"C# ControlFlowReachability": [
|
"C# ControlFlowReachability": [
|
||||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/ControlFlowReachability.qll",
|
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/ControlFlowReachability.qll",
|
||||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/ControlFlowReachability.qll"
|
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/ControlFlowReachability.qll"
|
||||||
@@ -381,7 +255,6 @@
|
|||||||
"cpp/ql/lib/semmle/code/cpp/XML.qll",
|
"cpp/ql/lib/semmle/code/cpp/XML.qll",
|
||||||
"csharp/ql/lib/semmle/code/csharp/XML.qll",
|
"csharp/ql/lib/semmle/code/csharp/XML.qll",
|
||||||
"java/ql/lib/semmle/code/xml/XML.qll",
|
"java/ql/lib/semmle/code/xml/XML.qll",
|
||||||
"javascript/ql/lib/semmle/javascript/XML.qll",
|
|
||||||
"python/ql/lib/semmle/python/xml/XML.qll"
|
"python/ql/lib/semmle/python/xml/XML.qll"
|
||||||
],
|
],
|
||||||
"DuplicationProblems.inc.qhelp": [
|
"DuplicationProblems.inc.qhelp": [
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
class Expr extends @expr {
|
||||||
|
string toString() { none() }
|
||||||
|
}
|
||||||
|
|
||||||
|
class Location extends @location_expr {
|
||||||
|
string toString() { none() }
|
||||||
|
}
|
||||||
|
|
||||||
|
from Expr expr, int kind, int kind_new, Location loc
|
||||||
|
where
|
||||||
|
exprs(expr, kind, loc) and
|
||||||
|
if kind = 363 then kind_new = 1 else kind_new = kind
|
||||||
|
select expr, kind_new, loc
|
||||||
2250
cpp/downgrades/aa7ff0ab32cd4674f6ab731d32fea64116997b05/old.dbscheme
Normal file
2250
cpp/downgrades/aa7ff0ab32cd4674f6ab731d32fea64116997b05/old.dbscheme
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,4 @@
|
|||||||
|
description: Introduce re-use expressions
|
||||||
|
compatibility: partial
|
||||||
|
expr_reuse.rel: delete
|
||||||
|
exprs.rel: run exprs.qlo
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
category: feature
|
||||||
|
---
|
||||||
|
* Added a predicate `GuardCondition.valueControls` to query whether a basic block is guarded by a particular `case` of a `switch` statement.
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
category: feature
|
||||||
|
---
|
||||||
|
* Added a predicate `GuardCondition.comparesLt/4` to query whether an expression is compared to a constant.
|
||||||
|
* Added a predicate `GuardCondition.ensuresLt/4` to query whether a basic block is guarded by an expression being less than a constant.
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
category: feature
|
||||||
|
---
|
||||||
|
* Added a predicate `GuardCondition.comparesEq/4` to query whether an expression is compared to a constant.
|
||||||
|
* Added a predicate `GuardCondition.ensuresEq/4` to query whether a basic block is guarded by an expression being equal to a constant.
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
name: codeql/cpp-all
|
name: codeql/cpp-all
|
||||||
version: 0.12.8
|
version: 0.12.9-dev
|
||||||
groups: cpp
|
groups: cpp
|
||||||
dbscheme: semmlecode.cpp.dbscheme
|
dbscheme: semmlecode.cpp.dbscheme
|
||||||
extractor: cpp
|
extractor: cpp
|
||||||
|
|||||||
@@ -309,9 +309,12 @@ class ExprNode extends AstNode {
|
|||||||
override AstNode getChildInternal(int childIndex) {
|
override AstNode getChildInternal(int childIndex) {
|
||||||
result.getAst() = expr.getChild(childIndex)
|
result.getAst() = expr.getChild(childIndex)
|
||||||
or
|
or
|
||||||
|
childIndex = max(int index | exists(expr.getChild(index)) or index = 0) + 1 and
|
||||||
|
result.getAst() = expr.(ConditionDeclExpr).getInitializingExpr()
|
||||||
|
or
|
||||||
exists(int destructorIndex |
|
exists(int destructorIndex |
|
||||||
result.getAst() = expr.getImplicitDestructorCall(destructorIndex) and
|
result.getAst() = expr.getImplicitDestructorCall(destructorIndex) and
|
||||||
childIndex = destructorIndex + max(int index | exists(expr.getChild(index)) or index = 0) + 1
|
childIndex = destructorIndex + max(int index | exists(expr.getChild(index)) or index = 0) + 2
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -686,6 +689,8 @@ private string getChildAccessorWithoutConversions(Locatable parent, Element chil
|
|||||||
not namedExprChildPredicates(expr, child, _) and
|
not namedExprChildPredicates(expr, child, _) and
|
||||||
exists(int n | expr.getChild(n) = child and result = "getChild(" + n + ")")
|
exists(int n | expr.getChild(n) = child and result = "getChild(" + n + ")")
|
||||||
or
|
or
|
||||||
|
expr.(ConditionDeclExpr).getInitializingExpr() = child and result = "getInitializingExpr()"
|
||||||
|
or
|
||||||
exists(int n |
|
exists(int n |
|
||||||
expr.getImplicitDestructorCall(n) = child and
|
expr.getImplicitDestructorCall(n) = child and
|
||||||
result = "getImplicitDestructorCall(" + n + ")"
|
result = "getImplicitDestructorCall(" + n + ")"
|
||||||
|
|||||||
@@ -20,6 +20,44 @@ private predicate isUnreachedBlock(IRBlock block) {
|
|||||||
block.getFirstInstruction() instanceof UnreachedInstruction
|
block.getFirstInstruction() instanceof UnreachedInstruction
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private newtype TAbstractValue =
|
||||||
|
TBooleanValue(boolean b) { b = true or b = false } or
|
||||||
|
TMatchValue(CaseEdge c)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An abstract value. This is either a boolean value, or a `switch` case.
|
||||||
|
*/
|
||||||
|
abstract class AbstractValue extends TAbstractValue {
|
||||||
|
/** Gets an abstract value that represents the dual of this value, if any. */
|
||||||
|
abstract AbstractValue getDualValue();
|
||||||
|
|
||||||
|
/** Gets a textual representation of this abstract value. */
|
||||||
|
abstract string toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A Boolean value. */
|
||||||
|
class BooleanValue extends AbstractValue, TBooleanValue {
|
||||||
|
/** Gets the underlying Boolean value. */
|
||||||
|
boolean getValue() { this = TBooleanValue(result) }
|
||||||
|
|
||||||
|
override BooleanValue getDualValue() { result.getValue() = this.getValue().booleanNot() }
|
||||||
|
|
||||||
|
override string toString() { result = this.getValue().toString() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A value that represents a match against a specific `switch` case. */
|
||||||
|
class MatchValue extends AbstractValue, TMatchValue {
|
||||||
|
/** Gets the case. */
|
||||||
|
CaseEdge getCase() { this = TMatchValue(result) }
|
||||||
|
|
||||||
|
override MatchValue getDualValue() {
|
||||||
|
// A `MatchValue` has no dual.
|
||||||
|
none()
|
||||||
|
}
|
||||||
|
|
||||||
|
override string toString() { result = this.getCase().toString() }
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Boolean condition in the AST that guards one or more basic blocks. This includes
|
* A Boolean condition in the AST that guards one or more basic blocks. This includes
|
||||||
* operands of logical operators but not switch statements.
|
* operands of logical operators but not switch statements.
|
||||||
@@ -34,6 +72,15 @@ class GuardCondition extends Expr {
|
|||||||
this.(BinaryLogicalOperation).getAnOperand() instanceof GuardCondition
|
this.(BinaryLogicalOperation).getAnOperand() instanceof GuardCondition
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if this condition controls `controlled`, meaning that `controlled` is only
|
||||||
|
* entered if the value of this condition is `v`.
|
||||||
|
*
|
||||||
|
* For details on what "controls" mean, see the QLDoc for `controls`.
|
||||||
|
*/
|
||||||
|
cached
|
||||||
|
predicate valueControls(BasicBlock controlled, AbstractValue v) { none() }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if this condition controls `controlled`, meaning that `controlled` is only
|
* Holds if this condition controls `controlled`, meaning that `controlled` is only
|
||||||
* entered if the value of this condition is `testIsTrue`.
|
* entered if the value of this condition is `testIsTrue`.
|
||||||
@@ -61,7 +108,9 @@ class GuardCondition extends Expr {
|
|||||||
* true (for `&&`) or false (for `||`) branch.
|
* true (for `&&`) or false (for `||`) branch.
|
||||||
*/
|
*/
|
||||||
cached
|
cached
|
||||||
predicate controls(BasicBlock controlled, boolean testIsTrue) { none() }
|
final predicate controls(BasicBlock controlled, boolean testIsTrue) {
|
||||||
|
this.valueControls(controlled, any(BooleanValue bv | bv.getValue() = testIsTrue))
|
||||||
|
}
|
||||||
|
|
||||||
/** Holds if (determined by this guard) `left < right + k` evaluates to `isLessThan` if this expression evaluates to `testIsTrue`. */
|
/** Holds if (determined by this guard) `left < right + k` evaluates to `isLessThan` if this expression evaluates to `testIsTrue`. */
|
||||||
cached
|
cached
|
||||||
@@ -76,6 +125,20 @@ class GuardCondition extends Expr {
|
|||||||
cached
|
cached
|
||||||
predicate ensuresLt(Expr left, Expr right, int k, BasicBlock block, boolean isLessThan) { none() }
|
predicate ensuresLt(Expr left, Expr right, int k, BasicBlock block, boolean isLessThan) { none() }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if (determined by this guard) `e < k` evaluates to `isLessThan` if
|
||||||
|
* this expression evaluates to `value`.
|
||||||
|
*/
|
||||||
|
cached
|
||||||
|
predicate comparesLt(Expr e, int k, boolean isLessThan, AbstractValue value) { none() }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if (determined by this guard) `e < k` must be `isLessThan` in `block`.
|
||||||
|
* If `isLessThan = false` then this implies `e >= k`.
|
||||||
|
*/
|
||||||
|
cached
|
||||||
|
predicate ensuresLt(Expr e, int k, BasicBlock block, boolean isLessThan) { none() }
|
||||||
|
|
||||||
/** Holds if (determined by this guard) `left == right + k` evaluates to `areEqual` if this expression evaluates to `testIsTrue`. */
|
/** Holds if (determined by this guard) `left == right + k` evaluates to `areEqual` if this expression evaluates to `testIsTrue`. */
|
||||||
cached
|
cached
|
||||||
predicate comparesEq(Expr left, Expr right, int k, boolean areEqual, boolean testIsTrue) {
|
predicate comparesEq(Expr left, Expr right, int k, boolean areEqual, boolean testIsTrue) {
|
||||||
@@ -88,6 +151,17 @@ class GuardCondition extends Expr {
|
|||||||
*/
|
*/
|
||||||
cached
|
cached
|
||||||
predicate ensuresEq(Expr left, Expr right, int k, BasicBlock block, boolean areEqual) { none() }
|
predicate ensuresEq(Expr left, Expr right, int k, BasicBlock block, boolean areEqual) { none() }
|
||||||
|
|
||||||
|
/** Holds if (determined by this guard) `e == k` evaluates to `areEqual` if this expression evaluates to `value`. */
|
||||||
|
cached
|
||||||
|
predicate comparesEq(Expr e, int k, boolean areEqual, AbstractValue value) { none() }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if (determined by this guard) `e == k` must be `areEqual` in `block`.
|
||||||
|
* If `areEqual = false` then this implies `e != k`.
|
||||||
|
*/
|
||||||
|
cached
|
||||||
|
predicate ensuresEq(Expr e, int k, BasicBlock block, boolean areEqual) { none() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -98,13 +172,13 @@ private class GuardConditionFromBinaryLogicalOperator extends GuardCondition {
|
|||||||
this.(BinaryLogicalOperation).getAnOperand() instanceof GuardCondition
|
this.(BinaryLogicalOperation).getAnOperand() instanceof GuardCondition
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate controls(BasicBlock controlled, boolean testIsTrue) {
|
override predicate valueControls(BasicBlock controlled, AbstractValue v) {
|
||||||
exists(BinaryLogicalOperation binop, GuardCondition lhs, GuardCondition rhs |
|
exists(BinaryLogicalOperation binop, GuardCondition lhs, GuardCondition rhs |
|
||||||
this = binop and
|
this = binop and
|
||||||
lhs = binop.getLeftOperand() and
|
lhs = binop.getLeftOperand() and
|
||||||
rhs = binop.getRightOperand() and
|
rhs = binop.getRightOperand() and
|
||||||
lhs.controls(controlled, testIsTrue) and
|
lhs.valueControls(controlled, v) and
|
||||||
rhs.controls(controlled, testIsTrue)
|
rhs.valueControls(controlled, v)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,12 +190,27 @@ private class GuardConditionFromBinaryLogicalOperator extends GuardCondition {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override predicate comparesLt(Expr e, int k, boolean isLessThan, AbstractValue value) {
|
||||||
|
exists(BooleanValue partValue, GuardCondition part |
|
||||||
|
this.(BinaryLogicalOperation)
|
||||||
|
.impliesValue(part, partValue.getValue(), value.(BooleanValue).getValue())
|
||||||
|
|
|
||||||
|
part.comparesLt(e, k, isLessThan, partValue)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
override predicate ensuresLt(Expr left, Expr right, int k, BasicBlock block, boolean isLessThan) {
|
override predicate ensuresLt(Expr left, Expr right, int k, BasicBlock block, boolean isLessThan) {
|
||||||
exists(boolean testIsTrue |
|
exists(boolean testIsTrue |
|
||||||
this.comparesLt(left, right, k, isLessThan, testIsTrue) and this.controls(block, testIsTrue)
|
this.comparesLt(left, right, k, isLessThan, testIsTrue) and this.controls(block, testIsTrue)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override predicate ensuresLt(Expr e, int k, BasicBlock block, boolean isLessThan) {
|
||||||
|
exists(AbstractValue value |
|
||||||
|
this.comparesLt(e, k, isLessThan, value) and this.valueControls(block, value)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
override predicate comparesEq(Expr left, Expr right, int k, boolean areEqual, boolean testIsTrue) {
|
override predicate comparesEq(Expr left, Expr right, int k, boolean areEqual, boolean testIsTrue) {
|
||||||
exists(boolean partIsTrue, GuardCondition part |
|
exists(boolean partIsTrue, GuardCondition part |
|
||||||
this.(BinaryLogicalOperation).impliesValue(part, partIsTrue, testIsTrue)
|
this.(BinaryLogicalOperation).impliesValue(part, partIsTrue, testIsTrue)
|
||||||
@@ -135,6 +224,21 @@ private class GuardConditionFromBinaryLogicalOperator extends GuardCondition {
|
|||||||
this.comparesEq(left, right, k, areEqual, testIsTrue) and this.controls(block, testIsTrue)
|
this.comparesEq(left, right, k, areEqual, testIsTrue) and this.controls(block, testIsTrue)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override predicate comparesEq(Expr e, int k, boolean areEqual, AbstractValue value) {
|
||||||
|
exists(BooleanValue partValue, GuardCondition part |
|
||||||
|
this.(BinaryLogicalOperation)
|
||||||
|
.impliesValue(part, partValue.getValue(), value.(BooleanValue).getValue())
|
||||||
|
|
|
||||||
|
part.comparesEq(e, k, areEqual, partValue)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override predicate ensuresEq(Expr e, int k, BasicBlock block, boolean areEqual) {
|
||||||
|
exists(AbstractValue value |
|
||||||
|
this.comparesEq(e, k, areEqual, value) and this.valueControls(block, value)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -146,13 +250,12 @@ private class GuardConditionFromIR extends GuardCondition {
|
|||||||
|
|
||||||
GuardConditionFromIR() { this = ir.getUnconvertedResultExpression() }
|
GuardConditionFromIR() { this = ir.getUnconvertedResultExpression() }
|
||||||
|
|
||||||
override predicate controls(BasicBlock controlled, boolean testIsTrue) {
|
override predicate valueControls(BasicBlock controlled, AbstractValue v) {
|
||||||
// This condition must determine the flow of control; that is, this
|
// This condition must determine the flow of control; that is, this
|
||||||
// node must be a top-level condition.
|
// node must be a top-level condition.
|
||||||
this.controlsBlock(controlled, testIsTrue)
|
this.controlsBlock(controlled, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Holds if (determined by this guard) `left < right + k` evaluates to `isLessThan` if this expression evaluates to `testIsTrue`. */
|
|
||||||
override predicate comparesLt(Expr left, Expr right, int k, boolean isLessThan, boolean testIsTrue) {
|
override predicate comparesLt(Expr left, Expr right, int k, boolean isLessThan, boolean testIsTrue) {
|
||||||
exists(Instruction li, Instruction ri |
|
exists(Instruction li, Instruction ri |
|
||||||
li.getUnconvertedResultExpression() = left and
|
li.getUnconvertedResultExpression() = left and
|
||||||
@@ -161,10 +264,13 @@ private class GuardConditionFromIR extends GuardCondition {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
override predicate comparesLt(Expr e, int k, boolean isLessThan, AbstractValue value) {
|
||||||
* Holds if (determined by this guard) `left < right + k` must be `isLessThan` in `block`.
|
exists(Instruction i |
|
||||||
* If `isLessThan = false` then this implies `left >= right + k`.
|
i.getUnconvertedResultExpression() = e and
|
||||||
*/
|
ir.comparesLt(i.getAUse(), k, isLessThan, value)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
override predicate ensuresLt(Expr left, Expr right, int k, BasicBlock block, boolean isLessThan) {
|
override predicate ensuresLt(Expr left, Expr right, int k, BasicBlock block, boolean isLessThan) {
|
||||||
exists(Instruction li, Instruction ri, boolean testIsTrue |
|
exists(Instruction li, Instruction ri, boolean testIsTrue |
|
||||||
li.getUnconvertedResultExpression() = left and
|
li.getUnconvertedResultExpression() = left and
|
||||||
@@ -174,7 +280,14 @@ private class GuardConditionFromIR extends GuardCondition {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Holds if (determined by this guard) `left == right + k` evaluates to `areEqual` if this expression evaluates to `testIsTrue`. */
|
override predicate ensuresLt(Expr e, int k, BasicBlock block, boolean isLessThan) {
|
||||||
|
exists(Instruction i, AbstractValue value |
|
||||||
|
i.getUnconvertedResultExpression() = e and
|
||||||
|
ir.comparesLt(i.getAUse(), k, isLessThan, value) and
|
||||||
|
this.valueControls(block, value)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
override predicate comparesEq(Expr left, Expr right, int k, boolean areEqual, boolean testIsTrue) {
|
override predicate comparesEq(Expr left, Expr right, int k, boolean areEqual, boolean testIsTrue) {
|
||||||
exists(Instruction li, Instruction ri |
|
exists(Instruction li, Instruction ri |
|
||||||
li.getUnconvertedResultExpression() = left and
|
li.getUnconvertedResultExpression() = left and
|
||||||
@@ -183,10 +296,6 @@ private class GuardConditionFromIR extends GuardCondition {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Holds if (determined by this guard) `left == right + k` must be `areEqual` in `block`.
|
|
||||||
* If `areEqual = false` then this implies `left != right + k`.
|
|
||||||
*/
|
|
||||||
override predicate ensuresEq(Expr left, Expr right, int k, BasicBlock block, boolean areEqual) {
|
override predicate ensuresEq(Expr left, Expr right, int k, BasicBlock block, boolean areEqual) {
|
||||||
exists(Instruction li, Instruction ri, boolean testIsTrue |
|
exists(Instruction li, Instruction ri, boolean testIsTrue |
|
||||||
li.getUnconvertedResultExpression() = left and
|
li.getUnconvertedResultExpression() = left and
|
||||||
@@ -196,15 +305,30 @@ private class GuardConditionFromIR extends GuardCondition {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override predicate comparesEq(Expr e, int k, boolean areEqual, AbstractValue value) {
|
||||||
|
exists(Instruction i |
|
||||||
|
i.getUnconvertedResultExpression() = e and
|
||||||
|
ir.comparesEq(i.getAUse(), k, areEqual, value)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override predicate ensuresEq(Expr e, int k, BasicBlock block, boolean areEqual) {
|
||||||
|
exists(Instruction i, AbstractValue value |
|
||||||
|
i.getUnconvertedResultExpression() = e and
|
||||||
|
ir.comparesEq(i.getAUse(), k, areEqual, value) and
|
||||||
|
this.valueControls(block, value)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if this condition controls `block`, meaning that `block` is only
|
* Holds if this condition controls `block`, meaning that `block` is only
|
||||||
* entered if the value of this condition is `testIsTrue`. This helper
|
* entered if the value of this condition is `v`. This helper
|
||||||
* predicate does not necessarily hold for binary logical operations like
|
* predicate does not necessarily hold for binary logical operations like
|
||||||
* `&&` and `||`. See the detailed explanation on predicate `controls`.
|
* `&&` and `||`. See the detailed explanation on predicate `controls`.
|
||||||
*/
|
*/
|
||||||
private predicate controlsBlock(BasicBlock controlled, boolean testIsTrue) {
|
private predicate controlsBlock(BasicBlock controlled, AbstractValue v) {
|
||||||
exists(IRBlock irb |
|
exists(IRBlock irb |
|
||||||
ir.controls(irb, testIsTrue) and
|
ir.valueControls(irb, v) and
|
||||||
nonExcludedIRAndBasicBlock(irb, controlled) and
|
nonExcludedIRAndBasicBlock(irb, controlled) and
|
||||||
not isUnreachedBlock(irb)
|
not isUnreachedBlock(irb)
|
||||||
)
|
)
|
||||||
@@ -249,10 +373,28 @@ private predicate nonExcludedIRAndBasicBlock(IRBlock irb, BasicBlock controlled)
|
|||||||
*/
|
*/
|
||||||
cached
|
cached
|
||||||
class IRGuardCondition extends Instruction {
|
class IRGuardCondition extends Instruction {
|
||||||
ConditionalBranchInstruction branch;
|
Instruction branch;
|
||||||
|
|
||||||
cached
|
cached
|
||||||
IRGuardCondition() { branch = get_branch_for_condition(this) }
|
IRGuardCondition() { branch = getBranchForCondition(this) }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if this condition controls `controlled`, meaning that `controlled` is only
|
||||||
|
* entered if the value of this condition is `v`.
|
||||||
|
*
|
||||||
|
* For details on what "controls" mean, see the QLDoc for `controls`.
|
||||||
|
*/
|
||||||
|
cached
|
||||||
|
predicate valueControls(IRBlock controlled, AbstractValue v) {
|
||||||
|
// This condition must determine the flow of control; that is, this
|
||||||
|
// node must be a top-level condition.
|
||||||
|
this.controlsBlock(controlled, v)
|
||||||
|
or
|
||||||
|
exists(IRGuardCondition ne |
|
||||||
|
this = ne.(LogicalNotInstruction).getUnary() and
|
||||||
|
ne.valueControls(controlled, v.getDualValue())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if this condition controls `controlled`, meaning that `controlled` is only
|
* Holds if this condition controls `controlled`, meaning that `controlled` is only
|
||||||
@@ -282,13 +424,25 @@ class IRGuardCondition extends Instruction {
|
|||||||
*/
|
*/
|
||||||
cached
|
cached
|
||||||
predicate controls(IRBlock controlled, boolean testIsTrue) {
|
predicate controls(IRBlock controlled, boolean testIsTrue) {
|
||||||
// This condition must determine the flow of control; that is, this
|
this.valueControls(controlled, any(BooleanValue bv | bv.getValue() = testIsTrue))
|
||||||
// node must be a top-level condition.
|
}
|
||||||
this.controlsBlock(controlled, testIsTrue)
|
|
||||||
|
/**
|
||||||
|
* Holds if the control-flow edge `(pred, succ)` may be taken only if
|
||||||
|
* the value of this condition is `v`.
|
||||||
|
*/
|
||||||
|
cached
|
||||||
|
predicate valueControlsEdge(IRBlock pred, IRBlock succ, AbstractValue v) {
|
||||||
|
pred.getASuccessor() = succ and
|
||||||
|
this.valueControls(pred, v)
|
||||||
or
|
or
|
||||||
exists(IRGuardCondition ne |
|
succ = this.getBranchSuccessor(v) and
|
||||||
this = ne.(LogicalNotInstruction).getUnary() and
|
(
|
||||||
ne.controls(controlled, testIsTrue.booleanNot())
|
branch.(ConditionalBranchInstruction).getCondition() = this and
|
||||||
|
branch.getBlock() = pred
|
||||||
|
or
|
||||||
|
branch.(SwitchInstruction).getExpression() = this and
|
||||||
|
branch.getBlock() = pred
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -297,17 +451,12 @@ class IRGuardCondition extends Instruction {
|
|||||||
* the value of this condition is `testIsTrue`.
|
* the value of this condition is `testIsTrue`.
|
||||||
*/
|
*/
|
||||||
cached
|
cached
|
||||||
predicate controlsEdge(IRBlock pred, IRBlock succ, boolean testIsTrue) {
|
final predicate controlsEdge(IRBlock pred, IRBlock succ, boolean testIsTrue) {
|
||||||
pred.getASuccessor() = succ and
|
this.valueControlsEdge(pred, succ, any(BooleanValue bv | bv.getValue() = testIsTrue))
|
||||||
this.controls(pred, testIsTrue)
|
|
||||||
or
|
|
||||||
succ = this.getBranchSuccessor(testIsTrue) and
|
|
||||||
branch.getCondition() = this and
|
|
||||||
branch.getBlock() = pred
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the block to which `branch` jumps directly when this condition is `testIsTrue`.
|
* Gets the block to which `branch` jumps directly when the value of this condition is `v`.
|
||||||
*
|
*
|
||||||
* This predicate is intended to help with situations in which an inference can only be made
|
* This predicate is intended to help with situations in which an inference can only be made
|
||||||
* based on an edge between a block with multiple successors and a block with multiple
|
* based on an edge between a block with multiple successors and a block with multiple
|
||||||
@@ -321,21 +470,39 @@ class IRGuardCondition extends Instruction {
|
|||||||
* return x;
|
* return x;
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
private IRBlock getBranchSuccessor(boolean testIsTrue) {
|
private IRBlock getBranchSuccessor(AbstractValue v) {
|
||||||
branch.getCondition() = this and
|
branch.(ConditionalBranchInstruction).getCondition() = this and
|
||||||
(
|
exists(BooleanValue bv | bv = v |
|
||||||
testIsTrue = true and
|
bv.getValue() = true and
|
||||||
result.getFirstInstruction() = branch.getTrueSuccessor()
|
result.getFirstInstruction() = branch.(ConditionalBranchInstruction).getTrueSuccessor()
|
||||||
or
|
or
|
||||||
testIsTrue = false and
|
bv.getValue() = false and
|
||||||
result.getFirstInstruction() = branch.getFalseSuccessor()
|
result.getFirstInstruction() = branch.(ConditionalBranchInstruction).getFalseSuccessor()
|
||||||
|
)
|
||||||
|
or
|
||||||
|
exists(SwitchInstruction switch, CaseEdge kind | switch = branch |
|
||||||
|
switch.getExpression() = this and
|
||||||
|
result.getFirstInstruction() = switch.getSuccessor(kind) and
|
||||||
|
kind = v.(MatchValue).getCase()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Holds if (determined by this guard) `left < right + k` evaluates to `isLessThan` if this expression evaluates to `testIsTrue`. */
|
/** Holds if (determined by this guard) `left < right + k` evaluates to `isLessThan` if this expression evaluates to `testIsTrue`. */
|
||||||
cached
|
cached
|
||||||
predicate comparesLt(Operand left, Operand right, int k, boolean isLessThan, boolean testIsTrue) {
|
predicate comparesLt(Operand left, Operand right, int k, boolean isLessThan, boolean testIsTrue) {
|
||||||
compares_lt(this, left, right, k, isLessThan, testIsTrue)
|
exists(BooleanValue value |
|
||||||
|
compares_lt(this, left, right, k, isLessThan, value) and
|
||||||
|
value.getValue() = testIsTrue
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if (determined by this guard) `op < k` evaluates to `isLessThan` if
|
||||||
|
* this expression evaluates to `value`.
|
||||||
|
*/
|
||||||
|
cached
|
||||||
|
predicate comparesLt(Operand op, int k, boolean isLessThan, AbstractValue value) {
|
||||||
|
compares_lt(this, op, k, isLessThan, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -344,8 +511,19 @@ class IRGuardCondition extends Instruction {
|
|||||||
*/
|
*/
|
||||||
cached
|
cached
|
||||||
predicate ensuresLt(Operand left, Operand right, int k, IRBlock block, boolean isLessThan) {
|
predicate ensuresLt(Operand left, Operand right, int k, IRBlock block, boolean isLessThan) {
|
||||||
exists(boolean testIsTrue |
|
exists(AbstractValue value |
|
||||||
compares_lt(this, left, right, k, isLessThan, testIsTrue) and this.controls(block, testIsTrue)
|
compares_lt(this, left, right, k, isLessThan, value) and this.valueControls(block, value)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if (determined by this guard) `op < k` must be `isLessThan` in `block`.
|
||||||
|
* If `isLessThan = false` then this implies `op >= k`.
|
||||||
|
*/
|
||||||
|
cached
|
||||||
|
predicate ensuresLt(Operand op, int k, IRBlock block, boolean isLessThan) {
|
||||||
|
exists(AbstractValue value |
|
||||||
|
compares_lt(this, op, k, isLessThan, value) and this.valueControls(block, value)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -357,16 +535,37 @@ class IRGuardCondition extends Instruction {
|
|||||||
predicate ensuresLtEdge(
|
predicate ensuresLtEdge(
|
||||||
Operand left, Operand right, int k, IRBlock pred, IRBlock succ, boolean isLessThan
|
Operand left, Operand right, int k, IRBlock pred, IRBlock succ, boolean isLessThan
|
||||||
) {
|
) {
|
||||||
exists(boolean testIsTrue |
|
exists(AbstractValue value |
|
||||||
compares_lt(this, left, right, k, isLessThan, testIsTrue) and
|
compares_lt(this, left, right, k, isLessThan, value) and
|
||||||
this.controlsEdge(pred, succ, testIsTrue)
|
this.valueControlsEdge(pred, succ, value)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if (determined by this guard) `op < k` must be `isLessThan` on the edge from
|
||||||
|
* `pred` to `succ`. If `isLessThan = false` then this implies `op >= k`.
|
||||||
|
*/
|
||||||
|
cached
|
||||||
|
predicate ensuresLtEdge(Operand left, int k, IRBlock pred, IRBlock succ, boolean isLessThan) {
|
||||||
|
exists(AbstractValue value |
|
||||||
|
compares_lt(this, left, k, isLessThan, value) and
|
||||||
|
this.valueControlsEdge(pred, succ, value)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Holds if (determined by this guard) `left == right + k` evaluates to `areEqual` if this expression evaluates to `testIsTrue`. */
|
/** Holds if (determined by this guard) `left == right + k` evaluates to `areEqual` if this expression evaluates to `testIsTrue`. */
|
||||||
cached
|
cached
|
||||||
predicate comparesEq(Operand left, Operand right, int k, boolean areEqual, boolean testIsTrue) {
|
predicate comparesEq(Operand left, Operand right, int k, boolean areEqual, boolean testIsTrue) {
|
||||||
compares_eq(this, left, right, k, areEqual, testIsTrue)
|
exists(BooleanValue value |
|
||||||
|
compares_eq(this, left, right, k, areEqual, value) and
|
||||||
|
value.getValue() = testIsTrue
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Holds if (determined by this guard) `op == k` evaluates to `areEqual` if this expression evaluates to `value`. */
|
||||||
|
cached
|
||||||
|
predicate comparesEq(Operand op, int k, boolean areEqual, AbstractValue value) {
|
||||||
|
compares_eq(this, op, k, areEqual, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -375,8 +574,19 @@ class IRGuardCondition extends Instruction {
|
|||||||
*/
|
*/
|
||||||
cached
|
cached
|
||||||
predicate ensuresEq(Operand left, Operand right, int k, IRBlock block, boolean areEqual) {
|
predicate ensuresEq(Operand left, Operand right, int k, IRBlock block, boolean areEqual) {
|
||||||
exists(boolean testIsTrue |
|
exists(AbstractValue value |
|
||||||
compares_eq(this, left, right, k, areEqual, testIsTrue) and this.controls(block, testIsTrue)
|
compares_eq(this, left, right, k, areEqual, value) and this.valueControls(block, value)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if (determined by this guard) `op == k` must be `areEqual` in `block`.
|
||||||
|
* If `areEqual = false` then this implies `op != k`.
|
||||||
|
*/
|
||||||
|
cached
|
||||||
|
predicate ensuresEq(Operand op, int k, IRBlock block, boolean areEqual) {
|
||||||
|
exists(AbstractValue value |
|
||||||
|
compares_eq(this, op, k, areEqual, value) and this.valueControls(block, value)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -388,19 +598,31 @@ class IRGuardCondition extends Instruction {
|
|||||||
predicate ensuresEqEdge(
|
predicate ensuresEqEdge(
|
||||||
Operand left, Operand right, int k, IRBlock pred, IRBlock succ, boolean areEqual
|
Operand left, Operand right, int k, IRBlock pred, IRBlock succ, boolean areEqual
|
||||||
) {
|
) {
|
||||||
exists(boolean testIsTrue |
|
exists(AbstractValue value |
|
||||||
compares_eq(this, left, right, k, areEqual, testIsTrue) and
|
compares_eq(this, left, right, k, areEqual, value) and
|
||||||
this.controlsEdge(pred, succ, testIsTrue)
|
this.valueControlsEdge(pred, succ, value)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if (determined by this guard) `op == k` must be `areEqual` on the edge from
|
||||||
|
* `pred` to `succ`. If `areEqual = false` then this implies `op != k`.
|
||||||
|
*/
|
||||||
|
cached
|
||||||
|
predicate ensuresEqEdge(Operand op, int k, IRBlock pred, IRBlock succ, boolean areEqual) {
|
||||||
|
exists(AbstractValue value |
|
||||||
|
compares_eq(this, op, k, areEqual, value) and
|
||||||
|
this.valueControlsEdge(pred, succ, value)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if this condition controls `block`, meaning that `block` is only
|
* Holds if this condition controls `block`, meaning that `block` is only
|
||||||
* entered if the value of this condition is `testIsTrue`. This helper
|
* entered if the value of this condition is `v`. This helper
|
||||||
* predicate does not necessarily hold for binary logical operations like
|
* predicate does not necessarily hold for binary logical operations like
|
||||||
* `&&` and `||`. See the detailed explanation on predicate `controls`.
|
* `&&` and `||`. See the detailed explanation on predicate `controls`.
|
||||||
*/
|
*/
|
||||||
private predicate controlsBlock(IRBlock controlled, boolean testIsTrue) {
|
private predicate controlsBlock(IRBlock controlled, AbstractValue v) {
|
||||||
not isUnreachedBlock(controlled) and
|
not isUnreachedBlock(controlled) and
|
||||||
//
|
//
|
||||||
// For this block to control the block `controlled` with `testIsTrue` the
|
// For this block to control the block `controlled` with `testIsTrue` the
|
||||||
@@ -441,7 +663,7 @@ class IRGuardCondition extends Instruction {
|
|||||||
// that `this` strictly dominates `controlled` so that isn't necessary to check
|
// that `this` strictly dominates `controlled` so that isn't necessary to check
|
||||||
// directly.
|
// directly.
|
||||||
exists(IRBlock succ |
|
exists(IRBlock succ |
|
||||||
succ = this.getBranchSuccessor(testIsTrue) and
|
succ = this.getBranchSuccessor(v) and
|
||||||
this.hasDominatingEdgeTo(succ) and
|
this.hasDominatingEdgeTo(succ) and
|
||||||
succ.dominates(controlled)
|
succ.dominates(controlled)
|
||||||
)
|
)
|
||||||
@@ -476,12 +698,14 @@ class IRGuardCondition extends Instruction {
|
|||||||
private IRBlock getBranchBlock() { result = branch.getBlock() }
|
private IRBlock getBranchBlock() { result = branch.getBlock() }
|
||||||
}
|
}
|
||||||
|
|
||||||
private ConditionalBranchInstruction get_branch_for_condition(Instruction guard) {
|
private Instruction getBranchForCondition(Instruction guard) {
|
||||||
result.getCondition() = guard
|
result.(ConditionalBranchInstruction).getCondition() = guard
|
||||||
or
|
or
|
||||||
exists(LogicalNotInstruction cond |
|
exists(LogicalNotInstruction cond |
|
||||||
result = get_branch_for_condition(cond) and cond.getUnary() = guard
|
result = getBranchForCondition(cond) and cond.getUnary() = guard
|
||||||
)
|
)
|
||||||
|
or
|
||||||
|
result.(SwitchInstruction).getExpression() = guard
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -490,52 +714,98 @@ private ConditionalBranchInstruction get_branch_for_condition(Instruction guard)
|
|||||||
* Beware making mistaken logical implications here relating `areEqual` and `testIsTrue`.
|
* Beware making mistaken logical implications here relating `areEqual` and `testIsTrue`.
|
||||||
*/
|
*/
|
||||||
private predicate compares_eq(
|
private predicate compares_eq(
|
||||||
Instruction test, Operand left, Operand right, int k, boolean areEqual, boolean testIsTrue
|
Instruction test, Operand left, Operand right, int k, boolean areEqual, AbstractValue value
|
||||||
) {
|
) {
|
||||||
/* The simple case where the test *is* the comparison so areEqual = testIsTrue xor eq. */
|
/* The simple case where the test *is* the comparison so areEqual = testIsTrue xor eq. */
|
||||||
exists(boolean eq | simple_comparison_eq(test, left, right, k, eq) |
|
exists(AbstractValue v | simple_comparison_eq(test, left, right, k, v) |
|
||||||
areEqual = true and testIsTrue = eq
|
areEqual = true and value = v
|
||||||
or
|
or
|
||||||
areEqual = false and testIsTrue = eq.booleanNot()
|
areEqual = false and value = v.getDualValue()
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
// I think this is handled by forwarding in controlsBlock.
|
// I think this is handled by forwarding in controlsBlock.
|
||||||
//or
|
//or
|
||||||
//logical_comparison_eq(test, left, right, k, areEqual, testIsTrue)
|
//logical_comparison_eq(test, left, right, k, areEqual, testIsTrue)
|
||||||
/* a == b + k => b == a - k */
|
/* a == b + k => b == a - k */
|
||||||
exists(int mk | k = -mk | compares_eq(test, right, left, mk, areEqual, testIsTrue))
|
exists(int mk | k = -mk | compares_eq(test, right, left, mk, areEqual, value))
|
||||||
or
|
or
|
||||||
complex_eq(test, left, right, k, areEqual, testIsTrue)
|
complex_eq(test, left, right, k, areEqual, value)
|
||||||
or
|
or
|
||||||
/* (x is true => (left == right + k)) => (!x is false => (left == right + k)) */
|
/* (x is true => (left == right + k)) => (!x is false => (left == right + k)) */
|
||||||
exists(boolean isFalse | testIsTrue = isFalse.booleanNot() |
|
exists(AbstractValue dual | value = dual.getDualValue() |
|
||||||
compares_eq(test.(LogicalNotInstruction).getUnary(), left, right, k, areEqual, isFalse)
|
compares_eq(test.(LogicalNotInstruction).getUnary(), left, right, k, areEqual, dual)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Holds if `op == k` is `areEqual` given that `test` is equal to `value`. */
|
||||||
|
private predicate compares_eq(
|
||||||
|
Instruction test, Operand op, int k, boolean areEqual, AbstractValue value
|
||||||
|
) {
|
||||||
|
/* The simple case where the test *is* the comparison so areEqual = testIsTrue xor eq. */
|
||||||
|
exists(AbstractValue v | simple_comparison_eq(test, op, k, v) |
|
||||||
|
areEqual = true and value = v
|
||||||
|
or
|
||||||
|
areEqual = false and value = v.getDualValue()
|
||||||
|
)
|
||||||
|
or
|
||||||
|
complex_eq(test, op, k, areEqual, value)
|
||||||
|
or
|
||||||
|
/* (x is true => (op == k)) => (!x is false => (op == k)) */
|
||||||
|
exists(AbstractValue dual | value = dual.getDualValue() |
|
||||||
|
compares_eq(test.(LogicalNotInstruction).getUnary(), op, k, areEqual, dual)
|
||||||
|
)
|
||||||
|
or
|
||||||
|
// ((test is `areEqual` => op == const + k2) and const == `k1`) =>
|
||||||
|
// test is `areEqual` => op == k1 + k2
|
||||||
|
exists(int k1, int k2, ConstantInstruction const |
|
||||||
|
compares_eq(test, op, const.getAUse(), k2, areEqual, value) and
|
||||||
|
int_value(const) = k1 and
|
||||||
|
k = k1 + k2
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Rearrange various simple comparisons into `left == right + k` form. */
|
/** Rearrange various simple comparisons into `left == right + k` form. */
|
||||||
private predicate simple_comparison_eq(
|
private predicate simple_comparison_eq(
|
||||||
CompareInstruction cmp, Operand left, Operand right, int k, boolean areEqual
|
CompareInstruction cmp, Operand left, Operand right, int k, AbstractValue value
|
||||||
) {
|
) {
|
||||||
left = cmp.getLeftOperand() and
|
left = cmp.getLeftOperand() and
|
||||||
cmp instanceof CompareEQInstruction and
|
cmp instanceof CompareEQInstruction and
|
||||||
right = cmp.getRightOperand() and
|
right = cmp.getRightOperand() and
|
||||||
k = 0 and
|
k = 0 and
|
||||||
areEqual = true
|
value.(BooleanValue).getValue() = true
|
||||||
or
|
or
|
||||||
left = cmp.getLeftOperand() and
|
left = cmp.getLeftOperand() and
|
||||||
cmp instanceof CompareNEInstruction and
|
cmp instanceof CompareNEInstruction and
|
||||||
right = cmp.getRightOperand() and
|
right = cmp.getRightOperand() and
|
||||||
k = 0 and
|
k = 0 and
|
||||||
areEqual = false
|
value.(BooleanValue).getValue() = false
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Rearrange various simple comparisons into `op == k` form. */
|
||||||
|
private predicate simple_comparison_eq(Instruction test, Operand op, int k, AbstractValue value) {
|
||||||
|
exists(SwitchInstruction switch, CaseEdge case |
|
||||||
|
test = switch.getExpression() and
|
||||||
|
op.getDef() = test and
|
||||||
|
case = value.(MatchValue).getCase() and
|
||||||
|
exists(switch.getSuccessor(case)) and
|
||||||
|
case.getValue().toInt() = k
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private predicate complex_eq(
|
private predicate complex_eq(
|
||||||
CompareInstruction cmp, Operand left, Operand right, int k, boolean areEqual, boolean testIsTrue
|
CompareInstruction cmp, Operand left, Operand right, int k, boolean areEqual, AbstractValue value
|
||||||
) {
|
) {
|
||||||
sub_eq(cmp, left, right, k, areEqual, testIsTrue)
|
sub_eq(cmp, left, right, k, areEqual, value)
|
||||||
or
|
or
|
||||||
add_eq(cmp, left, right, k, areEqual, testIsTrue)
|
add_eq(cmp, left, right, k, areEqual, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
private predicate complex_eq(
|
||||||
|
Instruction test, Operand op, int k, boolean areEqual, AbstractValue value
|
||||||
|
) {
|
||||||
|
sub_eq(test, op, k, areEqual, value)
|
||||||
|
or
|
||||||
|
add_eq(test, op, k, areEqual, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -545,31 +815,46 @@ private predicate complex_eq(
|
|||||||
|
|
||||||
/** Holds if `left < right + k` evaluates to `isLt` given that test is `testIsTrue`. */
|
/** Holds if `left < right + k` evaluates to `isLt` given that test is `testIsTrue`. */
|
||||||
private predicate compares_lt(
|
private predicate compares_lt(
|
||||||
Instruction test, Operand left, Operand right, int k, boolean isLt, boolean testIsTrue
|
Instruction test, Operand left, Operand right, int k, boolean isLt, AbstractValue value
|
||||||
) {
|
) {
|
||||||
/* In the simple case, the test is the comparison, so isLt = testIsTrue */
|
/* In the simple case, the test is the comparison, so isLt = testIsTrue */
|
||||||
simple_comparison_lt(test, left, right, k) and isLt = true and testIsTrue = true
|
simple_comparison_lt(test, left, right, k) and
|
||||||
|
value.(BooleanValue).getValue() = isLt
|
||||||
or
|
or
|
||||||
simple_comparison_lt(test, left, right, k) and isLt = false and testIsTrue = false
|
complex_lt(test, left, right, k, isLt, value)
|
||||||
or
|
|
||||||
complex_lt(test, left, right, k, isLt, testIsTrue)
|
|
||||||
or
|
or
|
||||||
/* (not (left < right + k)) => (left >= right + k) */
|
/* (not (left < right + k)) => (left >= right + k) */
|
||||||
exists(boolean isGe | isLt = isGe.booleanNot() |
|
exists(boolean isGe | isLt = isGe.booleanNot() | compares_ge(test, left, right, k, isGe, value))
|
||||||
compares_ge(test, left, right, k, isGe, testIsTrue)
|
|
||||||
)
|
|
||||||
or
|
or
|
||||||
/* (x is true => (left < right + k)) => (!x is false => (left < right + k)) */
|
/* (x is true => (left < right + k)) => (!x is false => (left < right + k)) */
|
||||||
exists(boolean isFalse | testIsTrue = isFalse.booleanNot() |
|
exists(AbstractValue dual | value = dual.getDualValue() |
|
||||||
compares_lt(test.(LogicalNotInstruction).getUnary(), left, right, k, isLt, isFalse)
|
compares_lt(test.(LogicalNotInstruction).getUnary(), left, right, k, isLt, dual)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Holds if `op < k` evaluates to `isLt` given that `test` evaluates to `value`. */
|
||||||
|
private predicate compares_lt(Instruction test, Operand op, int k, boolean isLt, AbstractValue value) {
|
||||||
|
simple_comparison_lt(test, op, k, isLt, value)
|
||||||
|
or
|
||||||
|
complex_lt(test, op, k, isLt, value)
|
||||||
|
or
|
||||||
|
/* (x is true => (op < k)) => (!x is false => (op < k)) */
|
||||||
|
exists(AbstractValue dual | value = dual.getDualValue() |
|
||||||
|
compares_lt(test.(LogicalNotInstruction).getUnary(), op, k, isLt, dual)
|
||||||
|
)
|
||||||
|
or
|
||||||
|
exists(int k1, int k2, ConstantInstruction const |
|
||||||
|
compares_lt(test, op, const.getAUse(), k2, isLt, value) and
|
||||||
|
int_value(const) = k1 and
|
||||||
|
k = k1 + k2
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** `(a < b + k) => (b > a - k) => (b >= a + (1-k))` */
|
/** `(a < b + k) => (b > a - k) => (b >= a + (1-k))` */
|
||||||
private predicate compares_ge(
|
private predicate compares_ge(
|
||||||
Instruction test, Operand left, Operand right, int k, boolean isGe, boolean testIsTrue
|
Instruction test, Operand left, Operand right, int k, boolean isGe, AbstractValue value
|
||||||
) {
|
) {
|
||||||
exists(int onemk | k = 1 - onemk | compares_lt(test, right, left, onemk, isGe, testIsTrue))
|
exists(int onemk | k = 1 - onemk | compares_lt(test, right, left, onemk, isGe, value))
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Rearrange various simple comparisons into `left < right + k` form. */
|
/** Rearrange various simple comparisons into `left < right + k` form. */
|
||||||
@@ -595,55 +880,99 @@ private predicate simple_comparison_lt(CompareInstruction cmp, Operand left, Ope
|
|||||||
k = 1
|
k = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
private predicate complex_lt(
|
/** Rearrange various simple comparisons into `op < k` form. */
|
||||||
CompareInstruction cmp, Operand left, Operand right, int k, boolean isLt, boolean testIsTrue
|
private predicate simple_comparison_lt(
|
||||||
|
Instruction test, Operand op, int k, boolean isLt, AbstractValue value
|
||||||
) {
|
) {
|
||||||
sub_lt(cmp, left, right, k, isLt, testIsTrue)
|
exists(SwitchInstruction switch, CaseEdge case |
|
||||||
|
test = switch.getExpression() and
|
||||||
|
op.getDef() = test and
|
||||||
|
case = value.(MatchValue).getCase() and
|
||||||
|
exists(switch.getSuccessor(case)) and
|
||||||
|
case.getMaxValue() > case.getMinValue()
|
||||||
|
|
|
||||||
|
// op <= k => op < k - 1
|
||||||
|
isLt = true and
|
||||||
|
case.getMaxValue().toInt() = k - 1
|
||||||
or
|
or
|
||||||
add_lt(cmp, left, right, k, isLt, testIsTrue)
|
isLt = false and
|
||||||
|
case.getMinValue().toInt() = k
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private predicate complex_lt(
|
||||||
|
CompareInstruction cmp, Operand left, Operand right, int k, boolean isLt, AbstractValue value
|
||||||
|
) {
|
||||||
|
sub_lt(cmp, left, right, k, isLt, value)
|
||||||
|
or
|
||||||
|
add_lt(cmp, left, right, k, isLt, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
private predicate complex_lt(
|
||||||
|
Instruction test, Operand left, int k, boolean isLt, AbstractValue value
|
||||||
|
) {
|
||||||
|
sub_lt(test, left, k, isLt, value)
|
||||||
|
or
|
||||||
|
add_lt(test, left, k, isLt, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
// left - x < right + c => left < right + (c+x)
|
// left - x < right + c => left < right + (c+x)
|
||||||
// left < (right - x) + c => left < right + (c-x)
|
// left < (right - x) + c => left < right + (c-x)
|
||||||
private predicate sub_lt(
|
private predicate sub_lt(
|
||||||
CompareInstruction cmp, Operand left, Operand right, int k, boolean isLt, boolean testIsTrue
|
CompareInstruction cmp, Operand left, Operand right, int k, boolean isLt, AbstractValue value
|
||||||
) {
|
) {
|
||||||
exists(SubInstruction lhs, int c, int x |
|
exists(SubInstruction lhs, int c, int x |
|
||||||
compares_lt(cmp, lhs.getAUse(), right, c, isLt, testIsTrue) and
|
compares_lt(cmp, lhs.getAUse(), right, c, isLt, value) and
|
||||||
left = lhs.getLeftOperand() and
|
left = lhs.getLeftOperand() and
|
||||||
x = int_value(lhs.getRight()) and
|
x = int_value(lhs.getRight()) and
|
||||||
k = c + x
|
k = c + x
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(SubInstruction rhs, int c, int x |
|
exists(SubInstruction rhs, int c, int x |
|
||||||
compares_lt(cmp, left, rhs.getAUse(), c, isLt, testIsTrue) and
|
compares_lt(cmp, left, rhs.getAUse(), c, isLt, value) and
|
||||||
right = rhs.getLeftOperand() and
|
right = rhs.getLeftOperand() and
|
||||||
x = int_value(rhs.getRight()) and
|
x = int_value(rhs.getRight()) and
|
||||||
k = c - x
|
k = c - x
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(PointerSubInstruction lhs, int c, int x |
|
exists(PointerSubInstruction lhs, int c, int x |
|
||||||
compares_lt(cmp, lhs.getAUse(), right, c, isLt, testIsTrue) and
|
compares_lt(cmp, lhs.getAUse(), right, c, isLt, value) and
|
||||||
left = lhs.getLeftOperand() and
|
left = lhs.getLeftOperand() and
|
||||||
x = int_value(lhs.getRight()) and
|
x = int_value(lhs.getRight()) and
|
||||||
k = c + x
|
k = c + x
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(PointerSubInstruction rhs, int c, int x |
|
exists(PointerSubInstruction rhs, int c, int x |
|
||||||
compares_lt(cmp, left, rhs.getAUse(), c, isLt, testIsTrue) and
|
compares_lt(cmp, left, rhs.getAUse(), c, isLt, value) and
|
||||||
right = rhs.getLeftOperand() and
|
right = rhs.getLeftOperand() and
|
||||||
x = int_value(rhs.getRight()) and
|
x = int_value(rhs.getRight()) and
|
||||||
k = c - x
|
k = c - x
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private predicate sub_lt(Instruction test, Operand left, int k, boolean isLt, AbstractValue value) {
|
||||||
|
exists(SubInstruction lhs, int c, int x |
|
||||||
|
compares_lt(test, lhs.getAUse(), c, isLt, value) and
|
||||||
|
left = lhs.getLeftOperand() and
|
||||||
|
x = int_value(lhs.getRight()) and
|
||||||
|
k = c + x
|
||||||
|
)
|
||||||
|
or
|
||||||
|
exists(PointerSubInstruction lhs, int c, int x |
|
||||||
|
compares_lt(test, lhs.getAUse(), c, isLt, value) and
|
||||||
|
left = lhs.getLeftOperand() and
|
||||||
|
x = int_value(lhs.getRight()) and
|
||||||
|
k = c + x
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// left + x < right + c => left < right + (c-x)
|
// left + x < right + c => left < right + (c-x)
|
||||||
// left < (right + x) + c => left < right + (c+x)
|
// left < (right + x) + c => left < right + (c+x)
|
||||||
private predicate add_lt(
|
private predicate add_lt(
|
||||||
CompareInstruction cmp, Operand left, Operand right, int k, boolean isLt, boolean testIsTrue
|
CompareInstruction cmp, Operand left, Operand right, int k, boolean isLt, AbstractValue value
|
||||||
) {
|
) {
|
||||||
exists(AddInstruction lhs, int c, int x |
|
exists(AddInstruction lhs, int c, int x |
|
||||||
compares_lt(cmp, lhs.getAUse(), right, c, isLt, testIsTrue) and
|
compares_lt(cmp, lhs.getAUse(), right, c, isLt, value) and
|
||||||
(
|
(
|
||||||
left = lhs.getLeftOperand() and x = int_value(lhs.getRight())
|
left = lhs.getLeftOperand() and x = int_value(lhs.getRight())
|
||||||
or
|
or
|
||||||
@@ -653,7 +982,7 @@ private predicate add_lt(
|
|||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(AddInstruction rhs, int c, int x |
|
exists(AddInstruction rhs, int c, int x |
|
||||||
compares_lt(cmp, left, rhs.getAUse(), c, isLt, testIsTrue) and
|
compares_lt(cmp, left, rhs.getAUse(), c, isLt, value) and
|
||||||
(
|
(
|
||||||
right = rhs.getLeftOperand() and x = int_value(rhs.getRight())
|
right = rhs.getLeftOperand() and x = int_value(rhs.getRight())
|
||||||
or
|
or
|
||||||
@@ -663,7 +992,7 @@ private predicate add_lt(
|
|||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(PointerAddInstruction lhs, int c, int x |
|
exists(PointerAddInstruction lhs, int c, int x |
|
||||||
compares_lt(cmp, lhs.getAUse(), right, c, isLt, testIsTrue) and
|
compares_lt(cmp, lhs.getAUse(), right, c, isLt, value) and
|
||||||
(
|
(
|
||||||
left = lhs.getLeftOperand() and x = int_value(lhs.getRight())
|
left = lhs.getLeftOperand() and x = int_value(lhs.getRight())
|
||||||
or
|
or
|
||||||
@@ -673,7 +1002,7 @@ private predicate add_lt(
|
|||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(PointerAddInstruction rhs, int c, int x |
|
exists(PointerAddInstruction rhs, int c, int x |
|
||||||
compares_lt(cmp, left, rhs.getAUse(), c, isLt, testIsTrue) and
|
compares_lt(cmp, left, rhs.getAUse(), c, isLt, value) and
|
||||||
(
|
(
|
||||||
right = rhs.getLeftOperand() and x = int_value(rhs.getRight())
|
right = rhs.getLeftOperand() and x = int_value(rhs.getRight())
|
||||||
or
|
or
|
||||||
@@ -683,47 +1012,86 @@ private predicate add_lt(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private predicate add_lt(Instruction test, Operand left, int k, boolean isLt, AbstractValue value) {
|
||||||
|
exists(AddInstruction lhs, int c, int x |
|
||||||
|
compares_lt(test, lhs.getAUse(), c, isLt, value) and
|
||||||
|
(
|
||||||
|
left = lhs.getLeftOperand() and x = int_value(lhs.getRight())
|
||||||
|
or
|
||||||
|
left = lhs.getRightOperand() and x = int_value(lhs.getLeft())
|
||||||
|
) and
|
||||||
|
k = c - x
|
||||||
|
)
|
||||||
|
or
|
||||||
|
exists(PointerAddInstruction lhs, int c, int x |
|
||||||
|
compares_lt(test, lhs.getAUse(), c, isLt, value) and
|
||||||
|
(
|
||||||
|
left = lhs.getLeftOperand() and x = int_value(lhs.getRight())
|
||||||
|
or
|
||||||
|
left = lhs.getRightOperand() and x = int_value(lhs.getLeft())
|
||||||
|
) and
|
||||||
|
k = c - x
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// left - x == right + c => left == right + (c+x)
|
// left - x == right + c => left == right + (c+x)
|
||||||
// left == (right - x) + c => left == right + (c-x)
|
// left == (right - x) + c => left == right + (c-x)
|
||||||
private predicate sub_eq(
|
private predicate sub_eq(
|
||||||
CompareInstruction cmp, Operand left, Operand right, int k, boolean areEqual, boolean testIsTrue
|
CompareInstruction cmp, Operand left, Operand right, int k, boolean areEqual, AbstractValue value
|
||||||
) {
|
) {
|
||||||
exists(SubInstruction lhs, int c, int x |
|
exists(SubInstruction lhs, int c, int x |
|
||||||
compares_eq(cmp, lhs.getAUse(), right, c, areEqual, testIsTrue) and
|
compares_eq(cmp, lhs.getAUse(), right, c, areEqual, value) and
|
||||||
left = lhs.getLeftOperand() and
|
left = lhs.getLeftOperand() and
|
||||||
x = int_value(lhs.getRight()) and
|
x = int_value(lhs.getRight()) and
|
||||||
k = c + x
|
k = c + x
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(SubInstruction rhs, int c, int x |
|
exists(SubInstruction rhs, int c, int x |
|
||||||
compares_eq(cmp, left, rhs.getAUse(), c, areEqual, testIsTrue) and
|
compares_eq(cmp, left, rhs.getAUse(), c, areEqual, value) and
|
||||||
right = rhs.getLeftOperand() and
|
right = rhs.getLeftOperand() and
|
||||||
x = int_value(rhs.getRight()) and
|
x = int_value(rhs.getRight()) and
|
||||||
k = c - x
|
k = c - x
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(PointerSubInstruction lhs, int c, int x |
|
exists(PointerSubInstruction lhs, int c, int x |
|
||||||
compares_eq(cmp, lhs.getAUse(), right, c, areEqual, testIsTrue) and
|
compares_eq(cmp, lhs.getAUse(), right, c, areEqual, value) and
|
||||||
left = lhs.getLeftOperand() and
|
left = lhs.getLeftOperand() and
|
||||||
x = int_value(lhs.getRight()) and
|
x = int_value(lhs.getRight()) and
|
||||||
k = c + x
|
k = c + x
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(PointerSubInstruction rhs, int c, int x |
|
exists(PointerSubInstruction rhs, int c, int x |
|
||||||
compares_eq(cmp, left, rhs.getAUse(), c, areEqual, testIsTrue) and
|
compares_eq(cmp, left, rhs.getAUse(), c, areEqual, value) and
|
||||||
right = rhs.getLeftOperand() and
|
right = rhs.getLeftOperand() and
|
||||||
x = int_value(rhs.getRight()) and
|
x = int_value(rhs.getRight()) and
|
||||||
k = c - x
|
k = c - x
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// op - x == c => op == (c+x)
|
||||||
|
private predicate sub_eq(Instruction test, Operand op, int k, boolean areEqual, AbstractValue value) {
|
||||||
|
exists(SubInstruction sub, int c, int x |
|
||||||
|
compares_eq(test, sub.getAUse(), c, areEqual, value) and
|
||||||
|
op = sub.getLeftOperand() and
|
||||||
|
x = int_value(sub.getRight()) and
|
||||||
|
k = c + x
|
||||||
|
)
|
||||||
|
or
|
||||||
|
exists(PointerSubInstruction sub, int c, int x |
|
||||||
|
compares_eq(test, sub.getAUse(), c, areEqual, value) and
|
||||||
|
op = sub.getLeftOperand() and
|
||||||
|
x = int_value(sub.getRight()) and
|
||||||
|
k = c + x
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// left + x == right + c => left == right + (c-x)
|
// left + x == right + c => left == right + (c-x)
|
||||||
// left == (right + x) + c => left == right + (c+x)
|
// left == (right + x) + c => left == right + (c+x)
|
||||||
private predicate add_eq(
|
private predicate add_eq(
|
||||||
CompareInstruction cmp, Operand left, Operand right, int k, boolean areEqual, boolean testIsTrue
|
CompareInstruction cmp, Operand left, Operand right, int k, boolean areEqual, AbstractValue value
|
||||||
) {
|
) {
|
||||||
exists(AddInstruction lhs, int c, int x |
|
exists(AddInstruction lhs, int c, int x |
|
||||||
compares_eq(cmp, lhs.getAUse(), right, c, areEqual, testIsTrue) and
|
compares_eq(cmp, lhs.getAUse(), right, c, areEqual, value) and
|
||||||
(
|
(
|
||||||
left = lhs.getLeftOperand() and x = int_value(lhs.getRight())
|
left = lhs.getLeftOperand() and x = int_value(lhs.getRight())
|
||||||
or
|
or
|
||||||
@@ -733,7 +1101,7 @@ private predicate add_eq(
|
|||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(AddInstruction rhs, int c, int x |
|
exists(AddInstruction rhs, int c, int x |
|
||||||
compares_eq(cmp, left, rhs.getAUse(), c, areEqual, testIsTrue) and
|
compares_eq(cmp, left, rhs.getAUse(), c, areEqual, value) and
|
||||||
(
|
(
|
||||||
right = rhs.getLeftOperand() and x = int_value(rhs.getRight())
|
right = rhs.getLeftOperand() and x = int_value(rhs.getRight())
|
||||||
or
|
or
|
||||||
@@ -743,7 +1111,7 @@ private predicate add_eq(
|
|||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(PointerAddInstruction lhs, int c, int x |
|
exists(PointerAddInstruction lhs, int c, int x |
|
||||||
compares_eq(cmp, lhs.getAUse(), right, c, areEqual, testIsTrue) and
|
compares_eq(cmp, lhs.getAUse(), right, c, areEqual, value) and
|
||||||
(
|
(
|
||||||
left = lhs.getLeftOperand() and x = int_value(lhs.getRight())
|
left = lhs.getLeftOperand() and x = int_value(lhs.getRight())
|
||||||
or
|
or
|
||||||
@@ -753,7 +1121,7 @@ private predicate add_eq(
|
|||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(PointerAddInstruction rhs, int c, int x |
|
exists(PointerAddInstruction rhs, int c, int x |
|
||||||
compares_eq(cmp, left, rhs.getAUse(), c, areEqual, testIsTrue) and
|
compares_eq(cmp, left, rhs.getAUse(), c, areEqual, value) and
|
||||||
(
|
(
|
||||||
right = rhs.getLeftOperand() and x = int_value(rhs.getRight())
|
right = rhs.getLeftOperand() and x = int_value(rhs.getRight())
|
||||||
or
|
or
|
||||||
@@ -763,5 +1131,30 @@ private predicate add_eq(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// left + x == right + c => left == right + (c-x)
|
||||||
|
private predicate add_eq(
|
||||||
|
Instruction test, Operand left, int k, boolean areEqual, AbstractValue value
|
||||||
|
) {
|
||||||
|
exists(AddInstruction lhs, int c, int x |
|
||||||
|
compares_eq(test, lhs.getAUse(), c, areEqual, value) and
|
||||||
|
(
|
||||||
|
left = lhs.getLeftOperand() and x = int_value(lhs.getRight())
|
||||||
|
or
|
||||||
|
left = lhs.getRightOperand() and x = int_value(lhs.getLeft())
|
||||||
|
) and
|
||||||
|
k = c - x
|
||||||
|
)
|
||||||
|
or
|
||||||
|
exists(PointerAddInstruction lhs, int c, int x |
|
||||||
|
compares_eq(test, lhs.getAUse(), c, areEqual, value) and
|
||||||
|
(
|
||||||
|
left = lhs.getLeftOperand() and x = int_value(lhs.getRight())
|
||||||
|
or
|
||||||
|
left = lhs.getRightOperand() and x = int_value(lhs.getLeft())
|
||||||
|
) and
|
||||||
|
k = c - x
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/** The int value of integer constant expression. */
|
/** The int value of integer constant expression. */
|
||||||
private int int_value(Instruction i) { result = i.(IntegerConstantInstruction).getValue().toInt() }
|
private int int_value(Instruction i) { result = i.(IntegerConstantInstruction).getValue().toInt() }
|
||||||
|
|||||||
@@ -28,6 +28,6 @@ import cpp
|
|||||||
deprecated module DataFlow {
|
deprecated module DataFlow {
|
||||||
private import semmle.code.cpp.dataflow.internal.DataFlowImplSpecific
|
private import semmle.code.cpp.dataflow.internal.DataFlowImplSpecific
|
||||||
private import codeql.dataflow.DataFlow
|
private import codeql.dataflow.DataFlow
|
||||||
import DataFlowMake<CppOldDataFlow>
|
import DataFlowMake<Location, CppOldDataFlow>
|
||||||
import semmle.code.cpp.dataflow.internal.DataFlowImpl1
|
import semmle.code.cpp.dataflow.internal.DataFlowImpl1
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,6 @@ deprecated module TaintTracking {
|
|||||||
private import semmle.code.cpp.dataflow.internal.DataFlowImplSpecific
|
private import semmle.code.cpp.dataflow.internal.DataFlowImplSpecific
|
||||||
private import semmle.code.cpp.dataflow.internal.TaintTrackingImplSpecific
|
private import semmle.code.cpp.dataflow.internal.TaintTrackingImplSpecific
|
||||||
private import codeql.dataflow.TaintTracking
|
private import codeql.dataflow.TaintTracking
|
||||||
import TaintFlowMake<CppOldDataFlow, CppOldTaintTracking>
|
import TaintFlowMake<Location, CppOldDataFlow, CppOldTaintTracking>
|
||||||
import semmle.code.cpp.dataflow.internal.tainttracking1.TaintTrackingImpl
|
import semmle.code.cpp.dataflow.internal.tainttracking1.TaintTrackingImpl
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
* DEPRECATED: Use `semmle.code.cpp.dataflow.new.DataFlow` instead.
|
* DEPRECATED: Use `semmle.code.cpp.dataflow.new.DataFlow` instead.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
private import semmle.code.cpp.Location
|
||||||
private import DataFlowImplSpecific
|
private import DataFlowImplSpecific
|
||||||
private import codeql.dataflow.internal.DataFlowImpl
|
private import codeql.dataflow.internal.DataFlowImpl
|
||||||
import MakeImpl<CppOldDataFlow>
|
import MakeImpl<Location, CppOldDataFlow>
|
||||||
|
|||||||
@@ -285,6 +285,8 @@ deprecated private module Config implements FullStateConfigSig {
|
|||||||
|
|
||||||
int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) }
|
int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) }
|
||||||
|
|
||||||
|
int accessPathLimit() { result = 5 }
|
||||||
|
|
||||||
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
||||||
|
|
||||||
predicate sourceGrouping(Node source, string sourceGroup) {
|
predicate sourceGrouping(Node source, string sourceGroup) {
|
||||||
|
|||||||
@@ -285,6 +285,8 @@ deprecated private module Config implements FullStateConfigSig {
|
|||||||
|
|
||||||
int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) }
|
int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) }
|
||||||
|
|
||||||
|
int accessPathLimit() { result = 5 }
|
||||||
|
|
||||||
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
||||||
|
|
||||||
predicate sourceGrouping(Node source, string sourceGroup) {
|
predicate sourceGrouping(Node source, string sourceGroup) {
|
||||||
|
|||||||
@@ -285,6 +285,8 @@ deprecated private module Config implements FullStateConfigSig {
|
|||||||
|
|
||||||
int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) }
|
int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) }
|
||||||
|
|
||||||
|
int accessPathLimit() { result = 5 }
|
||||||
|
|
||||||
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
||||||
|
|
||||||
predicate sourceGrouping(Node source, string sourceGroup) {
|
predicate sourceGrouping(Node source, string sourceGroup) {
|
||||||
|
|||||||
@@ -285,6 +285,8 @@ deprecated private module Config implements FullStateConfigSig {
|
|||||||
|
|
||||||
int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) }
|
int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) }
|
||||||
|
|
||||||
|
int accessPathLimit() { result = 5 }
|
||||||
|
|
||||||
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
||||||
|
|
||||||
predicate sourceGrouping(Node source, string sourceGroup) {
|
predicate sourceGrouping(Node source, string sourceGroup) {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
* DEPRECATED: Use `semmle.code.cpp.dataflow.new.DataFlow` instead.
|
* DEPRECATED: Use `semmle.code.cpp.dataflow.new.DataFlow` instead.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
private import semmle.code.cpp.Location
|
||||||
private import DataFlowImplSpecific
|
private import DataFlowImplSpecific
|
||||||
private import codeql.dataflow.internal.DataFlowImplCommon
|
private import codeql.dataflow.internal.DataFlowImplCommon
|
||||||
import MakeImplCommon<CppOldDataFlow>
|
import MakeImplCommon<Location, CppOldDataFlow>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ private import DataFlowImplSpecific
|
|||||||
private import TaintTrackingImplSpecific
|
private import TaintTrackingImplSpecific
|
||||||
private import codeql.dataflow.internal.DataFlowImplConsistency
|
private import codeql.dataflow.internal.DataFlowImplConsistency
|
||||||
|
|
||||||
private module Input implements InputSig<CppOldDataFlow> {
|
private module Input implements InputSig<Location, CppOldDataFlow> {
|
||||||
predicate argHasPostUpdateExclude(Private::ArgumentNode n) {
|
predicate argHasPostUpdateExclude(Private::ArgumentNode n) {
|
||||||
// Is the null pointer (or something that's not really a pointer)
|
// Is the null pointer (or something that's not really a pointer)
|
||||||
exists(n.asExpr().getValue())
|
exists(n.asExpr().getValue())
|
||||||
@@ -26,4 +26,4 @@ private module Input implements InputSig<CppOldDataFlow> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module Consistency = MakeConsistency<CppOldDataFlow, CppOldTaintTracking, Input>;
|
module Consistency = MakeConsistency<Location, CppOldDataFlow, CppOldTaintTracking, Input>;
|
||||||
|
|||||||
@@ -285,6 +285,8 @@ deprecated private module Config implements FullStateConfigSig {
|
|||||||
|
|
||||||
int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) }
|
int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) }
|
||||||
|
|
||||||
|
int accessPathLimit() { result = 5 }
|
||||||
|
|
||||||
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
||||||
|
|
||||||
predicate sourceGrouping(Node source, string sourceGroup) {
|
predicate sourceGrouping(Node source, string sourceGroup) {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
* Provides C++-specific definitions for use in the data flow library.
|
* Provides C++-specific definitions for use in the data flow library.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
private import semmle.code.cpp.Location
|
||||||
private import codeql.dataflow.DataFlow
|
private import codeql.dataflow.DataFlow
|
||||||
|
|
||||||
module Private {
|
module Private {
|
||||||
@@ -15,7 +16,7 @@ module Public {
|
|||||||
import DataFlowUtil
|
import DataFlowUtil
|
||||||
}
|
}
|
||||||
|
|
||||||
module CppOldDataFlow implements InputSig {
|
module CppOldDataFlow implements InputSig<Location> {
|
||||||
import Private
|
import Private
|
||||||
import Public
|
import Public
|
||||||
|
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ class Node extends TNode {
|
|||||||
* For more information, see
|
* For more information, see
|
||||||
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||||
*/
|
*/
|
||||||
predicate hasLocationInfo(
|
deprecated predicate hasLocationInfo(
|
||||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||||
) {
|
) {
|
||||||
this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||||
|
|||||||
@@ -4,9 +4,10 @@
|
|||||||
* Provides C++-specific definitions for use in the taint tracking library.
|
* Provides C++-specific definitions for use in the taint tracking library.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
private import semmle.code.cpp.Location
|
||||||
private import codeql.dataflow.TaintTracking
|
private import codeql.dataflow.TaintTracking
|
||||||
private import DataFlowImplSpecific
|
private import DataFlowImplSpecific
|
||||||
|
|
||||||
module CppOldTaintTracking implements InputSig<CppOldDataFlow> {
|
module CppOldTaintTracking implements InputSig<Location, CppOldDataFlow> {
|
||||||
import TaintTrackingUtil
|
import TaintTrackingUtil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,6 @@ import cpp
|
|||||||
module DataFlow {
|
module DataFlow {
|
||||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowImplSpecific
|
private import semmle.code.cpp.ir.dataflow.internal.DataFlowImplSpecific
|
||||||
private import codeql.dataflow.DataFlow
|
private import codeql.dataflow.DataFlow
|
||||||
import DataFlowMake<CppDataFlow>
|
import DataFlowMake<Location, CppDataFlow>
|
||||||
import semmle.code.cpp.ir.dataflow.internal.DataFlowImpl1
|
import semmle.code.cpp.ir.dataflow.internal.DataFlowImpl1
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ module TaintTracking {
|
|||||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowImplSpecific
|
private import semmle.code.cpp.ir.dataflow.internal.DataFlowImplSpecific
|
||||||
private import semmle.code.cpp.ir.dataflow.internal.TaintTrackingImplSpecific
|
private import semmle.code.cpp.ir.dataflow.internal.TaintTrackingImplSpecific
|
||||||
private import codeql.dataflow.TaintTracking
|
private import codeql.dataflow.TaintTracking
|
||||||
import TaintFlowMake<CppDataFlow, CppTaintTracking>
|
private import semmle.code.cpp.Location
|
||||||
|
import TaintFlowMake<Location, CppDataFlow, CppTaintTracking>
|
||||||
import semmle.code.cpp.ir.dataflow.internal.tainttracking1.TaintTrackingImpl
|
import semmle.code.cpp.ir.dataflow.internal.tainttracking1.TaintTrackingImpl
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1322,3 +1322,23 @@ class CoYieldExpr extends UnaryOperation, @co_yield {
|
|||||||
|
|
||||||
override int getPrecedence() { result = 2 }
|
override int getPrecedence() { result = 2 }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An expression representing the re-use of another expression.
|
||||||
|
*
|
||||||
|
* In some specific cases an expression may be referred to outside its
|
||||||
|
* original context. A re-use expression wraps any such reference. A
|
||||||
|
* re-use expression can for example occur as the qualifier of an implicit
|
||||||
|
* destructor called on a temporary object, where the original use of the
|
||||||
|
* expression is in the definition of the temporary.
|
||||||
|
*/
|
||||||
|
class ReuseExpr extends Expr, @reuseexpr {
|
||||||
|
override string getAPrimaryQlClass() { result = "ReuseExpr" }
|
||||||
|
|
||||||
|
override string toString() { result = "reuse of " + this.getReusedExpr().toString() }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the expression that is being re-used.
|
||||||
|
*/
|
||||||
|
Expr getReusedExpr() { expr_reuse(underlyingElement(this), unresolveElement(result)) }
|
||||||
|
}
|
||||||
|
|||||||
@@ -24,6 +24,6 @@ import cpp
|
|||||||
module DataFlow {
|
module DataFlow {
|
||||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowImplSpecific
|
private import semmle.code.cpp.ir.dataflow.internal.DataFlowImplSpecific
|
||||||
private import codeql.dataflow.DataFlow
|
private import codeql.dataflow.DataFlow
|
||||||
import DataFlowMake<CppDataFlow>
|
import DataFlowMake<Location, CppDataFlow>
|
||||||
import semmle.code.cpp.ir.dataflow.internal.DataFlowImpl1
|
import semmle.code.cpp.ir.dataflow.internal.DataFlowImpl1
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,6 @@ module TaintTracking {
|
|||||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowImplSpecific
|
private import semmle.code.cpp.ir.dataflow.internal.DataFlowImplSpecific
|
||||||
private import semmle.code.cpp.ir.dataflow.internal.TaintTrackingImplSpecific
|
private import semmle.code.cpp.ir.dataflow.internal.TaintTrackingImplSpecific
|
||||||
private import codeql.dataflow.TaintTracking
|
private import codeql.dataflow.TaintTracking
|
||||||
import TaintFlowMake<CppDataFlow, CppTaintTracking>
|
import TaintFlowMake<Location, CppDataFlow, CppTaintTracking>
|
||||||
import semmle.code.cpp.ir.dataflow.internal.tainttracking1.TaintTrackingImpl
|
import semmle.code.cpp.ir.dataflow.internal.tainttracking1.TaintTrackingImpl
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
private import semmle.code.cpp.Location
|
||||||
private import DataFlowImplSpecific
|
private import DataFlowImplSpecific
|
||||||
private import codeql.dataflow.internal.DataFlowImpl
|
private import codeql.dataflow.internal.DataFlowImpl
|
||||||
import MakeImpl<CppDataFlow>
|
import MakeImpl<Location, CppDataFlow>
|
||||||
|
|||||||
@@ -285,6 +285,8 @@ deprecated private module Config implements FullStateConfigSig {
|
|||||||
|
|
||||||
int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) }
|
int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) }
|
||||||
|
|
||||||
|
int accessPathLimit() { result = 5 }
|
||||||
|
|
||||||
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
||||||
|
|
||||||
predicate sourceGrouping(Node source, string sourceGroup) {
|
predicate sourceGrouping(Node source, string sourceGroup) {
|
||||||
|
|||||||
@@ -285,6 +285,8 @@ deprecated private module Config implements FullStateConfigSig {
|
|||||||
|
|
||||||
int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) }
|
int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) }
|
||||||
|
|
||||||
|
int accessPathLimit() { result = 5 }
|
||||||
|
|
||||||
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
||||||
|
|
||||||
predicate sourceGrouping(Node source, string sourceGroup) {
|
predicate sourceGrouping(Node source, string sourceGroup) {
|
||||||
|
|||||||
@@ -285,6 +285,8 @@ deprecated private module Config implements FullStateConfigSig {
|
|||||||
|
|
||||||
int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) }
|
int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) }
|
||||||
|
|
||||||
|
int accessPathLimit() { result = 5 }
|
||||||
|
|
||||||
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
||||||
|
|
||||||
predicate sourceGrouping(Node source, string sourceGroup) {
|
predicate sourceGrouping(Node source, string sourceGroup) {
|
||||||
|
|||||||
@@ -285,6 +285,8 @@ deprecated private module Config implements FullStateConfigSig {
|
|||||||
|
|
||||||
int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) }
|
int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) }
|
||||||
|
|
||||||
|
int accessPathLimit() { result = 5 }
|
||||||
|
|
||||||
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
||||||
|
|
||||||
predicate sourceGrouping(Node source, string sourceGroup) {
|
predicate sourceGrouping(Node source, string sourceGroup) {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
private import semmle.code.cpp.Location
|
||||||
private import DataFlowImplSpecific
|
private import DataFlowImplSpecific
|
||||||
private import codeql.dataflow.internal.DataFlowImplCommon
|
private import codeql.dataflow.internal.DataFlowImplCommon
|
||||||
import MakeImplCommon<CppDataFlow>
|
import MakeImplCommon<Location, CppDataFlow>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ private import DataFlowImplSpecific
|
|||||||
private import TaintTrackingImplSpecific
|
private import TaintTrackingImplSpecific
|
||||||
private import codeql.dataflow.internal.DataFlowImplConsistency
|
private import codeql.dataflow.internal.DataFlowImplConsistency
|
||||||
|
|
||||||
private module Input implements InputSig<CppDataFlow> {
|
private module Input implements InputSig<Location, CppDataFlow> {
|
||||||
predicate argHasPostUpdateExclude(Private::ArgumentNode n) {
|
predicate argHasPostUpdateExclude(Private::ArgumentNode n) {
|
||||||
// The rules for whether an IR argument gets a post-update node are too
|
// The rules for whether an IR argument gets a post-update node are too
|
||||||
// complex to model here.
|
// complex to model here.
|
||||||
@@ -16,4 +16,4 @@ private module Input implements InputSig<CppDataFlow> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module Consistency = MakeConsistency<CppDataFlow, CppTaintTracking, Input>;
|
module Consistency = MakeConsistency<Location, CppDataFlow, CppTaintTracking, Input>;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
private import codeql.dataflow.DataFlow
|
private import codeql.dataflow.DataFlow
|
||||||
|
private import semmle.code.cpp.Location
|
||||||
|
|
||||||
module Private {
|
module Private {
|
||||||
import DataFlowPrivate
|
import DataFlowPrivate
|
||||||
@@ -13,7 +14,7 @@ module Public {
|
|||||||
import DataFlowUtil
|
import DataFlowUtil
|
||||||
}
|
}
|
||||||
|
|
||||||
module CppDataFlow implements InputSig {
|
module CppDataFlow implements InputSig<Location> {
|
||||||
import Private
|
import Private
|
||||||
import Public
|
import Public
|
||||||
|
|
||||||
|
|||||||
@@ -448,7 +448,7 @@ class Node extends TIRDataFlowNode {
|
|||||||
* For more information, see
|
* For more information, see
|
||||||
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||||
*/
|
*/
|
||||||
predicate hasLocationInfo(
|
deprecated predicate hasLocationInfo(
|
||||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||||
) {
|
) {
|
||||||
this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||||
@@ -1331,6 +1331,7 @@ private import GetConvertedResultExpression
|
|||||||
|
|
||||||
/** Holds if `node` is an `OperandNode` that should map `node.asExpr()` to `e`. */
|
/** Holds if `node` is an `OperandNode` that should map `node.asExpr()` to `e`. */
|
||||||
predicate exprNodeShouldBeOperand(OperandNode node, Expr e, int n) {
|
predicate exprNodeShouldBeOperand(OperandNode node, Expr e, int n) {
|
||||||
|
not exprNodeShouldBeIndirectOperand(_, e, n) and
|
||||||
exists(Instruction def |
|
exists(Instruction def |
|
||||||
unique( | | getAUse(def)) = node.getOperand() and
|
unique( | | getAUse(def)) = node.getOperand() and
|
||||||
e = getConvertedResultExpression(def, n)
|
e = getConvertedResultExpression(def, n)
|
||||||
@@ -1347,6 +1348,22 @@ private predicate indirectExprNodeShouldBeIndirectOperand(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Holds if `node` should be an `IndirectOperand` that maps `node.asExpr()` to `e`. */
|
||||||
|
private predicate exprNodeShouldBeIndirectOperand(IndirectOperand node, Expr e, int n) {
|
||||||
|
exists(ArgumentOperand operand |
|
||||||
|
// When an argument (qualifier or positional) is a prvalue and the
|
||||||
|
// parameter (qualifier or positional) is a (const) reference, IR
|
||||||
|
// construction introduces a temporary `IRVariable`. The `VariableAddress`
|
||||||
|
// instruction has the argument as its `getConvertedResultExpression`
|
||||||
|
// result. However, the instruction actually represents the _address_ of
|
||||||
|
// the argument. So to fix this mismatch, we have the indirection of the
|
||||||
|
// `VariableAddressInstruction` map to the expression.
|
||||||
|
node.hasOperandAndIndirectionIndex(operand, 1) and
|
||||||
|
e = getConvertedResultExpression(operand.getDef(), n) and
|
||||||
|
operand.getDef().(VariableAddressInstruction).getIRVariable() instanceof IRTempVariable
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private predicate exprNodeShouldBeIndirectOutNode(IndirectArgumentOutNode node, Expr e, int n) {
|
private predicate exprNodeShouldBeIndirectOutNode(IndirectArgumentOutNode node, Expr e, int n) {
|
||||||
exists(CallInstruction call |
|
exists(CallInstruction call |
|
||||||
call.getStaticCallTarget() instanceof Constructor and
|
call.getStaticCallTarget() instanceof Constructor and
|
||||||
@@ -1359,6 +1376,7 @@ private predicate exprNodeShouldBeIndirectOutNode(IndirectArgumentOutNode node,
|
|||||||
predicate exprNodeShouldBeInstruction(Node node, Expr e, int n) {
|
predicate exprNodeShouldBeInstruction(Node node, Expr e, int n) {
|
||||||
not exprNodeShouldBeOperand(_, e, n) and
|
not exprNodeShouldBeOperand(_, e, n) and
|
||||||
not exprNodeShouldBeIndirectOutNode(_, e, n) and
|
not exprNodeShouldBeIndirectOutNode(_, e, n) and
|
||||||
|
not exprNodeShouldBeIndirectOperand(_, e, n) and
|
||||||
e = getConvertedResultExpression(node.asInstruction(), n)
|
e = getConvertedResultExpression(node.asInstruction(), n)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1391,7 +1409,8 @@ abstract private class ExprNodeBase extends Node {
|
|||||||
private predicate exprNodeShouldBe(Expr e, int n) {
|
private predicate exprNodeShouldBe(Expr e, int n) {
|
||||||
exprNodeShouldBeInstruction(_, e, n) or
|
exprNodeShouldBeInstruction(_, e, n) or
|
||||||
exprNodeShouldBeOperand(_, e, n) or
|
exprNodeShouldBeOperand(_, e, n) or
|
||||||
exprNodeShouldBeIndirectOutNode(_, e, n)
|
exprNodeShouldBeIndirectOutNode(_, e, n) or
|
||||||
|
exprNodeShouldBeIndirectOperand(_, e, n)
|
||||||
}
|
}
|
||||||
|
|
||||||
private class InstructionExprNode extends ExprNodeBase, InstructionNode {
|
private class InstructionExprNode extends ExprNodeBase, InstructionNode {
|
||||||
@@ -1533,6 +1552,12 @@ private class IndirectArgumentOutExprNode extends ExprNodeBase, IndirectArgument
|
|||||||
final override Expr getConvertedExpr(int n) { exprNodeShouldBeIndirectOutNode(this, result, n) }
|
final override Expr getConvertedExpr(int n) { exprNodeShouldBeIndirectOutNode(this, result, n) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class IndirectOperandExprNode extends ExprNodeBase instanceof IndirectOperand {
|
||||||
|
IndirectOperandExprNode() { exprNodeShouldBeIndirectOperand(this, _, _) }
|
||||||
|
|
||||||
|
final override Expr getConvertedExpr(int n) { exprNodeShouldBeIndirectOperand(this, result, n) }
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An expression, viewed as a node in a data flow graph.
|
* An expression, viewed as a node in a data flow graph.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -10,13 +10,12 @@ private import semmle.code.cpp.models.interfaces.FunctionInputsAndOutputs as FIO
|
|||||||
private import semmle.code.cpp.ir.internal.IRCppLanguage
|
private import semmle.code.cpp.ir.internal.IRCppLanguage
|
||||||
private import semmle.code.cpp.ir.dataflow.internal.ModelUtil
|
private import semmle.code.cpp.ir.dataflow.internal.ModelUtil
|
||||||
private import DataFlowPrivate
|
private import DataFlowPrivate
|
||||||
private import ssa0.SsaInternals as SsaInternals0
|
|
||||||
import SsaInternalsCommon
|
import SsaInternalsCommon
|
||||||
|
|
||||||
private module SourceVariables {
|
private module SourceVariables {
|
||||||
cached
|
cached
|
||||||
private newtype TSourceVariable =
|
private newtype TSourceVariable =
|
||||||
TMkSourceVariable(SsaInternals0::SourceVariable base, int ind) {
|
TMkSourceVariable(BaseSourceVariable base, int ind) {
|
||||||
ind = [0 .. countIndirectionsForCppType(base.getLanguageType()) + 1]
|
ind = [0 .. countIndirectionsForCppType(base.getLanguageType()) + 1]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,7 +29,7 @@ private module SourceVariables {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class SourceVariable extends TSourceVariable {
|
class SourceVariable extends TSourceVariable {
|
||||||
SsaInternals0::SourceVariable base;
|
BaseSourceVariable base;
|
||||||
int ind;
|
int ind;
|
||||||
|
|
||||||
SourceVariable() { this = TMkSourceVariable(base, ind) }
|
SourceVariable() { this = TMkSourceVariable(base, ind) }
|
||||||
@@ -42,7 +41,7 @@ private module SourceVariables {
|
|||||||
* Gets the base source variable (i.e., the variable without any
|
* Gets the base source variable (i.e., the variable without any
|
||||||
* indirections) of this source variable.
|
* indirections) of this source variable.
|
||||||
*/
|
*/
|
||||||
SsaInternals0::SourceVariable getBaseVariable() { result = base }
|
BaseSourceVariable getBaseVariable() { result = base }
|
||||||
|
|
||||||
/** Gets a textual representation of this element. */
|
/** Gets a textual representation of this element. */
|
||||||
string toString() { result = repeatStars(this.getIndirection()) + base.toString() }
|
string toString() { result = repeatStars(this.getIndirection()) + base.toString() }
|
||||||
@@ -104,17 +103,9 @@ predicate hasRawIndirectInstruction(Instruction instr, int indirectionIndex) {
|
|||||||
|
|
||||||
cached
|
cached
|
||||||
private newtype TDefOrUseImpl =
|
private newtype TDefOrUseImpl =
|
||||||
|
TDefAddressImpl(BaseIRVariable v) or
|
||||||
TDefImpl(BaseSourceVariableInstruction base, Operand address, int indirectionIndex) {
|
TDefImpl(BaseSourceVariableInstruction base, Operand address, int indirectionIndex) {
|
||||||
isDef(_, _, address, base, _, indirectionIndex) and
|
isDef(_, _, address, base, _, indirectionIndex)
|
||||||
(
|
|
||||||
// We only include the definition if the SSA pruning stage
|
|
||||||
// concluded that the definition is live after the write.
|
|
||||||
any(SsaInternals0::Def def).getAddressOperand() = address
|
|
||||||
or
|
|
||||||
// Since the pruning stage doesn't know about global variables we can't use the above check to
|
|
||||||
// rule out dead assignments to globals.
|
|
||||||
base.(VariableAddressInstruction).getAstVariable() instanceof GlobalLikeVariable
|
|
||||||
)
|
|
||||||
} or
|
} or
|
||||||
TUseImpl(BaseSourceVariableInstruction base, Operand operand, int indirectionIndex) {
|
TUseImpl(BaseSourceVariableInstruction base, Operand operand, int indirectionIndex) {
|
||||||
isUse(_, operand, base, _, indirectionIndex) and
|
isUse(_, operand, base, _, indirectionIndex) and
|
||||||
@@ -133,8 +124,7 @@ private newtype TDefOrUseImpl =
|
|||||||
TIteratorDef(
|
TIteratorDef(
|
||||||
Operand iteratorDerefAddress, BaseSourceVariableInstruction container, int indirectionIndex
|
Operand iteratorDerefAddress, BaseSourceVariableInstruction container, int indirectionIndex
|
||||||
) {
|
) {
|
||||||
isIteratorDef(container, iteratorDerefAddress, _, _, indirectionIndex) and
|
isIteratorDef(container, iteratorDerefAddress, _, _, indirectionIndex)
|
||||||
any(SsaInternals0::Def def | def.isIteratorDef()).getAddressOperand() = iteratorDerefAddress
|
|
||||||
} or
|
} or
|
||||||
TIteratorUse(
|
TIteratorUse(
|
||||||
Operand iteratorAddress, BaseSourceVariableInstruction container, int indirectionIndex
|
Operand iteratorAddress, BaseSourceVariableInstruction container, int indirectionIndex
|
||||||
@@ -267,24 +257,64 @@ private predicate sourceVariableHasBaseAndIndex(SourceVariable v, BaseSourceVari
|
|||||||
}
|
}
|
||||||
|
|
||||||
abstract class DefImpl extends DefOrUseImpl {
|
abstract class DefImpl extends DefOrUseImpl {
|
||||||
Operand address;
|
|
||||||
int ind;
|
int ind;
|
||||||
|
|
||||||
bindingset[ind]
|
bindingset[ind]
|
||||||
DefImpl() { any() }
|
DefImpl() { any() }
|
||||||
|
|
||||||
abstract int getIndirection();
|
|
||||||
|
|
||||||
abstract Node0Impl getValue();
|
|
||||||
|
|
||||||
abstract predicate isCertain();
|
|
||||||
|
|
||||||
Operand getAddressOperand() { result = address }
|
|
||||||
|
|
||||||
override int getIndirectionIndex() { result = ind }
|
override int getIndirectionIndex() { result = ind }
|
||||||
|
|
||||||
override string toString() { result = "Def of " + this.getSourceVariable() }
|
override string toString() { result = "Def of " + this.getSourceVariable() }
|
||||||
|
|
||||||
|
abstract int getIndirection();
|
||||||
|
|
||||||
|
abstract predicate isCertain();
|
||||||
|
|
||||||
|
abstract Node0Impl getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** An initial definition of an `IRVariable`'s address. */
|
||||||
|
private class DefAddressImpl extends DefImpl, TDefAddressImpl {
|
||||||
|
BaseIRVariable v;
|
||||||
|
|
||||||
|
DefAddressImpl() {
|
||||||
|
this = TDefAddressImpl(v) and
|
||||||
|
ind = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
final override int getIndirection() { result = 0 }
|
||||||
|
|
||||||
|
final override predicate isCertain() { any() }
|
||||||
|
|
||||||
|
final override Node0Impl getValue() { none() }
|
||||||
|
|
||||||
|
final override predicate hasIndexInBlock(IRBlock block, int index) {
|
||||||
|
block = v.getIRVariable().getEnclosingIRFunction().getEntryBlock() and
|
||||||
|
index = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
override Cpp::Location getLocation() { result = v.getIRVariable().getLocation() }
|
||||||
|
|
||||||
|
final override SourceVariable getSourceVariable() {
|
||||||
|
result.getBaseVariable() = v and
|
||||||
|
result.getIndirection() = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
final override BaseSourceVariableInstruction getBase() { none() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An SSA definition that has an associated `Operand` representing the address
|
||||||
|
* that is being written to.
|
||||||
|
*/
|
||||||
|
abstract private class OperandBasedDef extends DefImpl {
|
||||||
|
Operand address;
|
||||||
|
|
||||||
|
bindingset[ind]
|
||||||
|
OperandBasedDef() { any() }
|
||||||
|
|
||||||
|
Operand getAddressOperand() { result = address }
|
||||||
|
|
||||||
override Cpp::Location getLocation() { result = this.getAddressOperand().getUse().getLocation() }
|
override Cpp::Location getLocation() { result = this.getAddressOperand().getUse().getLocation() }
|
||||||
|
|
||||||
final override predicate hasIndexInBlock(IRBlock block, int index) {
|
final override predicate hasIndexInBlock(IRBlock block, int index) {
|
||||||
@@ -292,7 +322,7 @@ abstract class DefImpl extends DefOrUseImpl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class DirectDef extends DefImpl, TDefImpl {
|
private class DirectDef extends OperandBasedDef, TDefImpl {
|
||||||
BaseSourceVariableInstruction base;
|
BaseSourceVariableInstruction base;
|
||||||
|
|
||||||
DirectDef() { this = TDefImpl(base, address, ind) }
|
DirectDef() { this = TDefImpl(base, address, ind) }
|
||||||
@@ -306,7 +336,7 @@ private class DirectDef extends DefImpl, TDefImpl {
|
|||||||
override predicate isCertain() { isDef(true, _, address, base, _, ind) }
|
override predicate isCertain() { isDef(true, _, address, base, _, ind) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private class IteratorDef extends DefImpl, TIteratorDef {
|
private class IteratorDef extends OperandBasedDef, TIteratorDef {
|
||||||
BaseSourceVariableInstruction container;
|
BaseSourceVariableInstruction container;
|
||||||
|
|
||||||
IteratorDef() { this = TIteratorDef(address, container, ind) }
|
IteratorDef() { this = TIteratorDef(address, container, ind) }
|
||||||
@@ -984,17 +1014,6 @@ predicate fromPhiNode(SsaPhiNode nodeFrom, Node nodeTo) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Holds if there is a write at index `i` in basic block `bb` to variable `v` that's
|
|
||||||
* subsequently read (as determined by the SSA pruning stage).
|
|
||||||
*/
|
|
||||||
private predicate variableWriteCand(IRBlock bb, int i, SourceVariable v) {
|
|
||||||
exists(SsaInternals0::Def def, SsaInternals0::SourceVariable v0 |
|
|
||||||
def.asDefOrUse().hasIndexInBlock(bb, i, v0) and
|
|
||||||
v0 = v.getBaseVariable()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private predicate sourceVariableIsGlobal(
|
private predicate sourceVariableIsGlobal(
|
||||||
SourceVariable sv, GlobalLikeVariable global, IRFunction func, int indirectionIndex
|
SourceVariable sv, GlobalLikeVariable global, IRFunction func, int indirectionIndex
|
||||||
) {
|
) {
|
||||||
@@ -1018,9 +1037,6 @@ private module SsaInput implements SsaImplCommon::InputSig<Location> {
|
|||||||
predicate variableWrite(IRBlock bb, int i, SourceVariable v, boolean certain) {
|
predicate variableWrite(IRBlock bb, int i, SourceVariable v, boolean certain) {
|
||||||
DataFlowImplCommon::forceCachingInSameStage() and
|
DataFlowImplCommon::forceCachingInSameStage() and
|
||||||
(
|
(
|
||||||
variableWriteCand(bb, i, v) or
|
|
||||||
sourceVariableIsGlobal(v, _, _, _)
|
|
||||||
) and
|
|
||||||
exists(DefImpl def | def.hasIndexInBlock(bb, i, v) |
|
exists(DefImpl def | def.hasIndexInBlock(bb, i, v) |
|
||||||
if def.isCertain() then certain = true else certain = false
|
if def.isCertain() then certain = true else certain = false
|
||||||
)
|
)
|
||||||
@@ -1029,6 +1045,7 @@ private module SsaInput implements SsaImplCommon::InputSig<Location> {
|
|||||||
global.hasIndexInBlock(bb, i, v) and
|
global.hasIndexInBlock(bb, i, v) and
|
||||||
certain = true
|
certain = true
|
||||||
)
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1202,7 +1219,7 @@ class UseOrPhi extends SsaDefOrUse {
|
|||||||
class Def extends DefOrUse {
|
class Def extends DefOrUse {
|
||||||
override DefImpl defOrUse;
|
override DefImpl defOrUse;
|
||||||
|
|
||||||
Operand getAddressOperand() { result = defOrUse.getAddressOperand() }
|
Operand getAddressOperand() { result = defOrUse.(OperandBasedDef).getAddressOperand() }
|
||||||
|
|
||||||
Instruction getAddress() { result = this.getAddressOperand().getDef() }
|
Instruction getAddress() { result = this.getAddressOperand().getDef() }
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,8 @@
|
|||||||
|
|
||||||
private import codeql.dataflow.TaintTracking
|
private import codeql.dataflow.TaintTracking
|
||||||
private import DataFlowImplSpecific
|
private import DataFlowImplSpecific
|
||||||
|
private import semmle.code.cpp.Location
|
||||||
|
|
||||||
module CppTaintTracking implements InputSig<CppDataFlow> {
|
module CppTaintTracking implements InputSig<Location, CppDataFlow> {
|
||||||
import TaintTrackingUtil
|
import TaintTrackingUtil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,347 +0,0 @@
|
|||||||
/**
|
|
||||||
* This module defines an initial SSA pruning stage that doesn't take
|
|
||||||
* indirections into account.
|
|
||||||
*/
|
|
||||||
|
|
||||||
private import codeql.ssa.Ssa as SsaImplCommon
|
|
||||||
private import semmle.code.cpp.ir.IR
|
|
||||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowImplCommon as DataFlowImplCommon
|
|
||||||
private import semmle.code.cpp.models.interfaces.Allocation as Alloc
|
|
||||||
private import semmle.code.cpp.models.interfaces.DataFlow as DataFlow
|
|
||||||
private import semmle.code.cpp.ir.implementation.raw.internal.SideEffects as SideEffects
|
|
||||||
private import semmle.code.cpp.ir.internal.IRCppLanguage
|
|
||||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate
|
|
||||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
|
|
||||||
private import semmle.code.cpp.ir.dataflow.internal.SsaInternalsCommon
|
|
||||||
|
|
||||||
private module SourceVariables {
|
|
||||||
class SourceVariable extends BaseSourceVariable {
|
|
||||||
/**
|
|
||||||
* Gets the base source variable of this `SourceVariable`.
|
|
||||||
*/
|
|
||||||
BaseSourceVariable getBaseVariable() { result = this }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
import SourceVariables
|
|
||||||
|
|
||||||
private newtype TDefOrUseImpl =
|
|
||||||
TDefImpl(Operand address) { isDef(_, _, address, _, _, _) } or
|
|
||||||
TUseImpl(Operand operand) {
|
|
||||||
isUse(_, operand, _, _, _) and
|
|
||||||
not isDef(true, _, operand, _, _, _)
|
|
||||||
} or
|
|
||||||
TIteratorDef(BaseSourceVariableInstruction container, Operand iteratorAddress) {
|
|
||||||
isIteratorDef(container, iteratorAddress, _, _, _)
|
|
||||||
} or
|
|
||||||
TIteratorUse(BaseSourceVariableInstruction container, Operand iteratorAddress) {
|
|
||||||
isIteratorUse(container, iteratorAddress, _, _)
|
|
||||||
} or
|
|
||||||
TFinalParameterUse(Parameter p) {
|
|
||||||
any(Indirection indirection).getType() = p.getUnspecifiedType()
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract private class DefOrUseImpl extends TDefOrUseImpl {
|
|
||||||
/** Gets a textual representation of this element. */
|
|
||||||
abstract string toString();
|
|
||||||
|
|
||||||
/** Gets the block of this definition or use. */
|
|
||||||
final IRBlock getBlock() { this.hasIndexInBlock(result, _) }
|
|
||||||
|
|
||||||
/** Holds if this definition or use has index `index` in block `block`. */
|
|
||||||
abstract predicate hasIndexInBlock(IRBlock block, int index);
|
|
||||||
|
|
||||||
final predicate hasIndexInBlock(IRBlock block, int index, SourceVariable sv) {
|
|
||||||
this.hasIndexInBlock(block, index) and
|
|
||||||
sv = this.getSourceVariable()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Gets the location of this element. */
|
|
||||||
abstract Cpp::Location getLocation();
|
|
||||||
|
|
||||||
abstract BaseSourceVariableInstruction getBase();
|
|
||||||
|
|
||||||
final BaseSourceVariable getBaseSourceVariable() {
|
|
||||||
result = this.getBase().getBaseSourceVariable()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Gets the variable that is defined or used. */
|
|
||||||
final SourceVariable getSourceVariable() {
|
|
||||||
result.getBaseVariable() = this.getBaseSourceVariable()
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract predicate isCertain();
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class DefImpl extends DefOrUseImpl {
|
|
||||||
Operand address;
|
|
||||||
|
|
||||||
Operand getAddressOperand() { result = address }
|
|
||||||
|
|
||||||
abstract Node0Impl getValue();
|
|
||||||
|
|
||||||
override string toString() { result = address.toString() }
|
|
||||||
|
|
||||||
override Cpp::Location getLocation() { result = this.getAddressOperand().getLocation() }
|
|
||||||
|
|
||||||
final override predicate hasIndexInBlock(IRBlock block, int index) {
|
|
||||||
this.getAddressOperand().getUse() = block.getInstruction(index)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class DirectDef extends DefImpl, TDefImpl {
|
|
||||||
DirectDef() { this = TDefImpl(address) }
|
|
||||||
|
|
||||||
override BaseSourceVariableInstruction getBase() { isDef(_, _, address, result, _, _) }
|
|
||||||
|
|
||||||
override Node0Impl getValue() { isDef(_, result, address, _, _, _) }
|
|
||||||
|
|
||||||
override predicate isCertain() { isDef(true, _, address, _, _, _) }
|
|
||||||
}
|
|
||||||
|
|
||||||
private class IteratorDef extends DefImpl, TIteratorDef {
|
|
||||||
BaseSourceVariableInstruction container;
|
|
||||||
|
|
||||||
IteratorDef() { this = TIteratorDef(container, address) }
|
|
||||||
|
|
||||||
override BaseSourceVariableInstruction getBase() { result = container }
|
|
||||||
|
|
||||||
override Node0Impl getValue() { isIteratorDef(_, address, result, _, _) }
|
|
||||||
|
|
||||||
override predicate isCertain() { none() }
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class UseImpl extends DefOrUseImpl { }
|
|
||||||
|
|
||||||
abstract private class OperandBasedUse extends UseImpl {
|
|
||||||
Operand operand;
|
|
||||||
|
|
||||||
override string toString() { result = operand.toString() }
|
|
||||||
|
|
||||||
final override predicate hasIndexInBlock(IRBlock block, int index) {
|
|
||||||
// Ideally, this would just be implemented as:
|
|
||||||
// ```
|
|
||||||
// operand.getUse() = block.getInstruction(index)
|
|
||||||
// ```
|
|
||||||
// but because the IR generated for a snippet such as
|
|
||||||
// ```
|
|
||||||
// int x = *p++;
|
|
||||||
// ```
|
|
||||||
// looks like
|
|
||||||
// ```
|
|
||||||
// r1(glval<int>) = VariableAddress[x] :
|
|
||||||
// r2(glval<int *>) = VariableAddress[p] :
|
|
||||||
// r3(int *) = Load[p] : &:r2, m1
|
|
||||||
// r4(int) = Constant[1] :
|
|
||||||
// r5(int *) = PointerAdd[4] : r3, r4
|
|
||||||
// m3(int *) = Store[p] : &:r2, r5
|
|
||||||
// r6(int *) = CopyValue : r3
|
|
||||||
// r7(int) = Load[?] : &:r6, ~m2
|
|
||||||
// m2(int) = Store[x] : &:r1, r7
|
|
||||||
// ```
|
|
||||||
// we need to ensure that the `r3` operand of the `CopyValue` instruction isn't seen as a fresh use
|
|
||||||
// of `p` that happens after the increment. So if the base instruction of this use comes from a
|
|
||||||
// post-fix crement operation we set the index of the SSA use that wraps the `r3` operand at the
|
|
||||||
// `CopyValue` instruction to be the same index as the `r3` operand at the `PointerAdd` instruction.
|
|
||||||
// This ensures that the SSA library doesn't create flow from the `PointerAdd` to `r6`.
|
|
||||||
exists(BaseSourceVariableInstruction base | base = this.getBase() |
|
|
||||||
if base.getAst() = any(Cpp::PostfixCrementOperation c).getOperand()
|
|
||||||
then
|
|
||||||
exists(Operand op |
|
|
||||||
op =
|
|
||||||
min(Operand cand, int i |
|
|
||||||
isUse(_, cand, base, _, _) and
|
|
||||||
block.getInstruction(i) = cand.getUse()
|
|
||||||
|
|
|
||||||
cand order by i
|
|
||||||
) and
|
|
||||||
block.getInstruction(index) = op.getUse()
|
|
||||||
)
|
|
||||||
else operand.getUse() = block.getInstruction(index)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
final override Cpp::Location getLocation() { result = operand.getLocation() }
|
|
||||||
}
|
|
||||||
|
|
||||||
private class DirectUse extends OperandBasedUse, TUseImpl {
|
|
||||||
DirectUse() { this = TUseImpl(operand) }
|
|
||||||
|
|
||||||
override BaseSourceVariableInstruction getBase() { isUse(_, operand, result, _, _) }
|
|
||||||
|
|
||||||
override predicate isCertain() { isUse(true, operand, _, _, _) }
|
|
||||||
}
|
|
||||||
|
|
||||||
private class IteratorUse extends OperandBasedUse, TIteratorUse {
|
|
||||||
BaseSourceVariableInstruction container;
|
|
||||||
|
|
||||||
IteratorUse() { this = TIteratorUse(container, operand) }
|
|
||||||
|
|
||||||
override BaseSourceVariableInstruction getBase() { result = container }
|
|
||||||
|
|
||||||
override predicate isCertain() { none() }
|
|
||||||
}
|
|
||||||
|
|
||||||
private class FinalParameterUse extends UseImpl, TFinalParameterUse {
|
|
||||||
Parameter p;
|
|
||||||
|
|
||||||
FinalParameterUse() { this = TFinalParameterUse(p) }
|
|
||||||
|
|
||||||
override string toString() { result = p.toString() }
|
|
||||||
|
|
||||||
final override predicate hasIndexInBlock(IRBlock block, int index) {
|
|
||||||
// Ideally, this should always be a `ReturnInstruction`, but if
|
|
||||||
// someone forgets to write a `return` statement in a function
|
|
||||||
// with a non-void return type we generate an `UnreachedInstruction`.
|
|
||||||
// In this case we still want to generate flow out of such functions
|
|
||||||
// if they write to a parameter. So we pick the index of the
|
|
||||||
// `UnreachedInstruction` as the index of this use.
|
|
||||||
// Note that a function may have both a `ReturnInstruction` and an
|
|
||||||
// `UnreachedInstruction`. If that's the case this predicate will
|
|
||||||
// return multiple results. I don't think this is detrimental to
|
|
||||||
// performance, however.
|
|
||||||
exists(Instruction return |
|
|
||||||
return instanceof ReturnInstruction or
|
|
||||||
return instanceof UnreachedInstruction
|
|
||||||
|
|
|
||||||
block.getInstruction(index) = return and
|
|
||||||
return.getEnclosingFunction() = p.getFunction()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
final override Cpp::Location getLocation() {
|
|
||||||
// Parameters can have multiple locations. When there's a unique location we use
|
|
||||||
// that one, but if multiple locations exist we default to an unknown location.
|
|
||||||
result = unique( | | p.getLocation())
|
|
||||||
or
|
|
||||||
not exists(unique( | | p.getLocation())) and
|
|
||||||
result instanceof UnknownDefaultLocation
|
|
||||||
}
|
|
||||||
|
|
||||||
override BaseSourceVariableInstruction getBase() {
|
|
||||||
exists(InitializeParameterInstruction init |
|
|
||||||
init.getParameter() = p and
|
|
||||||
// This is always a `VariableAddressInstruction`
|
|
||||||
result = init.getAnOperand().getDef()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override predicate isCertain() { any() }
|
|
||||||
}
|
|
||||||
|
|
||||||
private module SsaInput implements SsaImplCommon::InputSig<Location> {
|
|
||||||
import InputSigCommon
|
|
||||||
import SourceVariables
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Holds if the `i`'th write in block `bb` writes to the variable `v`.
|
|
||||||
* `certain` is `true` if the write is guaranteed to overwrite the entire variable.
|
|
||||||
*/
|
|
||||||
predicate variableWrite(IRBlock bb, int i, SourceVariable v, boolean certain) {
|
|
||||||
DataFlowImplCommon::forceCachingInSameStage() and
|
|
||||||
exists(DefImpl def | def.hasIndexInBlock(bb, i, v) |
|
|
||||||
if def.isCertain() then certain = true else certain = false
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Holds if the `i`'th read in block `bb` reads to the variable `v`.
|
|
||||||
* `certain` is `true` if the read is guaranteed.
|
|
||||||
*/
|
|
||||||
predicate variableRead(IRBlock bb, int i, SourceVariable v, boolean certain) {
|
|
||||||
exists(UseImpl use | use.hasIndexInBlock(bb, i, v) |
|
|
||||||
if use.isCertain() then certain = true else certain = false
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private newtype TSsaDefOrUse =
|
|
||||||
TDefOrUse(DefOrUseImpl defOrUse) {
|
|
||||||
defOrUse instanceof UseImpl
|
|
||||||
or
|
|
||||||
// If `defOrUse` is a definition we only include it if the
|
|
||||||
// SSA library concludes that it's live after the write.
|
|
||||||
exists(DefinitionExt def, SourceVariable sv, IRBlock bb, int i |
|
|
||||||
def.definesAt(sv, bb, i, _) and
|
|
||||||
defOrUse.(DefImpl).hasIndexInBlock(bb, i, sv)
|
|
||||||
)
|
|
||||||
} or
|
|
||||||
TPhi(PhiNode phi)
|
|
||||||
|
|
||||||
abstract private class SsaDefOrUse extends TSsaDefOrUse {
|
|
||||||
string toString() { result = "SsaDefOrUse" }
|
|
||||||
|
|
||||||
DefOrUseImpl asDefOrUse() { none() }
|
|
||||||
|
|
||||||
PhiNode asPhi() { none() }
|
|
||||||
|
|
||||||
abstract Location getLocation();
|
|
||||||
}
|
|
||||||
|
|
||||||
class DefOrUse extends TDefOrUse, SsaDefOrUse {
|
|
||||||
DefOrUseImpl defOrUse;
|
|
||||||
|
|
||||||
DefOrUse() { this = TDefOrUse(defOrUse) }
|
|
||||||
|
|
||||||
final override DefOrUseImpl asDefOrUse() { result = defOrUse }
|
|
||||||
|
|
||||||
final override Location getLocation() { result = defOrUse.getLocation() }
|
|
||||||
|
|
||||||
final SourceVariable getSourceVariable() { result = defOrUse.getSourceVariable() }
|
|
||||||
}
|
|
||||||
|
|
||||||
class Phi extends TPhi, SsaDefOrUse {
|
|
||||||
PhiNode phi;
|
|
||||||
|
|
||||||
Phi() { this = TPhi(phi) }
|
|
||||||
|
|
||||||
final override PhiNode asPhi() { result = phi }
|
|
||||||
|
|
||||||
final override Location getLocation() { result = phi.getBasicBlock().getLocation() }
|
|
||||||
}
|
|
||||||
|
|
||||||
class UseOrPhi extends SsaDefOrUse {
|
|
||||||
UseOrPhi() {
|
|
||||||
this.asDefOrUse() instanceof UseImpl
|
|
||||||
or
|
|
||||||
this instanceof Phi
|
|
||||||
}
|
|
||||||
|
|
||||||
final override Location getLocation() {
|
|
||||||
result = this.asDefOrUse().getLocation() or result = this.(Phi).getLocation()
|
|
||||||
}
|
|
||||||
|
|
||||||
override string toString() {
|
|
||||||
result = this.asDefOrUse().toString()
|
|
||||||
or
|
|
||||||
this instanceof Phi and
|
|
||||||
result = "Phi"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Def extends DefOrUse {
|
|
||||||
override DefImpl defOrUse;
|
|
||||||
|
|
||||||
Operand getAddressOperand() { result = defOrUse.getAddressOperand() }
|
|
||||||
|
|
||||||
Instruction getAddress() { result = this.getAddressOperand().getDef() }
|
|
||||||
|
|
||||||
Node0Impl getValue() { result = defOrUse.getValue() }
|
|
||||||
|
|
||||||
override string toString() { result = this.asDefOrUse().toString() }
|
|
||||||
|
|
||||||
BaseSourceVariableInstruction getBase() { result = defOrUse.getBase() }
|
|
||||||
|
|
||||||
predicate isIteratorDef() { defOrUse instanceof IteratorDef }
|
|
||||||
}
|
|
||||||
|
|
||||||
private module SsaImpl = SsaImplCommon::Make<Location, SsaInput>;
|
|
||||||
|
|
||||||
class PhiNode extends SsaImpl::DefinitionExt {
|
|
||||||
PhiNode() {
|
|
||||||
this instanceof SsaImpl::PhiNode or
|
|
||||||
this instanceof SsaImpl::PhiReadNode
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class DefinitionExt = SsaImpl::DefinitionExt;
|
|
||||||
@@ -90,6 +90,15 @@ class CaseEdge extends EdgeKind, TCaseEdge {
|
|||||||
* Gets the largest value of the switch expression for which control will flow along this edge.
|
* Gets the largest value of the switch expression for which control will flow along this edge.
|
||||||
*/
|
*/
|
||||||
final string getMaxValue() { result = maxValue }
|
final string getMaxValue() { result = maxValue }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the unique value of the switch expression for which control will
|
||||||
|
* flow along this edge, if any.
|
||||||
|
*/
|
||||||
|
final string getValue() {
|
||||||
|
minValue = maxValue and
|
||||||
|
result = minValue
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -38,6 +38,12 @@ private int getBinaryInstructionValue(BinaryInstruction instr) {
|
|||||||
or
|
or
|
||||||
instr instanceof DivInstruction and result = div(left, right)
|
instr instanceof DivInstruction and result = div(left, right)
|
||||||
or
|
or
|
||||||
|
instr instanceof BitOrInstruction and result = bitOr(left, right)
|
||||||
|
or
|
||||||
|
instr instanceof BitAndInstruction and result = bitAnd(left, right)
|
||||||
|
or
|
||||||
|
instr instanceof BitXorInstruction and result = bitXor(left, right)
|
||||||
|
or
|
||||||
instr instanceof CompareEQInstruction and result = compareEQ(left, right)
|
instr instanceof CompareEQInstruction and result = compareEQ(left, right)
|
||||||
or
|
or
|
||||||
instr instanceof CompareNEInstruction and result = compareNE(left, right)
|
instr instanceof CompareNEInstruction and result = compareNE(left, right)
|
||||||
|
|||||||
@@ -38,6 +38,12 @@ private int getBinaryInstructionValue(BinaryInstruction instr) {
|
|||||||
or
|
or
|
||||||
instr instanceof DivInstruction and result = div(left, right)
|
instr instanceof DivInstruction and result = div(left, right)
|
||||||
or
|
or
|
||||||
|
instr instanceof BitOrInstruction and result = bitOr(left, right)
|
||||||
|
or
|
||||||
|
instr instanceof BitAndInstruction and result = bitAnd(left, right)
|
||||||
|
or
|
||||||
|
instr instanceof BitXorInstruction and result = bitXor(left, right)
|
||||||
|
or
|
||||||
instr instanceof CompareEQInstruction and result = compareEQ(left, right)
|
instr instanceof CompareEQInstruction and result = compareEQ(left, right)
|
||||||
or
|
or
|
||||||
instr instanceof CompareNEInstruction and result = compareNE(left, right)
|
instr instanceof CompareNEInstruction and result = compareNE(left, right)
|
||||||
|
|||||||
@@ -40,12 +40,43 @@ IRTempVariable getIRTempVariable(Locatable ast, TempVariableTag tag) {
|
|||||||
result.getTag() = tag
|
result.getTag() = tag
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Gets an operand of `op`. */
|
||||||
|
private Expr getAnOperand(Operation op) { result = op.getAnOperand() }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the number of nested operands of `op`. For example,
|
||||||
|
* `getNumberOfNestedBinaryOperands((1 + 2) + 3))` is `3`.
|
||||||
|
*/
|
||||||
|
private int getNumberOfNestedBinaryOperands(Operation op) { result = count(getAnOperand*(op)) }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if `op` should not be translated to a `ConstantInstruction` as part of
|
||||||
|
* IR generation, even if the value of `op` is constant.
|
||||||
|
*/
|
||||||
|
private predicate ignoreConstantValue(Operation op) {
|
||||||
|
op instanceof BitwiseAndExpr
|
||||||
|
or
|
||||||
|
op instanceof BitwiseOrExpr
|
||||||
|
or
|
||||||
|
op instanceof BitwiseXorExpr
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if `expr` is a constant of a type that can be replaced directly with
|
* Holds if `expr` is a constant of a type that can be replaced directly with
|
||||||
* its value in the IR. This does not include address constants as we have no
|
* its value in the IR. This does not include address constants as we have no
|
||||||
* means to express those as QL values.
|
* means to express those as QL values.
|
||||||
*/
|
*/
|
||||||
predicate isIRConstant(Expr expr) { exists(expr.getValue()) }
|
predicate isIRConstant(Expr expr) {
|
||||||
|
exists(expr.getValue()) and
|
||||||
|
// We avoid constant folding certain operations since it's often useful to
|
||||||
|
// mark one of those as a source in dataflow, and if the operation is
|
||||||
|
// constant folded it's not possible to mark its operands as a source (or
|
||||||
|
// sink).
|
||||||
|
// But to avoid creating an outrageous amount of IR from very large
|
||||||
|
// constant expressions we fall back to constant folding if the operation
|
||||||
|
// has more than 50 operands (i.e., 1 + 2 + 3 + 4 + ... + 50)
|
||||||
|
if ignoreConstantValue(expr) then getNumberOfNestedBinaryOperands(expr) > 50 else any()
|
||||||
|
}
|
||||||
|
|
||||||
// Pulled out for performance. See
|
// Pulled out for performance. See
|
||||||
// https://github.com/github/codeql-coreql-team/issues/1044.
|
// https://github.com/github/codeql-coreql-team/issues/1044.
|
||||||
@@ -96,6 +127,14 @@ private predicate ignoreExprAndDescendants(Expr expr) {
|
|||||||
exists(BuiltInVarArgsStart vaStartExpr |
|
exists(BuiltInVarArgsStart vaStartExpr |
|
||||||
vaStartExpr.getLastNamedParameter().getFullyConverted() = expr
|
vaStartExpr.getLastNamedParameter().getFullyConverted() = expr
|
||||||
)
|
)
|
||||||
|
or
|
||||||
|
// suppress destructors of temporary variables until proper support is added for them.
|
||||||
|
exists(Expr parent | parent.getAnImplicitDestructorCall() = expr)
|
||||||
|
or
|
||||||
|
exists(Stmt parent |
|
||||||
|
parent.getAnImplicitDestructorCall() = expr and
|
||||||
|
expr.(DestructorCall).getQualifier() instanceof ReuseExpr
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -744,9 +783,13 @@ newtype TTranslatedElement =
|
|||||||
// The declaration/initialization part of a `ConditionDeclExpr`
|
// The declaration/initialization part of a `ConditionDeclExpr`
|
||||||
TTranslatedConditionDecl(ConditionDeclExpr expr) { not ignoreExpr(expr) } or
|
TTranslatedConditionDecl(ConditionDeclExpr expr) { not ignoreExpr(expr) } or
|
||||||
// The side effects of a `Call`
|
// The side effects of a `Call`
|
||||||
TTranslatedCallSideEffects(CallOrAllocationExpr expr) { not ignoreSideEffects(expr) } or
|
TTranslatedCallSideEffects(CallOrAllocationExpr expr) {
|
||||||
|
not ignoreExpr(expr) and
|
||||||
|
not ignoreSideEffects(expr)
|
||||||
|
} or
|
||||||
// The non-argument-specific side effect of a `Call`
|
// The non-argument-specific side effect of a `Call`
|
||||||
TTranslatedCallSideEffect(Expr expr, SideEffectOpcode opcode) {
|
TTranslatedCallSideEffect(Expr expr, SideEffectOpcode opcode) {
|
||||||
|
not ignoreExpr(expr) and
|
||||||
not ignoreSideEffects(expr) and
|
not ignoreSideEffects(expr) and
|
||||||
opcode = getCallSideEffectOpcode(expr)
|
opcode = getCallSideEffectOpcode(expr)
|
||||||
} or
|
} or
|
||||||
@@ -764,6 +807,7 @@ newtype TTranslatedElement =
|
|||||||
// Constructor calls lack a qualifier (`this`) expression, so we need to handle the side effects
|
// Constructor calls lack a qualifier (`this`) expression, so we need to handle the side effects
|
||||||
// on `*this` without an `Expr`.
|
// on `*this` without an `Expr`.
|
||||||
TTranslatedStructorQualifierSideEffect(Call call, SideEffectOpcode opcode) {
|
TTranslatedStructorQualifierSideEffect(Call call, SideEffectOpcode opcode) {
|
||||||
|
not ignoreExpr(call) and
|
||||||
not ignoreSideEffects(call) and
|
not ignoreSideEffects(call) and
|
||||||
call instanceof ConstructorCall and
|
call instanceof ConstructorCall and
|
||||||
opcode = getASideEffectOpcode(call, -1)
|
opcode = getASideEffectOpcode(call, -1)
|
||||||
|
|||||||
@@ -97,19 +97,6 @@ abstract class TranslatedExpr extends TranslatedElement {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
final override predicate hasAnImplicitDestructorCall() {
|
|
||||||
exists(expr.getAnImplicitDestructorCall())
|
|
||||||
}
|
|
||||||
|
|
||||||
final override int getFirstDestructorCallIndex() {
|
|
||||||
not this.handlesDestructorsExplicitly() and
|
|
||||||
(
|
|
||||||
result = max(int childId | exists(this.getChildInternal(childId))) + 1
|
|
||||||
or
|
|
||||||
not exists(this.getChildInternal(_)) and result = 0
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
final override Locatable getAst() { result = expr }
|
final override Locatable getAst() { result = expr }
|
||||||
|
|
||||||
final override Declaration getFunction() { result = getEnclosingDeclaration(expr) }
|
final override Declaration getFunction() { result = getEnclosingDeclaration(expr) }
|
||||||
|
|||||||
@@ -248,9 +248,19 @@ abstract class TranslatedStmt extends TranslatedElement, TTranslatedStmt {
|
|||||||
final override TranslatedElement getChild(int id) {
|
final override TranslatedElement getChild(int id) {
|
||||||
result = this.getChildInternal(id)
|
result = this.getChildInternal(id)
|
||||||
or
|
or
|
||||||
exists(int destructorIndex |
|
exists(int destructorIndex, int tempDestructorCount |
|
||||||
result.(TranslatedExpr).getExpr() = stmt.getImplicitDestructorCall(destructorIndex) and
|
result.(TranslatedExpr).getExpr() = stmt.getImplicitDestructorCall(destructorIndex) and
|
||||||
id = this.getFirstDestructorCallIndex() + destructorIndex
|
id = this.getFirstDestructorCallIndex() + destructorIndex - tempDestructorCount and
|
||||||
|
// suppress destructors of temporary variables until proper support is added for them.
|
||||||
|
tempDestructorCount =
|
||||||
|
count(DestructorCall call, int tempIndex |
|
||||||
|
stmt.getImplicitDestructorCall(tempIndex) = call and
|
||||||
|
tempIndex < destructorIndex and
|
||||||
|
call.getQualifier() instanceof ReuseExpr
|
||||||
|
|
|
||||||
|
call
|
||||||
|
) and
|
||||||
|
not stmt.getImplicitDestructorCall(destructorIndex).getQualifier() instanceof ReuseExpr
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -261,7 +271,11 @@ abstract class TranslatedStmt extends TranslatedElement, TTranslatedStmt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final override predicate hasAnImplicitDestructorCall() {
|
final override predicate hasAnImplicitDestructorCall() {
|
||||||
exists(stmt.getAnImplicitDestructorCall())
|
exists(stmt.getAnImplicitDestructorCall()) and
|
||||||
|
// suppress destructors of temporary variables until proper support is added for them.
|
||||||
|
exists(Expr expr | stmt.getAnImplicitDestructorCall().getQualifier() = expr |
|
||||||
|
not expr instanceof ReuseExpr
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
final override string toString() { result = stmt.toString() }
|
final override string toString() { result = stmt.toString() }
|
||||||
|
|||||||
@@ -38,6 +38,12 @@ private int getBinaryInstructionValue(BinaryInstruction instr) {
|
|||||||
or
|
or
|
||||||
instr instanceof DivInstruction and result = div(left, right)
|
instr instanceof DivInstruction and result = div(left, right)
|
||||||
or
|
or
|
||||||
|
instr instanceof BitOrInstruction and result = bitOr(left, right)
|
||||||
|
or
|
||||||
|
instr instanceof BitAndInstruction and result = bitAnd(left, right)
|
||||||
|
or
|
||||||
|
instr instanceof BitXorInstruction and result = bitXor(left, right)
|
||||||
|
or
|
||||||
instr instanceof CompareEQInstruction and result = compareEQ(left, right)
|
instr instanceof CompareEQInstruction and result = compareEQ(left, right)
|
||||||
or
|
or
|
||||||
instr instanceof CompareNEInstruction and result = compareNE(left, right)
|
instr instanceof CompareNEInstruction and result = compareNE(left, right)
|
||||||
|
|||||||
@@ -89,6 +89,18 @@ int compareLE(int a, int b) { if a <= b then result = 1 else result = 0 }
|
|||||||
bindingset[a, b]
|
bindingset[a, b]
|
||||||
int compareGE(int a, int b) { if a >= b then result = 1 else result = 0 }
|
int compareGE(int a, int b) { if a >= b then result = 1 else result = 0 }
|
||||||
|
|
||||||
|
/** Returns `a | b`. */
|
||||||
|
bindingset[a, b]
|
||||||
|
int bitOr(int a, int b) { result = a.bitOr(b) }
|
||||||
|
|
||||||
|
/** Returns `a & b`. */
|
||||||
|
bindingset[a, b]
|
||||||
|
int bitAnd(int a, int b) { result = a.bitAnd(b) }
|
||||||
|
|
||||||
|
/** Returns `a ^ b`. */
|
||||||
|
bindingset[a, b]
|
||||||
|
int bitXor(int a, int b) { result = a.bitXor(b) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns `-a`. If the negation would overflow, there is no result.
|
* Returns `-a`. If the negation would overflow, there is no result.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -36,7 +36,9 @@ private class MallocAllocationFunction extends AllocationFunction {
|
|||||||
"CRYPTO_malloc", // CRYPTO_malloc(size_t num, const char *file, int line)
|
"CRYPTO_malloc", // CRYPTO_malloc(size_t num, const char *file, int line)
|
||||||
"CRYPTO_zalloc", // CRYPTO_zalloc(size_t num, const char *file, int line)
|
"CRYPTO_zalloc", // CRYPTO_zalloc(size_t num, const char *file, int line)
|
||||||
"CRYPTO_secure_malloc", // CRYPTO_secure_malloc(size_t num, const char *file, int line)
|
"CRYPTO_secure_malloc", // CRYPTO_secure_malloc(size_t num, const char *file, int line)
|
||||||
"CRYPTO_secure_zalloc" // CRYPTO_secure_zalloc(size_t num, const char *file, int line)
|
"CRYPTO_secure_zalloc", // CRYPTO_secure_zalloc(size_t num, const char *file, int line)
|
||||||
|
"g_malloc", // g_malloc (n_bytes);
|
||||||
|
"g_try_malloc" // g_try_malloc(n_bytes);
|
||||||
]) and
|
]) and
|
||||||
sizeArg = 0
|
sizeArg = 0
|
||||||
or
|
or
|
||||||
@@ -139,7 +141,9 @@ private class ReallocAllocationFunction extends AllocationFunction, TaintFunctio
|
|||||||
// --- Windows COM allocation
|
// --- Windows COM allocation
|
||||||
"CoTaskMemRealloc", // CoTaskMemRealloc(ptr, size)
|
"CoTaskMemRealloc", // CoTaskMemRealloc(ptr, size)
|
||||||
// --- OpenSSL memory allocation
|
// --- OpenSSL memory allocation
|
||||||
"CRYPTO_realloc" // CRYPTO_realloc(void *addr, size_t num, const char *file, int line)
|
"CRYPTO_realloc", // CRYPTO_realloc(void *addr, size_t num, const char *file, int line)
|
||||||
|
"g_realloc", // g_realloc(mem, n_bytes);
|
||||||
|
"g_try_realloc" // g_try_realloc(mem, n_bytes);
|
||||||
]) and
|
]) and
|
||||||
sizeArg = 1 and
|
sizeArg = 1 and
|
||||||
reallocArg = 0
|
reallocArg = 0
|
||||||
|
|||||||
@@ -20,8 +20,10 @@ private class StandardDeallocationFunction extends DeallocationFunction {
|
|||||||
freedArg = 0
|
freedArg = 0
|
||||||
or
|
or
|
||||||
this.hasGlobalName([
|
this.hasGlobalName([
|
||||||
// --- OpenSSL memory allocation
|
// --- OpenSSL memory deallocation
|
||||||
"CRYPTO_free", "CRYPTO_secure_free"
|
"CRYPTO_free", "CRYPTO_secure_free",
|
||||||
|
// --- glib memory deallocation
|
||||||
|
"g_free"
|
||||||
]) and
|
]) and
|
||||||
freedArg = 0
|
freedArg = 0
|
||||||
or
|
or
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ import cpp
|
|||||||
import semmle.code.cpp.models.interfaces.Taint
|
import semmle.code.cpp.models.interfaces.Taint
|
||||||
import semmle.code.cpp.models.interfaces.DataFlow
|
import semmle.code.cpp.models.interfaces.DataFlow
|
||||||
import semmle.code.cpp.models.interfaces.Iterator
|
import semmle.code.cpp.models.interfaces.Iterator
|
||||||
|
import semmle.code.cpp.models.interfaces.Alias
|
||||||
|
import semmle.code.cpp.models.interfaces.SideEffect
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An instantiation of the `std::iterator_traits` template.
|
* An instantiation of the `std::iterator_traits` template.
|
||||||
@@ -438,7 +440,7 @@ private class IteratorAssignmentMemberOperatorModel extends IteratorAssignmentMe
|
|||||||
* A `begin` or `end` member function, or a related member function, that
|
* A `begin` or `end` member function, or a related member function, that
|
||||||
* returns an iterator.
|
* returns an iterator.
|
||||||
*/
|
*/
|
||||||
private class BeginOrEndFunction extends MemberFunction, TaintFunction, GetIteratorFunction {
|
class BeginOrEndFunction extends MemberFunction {
|
||||||
BeginOrEndFunction() {
|
BeginOrEndFunction() {
|
||||||
this.hasName([
|
this.hasName([
|
||||||
"begin", "cbegin", "rbegin", "crbegin", "end", "cend", "rend", "crend", "before_begin",
|
"begin", "cbegin", "rbegin", "crbegin", "end", "cend", "rend", "crend", "before_begin",
|
||||||
@@ -446,7 +448,11 @@ private class BeginOrEndFunction extends MemberFunction, TaintFunction, GetItera
|
|||||||
]) and
|
]) and
|
||||||
this.getType().getUnspecifiedType() instanceof Iterator
|
this.getType().getUnspecifiedType() instanceof Iterator
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class BeginOrEndFunctionModels extends BeginOrEndFunction, TaintFunction,
|
||||||
|
GetIteratorFunction, AliasFunction, SideEffectFunction
|
||||||
|
{
|
||||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||||
input.isQualifierObject() and
|
input.isQualifierObject() and
|
||||||
output.isReturnValue()
|
output.isReturnValue()
|
||||||
@@ -456,6 +462,22 @@ private class BeginOrEndFunction extends MemberFunction, TaintFunction, GetItera
|
|||||||
input.isQualifierObject() and
|
input.isQualifierObject() and
|
||||||
output.isReturnValue()
|
output.isReturnValue()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override predicate parameterNeverEscapes(int index) { index = -1 }
|
||||||
|
|
||||||
|
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||||
|
|
||||||
|
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||||
|
|
||||||
|
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||||
|
|
||||||
|
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||||
|
none()
|
||||||
|
}
|
||||||
|
|
||||||
|
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||||
|
i = -1 and buffer = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -253,13 +253,15 @@ private class StdSequenceContainerAssign extends TaintFunction {
|
|||||||
/**
|
/**
|
||||||
* The standard container functions `at` and `operator[]`.
|
* The standard container functions `at` and `operator[]`.
|
||||||
*/
|
*/
|
||||||
private class StdSequenceContainerAt extends TaintFunction {
|
class StdSequenceContainerAt extends MemberFunction {
|
||||||
StdSequenceContainerAt() {
|
StdSequenceContainerAt() {
|
||||||
this.getClassAndName(["at", "operator[]"]) instanceof Array or
|
this.getClassAndName(["at", "operator[]"]) instanceof Array or
|
||||||
this.getClassAndName(["at", "operator[]"]) instanceof Deque or
|
this.getClassAndName(["at", "operator[]"]) instanceof Deque or
|
||||||
this.getClassAndName(["at", "operator[]"]) instanceof Vector
|
this.getClassAndName(["at", "operator[]"]) instanceof Vector
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class StdSequenceContainerAtModel extends StdSequenceContainerAt, TaintFunction {
|
||||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||||
// flow from qualifier to referenced return value
|
// flow from qualifier to referenced return value
|
||||||
input.isQualifierObject() and
|
input.isQualifierObject() and
|
||||||
|
|||||||
@@ -129,9 +129,11 @@ private class StdMapMerge extends TaintFunction {
|
|||||||
/**
|
/**
|
||||||
* The standard map functions `at` and `operator[]`.
|
* The standard map functions `at` and `operator[]`.
|
||||||
*/
|
*/
|
||||||
private class StdMapAt extends TaintFunction {
|
class StdMapAt extends MemberFunction {
|
||||||
StdMapAt() { this.getClassAndName(["at", "operator[]"]) instanceof MapOrUnorderedMap }
|
StdMapAt() { this.getClassAndName(["at", "operator[]"]) instanceof MapOrUnorderedMap }
|
||||||
|
}
|
||||||
|
|
||||||
|
private class StdMapAtModels extends StdMapAt, TaintFunction {
|
||||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||||
// flow from qualifier to referenced return value
|
// flow from qualifier to referenced return value
|
||||||
input.isQualifierObject() and
|
input.isQualifierObject() and
|
||||||
|
|||||||
@@ -1513,6 +1513,11 @@ exprs(
|
|||||||
int location: @location_expr ref
|
int location: @location_expr ref
|
||||||
);
|
);
|
||||||
|
|
||||||
|
expr_reuse(
|
||||||
|
int reuse: @expr ref,
|
||||||
|
int original: @expr ref
|
||||||
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
case @value.category of
|
case @value.category of
|
||||||
1 = prval
|
1 = prval
|
||||||
@@ -1741,6 +1746,7 @@ case @expr.kind of
|
|||||||
| 360 = @isunsigned
|
| 360 = @isunsigned
|
||||||
| 361 = @isvoid
|
| 361 = @isvoid
|
||||||
| 362 = @isvolatile
|
| 362 = @isvolatile
|
||||||
|
| 363 = @reuseexpr
|
||||||
;
|
;
|
||||||
|
|
||||||
@var_args_expr = @vastartexpr
|
@var_args_expr = @vastartexpr
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,2 @@
|
|||||||
|
description: Introduce re-use expressions
|
||||||
|
compatibility: backwards
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
* @name Missing return-value check for a 'scanf'-like function
|
* @name Missing return-value check for a 'scanf'-like function
|
||||||
* @description Failing to check that a call to 'scanf' actually writes to an
|
* @description Failing to check that a call to 'scanf' actually writes to an
|
||||||
* output variable can lead to unexpected behavior at reading time.
|
* output variable can lead to unexpected behavior at reading time.
|
||||||
* @kind problem
|
* @kind path-problem
|
||||||
* @problem.severity warning
|
* @problem.severity warning
|
||||||
* @security-severity 7.5
|
* @security-severity 7.5
|
||||||
* @precision medium
|
* @precision medium
|
||||||
@@ -18,16 +18,9 @@ import semmle.code.cpp.commons.Scanf
|
|||||||
import semmle.code.cpp.controlflow.Guards
|
import semmle.code.cpp.controlflow.Guards
|
||||||
import semmle.code.cpp.dataflow.new.DataFlow::DataFlow
|
import semmle.code.cpp.dataflow.new.DataFlow::DataFlow
|
||||||
import semmle.code.cpp.ir.IR
|
import semmle.code.cpp.ir.IR
|
||||||
import semmle.code.cpp.ir.ValueNumbering
|
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
|
||||||
import ScanfChecks
|
import ScanfChecks
|
||||||
|
import ScanfToUseFlow::PathGraph
|
||||||
/** Holds if `n` reaches an argument to a call to a `scanf`-like function. */
|
|
||||||
pragma[nomagic]
|
|
||||||
predicate revFlow0(Node n) {
|
|
||||||
isSink(_, _, n, _)
|
|
||||||
or
|
|
||||||
exists(Node succ | revFlow0(succ) | localFlowStep(n, succ))
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if `n` represents an uninitialized stack-allocated variable, or a
|
* Holds if `n` represents an uninitialized stack-allocated variable, or a
|
||||||
@@ -38,30 +31,45 @@ predicate isUninitialized(Node n) {
|
|||||||
n.asIndirectExpr(1) instanceof AllocationExpr
|
n.asIndirectExpr(1) instanceof AllocationExpr
|
||||||
}
|
}
|
||||||
|
|
||||||
pragma[nomagic]
|
|
||||||
predicate fwdFlow0(Node n) {
|
|
||||||
revFlow0(n) and
|
|
||||||
(
|
|
||||||
isUninitialized(n)
|
|
||||||
or
|
|
||||||
exists(Node prev |
|
|
||||||
fwdFlow0(prev) and
|
|
||||||
localFlowStep(prev, n)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
predicate isSink(ScanfFunctionCall call, int index, Node n, Expr input) {
|
predicate isSink(ScanfFunctionCall call, int index, Node n, Expr input) {
|
||||||
input = call.getOutputArgument(index) and
|
input = call.getOutputArgument(index) and
|
||||||
n.asIndirectExpr() = input
|
n.asIndirectExpr() = input
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A configuration to track a uninitialized data flowing to a `scanf`-like
|
||||||
|
* output parameter position.
|
||||||
|
*
|
||||||
|
* This is meant to be a simple flow to rule out cases like:
|
||||||
|
* ```
|
||||||
|
* int x = 0;
|
||||||
|
* scanf(..., &x);
|
||||||
|
* use(x);
|
||||||
|
* ```
|
||||||
|
* since `x` is already initialized it's not a security concern that `x` is
|
||||||
|
* used without checking the return value of `scanf`.
|
||||||
|
*
|
||||||
|
* Since this flow is meant to be simple, we disable field flow and require the
|
||||||
|
* source and the sink to be in the same callable.
|
||||||
|
*/
|
||||||
|
module UninitializedToScanfConfig implements ConfigSig {
|
||||||
|
predicate isSource(Node source) { isUninitialized(source) }
|
||||||
|
|
||||||
|
predicate isSink(Node sink) { isSink(_, _, sink, _) }
|
||||||
|
|
||||||
|
FlowFeature getAFeature() { result instanceof FeatureEqualSourceSinkCallContext }
|
||||||
|
|
||||||
|
int accessPathLimit() { result = 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
module UninitializedToScanfFlow = Global<UninitializedToScanfConfig>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if `call` is a `scanf`-like call and `output` is the `index`'th
|
* Holds if `call` is a `scanf`-like call and `output` is the `index`'th
|
||||||
* argument that has not been previously initialized.
|
* argument that has not been previously initialized.
|
||||||
*/
|
*/
|
||||||
predicate isRelevantScanfCall(ScanfFunctionCall call, int index, Expr output) {
|
predicate isRelevantScanfCall(ScanfFunctionCall call, int index, Expr output) {
|
||||||
exists(Node n | fwdFlow0(n) and isSink(call, index, n, output)) and
|
exists(Node n | UninitializedToScanfFlow::flowTo(n) and isSink(call, index, n, output)) and
|
||||||
// Exclude results from incorrectky checked scanf query
|
// Exclude results from incorrectky checked scanf query
|
||||||
not incorrectlyCheckedScanf(call)
|
not incorrectlyCheckedScanf(call)
|
||||||
}
|
}
|
||||||
@@ -77,31 +85,6 @@ predicate isSource(ScanfFunctionCall call, int index, Node n, Expr output) {
|
|||||||
n.asDefiningArgument() = output
|
n.asDefiningArgument() = output
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Holds if `n` is reachable from an output argument of a relevant call to
|
|
||||||
* a `scanf`-like function.
|
|
||||||
*/
|
|
||||||
pragma[nomagic]
|
|
||||||
predicate fwdFlow(Node n) {
|
|
||||||
isSource(_, _, n, _)
|
|
||||||
or
|
|
||||||
exists(Node prev |
|
|
||||||
fwdFlow(prev) and
|
|
||||||
localFlowStep(prev, n) and
|
|
||||||
not isSanitizerOut(prev)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Holds if `n` should not have outgoing flow. */
|
|
||||||
predicate isSanitizerOut(Node n) {
|
|
||||||
// We disable flow out of sinks to reduce result duplication
|
|
||||||
isSink(n, _)
|
|
||||||
or
|
|
||||||
// If the node is being passed to a function it may be
|
|
||||||
// modified, and thus it's safe to later read the value.
|
|
||||||
exists(n.asIndirectArgument())
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if `n` is a node such that `n.asExpr() = e` and `e` is not an
|
* Holds if `n` is a node such that `n.asExpr() = e` and `e` is not an
|
||||||
* argument of a deallocation expression.
|
* argument of a deallocation expression.
|
||||||
@@ -112,40 +95,37 @@ predicate isSink(Node n, Expr e) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if `n` is part of a path from a call to a `scanf`-like function
|
* A configuration to track flow from the output argument of a call to a
|
||||||
* to a use of the written variable.
|
* `scanf`-like function, and to a use of the defined variable.
|
||||||
*/
|
*/
|
||||||
pragma[nomagic]
|
module ScanfToUseConfig implements ConfigSig {
|
||||||
predicate revFlow(Node n) {
|
predicate isSource(Node source) { isSource(_, _, source, _) }
|
||||||
fwdFlow(n) and
|
|
||||||
(
|
predicate isSink(Node sink) { isSink(sink, _) }
|
||||||
|
|
||||||
|
predicate isBarrierOut(Node n) {
|
||||||
|
// We disable flow out of sinks to reduce result duplication
|
||||||
isSink(n, _)
|
isSink(n, _)
|
||||||
or
|
or
|
||||||
exists(Node succ |
|
// If the node is being passed to a function it may be
|
||||||
revFlow(succ) and
|
// modified, and thus it's safe to later read the value.
|
||||||
localFlowStep(n, succ) and
|
exists(n.asIndirectArgument())
|
||||||
not isSanitizerOut(n)
|
}
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A local flow step, restricted to relevant dataflow nodes. */
|
module ScanfToUseFlow = Global<ScanfToUseConfig>;
|
||||||
private predicate step(Node n1, Node n2) {
|
|
||||||
revFlow(n1) and
|
|
||||||
revFlow(n2) and
|
|
||||||
localFlowStep(n1, n2)
|
|
||||||
}
|
|
||||||
|
|
||||||
predicate hasFlow(Node n1, Node n2) = fastTC(step/2)(n1, n2)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if `source` is the `index`'th argument to the `scanf`-like call `call`, and `sink` is
|
* Holds if `source` is the `index`'th argument to the `scanf`-like call `call`, and `sink` is
|
||||||
* a dataflow node that represents the expression `e`.
|
* a dataflow node that represents the expression `e`.
|
||||||
*/
|
*/
|
||||||
predicate hasFlow(Node source, ScanfFunctionCall call, int index, Node sink, Expr e) {
|
predicate flowPath(
|
||||||
isSource(call, index, source, _) and
|
ScanfToUseFlow::PathNode source, ScanfFunctionCall call, int index, ScanfToUseFlow::PathNode sink,
|
||||||
hasFlow(source, sink) and
|
Expr e
|
||||||
isSink(sink, e)
|
) {
|
||||||
|
isSource(call, index, source.getNode(), _) and
|
||||||
|
ScanfToUseFlow::flowPath(source, sink) and
|
||||||
|
isSink(sink.getNode(), e)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -167,39 +147,33 @@ int getMinimumGuardConstant(ScanfFunctionCall call, int index) {
|
|||||||
* Holds the access to `e` isn't guarded by a check that ensures that `call` returned
|
* Holds the access to `e` isn't guarded by a check that ensures that `call` returned
|
||||||
* at least `minGuard`.
|
* at least `minGuard`.
|
||||||
*/
|
*/
|
||||||
predicate hasNonGuardedAccess(ScanfFunctionCall call, Expr e, int minGuard) {
|
predicate hasNonGuardedAccess(
|
||||||
|
ScanfToUseFlow::PathNode source, ScanfFunctionCall call, ScanfToUseFlow::PathNode sink, Expr e,
|
||||||
|
int minGuard
|
||||||
|
) {
|
||||||
exists(int index |
|
exists(int index |
|
||||||
hasFlow(_, call, index, _, e) and
|
flowPath(source, call, index, sink, e) and
|
||||||
minGuard = getMinimumGuardConstant(call, index)
|
minGuard = getMinimumGuardConstant(call, index)
|
||||||
|
|
|
|
||||||
not exists(int value |
|
not exists(GuardCondition guard |
|
||||||
e.getBasicBlock() = blockGuardedBy(value, "==", call) and minGuard <= value
|
// call == k and k >= minGuard so call >= minGuard
|
||||||
|
guard
|
||||||
|
.ensuresEq(globalValueNumber(call).getAnExpr(), any(int k | minGuard <= k),
|
||||||
|
e.getBasicBlock(), true)
|
||||||
or
|
or
|
||||||
e.getBasicBlock() = blockGuardedBy(value, "<", call) and minGuard - 1 <= value
|
// call >= k and k >= minGuard so call >= minGuard
|
||||||
or
|
guard
|
||||||
e.getBasicBlock() = blockGuardedBy(value, "<=", call) and minGuard <= value
|
.ensuresLt(globalValueNumber(call).getAnExpr(), any(int k | minGuard <= k),
|
||||||
|
e.getBasicBlock(), false)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns a block guarded by the assertion of `value op call` */
|
from
|
||||||
BasicBlock blockGuardedBy(int value, string op, ScanfFunctionCall call) {
|
ScanfToUseFlow::PathNode source, ScanfToUseFlow::PathNode sink, ScanfFunctionCall call, Expr e,
|
||||||
exists(GuardCondition g, Expr left, Expr right |
|
int minGuard
|
||||||
right = g.getAChild() and
|
where hasNonGuardedAccess(source, call, sink, e, minGuard)
|
||||||
value = left.getValue().toInt() and
|
select e, source, sink,
|
||||||
localExprFlow(call, right)
|
|
||||||
|
|
|
||||||
g.ensuresEq(left, right, 0, result, true) and op = "=="
|
|
||||||
or
|
|
||||||
g.ensuresLt(left, right, 0, result, true) and op = "<"
|
|
||||||
or
|
|
||||||
g.ensuresLt(left, right, 1, result, true) and op = "<="
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
from ScanfFunctionCall call, Expr e, int minGuard
|
|
||||||
where hasNonGuardedAccess(call, e, minGuard)
|
|
||||||
select e,
|
|
||||||
"This variable is read, but may not have been written. " +
|
"This variable is read, but may not have been written. " +
|
||||||
"It should be guarded by a check that the $@ returns at least " + minGuard + ".", call,
|
"It should be guarded by a check that the $@ returns at least " + minGuard + ".", call,
|
||||||
call.toString()
|
call.toString()
|
||||||
|
|||||||
@@ -3,15 +3,11 @@ private import semmle.code.cpp.commons.Scanf
|
|||||||
private import semmle.code.cpp.controlflow.IRGuards
|
private import semmle.code.cpp.controlflow.IRGuards
|
||||||
private import semmle.code.cpp.ir.ValueNumbering
|
private import semmle.code.cpp.ir.ValueNumbering
|
||||||
|
|
||||||
private ConstantInstruction getZeroInstruction() { result.getValue() = "0" }
|
|
||||||
|
|
||||||
private Operand zero() { result.getDef() = getZeroInstruction() }
|
|
||||||
|
|
||||||
private predicate exprInBooleanContext(Expr e) {
|
private predicate exprInBooleanContext(Expr e) {
|
||||||
exists(IRGuardCondition gc |
|
exists(IRGuardCondition gc |
|
||||||
exists(Instruction i |
|
exists(Instruction i |
|
||||||
i.getUnconvertedResultExpression() = e and
|
i.getUnconvertedResultExpression() = e and
|
||||||
gc.comparesEq(valueNumber(i).getAUse(), zero(), 0, _, _)
|
gc.comparesEq(valueNumber(i).getAUse(), 0, _, _)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
gc.getUnconvertedResultExpression() = e
|
gc.getUnconvertedResultExpression() = e
|
||||||
@@ -36,10 +32,6 @@ private string getEofValue() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private ConstantInstruction getEofInstruction() { result.getValue() = getEofValue() }
|
|
||||||
|
|
||||||
private Operand eof() { result.getDef() = getEofInstruction() }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if the value of `call` has been checked to not equal `EOF`.
|
* Holds if the value of `call` has been checked to not equal `EOF`.
|
||||||
*/
|
*/
|
||||||
@@ -47,10 +39,10 @@ private predicate checkedForEof(ScanfFunctionCall call) {
|
|||||||
exists(IRGuardCondition gc |
|
exists(IRGuardCondition gc |
|
||||||
exists(Instruction i | i.getUnconvertedResultExpression() = call |
|
exists(Instruction i | i.getUnconvertedResultExpression() = call |
|
||||||
// call == EOF
|
// call == EOF
|
||||||
gc.comparesEq(valueNumber(i).getAUse(), eof(), 0, _, _)
|
gc.comparesEq(valueNumber(i).getAUse(), getEofValue().toInt(), _, _)
|
||||||
or
|
or
|
||||||
// call < 0 (EOF is guaranteed to be negative)
|
// call < 0 (EOF is guaranteed to be negative)
|
||||||
gc.comparesLt(valueNumber(i).getAUse(), zero(), 0, true, _)
|
gc.comparesLt(valueNumber(i).getAUse(), 0, true, _)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,6 +37,37 @@ class UncalledFunction extends Function {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** The `unsigned short` type. */
|
||||||
|
class UnsignedShort extends ShortType {
|
||||||
|
UnsignedShort() { this.isUnsigned() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if `t` cannot refer to a string. That is, it's a built-in
|
||||||
|
* or arithmetic type that is not a "`char` like" type.
|
||||||
|
*/
|
||||||
|
predicate cannotContainString(Type t) {
|
||||||
|
exists(Type unspecified |
|
||||||
|
unspecified = t.getUnspecifiedType() and
|
||||||
|
not unspecified instanceof UnknownType and
|
||||||
|
not unspecified instanceof CharType and
|
||||||
|
not unspecified instanceof WideCharType and
|
||||||
|
not unspecified instanceof Char8Type and
|
||||||
|
not unspecified instanceof Char16Type and
|
||||||
|
not unspecified instanceof Char32Type and
|
||||||
|
// C often defines `wchar_t` as `unsigned short`
|
||||||
|
not unspecified instanceof UnsignedShort
|
||||||
|
|
|
||||||
|
unspecified instanceof ArithmeticType or
|
||||||
|
unspecified instanceof BuiltInType
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate dataFlowOrTaintFlowFunction(Function func, FunctionOutput output) {
|
||||||
|
func.(DataFlowFunction).hasDataFlow(_, output) or
|
||||||
|
func.(TaintFunction).hasTaintFlow(_, output)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if `node` is a non-constant source of data flow for non-const format string detection.
|
* Holds if `node` is a non-constant source of data flow for non-const format string detection.
|
||||||
* This is defined as either:
|
* This is defined as either:
|
||||||
@@ -69,7 +100,9 @@ predicate isNonConst(DataFlow::Node node) {
|
|||||||
// Parameters of uncalled functions that aren't const
|
// Parameters of uncalled functions that aren't const
|
||||||
exists(UncalledFunction f, Parameter p |
|
exists(UncalledFunction f, Parameter p |
|
||||||
f.getAParameter() = p and
|
f.getAParameter() = p and
|
||||||
p = node.asParameter() and
|
// We pick the indirection of the parameter since this query is focused
|
||||||
|
// on strings.
|
||||||
|
p = node.asParameter(1) and
|
||||||
// Ignore main's argv parameter as it is already considered a `FlowSource`
|
// Ignore main's argv parameter as it is already considered a `FlowSource`
|
||||||
// not ignoring it will result in path redundancies
|
// not ignoring it will result in path redundancies
|
||||||
(f.getName() = "main" implies p != f.getParameter(1))
|
(f.getName() = "main" implies p != f.getParameter(1))
|
||||||
@@ -82,30 +115,27 @@ predicate isNonConst(DataFlow::Node node) {
|
|||||||
// are considered as possible non-const sources
|
// are considered as possible non-const sources
|
||||||
// The function's output must also not be const to be considered a non-const source
|
// The function's output must also not be const to be considered a non-const source
|
||||||
exists(Function func, CallInstruction call |
|
exists(Function func, CallInstruction call |
|
||||||
// NOTE: could use `Call` getAnArgument() instead of `CallInstruction` but requires two
|
not func.hasDefinition() and
|
||||||
// variables representing the same call in ordoer to use `callOutput` below.
|
func = call.getStaticCallTarget()
|
||||||
exists(Expr arg |
|
|
||||||
call.getPositionalArgumentOperand(_).getDef().getUnconvertedResultExpression() = arg and
|
|
||||||
arg = node.asDefiningArgument()
|
|
||||||
)
|
|
||||||
or
|
|
||||||
call.getUnconvertedResultExpression() = node.asIndirectExpr()
|
|
||||||
|
|
|
|
||||||
func = call.getStaticCallTarget() and
|
// Case 1: It's a known dataflow or taintflow function with flow to the return value
|
||||||
|
call.getUnconvertedResultExpression() = node.asIndirectExpr() and
|
||||||
not exists(FunctionOutput output |
|
not exists(FunctionOutput output |
|
||||||
// NOTE: we must include dataflow and taintflow. e.g., including only dataflow we will find sprintf
|
dataFlowOrTaintFlowFunction(func, output) and
|
||||||
// variant function's output are now possible non-const sources
|
output.isReturnValueDeref(_) and
|
||||||
pragma[only_bind_out](func).(DataFlowFunction).hasDataFlow(_, output) or
|
|
||||||
pragma[only_bind_out](func).(TaintFunction).hasTaintFlow(_, output)
|
|
||||||
|
|
|
||||||
node = callOutput(call, output)
|
node = callOutput(call, output)
|
||||||
)
|
)
|
||||||
) and
|
or
|
||||||
not exists(Call c |
|
// Case 2: It's a known dataflow or taintflow function with flow to an output parameter
|
||||||
c.getTarget().hasDefinition() and
|
exists(int i |
|
||||||
if node instanceof DataFlow::DefinitionByReferenceNode
|
call.getPositionalArgumentOperand(i).getDef().getUnconvertedResultExpression() =
|
||||||
then c.getAnArgument() = node.asDefiningArgument()
|
node.asDefiningArgument() and
|
||||||
else c = [node.asExpr(), node.asIndirectExpr()]
|
not exists(FunctionOutput output |
|
||||||
|
dataFlowOrTaintFlowFunction(func, output) and
|
||||||
|
output.isParameterDeref(i, _) and
|
||||||
|
node = callOutput(call, output)
|
||||||
|
)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,18 +144,29 @@ predicate isNonConst(DataFlow::Node node) {
|
|||||||
* `FormattingFunctionCall`.
|
* `FormattingFunctionCall`.
|
||||||
*/
|
*/
|
||||||
predicate isSinkImpl(DataFlow::Node sink, Expr formatString) {
|
predicate isSinkImpl(DataFlow::Node sink, Expr formatString) {
|
||||||
[sink.asExpr(), sink.asIndirectExpr()] = formatString and
|
sink.asIndirectExpr() = formatString and
|
||||||
exists(FormattingFunctionCall fc | formatString = fc.getArgument(fc.getFormatParameterIndex()))
|
exists(FormattingFunctionCall fc | formatString = fc.getArgument(fc.getFormatParameterIndex()))
|
||||||
}
|
}
|
||||||
|
|
||||||
module NonConstFlowConfig implements DataFlow::ConfigSig {
|
module NonConstFlowConfig implements DataFlow::ConfigSig {
|
||||||
predicate isSource(DataFlow::Node source) { isNonConst(source) }
|
predicate isSource(DataFlow::Node source) {
|
||||||
|
exists(Type t |
|
||||||
|
isNonConst(source) and
|
||||||
|
t = source.getType() and
|
||||||
|
not cannotContainString(t)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
predicate isSink(DataFlow::Node sink) { isSinkImpl(sink, _) }
|
predicate isSink(DataFlow::Node sink) { isSinkImpl(sink, _) }
|
||||||
|
|
||||||
predicate isBarrier(DataFlow::Node node) {
|
predicate isBarrier(DataFlow::Node node) {
|
||||||
// Ignore tracing non-const through array indices
|
// Ignore tracing non-const through array indices
|
||||||
exists(ArrayExpr a | a.getArrayOffset() = node.asExpr())
|
exists(ArrayExpr a | a.getArrayOffset() = node.asIndirectExpr())
|
||||||
|
or
|
||||||
|
exists(Type t |
|
||||||
|
t = node.getType() and
|
||||||
|
cannotContainString(t)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* @name Potentially uninitialized local variable
|
* @name Potentially uninitialized local variable
|
||||||
* @description Reading from a local variable that has not been assigned to
|
* @description Reading from a local variable that has not been assigned to
|
||||||
* will typically yield garbage.
|
* will typically yield garbage.
|
||||||
* @kind problem
|
* @kind path-problem
|
||||||
* @id cpp/uninitialized-local
|
* @id cpp/uninitialized-local
|
||||||
* @problem.severity warning
|
* @problem.severity warning
|
||||||
* @security-severity 7.8
|
* @security-severity 7.8
|
||||||
@@ -15,6 +15,7 @@
|
|||||||
import cpp
|
import cpp
|
||||||
import semmle.code.cpp.ir.IR
|
import semmle.code.cpp.ir.IR
|
||||||
import semmle.code.cpp.ir.dataflow.MustFlow
|
import semmle.code.cpp.ir.dataflow.MustFlow
|
||||||
|
import PathGraph
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Auxiliary predicate: Types that don't require initialization
|
* Auxiliary predicate: Types that don't require initialization
|
||||||
@@ -89,4 +90,4 @@ where
|
|||||||
conf.hasFlowPath(source, sink) and
|
conf.hasFlowPath(source, sink) and
|
||||||
isSinkImpl(sink.getInstruction(), va) and
|
isSinkImpl(sink.getInstruction(), va) and
|
||||||
v = va.getTarget()
|
v = va.getTarget()
|
||||||
select va, "The variable $@ may not be initialized at this access.", v, v.getName()
|
select va, source, sink, "The variable $@ may not be initialized at this access.", v, v.getName()
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
/**
|
/**
|
||||||
* @name Boost_asio TLS Settings Misconfiguration
|
* @name boost::asio TLS settings misconfiguration
|
||||||
* @description Using the TLS or SSLv23 protocol from the boost::asio library, but not disabling deprecated protocols, or disabling minimum-recommended protocols.
|
* @description Using the TLS or SSLv23 protocol from the boost::asio library, but not disabling deprecated protocols, or disabling minimum-recommended protocols.
|
||||||
* @kind problem
|
* @kind problem
|
||||||
* @problem.severity error
|
* @problem.severity error
|
||||||
|
* @precision medium
|
||||||
* @security-severity 7.5
|
* @security-severity 7.5
|
||||||
* @id cpp/boost/tls-settings-misconfiguration
|
* @id cpp/boost/tls-settings-misconfiguration
|
||||||
* @tags security
|
* @tags security
|
||||||
@@ -12,36 +13,43 @@
|
|||||||
import cpp
|
import cpp
|
||||||
import semmle.code.cpp.security.boostorg.asio.protocols
|
import semmle.code.cpp.security.boostorg.asio.protocols
|
||||||
|
|
||||||
module ExistsAnyFlowConfig implements DataFlow::ConfigSig {
|
predicate isSourceImpl(DataFlow::Node source, ConstructorCall cc) {
|
||||||
predicate isSource(DataFlow::Node source) {
|
exists(BoostorgAsio::SslContextClass c | c.getAContructorCall() = cc and cc = source.asExpr())
|
||||||
exists(BoostorgAsio::SslContextClass c | c.getAContructorCall() = source.asExpr())
|
}
|
||||||
}
|
|
||||||
|
|
||||||
predicate isSink(DataFlow::Node sink) {
|
predicate isSinkImpl(DataFlow::Node sink, FunctionCall fcSetOptions) {
|
||||||
exists(BoostorgAsio::SslSetOptionsFunction f, FunctionCall fcSetOptions |
|
exists(BoostorgAsio::SslSetOptionsFunction f |
|
||||||
f.getACallToThisFunction() = fcSetOptions and
|
f.getACallToThisFunction() = fcSetOptions and
|
||||||
fcSetOptions.getQualifier() = sink.asExpr()
|
fcSetOptions.getQualifier() = sink.asIndirectExpr()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
module ExistsAnyFlowConfig implements DataFlow::ConfigSig {
|
||||||
|
predicate isSource(DataFlow::Node source) { isSourceImpl(source, _) }
|
||||||
|
|
||||||
|
predicate isSink(DataFlow::Node sink) { isSinkImpl(sink, _) }
|
||||||
}
|
}
|
||||||
|
|
||||||
module ExistsAnyFlow = DataFlow::Global<ExistsAnyFlowConfig>;
|
module ExistsAnyFlow = DataFlow::Global<ExistsAnyFlowConfig>;
|
||||||
|
|
||||||
bindingset[flag]
|
bindingset[flag]
|
||||||
predicate isOptionSet(ConstructorCall cc, int flag, FunctionCall fcSetOptions) {
|
predicate isOptionSet(ConstructorCall cc, int flag, FunctionCall fcSetOptions) {
|
||||||
exists(VariableAccess contextSetOptions |
|
exists(
|
||||||
ExistsAnyFlow::flow(DataFlow::exprNode(cc), DataFlow::exprNode(contextSetOptions)) and
|
VariableAccess contextSetOptions, BoostorgAsio::SslSetOptionsFunction f, DataFlow::Node source,
|
||||||
exists(BoostorgAsio::SslSetOptionsFunction f | f.getACallToThisFunction() = fcSetOptions |
|
DataFlow::Node sink
|
||||||
|
|
|
||||||
|
isSourceImpl(source, cc) and
|
||||||
|
isSinkImpl(sink, fcSetOptions) and
|
||||||
|
ExistsAnyFlow::flow(source, sink) and
|
||||||
|
f.getACallToThisFunction() = fcSetOptions and
|
||||||
contextSetOptions = fcSetOptions.getQualifier() and
|
contextSetOptions = fcSetOptions.getQualifier() and
|
||||||
forall(Expr optionArgument, Expr optionArgumentSource |
|
forex(Expr optionArgument |
|
||||||
optionArgument = fcSetOptions.getArgument(0) and
|
optionArgument = fcSetOptions.getArgument(0) and
|
||||||
BoostorgAsio::SslOptionFlow::flow(DataFlow::exprNode(optionArgumentSource),
|
BoostorgAsio::SslOptionFlow::flowTo(DataFlow::exprNode(optionArgument))
|
||||||
DataFlow::exprNode(optionArgument))
|
|
||||||
|
|
|
|
||||||
optionArgument.getValue().toInt().bitShiftRight(16).bitAnd(flag) = flag
|
optionArgument.getValue().toInt().bitShiftRight(16).bitAnd(flag) = flag
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bindingset[flag]
|
bindingset[flag]
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
/**
|
/**
|
||||||
* @name boost::asio Use of deprecated hardcoded Protocol
|
* @name boost::asio use of deprecated hardcoded protocol
|
||||||
* @description Using a deprecated hard-coded protocol using the boost::asio library.
|
* @description Using a deprecated hard-coded protocol using the boost::asio library.
|
||||||
* @kind problem
|
* @kind problem
|
||||||
* @problem.severity error
|
* @problem.severity error
|
||||||
|
* @precision medium
|
||||||
* @security-severity 7.5
|
* @security-severity 7.5
|
||||||
* @id cpp/boost/use-of-deprecated-hardcoded-security-protocol
|
* @id cpp/boost/use-of-deprecated-hardcoded-security-protocol
|
||||||
* @tags security
|
* @tags security
|
||||||
|
|||||||
50
cpp/ql/src/Security/CWE/CWE-843/TypeConfusion.qhelp
Normal file
50
cpp/ql/src/Security/CWE/CWE-843/TypeConfusion.qhelp
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
|
||||||
|
<qhelp>
|
||||||
|
|
||||||
|
<overview>
|
||||||
|
<p>
|
||||||
|
Certain casts in C and C++ place no restrictions on the target type. For
|
||||||
|
example, C style casts such as <code>(MyClass*)p</code> allows the programmer
|
||||||
|
to cast any pointer <code>p</code> to an expression of type <code>MyClass*</code>.
|
||||||
|
If the runtime type of <code>p</code> turns out to be a type that's incompatible
|
||||||
|
with <code>MyClass</code>, this results in undefined behavior.
|
||||||
|
</p>
|
||||||
|
</overview>
|
||||||
|
|
||||||
|
<recommendation>
|
||||||
|
<p>
|
||||||
|
If possible, use <code>dynamic_cast</code> to safely cast between polymorphic types.
|
||||||
|
If <code>dynamic_cast</code> is not an option, use <code>static_cast</code> to restrict
|
||||||
|
the kinds of conversions that the compiler is allowed to perform. If C++ style casts is
|
||||||
|
not an option, carefully check that all casts are safe.
|
||||||
|
</p>
|
||||||
|
</recommendation>
|
||||||
|
|
||||||
|
<example>
|
||||||
|
<p>
|
||||||
|
Consider the following class hierachy where we define a base class <code>Shape</code> and two
|
||||||
|
derived classes <code>Circle</code> and <code>Square</code> that are mutually incompatible:
|
||||||
|
</p>
|
||||||
|
<sample src="TypeConfusionCommon.cpp"/>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
The following code demonstrates a type confusion vulnerability where the programmer
|
||||||
|
assumes that the runtime type of <code>p</code> is always a <code>Square</code>.
|
||||||
|
However, if <code>p</code> is a <code>Circle</code>, the cast will result in undefined behavior.
|
||||||
|
</p>
|
||||||
|
<sample src="TypeConfusionBad.cpp"/>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
The following code fixes the vulnerability by using <code>dynamic_cast</code> to
|
||||||
|
safely cast between polymorphic types. If the cast fails, <code>dynamic_cast</code>
|
||||||
|
returns a null pointer, which can be checked for and handled appropriately.
|
||||||
|
</p>
|
||||||
|
<sample src="TypeConfusionGood.cpp"/>
|
||||||
|
</example>
|
||||||
|
|
||||||
|
<references>
|
||||||
|
<li>
|
||||||
|
Microsoft Learn: <a href="https://learn.microsoft.com/en-us/cpp/cpp/type-conversions-and-type-safety-modern-cpp">Type conversions and type safety</a>.
|
||||||
|
</li>
|
||||||
|
</references>
|
||||||
|
</qhelp>
|
||||||
263
cpp/ql/src/Security/CWE/CWE-843/TypeConfusion.ql
Normal file
263
cpp/ql/src/Security/CWE/CWE-843/TypeConfusion.ql
Normal file
@@ -0,0 +1,263 @@
|
|||||||
|
/**
|
||||||
|
* @name Type confusion
|
||||||
|
* @description Casting a value to an incompatible type can lead to undefined behavior.
|
||||||
|
* @kind path-problem
|
||||||
|
* @problem.severity warning
|
||||||
|
* @security-severity 9.3
|
||||||
|
* @precision medium
|
||||||
|
* @id cpp/type-confusion
|
||||||
|
* @tags security
|
||||||
|
* external/cwe/cwe-843
|
||||||
|
*/
|
||||||
|
|
||||||
|
import cpp
|
||||||
|
import semmle.code.cpp.dataflow.new.DataFlow
|
||||||
|
import Flow::PathGraph
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if `f` is a field located at byte offset `offset` in `c`.
|
||||||
|
*
|
||||||
|
* Note that predicate is recursive, so that given the following:
|
||||||
|
* ```cpp
|
||||||
|
* struct S1 {
|
||||||
|
* int a;
|
||||||
|
* void* b;
|
||||||
|
* };
|
||||||
|
*
|
||||||
|
* struct S2 {
|
||||||
|
* S1 s1;
|
||||||
|
* char c;
|
||||||
|
* };
|
||||||
|
* ```
|
||||||
|
* both `hasAFieldWithOffset(S2, s1, 0)` and `hasAFieldWithOffset(S2, a, 0)`
|
||||||
|
* holds.
|
||||||
|
*/
|
||||||
|
predicate hasAFieldWithOffset(Class c, Field f, int offset) {
|
||||||
|
// Base case: `f` is a field in `c`.
|
||||||
|
f = c.getAField() and
|
||||||
|
offset = f.getByteOffset() and
|
||||||
|
not f.getUnspecifiedType().(Class).hasDefinition()
|
||||||
|
or
|
||||||
|
// Otherwise, we find the struct that is a field of `c` which then has
|
||||||
|
// the field `f` as a member.
|
||||||
|
exists(Field g |
|
||||||
|
g = c.getAField() and
|
||||||
|
// Find the field with the largest offset that's less than or equal to
|
||||||
|
// offset. That's the struct we need to search recursively.
|
||||||
|
g =
|
||||||
|
max(Field cand, int candOffset |
|
||||||
|
cand = c.getAField() and
|
||||||
|
candOffset = cand.getByteOffset() and
|
||||||
|
offset >= candOffset
|
||||||
|
|
|
||||||
|
cand order by candOffset
|
||||||
|
) and
|
||||||
|
hasAFieldWithOffset(g.getUnspecifiedType(), f, offset - g.getByteOffset())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Holds if `f` is the last field of its declaring class. */
|
||||||
|
predicate lastField(Field f) {
|
||||||
|
exists(Class c | c = f.getDeclaringType() |
|
||||||
|
f =
|
||||||
|
max(Field cand, int byteOffset |
|
||||||
|
cand.getDeclaringType() = c and byteOffset = f.getByteOffset()
|
||||||
|
|
|
||||||
|
cand order by byteOffset
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if there exists a field in `c2` at offset `offset` that's compatible
|
||||||
|
* with `f1`.
|
||||||
|
*/
|
||||||
|
bindingset[f1, offset, c2]
|
||||||
|
pragma[inline_late]
|
||||||
|
predicate hasCompatibleFieldAtOffset(Field f1, int offset, Class c2) {
|
||||||
|
exists(Field f2 | hasAFieldWithOffset(c2, f2, offset) |
|
||||||
|
// Let's not deal with bit-fields for now.
|
||||||
|
f2 instanceof BitField
|
||||||
|
or
|
||||||
|
f1.getUnspecifiedType().getSize() = f2.getUnspecifiedType().getSize()
|
||||||
|
or
|
||||||
|
lastField(f1) and
|
||||||
|
f1.getUnspecifiedType().getSize() <= f2.getUnspecifiedType().getSize()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if `c1` is a prefix of `c2`.
|
||||||
|
*/
|
||||||
|
bindingset[c1, c2]
|
||||||
|
pragma[inline_late]
|
||||||
|
predicate prefix(Class c1, Class c2) {
|
||||||
|
not c1.isPolymorphic() and
|
||||||
|
not c2.isPolymorphic() and
|
||||||
|
if c1 instanceof Union
|
||||||
|
then
|
||||||
|
// If it's a union we just verify that one of it's variants is compatible with the other class
|
||||||
|
exists(Field f1, int offset |
|
||||||
|
// Let's not deal with bit-fields for now.
|
||||||
|
not f1 instanceof BitField and
|
||||||
|
hasAFieldWithOffset(c1, f1, offset)
|
||||||
|
|
|
||||||
|
hasCompatibleFieldAtOffset(f1, offset, c2)
|
||||||
|
)
|
||||||
|
else
|
||||||
|
forall(Field f1, int offset |
|
||||||
|
// Let's not deal with bit-fields for now.
|
||||||
|
not f1 instanceof BitField and
|
||||||
|
hasAFieldWithOffset(c1, f1, offset)
|
||||||
|
|
|
||||||
|
hasCompatibleFieldAtOffset(f1, offset, c2)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An unsafe cast is any explicit cast that is not
|
||||||
|
* a `dynamic_cast`.
|
||||||
|
*/
|
||||||
|
class UnsafeCast extends Cast {
|
||||||
|
private Class toType;
|
||||||
|
|
||||||
|
UnsafeCast() {
|
||||||
|
(
|
||||||
|
this instanceof CStyleCast
|
||||||
|
or
|
||||||
|
this instanceof StaticCast
|
||||||
|
or
|
||||||
|
this instanceof ReinterpretCast
|
||||||
|
) and
|
||||||
|
toType = this.getExplicitlyConverted().getUnspecifiedType().stripType() and
|
||||||
|
not this.isImplicit() and
|
||||||
|
exists(TypeDeclarationEntry tde |
|
||||||
|
tde = toType.getDefinition() and
|
||||||
|
not tde.isFromUninstantiatedTemplate(_)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Class getConvertedType() { result = toType }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if the result of this cast can safely be interpreted as a value of
|
||||||
|
* type `t`.
|
||||||
|
*
|
||||||
|
* The compatibility rules are as follows:
|
||||||
|
*
|
||||||
|
* 1. the result of `(T)x` is compatible with the type `T` for any `T`
|
||||||
|
* 2. the result of `(T)x` is compatible with the type `U` for any `U` such
|
||||||
|
* that `U` is a subtype of `T`, or `T` is a subtype of `U`.
|
||||||
|
* 3. the result of `(T)x` is compatible with the type `U` if the list
|
||||||
|
* of fields of `T` is a prefix of the list of fields of `U`.
|
||||||
|
* For example, if `U` is `struct { unsigned char x; int y; };`
|
||||||
|
* and `T` is `struct { unsigned char uc; };`.
|
||||||
|
* 4. the result of `(T)x` is compatible with the type `U` if the list
|
||||||
|
* of fields of `U` is a prefix of the list of fields of `T`.
|
||||||
|
*
|
||||||
|
* Condition 4 is a bit controversial, since it assumes that the additional
|
||||||
|
* fields in `T` won't be accessed. This may result in some FNs.
|
||||||
|
*/
|
||||||
|
bindingset[this, t]
|
||||||
|
pragma[inline_late]
|
||||||
|
predicate compatibleWith(Type t) {
|
||||||
|
// Conition 1
|
||||||
|
t.stripType() = this.getConvertedType()
|
||||||
|
or
|
||||||
|
// Condition 3
|
||||||
|
prefix(this.getConvertedType(), t.stripType())
|
||||||
|
or
|
||||||
|
// Condition 4
|
||||||
|
prefix(t.stripType(), this.getConvertedType())
|
||||||
|
or
|
||||||
|
// Condition 2 (a)
|
||||||
|
t.stripType().(Class).getABaseClass+() = this.getConvertedType()
|
||||||
|
or
|
||||||
|
// Condition 2 (b)
|
||||||
|
t.stripType() = this.getConvertedType().getABaseClass+()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if `source` is an allocation that allocates a value of type `type`.
|
||||||
|
*/
|
||||||
|
predicate isSourceImpl(DataFlow::Node source, Class type) {
|
||||||
|
exists(AllocationExpr alloc |
|
||||||
|
alloc = source.asExpr() and
|
||||||
|
type = alloc.getAllocatedElementType().stripType() and
|
||||||
|
not exists(
|
||||||
|
alloc
|
||||||
|
.(NewOrNewArrayExpr)
|
||||||
|
.getAllocator()
|
||||||
|
.(OperatorNewAllocationFunction)
|
||||||
|
.getPlacementArgument()
|
||||||
|
)
|
||||||
|
) and
|
||||||
|
exists(TypeDeclarationEntry tde |
|
||||||
|
tde = type.getDefinition() and
|
||||||
|
not tde.isFromUninstantiatedTemplate(_)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A configuration describing flow from an allocation to a potentially unsafe cast. */
|
||||||
|
module Config implements DataFlow::ConfigSig {
|
||||||
|
predicate isSource(DataFlow::Node source) { isSourceImpl(source, _) }
|
||||||
|
|
||||||
|
predicate isBarrier(DataFlow::Node node) {
|
||||||
|
// We disable flow through global variables to reduce FPs from infeasible paths
|
||||||
|
node instanceof DataFlow::VariableNode
|
||||||
|
or
|
||||||
|
exists(Class c | c = node.getType().stripType() |
|
||||||
|
not c.hasDefinition()
|
||||||
|
or
|
||||||
|
exists(TypeDeclarationEntry tde |
|
||||||
|
tde = c.getDefinition() and
|
||||||
|
tde.isFromUninstantiatedTemplate(_)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate isSink(DataFlow::Node sink) { sink.asExpr() = any(UnsafeCast cast).getUnconverted() }
|
||||||
|
|
||||||
|
int fieldFlowBranchLimit() { result = 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
module Flow = DataFlow::Global<Config>;
|
||||||
|
|
||||||
|
predicate relevantType(DataFlow::Node sink, Class allocatedType) {
|
||||||
|
exists(DataFlow::Node source |
|
||||||
|
Flow::flow(source, sink) and
|
||||||
|
isSourceImpl(source, allocatedType)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate isSinkImpl(
|
||||||
|
DataFlow::Node sink, Class allocatedType, Type convertedType, boolean compatible
|
||||||
|
) {
|
||||||
|
exists(UnsafeCast cast |
|
||||||
|
relevantType(sink, allocatedType) and
|
||||||
|
sink.asExpr() = cast.getUnconverted() and
|
||||||
|
convertedType = cast.getConvertedType()
|
||||||
|
|
|
||||||
|
if cast.compatibleWith(allocatedType) then compatible = true else compatible = false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
from
|
||||||
|
Flow::PathNode source, Flow::PathNode sink, Type badSourceType, Type sinkType,
|
||||||
|
DataFlow::Node sinkNode
|
||||||
|
where
|
||||||
|
Flow::flowPath(source, sink) and
|
||||||
|
sinkNode = sink.getNode() and
|
||||||
|
isSourceImpl(source.getNode(), badSourceType) and
|
||||||
|
isSinkImpl(sinkNode, badSourceType, sinkType, false) and
|
||||||
|
// If there is any flow that would result in a valid cast then we don't
|
||||||
|
// report an alert here. This reduces the number of FPs from infeasible paths
|
||||||
|
// significantly.
|
||||||
|
not exists(DataFlow::Node goodSource, Type goodSourceType |
|
||||||
|
isSourceImpl(goodSource, goodSourceType) and
|
||||||
|
isSinkImpl(sinkNode, goodSourceType, sinkType, true) and
|
||||||
|
Flow::flow(goodSource, sinkNode)
|
||||||
|
)
|
||||||
|
select sinkNode, source, sink, "Conversion from $@ to $@ is invalid.", badSourceType,
|
||||||
|
badSourceType.toString(), sinkType, sinkType.toString()
|
||||||
7
cpp/ql/src/Security/CWE/CWE-843/TypeConfusionBad.cpp
Normal file
7
cpp/ql/src/Security/CWE/CWE-843/TypeConfusionBad.cpp
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
void allocate_and_draw_bad() {
|
||||||
|
Shape* shape = new Circle;
|
||||||
|
// ...
|
||||||
|
// BAD: Assumes that shape is always a Square
|
||||||
|
Square* square = static_cast<Square*>(shape);
|
||||||
|
int length = square->getLength();
|
||||||
|
}
|
||||||
25
cpp/ql/src/Security/CWE/CWE-843/TypeConfusionCommon.cpp
Normal file
25
cpp/ql/src/Security/CWE/CWE-843/TypeConfusionCommon.cpp
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
struct Shape {
|
||||||
|
virtual ~Shape();
|
||||||
|
|
||||||
|
virtual void draw() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Circle : public Shape {
|
||||||
|
Circle();
|
||||||
|
|
||||||
|
void draw() override {
|
||||||
|
/* ... */
|
||||||
|
}
|
||||||
|
|
||||||
|
int getRadius();
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Square : public Shape {
|
||||||
|
Square();
|
||||||
|
|
||||||
|
void draw() override {
|
||||||
|
/* ... */
|
||||||
|
}
|
||||||
|
|
||||||
|
int getLength();
|
||||||
|
};
|
||||||
11
cpp/ql/src/Security/CWE/CWE-843/TypeConfusionGood.cpp
Normal file
11
cpp/ql/src/Security/CWE/CWE-843/TypeConfusionGood.cpp
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
void allocate_and_draw_good() {
|
||||||
|
Shape* shape = new Circle;
|
||||||
|
// ...
|
||||||
|
// GOOD: Dynamically checks if shape is a Square
|
||||||
|
Square* square = dynamic_cast<Square*>(shape);
|
||||||
|
if(square) {
|
||||||
|
int length = square->getLength();
|
||||||
|
} else {
|
||||||
|
// handle error
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
* @kind metric
|
* @kind metric
|
||||||
* @tags summary
|
* @tags summary
|
||||||
* lines-of-code
|
* lines-of-code
|
||||||
|
* debug
|
||||||
* @id cpp/summary/lines-of-user-code
|
* @id cpp/summary/lines-of-user-code
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
category: newQuery
|
||||||
|
---
|
||||||
|
* Added a new query, `cpp/type-confusion`, to detect casts to invalid types.
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
category: minorAnalysis
|
||||||
|
---
|
||||||
|
* Added models for `GLib` allocation and deallocation functions.
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
category: minorAnalysis
|
||||||
|
---
|
||||||
|
* The "Potentially uninitialized local variable" query (`cpp/uninitialized-local`) has been converted to a `path-problem` query.
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
category: minorAnalysis
|
||||||
|
---
|
||||||
|
* The "Missing return-value check for a 'scanf'-like function" query (`cpp/missing-check-scanf`) has been converted to a `path-problem` query.
|
||||||
4
cpp/ql/src/change-notes/2024-03-22-boost-ssl.md
Normal file
4
cpp/ql/src/change-notes/2024-03-22-boost-ssl.md
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
category: queryMetadata
|
||||||
|
---
|
||||||
|
* `@precision medium` metadata was added to the `cpp/boost/tls-settings-misconfiguration` and `cpp/boost/use-of-deprecated-hardcoded-security-protocol` queries, and these queries are now included in the security-extended suite. The `@name` metadata of these queries were also updated.
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
<!DOCTYPE qhelp PUBLIC
|
||||||
|
"-//Semmle//qhelp//EN"
|
||||||
|
"qhelp.dtd">
|
||||||
|
<qhelp>
|
||||||
|
<overview>
|
||||||
|
<p>
|
||||||
|
Using an iterator owned by a container after the lifetime of the container has expired can lead to undefined behavior.
|
||||||
|
This is because the iterator may be invalidated when the container is destroyed, and dereferencing an invalidated iterator is undefined behavior.
|
||||||
|
These problems can be hard to spot due to C++'s complex rules for temporary object lifetimes and their extensions.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</overview>
|
||||||
|
<recommendation>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Never create an iterator to a temporary container when the iterator is expected to be used after the container's lifetime has expired.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</recommendation>
|
||||||
|
<example>
|
||||||
|
<p>
|
||||||
|
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
The rules for lifetime extension ensures that the code in <code>lifetime_of_temp_extended</code> is well-defined. This is because the
|
||||||
|
lifetime of the temporary container returned by <code>get_vector</code> is extended to the end of the loop. However, prior to C++23,
|
||||||
|
the lifetime extension rules do not ensure that the container returned by <code>get_vector</code> is extended in <code>lifetime_of_temp_not_extended</code>.
|
||||||
|
This is because the temporary container is not bound to a rvalue reference.
|
||||||
|
</p>
|
||||||
|
<sample src="IteratorToExpiredContainerExtendedLifetime.cpp" />
|
||||||
|
|
||||||
|
</example>
|
||||||
|
<references>
|
||||||
|
|
||||||
|
<li>CERT C Coding Standard:
|
||||||
|
<a href="https://wiki.sei.cmu.edu/confluence/display/c/MEM30-C.+Do+not+access+freed+memory">MEM30-C. Do not access freed memory</a>.</li>
|
||||||
|
<li>
|
||||||
|
OWASP:
|
||||||
|
<a href="https://owasp.org/www-community/vulnerabilities/Using_freed_memory">Using freed memory</a>.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="https://github.com/isocpp/CppCoreGuidelines/blob/master/docs/Lifetime.pdf">Lifetime safety: Preventing common dangling</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="https://en.cppreference.com/w/cpp/container">Containers library</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="https://en.cppreference.com/w/cpp/language/range-for">Range-based for loop (since C++11)</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
</references>
|
||||||
|
</qhelp>
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
/**
|
||||||
|
* @name Iterator to expired container
|
||||||
|
* @description Using an iterator owned by a container whose lifetime has expired may lead to unexpected behavior.
|
||||||
|
* @kind problem
|
||||||
|
* @precision high
|
||||||
|
* @id cpp/iterator-to-expired-container
|
||||||
|
* @problem.severity warning
|
||||||
|
* @tags reliability
|
||||||
|
* security
|
||||||
|
* external/cwe/cwe-416
|
||||||
|
* external/cwe/cwe-664
|
||||||
|
*/
|
||||||
|
|
||||||
|
// IMPORTANT: This query does not currently find anything since it relies on extractor and analysis improvements that hasn't yet been released
|
||||||
|
import cpp
|
||||||
|
import semmle.code.cpp.ir.IR
|
||||||
|
import semmle.code.cpp.dataflow.new.DataFlow
|
||||||
|
import semmle.code.cpp.models.implementations.StdContainer
|
||||||
|
import semmle.code.cpp.models.implementations.StdMap
|
||||||
|
import semmle.code.cpp.models.implementations.Iterator
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A configuration to track flow from a temporary variable to the qualifier of
|
||||||
|
* a destructor call
|
||||||
|
*/
|
||||||
|
module TempToDestructorConfig implements DataFlow::ConfigSig {
|
||||||
|
predicate isSource(DataFlow::Node source) {
|
||||||
|
source.asInstruction().(VariableAddressInstruction).getIRVariable() instanceof IRTempVariable
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate isSink(DataFlow::Node sink) {
|
||||||
|
sink.asOperand().(ThisArgumentOperand).getCall().getStaticCallTarget() instanceof Destructor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module TempToDestructorFlow = DataFlow::Global<TempToDestructorConfig>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a `DataFlow::Node` that represents a temporary that will be destroyed
|
||||||
|
* by a call to a destructor, or a `DataFlow::Node` that will transitively be
|
||||||
|
* destroyed by a call to a destructor.
|
||||||
|
*
|
||||||
|
* For the latter case, consider something like:
|
||||||
|
* ```
|
||||||
|
* std::vector<std::vector<int>> get_2d_vector();
|
||||||
|
* auto& v = get_2d_vector()[0];
|
||||||
|
* ```
|
||||||
|
* Given the above, this predicate returns the node corresponding
|
||||||
|
* to `get_2d_vector()[0]` since the temporary `get_2d_vector()` gets
|
||||||
|
* destroyed by a call to `std::vector<std::vector<int>>::~vector`,
|
||||||
|
* and thus the result of `get_2d_vector()[0]` is also an invalid reference.
|
||||||
|
*/
|
||||||
|
DataFlow::Node getADestroyedNode() {
|
||||||
|
exists(TempToDestructorFlow::PathNode destroyedTemp | destroyedTemp.isSource() |
|
||||||
|
result = destroyedTemp.getNode()
|
||||||
|
or
|
||||||
|
exists(CallInstruction call |
|
||||||
|
result.asInstruction() = call and
|
||||||
|
DataFlow::localFlow(destroyedTemp.getNode(),
|
||||||
|
DataFlow::operandNode(call.getThisArgumentOperand()))
|
||||||
|
|
|
||||||
|
call.getStaticCallTarget() instanceof StdSequenceContainerAt or
|
||||||
|
call.getStaticCallTarget() instanceof StdMapAt
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate isSinkImpl(DataFlow::Node sink, FunctionCall fc) {
|
||||||
|
exists(CallInstruction call |
|
||||||
|
call = sink.asOperand().(ThisArgumentOperand).getCall() and
|
||||||
|
fc = call.getUnconvertedResultExpression() and
|
||||||
|
call.getStaticCallTarget() instanceof BeginOrEndFunction
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flow from any destroyed object to the qualifier of a `begin` or `end` call
|
||||||
|
*/
|
||||||
|
module DestroyedToBeginConfig implements DataFlow::ConfigSig {
|
||||||
|
predicate isSource(DataFlow::Node source) { source = getADestroyedNode() }
|
||||||
|
|
||||||
|
predicate isSink(DataFlow::Node sink) { isSinkImpl(sink, _) }
|
||||||
|
|
||||||
|
DataFlow::FlowFeature getAFeature() {
|
||||||
|
// By blocking argument-to-parameter flow we ensure that we don't enter a
|
||||||
|
// function body where the temporary outlives anything inside the function.
|
||||||
|
// This prevents false positives in cases like:
|
||||||
|
// ```cpp
|
||||||
|
// void foo(const std::vector<int>& v) {
|
||||||
|
// for(auto x : v) { ... } // this is fine since v outlives the loop
|
||||||
|
// }
|
||||||
|
// ...
|
||||||
|
// foo(create_temporary())
|
||||||
|
// ```
|
||||||
|
result instanceof DataFlow::FeatureHasSinkCallContext
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module DestroyedToBeginFlow = DataFlow::Global<DestroyedToBeginConfig>;
|
||||||
|
|
||||||
|
from DataFlow::Node source, DataFlow::Node sink, FunctionCall beginOrEnd
|
||||||
|
where DestroyedToBeginFlow::flow(source, sink) and isSinkImpl(sink, beginOrEnd)
|
||||||
|
select source, "This object is destroyed before $@ is called.", beginOrEnd, beginOrEnd.toString()
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
#include <vector>
|
||||||
|
|
||||||
|
std::vector<int> get_vector();
|
||||||
|
|
||||||
|
void use(int);
|
||||||
|
|
||||||
|
void lifetime_of_temp_extended() {
|
||||||
|
for(auto x : get_vector()) {
|
||||||
|
use(x); // GOOD: The lifetime of the vector returned by `get_vector()` is extended until the end of the loop.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Writes the the values of `v` to an external log and returns it unchanged.
|
||||||
|
const std::vector<int>& log_and_return_argument(const std::vector<int>& v);
|
||||||
|
|
||||||
|
void lifetime_of_temp_not_extended() {
|
||||||
|
for(auto x : log_and_return_argument(get_vector())) {
|
||||||
|
use(x); // BAD: The lifetime of the vector returned by `get_vector()` is not extended, and the behavior is undefined.
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
name: codeql/cpp-queries
|
name: codeql/cpp-queries
|
||||||
version: 0.9.7
|
version: 0.9.8-dev
|
||||||
groups:
|
groups:
|
||||||
- cpp
|
- cpp
|
||||||
- queries
|
- queries
|
||||||
|
|||||||
@@ -105,10 +105,24 @@ ConditionDecl.cpp:
|
|||||||
# 3| getVariableAccess(): [VariableAccess] k
|
# 3| getVariableAccess(): [VariableAccess] k
|
||||||
# 3| Type = [IntType] int
|
# 3| Type = [IntType] int
|
||||||
# 3| ValueCategory = prvalue(load)
|
# 3| ValueCategory = prvalue(load)
|
||||||
|
# 3| getInitializingExpr(): [LTExpr] ... < ...
|
||||||
|
# 3| Type = [BoolType] bool
|
||||||
|
# 3| ValueCategory = prvalue
|
||||||
|
# 3| getLesserOperand(): [VariableAccess] j
|
||||||
|
# 3| Type = [IntType] int
|
||||||
|
# 3| ValueCategory = prvalue(load)
|
||||||
|
# 3| getGreaterOperand(): [Literal] 5
|
||||||
|
# 3| Type = [IntType] int
|
||||||
|
# 3| Value = [Literal] 5
|
||||||
|
# 3| ValueCategory = prvalue
|
||||||
# 3| getVariableAccess().getFullyConverted(): [CStyleCast] (bool)...
|
# 3| getVariableAccess().getFullyConverted(): [CStyleCast] (bool)...
|
||||||
# 3| Conversion = [BoolConversion] conversion to bool
|
# 3| Conversion = [BoolConversion] conversion to bool
|
||||||
# 3| Type = [BoolType] bool
|
# 3| Type = [BoolType] bool
|
||||||
# 3| ValueCategory = prvalue
|
# 3| ValueCategory = prvalue
|
||||||
|
# 3| getInitializingExpr().getFullyConverted(): [CStyleCast] (int)...
|
||||||
|
# 3| Conversion = [IntegralConversion] integral conversion
|
||||||
|
# 3| Type = [IntType] int
|
||||||
|
# 3| ValueCategory = prvalue
|
||||||
# 3| getStmt(): [BlockStmt] { ... }
|
# 3| getStmt(): [BlockStmt] { ... }
|
||||||
# 5| getStmt(2): [ReturnStmt] return ...
|
# 5| getStmt(2): [ReturnStmt] return ...
|
||||||
ConstructorCall.cpp:
|
ConstructorCall.cpp:
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
experimental/Security/CWE/CWE-416/IteratorToExpiredContainer.ql
|
||||||
@@ -0,0 +1,768 @@
|
|||||||
|
|
||||||
|
typedef unsigned long size_t;
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
struct remove_const { typedef T type; };
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
struct remove_const<const T> { typedef T type; };
|
||||||
|
|
||||||
|
// `remove_const_t<T>` removes any `const` specifier from `T`
|
||||||
|
template<class T>
|
||||||
|
using remove_const_t = typename remove_const<T>::type;
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
struct remove_reference { typedef T type; };
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
struct remove_reference<T &> { typedef T type; };
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
struct remove_reference<T &&> { typedef T type; };
|
||||||
|
|
||||||
|
// `remove_reference_t<T>` removes any `&` from `T`
|
||||||
|
template<class T>
|
||||||
|
using remove_reference_t = typename remove_reference<T>::type;
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
struct decay_impl {
|
||||||
|
typedef T type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T, size_t t_size>
|
||||||
|
struct decay_impl<T[t_size]> {
|
||||||
|
typedef T* type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
using decay_t = typename decay_impl<remove_reference_t<T>>::type;
|
||||||
|
|
||||||
|
|
||||||
|
namespace std
|
||||||
|
{
|
||||||
|
template<class T> constexpr T&& forward(remove_reference_t<T>& t) noexcept;
|
||||||
|
template<class T> constexpr T&& forward(remove_reference_t<T>&& t) noexcept;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- iterator ---
|
||||||
|
|
||||||
|
namespace std {
|
||||||
|
struct ptrdiff_t;
|
||||||
|
|
||||||
|
template<class I> struct iterator_traits;
|
||||||
|
|
||||||
|
template <class Category,
|
||||||
|
class value_type,
|
||||||
|
class difference_type = ptrdiff_t,
|
||||||
|
class pointer_type = value_type*,
|
||||||
|
class reference_type = value_type&>
|
||||||
|
struct iterator {
|
||||||
|
typedef Category iterator_category;
|
||||||
|
|
||||||
|
iterator();
|
||||||
|
iterator(iterator<Category, remove_const_t<value_type> > const &other); // non-const -> const conversion constructor
|
||||||
|
|
||||||
|
iterator &operator++();
|
||||||
|
iterator operator++(int);
|
||||||
|
iterator &operator--();
|
||||||
|
iterator operator--(int);
|
||||||
|
bool operator==(iterator other) const;
|
||||||
|
bool operator!=(iterator other) const;
|
||||||
|
reference_type operator*() const;
|
||||||
|
pointer_type operator->() const;
|
||||||
|
iterator operator+(int);
|
||||||
|
iterator operator-(int);
|
||||||
|
iterator &operator+=(int);
|
||||||
|
iterator &operator-=(int);
|
||||||
|
int operator-(iterator);
|
||||||
|
reference_type operator[](int);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct input_iterator_tag {};
|
||||||
|
struct forward_iterator_tag : public input_iterator_tag {};
|
||||||
|
struct bidirectional_iterator_tag : public forward_iterator_tag {};
|
||||||
|
struct random_access_iterator_tag : public bidirectional_iterator_tag {};
|
||||||
|
|
||||||
|
struct output_iterator_tag {};
|
||||||
|
|
||||||
|
template<class Container>
|
||||||
|
class back_insert_iterator {
|
||||||
|
protected:
|
||||||
|
Container* container = nullptr;
|
||||||
|
public:
|
||||||
|
using iterator_category = output_iterator_tag;
|
||||||
|
using value_type = void;
|
||||||
|
using difference_type = ptrdiff_t;
|
||||||
|
using pointer = void;
|
||||||
|
using reference = void;
|
||||||
|
using container_type = Container;
|
||||||
|
constexpr back_insert_iterator() noexcept = default;
|
||||||
|
constexpr explicit back_insert_iterator(Container& x);
|
||||||
|
back_insert_iterator& operator=(const typename Container::value_type& value);
|
||||||
|
back_insert_iterator& operator=(typename Container::value_type&& value);
|
||||||
|
back_insert_iterator& operator*();
|
||||||
|
back_insert_iterator& operator++();
|
||||||
|
back_insert_iterator operator++(int);
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class Container>
|
||||||
|
constexpr back_insert_iterator<Container> back_inserter(Container& x) {
|
||||||
|
return back_insert_iterator<Container>(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Container>
|
||||||
|
class front_insert_iterator {
|
||||||
|
protected:
|
||||||
|
Container* container = nullptr;
|
||||||
|
public:
|
||||||
|
using iterator_category = output_iterator_tag;
|
||||||
|
using value_type = void;
|
||||||
|
using difference_type = ptrdiff_t;
|
||||||
|
using pointer = void;
|
||||||
|
using reference = void;
|
||||||
|
using container_type = Container;
|
||||||
|
constexpr front_insert_iterator() noexcept = default;
|
||||||
|
constexpr explicit front_insert_iterator(Container& x);
|
||||||
|
constexpr front_insert_iterator& operator=(const typename Container::value_type& value);
|
||||||
|
constexpr front_insert_iterator& operator=(typename Container::value_type&& value);
|
||||||
|
constexpr front_insert_iterator& operator*();
|
||||||
|
constexpr front_insert_iterator& operator++();
|
||||||
|
constexpr front_insert_iterator operator++(int);
|
||||||
|
};
|
||||||
|
template<class Container>
|
||||||
|
constexpr front_insert_iterator<Container> front_inserter(Container& x) {
|
||||||
|
return front_insert_iterator<Container>(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- string ---
|
||||||
|
|
||||||
|
namespace std
|
||||||
|
{
|
||||||
|
template<class charT> struct char_traits;
|
||||||
|
|
||||||
|
typedef size_t streamsize;
|
||||||
|
|
||||||
|
template <class T> class allocator {
|
||||||
|
public:
|
||||||
|
allocator() throw();
|
||||||
|
typedef size_t size_type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class charT, class traits = char_traits<charT>, class Allocator = allocator<charT> >
|
||||||
|
class basic_string {
|
||||||
|
public:
|
||||||
|
using value_type = charT;
|
||||||
|
using reference = value_type&;
|
||||||
|
using const_reference = const value_type&;
|
||||||
|
typedef typename Allocator::size_type size_type;
|
||||||
|
static const size_type npos = -1;
|
||||||
|
|
||||||
|
explicit basic_string(const Allocator& a = Allocator());
|
||||||
|
basic_string(const charT* s, const Allocator& a = Allocator());
|
||||||
|
template<class InputIterator> basic_string(InputIterator begin, InputIterator end, const Allocator& a = Allocator());
|
||||||
|
|
||||||
|
const charT* c_str() const;
|
||||||
|
charT* data() noexcept;
|
||||||
|
size_t length() const;
|
||||||
|
|
||||||
|
typedef std::iterator<random_access_iterator_tag, charT> iterator;
|
||||||
|
typedef std::iterator<random_access_iterator_tag, const charT> const_iterator;
|
||||||
|
|
||||||
|
iterator begin();
|
||||||
|
iterator end();
|
||||||
|
const_iterator begin() const;
|
||||||
|
const_iterator end() const;
|
||||||
|
const_iterator cbegin() const;
|
||||||
|
const_iterator cend() const;
|
||||||
|
|
||||||
|
void push_back(charT c);
|
||||||
|
|
||||||
|
const charT& front() const;
|
||||||
|
charT& front();
|
||||||
|
const charT& back() const;
|
||||||
|
charT& back();
|
||||||
|
|
||||||
|
const_reference operator[](size_type pos) const;
|
||||||
|
reference operator[](size_type pos);
|
||||||
|
const_reference at(size_type n) const;
|
||||||
|
reference at(size_type n);
|
||||||
|
template<class T> basic_string& operator+=(const T& t);
|
||||||
|
basic_string& operator+=(const charT* s);
|
||||||
|
basic_string& append(const basic_string& str);
|
||||||
|
basic_string& append(const charT* s);
|
||||||
|
basic_string& append(size_type n, charT c);
|
||||||
|
template<class InputIterator> basic_string& append(InputIterator first, InputIterator last);
|
||||||
|
basic_string& assign(const basic_string& str);
|
||||||
|
basic_string& assign(size_type n, charT c);
|
||||||
|
template<class InputIterator> basic_string& assign(InputIterator first, InputIterator last);
|
||||||
|
basic_string& insert(size_type pos, const basic_string& str);
|
||||||
|
basic_string& insert(size_type pos, size_type n, charT c);
|
||||||
|
basic_string& insert(size_type pos, const charT* s);
|
||||||
|
iterator insert(const_iterator p, size_type n, charT c);
|
||||||
|
template<class InputIterator> iterator insert(const_iterator p, InputIterator first, InputIterator last);
|
||||||
|
basic_string& replace(size_type pos1, size_type n1, const basic_string& str);
|
||||||
|
basic_string& replace(size_type pos1, size_type n1, size_type n2, charT c);
|
||||||
|
size_type copy(charT* s, size_type n, size_type pos = 0) const;
|
||||||
|
void clear() noexcept;
|
||||||
|
basic_string substr(size_type pos = 0, size_type n = npos) const;
|
||||||
|
void swap(basic_string& s) noexcept/*(allocator_traits<Allocator>::propagate_on_container_swap::value || allocator_traits<Allocator>::is_always_equal::value)*/;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class charT, class traits, class Allocator> basic_string<charT, traits, Allocator> operator+(const basic_string<charT, traits, Allocator>& lhs, const basic_string<charT, traits, Allocator>& rhs);
|
||||||
|
template<class charT, class traits, class Allocator> basic_string<charT, traits, Allocator> operator+(const basic_string<charT, traits, Allocator>& lhs, const charT* rhs);
|
||||||
|
|
||||||
|
typedef basic_string<char> string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- istring / ostream / stringstream ---
|
||||||
|
|
||||||
|
namespace std
|
||||||
|
{
|
||||||
|
template <class charT, class traits = char_traits<charT> >
|
||||||
|
class basic_istream /*: virtual public basic_ios<charT,traits> - not needed for this test */ {
|
||||||
|
public:
|
||||||
|
using char_type = charT;
|
||||||
|
using int_type = int; //typename traits::int_type;
|
||||||
|
|
||||||
|
basic_istream<charT, traits>& operator>>(int& n);
|
||||||
|
|
||||||
|
int_type get();
|
||||||
|
basic_istream<charT, traits>& get(char_type& c);
|
||||||
|
basic_istream<charT, traits>& get(char_type* s, streamsize n);
|
||||||
|
int_type peek();
|
||||||
|
basic_istream<charT, traits>& read (char_type* s, streamsize n);
|
||||||
|
streamsize readsome(char_type* s, streamsize n);
|
||||||
|
basic_istream<charT, traits>& putback(char_type c);
|
||||||
|
basic_istream<charT,traits>& unget();
|
||||||
|
|
||||||
|
basic_istream<charT,traits>& getline(char_type* s, streamsize n);
|
||||||
|
basic_istream<charT,traits>& getline(char_type* s, streamsize n, char_type delim);
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class charT, class traits> basic_istream<charT, traits>& operator>>(basic_istream<charT, traits>&, charT*);
|
||||||
|
template<class charT, class traits, class Allocator> basic_istream<charT, traits>& operator>>(basic_istream<charT, traits>& is, basic_string<charT, traits, Allocator>& str);
|
||||||
|
|
||||||
|
template<class charT, class traits, class Allocator> basic_istream<charT,traits>& getline(basic_istream<charT,traits>& is, basic_string<charT,traits,Allocator>& str, charT delim);
|
||||||
|
template<class charT, class traits, class Allocator> basic_istream<charT,traits>& getline(basic_istream<charT,traits>& is, basic_string<charT,traits,Allocator>& str);
|
||||||
|
|
||||||
|
template <class charT, class traits = char_traits<charT> >
|
||||||
|
class basic_ostream /*: virtual public basic_ios<charT,traits> - not needed for this test */ {
|
||||||
|
public:
|
||||||
|
typedef charT char_type;
|
||||||
|
|
||||||
|
basic_ostream<charT, traits>& operator<<(int n);
|
||||||
|
|
||||||
|
basic_ostream<charT, traits>& put(char_type c);
|
||||||
|
basic_ostream<charT, traits>& write(const char_type* s, streamsize n);
|
||||||
|
basic_ostream<charT,traits>& flush();
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class charT, class traits> basic_ostream<charT,traits>& operator<<(basic_ostream<charT,traits>&, const charT*);
|
||||||
|
template<class charT, class traits, class Allocator> basic_ostream<charT, traits>& operator<<(basic_ostream<charT, traits>& os, const basic_string<charT, traits, Allocator>& str);
|
||||||
|
|
||||||
|
template<class charT, class traits = char_traits<charT>>
|
||||||
|
class basic_iostream : public basic_istream<charT, traits>, public basic_ostream<charT, traits> {
|
||||||
|
public:
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class charT, class traits = char_traits<charT>, class Allocator = allocator<charT>>
|
||||||
|
class basic_stringstream : public basic_iostream<charT, traits> {
|
||||||
|
public:
|
||||||
|
explicit basic_stringstream(/*ios_base::openmode which = ios_base::out|ios_base::in - not needed for this test*/);
|
||||||
|
explicit basic_stringstream( const basic_string<charT, traits, Allocator>& str/*, ios_base::openmode which = ios_base::out | ios_base::in*/);
|
||||||
|
basic_stringstream(const basic_stringstream& rhs) = delete;
|
||||||
|
basic_stringstream(basic_stringstream&& rhs);
|
||||||
|
basic_stringstream& operator=(const basic_stringstream& rhs) = delete;
|
||||||
|
basic_stringstream& operator=(basic_stringstream&& rhs);
|
||||||
|
|
||||||
|
void swap(basic_stringstream& rhs);
|
||||||
|
|
||||||
|
basic_string<charT, traits, Allocator> str() const;
|
||||||
|
void str(const basic_string<charT, traits, Allocator>& str);
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef basic_istream<char> istream;
|
||||||
|
typedef basic_ostream<char> ostream;
|
||||||
|
extern istream cin;
|
||||||
|
extern ostream cout;
|
||||||
|
|
||||||
|
using stringstream = basic_stringstream<char>;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- vector ---
|
||||||
|
|
||||||
|
namespace std {
|
||||||
|
template<class T, class Allocator = allocator<T>>
|
||||||
|
class vector {
|
||||||
|
public:
|
||||||
|
using value_type = T;
|
||||||
|
using reference = value_type&;
|
||||||
|
using const_reference = const value_type&;
|
||||||
|
using size_type = unsigned int;
|
||||||
|
using iterator = std::iterator<random_access_iterator_tag, T>;
|
||||||
|
using const_iterator = std::iterator<random_access_iterator_tag, const T>;
|
||||||
|
|
||||||
|
vector() noexcept(noexcept(Allocator()));
|
||||||
|
vector(const std::vector<T, Allocator>&);
|
||||||
|
explicit vector(const Allocator&) noexcept;
|
||||||
|
explicit vector(size_type n, const Allocator& = Allocator());
|
||||||
|
vector(size_type n, const T& value, const Allocator& = Allocator());
|
||||||
|
template<class InputIterator, class IteratorCategory = typename InputIterator::iterator_category> vector(InputIterator first, InputIterator last, const Allocator& = Allocator());
|
||||||
|
// use of `iterator_category` makes sure InputIterator is (probably) an iterator, and not an `int` or
|
||||||
|
// similar that should match a different overload (SFINAE).
|
||||||
|
~vector();
|
||||||
|
|
||||||
|
vector& operator=(const vector& x);
|
||||||
|
vector& operator=(vector&& x) noexcept/*(allocator_traits<Allocator>::propagate_on_container_move_assignment::value || allocator_traits<Allocator>::is_always_equal::value)*/;
|
||||||
|
template<class InputIterator, class IteratorCategory = typename InputIterator::iterator_category> void assign(InputIterator first, InputIterator last);
|
||||||
|
// use of `iterator_category` makes sure InputIterator is (probably) an iterator, and not an `int` or
|
||||||
|
// similar that should match a different overload (SFINAE).
|
||||||
|
void assign(size_type n, const T& u);
|
||||||
|
|
||||||
|
iterator begin() noexcept;
|
||||||
|
const_iterator begin() const noexcept;
|
||||||
|
iterator end() noexcept;
|
||||||
|
const_iterator end() const noexcept;
|
||||||
|
|
||||||
|
size_type size() const noexcept;
|
||||||
|
|
||||||
|
reference operator[](size_type n);
|
||||||
|
const_reference operator[](size_type n) const;
|
||||||
|
const_reference at(size_type n) const;
|
||||||
|
reference at(size_type n);
|
||||||
|
reference front();
|
||||||
|
const_reference front() const;
|
||||||
|
reference back();
|
||||||
|
const_reference back() const;
|
||||||
|
|
||||||
|
T* data() noexcept;
|
||||||
|
const T* data() const noexcept;
|
||||||
|
|
||||||
|
void push_back(const T& x);
|
||||||
|
void push_back(T&& x);
|
||||||
|
|
||||||
|
iterator insert(const_iterator position, const T& x);
|
||||||
|
iterator insert(const_iterator position, T&& x);
|
||||||
|
iterator insert(const_iterator position, size_type n, const T& x);
|
||||||
|
template<class InputIterator> iterator insert(const_iterator position, InputIterator first, InputIterator last);
|
||||||
|
|
||||||
|
template <class... Args> iterator emplace (const_iterator position, Args&&... args);
|
||||||
|
template <class... Args> void emplace_back (Args&&... args);
|
||||||
|
|
||||||
|
void swap(vector&) noexcept/*(allocator_traits<Allocator>::propagate_on_container_swap::value || allocator_traits<Allocator>::is_always_equal::value)*/;
|
||||||
|
|
||||||
|
void clear() noexcept;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- make_shared / make_unique ---
|
||||||
|
|
||||||
|
namespace std {
|
||||||
|
template<typename T>
|
||||||
|
class shared_ptr {
|
||||||
|
public:
|
||||||
|
shared_ptr() noexcept;
|
||||||
|
explicit shared_ptr(T*);
|
||||||
|
shared_ptr(const shared_ptr&) noexcept;
|
||||||
|
template<class U> shared_ptr(const shared_ptr<U>&) noexcept;
|
||||||
|
template<class U> shared_ptr(shared_ptr<U>&&) noexcept;
|
||||||
|
|
||||||
|
shared_ptr<T>& operator=(const shared_ptr<T>&) noexcept;
|
||||||
|
shared_ptr<T>& operator=(shared_ptr<T>&&) noexcept;
|
||||||
|
|
||||||
|
T& operator*() const noexcept;
|
||||||
|
T* operator->() const noexcept;
|
||||||
|
|
||||||
|
T* get() const noexcept;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
class unique_ptr {
|
||||||
|
public:
|
||||||
|
constexpr unique_ptr() noexcept;
|
||||||
|
explicit unique_ptr(T*) noexcept;
|
||||||
|
unique_ptr(unique_ptr<T>&&) noexcept;
|
||||||
|
|
||||||
|
unique_ptr<T>& operator=(unique_ptr<T>&&) noexcept;
|
||||||
|
|
||||||
|
T& operator*() const;
|
||||||
|
T* operator->() const noexcept;
|
||||||
|
|
||||||
|
T* get() const noexcept;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T, class... Args> unique_ptr<T> make_unique(Args&&...);
|
||||||
|
|
||||||
|
template<typename T, class... Args> shared_ptr<T> make_shared(Args&&...);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- pair ---
|
||||||
|
|
||||||
|
namespace std {
|
||||||
|
template <class T1, class T2>
|
||||||
|
struct pair {
|
||||||
|
typedef T1 first_type;
|
||||||
|
typedef T2 second_type;
|
||||||
|
|
||||||
|
T1 first;
|
||||||
|
T2 second;
|
||||||
|
pair();
|
||||||
|
pair(const T1& x, const T2& y);
|
||||||
|
template<class U, class V> pair(const pair<U, V> &p);
|
||||||
|
|
||||||
|
void swap(pair& p) /*noexcept(...)*/;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T1, class T2> constexpr pair<decay_t<T1>, decay_t<T2>> make_pair(T1&& x, T2&& y) {
|
||||||
|
return pair<decay_t<T1>, decay_t<T2>>(std::forward<T1>(x), std::forward<T2>(y));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- map ---
|
||||||
|
|
||||||
|
namespace std {
|
||||||
|
template<class T = void> struct less;
|
||||||
|
|
||||||
|
template<class Key, class T, class Compare = less<Key>, class Allocator = allocator<pair<const Key, T>>>
|
||||||
|
class map {
|
||||||
|
public:
|
||||||
|
using key_type = Key;
|
||||||
|
using mapped_type = T;
|
||||||
|
using value_type = pair<const Key, T>;
|
||||||
|
using iterator = std::iterator<random_access_iterator_tag, value_type >;
|
||||||
|
using const_iterator = std::iterator<random_access_iterator_tag, const value_type >;
|
||||||
|
|
||||||
|
map();
|
||||||
|
map(const map& x);
|
||||||
|
map(map&& x);
|
||||||
|
~map();
|
||||||
|
|
||||||
|
map& operator=(const map& x);
|
||||||
|
map& operator=(map&& x) /*noexcept(allocator_traits<Allocator>::is_always_equal::value && is_nothrow_move_assignable_v<Compare>)*/;
|
||||||
|
|
||||||
|
iterator begin() noexcept;
|
||||||
|
const_iterator begin() const noexcept;
|
||||||
|
iterator end() noexcept;
|
||||||
|
const_iterator end() const noexcept;
|
||||||
|
|
||||||
|
T& operator[](const key_type& x);
|
||||||
|
T& operator[](key_type&& x);
|
||||||
|
T& at(const key_type& x);
|
||||||
|
const T& at(const key_type& x) const;
|
||||||
|
|
||||||
|
template<class... Args> pair<iterator, bool> emplace(Args&&... args);
|
||||||
|
template<class... Args> iterator emplace_hint(const_iterator position, Args&&... args);
|
||||||
|
|
||||||
|
pair<iterator, bool> insert(const value_type& x);
|
||||||
|
pair<iterator, bool> insert(value_type&& x);
|
||||||
|
iterator insert(const_iterator position, const value_type& x);
|
||||||
|
iterator insert(const_iterator position, value_type&& x);
|
||||||
|
|
||||||
|
template<class... Args> pair<iterator, bool> try_emplace(const key_type& k, Args&&... args);
|
||||||
|
template<class... Args> pair<iterator, bool> try_emplace(key_type&& k, Args&&... args);
|
||||||
|
template<class... Args> iterator try_emplace(const_iterator hint, const key_type& k, Args&&... args);
|
||||||
|
template<class... Args> iterator try_emplace(const_iterator hint, key_type&& k, Args&&... args);
|
||||||
|
template<class M> pair<iterator, bool> insert_or_assign(const key_type& k, M&& obj);
|
||||||
|
template<class M> pair<iterator, bool> insert_or_assign(key_type&& k, M&& obj);
|
||||||
|
template<class M> iterator insert_or_assign(const_iterator hint, const key_type& k, M&& obj);
|
||||||
|
template<class M> iterator insert_or_assign(const_iterator hint, key_type&& k, M&& obj);
|
||||||
|
|
||||||
|
iterator erase(iterator position);
|
||||||
|
iterator erase(const_iterator position);
|
||||||
|
iterator erase(const_iterator first, const_iterator last);
|
||||||
|
void swap(map&) /*noexcept(/*==allocator_traits<Allocator>::is_always_equal::value && is_nothrow_swappable_v<Compare>)*/;
|
||||||
|
void clear() noexcept;
|
||||||
|
|
||||||
|
template<class C2> void merge(map<Key, T, C2, Allocator>& source);
|
||||||
|
template<class C2> void merge(map<Key, T, C2, Allocator>&& source);
|
||||||
|
|
||||||
|
iterator find(const key_type& x);
|
||||||
|
const_iterator find(const key_type& x) const;
|
||||||
|
|
||||||
|
iterator lower_bound(const key_type& x);
|
||||||
|
const_iterator lower_bound(const key_type& x) const;
|
||||||
|
iterator upper_bound(const key_type& x);
|
||||||
|
const_iterator upper_bound(const key_type& x) const;
|
||||||
|
|
||||||
|
pair<iterator, iterator> equal_range(const key_type& x);
|
||||||
|
pair<const_iterator, const_iterator> equal_range(const key_type& x) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T> struct hash;
|
||||||
|
template<class T = void> struct equal_to;
|
||||||
|
|
||||||
|
template<class Key, class T, class Hash = hash<Key>, class Pred = equal_to<Key>, class Allocator = allocator<pair<const Key, T>>>
|
||||||
|
class unordered_map {
|
||||||
|
public:
|
||||||
|
using key_type = Key;
|
||||||
|
using mapped_type = T;
|
||||||
|
using value_type = pair<const Key, T>;
|
||||||
|
using iterator = std::iterator<random_access_iterator_tag, value_type >;
|
||||||
|
using const_iterator = std::iterator<random_access_iterator_tag, const value_type >;
|
||||||
|
|
||||||
|
unordered_map();
|
||||||
|
unordered_map(const unordered_map&);
|
||||||
|
unordered_map(unordered_map&&);
|
||||||
|
~unordered_map();
|
||||||
|
|
||||||
|
unordered_map& operator=(const unordered_map&);
|
||||||
|
unordered_map& operator=(unordered_map&&) /*noexcept(allocator_traits<Allocator>::is_always_equal::value && is_nothrow_move_assignable_v<Hash> && is_nothrow_move_assignable_v<Pred>)*/;
|
||||||
|
|
||||||
|
iterator begin() noexcept;
|
||||||
|
const_iterator begin() const noexcept;
|
||||||
|
iterator end() noexcept;
|
||||||
|
const_iterator end() const noexcept;
|
||||||
|
|
||||||
|
mapped_type& operator[](const key_type& k);
|
||||||
|
mapped_type& operator[](key_type&& k);
|
||||||
|
mapped_type& at(const key_type& k);
|
||||||
|
const mapped_type& at(const key_type& k) const;
|
||||||
|
|
||||||
|
template<class... Args> pair<iterator, bool> emplace(Args&&... args);
|
||||||
|
template<class... Args> iterator emplace_hint(const_iterator position, Args&&... args);
|
||||||
|
|
||||||
|
pair<iterator, bool> insert(const value_type& obj);
|
||||||
|
pair<iterator, bool> insert(value_type&& obj);
|
||||||
|
iterator insert(const_iterator hint, const value_type& obj);
|
||||||
|
iterator insert(const_iterator hint, value_type&& obj);
|
||||||
|
|
||||||
|
template<class... Args> pair<iterator, bool> try_emplace(const key_type& k, Args&&... args);
|
||||||
|
template<class... Args> pair<iterator, bool> try_emplace(key_type&& k, Args&&... args);
|
||||||
|
template<class... Args> iterator try_emplace(const_iterator hint, const key_type& k, Args&&... args);
|
||||||
|
template<class... Args> iterator try_emplace(const_iterator hint, key_type&& k, Args&&... args);
|
||||||
|
template<class M> pair<iterator, bool> insert_or_assign(const key_type& k, M&& obj);
|
||||||
|
template<class M> pair<iterator, bool> insert_or_assign(key_type&& k, M&& obj);
|
||||||
|
template<class M> iterator insert_or_assign(const_iterator hint, const key_type& k, M&& obj);
|
||||||
|
template<class M> iterator insert_or_assign(const_iterator hint, key_type&& k, M&& obj);
|
||||||
|
|
||||||
|
iterator erase(iterator position);
|
||||||
|
iterator erase(const_iterator position);
|
||||||
|
iterator erase(const_iterator first, const_iterator last);
|
||||||
|
void swap(unordered_map&) /*noexcept(allocator_traits<Allocator>::is_always_equal::value && is_nothrow_swappable_v<Hash> && is_nothrow_swappable_v<Pred>)*/;
|
||||||
|
void clear() noexcept;
|
||||||
|
|
||||||
|
template<class H2, class P2> void merge(unordered_map<Key, T, H2, P2, Allocator>& source);
|
||||||
|
template<class H2, class P2> void merge(unordered_map<Key, T, H2, P2, Allocator>&& source);
|
||||||
|
|
||||||
|
iterator find(const key_type& k);
|
||||||
|
const_iterator find(const key_type& k) const;
|
||||||
|
|
||||||
|
pair<iterator, iterator> equal_range(const key_type& k);
|
||||||
|
pair<const_iterator, const_iterator> equal_range(const key_type& k) const;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// --- set ---
|
||||||
|
|
||||||
|
namespace std {
|
||||||
|
template<class Key, class Compare = less<Key>, class Allocator = allocator<Key>>
|
||||||
|
class set {
|
||||||
|
public:
|
||||||
|
using key_type = Key;
|
||||||
|
using value_type = Key;
|
||||||
|
using size_type = size_t;
|
||||||
|
using allocator_type = Allocator;
|
||||||
|
using iterator = std::iterator<random_access_iterator_tag, value_type >;
|
||||||
|
using const_iterator = std::iterator<random_access_iterator_tag, const value_type >;
|
||||||
|
|
||||||
|
set();
|
||||||
|
set(const set& x);
|
||||||
|
set(set&& x);
|
||||||
|
template<class InputIterator> set(InputIterator first, InputIterator last/*, const Compare& comp = Compare(), const Allocator& = Allocator()*/);
|
||||||
|
~set();
|
||||||
|
|
||||||
|
set& operator=(const set& x);
|
||||||
|
set& operator=(set&& x) noexcept/*(allocator_traits<Allocator>::is_always_equal::value && is_nothrow_move_assignable_v<Compare>)*/;
|
||||||
|
|
||||||
|
iterator begin() noexcept;
|
||||||
|
const_iterator begin() const noexcept;
|
||||||
|
iterator end() noexcept;
|
||||||
|
const_iterator end() const noexcept;
|
||||||
|
|
||||||
|
template<class... Args> pair<iterator, bool> emplace(Args&&... args);
|
||||||
|
template<class... Args> iterator emplace_hint(const_iterator position, Args&&... args);
|
||||||
|
pair<iterator,bool> insert(const value_type& x);
|
||||||
|
pair<iterator,bool> insert(value_type&& x);
|
||||||
|
iterator insert(const_iterator position, const value_type& x);
|
||||||
|
iterator insert(const_iterator position, value_type&& x);
|
||||||
|
template<class InputIterator> void insert(InputIterator first, InputIterator last);
|
||||||
|
|
||||||
|
iterator erase(iterator position);
|
||||||
|
iterator erase(const_iterator position);
|
||||||
|
iterator erase(const_iterator first, const_iterator last);
|
||||||
|
void swap(set&) noexcept/*(allocator_traits<Allocator>::is_always_equal::value && is_nothrow_swappable_v<Compare>)*/;
|
||||||
|
void clear() noexcept;
|
||||||
|
|
||||||
|
template<class C2> void merge(set<Key, C2, Allocator>& source);
|
||||||
|
template<class C2> void merge(set<Key, C2, Allocator>&& source);
|
||||||
|
|
||||||
|
iterator find(const key_type& x);
|
||||||
|
const_iterator find(const key_type& x) const;
|
||||||
|
|
||||||
|
iterator lower_bound(const key_type& x);
|
||||||
|
const_iterator lower_bound(const key_type& x) const;
|
||||||
|
iterator upper_bound(const key_type& x);
|
||||||
|
const_iterator upper_bound(const key_type& x) const;
|
||||||
|
pair<iterator, iterator> equal_range(const key_type& x);
|
||||||
|
pair<const_iterator, const_iterator> equal_range(const key_type& x) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class Key, class Hash = hash<Key>, class Pred = equal_to<Key>, class Allocator = allocator<Key>>
|
||||||
|
class unordered_set {
|
||||||
|
public:
|
||||||
|
using key_type = Key;
|
||||||
|
using value_type = Key;
|
||||||
|
using hasher = Hash;
|
||||||
|
using key_equal = Pred;
|
||||||
|
using allocator_type = Allocator;
|
||||||
|
using size_type = size_t;
|
||||||
|
using iterator = std::iterator<random_access_iterator_tag, value_type >;
|
||||||
|
using const_iterator = std::iterator<random_access_iterator_tag, const value_type >;
|
||||||
|
|
||||||
|
unordered_set();
|
||||||
|
unordered_set(const unordered_set&);
|
||||||
|
unordered_set(unordered_set&&);
|
||||||
|
template<class InputIterator> unordered_set(InputIterator f, InputIterator l, size_type n = 0/*, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type()*/);
|
||||||
|
~unordered_set();
|
||||||
|
|
||||||
|
unordered_set& operator=(const unordered_set&);
|
||||||
|
unordered_set& operator=(unordered_set&&) noexcept/*(allocator_traits<Allocator>::is_always_equal::value && is_nothrow_move_assignable_v<Hash> && is_nothrow_move_assignable_v<Pred>)*/;
|
||||||
|
|
||||||
|
iterator begin() noexcept;
|
||||||
|
const_iterator begin() const noexcept;
|
||||||
|
iterator end() noexcept;
|
||||||
|
const_iterator end() const noexcept;
|
||||||
|
|
||||||
|
template<class... Args> pair<iterator, bool> emplace(Args&&... args);
|
||||||
|
template<class... Args> iterator emplace_hint(const_iterator position, Args&&... args);
|
||||||
|
pair<iterator, bool> insert(const value_type& obj);
|
||||||
|
pair<iterator, bool> insert(value_type&& obj);
|
||||||
|
iterator insert(const_iterator hint, const value_type& obj);
|
||||||
|
iterator insert(const_iterator hint, value_type&& obj);
|
||||||
|
template<class InputIterator> void insert(InputIterator first, InputIterator last);
|
||||||
|
|
||||||
|
iterator erase(iterator position);
|
||||||
|
iterator erase(const_iterator position);
|
||||||
|
iterator erase(const_iterator first, const_iterator last);
|
||||||
|
void swap(unordered_set&) noexcept/*(allocator_traits<Allocator>::is_always_equal::value && is_nothrow_swappable_v<Hash> && is_nothrow_swappable_v<Pred>)*/;
|
||||||
|
void clear() noexcept;
|
||||||
|
|
||||||
|
template<class H2, class P2> void merge(unordered_set<Key, H2, P2, Allocator>& source);
|
||||||
|
template<class H2, class P2> void merge(unordered_set<Key, H2, P2, Allocator>&& source);
|
||||||
|
|
||||||
|
iterator find(const key_type& k);
|
||||||
|
const_iterator find(const key_type& k) const;
|
||||||
|
pair<iterator, iterator> equal_range(const key_type& k);
|
||||||
|
pair<const_iterator, const_iterator> equal_range(const key_type& k) const;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::vector<int>> returnValue();
|
||||||
|
std::vector<std::vector<int>>& returnRef();
|
||||||
|
|
||||||
|
std::vector<std::vector<int>> external_by_value(std::vector<std::vector<int>>);
|
||||||
|
|
||||||
|
std::vector<std::vector<int>> external_by_const_ref(const std::vector<std::vector<int>>&);
|
||||||
|
|
||||||
|
// *: Will be detected once extract destruction of unnamed temporaries and generate IR for them
|
||||||
|
|
||||||
|
const std::vector<std::vector<int>>& return_self_by_ref(const std::vector<std::vector<int>>& v) {
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::vector<int>> return_self_by_value(const std::vector<std::vector<int>>& v) {
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
void test() {
|
||||||
|
for (auto x : returnValue()) {} // GOOD
|
||||||
|
for (auto x : returnValue()[0]) {} // BAD [NOT DETECTED] (see *)
|
||||||
|
for (auto x : external_by_value(returnValue())) {} // GOOD
|
||||||
|
for (auto x : external_by_const_ref(returnValue())) {} // GOOD
|
||||||
|
for (auto x : returnValue().at(0)) {} // BAD [NOT DETECTED] (see *)
|
||||||
|
|
||||||
|
for (auto x : returnRef()) {} // GOOD
|
||||||
|
for (auto x : returnRef()[0]) {} // GOOD
|
||||||
|
for (auto x : returnRef().at(0)) {} // GOOD
|
||||||
|
|
||||||
|
for(auto it = returnValue().begin(); it != returnValue().end(); ++it) {} // BAD
|
||||||
|
|
||||||
|
{
|
||||||
|
auto v = returnValue();
|
||||||
|
for(auto it = v.begin(); it != v.end(); ++it) {} // GOOD
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto&& v = returnValue();
|
||||||
|
for(auto it = v.begin(); it != v.end(); ++it) {} // GOOD
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto&& v = returnValue()[0];
|
||||||
|
for(auto it = v.begin(); it != v.end(); ++it) {} // BAD [NOT DETECTED] (see *)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto&& v = returnRef();
|
||||||
|
for(auto it = v.begin(); it != v.end(); ++it) {} // GOOD
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto&& v = returnRef()[0];
|
||||||
|
for(auto it = v.begin(); it != v.end(); ++it) {} // GOOD
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto x : return_self_by_ref(returnValue())) {} // BAD [NOT DETECTED] (see *)
|
||||||
|
|
||||||
|
for (auto x : return_self_by_value(returnValue())) {} // GOOD
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void iterate(const std::vector<T>& v) {
|
||||||
|
for (auto x : v) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<int>& ref_to_first_in_returnValue_1() {
|
||||||
|
return returnValue()[0]; // BAD [NOT DETECTED] (see *)
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<int>& ref_to_first_in_returnValue_2() {
|
||||||
|
return returnValue()[0]; // BAD [NOT DETECTED]
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<int>& ref_to_first_in_returnValue_3() {
|
||||||
|
return returnValue()[0]; // BAD [NOT DETECTED] (see *)
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<int> first_in_returnValue_1() {
|
||||||
|
return returnValue()[0]; // GOOD
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<int> first_in_returnValue_2() {
|
||||||
|
return returnValue()[0]; // GOOD
|
||||||
|
}
|
||||||
|
|
||||||
|
void test2() {
|
||||||
|
iterate(returnValue()); // GOOD
|
||||||
|
iterate(returnValue()[0]); // GOOD
|
||||||
|
|
||||||
|
for (auto x : ref_to_first_in_returnValue_1()) {}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto value = ref_to_first_in_returnValue_2();
|
||||||
|
for (auto x : value) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto& ref = ref_to_first_in_returnValue_3();
|
||||||
|
for (auto x : ref) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto x : first_in_returnValue_1()) {}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto value = first_in_returnValue_2();
|
||||||
|
for (auto x : value) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -62,7 +62,9 @@ astGuardsCompare
|
|||||||
| 26 | x >= 0+1 when ... > ... is true |
|
| 26 | x >= 0+1 when ... > ... is true |
|
||||||
| 31 | - ... != x+0 when ... == ... is false |
|
| 31 | - ... != x+0 when ... == ... is false |
|
||||||
| 31 | - ... == x+0 when ... == ... is true |
|
| 31 | - ... == x+0 when ... == ... is true |
|
||||||
|
| 31 | x != -1 when ... == ... is false |
|
||||||
| 31 | x != - ...+0 when ... == ... is false |
|
| 31 | x != - ...+0 when ... == ... is false |
|
||||||
|
| 31 | x == -1 when ... == ... is true |
|
||||||
| 31 | x == - ...+0 when ... == ... is true |
|
| 31 | x == - ...+0 when ... == ... is true |
|
||||||
| 34 | 10 < j+1 when ... < ... is false |
|
| 34 | 10 < j+1 when ... < ... is false |
|
||||||
| 34 | 10 >= j+1 when ... < ... is true |
|
| 34 | 10 >= j+1 when ... < ... is true |
|
||||||
@@ -86,15 +88,20 @@ astGuardsCompare
|
|||||||
| 58 | 0 < y+1 when ... \|\| ... is false |
|
| 58 | 0 < y+1 when ... \|\| ... is false |
|
||||||
| 58 | 0 == x+0 when ... == ... is true |
|
| 58 | 0 == x+0 when ... == ... is true |
|
||||||
| 58 | 0 >= y+1 when ... < ... is true |
|
| 58 | 0 >= y+1 when ... < ... is true |
|
||||||
|
| 58 | x != 0 when ... == ... is false |
|
||||||
|
| 58 | x != 0 when ... \|\| ... is false |
|
||||||
| 58 | x != 0+0 when ... == ... is false |
|
| 58 | x != 0+0 when ... == ... is false |
|
||||||
| 58 | x != 0+0 when ... \|\| ... is false |
|
| 58 | x != 0+0 when ... \|\| ... is false |
|
||||||
|
| 58 | x == 0 when ... == ... is true |
|
||||||
| 58 | x == 0+0 when ... == ... is true |
|
| 58 | x == 0+0 when ... == ... is true |
|
||||||
| 58 | y < 0+0 when ... < ... is true |
|
| 58 | y < 0+0 when ... < ... is true |
|
||||||
| 58 | y >= 0+0 when ... < ... is false |
|
| 58 | y >= 0+0 when ... < ... is false |
|
||||||
| 58 | y >= 0+0 when ... \|\| ... is false |
|
| 58 | y >= 0+0 when ... \|\| ... is false |
|
||||||
| 75 | 0 != x+0 when ... == ... is false |
|
| 75 | 0 != x+0 when ... == ... is false |
|
||||||
| 75 | 0 == x+0 when ... == ... is true |
|
| 75 | 0 == x+0 when ... == ... is true |
|
||||||
|
| 75 | x != 0 when ... == ... is false |
|
||||||
| 75 | x != 0+0 when ... == ... is false |
|
| 75 | x != 0+0 when ... == ... is false |
|
||||||
|
| 75 | x == 0 when ... == ... is true |
|
||||||
| 75 | x == 0+0 when ... == ... is true |
|
| 75 | x == 0+0 when ... == ... is true |
|
||||||
| 85 | 0 != x+0 when ... == ... is false |
|
| 85 | 0 != x+0 when ... == ... is false |
|
||||||
| 85 | 0 != y+0 when ... != ... is true |
|
| 85 | 0 != y+0 when ... != ... is true |
|
||||||
@@ -102,15 +109,23 @@ astGuardsCompare
|
|||||||
| 85 | 0 == x+0 when ... && ... is true |
|
| 85 | 0 == x+0 when ... && ... is true |
|
||||||
| 85 | 0 == x+0 when ... == ... is true |
|
| 85 | 0 == x+0 when ... == ... is true |
|
||||||
| 85 | 0 == y+0 when ... != ... is false |
|
| 85 | 0 == y+0 when ... != ... is false |
|
||||||
|
| 85 | x != 0 when ... == ... is false |
|
||||||
| 85 | x != 0+0 when ... == ... is false |
|
| 85 | x != 0+0 when ... == ... is false |
|
||||||
|
| 85 | x == 0 when ... && ... is true |
|
||||||
|
| 85 | x == 0 when ... == ... is true |
|
||||||
| 85 | x == 0+0 when ... && ... is true |
|
| 85 | x == 0+0 when ... && ... is true |
|
||||||
| 85 | x == 0+0 when ... == ... is true |
|
| 85 | x == 0+0 when ... == ... is true |
|
||||||
|
| 85 | y != 0 when ... != ... is true |
|
||||||
|
| 85 | y != 0 when ... && ... is true |
|
||||||
| 85 | y != 0+0 when ... != ... is true |
|
| 85 | y != 0+0 when ... != ... is true |
|
||||||
| 85 | y != 0+0 when ... && ... is true |
|
| 85 | y != 0+0 when ... && ... is true |
|
||||||
|
| 85 | y == 0 when ... != ... is false |
|
||||||
| 85 | y == 0+0 when ... != ... is false |
|
| 85 | y == 0+0 when ... != ... is false |
|
||||||
| 94 | 0 != x+0 when ... != ... is true |
|
| 94 | 0 != x+0 when ... != ... is true |
|
||||||
| 94 | 0 == x+0 when ... != ... is false |
|
| 94 | 0 == x+0 when ... != ... is false |
|
||||||
|
| 94 | x != 0 when ... != ... is true |
|
||||||
| 94 | x != 0+0 when ... != ... is true |
|
| 94 | x != 0+0 when ... != ... is true |
|
||||||
|
| 94 | x == 0 when ... != ... is false |
|
||||||
| 94 | x == 0+0 when ... != ... is false |
|
| 94 | x == 0+0 when ... != ... is false |
|
||||||
| 102 | 10 < j+1 when ... < ... is false |
|
| 102 | 10 < j+1 when ... < ... is false |
|
||||||
| 102 | 10 >= j+1 when ... < ... is true |
|
| 102 | 10 >= j+1 when ... < ... is true |
|
||||||
@@ -122,8 +137,11 @@ astGuardsCompare
|
|||||||
| 109 | 0 < y+1 when ... \|\| ... is false |
|
| 109 | 0 < y+1 when ... \|\| ... is false |
|
||||||
| 109 | 0 == x+0 when ... == ... is true |
|
| 109 | 0 == x+0 when ... == ... is true |
|
||||||
| 109 | 0 >= y+1 when ... < ... is true |
|
| 109 | 0 >= y+1 when ... < ... is true |
|
||||||
|
| 109 | x != 0 when ... == ... is false |
|
||||||
|
| 109 | x != 0 when ... \|\| ... is false |
|
||||||
| 109 | x != 0+0 when ... == ... is false |
|
| 109 | x != 0+0 when ... == ... is false |
|
||||||
| 109 | x != 0+0 when ... \|\| ... is false |
|
| 109 | x != 0+0 when ... \|\| ... is false |
|
||||||
|
| 109 | x == 0 when ... == ... is true |
|
||||||
| 109 | x == 0+0 when ... == ... is true |
|
| 109 | x == 0+0 when ... == ... is true |
|
||||||
| 109 | y < 0+0 when ... < ... is true |
|
| 109 | y < 0+0 when ... < ... is true |
|
||||||
| 109 | y >= 0+0 when ... < ... is false |
|
| 109 | y >= 0+0 when ... < ... is false |
|
||||||
@@ -162,7 +180,9 @@ astGuardsCompare
|
|||||||
| 165 | y >= x+43 when ... < ... is true |
|
| 165 | y >= x+43 when ... < ... is true |
|
||||||
| 175 | 0 != call to foo+0 when ... == ... is false |
|
| 175 | 0 != call to foo+0 when ... == ... is false |
|
||||||
| 175 | 0 == call to foo+0 when ... == ... is true |
|
| 175 | 0 == call to foo+0 when ... == ... is true |
|
||||||
|
| 175 | call to foo != 0 when ... == ... is false |
|
||||||
| 175 | call to foo != 0+0 when ... == ... is false |
|
| 175 | call to foo != 0+0 when ... == ... is false |
|
||||||
|
| 175 | call to foo == 0 when ... == ... is true |
|
||||||
| 175 | call to foo == 0+0 when ... == ... is true |
|
| 175 | call to foo == 0+0 when ... == ... is true |
|
||||||
astGuardsControl
|
astGuardsControl
|
||||||
| test.c:7:9:7:13 | ... > ... | false | 10 | 11 |
|
| test.c:7:9:7:13 | ... > ... | false | 10 | 11 |
|
||||||
@@ -443,6 +463,34 @@ astGuardsEnsure
|
|||||||
| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:12:31:13 | - ... | != | test.cpp:31:7:31:7 | x | 0 | 34 | 34 |
|
| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:12:31:13 | - ... | != | test.cpp:31:7:31:7 | x | 0 | 34 | 34 |
|
||||||
| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:12:31:13 | - ... | == | test.cpp:31:7:31:7 | x | 0 | 30 | 30 |
|
| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:12:31:13 | - ... | == | test.cpp:31:7:31:7 | x | 0 | 30 | 30 |
|
||||||
| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:12:31:13 | - ... | == | test.cpp:31:7:31:7 | x | 0 | 31 | 32 |
|
| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:12:31:13 | - ... | == | test.cpp:31:7:31:7 | x | 0 | 31 | 32 |
|
||||||
|
astGuardsEnsure_const
|
||||||
|
| test.c:58:9:58:14 | ... == ... | test.c:58:9:58:9 | x | != | 0 | 58 | 58 |
|
||||||
|
| test.c:58:9:58:14 | ... == ... | test.c:58:9:58:9 | x | != | 0 | 62 | 62 |
|
||||||
|
| test.c:58:9:58:23 | ... \|\| ... | test.c:58:9:58:9 | x | != | 0 | 62 | 62 |
|
||||||
|
| test.c:75:9:75:14 | ... == ... | test.c:75:9:75:9 | x | != | 0 | 78 | 79 |
|
||||||
|
| test.c:75:9:75:14 | ... == ... | test.c:75:9:75:9 | x | == | 0 | 75 | 77 |
|
||||||
|
| test.c:85:8:85:13 | ... == ... | test.c:85:8:85:8 | x | == | 0 | 85 | 85 |
|
||||||
|
| test.c:85:8:85:13 | ... == ... | test.c:85:8:85:8 | x | == | 0 | 86 | 86 |
|
||||||
|
| test.c:85:8:85:23 | ... && ... | test.c:85:8:85:8 | x | == | 0 | 86 | 86 |
|
||||||
|
| test.c:85:8:85:23 | ... && ... | test.c:85:18:85:18 | y | != | 0 | 86 | 86 |
|
||||||
|
| test.c:85:18:85:23 | ... != ... | test.c:85:18:85:18 | y | != | 0 | 86 | 86 |
|
||||||
|
| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:11 | x | != | 0 | 94 | 96 |
|
||||||
|
| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:11 | x | == | 0 | 70 | 70 |
|
||||||
|
| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:11 | x | == | 0 | 99 | 102 |
|
||||||
|
| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:11 | x | == | 0 | 102 | 102 |
|
||||||
|
| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:11 | x | == | 0 | 107 | 109 |
|
||||||
|
| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:11 | x | == | 0 | 109 | 109 |
|
||||||
|
| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:11 | x | == | 0 | 109 | 117 |
|
||||||
|
| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:11 | x | == | 0 | 113 | 113 |
|
||||||
|
| test.c:109:9:109:14 | ... == ... | test.c:109:9:109:9 | x | != | 0 | 109 | 109 |
|
||||||
|
| test.c:109:9:109:14 | ... == ... | test.c:109:9:109:9 | x | != | 0 | 113 | 113 |
|
||||||
|
| test.c:109:9:109:23 | ... \|\| ... | test.c:109:9:109:9 | x | != | 0 | 113 | 113 |
|
||||||
|
| test.c:175:13:175:32 | ... == ... | test.c:175:13:175:15 | call to foo | != | 0 | 175 | 175 |
|
||||||
|
| test.c:175:13:175:32 | ... == ... | test.c:175:13:175:15 | call to foo | == | 0 | 175 | 175 |
|
||||||
|
| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:7:31:7 | x | != | -1 | 30 | 30 |
|
||||||
|
| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:7:31:7 | x | != | -1 | 34 | 34 |
|
||||||
|
| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:7:31:7 | x | == | -1 | 30 | 30 |
|
||||||
|
| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:7:31:7 | x | == | -1 | 31 | 32 |
|
||||||
irGuards
|
irGuards
|
||||||
| test.c:7:9:7:13 | CompareGT: ... > ... |
|
| test.c:7:9:7:13 | CompareGT: ... > ... |
|
||||||
| test.c:17:8:17:12 | CompareLT: ... < ... |
|
| test.c:17:8:17:12 | CompareLT: ... < ... |
|
||||||
@@ -482,74 +530,110 @@ irGuardsCompare
|
|||||||
| 7 | 0 < x+0 when CompareGT: ... > ... is true |
|
| 7 | 0 < x+0 when CompareGT: ... > ... is true |
|
||||||
| 7 | 0 >= x+0 when CompareGT: ... > ... is false |
|
| 7 | 0 >= x+0 when CompareGT: ... > ... is false |
|
||||||
| 7 | x < 0+1 when CompareGT: ... > ... is false |
|
| 7 | x < 0+1 when CompareGT: ... > ... is false |
|
||||||
|
| 7 | x < 1 when CompareGT: ... > ... is false |
|
||||||
| 7 | x >= 0+1 when CompareGT: ... > ... is true |
|
| 7 | x >= 0+1 when CompareGT: ... > ... is true |
|
||||||
|
| 7 | x >= 1 when CompareGT: ... > ... is true |
|
||||||
| 17 | 0 < x+1 when CompareLT: ... < ... is false |
|
| 17 | 0 < x+1 when CompareLT: ... < ... is false |
|
||||||
| 17 | 0 >= x+1 when CompareLT: ... < ... is true |
|
| 17 | 0 >= x+1 when CompareLT: ... < ... is true |
|
||||||
| 17 | 1 < y+0 when CompareGT: ... > ... is true |
|
| 17 | 1 < y+0 when CompareGT: ... > ... is true |
|
||||||
| 17 | 1 >= y+0 when CompareGT: ... > ... is false |
|
| 17 | 1 >= y+0 when CompareGT: ... > ... is false |
|
||||||
|
| 17 | x < 0 when CompareLT: ... < ... is true |
|
||||||
| 17 | x < 0+0 when CompareLT: ... < ... is true |
|
| 17 | x < 0+0 when CompareLT: ... < ... is true |
|
||||||
|
| 17 | x >= 0 when CompareLT: ... < ... is false |
|
||||||
| 17 | x >= 0+0 when CompareLT: ... < ... is false |
|
| 17 | x >= 0+0 when CompareLT: ... < ... is false |
|
||||||
| 17 | y < 1+1 when CompareGT: ... > ... is false |
|
| 17 | y < 1+1 when CompareGT: ... > ... is false |
|
||||||
|
| 17 | y < 2 when CompareGT: ... > ... is false |
|
||||||
| 17 | y >= 1+1 when CompareGT: ... > ... is true |
|
| 17 | y >= 1+1 when CompareGT: ... > ... is true |
|
||||||
|
| 17 | y >= 2 when CompareGT: ... > ... is true |
|
||||||
| 26 | 0 < x+0 when CompareGT: ... > ... is true |
|
| 26 | 0 < x+0 when CompareGT: ... > ... is true |
|
||||||
| 26 | 0 >= x+0 when CompareGT: ... > ... is false |
|
| 26 | 0 >= x+0 when CompareGT: ... > ... is false |
|
||||||
| 26 | x < 0+1 when CompareGT: ... > ... is false |
|
| 26 | x < 0+1 when CompareGT: ... > ... is false |
|
||||||
|
| 26 | x < 1 when CompareGT: ... > ... is false |
|
||||||
| 26 | x >= 0+1 when CompareGT: ... > ... is true |
|
| 26 | x >= 0+1 when CompareGT: ... > ... is true |
|
||||||
|
| 26 | x >= 1 when CompareGT: ... > ... is true |
|
||||||
| 31 | - ... != x+0 when CompareEQ: ... == ... is false |
|
| 31 | - ... != x+0 when CompareEQ: ... == ... is false |
|
||||||
| 31 | - ... == x+0 when CompareEQ: ... == ... is true |
|
| 31 | - ... == x+0 when CompareEQ: ... == ... is true |
|
||||||
|
| 31 | x != -1 when CompareEQ: ... == ... is false |
|
||||||
| 31 | x != - ...+0 when CompareEQ: ... == ... is false |
|
| 31 | x != - ...+0 when CompareEQ: ... == ... is false |
|
||||||
|
| 31 | x == -1 when CompareEQ: ... == ... is true |
|
||||||
| 31 | x == - ...+0 when CompareEQ: ... == ... is true |
|
| 31 | x == - ...+0 when CompareEQ: ... == ... is true |
|
||||||
| 34 | 10 < j+1 when CompareLT: ... < ... is false |
|
| 34 | 10 < j+1 when CompareLT: ... < ... is false |
|
||||||
| 34 | 10 >= j+1 when CompareLT: ... < ... is true |
|
| 34 | 10 >= j+1 when CompareLT: ... < ... is true |
|
||||||
|
| 34 | j < 10 when CompareLT: ... < ... is true |
|
||||||
| 34 | j < 10+0 when CompareLT: ... < ... is true |
|
| 34 | j < 10+0 when CompareLT: ... < ... is true |
|
||||||
|
| 34 | j >= 10 when CompareLT: ... < ... is false |
|
||||||
| 34 | j >= 10+0 when CompareLT: ... < ... is false |
|
| 34 | j >= 10+0 when CompareLT: ... < ... is false |
|
||||||
| 42 | 10 < j+1 when CompareLT: ... < ... is false |
|
| 42 | 10 < j+1 when CompareLT: ... < ... is false |
|
||||||
| 42 | 10 >= j+1 when CompareLT: ... < ... is true |
|
| 42 | 10 >= j+1 when CompareLT: ... < ... is true |
|
||||||
|
| 42 | j < 10 when CompareLT: ... < ... is true |
|
||||||
| 42 | j < 10+0 when CompareLT: ... < ... is true |
|
| 42 | j < 10+0 when CompareLT: ... < ... is true |
|
||||||
|
| 42 | j >= 10 when CompareLT: ... < ... is false |
|
||||||
| 42 | j >= 10+0 when CompareLT: ... < ... is false |
|
| 42 | j >= 10+0 when CompareLT: ... < ... is false |
|
||||||
| 44 | 0 < z+0 when CompareGT: ... > ... is true |
|
| 44 | 0 < z+0 when CompareGT: ... > ... is true |
|
||||||
| 44 | 0 >= z+0 when CompareGT: ... > ... is false |
|
| 44 | 0 >= z+0 when CompareGT: ... > ... is false |
|
||||||
| 44 | z < 0+1 when CompareGT: ... > ... is false |
|
| 44 | z < 0+1 when CompareGT: ... > ... is false |
|
||||||
|
| 44 | z < 1 when CompareGT: ... > ... is false |
|
||||||
| 44 | z >= 0+1 when CompareGT: ... > ... is true |
|
| 44 | z >= 0+1 when CompareGT: ... > ... is true |
|
||||||
|
| 44 | z >= 1 when CompareGT: ... > ... is true |
|
||||||
| 45 | 0 < y+0 when CompareGT: ... > ... is true |
|
| 45 | 0 < y+0 when CompareGT: ... > ... is true |
|
||||||
| 45 | 0 >= y+0 when CompareGT: ... > ... is false |
|
| 45 | 0 >= y+0 when CompareGT: ... > ... is false |
|
||||||
| 45 | y < 0+1 when CompareGT: ... > ... is false |
|
| 45 | y < 0+1 when CompareGT: ... > ... is false |
|
||||||
|
| 45 | y < 1 when CompareGT: ... > ... is false |
|
||||||
| 45 | y >= 0+1 when CompareGT: ... > ... is true |
|
| 45 | y >= 0+1 when CompareGT: ... > ... is true |
|
||||||
|
| 45 | y >= 1 when CompareGT: ... > ... is true |
|
||||||
| 58 | 0 != x+0 when CompareEQ: ... == ... is false |
|
| 58 | 0 != x+0 when CompareEQ: ... == ... is false |
|
||||||
| 58 | 0 < y+1 when CompareLT: ... < ... is false |
|
| 58 | 0 < y+1 when CompareLT: ... < ... is false |
|
||||||
| 58 | 0 == x+0 when CompareEQ: ... == ... is true |
|
| 58 | 0 == x+0 when CompareEQ: ... == ... is true |
|
||||||
| 58 | 0 >= y+1 when CompareLT: ... < ... is true |
|
| 58 | 0 >= y+1 when CompareLT: ... < ... is true |
|
||||||
|
| 58 | x != 0 when CompareEQ: ... == ... is false |
|
||||||
| 58 | x != 0+0 when CompareEQ: ... == ... is false |
|
| 58 | x != 0+0 when CompareEQ: ... == ... is false |
|
||||||
|
| 58 | x == 0 when CompareEQ: ... == ... is true |
|
||||||
| 58 | x == 0+0 when CompareEQ: ... == ... is true |
|
| 58 | x == 0+0 when CompareEQ: ... == ... is true |
|
||||||
|
| 58 | y < 0 when CompareLT: ... < ... is true |
|
||||||
| 58 | y < 0+0 when CompareLT: ... < ... is true |
|
| 58 | y < 0+0 when CompareLT: ... < ... is true |
|
||||||
|
| 58 | y >= 0 when CompareLT: ... < ... is false |
|
||||||
| 58 | y >= 0+0 when CompareLT: ... < ... is false |
|
| 58 | y >= 0+0 when CompareLT: ... < ... is false |
|
||||||
| 75 | 0 != x+0 when CompareEQ: ... == ... is false |
|
| 75 | 0 != x+0 when CompareEQ: ... == ... is false |
|
||||||
| 75 | 0 == x+0 when CompareEQ: ... == ... is true |
|
| 75 | 0 == x+0 when CompareEQ: ... == ... is true |
|
||||||
|
| 75 | x != 0 when CompareEQ: ... == ... is false |
|
||||||
| 75 | x != 0+0 when CompareEQ: ... == ... is false |
|
| 75 | x != 0+0 when CompareEQ: ... == ... is false |
|
||||||
|
| 75 | x == 0 when CompareEQ: ... == ... is true |
|
||||||
| 75 | x == 0+0 when CompareEQ: ... == ... is true |
|
| 75 | x == 0+0 when CompareEQ: ... == ... is true |
|
||||||
| 85 | 0 != x+0 when CompareEQ: ... == ... is false |
|
| 85 | 0 != x+0 when CompareEQ: ... == ... is false |
|
||||||
| 85 | 0 != y+0 when CompareNE: ... != ... is true |
|
| 85 | 0 != y+0 when CompareNE: ... != ... is true |
|
||||||
| 85 | 0 == x+0 when CompareEQ: ... == ... is true |
|
| 85 | 0 == x+0 when CompareEQ: ... == ... is true |
|
||||||
| 85 | 0 == y+0 when CompareNE: ... != ... is false |
|
| 85 | 0 == y+0 when CompareNE: ... != ... is false |
|
||||||
|
| 85 | x != 0 when CompareEQ: ... == ... is false |
|
||||||
| 85 | x != 0+0 when CompareEQ: ... == ... is false |
|
| 85 | x != 0+0 when CompareEQ: ... == ... is false |
|
||||||
|
| 85 | x == 0 when CompareEQ: ... == ... is true |
|
||||||
| 85 | x == 0+0 when CompareEQ: ... == ... is true |
|
| 85 | x == 0+0 when CompareEQ: ... == ... is true |
|
||||||
|
| 85 | y != 0 when CompareNE: ... != ... is true |
|
||||||
| 85 | y != 0+0 when CompareNE: ... != ... is true |
|
| 85 | y != 0+0 when CompareNE: ... != ... is true |
|
||||||
|
| 85 | y == 0 when CompareNE: ... != ... is false |
|
||||||
| 85 | y == 0+0 when CompareNE: ... != ... is false |
|
| 85 | y == 0+0 when CompareNE: ... != ... is false |
|
||||||
| 94 | 0 != x+0 when CompareNE: ... != ... is true |
|
| 94 | 0 != x+0 when CompareNE: ... != ... is true |
|
||||||
| 94 | 0 == x+0 when CompareNE: ... != ... is false |
|
| 94 | 0 == x+0 when CompareNE: ... != ... is false |
|
||||||
|
| 94 | x != 0 when CompareNE: ... != ... is true |
|
||||||
| 94 | x != 0+0 when CompareNE: ... != ... is true |
|
| 94 | x != 0+0 when CompareNE: ... != ... is true |
|
||||||
|
| 94 | x == 0 when CompareNE: ... != ... is false |
|
||||||
| 94 | x == 0+0 when CompareNE: ... != ... is false |
|
| 94 | x == 0+0 when CompareNE: ... != ... is false |
|
||||||
| 102 | 10 < j+1 when CompareLT: ... < ... is false |
|
| 102 | 10 < j+1 when CompareLT: ... < ... is false |
|
||||||
| 102 | 10 >= j+1 when CompareLT: ... < ... is true |
|
| 102 | 10 >= j+1 when CompareLT: ... < ... is true |
|
||||||
|
| 102 | j < 10 when CompareLT: ... < ... is true |
|
||||||
| 102 | j < 10+0 when CompareLT: ... < ... is true |
|
| 102 | j < 10+0 when CompareLT: ... < ... is true |
|
||||||
|
| 102 | j >= 10 when CompareLT: ... < ... is false |
|
||||||
| 102 | j >= 10+0 when CompareLT: ... < ... is false |
|
| 102 | j >= 10+0 when CompareLT: ... < ... is false |
|
||||||
| 109 | 0 != x+0 when CompareEQ: ... == ... is false |
|
| 109 | 0 != x+0 when CompareEQ: ... == ... is false |
|
||||||
| 109 | 0 < y+1 when CompareLT: ... < ... is false |
|
| 109 | 0 < y+1 when CompareLT: ... < ... is false |
|
||||||
| 109 | 0 == x+0 when CompareEQ: ... == ... is true |
|
| 109 | 0 == x+0 when CompareEQ: ... == ... is true |
|
||||||
| 109 | 0 >= y+1 when CompareLT: ... < ... is true |
|
| 109 | 0 >= y+1 when CompareLT: ... < ... is true |
|
||||||
|
| 109 | x != 0 when CompareEQ: ... == ... is false |
|
||||||
| 109 | x != 0+0 when CompareEQ: ... == ... is false |
|
| 109 | x != 0+0 when CompareEQ: ... == ... is false |
|
||||||
|
| 109 | x == 0 when CompareEQ: ... == ... is true |
|
||||||
| 109 | x == 0+0 when CompareEQ: ... == ... is true |
|
| 109 | x == 0+0 when CompareEQ: ... == ... is true |
|
||||||
|
| 109 | y < 0 when CompareLT: ... < ... is true |
|
||||||
| 109 | y < 0+0 when CompareLT: ... < ... is true |
|
| 109 | y < 0+0 when CompareLT: ... < ... is true |
|
||||||
|
| 109 | y >= 0 when CompareLT: ... < ... is false |
|
||||||
| 109 | y >= 0+0 when CompareLT: ... < ... is false |
|
| 109 | y >= 0+0 when CompareLT: ... < ... is false |
|
||||||
| 156 | ... + ... != x+0 when CompareEQ: ... == ... is false |
|
| 156 | ... + ... != x+0 when CompareEQ: ... == ... is false |
|
||||||
| 156 | ... + ... == x+0 when CompareEQ: ... == ... is true |
|
| 156 | ... + ... == x+0 when CompareEQ: ... == ... is true |
|
||||||
@@ -585,7 +669,9 @@ irGuardsCompare
|
|||||||
| 165 | y >= x+43 when CompareLT: ... < ... is true |
|
| 165 | y >= x+43 when CompareLT: ... < ... is true |
|
||||||
| 175 | 0 != call to foo+0 when CompareEQ: ... == ... is false |
|
| 175 | 0 != call to foo+0 when CompareEQ: ... == ... is false |
|
||||||
| 175 | 0 == call to foo+0 when CompareEQ: ... == ... is true |
|
| 175 | 0 == call to foo+0 when CompareEQ: ... == ... is true |
|
||||||
|
| 175 | call to foo != 0 when CompareEQ: ... == ... is false |
|
||||||
| 175 | call to foo != 0+0 when CompareEQ: ... == ... is false |
|
| 175 | call to foo != 0+0 when CompareEQ: ... == ... is false |
|
||||||
|
| 175 | call to foo == 0 when CompareEQ: ... == ... is true |
|
||||||
| 175 | call to foo == 0+0 when CompareEQ: ... == ... is true |
|
| 175 | call to foo == 0+0 when CompareEQ: ... == ... is true |
|
||||||
irGuardsControl
|
irGuardsControl
|
||||||
| test.c:7:9:7:13 | CompareGT: ... > ... | false | 11 | 11 |
|
| test.c:7:9:7:13 | CompareGT: ... > ... | false | 11 | 11 |
|
||||||
@@ -841,3 +927,75 @@ irGuardsEnsure
|
|||||||
| test.cpp:31:7:31:13 | CompareEQ: ... == ... | test.cpp:31:12:31:13 | Constant: - ... | != | test.cpp:31:7:31:7 | Load: x | 0 | 34 | 34 |
|
| test.cpp:31:7:31:13 | CompareEQ: ... == ... | test.cpp:31:12:31:13 | Constant: - ... | != | test.cpp:31:7:31:7 | Load: x | 0 | 34 | 34 |
|
||||||
| test.cpp:31:7:31:13 | CompareEQ: ... == ... | test.cpp:31:12:31:13 | Constant: - ... | == | test.cpp:31:7:31:7 | Load: x | 0 | 30 | 30 |
|
| test.cpp:31:7:31:13 | CompareEQ: ... == ... | test.cpp:31:12:31:13 | Constant: - ... | == | test.cpp:31:7:31:7 | Load: x | 0 | 30 | 30 |
|
||||||
| test.cpp:31:7:31:13 | CompareEQ: ... == ... | test.cpp:31:12:31:13 | Constant: - ... | == | test.cpp:31:7:31:7 | Load: x | 0 | 32 | 32 |
|
| test.cpp:31:7:31:13 | CompareEQ: ... == ... | test.cpp:31:12:31:13 | Constant: - ... | == | test.cpp:31:7:31:7 | Load: x | 0 | 32 | 32 |
|
||||||
|
irGuardsEnsure_const
|
||||||
|
| test.c:7:9:7:13 | CompareGT: ... > ... | test.c:7:9:7:9 | Load: x | < | 1 | 11 | 11 |
|
||||||
|
| test.c:7:9:7:13 | CompareGT: ... > ... | test.c:7:9:7:9 | Load: x | >= | 1 | 8 | 8 |
|
||||||
|
| test.c:17:8:17:12 | CompareLT: ... < ... | test.c:17:8:17:8 | Load: x | < | 0 | 17 | 17 |
|
||||||
|
| test.c:17:8:17:12 | CompareLT: ... < ... | test.c:17:8:17:8 | Load: x | < | 0 | 18 | 18 |
|
||||||
|
| test.c:17:17:17:21 | CompareGT: ... > ... | test.c:17:17:17:17 | Load: y | >= | 2 | 18 | 18 |
|
||||||
|
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:11 | Load: x | < | 1 | 2 | 2 |
|
||||||
|
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:11 | Load: x | < | 1 | 31 | 31 |
|
||||||
|
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:11 | Load: x | < | 1 | 34 | 34 |
|
||||||
|
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:11 | Load: x | < | 1 | 35 | 35 |
|
||||||
|
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:11 | Load: x | < | 1 | 39 | 39 |
|
||||||
|
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:11 | Load: x | < | 1 | 42 | 42 |
|
||||||
|
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:11 | Load: x | < | 1 | 43 | 43 |
|
||||||
|
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:11 | Load: x | < | 1 | 45 | 45 |
|
||||||
|
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:11 | Load: x | < | 1 | 46 | 46 |
|
||||||
|
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:11 | Load: x | < | 1 | 52 | 52 |
|
||||||
|
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:11 | Load: x | < | 1 | 56 | 56 |
|
||||||
|
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:11 | Load: x | < | 1 | 58 | 58 |
|
||||||
|
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:11 | Load: x | < | 1 | 59 | 59 |
|
||||||
|
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:11 | Load: x | < | 1 | 62 | 62 |
|
||||||
|
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:11 | Load: x | >= | 1 | 27 | 27 |
|
||||||
|
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:16:34:16 | Load: j | < | 10 | 35 | 35 |
|
||||||
|
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:16:34:16 | Load: j | >= | 10 | 2 | 2 |
|
||||||
|
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:16:34:16 | Load: j | >= | 10 | 39 | 39 |
|
||||||
|
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:16:34:16 | Load: j | >= | 10 | 42 | 42 |
|
||||||
|
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:16:34:16 | Load: j | >= | 10 | 43 | 43 |
|
||||||
|
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:16:34:16 | Load: j | >= | 10 | 45 | 45 |
|
||||||
|
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:16:34:16 | Load: j | >= | 10 | 46 | 46 |
|
||||||
|
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:16:34:16 | Load: j | >= | 10 | 52 | 52 |
|
||||||
|
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:16:34:16 | Load: j | >= | 10 | 56 | 56 |
|
||||||
|
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:16:34:16 | Load: j | >= | 10 | 58 | 58 |
|
||||||
|
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:16:34:16 | Load: j | >= | 10 | 59 | 59 |
|
||||||
|
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:16:34:16 | Load: j | >= | 10 | 62 | 62 |
|
||||||
|
| test.c:42:16:42:21 | CompareLT: ... < ... | test.c:42:16:42:16 | Load: j | < | 10 | 43 | 43 |
|
||||||
|
| test.c:42:16:42:21 | CompareLT: ... < ... | test.c:42:16:42:16 | Load: j | < | 10 | 45 | 45 |
|
||||||
|
| test.c:42:16:42:21 | CompareLT: ... < ... | test.c:42:16:42:16 | Load: j | < | 10 | 46 | 46 |
|
||||||
|
| test.c:42:16:42:21 | CompareLT: ... < ... | test.c:42:16:42:16 | Load: j | < | 10 | 52 | 52 |
|
||||||
|
| test.c:44:12:44:16 | CompareGT: ... > ... | test.c:44:12:44:12 | Load: z | < | 1 | 52 | 52 |
|
||||||
|
| test.c:44:12:44:16 | CompareGT: ... > ... | test.c:44:12:44:12 | Load: z | >= | 1 | 45 | 45 |
|
||||||
|
| test.c:44:12:44:16 | CompareGT: ... > ... | test.c:44:12:44:12 | Load: z | >= | 1 | 46 | 46 |
|
||||||
|
| test.c:45:16:45:20 | CompareGT: ... > ... | test.c:45:16:45:16 | Load: y | >= | 1 | 46 | 46 |
|
||||||
|
| test.c:58:9:58:14 | CompareEQ: ... == ... | test.c:58:9:58:9 | Load: x | != | 0 | 58 | 58 |
|
||||||
|
| test.c:58:9:58:14 | CompareEQ: ... == ... | test.c:58:9:58:9 | Load: x | != | 0 | 62 | 62 |
|
||||||
|
| test.c:58:19:58:23 | CompareLT: ... < ... | test.c:58:19:58:19 | Load: y | >= | 0 | 62 | 62 |
|
||||||
|
| test.c:75:9:75:14 | CompareEQ: ... == ... | test.c:75:9:75:9 | Load: x | != | 0 | 79 | 79 |
|
||||||
|
| test.c:75:9:75:14 | CompareEQ: ... == ... | test.c:75:9:75:9 | Load: x | == | 0 | 76 | 76 |
|
||||||
|
| test.c:85:8:85:13 | CompareEQ: ... == ... | test.c:85:8:85:8 | Load: x | == | 0 | 85 | 85 |
|
||||||
|
| test.c:85:8:85:13 | CompareEQ: ... == ... | test.c:85:8:85:8 | Load: x | == | 0 | 86 | 86 |
|
||||||
|
| test.c:85:18:85:23 | CompareNE: ... != ... | test.c:85:18:85:18 | Load: y | != | 0 | 86 | 86 |
|
||||||
|
| test.c:94:11:94:16 | CompareNE: ... != ... | test.c:94:11:94:11 | Load: x | != | 0 | 95 | 95 |
|
||||||
|
| test.c:94:11:94:16 | CompareNE: ... != ... | test.c:94:11:94:11 | Load: x | == | 0 | 70 | 70 |
|
||||||
|
| test.c:94:11:94:16 | CompareNE: ... != ... | test.c:94:11:94:11 | Load: x | == | 0 | 99 | 99 |
|
||||||
|
| test.c:94:11:94:16 | CompareNE: ... != ... | test.c:94:11:94:11 | Load: x | == | 0 | 102 | 102 |
|
||||||
|
| test.c:94:11:94:16 | CompareNE: ... != ... | test.c:94:11:94:11 | Load: x | == | 0 | 103 | 103 |
|
||||||
|
| test.c:94:11:94:16 | CompareNE: ... != ... | test.c:94:11:94:11 | Load: x | == | 0 | 107 | 107 |
|
||||||
|
| test.c:94:11:94:16 | CompareNE: ... != ... | test.c:94:11:94:11 | Load: x | == | 0 | 109 | 109 |
|
||||||
|
| test.c:94:11:94:16 | CompareNE: ... != ... | test.c:94:11:94:11 | Load: x | == | 0 | 110 | 110 |
|
||||||
|
| test.c:94:11:94:16 | CompareNE: ... != ... | test.c:94:11:94:11 | Load: x | == | 0 | 113 | 113 |
|
||||||
|
| test.c:102:16:102:21 | CompareLT: ... < ... | test.c:102:16:102:16 | Load: j | < | 10 | 103 | 103 |
|
||||||
|
| test.c:102:16:102:21 | CompareLT: ... < ... | test.c:102:16:102:16 | Load: j | >= | 10 | 70 | 70 |
|
||||||
|
| test.c:102:16:102:21 | CompareLT: ... < ... | test.c:102:16:102:16 | Load: j | >= | 10 | 107 | 107 |
|
||||||
|
| test.c:102:16:102:21 | CompareLT: ... < ... | test.c:102:16:102:16 | Load: j | >= | 10 | 109 | 109 |
|
||||||
|
| test.c:102:16:102:21 | CompareLT: ... < ... | test.c:102:16:102:16 | Load: j | >= | 10 | 110 | 110 |
|
||||||
|
| test.c:102:16:102:21 | CompareLT: ... < ... | test.c:102:16:102:16 | Load: j | >= | 10 | 113 | 113 |
|
||||||
|
| test.c:109:9:109:14 | CompareEQ: ... == ... | test.c:109:9:109:9 | Load: x | != | 0 | 109 | 109 |
|
||||||
|
| test.c:109:9:109:14 | CompareEQ: ... == ... | test.c:109:9:109:9 | Load: x | != | 0 | 113 | 113 |
|
||||||
|
| test.c:109:19:109:23 | CompareLT: ... < ... | test.c:109:19:109:19 | Load: y | >= | 0 | 113 | 113 |
|
||||||
|
| test.c:175:13:175:32 | CompareEQ: ... == ... | test.c:175:13:175:15 | Call: call to foo | != | 0 | 175 | 175 |
|
||||||
|
| test.c:175:13:175:32 | CompareEQ: ... == ... | test.c:175:13:175:15 | Call: call to foo | == | 0 | 175 | 175 |
|
||||||
|
| test.cpp:31:7:31:13 | CompareEQ: ... == ... | test.cpp:31:7:31:7 | Load: x | != | -1 | 34 | 34 |
|
||||||
|
| test.cpp:31:7:31:13 | CompareEQ: ... == ... | test.cpp:31:7:31:7 | Load: x | == | -1 | 30 | 30 |
|
||||||
|
| test.cpp:31:7:31:13 | CompareEQ: ... == ... | test.cpp:31:7:31:7 | Load: x | == | -1 | 32 | 32 |
|
||||||
|
|||||||
@@ -4,12 +4,13 @@ import semmle.code.cpp.controlflow.IRGuards
|
|||||||
query predicate astGuards(GuardCondition guard) { any() }
|
query predicate astGuards(GuardCondition guard) { any() }
|
||||||
|
|
||||||
query predicate astGuardsCompare(int startLine, string msg) {
|
query predicate astGuardsCompare(int startLine, string msg) {
|
||||||
exists(GuardCondition guard, Expr left, Expr right, int k, string which, string op |
|
exists(GuardCondition guard, Expr left, int k, string op |
|
||||||
exists(boolean sense |
|
exists(boolean sense, string which |
|
||||||
sense = true and which = "true"
|
sense = true and which = "true"
|
||||||
or
|
or
|
||||||
sense = false and which = "false"
|
sense = false and which = "false"
|
||||||
|
|
|
|
||||||
|
exists(Expr right |
|
||||||
guard.comparesLt(left, right, k, true, sense) and op = " < "
|
guard.comparesLt(left, right, k, true, sense) and op = " < "
|
||||||
or
|
or
|
||||||
guard.comparesLt(left, right, k, false, sense) and op = " >= "
|
guard.comparesLt(left, right, k, false, sense) and op = " >= "
|
||||||
@@ -17,10 +18,21 @@ query predicate astGuardsCompare(int startLine, string msg) {
|
|||||||
guard.comparesEq(left, right, k, true, sense) and op = " == "
|
guard.comparesEq(left, right, k, true, sense) and op = " == "
|
||||||
or
|
or
|
||||||
guard.comparesEq(left, right, k, false, sense) and op = " != "
|
guard.comparesEq(left, right, k, false, sense) and op = " != "
|
||||||
) and
|
|
|
||||||
startLine = guard.getLocation().getStartLine() and
|
|
||||||
msg = left + op + right + "+" + k + " when " + guard + " is " + which
|
msg = left + op + right + "+" + k + " when " + guard + " is " + which
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
or
|
||||||
|
exists(AbstractValue value |
|
||||||
|
guard.comparesEq(left, k, true, value) and op = " == "
|
||||||
|
or
|
||||||
|
guard.comparesEq(left, k, false, value) and op = " != "
|
||||||
|
|
|
||||||
|
msg = left + op + k + " when " + guard + " is " + value
|
||||||
|
)
|
||||||
|
|
|
||||||
|
startLine = guard.getLocation().getStartLine()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
query predicate astGuardsControl(GuardCondition guard, boolean sense, int start, int end) {
|
query predicate astGuardsControl(GuardCondition guard, boolean sense, int start, int end) {
|
||||||
@@ -46,15 +58,28 @@ query predicate astGuardsEnsure(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
query predicate astGuardsEnsure_const(
|
||||||
|
GuardCondition guard, Expr left, string op, int k, int start, int end
|
||||||
|
) {
|
||||||
|
exists(BasicBlock block |
|
||||||
|
guard.ensuresEq(left, k, block, true) and op = "=="
|
||||||
|
or
|
||||||
|
guard.ensuresEq(left, k, block, false) and op = "!="
|
||||||
|
|
|
||||||
|
block.hasLocationInfo(_, start, _, end, _)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
query predicate irGuards(IRGuardCondition guard) { any() }
|
query predicate irGuards(IRGuardCondition guard) { any() }
|
||||||
|
|
||||||
query predicate irGuardsCompare(int startLine, string msg) {
|
query predicate irGuardsCompare(int startLine, string msg) {
|
||||||
exists(IRGuardCondition guard, Operand left, Operand right, int k, string which, string op |
|
exists(IRGuardCondition guard, Operand left, int k, string op |
|
||||||
exists(boolean sense |
|
exists(boolean sense, string which |
|
||||||
sense = true and which = "true"
|
sense = true and which = "true"
|
||||||
or
|
or
|
||||||
sense = false and which = "false"
|
sense = false and which = "false"
|
||||||
|
|
|
|
||||||
|
exists(Operand right |
|
||||||
guard.comparesLt(left, right, k, true, sense) and op = " < "
|
guard.comparesLt(left, right, k, true, sense) and op = " < "
|
||||||
or
|
or
|
||||||
guard.comparesLt(left, right, k, false, sense) and op = " >= "
|
guard.comparesLt(left, right, k, false, sense) and op = " >= "
|
||||||
@@ -62,12 +87,29 @@ query predicate irGuardsCompare(int startLine, string msg) {
|
|||||||
guard.comparesEq(left, right, k, true, sense) and op = " == "
|
guard.comparesEq(left, right, k, true, sense) and op = " == "
|
||||||
or
|
or
|
||||||
guard.comparesEq(left, right, k, false, sense) and op = " != "
|
guard.comparesEq(left, right, k, false, sense) and op = " != "
|
||||||
) and
|
|
|
||||||
startLine = guard.getLocation().getStartLine() and
|
|
||||||
msg =
|
msg =
|
||||||
left.getAnyDef().getUnconvertedResultExpression() + op +
|
left.getAnyDef().getUnconvertedResultExpression() + op +
|
||||||
right.getAnyDef().getUnconvertedResultExpression() + "+" + k + " when " + guard + " is " +
|
right.getAnyDef().getUnconvertedResultExpression() + "+" + k + " when " + guard + " is "
|
||||||
which
|
+ which
|
||||||
|
)
|
||||||
|
)
|
||||||
|
or
|
||||||
|
exists(AbstractValue value |
|
||||||
|
guard.comparesLt(left, k, true, value) and op = " < "
|
||||||
|
or
|
||||||
|
guard.comparesLt(left, k, false, value) and op = " >= "
|
||||||
|
or
|
||||||
|
guard.comparesEq(left, k, true, value) and op = " == "
|
||||||
|
or
|
||||||
|
guard.comparesEq(left, k, false, value) and op = " != "
|
||||||
|
|
|
||||||
|
msg =
|
||||||
|
left.getAnyDef().getUnconvertedResultExpression() + op + k + " when " + guard + " is " +
|
||||||
|
value
|
||||||
|
)
|
||||||
|
|
|
||||||
|
startLine = guard.getLocation().getStartLine()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,3 +137,20 @@ query predicate irGuardsEnsure(
|
|||||||
block.getLocation().hasLocationInfo(_, start, _, end, _)
|
block.getLocation().hasLocationInfo(_, start, _, end, _)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
query predicate irGuardsEnsure_const(
|
||||||
|
IRGuardCondition guard, Instruction left, string op, int k, int start, int end
|
||||||
|
) {
|
||||||
|
exists(IRBlock block, Operand leftOp |
|
||||||
|
guard.ensuresLt(leftOp, k, block, true) and op = "<"
|
||||||
|
or
|
||||||
|
guard.ensuresLt(leftOp, k, block, false) and op = ">="
|
||||||
|
or
|
||||||
|
guard.ensuresEq(leftOp, k, block, true) and op = "=="
|
||||||
|
or
|
||||||
|
guard.ensuresEq(leftOp, k, block, false) and op = "!="
|
||||||
|
|
|
||||||
|
leftOp = left.getAUse() and
|
||||||
|
block.getLocation().hasLocationInfo(_, start, _, end, _)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
@@ -29,3 +29,6 @@
|
|||||||
| test.cpp:18:8:18:10 | call to get |
|
| test.cpp:18:8:18:10 | call to get |
|
||||||
| test.cpp:31:7:31:13 | ... == ... |
|
| test.cpp:31:7:31:13 | ... == ... |
|
||||||
| test.cpp:42:13:42:20 | call to getABool |
|
| test.cpp:42:13:42:20 | call to getABool |
|
||||||
|
| test.cpp:61:10:61:10 | i |
|
||||||
|
| test.cpp:74:10:74:10 | i |
|
||||||
|
| test.cpp:84:10:84:10 | i |
|
||||||
|
|||||||
@@ -1,58 +1,93 @@
|
|||||||
| 7 | 0 < x+0 when ... > ... is true |
|
| 7 | 0 < x+0 when ... > ... is true |
|
||||||
| 7 | 0 >= x+0 when ... > ... is false |
|
| 7 | 0 >= x+0 when ... > ... is false |
|
||||||
| 7 | x < 0+1 when ... > ... is false |
|
| 7 | x < 0+1 when ... > ... is false |
|
||||||
|
| 7 | x < 1 when ... > ... is false |
|
||||||
| 7 | x >= 0+1 when ... > ... is true |
|
| 7 | x >= 0+1 when ... > ... is true |
|
||||||
|
| 7 | x >= 1 when ... > ... is true |
|
||||||
| 17 | 0 < x+1 when ... < ... is false |
|
| 17 | 0 < x+1 when ... < ... is false |
|
||||||
| 17 | 0 >= x+1 when ... && ... is true |
|
| 17 | 0 >= x+1 when ... && ... is true |
|
||||||
| 17 | 0 >= x+1 when ... < ... is true |
|
| 17 | 0 >= x+1 when ... < ... is true |
|
||||||
| 17 | 1 < y+0 when ... && ... is true |
|
| 17 | 1 < y+0 when ... && ... is true |
|
||||||
| 17 | 1 < y+0 when ... > ... is true |
|
| 17 | 1 < y+0 when ... > ... is true |
|
||||||
| 17 | 1 >= y+0 when ... > ... is false |
|
| 17 | 1 >= y+0 when ... > ... is false |
|
||||||
|
| 17 | x < 0 when ... && ... is true |
|
||||||
|
| 17 | x < 0 when ... < ... is true |
|
||||||
| 17 | x < 0+0 when ... && ... is true |
|
| 17 | x < 0+0 when ... && ... is true |
|
||||||
| 17 | x < 0+0 when ... < ... is true |
|
| 17 | x < 0+0 when ... < ... is true |
|
||||||
|
| 17 | x >= 0 when ... < ... is false |
|
||||||
| 17 | x >= 0+0 when ... < ... is false |
|
| 17 | x >= 0+0 when ... < ... is false |
|
||||||
| 17 | y < 1+1 when ... > ... is false |
|
| 17 | y < 1+1 when ... > ... is false |
|
||||||
|
| 17 | y < 2 when ... > ... is false |
|
||||||
| 17 | y >= 1+1 when ... && ... is true |
|
| 17 | y >= 1+1 when ... && ... is true |
|
||||||
| 17 | y >= 1+1 when ... > ... is true |
|
| 17 | y >= 1+1 when ... > ... is true |
|
||||||
|
| 17 | y >= 2 when ... && ... is true |
|
||||||
|
| 17 | y >= 2 when ... > ... is true |
|
||||||
| 26 | 0 < x+0 when ... > ... is true |
|
| 26 | 0 < x+0 when ... > ... is true |
|
||||||
| 26 | 0 >= x+0 when ... > ... is false |
|
| 26 | 0 >= x+0 when ... > ... is false |
|
||||||
| 26 | x < 0+1 when ... > ... is false |
|
| 26 | x < 0+1 when ... > ... is false |
|
||||||
|
| 26 | x < 1 when ... > ... is false |
|
||||||
| 26 | x >= 0+1 when ... > ... is true |
|
| 26 | x >= 0+1 when ... > ... is true |
|
||||||
|
| 26 | x >= 1 when ... > ... is true |
|
||||||
| 31 | - ... != x+0 when ... == ... is false |
|
| 31 | - ... != x+0 when ... == ... is false |
|
||||||
| 31 | - ... == x+0 when ... == ... is true |
|
| 31 | - ... == x+0 when ... == ... is true |
|
||||||
|
| 31 | x != -1 when ... == ... is false |
|
||||||
| 31 | x != - ...+0 when ... == ... is false |
|
| 31 | x != - ...+0 when ... == ... is false |
|
||||||
|
| 31 | x == -1 when ... == ... is true |
|
||||||
| 31 | x == - ...+0 when ... == ... is true |
|
| 31 | x == - ...+0 when ... == ... is true |
|
||||||
| 34 | 10 < j+1 when ... < ... is false |
|
| 34 | 10 < j+1 when ... < ... is false |
|
||||||
| 34 | 10 >= j+1 when ... < ... is true |
|
| 34 | 10 >= j+1 when ... < ... is true |
|
||||||
|
| 34 | j < 10 when ... < ... is true |
|
||||||
| 34 | j < 10+0 when ... < ... is true |
|
| 34 | j < 10+0 when ... < ... is true |
|
||||||
|
| 34 | j >= 10 when ... < ... is false |
|
||||||
| 34 | j >= 10+0 when ... < ... is false |
|
| 34 | j >= 10+0 when ... < ... is false |
|
||||||
| 42 | 10 < j+1 when ... < ... is false |
|
| 42 | 10 < j+1 when ... < ... is false |
|
||||||
| 42 | 10 >= j+1 when ... < ... is true |
|
| 42 | 10 >= j+1 when ... < ... is true |
|
||||||
|
| 42 | j < 10 when ... < ... is true |
|
||||||
| 42 | j < 10+0 when ... < ... is true |
|
| 42 | j < 10+0 when ... < ... is true |
|
||||||
|
| 42 | j >= 10 when ... < ... is false |
|
||||||
| 42 | j >= 10+0 when ... < ... is false |
|
| 42 | j >= 10+0 when ... < ... is false |
|
||||||
| 44 | 0 < z+0 when ... > ... is true |
|
| 44 | 0 < z+0 when ... > ... is true |
|
||||||
| 44 | 0 >= z+0 when ... > ... is false |
|
| 44 | 0 >= z+0 when ... > ... is false |
|
||||||
| 44 | z < 0+1 when ... > ... is false |
|
| 44 | z < 0+1 when ... > ... is false |
|
||||||
|
| 44 | z < 1 when ... > ... is false |
|
||||||
| 44 | z >= 0+1 when ... > ... is true |
|
| 44 | z >= 0+1 when ... > ... is true |
|
||||||
|
| 44 | z >= 1 when ... > ... is true |
|
||||||
| 45 | 0 < y+0 when ... > ... is true |
|
| 45 | 0 < y+0 when ... > ... is true |
|
||||||
| 45 | 0 >= y+0 when ... > ... is false |
|
| 45 | 0 >= y+0 when ... > ... is false |
|
||||||
| 45 | y < 0+1 when ... > ... is false |
|
| 45 | y < 0+1 when ... > ... is false |
|
||||||
|
| 45 | y < 1 when ... > ... is false |
|
||||||
| 45 | y >= 0+1 when ... > ... is true |
|
| 45 | y >= 0+1 when ... > ... is true |
|
||||||
|
| 45 | y >= 1 when ... > ... is true |
|
||||||
| 58 | 0 != x+0 when ... == ... is false |
|
| 58 | 0 != x+0 when ... == ... is false |
|
||||||
| 58 | 0 != x+0 when ... \|\| ... is false |
|
| 58 | 0 != x+0 when ... \|\| ... is false |
|
||||||
| 58 | 0 < y+1 when ... < ... is false |
|
| 58 | 0 < y+1 when ... < ... is false |
|
||||||
| 58 | 0 < y+1 when ... \|\| ... is false |
|
| 58 | 0 < y+1 when ... \|\| ... is false |
|
||||||
| 58 | 0 == x+0 when ... == ... is true |
|
| 58 | 0 == x+0 when ... == ... is true |
|
||||||
| 58 | 0 >= y+1 when ... < ... is true |
|
| 58 | 0 >= y+1 when ... < ... is true |
|
||||||
|
| 58 | x != 0 when ... == ... is false |
|
||||||
|
| 58 | x != 0 when ... \|\| ... is false |
|
||||||
| 58 | x != 0+0 when ... == ... is false |
|
| 58 | x != 0+0 when ... == ... is false |
|
||||||
| 58 | x != 0+0 when ... \|\| ... is false |
|
| 58 | x != 0+0 when ... \|\| ... is false |
|
||||||
|
| 58 | x == 0 when ... == ... is true |
|
||||||
| 58 | x == 0+0 when ... == ... is true |
|
| 58 | x == 0+0 when ... == ... is true |
|
||||||
|
| 58 | y < 0 when ... < ... is true |
|
||||||
| 58 | y < 0+0 when ... < ... is true |
|
| 58 | y < 0+0 when ... < ... is true |
|
||||||
|
| 58 | y >= 0 when ... < ... is false |
|
||||||
|
| 58 | y >= 0 when ... \|\| ... is false |
|
||||||
| 58 | y >= 0+0 when ... < ... is false |
|
| 58 | y >= 0+0 when ... < ... is false |
|
||||||
| 58 | y >= 0+0 when ... \|\| ... is false |
|
| 58 | y >= 0+0 when ... \|\| ... is false |
|
||||||
|
| 61 | i == 0 when i is Case[0] |
|
||||||
|
| 61 | i == 1 when i is Case[1] |
|
||||||
|
| 61 | i == 2 when i is Case[2] |
|
||||||
|
| 74 | i < 11 when i is Case[0..10] |
|
||||||
|
| 74 | i < 21 when i is Case[11..20] |
|
||||||
|
| 74 | i >= 0 when i is Case[0..10] |
|
||||||
|
| 74 | i >= 11 when i is Case[11..20] |
|
||||||
| 75 | 0 != x+0 when ... == ... is false |
|
| 75 | 0 != x+0 when ... == ... is false |
|
||||||
| 75 | 0 == x+0 when ... == ... is true |
|
| 75 | 0 == x+0 when ... == ... is true |
|
||||||
|
| 75 | x != 0 when ... == ... is false |
|
||||||
| 75 | x != 0+0 when ... == ... is false |
|
| 75 | x != 0+0 when ... == ... is false |
|
||||||
|
| 75 | x == 0 when ... == ... is true |
|
||||||
| 75 | x == 0+0 when ... == ... is true |
|
| 75 | x == 0+0 when ... == ... is true |
|
||||||
| 85 | 0 != x+0 when ... == ... is false |
|
| 85 | 0 != x+0 when ... == ... is false |
|
||||||
| 85 | 0 != y+0 when ... != ... is true |
|
| 85 | 0 != y+0 when ... != ... is true |
|
||||||
@@ -60,19 +95,29 @@
|
|||||||
| 85 | 0 == x+0 when ... && ... is true |
|
| 85 | 0 == x+0 when ... && ... is true |
|
||||||
| 85 | 0 == x+0 when ... == ... is true |
|
| 85 | 0 == x+0 when ... == ... is true |
|
||||||
| 85 | 0 == y+0 when ... != ... is false |
|
| 85 | 0 == y+0 when ... != ... is false |
|
||||||
|
| 85 | x != 0 when ... == ... is false |
|
||||||
| 85 | x != 0+0 when ... == ... is false |
|
| 85 | x != 0+0 when ... == ... is false |
|
||||||
|
| 85 | x == 0 when ... && ... is true |
|
||||||
|
| 85 | x == 0 when ... == ... is true |
|
||||||
| 85 | x == 0+0 when ... && ... is true |
|
| 85 | x == 0+0 when ... && ... is true |
|
||||||
| 85 | x == 0+0 when ... == ... is true |
|
| 85 | x == 0+0 when ... == ... is true |
|
||||||
|
| 85 | y != 0 when ... != ... is true |
|
||||||
|
| 85 | y != 0 when ... && ... is true |
|
||||||
| 85 | y != 0+0 when ... != ... is true |
|
| 85 | y != 0+0 when ... != ... is true |
|
||||||
| 85 | y != 0+0 when ... && ... is true |
|
| 85 | y != 0+0 when ... && ... is true |
|
||||||
|
| 85 | y == 0 when ... != ... is false |
|
||||||
| 85 | y == 0+0 when ... != ... is false |
|
| 85 | y == 0+0 when ... != ... is false |
|
||||||
| 94 | 0 != x+0 when ... != ... is true |
|
| 94 | 0 != x+0 when ... != ... is true |
|
||||||
| 94 | 0 == x+0 when ... != ... is false |
|
| 94 | 0 == x+0 when ... != ... is false |
|
||||||
|
| 94 | x != 0 when ... != ... is true |
|
||||||
| 94 | x != 0+0 when ... != ... is true |
|
| 94 | x != 0+0 when ... != ... is true |
|
||||||
|
| 94 | x == 0 when ... != ... is false |
|
||||||
| 94 | x == 0+0 when ... != ... is false |
|
| 94 | x == 0+0 when ... != ... is false |
|
||||||
| 102 | 10 < j+1 when ... < ... is false |
|
| 102 | 10 < j+1 when ... < ... is false |
|
||||||
| 102 | 10 >= j+1 when ... < ... is true |
|
| 102 | 10 >= j+1 when ... < ... is true |
|
||||||
|
| 102 | j < 10 when ... < ... is true |
|
||||||
| 102 | j < 10+0 when ... < ... is true |
|
| 102 | j < 10+0 when ... < ... is true |
|
||||||
|
| 102 | j >= 10 when ... < ... is false |
|
||||||
| 102 | j >= 10+0 when ... < ... is false |
|
| 102 | j >= 10+0 when ... < ... is false |
|
||||||
| 109 | 0 != x+0 when ... == ... is false |
|
| 109 | 0 != x+0 when ... == ... is false |
|
||||||
| 109 | 0 != x+0 when ... \|\| ... is false |
|
| 109 | 0 != x+0 when ... \|\| ... is false |
|
||||||
@@ -80,9 +125,15 @@
|
|||||||
| 109 | 0 < y+1 when ... \|\| ... is false |
|
| 109 | 0 < y+1 when ... \|\| ... is false |
|
||||||
| 109 | 0 == x+0 when ... == ... is true |
|
| 109 | 0 == x+0 when ... == ... is true |
|
||||||
| 109 | 0 >= y+1 when ... < ... is true |
|
| 109 | 0 >= y+1 when ... < ... is true |
|
||||||
|
| 109 | x != 0 when ... == ... is false |
|
||||||
|
| 109 | x != 0 when ... \|\| ... is false |
|
||||||
| 109 | x != 0+0 when ... == ... is false |
|
| 109 | x != 0+0 when ... == ... is false |
|
||||||
| 109 | x != 0+0 when ... \|\| ... is false |
|
| 109 | x != 0+0 when ... \|\| ... is false |
|
||||||
|
| 109 | x == 0 when ... == ... is true |
|
||||||
| 109 | x == 0+0 when ... == ... is true |
|
| 109 | x == 0+0 when ... == ... is true |
|
||||||
|
| 109 | y < 0 when ... < ... is true |
|
||||||
| 109 | y < 0+0 when ... < ... is true |
|
| 109 | y < 0+0 when ... < ... is true |
|
||||||
|
| 109 | y >= 0 when ... < ... is false |
|
||||||
|
| 109 | y >= 0 when ... \|\| ... is false |
|
||||||
| 109 | y >= 0+0 when ... < ... is false |
|
| 109 | y >= 0+0 when ... < ... is false |
|
||||||
| 109 | y >= 0+0 when ... \|\| ... is false |
|
| 109 | y >= 0+0 when ... \|\| ... is false |
|
||||||
|
|||||||
@@ -7,13 +7,14 @@
|
|||||||
import cpp
|
import cpp
|
||||||
import semmle.code.cpp.controlflow.Guards
|
import semmle.code.cpp.controlflow.Guards
|
||||||
|
|
||||||
from GuardCondition guard, Expr left, Expr right, int k, string which, string op, string msg
|
from GuardCondition guard, Expr left, int k, string op, string msg
|
||||||
where
|
where
|
||||||
exists(boolean sense |
|
exists(boolean sense, string which |
|
||||||
sense = true and which = "true"
|
sense = true and which = "true"
|
||||||
or
|
or
|
||||||
sense = false and which = "false"
|
sense = false and which = "false"
|
||||||
|
|
|
|
||||||
|
exists(Expr right |
|
||||||
guard.comparesLt(left, right, k, true, sense) and op = " < "
|
guard.comparesLt(left, right, k, true, sense) and op = " < "
|
||||||
or
|
or
|
||||||
guard.comparesLt(left, right, k, false, sense) and op = " >= "
|
guard.comparesLt(left, right, k, false, sense) and op = " >= "
|
||||||
@@ -21,6 +22,20 @@ where
|
|||||||
guard.comparesEq(left, right, k, true, sense) and op = " == "
|
guard.comparesEq(left, right, k, true, sense) and op = " == "
|
||||||
or
|
or
|
||||||
guard.comparesEq(left, right, k, false, sense) and op = " != "
|
guard.comparesEq(left, right, k, false, sense) and op = " != "
|
||||||
) and
|
|
|
||||||
msg = left + op + right + "+" + k + " when " + guard + " is " + which
|
msg = left + op + right + "+" + k + " when " + guard + " is " + which
|
||||||
|
)
|
||||||
|
)
|
||||||
|
or
|
||||||
|
exists(AbstractValue value |
|
||||||
|
guard.comparesLt(left, k, true, value) and op = " < "
|
||||||
|
or
|
||||||
|
guard.comparesLt(left, k, false, value) and op = " >= "
|
||||||
|
or
|
||||||
|
guard.comparesEq(left, k, true, value) and op = " == "
|
||||||
|
or
|
||||||
|
guard.comparesEq(left, k, false, value) and op = " != "
|
||||||
|
|
|
||||||
|
msg = left + op + k + " when " + guard + " is " + value
|
||||||
|
)
|
||||||
select guard.getLocation().getStartLine(), msg
|
select guard.getLocation().getStartLine(), msg
|
||||||
|
|||||||
@@ -86,3 +86,7 @@
|
|||||||
| test.cpp:31:7:31:13 | ... == ... | true | 31 | 32 |
|
| test.cpp:31:7:31:13 | ... == ... | true | 31 | 32 |
|
||||||
| test.cpp:42:13:42:20 | call to getABool | false | 53 | 53 |
|
| test.cpp:42:13:42:20 | call to getABool | false | 53 | 53 |
|
||||||
| test.cpp:42:13:42:20 | call to getABool | true | 43 | 45 |
|
| test.cpp:42:13:42:20 | call to getABool | true | 43 | 45 |
|
||||||
|
| test.cpp:61:10:61:10 | i | Case[0] | 62 | 64 |
|
||||||
|
| test.cpp:61:10:61:10 | i | Case[1] | 65 | 66 |
|
||||||
|
| test.cpp:74:10:74:10 | i | Case[0..10] | 75 | 77 |
|
||||||
|
| test.cpp:74:10:74:10 | i | Case[11..20] | 78 | 79 |
|
||||||
|
|||||||
@@ -7,10 +7,10 @@
|
|||||||
import cpp
|
import cpp
|
||||||
import semmle.code.cpp.controlflow.Guards
|
import semmle.code.cpp.controlflow.Guards
|
||||||
|
|
||||||
from GuardCondition guard, boolean sense, int start, int end
|
from GuardCondition guard, AbstractValue value, int start, int end
|
||||||
where
|
where
|
||||||
exists(BasicBlock block |
|
exists(BasicBlock block |
|
||||||
guard.controls(block, sense) and
|
guard.valueControls(block, value) and
|
||||||
block.hasLocationInfo(_, start, _, end, _)
|
block.hasLocationInfo(_, start, _, end, _)
|
||||||
)
|
)
|
||||||
select guard, sense, start, end
|
select guard, value, start, end
|
||||||
|
|||||||
@@ -52,3 +52,37 @@ bool testWithCatch0(int v)
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void use1(int);
|
||||||
|
void use2(int);
|
||||||
|
void use3(int);
|
||||||
|
|
||||||
|
void test_switches_simple(int i) {
|
||||||
|
switch(i) {
|
||||||
|
case 0:
|
||||||
|
use1(i);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
use2(i);
|
||||||
|
/* NOTE: fallthrough */
|
||||||
|
case 2:
|
||||||
|
use3(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_switches_range(int i) {
|
||||||
|
switch(i) {
|
||||||
|
case 0 ... 10:
|
||||||
|
use1(i);
|
||||||
|
break;
|
||||||
|
case 11 ... 20:
|
||||||
|
use2(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_switches_default(int i) {
|
||||||
|
switch(i) {
|
||||||
|
default:
|
||||||
|
use1(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -166,6 +166,8 @@ postWithInFlow
|
|||||||
| test.cpp:932:5:932:19 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
|
| test.cpp:932:5:932:19 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
|
||||||
| test.cpp:932:6:932:19 | global_pointer [inner post update] | PostUpdateNode should not be the target of local flow. |
|
| test.cpp:932:6:932:19 | global_pointer [inner post update] | PostUpdateNode should not be the target of local flow. |
|
||||||
| test.cpp:1045:9:1045:11 | ref arg buf | PostUpdateNode should not be the target of local flow. |
|
| test.cpp:1045:9:1045:11 | ref arg buf | PostUpdateNode should not be the target of local flow. |
|
||||||
|
| test.cpp:1051:5:1051:11 | content [post update] | PostUpdateNode should not be the target of local flow. |
|
||||||
|
| test.cpp:1052:9:1052:9 | a [inner post update] | PostUpdateNode should not be the target of local flow. |
|
||||||
viableImplInCallContextTooLarge
|
viableImplInCallContextTooLarge
|
||||||
uniqueParameterNodeAtPosition
|
uniqueParameterNodeAtPosition
|
||||||
uniqueParameterNodePosition
|
uniqueParameterNodePosition
|
||||||
|
|||||||
@@ -0,0 +1,197 @@
|
|||||||
|
| example.c:15:37:15:37 | **b | example.c:15:37:15:37 | **b |
|
||||||
|
| example.c:15:37:15:37 | **b | example.c:15:37:15:37 | **b |
|
||||||
|
| example.c:15:37:15:37 | **b | example.c:15:37:15:37 | *b |
|
||||||
|
| example.c:15:37:15:37 | **b | example.c:19:6:19:6 | *b |
|
||||||
|
| example.c:15:37:15:37 | *b | example.c:15:37:15:37 | **b |
|
||||||
|
| example.c:15:37:15:37 | *b | example.c:15:37:15:37 | *b |
|
||||||
|
| example.c:15:37:15:37 | *b | example.c:15:37:15:37 | *b |
|
||||||
|
| example.c:15:37:15:37 | *b | example.c:15:37:15:37 | b |
|
||||||
|
| example.c:15:37:15:37 | *b | example.c:19:6:19:6 | b |
|
||||||
|
| example.c:15:37:15:37 | b | example.c:15:37:15:37 | *b |
|
||||||
|
| example.c:15:37:15:37 | b | example.c:15:37:15:37 | b |
|
||||||
|
| example.c:15:37:15:37 | b | example.c:15:37:15:37 | b |
|
||||||
|
| example.c:15:37:15:37 | b | example.c:19:6:19:6 | b |
|
||||||
|
| example.c:15:44:15:46 | pos | example.c:24:24:24:26 | pos |
|
||||||
|
| example.c:17:11:17:16 | *definition of coords | example.c:17:11:17:16 | *definition of coords |
|
||||||
|
| example.c:17:11:17:16 | *definition of coords | example.c:17:11:17:16 | *definition of coords |
|
||||||
|
| example.c:17:11:17:16 | *definition of coords | example.c:17:11:17:16 | *definition of coords |
|
||||||
|
| example.c:17:11:17:16 | *definition of coords | example.c:17:11:17:16 | *definition of coords |
|
||||||
|
| example.c:17:11:17:16 | *definition of coords | example.c:24:13:24:18 | *coords |
|
||||||
|
| example.c:17:11:17:16 | *definition of coords [post update] | example.c:17:11:17:16 | *definition of coords |
|
||||||
|
| example.c:17:11:17:16 | *definition of coords [post update] | example.c:24:13:24:18 | *coords |
|
||||||
|
| example.c:17:11:17:16 | definition of coords | example.c:17:11:17:16 | *definition of coords |
|
||||||
|
| example.c:17:11:17:16 | definition of coords | example.c:17:11:17:16 | definition of coords |
|
||||||
|
| example.c:17:11:17:16 | definition of coords | example.c:17:11:17:16 | definition of coords |
|
||||||
|
| example.c:17:11:17:16 | definition of coords | example.c:17:11:17:16 | definition of coords |
|
||||||
|
| example.c:17:11:17:16 | definition of coords | example.c:17:11:17:16 | definition of coords |
|
||||||
|
| example.c:17:11:17:16 | definition of coords | example.c:24:13:24:18 | coords |
|
||||||
|
| example.c:17:11:17:16 | definition of coords [post update] | example.c:17:11:17:16 | definition of coords |
|
||||||
|
| example.c:17:11:17:16 | definition of coords [post update] | example.c:24:13:24:18 | coords |
|
||||||
|
| example.c:17:19:17:22 | {...} | example.c:17:19:17:22 | {...} |
|
||||||
|
| example.c:17:21:17:21 | 0 | example.c:17:21:17:21 | 0 |
|
||||||
|
| example.c:19:6:19:6 | *b | example.c:15:37:15:37 | *b |
|
||||||
|
| example.c:19:6:19:6 | *b [post update] | example.c:15:37:15:37 | *b |
|
||||||
|
| example.c:19:6:19:6 | *b [post update] | example.c:19:6:19:6 | *b |
|
||||||
|
| example.c:19:6:19:6 | b [post update] | example.c:19:6:19:6 | b |
|
||||||
|
| example.c:24:2:24:7 | *coords | example.c:26:18:26:24 | *& ... |
|
||||||
|
| example.c:24:2:24:7 | *coords [post update] | example.c:26:18:26:24 | *& ... |
|
||||||
|
| example.c:24:2:24:7 | coords | example.c:26:18:26:24 | & ... |
|
||||||
|
| example.c:24:2:24:7 | coords [post update] | example.c:26:18:26:24 | & ... |
|
||||||
|
| example.c:24:13:24:18 | *coords | example.c:24:2:24:7 | *coords |
|
||||||
|
| example.c:24:13:24:18 | *coords [post update] | example.c:24:2:24:7 | *coords |
|
||||||
|
| example.c:24:13:24:18 | coords | example.c:24:2:24:7 | coords |
|
||||||
|
| example.c:24:13:24:18 | coords [post update] | example.c:24:2:24:7 | coords |
|
||||||
|
| example.c:24:13:24:30 | ... = ... | example.c:24:2:24:30 | ... = ... |
|
||||||
|
| example.c:24:20:24:20 | *y | example.c:24:20:24:20 | *y |
|
||||||
|
| example.c:24:20:24:20 | y | example.c:24:20:24:20 | y |
|
||||||
|
| example.c:24:20:24:20 | y | example.c:24:20:24:20 | y |
|
||||||
|
| example.c:24:24:24:26 | pos | example.c:28:14:28:25 | & ... |
|
||||||
|
| example.c:24:24:24:26 | pos | example.c:28:14:28:25 | *& ... |
|
||||||
|
| example.c:24:24:24:30 | ... + ... | example.c:24:13:24:30 | ... = ... |
|
||||||
|
| example.c:26:13:26:16 | call to getX | example.c:26:2:26:25 | ... = ... |
|
||||||
|
| example.c:26:18:26:24 | & ... | example.c:26:2:26:7 | coords |
|
||||||
|
| example.c:26:18:26:24 | *& ... | example.c:26:2:26:7 | *coords |
|
||||||
|
| example.c:26:18:26:24 | getX output argument | example.c:26:2:26:7 | *coords |
|
||||||
|
| example.c:26:18:26:24 | pointer to getX output argument | example.c:26:2:26:7 | coords |
|
||||||
|
| example.c:26:19:26:24 | *coords | example.c:26:18:26:24 | *& ... |
|
||||||
|
| example.c:26:19:26:24 | coords | example.c:26:18:26:24 | & ... |
|
||||||
|
| example.c:28:22:28:25 | & ... | example.c:28:14:28:25 | & ... |
|
||||||
|
| example.c:28:22:28:25 | *& ... | example.c:28:14:28:25 | *& ... |
|
||||||
|
| example.c:28:23:28:25 | *pos | example.c:28:22:28:25 | *& ... |
|
||||||
|
| example.c:28:23:28:25 | pos | example.c:28:22:28:25 | & ... |
|
||||||
|
| test.cpp:6:12:6:17 | call to source | test.cpp:6:12:6:17 | call to source |
|
||||||
|
| test.cpp:6:12:6:17 | call to source | test.cpp:7:8:7:9 | t1 |
|
||||||
|
| test.cpp:7:8:7:9 | t1 | test.cpp:8:8:8:9 | t1 |
|
||||||
|
| test.cpp:7:8:7:9 | t1 | test.cpp:8:8:8:9 | t1 |
|
||||||
|
| test.cpp:8:3:8:9 | ... = ... | test.cpp:10:8:10:9 | t2 |
|
||||||
|
| test.cpp:8:8:8:9 | t1 | test.cpp:8:3:8:9 | ... = ... |
|
||||||
|
| test.cpp:8:8:8:9 | t1 | test.cpp:9:8:9:9 | t1 |
|
||||||
|
| test.cpp:8:8:8:9 | t1 | test.cpp:9:8:9:9 | t1 |
|
||||||
|
| test.cpp:9:8:9:9 | t1 | test.cpp:11:7:11:8 | t1 |
|
||||||
|
| test.cpp:9:8:9:9 | t1 | test.cpp:11:7:11:8 | t1 |
|
||||||
|
| test.cpp:10:8:10:9 | t2 | test.cpp:13:10:13:11 | t2 |
|
||||||
|
| test.cpp:10:8:10:9 | t2 | test.cpp:15:3:15:6 | Phi |
|
||||||
|
| test.cpp:10:8:10:9 | t2 | test.cpp:15:3:15:6 | Phi |
|
||||||
|
| test.cpp:11:7:11:8 | t1 | test.cpp:21:8:21:9 | t1 |
|
||||||
|
| test.cpp:12:5:12:10 | ... = ... | test.cpp:13:10:13:11 | t2 |
|
||||||
|
| test.cpp:12:10:12:10 | 0 | test.cpp:12:5:12:10 | ... = ... |
|
||||||
|
| test.cpp:13:10:13:11 | t2 | test.cpp:15:3:15:6 | Phi |
|
||||||
|
| test.cpp:13:10:13:11 | t2 | test.cpp:15:3:15:6 | Phi |
|
||||||
|
| test.cpp:15:3:15:6 | Phi | test.cpp:15:8:15:9 | t2 |
|
||||||
|
| test.cpp:15:3:15:6 | Phi | test.cpp:15:8:15:9 | t2 |
|
||||||
|
| test.cpp:15:8:15:9 | t2 | test.cpp:23:19:23:19 | Phi |
|
||||||
|
| test.cpp:15:8:15:9 | t2 | test.cpp:23:19:23:19 | Phi |
|
||||||
|
| test.cpp:17:3:17:8 | ... = ... | test.cpp:21:8:21:9 | t1 |
|
||||||
|
| test.cpp:17:8:17:8 | 0 | test.cpp:17:3:17:8 | ... = ... |
|
||||||
|
| test.cpp:21:8:21:9 | t1 | test.cpp:23:19:23:19 | Phi |
|
||||||
|
| test.cpp:21:8:21:9 | t1 | test.cpp:23:19:23:19 | Phi |
|
||||||
|
| test.cpp:23:15:23:16 | 0 | test.cpp:23:15:23:16 | 0 |
|
||||||
|
| test.cpp:23:15:23:16 | 0 | test.cpp:23:19:23:19 | Phi |
|
||||||
|
| test.cpp:23:19:23:19 | Phi | test.cpp:23:19:23:19 | i |
|
||||||
|
| test.cpp:23:19:23:19 | Phi | test.cpp:23:19:23:19 | i |
|
||||||
|
| test.cpp:23:19:23:19 | Phi | test.cpp:23:23:23:24 | t1 |
|
||||||
|
| test.cpp:23:19:23:19 | Phi | test.cpp:23:23:23:24 | t1 |
|
||||||
|
| test.cpp:23:19:23:19 | Phi | test.cpp:24:10:24:11 | t2 |
|
||||||
|
| test.cpp:23:19:23:19 | Phi | test.cpp:24:10:24:11 | t2 |
|
||||||
|
| test.cpp:23:19:23:19 | i | test.cpp:23:27:23:27 | i |
|
||||||
|
| test.cpp:23:19:23:19 | i | test.cpp:23:27:23:27 | i |
|
||||||
|
| test.cpp:23:23:23:24 | t1 | test.cpp:23:19:23:19 | Phi |
|
||||||
|
| test.cpp:23:23:23:24 | t1 | test.cpp:26:8:26:9 | t1 |
|
||||||
|
| test.cpp:23:23:23:24 | t1 | test.cpp:26:8:26:9 | t1 |
|
||||||
|
| test.cpp:23:27:23:27 | *i | test.cpp:23:27:23:27 | *i |
|
||||||
|
| test.cpp:23:27:23:27 | *i | test.cpp:23:27:23:27 | i |
|
||||||
|
| test.cpp:23:27:23:27 | i | test.cpp:23:19:23:19 | Phi |
|
||||||
|
| test.cpp:23:27:23:27 | i | test.cpp:23:27:23:27 | i |
|
||||||
|
| test.cpp:23:27:23:27 | i | test.cpp:23:27:23:27 | i |
|
||||||
|
| test.cpp:23:27:23:29 | ... ++ | test.cpp:23:19:23:19 | Phi |
|
||||||
|
| test.cpp:23:27:23:29 | ... ++ | test.cpp:23:27:23:29 | ... ++ |
|
||||||
|
| test.cpp:24:5:24:11 | ... = ... | test.cpp:23:19:23:19 | Phi |
|
||||||
|
| test.cpp:24:10:24:11 | t2 | test.cpp:23:19:23:19 | Phi |
|
||||||
|
| test.cpp:24:10:24:11 | t2 | test.cpp:23:19:23:19 | Phi |
|
||||||
|
| test.cpp:24:10:24:11 | t2 | test.cpp:24:5:24:11 | ... = ... |
|
||||||
|
| test.cpp:382:48:382:54 | source1 | test.cpp:384:16:384:23 | *& ... |
|
||||||
|
| test.cpp:383:12:383:13 | 0 | test.cpp:383:12:383:13 | 0 |
|
||||||
|
| test.cpp:383:12:383:13 | 0 | test.cpp:384:10:384:13 | *& ... |
|
||||||
|
| test.cpp:384:10:384:13 | & ... | test.cpp:384:3:384:8 | call to memcpy |
|
||||||
|
| test.cpp:384:10:384:13 | & ... | test.cpp:384:10:384:13 | & ... |
|
||||||
|
| test.cpp:384:10:384:13 | & ... | test.cpp:385:8:385:10 | tmp |
|
||||||
|
| test.cpp:384:10:384:13 | *& ... | test.cpp:384:10:384:13 | *& ... |
|
||||||
|
| test.cpp:384:10:384:13 | memcpy output argument | test.cpp:385:8:385:10 | tmp |
|
||||||
|
| test.cpp:384:10:384:13 | pointer to memcpy output argument | test.cpp:385:8:385:10 | tmp |
|
||||||
|
| test.cpp:384:11:384:13 | *tmp | test.cpp:384:10:384:13 | *& ... |
|
||||||
|
| test.cpp:384:11:384:13 | tmp | test.cpp:384:10:384:13 | & ... |
|
||||||
|
| test.cpp:384:16:384:23 | & ... | test.cpp:384:16:384:23 | & ... |
|
||||||
|
| test.cpp:384:16:384:23 | *& ... | test.cpp:384:3:384:8 | **call to memcpy |
|
||||||
|
| test.cpp:384:16:384:23 | *& ... | test.cpp:384:3:384:8 | *call to memcpy |
|
||||||
|
| test.cpp:384:16:384:23 | *& ... | test.cpp:384:10:384:13 | memcpy output argument |
|
||||||
|
| test.cpp:384:16:384:23 | *& ... | test.cpp:384:16:384:23 | *& ... |
|
||||||
|
| test.cpp:384:16:384:23 | **(const void *)... | test.cpp:384:3:384:8 | **call to memcpy |
|
||||||
|
| test.cpp:384:16:384:23 | **(const void *)... | test.cpp:384:10:384:13 | memcpy output argument |
|
||||||
|
| test.cpp:384:17:384:23 | *source1 | test.cpp:384:16:384:23 | *& ... |
|
||||||
|
| test.cpp:384:17:384:23 | source1 | test.cpp:384:16:384:23 | & ... |
|
||||||
|
| test.cpp:388:53:388:59 | source1 | test.cpp:391:16:391:23 | *& ... |
|
||||||
|
| test.cpp:388:66:388:66 | b | test.cpp:393:7:393:7 | b |
|
||||||
|
| test.cpp:389:12:389:13 | 0 | test.cpp:389:12:389:13 | 0 |
|
||||||
|
| test.cpp:389:12:389:13 | 0 | test.cpp:390:18:390:21 | *& ... |
|
||||||
|
| test.cpp:390:18:390:21 | & ... | test.cpp:390:18:390:21 | & ... |
|
||||||
|
| test.cpp:390:18:390:21 | & ... | test.cpp:391:10:391:13 | & ... |
|
||||||
|
| test.cpp:390:18:390:21 | *& ... | test.cpp:390:18:390:21 | *& ... |
|
||||||
|
| test.cpp:390:18:390:21 | *& ... | test.cpp:391:10:391:13 | *& ... |
|
||||||
|
| test.cpp:390:19:390:21 | *tmp | test.cpp:390:18:390:21 | *& ... |
|
||||||
|
| test.cpp:390:19:390:21 | tmp | test.cpp:390:18:390:21 | & ... |
|
||||||
|
| test.cpp:391:10:391:13 | & ... | test.cpp:391:3:391:8 | call to memcpy |
|
||||||
|
| test.cpp:391:10:391:13 | & ... | test.cpp:391:10:391:13 | & ... |
|
||||||
|
| test.cpp:391:10:391:13 | & ... | test.cpp:392:8:392:10 | tmp |
|
||||||
|
| test.cpp:391:10:391:13 | *& ... | test.cpp:391:10:391:13 | *& ... |
|
||||||
|
| test.cpp:391:10:391:13 | memcpy output argument | test.cpp:392:8:392:10 | tmp |
|
||||||
|
| test.cpp:391:10:391:13 | pointer to memcpy output argument | test.cpp:392:8:392:10 | tmp |
|
||||||
|
| test.cpp:391:11:391:13 | *tmp | test.cpp:391:10:391:13 | *& ... |
|
||||||
|
| test.cpp:391:11:391:13 | tmp | test.cpp:391:10:391:13 | & ... |
|
||||||
|
| test.cpp:391:16:391:23 | & ... | test.cpp:391:16:391:23 | & ... |
|
||||||
|
| test.cpp:391:16:391:23 | *& ... | test.cpp:391:3:391:8 | **call to memcpy |
|
||||||
|
| test.cpp:391:16:391:23 | *& ... | test.cpp:391:3:391:8 | *call to memcpy |
|
||||||
|
| test.cpp:391:16:391:23 | *& ... | test.cpp:391:10:391:13 | memcpy output argument |
|
||||||
|
| test.cpp:391:16:391:23 | *& ... | test.cpp:391:16:391:23 | *& ... |
|
||||||
|
| test.cpp:391:16:391:23 | **(const void *)... | test.cpp:391:3:391:8 | **call to memcpy |
|
||||||
|
| test.cpp:391:16:391:23 | **(const void *)... | test.cpp:391:10:391:13 | memcpy output argument |
|
||||||
|
| test.cpp:391:17:391:23 | *source1 | test.cpp:391:16:391:23 | *& ... |
|
||||||
|
| test.cpp:391:17:391:23 | source1 | test.cpp:391:16:391:23 | & ... |
|
||||||
|
| test.cpp:392:8:392:10 | tmp | test.cpp:394:10:394:12 | tmp |
|
||||||
|
| test.cpp:392:8:392:10 | tmp | test.cpp:394:10:394:12 | tmp |
|
||||||
|
| test.cpp:487:67:487:67 | **s | test.cpp:487:67:487:67 | **s |
|
||||||
|
| test.cpp:487:67:487:67 | **s | test.cpp:487:67:487:67 | **s |
|
||||||
|
| test.cpp:487:67:487:67 | **s | test.cpp:487:67:487:67 | *s |
|
||||||
|
| test.cpp:487:67:487:67 | **s | test.cpp:488:21:488:21 | *s |
|
||||||
|
| test.cpp:487:67:487:67 | *s | test.cpp:487:67:487:67 | **s |
|
||||||
|
| test.cpp:487:67:487:67 | *s | test.cpp:487:67:487:67 | *s |
|
||||||
|
| test.cpp:487:67:487:67 | *s | test.cpp:487:67:487:67 | *s |
|
||||||
|
| test.cpp:487:67:487:67 | *s | test.cpp:487:67:487:67 | s |
|
||||||
|
| test.cpp:487:67:487:67 | *s | test.cpp:488:21:488:21 | s |
|
||||||
|
| test.cpp:487:67:487:67 | s | test.cpp:487:67:487:67 | *s |
|
||||||
|
| test.cpp:487:67:487:67 | s | test.cpp:487:67:487:67 | s |
|
||||||
|
| test.cpp:487:67:487:67 | s | test.cpp:487:67:487:67 | s |
|
||||||
|
| test.cpp:487:67:487:67 | s | test.cpp:488:21:488:21 | s |
|
||||||
|
| test.cpp:488:21:488:21 | *s | test.cpp:489:20:489:20 | *s |
|
||||||
|
| test.cpp:488:21:488:21 | *s [post update] | test.cpp:489:20:489:20 | *s |
|
||||||
|
| test.cpp:488:21:488:21 | s | test.cpp:489:20:489:20 | s |
|
||||||
|
| test.cpp:488:21:488:21 | s | test.cpp:489:20:489:20 | s |
|
||||||
|
| test.cpp:488:21:488:21 | s [post update] | test.cpp:489:20:489:20 | s |
|
||||||
|
| test.cpp:488:24:488:30 | *content | test.cpp:488:21:488:30 | *content |
|
||||||
|
| test.cpp:488:24:488:30 | content | test.cpp:488:21:488:30 | content |
|
||||||
|
| test.cpp:489:20:489:20 | *s | test.cpp:487:67:487:67 | *s |
|
||||||
|
| test.cpp:489:20:489:20 | *s [post update] | test.cpp:487:67:487:67 | *s |
|
||||||
|
| test.cpp:489:20:489:20 | *s [post update] | test.cpp:489:20:489:20 | *s |
|
||||||
|
| test.cpp:489:20:489:20 | s [post update] | test.cpp:489:20:489:20 | s |
|
||||||
|
| test.cpp:489:23:489:29 | *content | test.cpp:489:23:489:29 | *content |
|
||||||
|
| test.cpp:489:23:489:29 | *content | test.cpp:490:8:490:17 | * ... |
|
||||||
|
| test.cpp:489:23:489:29 | content | test.cpp:489:23:489:29 | content |
|
||||||
|
| test.cpp:489:23:489:29 | content | test.cpp:490:9:490:17 | p_content |
|
||||||
|
| test.cpp:1050:12:1050:12 | definition of a | test.cpp:1051:3:1051:3 | *a |
|
||||||
|
| test.cpp:1051:3:1051:3 | *a | test.cpp:1052:8:1052:9 | *& ... |
|
||||||
|
| test.cpp:1051:3:1051:3 | *a [post update] | test.cpp:1052:8:1052:9 | *& ... |
|
||||||
|
| test.cpp:1051:3:1051:3 | a | test.cpp:1052:8:1052:9 | & ... |
|
||||||
|
| test.cpp:1051:3:1051:3 | a [post update] | test.cpp:1052:8:1052:9 | & ... |
|
||||||
|
| test.cpp:1051:15:1051:21 | 0 | test.cpp:1051:3:1051:21 | ... = ... |
|
||||||
|
| test.cpp:1051:15:1051:21 | *0 | test.cpp:1051:3:1051:21 | *... = ... |
|
||||||
|
| test.cpp:1052:9:1052:9 | *a | test.cpp:1052:8:1052:9 | *& ... |
|
||||||
|
| test.cpp:1052:9:1052:9 | a | test.cpp:1052:8:1052:9 | & ... |
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import cpp
|
||||||
|
import semmle.code.cpp.dataflow.new.DataFlow
|
||||||
|
|
||||||
|
from DataFlow::Node nodeFrom, DataFlow::Node nodeTo
|
||||||
|
where
|
||||||
|
DataFlow::localFlowStep(nodeFrom, nodeTo) and
|
||||||
|
nodeFrom.getFunction().getName().matches("%\\_with\\_local\\_flow")
|
||||||
|
select nodeFrom, nodeTo
|
||||||
@@ -81,3 +81,10 @@ WARNING: Module DataFlow has been deprecated and may be removed in future (local
|
|||||||
| test.cpp:488:21:488:21 | s [post update] | test.cpp:489:20:489:20 | s |
|
| test.cpp:488:21:488:21 | s [post update] | test.cpp:489:20:489:20 | s |
|
||||||
| test.cpp:488:24:488:30 | ref arg content | test.cpp:489:23:489:29 | content |
|
| test.cpp:488:24:488:30 | ref arg content | test.cpp:489:23:489:29 | content |
|
||||||
| test.cpp:489:23:489:29 | content | test.cpp:490:9:490:17 | p_content |
|
| test.cpp:489:23:489:29 | content | test.cpp:490:9:490:17 | p_content |
|
||||||
|
| test.cpp:1050:12:1050:12 | a | test.cpp:1051:3:1051:3 | a |
|
||||||
|
| test.cpp:1050:12:1050:12 | a | test.cpp:1052:9:1052:9 | a |
|
||||||
|
| test.cpp:1051:3:1051:3 | a [post update] | test.cpp:1052:9:1052:9 | a |
|
||||||
|
| test.cpp:1051:3:1051:21 | ... = ... | test.cpp:1051:5:1051:11 | content [post update] |
|
||||||
|
| test.cpp:1051:15:1051:21 | 0 | test.cpp:1051:3:1051:21 | ... = ... |
|
||||||
|
| test.cpp:1052:8:1052:9 | ref arg & ... | test.cpp:1052:9:1052:9 | a [inner post update] |
|
||||||
|
| test.cpp:1052:9:1052:9 | a | test.cpp:1052:8:1052:9 | & ... |
|
||||||
|
|||||||
@@ -123,6 +123,7 @@ astFlow
|
|||||||
| test.cpp:842:11:842:16 | call to source | test.cpp:844:8:844:8 | y |
|
| test.cpp:842:11:842:16 | call to source | test.cpp:844:8:844:8 | y |
|
||||||
| test.cpp:846:13:846:27 | call to indirect_source | test.cpp:848:23:848:25 | rpx |
|
| test.cpp:846:13:846:27 | call to indirect_source | test.cpp:848:23:848:25 | rpx |
|
||||||
| test.cpp:860:54:860:59 | call to source | test.cpp:861:10:861:37 | static_local_pointer_dynamic |
|
| test.cpp:860:54:860:59 | call to source | test.cpp:861:10:861:37 | static_local_pointer_dynamic |
|
||||||
|
| test.cpp:1050:12:1050:12 | a | test.cpp:1052:8:1052:9 | & ... |
|
||||||
| true_upon_entry.cpp:17:11:17:16 | call to source | true_upon_entry.cpp:21:8:21:8 | x |
|
| true_upon_entry.cpp:17:11:17:16 | call to source | true_upon_entry.cpp:21:8:21:8 | x |
|
||||||
| true_upon_entry.cpp:27:9:27:14 | call to source | true_upon_entry.cpp:29:8:29:8 | x |
|
| true_upon_entry.cpp:27:9:27:14 | call to source | true_upon_entry.cpp:29:8:29:8 | x |
|
||||||
| true_upon_entry.cpp:33:11:33:16 | call to source | true_upon_entry.cpp:39:8:39:8 | x |
|
| true_upon_entry.cpp:33:11:33:16 | call to source | true_upon_entry.cpp:39:8:39:8 | x |
|
||||||
|
|||||||
@@ -1045,3 +1045,9 @@ void memset_test(char* buf) { // $ ast-def=buf ir-def=*buf
|
|||||||
memset(buf, source(), 10);
|
memset(buf, source(), 10);
|
||||||
sink(*buf); // $ ir MISSING: ast
|
sink(*buf); // $ ir MISSING: ast
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void flow_out_of_address_with_local_flow() {
|
||||||
|
MyStruct a;
|
||||||
|
a.content = nullptr;
|
||||||
|
sink(&a); // $ SPURIOUS: ast
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user