mirror of
https://github.com/github/codeql.git
synced 2026-05-16 04:09:27 +02:00
Compare commits
628 Commits
codeql-cli
...
dependabot
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4ffe164a8d | ||
|
|
9a4bc69843 | ||
|
|
f99f26f908 | ||
|
|
f6c81ff30a | ||
|
|
02f8984aff | ||
|
|
b63e34d467 | ||
|
|
ec726f5941 | ||
|
|
208ae7aa01 | ||
|
|
be746b775b | ||
|
|
bc518c08c7 | ||
|
|
06ea72ccc7 | ||
|
|
57086f60b9 | ||
|
|
6452cc549f | ||
|
|
f59bacab30 | ||
|
|
9c6276ef48 | ||
|
|
21f2c81f24 | ||
|
|
d30aab47ea | ||
|
|
fef758998c | ||
|
|
b9592fef2d | ||
|
|
7a33e2f539 | ||
|
|
34101b5ca0 | ||
|
|
07db9cf3c4 | ||
|
|
92c9a8e146 | ||
|
|
21cb11ea5d | ||
|
|
4c525ce7ab | ||
|
|
0f794b57ed | ||
|
|
2e987f8d78 | ||
|
|
2139b97628 | ||
|
|
7fc1d53ede | ||
|
|
50d83ada95 | ||
|
|
10678d3a42 | ||
|
|
7d538988a6 | ||
|
|
7f17b7716d | ||
|
|
b57fa1bffa | ||
|
|
662b1e7df6 | ||
|
|
e0b06c8e72 | ||
|
|
8d6aceb008 | ||
|
|
3fad6bdc0c | ||
|
|
518d170acd | ||
|
|
b9ad36c11d | ||
|
|
4ca071210b | ||
|
|
1ddf81c58c | ||
|
|
c155394f25 | ||
|
|
2c76e6e637 | ||
|
|
2ff5c2c234 | ||
|
|
98d8cd1d6d | ||
|
|
e8e46accc0 | ||
|
|
06f0c1189f | ||
|
|
082dc61620 | ||
|
|
2e7da72277 | ||
|
|
5b17d8cf76 | ||
|
|
f2a0724620 | ||
|
|
b8222167d2 | ||
|
|
6efd844180 | ||
|
|
34f405f465 | ||
|
|
d4a0846c6c | ||
|
|
d180900ab4 | ||
|
|
97670b3674 | ||
|
|
ff48ac5434 | ||
|
|
af63e63686 | ||
|
|
19faf8f30b | ||
|
|
3aaee9d981 | ||
|
|
8e19b05a25 | ||
|
|
1ac9e5a2a4 | ||
|
|
9cb1c89a02 | ||
|
|
a57f803b37 | ||
|
|
1130870168 | ||
|
|
8ddfee9971 | ||
|
|
8df4dfb585 | ||
|
|
2f7526d70b | ||
|
|
eeb09ae389 | ||
|
|
3c4a386f3f | ||
|
|
e3dbf5b022 | ||
|
|
72534e882b | ||
|
|
a99b3f2c3b | ||
|
|
92718a98d0 | ||
|
|
e70727524a | ||
|
|
d6055754b6 | ||
|
|
c24b43d01e | ||
|
|
179a4cd41a | ||
|
|
a929c0bf24 | ||
|
|
427ccee3b9 | ||
|
|
22f16dda85 | ||
|
|
db0a3e38e2 | ||
|
|
e7edf15031 | ||
|
|
4c77e0f315 | ||
|
|
7124cd4e6e | ||
|
|
c076992b83 | ||
|
|
659d8e7c90 | ||
|
|
1e8de0511b | ||
|
|
bce0a4d2a7 | ||
|
|
2160910d56 | ||
|
|
a5c8a5b5f8 | ||
|
|
f9f1d9eecc | ||
|
|
d52e9bc18c | ||
|
|
b8b841cfba | ||
|
|
df9f8ee386 | ||
|
|
99f4930e24 | ||
|
|
e9df9147ad | ||
|
|
f32f85399a | ||
|
|
c56feb7644 | ||
|
|
7ef60a8649 | ||
|
|
ca2838b361 | ||
|
|
7a6ab70091 | ||
|
|
7d6e08ecf1 | ||
|
|
dfa6d20072 | ||
|
|
821cc0e875 | ||
|
|
7094fb07a4 | ||
|
|
52809133f5 | ||
|
|
056aa342fe | ||
|
|
f58a6e5d3a | ||
|
|
f11815c633 | ||
|
|
52cfd49087 | ||
|
|
8c1c039edf | ||
|
|
c9e0927992 | ||
|
|
d5f667e585 | ||
|
|
b758732a28 | ||
|
|
ba3fadbf20 | ||
|
|
d7d1554461 | ||
|
|
12e0f3f359 | ||
|
|
0bb6ff58cc | ||
|
|
b9c0aca11a | ||
|
|
ee3674cb80 | ||
|
|
3ee369b710 | ||
|
|
e16bb226c0 | ||
|
|
48bf4fd82a | ||
|
|
c271755985 | ||
|
|
a16c43881b | ||
|
|
39e0382089 | ||
|
|
22e012c6f4 | ||
|
|
1b6f3a43ef | ||
|
|
b8c44be599 | ||
|
|
84d1828a9c | ||
|
|
ca7017f3d7 | ||
|
|
f2e7dca65c | ||
|
|
b9b3b3a0b5 | ||
|
|
4a39055322 | ||
|
|
6552c849f0 | ||
|
|
00d8a10051 | ||
|
|
da7da80b2b | ||
|
|
5db30c9947 | ||
|
|
6fb10555ff | ||
|
|
bbd02b855b | ||
|
|
48a03e2a04 | ||
|
|
a92d97744f | ||
|
|
ef6c1a9968 | ||
|
|
72142b51f7 | ||
|
|
c06d4d2647 | ||
|
|
4a001f960f | ||
|
|
1253553aec | ||
|
|
68dfa5c83b | ||
|
|
25a20f74f0 | ||
|
|
5a65282241 | ||
|
|
2e04d4b888 | ||
|
|
79499c240a | ||
|
|
267a46d01b | ||
|
|
341059d2d0 | ||
|
|
79841bbc00 | ||
|
|
3c3c58b0a9 | ||
|
|
9bf1072a01 | ||
|
|
a5f23ade8c | ||
|
|
017b6f2e44 | ||
|
|
6a6bb5ebf9 | ||
|
|
b631138b63 | ||
|
|
093d36ebe6 | ||
|
|
c7349740f0 | ||
|
|
efa797a21d | ||
|
|
77d4f5a2dc | ||
|
|
edf88b34da | ||
|
|
0215ea3ee3 | ||
|
|
35ac66d3aa | ||
|
|
219fe03637 | ||
|
|
dbb8bb86ba | ||
|
|
afb2243984 | ||
|
|
a7e426d89f | ||
|
|
fde51e0c29 | ||
|
|
69ed88bccd | ||
|
|
97e1c96200 | ||
|
|
46ba1f9160 | ||
|
|
5d74ad5bc6 | ||
|
|
4013f00b19 | ||
|
|
e0e5319b11 | ||
|
|
d8007a85e6 | ||
|
|
512e27187e | ||
|
|
f2bad1e6e1 | ||
|
|
c5360ba46c | ||
|
|
be9c1d074f | ||
|
|
097681e705 | ||
|
|
63e8061917 | ||
|
|
8bbb0ec954 | ||
|
|
d81b9aa5fd | ||
|
|
fd7093e74d | ||
|
|
a6de855549 | ||
|
|
3b9eba2afc | ||
|
|
4dca9aa958 | ||
|
|
84bef5d4bc | ||
|
|
da99d3660d | ||
|
|
7f6fd34d46 | ||
|
|
d23a3f821e | ||
|
|
2340369e2d | ||
|
|
70c1b58492 | ||
|
|
f3dc0412b5 | ||
|
|
a3e9aed00a | ||
|
|
e96ba4806b | ||
|
|
76346eccd8 | ||
|
|
feb45e5731 | ||
|
|
66ca10c338 | ||
|
|
fa61f6f3df | ||
|
|
0561a63003 | ||
|
|
ff41917147 | ||
|
|
838f3b90e7 | ||
|
|
3c36a9e308 | ||
|
|
eb81743fb5 | ||
|
|
d9ef9f82e1 | ||
|
|
92a719092a | ||
|
|
ffa5110522 | ||
|
|
c9fa7fa283 | ||
|
|
8ef4be49aa | ||
|
|
e6996ea29a | ||
|
|
579c871b69 | ||
|
|
63c71b418c | ||
|
|
3e7a966c0d | ||
|
|
926725a87f | ||
|
|
9bf4262dbb | ||
|
|
c82f75604a | ||
|
|
ea77c0d86c | ||
|
|
ec1d034ee0 | ||
|
|
37a8fc85eb | ||
|
|
b5bf1c578c | ||
|
|
f3898329d6 | ||
|
|
acd6f4156b | ||
|
|
e22d3a1074 | ||
|
|
212374b94b | ||
|
|
aa7a730041 | ||
|
|
1c5afb2306 | ||
|
|
5b30e945ef | ||
|
|
2b3111441d | ||
|
|
99a4fe4828 | ||
|
|
aa28c94562 | ||
|
|
501485b9f6 | ||
|
|
db491fc985 | ||
|
|
1950fd33db | ||
|
|
91b6801db1 | ||
|
|
ea30f02271 | ||
|
|
f41c30e335 | ||
|
|
ddebdad9e1 | ||
|
|
f8f8991d36 | ||
|
|
3c129fcd23 | ||
|
|
6001c735ff | ||
|
|
5a97348e78 | ||
|
|
0eccd902c2 | ||
|
|
45eb14975a | ||
|
|
badfa1a5c5 | ||
|
|
b475f14575 | ||
|
|
d4ba2d68f9 | ||
|
|
05a77a2005 | ||
|
|
ef345a3279 | ||
|
|
2357ef07cc | ||
|
|
088913d925 | ||
|
|
83155df1f7 | ||
|
|
b7992ed8cd | ||
|
|
219ea28217 | ||
|
|
fbf40ef02a | ||
|
|
370c5157f1 | ||
|
|
2782d90d0f | ||
|
|
ad5ab9f270 | ||
|
|
4474e252fe | ||
|
|
18d2f586b3 | ||
|
|
189c16095d | ||
|
|
a604a68fe9 | ||
|
|
4e2a93df55 | ||
|
|
6a904eddd4 | ||
|
|
fe032a5834 | ||
|
|
cfd4be6b4e | ||
|
|
93a28cbfaf | ||
|
|
13ce515aab | ||
|
|
8807217e49 | ||
|
|
daefd5988e | ||
|
|
d9ea78bfb8 | ||
|
|
f02abb3e93 | ||
|
|
a2f45f1b5b | ||
|
|
bb5bfda14b | ||
|
|
e152f08468 | ||
|
|
16cd3a8bc0 | ||
|
|
7d30e3ca5e | ||
|
|
319e3d1ba4 | ||
|
|
8380474acd | ||
|
|
a3d15dbaa3 | ||
|
|
ec7e6e8e03 | ||
|
|
4e63b83fd3 | ||
|
|
ea1fc43732 | ||
|
|
13959ab91e | ||
|
|
df7379c0d2 | ||
|
|
7795badd18 | ||
|
|
e695477f4f | ||
|
|
627654cff9 | ||
|
|
f2cc0da936 | ||
|
|
bd0d69ffca | ||
|
|
97ed67e284 | ||
|
|
db33dadb8e | ||
|
|
1139059d77 | ||
|
|
92f26027e1 | ||
|
|
85875c2879 | ||
|
|
17e6fd2fe9 | ||
|
|
5d75b255a8 | ||
|
|
26e8701ae3 | ||
|
|
cdb41588a9 | ||
|
|
1eccb8ea93 | ||
|
|
66611323e2 | ||
|
|
d804fc5168 | ||
|
|
f223c957ba | ||
|
|
86bd0c0dc3 | ||
|
|
6e0c5615fe | ||
|
|
edde4149aa | ||
|
|
87478d016a | ||
|
|
09d74a3b3e | ||
|
|
271a759490 | ||
|
|
b9595d985e | ||
|
|
ab94524328 | ||
|
|
155e21e729 | ||
|
|
d440b5fa85 | ||
|
|
47895b3334 | ||
|
|
71fb6bf915 | ||
|
|
c673bd9151 | ||
|
|
0f2de46648 | ||
|
|
f0f58dacb3 | ||
|
|
4a3b86c652 | ||
|
|
600f585a31 | ||
|
|
062fbf2b3c | ||
|
|
4280d35bf3 | ||
|
|
11a726d1b4 | ||
|
|
ec0b90f4b4 | ||
|
|
96a06bed8d | ||
|
|
f2dc585751 | ||
|
|
478f56b82f | ||
|
|
89e5a9bd72 | ||
|
|
cfbae50845 | ||
|
|
78f855d7e3 | ||
|
|
75ffb5fc4c | ||
|
|
75fea4245a | ||
|
|
5c108e5c12 | ||
|
|
53e886380c | ||
|
|
97f7a26e11 | ||
|
|
9b9c9304c7 | ||
|
|
c4f8748a42 | ||
|
|
5523b5e25f | ||
|
|
4e4d0555c0 | ||
|
|
1213369d75 | ||
|
|
ccc318106e | ||
|
|
4d0c72eafe | ||
|
|
15a2575949 | ||
|
|
968856ed96 | ||
|
|
5b5dc9c708 | ||
|
|
42e41c57d4 | ||
|
|
e0ab5ce49b | ||
|
|
f0e665d08c | ||
|
|
de9b1adf63 | ||
|
|
018674cfde | ||
|
|
266130b5cf | ||
|
|
8769059ce5 | ||
|
|
6bfb1e1fae | ||
|
|
f107235db2 | ||
|
|
06a8fd0e4a | ||
|
|
113565ba76 | ||
|
|
ae5ab9c67c | ||
|
|
884c61604e | ||
|
|
c5e1f0ccc9 | ||
|
|
3e2f6e571f | ||
|
|
ea9e4b3409 | ||
|
|
d36350aca4 | ||
|
|
0947323e78 | ||
|
|
15af6c1b20 | ||
|
|
f9869daa91 | ||
|
|
61d809b41a | ||
|
|
197ee9b9a6 | ||
|
|
7df44f9418 | ||
|
|
7351e82c92 | ||
|
|
8488039fb9 | ||
|
|
7de476aeb0 | ||
|
|
a255b4f50f | ||
|
|
003b539287 | ||
|
|
03a54bfbf9 | ||
|
|
d3fcc2a6cc | ||
|
|
e8427a59f5 | ||
|
|
e9511560b7 | ||
|
|
6b7f339287 | ||
|
|
0151e8427c | ||
|
|
e14b4f1c5c | ||
|
|
365bae1f9c | ||
|
|
79ac95d8a8 | ||
|
|
8719072519 | ||
|
|
af0bfe0981 | ||
|
|
d546b85163 | ||
|
|
2969feef89 | ||
|
|
9773775a08 | ||
|
|
532e1feacc | ||
|
|
7d7bbf2a50 | ||
|
|
0e543a9843 | ||
|
|
a83c53ec9a | ||
|
|
94121f19ca | ||
|
|
2b8e719034 | ||
|
|
bdbbd45909 | ||
|
|
0d0711f2a7 | ||
|
|
d4873dd35e | ||
|
|
f7317b6a2b | ||
|
|
352b3711f6 | ||
|
|
eb37c413f2 | ||
|
|
106a9d479f | ||
|
|
d84e0e262d | ||
|
|
8b0dd7b866 | ||
|
|
b798bc2c8f | ||
|
|
a72cf56a05 | ||
|
|
4d9c0e0c26 | ||
|
|
a6ee1df567 | ||
|
|
581679d27d | ||
|
|
fc8b7c04cf | ||
|
|
ccd28ff66a | ||
|
|
a844d60174 | ||
|
|
6ac8c4f544 | ||
|
|
e0eb653dcc | ||
|
|
fb2799bd47 | ||
|
|
12b9999289 | ||
|
|
7871cd74f6 | ||
|
|
1e9dcea88b | ||
|
|
6fbdb2c52b | ||
|
|
48e3724299 | ||
|
|
0c9931ff8a | ||
|
|
48d7d9cedb | ||
|
|
4a97a449fc | ||
|
|
2e987343dd | ||
|
|
723a896b99 | ||
|
|
ada9c452f0 | ||
|
|
99de5d4238 | ||
|
|
12bd709219 | ||
|
|
a935d97190 | ||
|
|
480ae619e6 | ||
|
|
8947f7afd8 | ||
|
|
9228304294 | ||
|
|
8eed18a8ff | ||
|
|
fdbd49a843 | ||
|
|
15fde872b5 | ||
|
|
1dba99f47d | ||
|
|
27638c7029 | ||
|
|
a5905a6f20 | ||
|
|
ca41ae0d1a | ||
|
|
38219f6ebb | ||
|
|
b08e4e1dba | ||
|
|
07099f17d6 | ||
|
|
e8de8433f4 | ||
|
|
20fea3955e | ||
|
|
a684943bb7 | ||
|
|
a0099d64c8 | ||
|
|
dfe451128e | ||
|
|
fd5c5b5635 | ||
|
|
97a02ed903 | ||
|
|
6e8f43ce2e | ||
|
|
6dfbd4e062 | ||
|
|
e11363280a | ||
|
|
6b6d8862b0 | ||
|
|
1d6b8c5120 | ||
|
|
05d681fe19 | ||
|
|
a2339305e5 | ||
|
|
f577e973bc | ||
|
|
df35f9f98b | ||
|
|
24f3d9ede0 | ||
|
|
3d4785f29f | ||
|
|
1357de90ec | ||
|
|
d7250a8abe | ||
|
|
116f5a253c | ||
|
|
22b55f3d6f | ||
|
|
93d417049c | ||
|
|
1bff7a3eb8 | ||
|
|
eb7f1989c7 | ||
|
|
de5470a85c | ||
|
|
b3681f7a0c | ||
|
|
6294c3b3b8 | ||
|
|
4aee99f0eb | ||
|
|
5df695bec9 | ||
|
|
1fa183ee2a | ||
|
|
d4bb92b038 | ||
|
|
3e4f42f8a3 | ||
|
|
fc429c1757 | ||
|
|
1d7a39a093 | ||
|
|
a1eaf42cbf | ||
|
|
fa73cd5d5c | ||
|
|
6760390d75 | ||
|
|
60295662b7 | ||
|
|
ea0d1bf262 | ||
|
|
0106072b88 | ||
|
|
779fd757a3 | ||
|
|
0b31ca4348 | ||
|
|
60b8213fdd | ||
|
|
004ebd386c | ||
|
|
9efe112026 | ||
|
|
5cf281a1b6 | ||
|
|
61dc1d673e | ||
|
|
0676ba1c07 | ||
|
|
1072d6a7b7 | ||
|
|
ceb3b21e0f | ||
|
|
c811fae876 | ||
|
|
549dcb31be | ||
|
|
05f9b4124d | ||
|
|
b8f9dd9de5 | ||
|
|
a8e93e7fa0 | ||
|
|
744ade6720 | ||
|
|
c877487e11 | ||
|
|
5151df456c | ||
|
|
8aa839f4c0 | ||
|
|
1a35a05ccc | ||
|
|
bd94ceddd9 | ||
|
|
3dc465f167 | ||
|
|
61e8f91404 | ||
|
|
e587541e55 | ||
|
|
8a051d7e57 | ||
|
|
fa27eaabef | ||
|
|
d3541b87d6 | ||
|
|
c3f0967e9b | ||
|
|
3aa21242cd | ||
|
|
94e3d86f6a | ||
|
|
31895c04f8 | ||
|
|
e299cccb6e | ||
|
|
4efbc6ea9b | ||
|
|
366ebcad83 | ||
|
|
b34777e67f | ||
|
|
b5898c5a30 | ||
|
|
cd62cdadff | ||
|
|
73194a5e86 | ||
|
|
26a1f4888a | ||
|
|
c29bac2bc1 | ||
|
|
43c6e7d250 | ||
|
|
9289ac2838 | ||
|
|
eec4739497 | ||
|
|
2b78a7b256 | ||
|
|
304cd12fff | ||
|
|
987b10ab3e | ||
|
|
72f5109ec2 | ||
|
|
248932db7a | ||
|
|
306d7d1b5d | ||
|
|
7ea96c43ec | ||
|
|
bd71db87be | ||
|
|
c46c662b72 | ||
|
|
df0f2f8ce4 | ||
|
|
51ebec9164 | ||
|
|
fd7b123ee3 | ||
|
|
79cbf2f1cf | ||
|
|
d0681c6ffb | ||
|
|
032c7ea034 | ||
|
|
da527ffc19 | ||
|
|
91c731f68d | ||
|
|
c4192b670b | ||
|
|
53b8f2abb1 | ||
|
|
178fbf9600 | ||
|
|
6da3a4557e | ||
|
|
31840902cd | ||
|
|
4b240ebf8a | ||
|
|
a5e6f6daf9 | ||
|
|
05a50ad21a | ||
|
|
c3a1eb181e | ||
|
|
d93f4850df | ||
|
|
97c0267614 | ||
|
|
113f3e880b | ||
|
|
edb2ed8df2 | ||
|
|
d1811bc1fe | ||
|
|
febc82dc19 | ||
|
|
4a7395b017 | ||
|
|
9bbbbefd34 | ||
|
|
36e4efe77e | ||
|
|
f5a38b4701 | ||
|
|
b39732ba02 | ||
|
|
dfe6ed2171 | ||
|
|
c64a6762d0 | ||
|
|
f7231f40eb | ||
|
|
a53218c2ce | ||
|
|
d1eb9ab5d2 | ||
|
|
e2ad1f6511 | ||
|
|
4d4e7a1b5c | ||
|
|
9f5b8141f0 | ||
|
|
40b908494c | ||
|
|
000d8244c4 | ||
|
|
9f9c353806 | ||
|
|
4bb110beb8 | ||
|
|
a91cf6b7cb | ||
|
|
9f8ed710e2 | ||
|
|
e4871fc519 | ||
|
|
37261b2f5c | ||
|
|
b38c3268fb | ||
|
|
ca90de2bed | ||
|
|
f8ed810a44 | ||
|
|
10a2824b82 | ||
|
|
f881d368f0 | ||
|
|
d1c63603ee | ||
|
|
578efeaaa3 | ||
|
|
4d6fb873ab | ||
|
|
39218f5e6b | ||
|
|
d33375cd16 | ||
|
|
23bab81855 | ||
|
|
df54459552 | ||
|
|
85ae4045c5 | ||
|
|
98b51149da | ||
|
|
8e36316ebf | ||
|
|
1796bc0abb | ||
|
|
36cc20989c | ||
|
|
2b806ad6fd | ||
|
|
a534d26449 | ||
|
|
ca18179bd2 | ||
|
|
d9feadcfec | ||
|
|
95d4a541bc | ||
|
|
6c171c804f | ||
|
|
a5aeadd31d | ||
|
|
08174d7ec9 | ||
|
|
f6c302b68c | ||
|
|
4f11913ee5 | ||
|
|
42f6e6a19c | ||
|
|
97f19d03ad | ||
|
|
97ddab0724 | ||
|
|
27e19813be | ||
|
|
88adb05d4b | ||
|
|
265922d2e5 | ||
|
|
7db97799c1 | ||
|
|
08b72d0a86 | ||
|
|
46a2a249f9 | ||
|
|
b8ba905253 | ||
|
|
9912aaaf1a | ||
|
|
05a487ec3b | ||
|
|
c0a5c63e8e | ||
|
|
9de5f5c72b | ||
|
|
d40071321a | ||
|
|
97f7dcb04a |
4
.bazelrc
4
.bazelrc
@@ -11,6 +11,8 @@ build --compilation_mode opt
|
||||
common --override_module=semmle_code=%workspace%/misc/bazel/semmle_code_stub
|
||||
|
||||
build --repo_env=CC=clang --repo_env=CXX=clang++
|
||||
# Disable Android SDK auto-detection (we don't use it, and rules_android has Bazel 9 compatibility issues)
|
||||
build --repo_env=ANDROID_HOME=
|
||||
|
||||
# print test output, like sembuild does.
|
||||
# Set to `errors` if this is too verbose.
|
||||
@@ -34,7 +36,7 @@ common --@rules_dotnet//dotnet/settings:strict_deps=false
|
||||
common --@rules_rust//rust/toolchain/channel=nightly
|
||||
|
||||
# Reduce this eventually to empty, once we've fixed all our usages of java, and https://github.com/bazel-contrib/rules_go/issues/4193 is fixed
|
||||
common --incompatible_autoload_externally="+@rules_java,+@rules_shell"
|
||||
common --incompatible_autoload_externally="+@rules_cc,+@rules_java,+@rules_shell"
|
||||
|
||||
build --java_language_version=17
|
||||
build --tool_java_language_version=17
|
||||
|
||||
@@ -1 +1 @@
|
||||
8.4.2
|
||||
9.0.0
|
||||
|
||||
2
.github/dependabot.yml
vendored
2
.github/dependabot.yml
vendored
@@ -45,3 +45,5 @@ updates:
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: weekly
|
||||
exclude-paths:
|
||||
- "misc/bazel/registry/**"
|
||||
|
||||
78
.github/workflows/compile-queries.yml
vendored
78
.github/workflows/compile-queries.yml
vendored
@@ -1,78 +0,0 @@
|
||||
name: "Compile all queries using the latest stable CodeQL CLI"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: # makes sure the cache gets populated - running on the branches people tend to merge into.
|
||||
- main
|
||||
- "rc/*"
|
||||
- "codeql-cli-*"
|
||||
pull_request:
|
||||
paths:
|
||||
- '**.ql'
|
||||
- '**.qll'
|
||||
- '**/qlpack.yml'
|
||||
- '**.dbscheme'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
detect-changes:
|
||||
if: github.repository_owner == 'github'
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
languages: ${{ steps.detect.outputs.languages }}
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- name: Detect changed languages
|
||||
id: detect
|
||||
run: |
|
||||
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
|
||||
# For PRs, detect which languages have changes
|
||||
changed_files=$(gh pr view ${{ github.event.pull_request.number }} --json files --jq '.files.[].path')
|
||||
languages=()
|
||||
for lang in actions cpp csharp go java javascript python ql ruby rust swift; do
|
||||
if echo "$changed_files" | grep -qE "^($lang/|shared/)" ; then
|
||||
languages+=("$lang")
|
||||
fi
|
||||
done
|
||||
echo "languages=$(jq -c -n '$ARGS.positional' --args "${languages[@]}")" >> $GITHUB_OUTPUT
|
||||
else
|
||||
# For pushes to main/rc branches, run all languages
|
||||
echo 'languages=["actions","cpp","csharp","go","java","javascript","python","ql","ruby","rust","swift"]' >> $GITHUB_OUTPUT
|
||||
fi
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
|
||||
compile-queries:
|
||||
needs: detect-changes
|
||||
if: github.repository_owner == 'github' && needs.detect-changes.outputs.languages != '[]'
|
||||
runs-on: ubuntu-latest-xl
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: ${{ fromJson(needs.detect-changes.outputs.languages) }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- name: Setup CodeQL
|
||||
uses: ./.github/actions/fetch-codeql
|
||||
with:
|
||||
channel: 'release'
|
||||
- name: Cache compilation cache
|
||||
id: query-cache
|
||||
uses: ./.github/actions/cache-query-compilation
|
||||
with:
|
||||
key: ${{ matrix.language }}-queries
|
||||
- name: check formatting
|
||||
run: find shared ${{ matrix.language }}/ql -type f \( -name "*.qll" -o -name "*.ql" \) -print0 | xargs -0 -n 3000 -P 10 codeql query format -q --check-only
|
||||
- name: compile queries - check-only
|
||||
# run with --check-only if running in a PR (github.sha != main)
|
||||
if : ${{ github.event_name == 'pull_request' }}
|
||||
shell: bash
|
||||
run: codeql query compile -q -j0 ${{ matrix.language }}/ql/{src,examples} --keep-going --warnings=error --check-only --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}" --compilation-cache-size=500 --ram=56000
|
||||
- name: compile queries - full
|
||||
# do full compile if running on main - this populates the cache
|
||||
if : ${{ github.event_name != 'pull_request' }}
|
||||
shell: bash
|
||||
run: codeql query compile -q -j0 ${{ matrix.language }}/ql/{src,examples} --keep-going --warnings=error --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}" --compilation-cache-size=500 --ram=56000
|
||||
@@ -76,7 +76,7 @@ jobs:
|
||||
needs: measure
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/download-artifact@v4
|
||||
- uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: measurements
|
||||
path: stats
|
||||
|
||||
236
.github/workflows/ruby-build.yml
vendored
236
.github/workflows/ruby-build.yml
vendored
@@ -1,236 +0,0 @@
|
||||
name: "Ruby: Build"
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- "ruby/**"
|
||||
- .github/workflows/ruby-build.yml
|
||||
- .github/actions/fetch-codeql/action.yml
|
||||
- codeql-workspace.yml
|
||||
- "shared/tree-sitter-extractor/**"
|
||||
branches:
|
||||
- main
|
||||
- "rc/*"
|
||||
pull_request:
|
||||
paths:
|
||||
- "ruby/**"
|
||||
- .github/workflows/ruby-build.yml
|
||||
- .github/actions/fetch-codeql/action.yml
|
||||
- codeql-workspace.yml
|
||||
- "shared/tree-sitter-extractor/**"
|
||||
branches:
|
||||
- main
|
||||
- "rc/*"
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
description: "Version tag to create"
|
||||
required: false
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ruby
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- name: Install GNU tar
|
||||
if: runner.os == 'macOS'
|
||||
run: |
|
||||
brew install gnu-tar
|
||||
echo "/usr/local/opt/gnu-tar/libexec/gnubin" >> $GITHUB_PATH
|
||||
- name: Prepare Windows
|
||||
if: runner.os == 'Windows'
|
||||
shell: powershell
|
||||
run: |
|
||||
git config --global core.longpaths true
|
||||
- uses: ./.github/actions/os-version
|
||||
id: os_version
|
||||
- name: Cache entire extractor
|
||||
uses: actions/cache@v3
|
||||
id: cache-extractor
|
||||
with:
|
||||
path: |
|
||||
target/release/codeql-extractor-ruby
|
||||
target/release/codeql-extractor-ruby.exe
|
||||
ruby/extractor/ql/lib/codeql/ruby/ast/internal/TreeSitter.qll
|
||||
key: ${{ runner.os }}-${{ steps.os_version.outputs.version }}-ruby-extractor-${{ hashFiles('ruby/extractor/rust-toolchain.toml', 'ruby/extractor/Cargo.lock') }}-${{ hashFiles('shared/tree-sitter-extractor') }}-${{ hashFiles('ruby/extractor/**/*.rs') }}
|
||||
- uses: actions/cache@v3
|
||||
if: steps.cache-extractor.outputs.cache-hit != 'true'
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
target
|
||||
key: ${{ runner.os }}-${{ steps.os_version.outputs.version }}-ruby-rust-cargo-${{ hashFiles('ruby/extractor/rust-toolchain.toml', 'ruby/extractor/**/Cargo.lock') }}
|
||||
- name: Check formatting
|
||||
if: steps.cache-extractor.outputs.cache-hit != 'true'
|
||||
run: cd extractor && cargo fmt -- --check
|
||||
- name: Build
|
||||
if: steps.cache-extractor.outputs.cache-hit != 'true'
|
||||
run: cd extractor && cargo build --verbose
|
||||
- name: Run tests
|
||||
if: steps.cache-extractor.outputs.cache-hit != 'true'
|
||||
run: cd extractor && cargo test --verbose
|
||||
- name: Release build
|
||||
if: steps.cache-extractor.outputs.cache-hit != 'true'
|
||||
run: cd extractor && cargo build --release
|
||||
- name: Generate dbscheme
|
||||
if: ${{ matrix.os == 'ubuntu-latest' && steps.cache-extractor.outputs.cache-hit != 'true'}}
|
||||
run: ../target/release/codeql-extractor-ruby generate --dbscheme ql/lib/ruby.dbscheme --library ql/lib/codeql/ruby/ast/internal/TreeSitter.qll
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: ${{ matrix.os == 'ubuntu-latest' }}
|
||||
with:
|
||||
name: ruby.dbscheme
|
||||
path: ruby/ql/lib/ruby.dbscheme
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: ${{ matrix.os == 'ubuntu-latest' }}
|
||||
with:
|
||||
name: TreeSitter.qll
|
||||
path: ruby/ql/lib/codeql/ruby/ast/internal/TreeSitter.qll
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: extractor-${{ matrix.os }}
|
||||
path: |
|
||||
target/release/codeql-extractor-ruby
|
||||
target/release/codeql-extractor-ruby.exe
|
||||
retention-days: 1
|
||||
compile-queries:
|
||||
if: github.repository_owner == 'github'
|
||||
runs-on: ubuntu-latest-xl
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- name: Fetch CodeQL
|
||||
uses: ./.github/actions/fetch-codeql
|
||||
- name: Cache compilation cache
|
||||
id: query-cache
|
||||
uses: ./.github/actions/cache-query-compilation
|
||||
with:
|
||||
key: ruby-build
|
||||
- name: Build Query Pack
|
||||
run: |
|
||||
PACKS=${{ runner.temp }}/query-packs
|
||||
rm -rf $PACKS
|
||||
codeql pack create ../misc/suite-helpers --output "$PACKS"
|
||||
codeql pack create ../shared/regex --output "$PACKS"
|
||||
codeql pack create ../shared/ssa --output "$PACKS"
|
||||
codeql pack create ../shared/tutorial --output "$PACKS"
|
||||
codeql pack create ql/lib --output "$PACKS"
|
||||
codeql pack create -j0 ql/src --output "$PACKS" --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}"
|
||||
PACK_FOLDER=$(readlink -f "$PACKS"/codeql/ruby-queries/*)
|
||||
codeql generate query-help --format=sarifv2.1.0 --output="${PACK_FOLDER}/rules.sarif" ql/src
|
||||
(cd ql/src; find queries \( -name '*.qhelp' -o -name '*.rb' -o -name '*.erb' \) -exec bash -c 'mkdir -p "'"${PACK_FOLDER}"'/$(dirname "{}")"' \; -exec cp "{}" "${PACK_FOLDER}/{}" \;)
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: codeql-ruby-queries
|
||||
path: |
|
||||
${{ runner.temp }}/query-packs/*
|
||||
retention-days: 1
|
||||
include-hidden-files: true
|
||||
|
||||
package:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build, compile-queries]
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ruby.dbscheme
|
||||
path: ruby/ruby
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: extractor-ubuntu-latest
|
||||
path: ruby/linux64
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: extractor-windows-latest
|
||||
path: ruby/win64
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: extractor-macos-latest
|
||||
path: ruby/osx64
|
||||
- run: |
|
||||
mkdir -p ruby
|
||||
cp -r codeql-extractor.yml tools ql/lib/ruby.dbscheme.stats ruby/
|
||||
mkdir -p ruby/tools/{linux64,osx64,win64}
|
||||
cp linux64/codeql-extractor-ruby ruby/tools/linux64/extractor
|
||||
cp osx64/codeql-extractor-ruby ruby/tools/osx64/extractor
|
||||
cp win64/codeql-extractor-ruby.exe ruby/tools/win64/extractor.exe
|
||||
chmod +x ruby/tools/{linux64,osx64}/extractor
|
||||
zip -rq codeql-ruby.zip ruby
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: codeql-ruby-pack
|
||||
path: ruby/codeql-ruby.zip
|
||||
retention-days: 1
|
||||
include-hidden-files: true
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: codeql-ruby-queries
|
||||
path: ruby/qlpacks
|
||||
- run: |
|
||||
echo '{
|
||||
"provide": [
|
||||
"ruby/codeql-extractor.yml",
|
||||
"qlpacks/*/*/*/qlpack.yml"
|
||||
]
|
||||
}' > .codeqlmanifest.json
|
||||
zip -rq codeql-ruby-bundle.zip .codeqlmanifest.json ruby qlpacks
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: codeql-ruby-bundle
|
||||
path: ruby/codeql-ruby-bundle.zip
|
||||
retention-days: 1
|
||||
include-hidden-files: true
|
||||
|
||||
test:
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ${{ github.workspace }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
needs: [package]
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- name: Fetch CodeQL
|
||||
uses: ./.github/actions/fetch-codeql
|
||||
|
||||
- name: Download Ruby bundle
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: codeql-ruby-bundle
|
||||
path: ${{ runner.temp }}
|
||||
- name: Unzip Ruby bundle
|
||||
shell: bash
|
||||
run: unzip -q -d "${{ runner.temp }}/ruby-bundle" "${{ runner.temp }}/codeql-ruby-bundle.zip"
|
||||
|
||||
- name: Run QL test
|
||||
shell: bash
|
||||
run: |
|
||||
codeql test run --search-path "${{ runner.temp }}/ruby-bundle" --additional-packs "${{ runner.temp }}/ruby-bundle" ruby/ql/test/library-tests/ast/constants/
|
||||
- name: Create database
|
||||
shell: bash
|
||||
run: |
|
||||
codeql database create --search-path "${{ runner.temp }}/ruby-bundle" --language ruby --source-root ruby/ql/test/library-tests/ast/constants/ ../database
|
||||
- name: Analyze database
|
||||
shell: bash
|
||||
run: |
|
||||
codeql database analyze --search-path "${{ runner.temp }}/ruby-bundle" --format=sarifv2.1.0 --output=out.sarif ../database ruby-code-scanning.qls
|
||||
75
.github/workflows/ruby-dataset-measure.yml
vendored
75
.github/workflows/ruby-dataset-measure.yml
vendored
@@ -1,75 +0,0 @@
|
||||
name: "Ruby: Collect database stats"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- "rc/*"
|
||||
paths:
|
||||
- ruby/ql/lib/ruby.dbscheme
|
||||
- .github/workflows/ruby-dataset-measure.yml
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- "rc/*"
|
||||
paths:
|
||||
- ruby/ql/lib/ruby.dbscheme
|
||||
- .github/workflows/ruby-dataset-measure.yml
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
measure:
|
||||
env:
|
||||
CODEQL_THREADS: 4 # TODO: remove this once it's set by the CLI
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
repo: [rails/rails, discourse/discourse, spree/spree, ruby/ruby]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- uses: ./.github/actions/fetch-codeql
|
||||
|
||||
- uses: ./ruby/actions/create-extractor-pack
|
||||
|
||||
- name: Checkout ${{ matrix.repo }}
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
repository: ${{ matrix.repo }}
|
||||
path: ${{ github.workspace }}/repo
|
||||
- name: Create database
|
||||
run: |
|
||||
codeql database create \
|
||||
--search-path "${{ github.workspace }}" \
|
||||
--threads 4 \
|
||||
--language ruby --source-root "${{ github.workspace }}/repo" \
|
||||
"${{ runner.temp }}/database"
|
||||
- name: Measure database
|
||||
run: |
|
||||
mkdir -p "stats/${{ matrix.repo }}"
|
||||
codeql dataset measure --threads 4 --output "stats/${{ matrix.repo }}/stats.xml" "${{ runner.temp }}/database/db-ruby"
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: measurements-${{ hashFiles('stats/**') }}
|
||||
path: stats
|
||||
retention-days: 1
|
||||
|
||||
merge:
|
||||
runs-on: ubuntu-latest
|
||||
needs: measure
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: stats
|
||||
- run: |
|
||||
python -m pip install --user lxml
|
||||
find stats -name 'stats.xml' | sort | xargs python ruby/scripts/merge_stats.py --output ruby/ql/lib/ruby.dbscheme.stats --normalise ruby_tokeninfo
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ruby.dbscheme.stats
|
||||
path: ruby/ql/lib/ruby.dbscheme.stats
|
||||
40
.github/workflows/ruby-qltest-rtjo.yml
vendored
40
.github/workflows/ruby-qltest-rtjo.yml
vendored
@@ -1,40 +0,0 @@
|
||||
name: "Ruby: Run RTJO Language Tests"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
- synchronize
|
||||
- reopened
|
||||
- labeled
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ruby
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
qltest-rtjo:
|
||||
if: "github.repository_owner == 'github' && github.event.label.name == 'Run: RTJO Language Tests'"
|
||||
runs-on: ubuntu-latest-xl
|
||||
strategy:
|
||||
fail-fast: false
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: ./.github/actions/fetch-codeql
|
||||
- uses: ./ruby/actions/create-extractor-pack
|
||||
- name: Cache compilation cache
|
||||
id: query-cache
|
||||
uses: ./.github/actions/cache-query-compilation
|
||||
with:
|
||||
key: ruby-qltest
|
||||
- name: Run QL tests
|
||||
run: |
|
||||
codeql test run --dynamic-join-order-mode=all --threads=0 --ram 50000 --search-path "${{ github.workspace }}" --check-databases --check-diff-informed --check-undefined-labels --check-unused-labels --check-repeated-labels --check-redefined-labels --check-use-before-definition --consistency-queries ql/consistency-queries ql/test --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}"
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
73
.github/workflows/ruby-qltest.yml
vendored
73
.github/workflows/ruby-qltest.yml
vendored
@@ -1,73 +0,0 @@
|
||||
name: "Ruby: Run QL Tests"
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- "ruby/**"
|
||||
- "shared/**"
|
||||
- .github/workflows/ruby-build.yml
|
||||
- .github/actions/fetch-codeql/action.yml
|
||||
- codeql-workspace.yml
|
||||
branches:
|
||||
- main
|
||||
- "rc/*"
|
||||
pull_request:
|
||||
paths:
|
||||
- "ruby/**"
|
||||
- "shared/**"
|
||||
- .github/workflows/ruby-qltest.yml
|
||||
- .github/actions/fetch-codeql/action.yml
|
||||
- codeql-workspace.yml
|
||||
branches:
|
||||
- main
|
||||
- "rc/*"
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ruby
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
qlupgrade:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: ./.github/actions/fetch-codeql
|
||||
- name: Check DB upgrade scripts
|
||||
run: |
|
||||
echo >empty.trap
|
||||
codeql dataset import -S ql/lib/upgrades/initial/ruby.dbscheme testdb empty.trap
|
||||
codeql dataset upgrade testdb --additional-packs ql/lib
|
||||
diff -q testdb/ruby.dbscheme ql/lib/ruby.dbscheme
|
||||
- name: Check DB downgrade scripts
|
||||
run: |
|
||||
echo >empty.trap
|
||||
rm -rf testdb; codeql dataset import -S ql/lib/ruby.dbscheme testdb empty.trap
|
||||
codeql resolve upgrades --format=lines --allow-downgrades --additional-packs downgrades \
|
||||
--dbscheme=ql/lib/ruby.dbscheme --target-dbscheme=downgrades/initial/ruby.dbscheme |
|
||||
xargs codeql execute upgrades testdb
|
||||
diff -q testdb/ruby.dbscheme downgrades/initial/ruby.dbscheme
|
||||
qltest:
|
||||
if: github.repository_owner == 'github'
|
||||
runs-on: ubuntu-latest-xl
|
||||
strategy:
|
||||
fail-fast: false
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: ./.github/actions/fetch-codeql
|
||||
- uses: ./ruby/actions/create-extractor-pack
|
||||
- name: Cache compilation cache
|
||||
id: query-cache
|
||||
uses: ./.github/actions/cache-query-compilation
|
||||
with:
|
||||
key: ruby-qltest
|
||||
- name: Run QL tests
|
||||
run: |
|
||||
codeql test run --threads=0 --ram 50000 --search-path "${{ github.workspace }}" --check-databases --check-diff-informed --check-undefined-labels --check-unused-labels --check-repeated-labels --check-redefined-labels --check-use-before-definition --consistency-queries ql/consistency-queries ql/test --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}"
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
77
MODULE.bazel
77
MODULE.bazel
@@ -15,21 +15,23 @@ local_path_override(
|
||||
# see https://registry.bazel.build/ for a list of available packages
|
||||
|
||||
bazel_dep(name = "platforms", version = "1.0.0")
|
||||
bazel_dep(name = "rules_go", version = "0.56.1")
|
||||
bazel_dep(name = "rules_pkg", version = "1.0.1")
|
||||
bazel_dep(name = "rules_nodejs", version = "6.2.0-codeql.1")
|
||||
bazel_dep(name = "rules_python", version = "0.40.0")
|
||||
bazel_dep(name = "rules_shell", version = "0.5.0")
|
||||
bazel_dep(name = "bazel_skylib", version = "1.8.1")
|
||||
bazel_dep(name = "abseil-cpp", version = "20240116.1", repo_name = "absl")
|
||||
bazel_dep(name = "rules_cc", version = "0.2.17")
|
||||
bazel_dep(name = "rules_go", version = "0.60.0")
|
||||
bazel_dep(name = "rules_java", version = "9.6.1")
|
||||
bazel_dep(name = "rules_pkg", version = "1.2.0")
|
||||
bazel_dep(name = "rules_nodejs", version = "6.7.3")
|
||||
bazel_dep(name = "rules_python", version = "1.9.0")
|
||||
bazel_dep(name = "rules_shell", version = "0.6.1")
|
||||
bazel_dep(name = "bazel_skylib", version = "1.9.0")
|
||||
bazel_dep(name = "abseil-cpp", version = "20260107.1", repo_name = "absl")
|
||||
bazel_dep(name = "nlohmann_json", version = "3.11.3", repo_name = "json")
|
||||
bazel_dep(name = "fmt", version = "12.1.0-codeql.1")
|
||||
bazel_dep(name = "rules_kotlin", version = "2.2.0-codeql.1")
|
||||
bazel_dep(name = "gazelle", version = "0.40.0")
|
||||
bazel_dep(name = "rules_kotlin", version = "2.2.2-codeql.1")
|
||||
bazel_dep(name = "gazelle", version = "0.47.0")
|
||||
bazel_dep(name = "rules_dotnet", version = "0.21.5-codeql.1")
|
||||
bazel_dep(name = "googletest", version = "1.14.0.bcr.1")
|
||||
bazel_dep(name = "rules_rust", version = "0.66.0")
|
||||
bazel_dep(name = "zstd", version = "1.5.5.bcr.1")
|
||||
bazel_dep(name = "googletest", version = "1.17.0.bcr.2")
|
||||
bazel_dep(name = "rules_rust", version = "0.69.0")
|
||||
bazel_dep(name = "zstd", version = "1.5.7.bcr.1")
|
||||
|
||||
bazel_dep(name = "buildifier_prebuilt", version = "6.4.0", dev_dependency = True)
|
||||
|
||||
@@ -41,7 +43,7 @@ RUST_EDITION = "2024"
|
||||
# a nightly toolchain is required to enable experimental_use_cc_common_link, which we require internally
|
||||
# we prefer to run the same version as internally, even if experimental_use_cc_common_link is not really
|
||||
# required in this repo
|
||||
RUST_VERSION = "nightly/2025-08-01"
|
||||
RUST_VERSION = "nightly/2026-01-22"
|
||||
|
||||
rust = use_extension("@rules_rust//rust:extensions.bzl", "rust")
|
||||
rust.toolchain(
|
||||
@@ -53,26 +55,26 @@ rust.toolchain(
|
||||
],
|
||||
# generated by buildutils-internal/scripts/fill-rust-sha256s.py (internal repo)
|
||||
sha256s = {
|
||||
"2025-08-01/rustc-nightly-x86_64-unknown-linux-gnu.tar.xz": "9bbeaf5d3fc7247d31463a9083aa251c995cc50662c8219e7a2254d76a72a9a4",
|
||||
"2025-08-01/rustc-nightly-x86_64-apple-darwin.tar.xz": "c9ea539a8eff0d5d162701f99f9e1aabe14dd0dfb420d62362817a5d09219de7",
|
||||
"2025-08-01/rustc-nightly-aarch64-apple-darwin.tar.xz": "ae83feebbc39cfd982e4ecc8297731fe79c185173aee138467b334c5404b3773",
|
||||
"2025-08-01/rustc-nightly-x86_64-pc-windows-msvc.tar.xz": "9f170c30d802a349be60cf52ec46260802093cb1013ad667fc0d528b7b10152f",
|
||||
"2025-08-01/clippy-nightly-x86_64-unknown-linux-gnu.tar.xz": "9ae5f3cd8f557c4f6df522597c69d14398cf604cfaed2b83e767c4b77a7eaaf6",
|
||||
"2025-08-01/clippy-nightly-x86_64-apple-darwin.tar.xz": "983cb9ee0b6b968188e04ab2d33743d54764b2681ce565e1b3f2b9135c696a3e",
|
||||
"2025-08-01/clippy-nightly-aarch64-apple-darwin.tar.xz": "ed2219dbc49d088225e1b7c5c4390fa295066e071fddaa2714018f6bb39ddbf0",
|
||||
"2025-08-01/clippy-nightly-x86_64-pc-windows-msvc.tar.xz": "911f40ab5cbdd686f40e00965271fe47c4805513a308ed01f30eafb25b448a50",
|
||||
"2025-08-01/cargo-nightly-x86_64-unknown-linux-gnu.tar.xz": "106463c284e48e4904c717471eeec2be5cc83a9d2cae8d6e948b52438cad2e69",
|
||||
"2025-08-01/cargo-nightly-x86_64-apple-darwin.tar.xz": "6ad35c40efc41a8c531ea43235058347b6902d98a9693bf0aed7fc16d5590cef",
|
||||
"2025-08-01/cargo-nightly-aarch64-apple-darwin.tar.xz": "dd28c365e9d298abc3154c797720ad36a0058f131265c9978b4c8e4e37012c8a",
|
||||
"2025-08-01/cargo-nightly-x86_64-pc-windows-msvc.tar.xz": "7b431286e12d6b3834b038f078389a00cac73f351e8c3152b2504a3c06420b3b",
|
||||
"2025-08-01/llvm-tools-nightly-x86_64-unknown-linux-gnu.tar.xz": "e342e305d7927cc288d386983b2bc253cfad3776b113386e903d0b302648ef47",
|
||||
"2025-08-01/llvm-tools-nightly-x86_64-apple-darwin.tar.xz": "e44dd3506524d85c37b3a54bcc91d01378fd2c590b2db5c5974d12f05c1b84d1",
|
||||
"2025-08-01/llvm-tools-nightly-aarch64-apple-darwin.tar.xz": "0c1b5f46dd81be4a9227b10283a0fcaa39c14fea7e81aea6fd6d9887ff6cdc41",
|
||||
"2025-08-01/llvm-tools-nightly-x86_64-pc-windows-msvc.tar.xz": "423e5fd11406adccbc31b8456ceb7375ce055cdf45e90d2c3babeb2d7f58383f",
|
||||
"2025-08-01/rust-std-nightly-x86_64-unknown-linux-gnu.tar.xz": "3c0ceb46a252647a1d4c7116d9ccae684fa5e42aaf3296419febd2c962c3b41d",
|
||||
"2025-08-01/rust-std-nightly-x86_64-apple-darwin.tar.xz": "3be416003cab10f767390a753d1d16ae4d26c7421c03c98992cf1943e5b0efe8",
|
||||
"2025-08-01/rust-std-nightly-aarch64-apple-darwin.tar.xz": "4046ac0ef951cb056b5028a399124f60999fa37792eab69d008d8d7965f389b4",
|
||||
"2025-08-01/rust-std-nightly-x86_64-pc-windows-msvc.tar.xz": "191ed9d8603c3a4fe5a7bbbc2feb72049078dae2df3d3b7d5dedf3abbf823e6e",
|
||||
"2026-01-22/rustc-nightly-x86_64-unknown-linux-gnu.tar.xz": "88db619323cc1321630d124efa51ed02fabc5e020f08cfa0eda2c0ac1afbe69a",
|
||||
"2026-01-22/rustc-nightly-x86_64-apple-darwin.tar.xz": "08484da3fa38db56f93629aeabdc0ae9ff8ed9704c0792d35259cbc849b3f54c",
|
||||
"2026-01-22/rustc-nightly-aarch64-apple-darwin.tar.xz": "a39c0b21b7058e364ea1bd43144e42e4bf1efade036b2e82455f2afce194ee81",
|
||||
"2026-01-22/rustc-nightly-x86_64-pc-windows-msvc.tar.xz": "d00248ee9850dbb6932b2578e32ff74fc7c429854c1aa071066ca31b65385a3b",
|
||||
"2026-01-22/clippy-nightly-x86_64-unknown-linux-gnu.tar.xz": "70656a0ce994ffff16d5a35a7b170a0acd41e9bb54a589c96ed45bf97b094a4d",
|
||||
"2026-01-22/clippy-nightly-x86_64-apple-darwin.tar.xz": "fe242519fa961522734733009705aec3c2d9a20cc57291f2aa614e5e6262c88f",
|
||||
"2026-01-22/clippy-nightly-aarch64-apple-darwin.tar.xz": "38bb226363ec97c9722edf966cd58774a683e19fd2ff2a6030094445d51e06f9",
|
||||
"2026-01-22/clippy-nightly-x86_64-pc-windows-msvc.tar.xz": "6da9b4470beea67abfebf046f141eee0d2a8db7c7a9e4e2294478734fd477228",
|
||||
"2026-01-22/cargo-nightly-x86_64-unknown-linux-gnu.tar.xz": "99004e9d10c43a01499642f53bb3184d41137a95d65bfb217098840a9e79e892",
|
||||
"2026-01-22/cargo-nightly-x86_64-apple-darwin.tar.xz": "6e021394cf8d8400ac6cfdfcef24e4d74f988e91eb8028b36de3a64ce3502990",
|
||||
"2026-01-22/cargo-nightly-aarch64-apple-darwin.tar.xz": "4b2494cb69ab64132cddbc411a38ea9f1105e54d6f986e43168d54f79510c673",
|
||||
"2026-01-22/cargo-nightly-x86_64-pc-windows-msvc.tar.xz": "c36613cf57407212d10d37b76e49a60ff42336e953cdff9e177283f530a83fc1",
|
||||
"2026-01-22/llvm-tools-nightly-x86_64-unknown-linux-gnu.tar.xz": "0b123c5027dbd833aae6845ffe9bd07d309bf798746a7176aadaea68fbcbd05d",
|
||||
"2026-01-22/llvm-tools-nightly-x86_64-apple-darwin.tar.xz": "a47864491ad5619158c950ab7570fb6e487d5117338585c27334d45824b406d8",
|
||||
"2026-01-22/llvm-tools-nightly-aarch64-apple-darwin.tar.xz": "db9bc826d6e2e7e914505d50157682e516ceb90357e83d77abddc32c2d962f41",
|
||||
"2026-01-22/llvm-tools-nightly-x86_64-pc-windows-msvc.tar.xz": "ffaa406932b2fe62e01dad61cf4ed34860a5d2a6f9306ca340d79e630d930039",
|
||||
"2026-01-22/rust-std-nightly-x86_64-unknown-linux-gnu.tar.xz": "e9c0d5e06e18a4b509391b3088f29293e310cdc8ccc865be8fa3f09733326925",
|
||||
"2026-01-22/rust-std-nightly-x86_64-apple-darwin.tar.xz": "25d75995cee679a4828ca9fe48c5a31a67c3b0846018440ef912e5a6208f53f6",
|
||||
"2026-01-22/rust-std-nightly-aarch64-apple-darwin.tar.xz": "e4132bf3f2eed4684c86756a02315bcf481c23e675e3e25630fc604c9cb4594c",
|
||||
"2026-01-22/rust-std-nightly-x86_64-pc-windows-msvc.tar.xz": "961bb535ef95ae8a5fa4e224cb94aff190f155c45a9bcf7a53e184b024aa41b1",
|
||||
},
|
||||
versions = [RUST_VERSION],
|
||||
)
|
||||
@@ -188,6 +190,15 @@ pip.parse(
|
||||
)
|
||||
use_repo(pip, "codegen_deps")
|
||||
|
||||
python = use_extension("@rules_python//python/extensions:python.bzl", "python")
|
||||
python.toolchain(
|
||||
is_default = True,
|
||||
python_version = "3.12",
|
||||
)
|
||||
use_repo(python, "python_3_12", "python_versions")
|
||||
|
||||
register_toolchains("@python_versions//3.12:all")
|
||||
|
||||
swift_deps = use_extension("//swift/third_party:load.bzl", "swift_deps")
|
||||
|
||||
# following list can be kept in sync with `bazel mod tidy`
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
## 0.4.30
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.4.29
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.4.28
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
3
actions/ql/lib/change-notes/released/0.4.29.md
Normal file
3
actions/ql/lib/change-notes/released/0.4.29.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.4.29
|
||||
|
||||
No user-facing changes.
|
||||
3
actions/ql/lib/change-notes/released/0.4.30.md
Normal file
3
actions/ql/lib/change-notes/released/0.4.30.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.4.30
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.4.28
|
||||
lastReleaseVersion: 0.4.30
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/actions-all
|
||||
version: 0.4.28
|
||||
version: 0.4.31-dev
|
||||
library: true
|
||||
warnOnImplicitThis: true
|
||||
dependencies:
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
## 0.6.22
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.6.21
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.6.20
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
3
actions/ql/src/change-notes/released/0.6.21.md
Normal file
3
actions/ql/src/change-notes/released/0.6.21.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.6.21
|
||||
|
||||
No user-facing changes.
|
||||
3
actions/ql/src/change-notes/released/0.6.22.md
Normal file
3
actions/ql/src/change-notes/released/0.6.22.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.6.22
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.6.20
|
||||
lastReleaseVersion: 0.6.22
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/actions-queries
|
||||
version: 0.6.20
|
||||
version: 0.6.23-dev
|
||||
library: false
|
||||
warnOnImplicitThis: true
|
||||
groups: [actions, queries]
|
||||
|
||||
@@ -199,6 +199,7 @@ def annotate_as_appropriate(filename, lines):
|
||||
# as overlay[local?]. It is not clear that these heuristics are exactly what we want,
|
||||
# but they seem to work well enough for now (as determined by speed and accuracy numbers).
|
||||
if (filename.endswith("Test.qll") or
|
||||
re.search(r"go/ql/lib/semmle/go/security/[^/]+[.]qll$", filename.replace(os.sep, "/")) or
|
||||
((filename.endswith("Query.qll") or filename.endswith("Config.qll")) and
|
||||
any("implements DataFlow::ConfigSig" in line for line in lines))):
|
||||
return None
|
||||
|
||||
@@ -172,10 +172,6 @@
|
||||
"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"
|
||||
],
|
||||
"C# ControlFlowReachability": [
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/ControlFlowReachability.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/ControlFlowReachability.qll"
|
||||
],
|
||||
"C++ ExternalAPIs": [
|
||||
"cpp/ql/src/Security/CWE/CWE-020/ExternalAPIs.qll",
|
||||
"cpp/ql/src/Security/CWE/CWE-020/ir/ExternalAPIs.qll"
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
class Element extends @element {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
class Trap extends @trap {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
class Tag extends @tag {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
from Element e, Trap trap
|
||||
where
|
||||
in_trap_or_tag(e, trap)
|
||||
or
|
||||
exists(Tag tag |
|
||||
in_trap_or_tag(e, tag) and
|
||||
trap_uses_tag(trap, tag)
|
||||
)
|
||||
select e, trap
|
||||
2545
cpp/downgrades/770002bb02322e04fa25345838ce6e82af285a0b/old.dbscheme
Normal file
2545
cpp/downgrades/770002bb02322e04fa25345838ce6e82af285a0b/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,13 @@
|
||||
class SourceFile extends @source_file {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
class Trap extends @trap {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
from SourceFile source_file, string name, Trap trap
|
||||
where
|
||||
source_file_uses_trap(source_file, trap) and
|
||||
source_file_name(source_file, name)
|
||||
select name, trap
|
||||
@@ -0,0 +1,8 @@
|
||||
description: Add source_file_name
|
||||
compatibility: backwards
|
||||
source_file_uses_trap.rel: run source_file_uses_trap.ql
|
||||
source_file_name.rel: delete
|
||||
tag_name.rel: delete
|
||||
trap_uses_tag.rel: delete
|
||||
in_trap.rel: run in_trap.ql
|
||||
in_trap_or_tag.rel: delete
|
||||
@@ -52,5 +52,6 @@ ql/cpp/ql/src/Summary/LinesOfUserCode.ql
|
||||
ql/cpp/ql/src/Telemetry/CompilerErrors.ql
|
||||
ql/cpp/ql/src/Telemetry/DatabaseQuality.ql
|
||||
ql/cpp/ql/src/Telemetry/ExtractionMetrics.ql
|
||||
ql/cpp/ql/src/Telemetry/ExtractorInformation.ql
|
||||
ql/cpp/ql/src/Telemetry/MissingIncludes.ql
|
||||
ql/cpp/ql/src/Telemetry/SucceededIncludes.ql
|
||||
|
||||
@@ -160,6 +160,7 @@ ql/cpp/ql/src/Summary/LinesOfUserCode.ql
|
||||
ql/cpp/ql/src/Telemetry/CompilerErrors.ql
|
||||
ql/cpp/ql/src/Telemetry/DatabaseQuality.ql
|
||||
ql/cpp/ql/src/Telemetry/ExtractionMetrics.ql
|
||||
ql/cpp/ql/src/Telemetry/ExtractorInformation.ql
|
||||
ql/cpp/ql/src/Telemetry/MissingIncludes.ql
|
||||
ql/cpp/ql/src/Telemetry/SucceededIncludes.ql
|
||||
ql/cpp/ql/src/jsf/4.06 Pre-Processing Directives/AV Rule 32.ql
|
||||
|
||||
@@ -93,5 +93,6 @@ ql/cpp/ql/src/Summary/LinesOfUserCode.ql
|
||||
ql/cpp/ql/src/Telemetry/CompilerErrors.ql
|
||||
ql/cpp/ql/src/Telemetry/DatabaseQuality.ql
|
||||
ql/cpp/ql/src/Telemetry/ExtractionMetrics.ql
|
||||
ql/cpp/ql/src/Telemetry/ExtractorInformation.ql
|
||||
ql/cpp/ql/src/Telemetry/MissingIncludes.ql
|
||||
ql/cpp/ql/src/Telemetry/SucceededIncludes.ql
|
||||
|
||||
@@ -1,3 +1,24 @@
|
||||
## 8.0.1
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Inline expectations test comments, which are of the form `// $ tag` or `// $ tag=value`, are now parsed more strictly and will not be recognized if there isn't a space after the `$` symbol.
|
||||
|
||||
## 8.0.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* CodeQL version 2.24.2 accidentally introduced a syntactical breaking change to `BarrierGuard<...>::getAnIndirectBarrierNode` and `InstructionBarrierGuard<...>::getAnIndirectBarrierNode`. These breaking changes have now been reverted so that the original code compiles again.
|
||||
* `MustFlow`, the inter-procedural must-flow data flow analysis library, has been re-worked to use parameterized modules. Like in the case of data flow and taint tracking, instead of extending the `MustFlowConfiguration` class, the user should now implement a module with the `MustFlow::ConfigSig` signature, and instantiate the `MustFlow::Global` parameterized module with the implemented module.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Refactored the "Year field changed using an arithmetic operation without checking for leap year" query (`cpp/leap-year/unchecked-after-arithmetic-year-modification`) to address large numbers of false positive results.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* The `allowInterproceduralFlow` predicate of must-flow data flow configurations now correctly handles direct recursion.
|
||||
|
||||
## 7.1.1
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
14
cpp/ql/lib/change-notes/released/8.0.0.md
Normal file
14
cpp/ql/lib/change-notes/released/8.0.0.md
Normal file
@@ -0,0 +1,14 @@
|
||||
## 8.0.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* CodeQL version 2.24.2 accidentally introduced a syntactical breaking change to `BarrierGuard<...>::getAnIndirectBarrierNode` and `InstructionBarrierGuard<...>::getAnIndirectBarrierNode`. These breaking changes have now been reverted so that the original code compiles again.
|
||||
* `MustFlow`, the inter-procedural must-flow data flow analysis library, has been re-worked to use parameterized modules. Like in the case of data flow and taint tracking, instead of extending the `MustFlowConfiguration` class, the user should now implement a module with the `MustFlow::ConfigSig` signature, and instantiate the `MustFlow::Global` parameterized module with the implemented module.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Refactored the "Year field changed using an arithmetic operation without checking for leap year" query (`cpp/leap-year/unchecked-after-arithmetic-year-modification`) to address large numbers of false positive results.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* The `allowInterproceduralFlow` predicate of must-flow data flow configurations now correctly handles direct recursion.
|
||||
5
cpp/ql/lib/change-notes/released/8.0.1.md
Normal file
5
cpp/ql/lib/change-notes/released/8.0.1.md
Normal file
@@ -0,0 +1,5 @@
|
||||
## 8.0.1
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Inline expectations test comments, which are of the form `// $ tag` or `// $ tag=value`, are now parsed more strictly and will not be recognized if there isn't a space after the `$` symbol.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 7.1.1
|
||||
lastReleaseVersion: 8.0.1
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/cpp-all
|
||||
version: 7.1.1
|
||||
version: 8.0.2-dev
|
||||
groups: cpp
|
||||
dbscheme: semmlecode.cpp.dbscheme
|
||||
extractor: cpp
|
||||
|
||||
@@ -524,6 +524,12 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
|
||||
not exists(NewOrNewArrayExpr new | e = new.getAllocatorCall().getArgument(0))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this function has an ambiguous return type, meaning that zero or multiple return
|
||||
* types for this function are present in the database (this can occur in `build-mode: none`).
|
||||
*/
|
||||
predicate hasAmbiguousReturnType() { count(this.getType()) != 1 }
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
|
||||
@@ -14,7 +14,9 @@ class PackedTimeType extends Type {
|
||||
}
|
||||
}
|
||||
|
||||
private predicate timeType(string typeName) { typeName = ["_SYSTEMTIME", "SYSTEMTIME", "tm"] }
|
||||
private predicate timeType(string typeName) {
|
||||
typeName = ["_SYSTEMTIME", "SYSTEMTIME", "tm", "TIME_FIELDS", "_TIME_FIELDS", "PTIME_FIELDS"]
|
||||
}
|
||||
|
||||
/**
|
||||
* A type that is used to represent times and dates in an 'unpacked' form, that is,
|
||||
@@ -95,3 +97,24 @@ class StructTmMonthFieldAccess extends MonthFieldAccess {
|
||||
class StructTmYearFieldAccess extends YearFieldAccess {
|
||||
StructTmYearFieldAccess() { this.getTarget().getName() = "tm_year" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `DayFieldAccess` for the `TIME_FIELDS` struct.
|
||||
*/
|
||||
class TimeFieldsDayFieldAccess extends DayFieldAccess {
|
||||
TimeFieldsDayFieldAccess() { this.getTarget().getName() = "Day" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `MonthFieldAccess` for the `TIME_FIELDS` struct.
|
||||
*/
|
||||
class TimeFieldsMonthFieldAccess extends MonthFieldAccess {
|
||||
TimeFieldsMonthFieldAccess() { this.getTarget().getName() = "Month" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `YearFieldAccess` for the `TIME_FIELDS` struct.
|
||||
*/
|
||||
class TimeFieldsYearFieldAccess extends YearFieldAccess {
|
||||
TimeFieldsYearFieldAccess() { this.getTarget().getName() = "Year" }
|
||||
}
|
||||
|
||||
@@ -1663,7 +1663,7 @@ private module Cached {
|
||||
private predicate compares_ge(
|
||||
ValueNumber test, Operand left, Operand right, int k, boolean isGe, GuardValue value
|
||||
) {
|
||||
exists(int onemk | k = 1 - onemk | compares_lt(test, right, left, onemk, isGe, value))
|
||||
compares_lt(test, right, left, 1 - k, isGe, value)
|
||||
}
|
||||
|
||||
/** Rearrange various simple comparisons into `left < right + k` form. */
|
||||
|
||||
@@ -353,12 +353,26 @@ module CsvValidation {
|
||||
)
|
||||
}
|
||||
|
||||
private string getIncorrectConstructorSummaryOutput() {
|
||||
exists(string namespace, string type, string name, string output |
|
||||
type = name or
|
||||
type = name + "<" + any(string s)
|
||||
|
|
||||
summaryModel(namespace, type, _, name, _, _, _, output, _, _, _) and
|
||||
output.matches("ReturnValue%") and
|
||||
result =
|
||||
"Constructor model for " + namespace + "." + type +
|
||||
" should use `Argument[this]` in the output, not `ReturnValue`."
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if some row in a CSV-based flow model appears to contain typos. */
|
||||
query predicate invalidModelRow(string msg) {
|
||||
msg =
|
||||
[
|
||||
getInvalidModelSignature(), getInvalidModelInput(), getInvalidModelOutput(),
|
||||
getInvalidModelSubtype(), getInvalidModelColumnCount(), KindVal::getInvalidModelKind()
|
||||
getInvalidModelSubtype(), getInvalidModelColumnCount(), KindVal::getInvalidModelKind(),
|
||||
getIncorrectConstructorSummaryOutput()
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -555,6 +569,7 @@ private Locatable getSupportedFunctionTemplateArgument(Function templateFunction
|
||||
* Normalize the `n`'th parameter of `f` by replacing template names
|
||||
* with `func:N` (where `N` is the index of the template).
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private string getTypeNameWithoutFunctionTemplates(Function f, int n, int remaining) {
|
||||
exists(Function templateFunction |
|
||||
templateFunction = getFullyTemplatedFunction(f) and
|
||||
|
||||
@@ -201,7 +201,7 @@ module SourceSinkInterpretationInput implements
|
||||
string toString() {
|
||||
result = this.asElement().toString()
|
||||
or
|
||||
result = this.asNode().toString()
|
||||
result = this.asNode().toStringImpl()
|
||||
or
|
||||
result = this.asCall().toString()
|
||||
}
|
||||
|
||||
@@ -6,85 +6,67 @@ private import OverlayXml
|
||||
|
||||
/**
|
||||
* Holds always for the overlay variant and never for the base variant.
|
||||
* This local predicate is used to define local predicates that behave
|
||||
* differently for the base and overlay variant.
|
||||
*/
|
||||
overlay[local]
|
||||
predicate isOverlay() { databaseMetadata("isOverlay", "true") }
|
||||
|
||||
overlay[local]
|
||||
private string getLocationFilePath(@location_default loc) {
|
||||
exists(@file file | locations_default(loc, file, _, _, _, _) | files(file, result))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the file path for an element with a single location.
|
||||
* Holds if the TRAP file or tag `t` is reachable from source file `sourceFile`
|
||||
* in the base (isOverlayVariant=false) or overlay (isOverlayVariant=true) variant.
|
||||
*/
|
||||
overlay[local]
|
||||
private string getSingleLocationFilePath(@element e) {
|
||||
exists(@location_default loc |
|
||||
var_decls(e, _, _, _, loc)
|
||||
or
|
||||
fun_decls(e, _, _, _, loc)
|
||||
or
|
||||
type_decls(e, _, loc)
|
||||
or
|
||||
namespace_decls(e, _, loc, _)
|
||||
or
|
||||
macroinvocations(e, _, loc, _)
|
||||
or
|
||||
preprocdirects(e, _, loc)
|
||||
|
|
||||
result = getLocationFilePath(loc)
|
||||
private predicate locallyReachableTrapOrTag(
|
||||
boolean isOverlayVariant, string sourceFile, @trap_or_tag t
|
||||
) {
|
||||
exists(@source_file sf, @trap trap |
|
||||
(if isOverlay() then isOverlayVariant = true else isOverlayVariant = false) and
|
||||
source_file_uses_trap(sf, trap) and
|
||||
source_file_name(sf, sourceFile) and
|
||||
(t = trap or trap_uses_tag(trap, t))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the file path for an element with potentially multiple locations.
|
||||
* Holds if element `e` is in TRAP file or tag `t`
|
||||
* in the base (isOverlayVariant=false) or overlay (isOverlayVariant=true) variant.
|
||||
*/
|
||||
overlay[local]
|
||||
private string getMultiLocationFilePath(@element e) {
|
||||
exists(@location_default loc |
|
||||
var_decls(_, e, _, _, loc)
|
||||
or
|
||||
fun_decls(_, e, _, _, loc)
|
||||
or
|
||||
type_decls(_, e, loc)
|
||||
or
|
||||
namespace_decls(_, e, loc, _)
|
||||
|
|
||||
result = getLocationFilePath(loc)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A local helper predicate that holds in the base variant and never in the
|
||||
* overlay variant.
|
||||
*/
|
||||
overlay[local]
|
||||
private predicate isBase() { not isOverlay() }
|
||||
|
||||
/**
|
||||
* Holds if `path` was extracted in the overlay database.
|
||||
*/
|
||||
overlay[local]
|
||||
private predicate overlayHasFile(string path) {
|
||||
isOverlay() and
|
||||
files(_, path) and
|
||||
path != ""
|
||||
private predicate locallyInTrapOrTag(boolean isOverlayVariant, @element e, @trap_or_tag t) {
|
||||
(if isOverlay() then isOverlayVariant = true else isOverlayVariant = false) and
|
||||
in_trap_or_tag(e, t)
|
||||
}
|
||||
|
||||
/**
|
||||
* Discards an element from the base variant if:
|
||||
* - It has a single location in a file extracted in the overlay, or
|
||||
* - All of its locations are in files extracted in the overlay.
|
||||
* - We have knowledge about what TRAP file or tag it is in (in the base).
|
||||
* - It is not in any overlay TRAP file or tag that is reachable from an overlay source file.
|
||||
* - For every base TRAP file or tag that contains it and is reachable from a base source file,
|
||||
* either the source file has changed, or the overlay has redefined the TRAP file or tag,
|
||||
* or the overlay runner has re-extracted the same source file.
|
||||
*/
|
||||
overlay[discard_entity]
|
||||
private predicate discardElement(@element e) {
|
||||
isBase() and
|
||||
(
|
||||
overlayHasFile(getSingleLocationFilePath(e))
|
||||
or
|
||||
forex(string path | path = getMultiLocationFilePath(e) | overlayHasFile(path))
|
||||
// If we don't have any knowledge about what TRAP file something
|
||||
// is in, then we don't want to discard it, so we only consider
|
||||
// entities that are known to be in a base TRAP file or tag.
|
||||
locallyInTrapOrTag(false, e, _) and
|
||||
// Anything that is reachable from an overlay source file should
|
||||
// not be discarded.
|
||||
not exists(@trap_or_tag t | locallyInTrapOrTag(true, e, t) |
|
||||
locallyReachableTrapOrTag(true, _, t)
|
||||
) and
|
||||
// Finally, we have to make sure the base variant does not retain it.
|
||||
// If it is reachable from a base source file, then that is
|
||||
// sufficient unless either the base source file has changed (in
|
||||
// particular, been deleted), or the overlay has redefined the TRAP
|
||||
// file or tag it is in, or the overlay runner has re-extracted the same
|
||||
// source file (e.g. because a header it includes has changed).
|
||||
forall(@trap_or_tag t, string sourceFile |
|
||||
locallyInTrapOrTag(false, e, t) and
|
||||
locallyReachableTrapOrTag(false, sourceFile, t)
|
||||
|
|
||||
overlayChangedFiles(sourceFile) or
|
||||
locallyReachableTrapOrTag(true, _, t) or
|
||||
locallyReachableTrapOrTag(true, sourceFile, _)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -8,81 +8,143 @@ private import cpp
|
||||
private import semmle.code.cpp.ir.IR
|
||||
|
||||
/**
|
||||
* A configuration of a data flow analysis that performs must-flow analysis. This is different
|
||||
* from `DataFlow.qll` which performs may-flow analysis (i.e., it finds paths where the source _may_
|
||||
* flow to the sink).
|
||||
*
|
||||
* Like in `DataFlow.qll`, each use of the `MustFlow.qll` library must define its own unique extension
|
||||
* of this abstract class. To create a configuration, extend this class with a subclass whose
|
||||
* characteristic predicate is a unique singleton string and override `isSource`, `isSink` (and
|
||||
* `isAdditionalFlowStep` if additional steps are required).
|
||||
* Provides an inter-procedural must-flow data flow analysis.
|
||||
*/
|
||||
abstract class MustFlowConfiguration extends string {
|
||||
bindingset[this]
|
||||
MustFlowConfiguration() { any() }
|
||||
|
||||
module MustFlow {
|
||||
/**
|
||||
* Holds if `source` is a relevant data flow source.
|
||||
* An input configuration of a data flow analysis that performs must-flow analysis. This is different
|
||||
* from `DataFlow.qll` which performs may-flow analysis (i.e., it finds paths where the source _may_
|
||||
* flow to the sink).
|
||||
*/
|
||||
abstract predicate isSource(Instruction source);
|
||||
signature module ConfigSig {
|
||||
/**
|
||||
* Holds if `source` is a relevant data flow source.
|
||||
*/
|
||||
predicate isSource(Instruction source);
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant data flow sink.
|
||||
*/
|
||||
abstract predicate isSink(Operand sink);
|
||||
/**
|
||||
* Holds if `sink` is a relevant data flow sink.
|
||||
*/
|
||||
predicate isSink(Operand sink);
|
||||
|
||||
/**
|
||||
* Holds if data flow through `instr` is prohibited.
|
||||
*/
|
||||
predicate isBarrier(Instruction instr) { none() }
|
||||
/**
|
||||
* Holds if data flow through `instr` is prohibited.
|
||||
*/
|
||||
default predicate isBarrier(Instruction instr) { none() }
|
||||
|
||||
/**
|
||||
* Holds if the additional flow step from `node1` to `node2` must be taken
|
||||
* into account in the analysis.
|
||||
*/
|
||||
predicate isAdditionalFlowStep(Operand node1, Instruction node2) { none() }
|
||||
/**
|
||||
* Holds if the additional flow step from `node1` to `node2` must be taken
|
||||
* into account in the analysis.
|
||||
*/
|
||||
default predicate isAdditionalFlowStep(Operand node1, Instruction node2) { none() }
|
||||
|
||||
/** Holds if this configuration allows flow from arguments to parameters. */
|
||||
predicate allowInterproceduralFlow() { any() }
|
||||
|
||||
/**
|
||||
* Holds if data must flow from `source` to `sink` for this configuration.
|
||||
*
|
||||
* The corresponding paths are generated from the end-points and the graph
|
||||
* included in the module `PathGraph`.
|
||||
*/
|
||||
final predicate hasFlowPath(MustFlowPathNode source, MustFlowPathSink sink) {
|
||||
this.isSource(source.getInstruction()) and
|
||||
source.getASuccessor*() = sink
|
||||
/** Holds if this configuration allows flow from arguments to parameters. */
|
||||
default predicate allowInterproceduralFlow() { any() }
|
||||
}
|
||||
}
|
||||
|
||||
/** Holds if `node` flows from a source. */
|
||||
pragma[nomagic]
|
||||
private predicate flowsFromSource(Instruction node, MustFlowConfiguration config) {
|
||||
not config.isBarrier(node) and
|
||||
(
|
||||
config.isSource(node)
|
||||
or
|
||||
exists(Instruction mid |
|
||||
step(mid, node, config) and
|
||||
flowsFromSource(mid, pragma[only_bind_into](config))
|
||||
)
|
||||
)
|
||||
}
|
||||
/**
|
||||
* Constructs a global must-flow computation.
|
||||
*/
|
||||
module Global<ConfigSig Config> {
|
||||
import Config
|
||||
|
||||
/** Holds if `node` flows to a sink. */
|
||||
pragma[nomagic]
|
||||
private predicate flowsToSink(Instruction node, MustFlowConfiguration config) {
|
||||
flowsFromSource(node, pragma[only_bind_into](config)) and
|
||||
(
|
||||
config.isSink(node.getAUse())
|
||||
or
|
||||
exists(Instruction mid |
|
||||
step(node, mid, config) and
|
||||
flowsToSink(mid, pragma[only_bind_into](config))
|
||||
)
|
||||
)
|
||||
/**
|
||||
* Holds if data must flow from `source` to `sink`.
|
||||
*
|
||||
* The corresponding paths are generated from the end-points and the graph
|
||||
* included in the module `PathGraph`.
|
||||
*/
|
||||
predicate flowPath(PathNode source, PathSink sink) {
|
||||
isSource(source.getInstruction()) and
|
||||
source.getASuccessor*() = sink
|
||||
}
|
||||
|
||||
/** Holds if `node` flows from a source. */
|
||||
pragma[nomagic]
|
||||
private predicate flowsFromSource(Instruction node) {
|
||||
not isBarrier(node) and
|
||||
(
|
||||
isSource(node)
|
||||
or
|
||||
exists(Instruction mid |
|
||||
step(mid, node) and
|
||||
flowsFromSource(mid)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `node` flows to a sink. */
|
||||
pragma[nomagic]
|
||||
private predicate flowsToSink(Instruction node) {
|
||||
flowsFromSource(node) and
|
||||
(
|
||||
isSink(node.getAUse())
|
||||
or
|
||||
exists(Instruction mid |
|
||||
step(node, mid) and
|
||||
flowsToSink(mid)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `nodeFrom` flows to `nodeTo`. */
|
||||
private predicate step(Instruction nodeFrom, Instruction nodeTo) {
|
||||
Cached::localStep(nodeFrom, nodeTo)
|
||||
or
|
||||
allowInterproceduralFlow() and
|
||||
Cached::flowThroughCallable(nodeFrom, nodeTo)
|
||||
or
|
||||
isAdditionalFlowStep(nodeFrom.getAUse(), nodeTo)
|
||||
}
|
||||
|
||||
private newtype TLocalPathNode =
|
||||
MkLocalPathNode(Instruction n) {
|
||||
flowsToSink(n) and
|
||||
(
|
||||
isSource(n)
|
||||
or
|
||||
exists(PathNode mid | step(mid.getInstruction(), n))
|
||||
)
|
||||
}
|
||||
|
||||
/** A `Node` that is in a path from a source to a sink. */
|
||||
class PathNode extends TLocalPathNode {
|
||||
Instruction n;
|
||||
|
||||
PathNode() { this = MkLocalPathNode(n) }
|
||||
|
||||
/** Gets the underlying node. */
|
||||
Instruction getInstruction() { result = n }
|
||||
|
||||
/** Gets a textual representation of this node. */
|
||||
string toString() { result = n.getAst().toString() }
|
||||
|
||||
/** Gets the location of this element. */
|
||||
Location getLocation() { result = n.getLocation() }
|
||||
|
||||
/** Gets a successor node, if any. */
|
||||
PathNode getASuccessor() { step(this.getInstruction(), result.getInstruction()) }
|
||||
}
|
||||
|
||||
private class PathSink extends PathNode {
|
||||
PathSink() { isSink(this.getInstruction().getAUse()) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the query predicates needed to include a graph in a path-problem query.
|
||||
*/
|
||||
module PathGraph {
|
||||
private predicate reach(PathNode n) { n instanceof PathSink or reach(n.getASuccessor()) }
|
||||
|
||||
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
|
||||
query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b and reach(b) }
|
||||
|
||||
/** Holds if `n` is a node in the graph of data flow path explanations. */
|
||||
query predicate nodes(PathNode n, string key, string val) {
|
||||
reach(n) and key = "semmle.label" and val = n.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cached
|
||||
@@ -102,7 +164,7 @@ private module Cached {
|
||||
not f.isVirtual() and
|
||||
call.getPositionalArgument(n) = instr and
|
||||
f = call.getStaticCallTarget() and
|
||||
getEnclosingNonVirtualFunctionInitializeParameter(init, f) and
|
||||
isEnclosingNonVirtualFunctionInitializeParameter(init, f) and
|
||||
init.getParameter().getIndex() = pragma[only_bind_into](pragma[only_bind_out](n))
|
||||
}
|
||||
|
||||
@@ -111,7 +173,7 @@ private module Cached {
|
||||
* corresponding initialization instruction that receives the value of `instr` in `f`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate getPositionalArgumentInitParam(
|
||||
private predicate isPositionalArgumentInitParam(
|
||||
CallInstruction call, Instruction instr, InitializeParameterInstruction init, Function f
|
||||
) {
|
||||
exists(int n |
|
||||
@@ -126,18 +188,18 @@ private module Cached {
|
||||
* `instr` in `f`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate getThisArgumentInitParam(
|
||||
private predicate isThisArgumentInitParam(
|
||||
CallInstruction call, Instruction instr, InitializeParameterInstruction init, Function f
|
||||
) {
|
||||
not f.isVirtual() and
|
||||
call.getStaticCallTarget() = f and
|
||||
getEnclosingNonVirtualFunctionInitializeParameter(init, f) and
|
||||
isEnclosingNonVirtualFunctionInitializeParameter(init, f) and
|
||||
call.getThisArgument() = instr and
|
||||
init.getIRVariable() instanceof IRThisVariable
|
||||
}
|
||||
|
||||
/** Holds if `f` is the enclosing non-virtual function of `init`. */
|
||||
private predicate getEnclosingNonVirtualFunctionInitializeParameter(
|
||||
private predicate isEnclosingNonVirtualFunctionInitializeParameter(
|
||||
InitializeParameterInstruction init, Function f
|
||||
) {
|
||||
not f.isVirtual() and
|
||||
@@ -145,7 +207,7 @@ private module Cached {
|
||||
}
|
||||
|
||||
/** Holds if `f` is the enclosing non-virtual function of `init`. */
|
||||
private predicate getEnclosingNonVirtualFunctionInitializeIndirection(
|
||||
private predicate isEnclosingNonVirtualFunctionInitializeIndirection(
|
||||
InitializeIndirectionInstruction init, Function f
|
||||
) {
|
||||
not f.isVirtual() and
|
||||
@@ -153,15 +215,16 @@ private module Cached {
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` is an argument (or argument indirection) to a call, and
|
||||
* `succ` is the corresponding initialization instruction in the call target.
|
||||
* Holds if `argument` is an argument (or argument indirection) to a call, and
|
||||
* `parameter` is the corresponding initialization instruction in the call target.
|
||||
*/
|
||||
private predicate flowThroughCallable(Instruction argument, Instruction parameter) {
|
||||
cached
|
||||
predicate flowThroughCallable(Instruction argument, Instruction parameter) {
|
||||
// Flow from an argument to a parameter
|
||||
exists(CallInstruction call, InitializeParameterInstruction init | init = parameter |
|
||||
getPositionalArgumentInitParam(call, argument, init, call.getStaticCallTarget())
|
||||
isPositionalArgumentInitParam(call, argument, init, call.getStaticCallTarget())
|
||||
or
|
||||
getThisArgumentInitParam(call, argument, init, call.getStaticCallTarget())
|
||||
isThisArgumentInitParam(call, argument, init, call.getStaticCallTarget())
|
||||
)
|
||||
or
|
||||
// Flow from argument indirection to parameter indirection
|
||||
@@ -170,7 +233,7 @@ private module Cached {
|
||||
|
|
||||
init = parameter and
|
||||
read.getPrimaryInstruction() = call and
|
||||
getEnclosingNonVirtualFunctionInitializeIndirection(init, call.getStaticCallTarget())
|
||||
isEnclosingNonVirtualFunctionInitializeIndirection(init, call.getStaticCallTarget())
|
||||
|
|
||||
exists(int n |
|
||||
read.getSideEffectOperand().getAnyDef() = argument and
|
||||
@@ -205,92 +268,10 @@ private module Cached {
|
||||
}
|
||||
|
||||
cached
|
||||
predicate step(Instruction nodeFrom, Instruction nodeTo) {
|
||||
predicate localStep(Instruction nodeFrom, Instruction nodeTo) {
|
||||
exists(Operand mid |
|
||||
instructionToOperandStep(nodeFrom, mid) and
|
||||
operandToInstructionStep(mid, nodeTo)
|
||||
)
|
||||
or
|
||||
flowThroughCallable(nodeFrom, nodeTo)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the enclosing callable of `n`. Unlike `n.getEnclosingCallable()`, this
|
||||
* predicate ensures that joins go from `n` to the result instead of the other
|
||||
* way around.
|
||||
*/
|
||||
pragma[inline]
|
||||
private IRFunction getEnclosingCallable(Instruction n) {
|
||||
pragma[only_bind_into](result) = pragma[only_bind_out](n).getEnclosingIRFunction()
|
||||
}
|
||||
|
||||
/** Holds if `nodeFrom` flows to `nodeTo`. */
|
||||
private predicate step(Instruction nodeFrom, Instruction nodeTo, MustFlowConfiguration config) {
|
||||
exists(config) and
|
||||
Cached::step(pragma[only_bind_into](nodeFrom), pragma[only_bind_into](nodeTo)) and
|
||||
(
|
||||
config.allowInterproceduralFlow()
|
||||
or
|
||||
getEnclosingCallable(nodeFrom) = getEnclosingCallable(nodeTo)
|
||||
)
|
||||
or
|
||||
config.isAdditionalFlowStep(nodeFrom.getAUse(), nodeTo)
|
||||
}
|
||||
|
||||
private newtype TLocalPathNode =
|
||||
MkLocalPathNode(Instruction n, MustFlowConfiguration config) {
|
||||
flowsToSink(n, config) and
|
||||
(
|
||||
config.isSource(n)
|
||||
or
|
||||
exists(MustFlowPathNode mid | step(mid.getInstruction(), n, config))
|
||||
)
|
||||
}
|
||||
|
||||
/** A `Node` that is in a path from a source to a sink. */
|
||||
class MustFlowPathNode extends TLocalPathNode {
|
||||
Instruction n;
|
||||
|
||||
MustFlowPathNode() { this = MkLocalPathNode(n, _) }
|
||||
|
||||
/** Gets the underlying node. */
|
||||
Instruction getInstruction() { result = n }
|
||||
|
||||
/** Gets a textual representation of this node. */
|
||||
string toString() { result = n.getAst().toString() }
|
||||
|
||||
/** Gets the location of this element. */
|
||||
Location getLocation() { result = n.getLocation() }
|
||||
|
||||
/** Gets a successor node, if any. */
|
||||
MustFlowPathNode getASuccessor() {
|
||||
step(this.getInstruction(), result.getInstruction(), this.getConfiguration())
|
||||
}
|
||||
|
||||
/** Gets the associated configuration. */
|
||||
MustFlowConfiguration getConfiguration() { this = MkLocalPathNode(_, result) }
|
||||
}
|
||||
|
||||
private class MustFlowPathSink extends MustFlowPathNode {
|
||||
MustFlowPathSink() { this.getConfiguration().isSink(this.getInstruction().getAUse()) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the query predicates needed to include a graph in a path-problem query.
|
||||
*/
|
||||
module PathGraph {
|
||||
private predicate reach(MustFlowPathNode n) {
|
||||
n instanceof MustFlowPathSink or reach(n.getASuccessor())
|
||||
}
|
||||
|
||||
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
|
||||
query predicate edges(MustFlowPathNode a, MustFlowPathNode b) {
|
||||
a.getASuccessor() = b and reach(b)
|
||||
}
|
||||
|
||||
/** Holds if `n` is a node in the graph of data flow path explanations. */
|
||||
query predicate nodes(MustFlowPathNode n, string key, string val) {
|
||||
reach(n) and key = "semmle.label" and val = n.toString()
|
||||
}
|
||||
}
|
||||
|
||||
1823
cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowNodes.qll
Normal file
1823
cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowNodes.qll
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,6 @@
|
||||
private import cpp as Cpp
|
||||
private import DataFlowUtil
|
||||
private import DataFlowNodes
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import DataFlowDispatch
|
||||
private import semmle.code.cpp.ir.internal.IRCppLanguage
|
||||
@@ -16,28 +17,42 @@ private import semmle.code.cpp.dataflow.ExternalFlow as External
|
||||
cached
|
||||
private module Cached {
|
||||
cached
|
||||
module Nodes0 {
|
||||
cached
|
||||
newtype TIRDataFlowNode0 =
|
||||
TInstructionNode0(Instruction i) {
|
||||
not Ssa::ignoreInstruction(i) and
|
||||
not exists(Operand op |
|
||||
not Ssa::ignoreOperand(op) and i = Ssa::getIRRepresentationOfOperand(op)
|
||||
) and
|
||||
// We exclude `void`-typed instructions because they cannot contain data.
|
||||
// However, if the instruction is a glvalue, and their type is `void`, then the result
|
||||
// type of the instruction is really `void*`, and thus we still want to have a dataflow
|
||||
// node for it.
|
||||
(not i.getResultType() instanceof VoidType or i.isGLValue())
|
||||
} or
|
||||
TMultipleUseOperandNode0(Operand op) {
|
||||
not Ssa::ignoreOperand(op) and not exists(Ssa::getIRRepresentationOfOperand(op))
|
||||
} or
|
||||
TSingleUseOperandNode0(Operand op) {
|
||||
not Ssa::ignoreOperand(op) and exists(Ssa::getIRRepresentationOfOperand(op))
|
||||
}
|
||||
newtype TIRDataFlowNode0 =
|
||||
TInstructionNode0(Instruction i) {
|
||||
not Ssa::ignoreInstruction(i) and
|
||||
not exists(Operand op |
|
||||
not Ssa::ignoreOperand(op) and i = Ssa::getIRRepresentationOfOperand(op)
|
||||
) and
|
||||
// We exclude `void`-typed instructions because they cannot contain data.
|
||||
// However, if the instruction is a glvalue, and their type is `void`, then the result
|
||||
// type of the instruction is really `void*`, and thus we still want to have a dataflow
|
||||
// node for it.
|
||||
(not i.getResultType() instanceof VoidType or i.isGLValue())
|
||||
} or
|
||||
TMultipleUseOperandNode0(Operand op) {
|
||||
not Ssa::ignoreOperand(op) and not exists(Ssa::getIRRepresentationOfOperand(op))
|
||||
} or
|
||||
TSingleUseOperandNode0(Operand op) {
|
||||
not Ssa::ignoreOperand(op) and exists(Ssa::getIRRepresentationOfOperand(op))
|
||||
}
|
||||
|
||||
cached
|
||||
string toStringCached(Node n) {
|
||||
result = toExprString(n)
|
||||
or
|
||||
not exists(toExprString(n)) and
|
||||
result = n.toStringImpl()
|
||||
}
|
||||
|
||||
cached
|
||||
Location getLocationCached(Node n) { result = n.getLocationImpl() }
|
||||
|
||||
cached
|
||||
newtype TContentApprox =
|
||||
TFieldApproxContent(string s) { fieldHasApproxName(_, s) } or
|
||||
TUnionApproxContent(string s) { unionHasApproxName(_, s) } or
|
||||
TElementApproxContent()
|
||||
|
||||
/**
|
||||
* Gets an additional term that is added to the `join` and `branch` computations to reflect
|
||||
* an additional forward or backwards branching factor that is not taken into account
|
||||
@@ -59,38 +74,174 @@ private module Cached {
|
||||
result = countNumberOfBranchesUsingParameter(switch, p)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
import Cached
|
||||
private import Nodes0
|
||||
cached
|
||||
newtype TDataFlowCallable =
|
||||
TSourceCallable(Cpp::Declaration decl) or
|
||||
TSummarizedCallable(FlowSummaryImpl::Public::SummarizedCallable c)
|
||||
|
||||
/**
|
||||
* A module for calculating the number of stars (i.e., `*`s) needed for various
|
||||
* dataflow node `toString` predicates.
|
||||
*/
|
||||
module NodeStars {
|
||||
private int getNumberOfIndirections(Node n) {
|
||||
result = n.(RawIndirectOperand).getIndirectionIndex()
|
||||
cached
|
||||
newtype TDataFlowCall =
|
||||
TNormalCall(CallInstruction call) or
|
||||
TSummaryCall(
|
||||
FlowSummaryImpl::Public::SummarizedCallable c, FlowSummaryImpl::Private::SummaryNode receiver
|
||||
) {
|
||||
FlowSummaryImpl::Private::summaryCallbackRange(c, receiver)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` in a way that loses the
|
||||
* calling context. For example, this would happen with flow through a
|
||||
* global or static variable.
|
||||
*/
|
||||
cached
|
||||
predicate jumpStep(Node n1, Node n2) {
|
||||
exists(GlobalLikeVariable v |
|
||||
exists(Ssa::GlobalUse globalUse |
|
||||
v = globalUse.getVariable() and
|
||||
n1.(FinalGlobalValue).getGlobalUse() = globalUse
|
||||
|
|
||||
globalUse.getIndirection() = getMinIndirectionForGlobalUse(globalUse) and
|
||||
v = n2.asVariable()
|
||||
or
|
||||
v = n2.asIndirectVariable(globalUse.getIndirection())
|
||||
)
|
||||
or
|
||||
exists(Ssa::GlobalDef globalDef |
|
||||
v = globalDef.getVariable() and
|
||||
n2.(InitialGlobalValue).getGlobalDef() = globalDef
|
||||
|
|
||||
globalDef.getIndirection() = getMinIndirectionForGlobalDef(globalDef) and
|
||||
v = n1.asVariable()
|
||||
or
|
||||
v = n1.asIndirectVariable(globalDef.getIndirection())
|
||||
)
|
||||
)
|
||||
or
|
||||
result = n.(RawIndirectInstruction).getIndirectionIndex()
|
||||
or
|
||||
result = n.(VariableNode).getIndirectionIndex()
|
||||
or
|
||||
result = n.(PostUpdateNodeImpl).getIndirectionIndex()
|
||||
or
|
||||
result = n.(FinalParameterNode).getIndirectionIndex()
|
||||
or
|
||||
result = n.(BodyLessParameterNodeImpl).getIndirectionIndex()
|
||||
// models-as-data summarized flow
|
||||
FlowSummaryImpl::Private::Steps::summaryJumpStep(n1.(FlowSummaryNode).getSummaryNode(),
|
||||
n2.(FlowSummaryNode).getSummaryNode())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of stars (i.e., `*`s) needed to produce the `toString`
|
||||
* output for `n`.
|
||||
* Holds if data can flow from `node1` to `node2` via an assignment to `f`.
|
||||
* Thus, `node2` references an object with a field `f` that contains the
|
||||
* value of `node1`.
|
||||
*
|
||||
* The boolean `certain` is true if the destination address does not involve
|
||||
* any pointer arithmetic, and false otherwise.
|
||||
*/
|
||||
string stars(Node n) { result = repeatStars(getNumberOfIndirections(n)) }
|
||||
cached
|
||||
predicate storeStepImpl(Node node1, Content c, Node node2, boolean certain) {
|
||||
exists(
|
||||
PostFieldUpdateNode postFieldUpdate, int indirectionIndex1, int numberOfLoads,
|
||||
StoreInstruction store, FieldContent fc
|
||||
|
|
||||
postFieldUpdate = node2 and
|
||||
fc = c and
|
||||
nodeHasInstruction(node1, pragma[only_bind_into](store),
|
||||
pragma[only_bind_into](indirectionIndex1)) and
|
||||
postFieldUpdate.getIndirectionIndex() = 1 and
|
||||
numberOfLoadsFromOperand(postFieldUpdate.getFieldAddress(),
|
||||
store.getDestinationAddressOperand(), numberOfLoads, certain) and
|
||||
fc.getAField() = postFieldUpdate.getUpdatedField() and
|
||||
getIndirectionIndexLate(fc) = 1 + indirectionIndex1 + numberOfLoads
|
||||
)
|
||||
or
|
||||
// models-as-data summarized flow
|
||||
FlowSummaryImpl::Private::Steps::summaryStoreStep(node1.(FlowSummaryNode).getSummaryNode(), c,
|
||||
node2.(FlowSummaryNode).getSummaryNode()) and
|
||||
certain = true
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` via an assignment to `f`.
|
||||
* Thus, `node2` references an object with a field `f` that contains the
|
||||
* value of `node1`.
|
||||
*/
|
||||
cached
|
||||
predicate storeStep(Node node1, ContentSet c, Node node2) { storeStepImpl(node1, c, node2, _) }
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` via a read of `f`.
|
||||
* Thus, `node1` references an object with a field `f` whose value ends up in
|
||||
* `node2`.
|
||||
*/
|
||||
cached
|
||||
predicate readStep(Node node1, ContentSet c, Node node2) {
|
||||
exists(
|
||||
FieldAddress fa1, Operand operand, int numberOfLoads, int indirectionIndex2, FieldContent fc
|
||||
|
|
||||
fc = c and
|
||||
nodeHasOperand(node2, operand, indirectionIndex2) and
|
||||
// The `1` here matches the `node2.getIndirectionIndex() = 1` conjunct
|
||||
// in `storeStep`.
|
||||
nodeHasOperand(node1, fa1.getObjectAddressOperand(), 1) and
|
||||
numberOfLoadsFromOperand(fa1, operand, numberOfLoads, _) and
|
||||
fc.getAField() = fa1.getField() and
|
||||
getIndirectionIndexLate(fc) = indirectionIndex2 + numberOfLoads
|
||||
)
|
||||
or
|
||||
// models-as-data summarized flow
|
||||
FlowSummaryImpl::Private::Steps::summaryReadStep(node1.(FlowSummaryNode).getSummaryNode(), c,
|
||||
node2.(FlowSummaryNode).getSummaryNode())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if values stored inside content `c` are cleared at node `n`.
|
||||
*/
|
||||
cached
|
||||
predicate clearsContent(Node n, ContentSet c) {
|
||||
n =
|
||||
any(PostUpdateNode pun, Content d |
|
||||
d.impliesClearOf(c) and storeStepImpl(_, d, pun, true)
|
||||
|
|
||||
pun
|
||||
).getPreUpdateNode() and
|
||||
(
|
||||
not exists(Operand op, Cpp::Operation p |
|
||||
n.(IndirectOperand).hasOperandAndIndirectionIndex(op, _) and
|
||||
(
|
||||
p instanceof Cpp::AssignPointerAddExpr or
|
||||
p instanceof Cpp::AssignPointerSubExpr or
|
||||
p instanceof Cpp::CrementOperation
|
||||
)
|
||||
|
|
||||
p.getAnOperand() = op.getUse().getAst()
|
||||
)
|
||||
or
|
||||
forex(PostUpdateNode pun, Content d |
|
||||
pragma[only_bind_into](d).impliesClearOf(pragma[only_bind_into](c)) and
|
||||
storeStepImpl(_, d, pun, true) and
|
||||
pun.getPreUpdateNode() = n
|
||||
|
|
||||
c.(Content).getIndirectionIndex() = d.getIndirectionIndex()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
import NodeStars
|
||||
import Cached
|
||||
|
||||
private int getNumberOfIndirections(Node n) {
|
||||
result = n.(RawIndirectOperand).getIndirectionIndex()
|
||||
or
|
||||
result = n.(RawIndirectInstruction).getIndirectionIndex()
|
||||
or
|
||||
result = n.(VariableNode).getIndirectionIndex()
|
||||
or
|
||||
result = n.(PostUpdateNodeImpl).getIndirectionIndex()
|
||||
or
|
||||
result = n.(FinalParameterNode).getIndirectionIndex()
|
||||
or
|
||||
result = n.(BodyLessParameterNodeImpl).getIndirectionIndex()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of stars (i.e., `*`s) needed to produce the `toString`
|
||||
* output for `n`.
|
||||
*/
|
||||
string stars(Node n) { result = repeatStars(getNumberOfIndirections(n)) }
|
||||
|
||||
/**
|
||||
* A cut-down `DataFlow::Node` class that does not depend on the output of SSA.
|
||||
@@ -828,85 +979,10 @@ private int getMinIndirectionForGlobalDef(Ssa::GlobalDef def) {
|
||||
result = getMinIndirectionsForType(def.getUnspecifiedType())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` in a way that loses the
|
||||
* calling context. For example, this would happen with flow through a
|
||||
* global or static variable.
|
||||
*/
|
||||
predicate jumpStep(Node n1, Node n2) {
|
||||
exists(GlobalLikeVariable v |
|
||||
exists(Ssa::GlobalUse globalUse |
|
||||
v = globalUse.getVariable() and
|
||||
n1.(FinalGlobalValue).getGlobalUse() = globalUse
|
||||
|
|
||||
globalUse.getIndirection() = getMinIndirectionForGlobalUse(globalUse) and
|
||||
v = n2.asVariable()
|
||||
or
|
||||
v = n2.asIndirectVariable(globalUse.getIndirection())
|
||||
)
|
||||
or
|
||||
exists(Ssa::GlobalDef globalDef |
|
||||
v = globalDef.getVariable() and
|
||||
n2.(InitialGlobalValue).getGlobalDef() = globalDef
|
||||
|
|
||||
globalDef.getIndirection() = getMinIndirectionForGlobalDef(globalDef) and
|
||||
v = n1.asVariable()
|
||||
or
|
||||
v = n1.asIndirectVariable(globalDef.getIndirection())
|
||||
)
|
||||
)
|
||||
or
|
||||
// models-as-data summarized flow
|
||||
FlowSummaryImpl::Private::Steps::summaryJumpStep(n1.(FlowSummaryNode).getSummaryNode(),
|
||||
n2.(FlowSummaryNode).getSummaryNode())
|
||||
}
|
||||
|
||||
bindingset[c]
|
||||
pragma[inline_late]
|
||||
private int getIndirectionIndexLate(Content c) { result = c.getIndirectionIndex() }
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` via an assignment to `f`.
|
||||
* Thus, `node2` references an object with a field `f` that contains the
|
||||
* value of `node1`.
|
||||
*
|
||||
* The boolean `certain` is true if the destination address does not involve
|
||||
* any pointer arithmetic, and false otherwise. This has to do with whether a
|
||||
* store step can be used to clear a field (see `clearsContent`).
|
||||
*/
|
||||
predicate storeStepImpl(Node node1, Content c, Node node2, boolean certain) {
|
||||
exists(
|
||||
PostFieldUpdateNode postFieldUpdate, int indirectionIndex1, int numberOfLoads,
|
||||
StoreInstruction store, FieldContent fc
|
||||
|
|
||||
postFieldUpdate = node2 and
|
||||
fc = c and
|
||||
nodeHasInstruction(node1, pragma[only_bind_into](store),
|
||||
pragma[only_bind_into](indirectionIndex1)) and
|
||||
postFieldUpdate.getIndirectionIndex() = 1 and
|
||||
numberOfLoadsFromOperand(postFieldUpdate.getFieldAddress(),
|
||||
store.getDestinationAddressOperand(), numberOfLoads, certain) and
|
||||
fc.getAField() = postFieldUpdate.getUpdatedField() and
|
||||
getIndirectionIndexLate(fc) = 1 + indirectionIndex1 + numberOfLoads
|
||||
)
|
||||
or
|
||||
// models-as-data summarized flow
|
||||
FlowSummaryImpl::Private::Steps::summaryStoreStep(node1.(FlowSummaryNode).getSummaryNode(), c,
|
||||
node2.(FlowSummaryNode).getSummaryNode()) and
|
||||
certain = true
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` via an assignment to `f`.
|
||||
* Thus, `node2` references an object with a field `f` that contains the
|
||||
* value of `node1`.
|
||||
*/
|
||||
predicate storeStep(Node node1, ContentSet c, Node node2) { storeStepImpl(node1, c, node2, _) }
|
||||
|
||||
/**
|
||||
* Holds if `operandFrom` flows to `operandTo` using a sequence of conversion-like
|
||||
* operations and exactly `n` `LoadInstruction` operations.
|
||||
*/
|
||||
private predicate numberOfLoadsFromOperandRec(
|
||||
Operand operandFrom, Operand operandTo, int ind, boolean certain
|
||||
) {
|
||||
@@ -957,63 +1033,6 @@ predicate nodeHasInstruction(Node node, Instruction instr, int indirectionIndex)
|
||||
hasInstructionAndIndex(node, instr, indirectionIndex)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` via a read of `f`.
|
||||
* Thus, `node1` references an object with a field `f` whose value ends up in
|
||||
* `node2`.
|
||||
*/
|
||||
predicate readStep(Node node1, ContentSet c, Node node2) {
|
||||
exists(
|
||||
FieldAddress fa1, Operand operand, int numberOfLoads, int indirectionIndex2, FieldContent fc
|
||||
|
|
||||
fc = c and
|
||||
nodeHasOperand(node2, operand, indirectionIndex2) and
|
||||
// The `1` here matches the `node2.getIndirectionIndex() = 1` conjunct
|
||||
// in `storeStep`.
|
||||
nodeHasOperand(node1, fa1.getObjectAddressOperand(), 1) and
|
||||
numberOfLoadsFromOperand(fa1, operand, numberOfLoads, _) and
|
||||
fc.getAField() = fa1.getField() and
|
||||
getIndirectionIndexLate(fc) = indirectionIndex2 + numberOfLoads
|
||||
)
|
||||
or
|
||||
// models-as-data summarized flow
|
||||
FlowSummaryImpl::Private::Steps::summaryReadStep(node1.(FlowSummaryNode).getSummaryNode(), c,
|
||||
node2.(FlowSummaryNode).getSummaryNode())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if values stored inside content `c` are cleared at node `n`.
|
||||
*/
|
||||
predicate clearsContent(Node n, ContentSet c) {
|
||||
n =
|
||||
any(PostUpdateNode pun, Content d | d.impliesClearOf(c) and storeStepImpl(_, d, pun, true) | pun)
|
||||
.getPreUpdateNode() and
|
||||
(
|
||||
// The crement operations and pointer addition and subtraction self-assign. We do not
|
||||
// want to clear the contents if it is indirectly pointed at by any of these operations,
|
||||
// as part of the contents might still be accessible afterwards. If there is no such
|
||||
// indirection clearing the contents is safe.
|
||||
not exists(Operand op, Cpp::Operation p |
|
||||
n.(IndirectOperand).hasOperandAndIndirectionIndex(op, _) and
|
||||
(
|
||||
p instanceof Cpp::AssignPointerAddExpr or
|
||||
p instanceof Cpp::AssignPointerSubExpr or
|
||||
p instanceof Cpp::CrementOperation
|
||||
)
|
||||
|
|
||||
p.getAnOperand() = op.getUse().getAst()
|
||||
)
|
||||
or
|
||||
forex(PostUpdateNode pun, Content d |
|
||||
pragma[only_bind_into](d).impliesClearOf(pragma[only_bind_into](c)) and
|
||||
storeStepImpl(_, d, pun, true) and
|
||||
pun.getPreUpdateNode() = n
|
||||
|
|
||||
c.(Content).getIndirectionIndex() = d.getIndirectionIndex()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the value that is being tracked is expected to be stored inside content `c`
|
||||
* at node `n`.
|
||||
@@ -1046,11 +1065,6 @@ class CastNode extends Node {
|
||||
CastNode() { none() } // stub implementation
|
||||
}
|
||||
|
||||
cached
|
||||
private newtype TDataFlowCallable =
|
||||
TSourceCallable(Cpp::Declaration decl) or
|
||||
TSummarizedCallable(FlowSummaryImpl::Public::SummarizedCallable c)
|
||||
|
||||
/**
|
||||
* A callable, which may be:
|
||||
* - a function (that may contain code)
|
||||
@@ -1134,15 +1148,6 @@ class DataFlowType extends TypeFinal {
|
||||
string toString() { result = "" }
|
||||
}
|
||||
|
||||
cached
|
||||
private newtype TDataFlowCall =
|
||||
TNormalCall(CallInstruction call) or
|
||||
TSummaryCall(
|
||||
FlowSummaryImpl::Public::SummarizedCallable c, FlowSummaryImpl::Private::SummaryNode receiver
|
||||
) {
|
||||
FlowSummaryImpl::Private::summaryCallbackRange(c, receiver)
|
||||
}
|
||||
|
||||
private predicate summarizedCallableIsManual(SummarizedCallable sc) {
|
||||
sc.asSummarizedCallable().hasManualModel()
|
||||
}
|
||||
@@ -1523,12 +1528,6 @@ private predicate fieldHasApproxName(Field f, string s) {
|
||||
|
||||
private predicate unionHasApproxName(Cpp::Union u, string s) { s = u.getName().charAt(0) }
|
||||
|
||||
cached
|
||||
private newtype TContentApprox =
|
||||
TFieldApproxContent(string s) { fieldHasApproxName(_, s) } or
|
||||
TUnionApproxContent(string s) { unionHasApproxName(_, s) } or
|
||||
TElementApproxContent()
|
||||
|
||||
/** An approximated `Content`. */
|
||||
class ContentApprox extends TContentApprox {
|
||||
string toString() { none() } // overridden in subclasses
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,8 +6,8 @@ private import cpp
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import DataFlowUtil
|
||||
private import DataFlowPrivate
|
||||
private import semmle.code.cpp.ir.implementation.raw.internal.TranslatedExpr
|
||||
private import semmle.code.cpp.ir.implementation.raw.internal.InstructionTag
|
||||
private import DataFlowNodes
|
||||
private import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction as IRConstruction
|
||||
|
||||
cached
|
||||
private module Cached {
|
||||
@@ -73,17 +73,9 @@ private module Cached {
|
||||
// a result for `getConvertedResultExpression`. We remap this here so that
|
||||
// this `ConvertInstruction` maps to the result of the expression that
|
||||
// represents the extent.
|
||||
exists(TranslatedNonConstantAllocationSize tas |
|
||||
result = tas.getExtent().getExpr() and
|
||||
instr = tas.getInstruction(AllocationExtentConvertTag())
|
||||
)
|
||||
result = IRConstruction::Raw::getAllocationExtentConvertExpr(instr)
|
||||
or
|
||||
// There's no instruction that returns `ParenthesisExpr`, but some queries
|
||||
// expect this
|
||||
exists(TranslatedTransparentConversion ttc |
|
||||
result = ttc.getExpr().(ParenthesisExpr) and
|
||||
instr = ttc.getResult()
|
||||
)
|
||||
result = IRConstruction::Raw::getTransparentConversionParenthesisExpr(instr)
|
||||
or
|
||||
// Certain expressions generate `CopyValueInstruction`s only when they
|
||||
// are needed. Examples of this include crement operations and compound
|
||||
@@ -112,10 +104,10 @@ private module Cached {
|
||||
// needed, and in that case the only value that will propagate forward in
|
||||
// the program is the value that's been updated. So in those cases we just
|
||||
// use the result of `node.asDefinition()` as the result of `node.asExpr()`.
|
||||
exists(TranslatedCoreExpr tco |
|
||||
tco.getInstruction(_) = instr and
|
||||
tco.producesExprResult() and
|
||||
result = asDefinitionImpl0(instr)
|
||||
exists(StoreInstruction store |
|
||||
store = instr and
|
||||
IRConstruction::Raw::instructionProducesExprResult(store) and
|
||||
result = asDefinitionImpl0(store)
|
||||
)
|
||||
or
|
||||
// IR construction breaks an array aggregate literal `{1, 2, 3}` into a
|
||||
@@ -145,18 +137,9 @@ private module Cached {
|
||||
// For an expression such as `i += 2` we pretend that the generated
|
||||
// `StoreInstruction` contains the result of the expression even though
|
||||
// this isn't totally aligned with the C/C++ standard.
|
||||
exists(TranslatedAssignOperation tao |
|
||||
store = tao.getInstruction(AssignmentStoreTag()) and
|
||||
result = tao.getExpr()
|
||||
)
|
||||
result = IRConstruction::Raw::getAssignOperationStoreExpr(store)
|
||||
or
|
||||
// Similarly for `i++` and `++i` we pretend that the generated
|
||||
// `StoreInstruction` contains the result of the expression even though
|
||||
// this isn't totally aligned with the C/C++ standard.
|
||||
exists(TranslatedCrementOperation tco |
|
||||
store = tco.getInstruction(CrementStoreTag()) and
|
||||
result = tco.getExpr()
|
||||
)
|
||||
result = IRConstruction::Raw::getCrementOperationStoreExpr(store)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -166,11 +149,7 @@ private module Cached {
|
||||
*/
|
||||
private predicate excludeAsDefinitionResult(StoreInstruction store) {
|
||||
// Exclude the store to the temporary generated by a ternary expression.
|
||||
exists(TranslatedConditionalExpr tce |
|
||||
store = tce.getInstruction(ConditionValueFalseStoreTag())
|
||||
or
|
||||
store = tce.getInstruction(ConditionValueTrueStoreTag())
|
||||
)
|
||||
IRConstruction::Raw::isConditionalExprTempStore(store)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import semmle.code.cpp.models.interfaces.FunctionInputsAndOutputs
|
||||
private import DataFlowUtil
|
||||
private import DataFlowNodes
|
||||
private import DataFlowPrivate
|
||||
private import SsaImpl as Ssa
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ private import cpp
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowNodes
|
||||
private import PrintIRUtilities
|
||||
|
||||
/** A property provider for local IR dataflow store steps. */
|
||||
|
||||
@@ -2,6 +2,7 @@ private import cpp
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowNodes
|
||||
private import SsaImpl as Ssa
|
||||
private import PrintIRUtilities
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ private import cpp
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowNodes
|
||||
|
||||
private Instruction getInstruction(Node n, string stars) {
|
||||
result = [n.asInstruction(), n.(RawIndirectInstruction).getInstruction()] and
|
||||
|
||||
@@ -10,8 +10,9 @@ private import semmle.code.cpp.models.interfaces.PartialFlow as PartialFlow
|
||||
private import semmle.code.cpp.models.interfaces.FunctionInputsAndOutputs as FIO
|
||||
private import semmle.code.cpp.ir.internal.IRCppLanguage
|
||||
private import semmle.code.cpp.ir.dataflow.internal.ModelUtil
|
||||
private import semmle.code.cpp.ir.implementation.raw.internal.TranslatedInitialization
|
||||
private import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction as IRConstruction
|
||||
private import DataFlowPrivate
|
||||
private import DataFlowNodes
|
||||
import SsaImplCommon
|
||||
|
||||
private module SourceVariables {
|
||||
@@ -438,10 +439,7 @@ private predicate sourceVariableHasBaseAndIndex(SourceVariable v, BaseSourceVari
|
||||
* initialize `v`.
|
||||
*/
|
||||
private Instruction getInitializationTargetAddress(IRVariable v) {
|
||||
exists(TranslatedVariableInitialization init |
|
||||
init.getIRVariable() = v and
|
||||
result = init.getTargetAddress()
|
||||
)
|
||||
result = IRConstruction::Raw::getInitializationTargetAddress(v)
|
||||
}
|
||||
|
||||
/** An initial definition of an SSA variable address. */
|
||||
|
||||
@@ -4,47 +4,12 @@ import semmle.code.cpp.ir.internal.IRCppLanguage
|
||||
private import semmle.code.cpp.ir.implementation.raw.internal.SideEffects as SideEffects
|
||||
private import DataFlowImplCommon as DataFlowImplCommon
|
||||
private import DataFlowUtil
|
||||
private import DataFlowNodes
|
||||
private import semmle.code.cpp.models.interfaces.PointerWrapper
|
||||
private import DataFlowPrivate
|
||||
private import TypeFlow
|
||||
private import semmle.code.cpp.ir.ValueNumbering
|
||||
|
||||
/**
|
||||
* Holds if `operand` is an operand that is not used by the dataflow library.
|
||||
* Ignored operands are not recognized as uses by SSA, and they don't have a
|
||||
* corresponding `(Indirect)OperandNode`.
|
||||
*/
|
||||
predicate ignoreOperand(Operand operand) {
|
||||
operand = any(Instruction instr | ignoreInstruction(instr)).getAnOperand() or
|
||||
operand = any(Instruction instr | ignoreInstruction(instr)).getAUse() or
|
||||
operand instanceof MemoryOperand
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` is an instruction that is not used by the dataflow library.
|
||||
* Ignored instructions are not recognized as reads/writes by SSA, and they
|
||||
* don't have a corresponding `(Indirect)InstructionNode`.
|
||||
*/
|
||||
predicate ignoreInstruction(Instruction instr) {
|
||||
DataFlowImplCommon::forceCachingInSameStage() and
|
||||
(
|
||||
instr instanceof CallSideEffectInstruction or
|
||||
instr instanceof CallReadSideEffectInstruction or
|
||||
instr instanceof ExitFunctionInstruction or
|
||||
instr instanceof EnterFunctionInstruction or
|
||||
instr instanceof WriteSideEffectInstruction or
|
||||
instr instanceof PhiInstruction or
|
||||
instr instanceof ReadSideEffectInstruction or
|
||||
instr instanceof ChiInstruction or
|
||||
instr instanceof InitializeIndirectionInstruction or
|
||||
instr instanceof AliasedDefinitionInstruction or
|
||||
instr instanceof AliasedUseInstruction or
|
||||
instr instanceof InitializeNonLocalInstruction or
|
||||
instr instanceof ReturnIndirectionInstruction or
|
||||
instr instanceof UninitializedGroupInstruction
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the C++ type of `this` in the member function `f`.
|
||||
* The result is a glvalue if `isGLValue` is true, and
|
||||
@@ -55,26 +20,6 @@ private CppType getThisType(Cpp::MemberFunction f, boolean isGLValue) {
|
||||
result.hasType(f.getTypeOfThis(), isGLValue)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the C++ type of the instruction `i`.
|
||||
*
|
||||
* This is equivalent to `i.getResultLanguageType()` with the exception
|
||||
* of instructions that directly references a `this` IRVariable. In this
|
||||
* case, `i.getResultLanguageType()` gives an unknown type, whereas the
|
||||
* predicate gives the expected type (i.e., a potentially cv-qualified
|
||||
* type `A*` where `A` is the declaring type of the member function that
|
||||
* contains `i`).
|
||||
*/
|
||||
cached
|
||||
CppType getResultLanguageType(Instruction i) {
|
||||
if i.(VariableAddressInstruction).getIRVariable() instanceof IRThisVariable
|
||||
then
|
||||
if i.isGLValue()
|
||||
then result = getThisType(i.getEnclosingFunction(), true)
|
||||
else result = getThisType(i.getEnclosingFunction(), false)
|
||||
else result = i.getResultLanguageType()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the C++ type of the operand `operand`.
|
||||
* This is equivalent to the type of the operand's defining instruction.
|
||||
@@ -347,10 +292,6 @@ predicate isWrite(Node0Impl value, Operand address, boolean certain) {
|
||||
)
|
||||
}
|
||||
|
||||
predicate isAdditionalConversionFlow(Operand opFrom, Instruction instrTo) {
|
||||
any(Indirection ind).isAdditionalConversionFlow(opFrom, instrTo)
|
||||
}
|
||||
|
||||
newtype TBaseSourceVariable =
|
||||
// Each IR variable gets its own source variable
|
||||
TBaseIRVariable(IRVariable var) or
|
||||
@@ -572,6 +513,69 @@ private class BaseCallInstruction extends BaseSourceVariableInstruction, CallIns
|
||||
|
||||
cached
|
||||
private module Cached {
|
||||
/**
|
||||
* Holds if `operand` is an operand that is not used by the dataflow library.
|
||||
* Ignored operands are not recognized as uses by SSA, and they don't have a
|
||||
* corresponding `(Indirect)OperandNode`.
|
||||
*/
|
||||
cached
|
||||
predicate ignoreOperand(Operand operand) {
|
||||
operand = any(Instruction instr | ignoreInstruction(instr)).getAnOperand() or
|
||||
operand = any(Instruction instr | ignoreInstruction(instr)).getAUse() or
|
||||
operand instanceof MemoryOperand
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` is an instruction that is not used by the dataflow library.
|
||||
* Ignored instructions are not recognized as reads/writes by SSA, and they
|
||||
* don't have a corresponding `(Indirect)InstructionNode`.
|
||||
*/
|
||||
cached
|
||||
predicate ignoreInstruction(Instruction instr) {
|
||||
DataFlowImplCommon::forceCachingInSameStage() and
|
||||
(
|
||||
instr instanceof CallSideEffectInstruction or
|
||||
instr instanceof CallReadSideEffectInstruction or
|
||||
instr instanceof ExitFunctionInstruction or
|
||||
instr instanceof EnterFunctionInstruction or
|
||||
instr instanceof WriteSideEffectInstruction or
|
||||
instr instanceof PhiInstruction or
|
||||
instr instanceof ReadSideEffectInstruction or
|
||||
instr instanceof ChiInstruction or
|
||||
instr instanceof InitializeIndirectionInstruction or
|
||||
instr instanceof AliasedDefinitionInstruction or
|
||||
instr instanceof AliasedUseInstruction or
|
||||
instr instanceof InitializeNonLocalInstruction or
|
||||
instr instanceof ReturnIndirectionInstruction or
|
||||
instr instanceof UninitializedGroupInstruction
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate isAdditionalConversionFlow(Operand opFrom, Instruction instrTo) {
|
||||
any(Indirection ind).isAdditionalConversionFlow(opFrom, instrTo)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the C++ type of the instruction `i`.
|
||||
*
|
||||
* This is equivalent to `i.getResultLanguageType()` with the exception
|
||||
* of instructions that directly references a `this` IRVariable. In this
|
||||
* case, `i.getResultLanguageType()` gives an unknown type, whereas the
|
||||
* predicate gives the expected type (i.e., a potentially cv-qualified
|
||||
* type `A*` where `A` is the declaring type of the member function that
|
||||
* contains `i`).
|
||||
*/
|
||||
cached
|
||||
CppType getResultLanguageType(Instruction i) {
|
||||
if i.(VariableAddressInstruction).getIRVariable() instanceof IRThisVariable
|
||||
then
|
||||
if i.isGLValue()
|
||||
then result = getThisType(i.getEnclosingFunction(), true)
|
||||
else result = getThisType(i.getEnclosingFunction(), false)
|
||||
else result = i.getResultLanguageType()
|
||||
}
|
||||
|
||||
/** Holds if `op` is the only use of its defining instruction, and that op is used in a conversation */
|
||||
private predicate isConversion(Operand op) {
|
||||
exists(Instruction def, Operand use |
|
||||
|
||||
@@ -5,64 +5,81 @@ private import semmle.code.cpp.models.interfaces.DataFlow
|
||||
private import semmle.code.cpp.models.interfaces.SideEffect
|
||||
private import DataFlowUtil
|
||||
private import DataFlowPrivate
|
||||
private import DataFlowNodes
|
||||
private import SsaImpl as Ssa
|
||||
private import semmle.code.cpp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
private import semmle.code.cpp.ir.dataflow.FlowSteps
|
||||
|
||||
/**
|
||||
* Holds if taint propagates from `nodeFrom` to `nodeTo` in exactly one local
|
||||
* (intra-procedural) step. This relation is only used for local taint flow
|
||||
* (for example `TaintTracking::localTaint(source, sink)`) so it may contain
|
||||
* special cases that should only apply to local taint flow.
|
||||
*/
|
||||
predicate localTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
// dataflow step
|
||||
DataFlow::localFlowStep(nodeFrom, nodeTo)
|
||||
or
|
||||
// taint flow step
|
||||
localAdditionalTaintStep(nodeFrom, nodeTo, _)
|
||||
or
|
||||
// models-as-data summarized flow for local data flow (i.e. special case for flow
|
||||
// through calls to modeled functions, without relying on global dataflow to join
|
||||
// the dots).
|
||||
FlowSummaryImpl::Private::Steps::summaryThroughStepTaint(nodeFrom, nodeTo, _)
|
||||
cached
|
||||
private module Cached {
|
||||
private import DataFlowImplCommon as DataFlowImplCommon
|
||||
|
||||
/**
|
||||
* This predicate exists to collapse the `cached` predicates in this module with the
|
||||
* `cached` predicates in other C/C++ dataflow files, which is then collapsed
|
||||
* with the `cached` predicates in `DataFlowImplCommon.qll`.
|
||||
*/
|
||||
cached
|
||||
predicate forceCachingInSameStage() { DataFlowImplCommon::forceCachingInSameStage() }
|
||||
|
||||
/**
|
||||
* Holds if taint propagates from `nodeFrom` to `nodeTo` in exactly one local
|
||||
* (intra-procedural) step. This relation is only used for local taint flow
|
||||
* (for example `TaintTracking::localTaint(source, sink)`) so it may contain
|
||||
* special cases that should only apply to local taint flow.
|
||||
*/
|
||||
cached
|
||||
predicate localTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
// dataflow step
|
||||
DataFlow::localFlowStep(nodeFrom, nodeTo)
|
||||
or
|
||||
// taint flow step
|
||||
localAdditionalTaintStep(nodeFrom, nodeTo, _)
|
||||
or
|
||||
// models-as-data summarized flow for local data flow (i.e. special case for flow
|
||||
// through calls to modeled functions, without relying on global dataflow to join
|
||||
// the dots).
|
||||
FlowSummaryImpl::Private::Steps::summaryThroughStepTaint(nodeFrom, nodeTo, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint can flow in one local step from `nodeFrom` to `nodeTo` excluding
|
||||
* local data flow steps. That is, `nodeFrom` and `nodeTo` are likely to represent
|
||||
* different objects.
|
||||
*/
|
||||
cached
|
||||
predicate localAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo, string model) {
|
||||
operandToInstructionTaintStep(nodeFrom.asOperand(), nodeTo.asInstruction()) and
|
||||
model = ""
|
||||
or
|
||||
modeledTaintStep(nodeFrom, nodeTo, model)
|
||||
or
|
||||
// Flow from (the indirection of) an operand of a pointer arithmetic instruction to the
|
||||
// indirection of the pointer arithmetic instruction. This provides flow from `source`
|
||||
// in `x[source]` to the result of the associated load instruction.
|
||||
exists(PointerArithmeticInstruction pai, int indirectionIndex |
|
||||
nodeHasOperand(nodeFrom, pai.getAnOperand(), pragma[only_bind_into](indirectionIndex)) and
|
||||
hasInstructionAndIndex(nodeTo, pai, indirectionIndex + 1)
|
||||
) and
|
||||
model = ""
|
||||
or
|
||||
any(Ssa::Indirection ind).isAdditionalTaintStep(nodeFrom, nodeTo) and
|
||||
model = ""
|
||||
or
|
||||
// models-as-data summarized flow
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom.(FlowSummaryNode).getSummaryNode(),
|
||||
nodeTo.(FlowSummaryNode).getSummaryNode(), false, model)
|
||||
or
|
||||
// object->field conflation for content that is a `TaintInheritingContent`.
|
||||
exists(DataFlow::ContentSet f |
|
||||
readStep(nodeFrom, f, nodeTo) and
|
||||
f.getAReadContent() instanceof TaintInheritingContent
|
||||
) and
|
||||
model = ""
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint can flow in one local step from `nodeFrom` to `nodeTo` excluding
|
||||
* local data flow steps. That is, `nodeFrom` and `nodeTo` are likely to represent
|
||||
* different objects.
|
||||
*/
|
||||
cached
|
||||
predicate localAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo, string model) {
|
||||
operandToInstructionTaintStep(nodeFrom.asOperand(), nodeTo.asInstruction()) and
|
||||
model = ""
|
||||
or
|
||||
modeledTaintStep(nodeFrom, nodeTo, model)
|
||||
or
|
||||
// Flow from (the indirection of) an operand of a pointer arithmetic instruction to the
|
||||
// indirection of the pointer arithmetic instruction. This provides flow from `source`
|
||||
// in `x[source]` to the result of the associated load instruction.
|
||||
exists(PointerArithmeticInstruction pai, int indirectionIndex |
|
||||
nodeHasOperand(nodeFrom, pai.getAnOperand(), pragma[only_bind_into](indirectionIndex)) and
|
||||
hasInstructionAndIndex(nodeTo, pai, indirectionIndex + 1)
|
||||
) and
|
||||
model = ""
|
||||
or
|
||||
any(Ssa::Indirection ind).isAdditionalTaintStep(nodeFrom, nodeTo) and
|
||||
model = ""
|
||||
or
|
||||
// models-as-data summarized flow
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom.(FlowSummaryNode).getSummaryNode(),
|
||||
nodeTo.(FlowSummaryNode).getSummaryNode(), false, model)
|
||||
or
|
||||
// object->field conflation for content that is a `TaintInheritingContent`.
|
||||
exists(DataFlow::ContentSet f |
|
||||
readStep(nodeFrom, f, nodeTo) and
|
||||
f.getAReadContent() instanceof TaintInheritingContent
|
||||
) and
|
||||
model = ""
|
||||
}
|
||||
import Cached
|
||||
|
||||
/**
|
||||
* Holds if taint propagates from `nodeFrom` to `nodeTo` in exactly one local
|
||||
@@ -196,7 +213,7 @@ predicate modeledTaintStep(DataFlow::Node nodeIn, DataFlow::Node nodeOut, string
|
||||
// Taint flow from a pointer argument to an output, when the model specifies flow from the deref
|
||||
// to that output, but the deref is not modeled in the IR for the caller.
|
||||
exists(
|
||||
CallInstruction call, DataFlow::SideEffectOperandNode indirectArgument, Function func,
|
||||
CallInstruction call, SideEffectOperandNode indirectArgument, Function func,
|
||||
FunctionInput modelIn, FunctionOutput modelOut
|
||||
|
|
||||
indirectArgument = callInput(call, modelIn) and
|
||||
|
||||
@@ -15,6 +15,7 @@ private import TranslatedCall
|
||||
private import TranslatedStmt
|
||||
private import TranslatedFunction
|
||||
private import TranslatedGlobalVar
|
||||
private import TranslatedInitialization
|
||||
|
||||
TranslatedElement getInstructionTranslatedElement(Instruction instruction) {
|
||||
instruction = TRawInstruction(result, _)
|
||||
@@ -194,6 +195,89 @@ module Raw {
|
||||
Expr getInstructionUnconvertedResultExpression(Instruction instruction) {
|
||||
result = getInstructionConvertedResultExpression(instruction).getUnconverted()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the expression associated with the instruction `instr` that computes
|
||||
* the `Convert` instruction on the extent expression of an allocation.
|
||||
*/
|
||||
cached
|
||||
Expr getAllocationExtentConvertExpr(Instruction instr) {
|
||||
exists(TranslatedNonConstantAllocationSize tas |
|
||||
instr = tas.getInstruction(AllocationExtentConvertTag()) and
|
||||
result = tas.getExtent().getExpr()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `ParenthesisExpr` associated with a transparent conversion
|
||||
* instruction, if any.
|
||||
*/
|
||||
cached
|
||||
ParenthesisExpr getTransparentConversionParenthesisExpr(Instruction instr) {
|
||||
exists(TranslatedTransparentConversion ttc |
|
||||
result = ttc.getExpr() and
|
||||
instr = ttc.getResult()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` belongs to a `TranslatedCoreExpr` that produces an
|
||||
* expression result. This indicates that the instruction represents a
|
||||
* definition whose result should be mapped back to the expression.
|
||||
*/
|
||||
cached
|
||||
predicate instructionProducesExprResult(Instruction instr) {
|
||||
exists(TranslatedCoreExpr tco |
|
||||
tco.getInstruction(_) = instr and
|
||||
tco.producesExprResult()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the expression associated with a `StoreInstruction` generated
|
||||
* by an `TranslatedAssignOperation`.
|
||||
*/
|
||||
cached
|
||||
Expr getAssignOperationStoreExpr(StoreInstruction store) {
|
||||
exists(TranslatedAssignOperation tao |
|
||||
store = tao.getInstruction(AssignmentStoreTag()) and
|
||||
result = tao.getExpr()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the expression associated with a `StoreInstruction` generated
|
||||
* by an `TranslatedCrementOperation`.
|
||||
*/
|
||||
cached
|
||||
Expr getCrementOperationStoreExpr(StoreInstruction store) {
|
||||
exists(TranslatedCrementOperation tco |
|
||||
store = tco.getInstruction(CrementStoreTag()) and
|
||||
result = tco.getExpr()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `store` is a `StoreInstruction` that defines the temporary
|
||||
* `IRVariable` generated as part of the translation of a ternary expression.
|
||||
*/
|
||||
cached
|
||||
predicate isConditionalExprTempStore(StoreInstruction store) {
|
||||
exists(TranslatedConditionalExpr tce |
|
||||
store = tce.getInstruction(ConditionValueFalseStoreTag())
|
||||
or
|
||||
store = tce.getInstruction(ConditionValueTrueStoreTag())
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the instruction that computes the address used to initialize `v`. */
|
||||
cached
|
||||
Instruction getInitializationTargetAddress(IRVariable v) {
|
||||
exists(TranslatedVariableInitialization init |
|
||||
init.getIRVariable() = v and
|
||||
result = init.getTargetAddress()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class TStageInstruction = TRawInstruction or TRawUnreachedInstruction;
|
||||
|
||||
@@ -552,34 +552,47 @@ private module BoundsEstimate {
|
||||
private float nrOfBoundsPhiGuard(RangeSsaDefinition def, StackVariable v) {
|
||||
// If we have
|
||||
//
|
||||
// if (x < c) { e1 } else { e2 }
|
||||
// e3
|
||||
//
|
||||
// then `{ e1 }` and `{ e2 }` are both guard phi nodes guarded by `x < c`.
|
||||
// The range analysis propagates bounds on `x` into both branches, filtered
|
||||
// by the condition. In this case all lower bounds flow to `{ e1 }` and only
|
||||
// lower bounds that are smaller than `c` flow to `{ e2 }`.
|
||||
//
|
||||
// The largest number of bounds possible for `e3` is the number of bounds on `x` plus
|
||||
// one. This happens when all bounds flow from `x` to `e1` to `e3` and the
|
||||
// bound `c` can flow to `e2` to `e3`.
|
||||
//
|
||||
// We want to optimize our bounds estimate for `e3`, as that is the estimate
|
||||
// that can continue propagating forward. We don't know how the existing
|
||||
// bounds will be split between the different branches. That depends on
|
||||
// whether the range analysis is tracking lower bounds or upper bounds, and
|
||||
// on the meaning of the condition.
|
||||
//
|
||||
// As a heuristic we divide the number of bounds on `x` by 2 to "average"
|
||||
// the effect of the condition and add 1 to account for the bound from the
|
||||
// condition itself. This will approximate estimates inside the branches,
|
||||
// but will give a good estimate after the branches are merged.
|
||||
//
|
||||
// This also handles cases such as this one
|
||||
//
|
||||
// if (x < c) { e1 }
|
||||
// e2
|
||||
// e3
|
||||
//
|
||||
// then `e2` is both a guard phi node (guarded by `x < c`) and a normal
|
||||
// phi node (control is merged after the `if` statement).
|
||||
//
|
||||
// Assume `x` has `n` bounds. Then `n` bounds are propagated to the guard
|
||||
// phi node `{ e1 }` and, since `{ e1 }` is input to `e2` as a normal phi
|
||||
// node, `n` bounds are propagated to `e2`. If we also propagate the `n`
|
||||
// bounds to `e2` as a guard phi node, then we square the number of
|
||||
// bounds.
|
||||
//
|
||||
// However in practice `x < c` is going to cut down the number of bounds:
|
||||
// The tracked bounds can't flow to both branches as that would require
|
||||
// them to simultaneously be greater and smaller than `c`. To approximate
|
||||
// this better, the contribution from a guard phi node that is also a
|
||||
// normal phi node is 1.
|
||||
exists(def.getAPhiInput(v)) and
|
||||
isGuardPhiWithBound(def, v, _) and
|
||||
result = 1
|
||||
or
|
||||
not exists(def.getAPhiInput(v)) and
|
||||
// If there's different `access`es, then they refer to the same variable
|
||||
// with the same lower bounds. Hence adding these guards make no sense (the
|
||||
// implementation will take the union, but they'll be removed by
|
||||
// deduplication). Hence we use `max` as an approximation.
|
||||
result =
|
||||
max(VariableAccess access | isGuardPhiWithBound(def, v, access) | nrOfBoundsExpr(access))
|
||||
// where `e3` is both a guard phi node (guarded by `x < c`) and a normal
|
||||
// phi node (control is merged after the `if` statement). Here half of the
|
||||
// bounds flow into the branch and then to `e3` as a normal phi node and the
|
||||
// "other" half flow from the condition to `e3` as a guard phi node.
|
||||
exists(float varBounds |
|
||||
// If there's different `access`es, then they refer to the same
|
||||
// variable with the same lower bounds. Hence adding these guards makes no
|
||||
// sense (the implementation will take the union, but they'll be removed by
|
||||
// deduplication). Hence we use `max` as an approximation.
|
||||
varBounds =
|
||||
max(VariableAccess access | isGuardPhiWithBound(def, v, access) | nrOfBoundsExpr(access)) and
|
||||
result = (varBounds + 1) / 2
|
||||
)
|
||||
or
|
||||
def.isPhiNode(v) and
|
||||
not isGuardPhiWithBound(def, v, _) and
|
||||
@@ -2180,6 +2193,16 @@ module SimpleRangeAnalysisInternal {
|
||||
|
||||
/** Gets the estimate of the number of bounds for `e`. */
|
||||
float estimateNrOfBounds(Expr e) { result = BoundsEstimate::nrOfBoundsExpr(e) }
|
||||
|
||||
/** Counts the numbers of lower bounds that are computed internally for `e`. */
|
||||
float countNrOfLowerBounds(Expr e) {
|
||||
result = strictcount(float lb | lb = getLowerBoundsImpl(e) | lb)
|
||||
}
|
||||
|
||||
/** Counts the numbers of upper bounds that are computed internally for `e`. */
|
||||
float countNrOfUpperBounds(Expr e) {
|
||||
result = strictcount(float ub | ub = getUpperBoundsImpl(e) | ub)
|
||||
}
|
||||
}
|
||||
|
||||
/** Provides predicates for debugging the simple range analysis library. */
|
||||
@@ -2208,7 +2231,7 @@ private module Debug {
|
||||
*/
|
||||
predicate countGetLowerBoundsImpl(Expr e, int n) {
|
||||
e = getRelevantLocatable() and
|
||||
n = strictcount(float lb | lb = getLowerBoundsImpl(e) | lb)
|
||||
n = SimpleRangeAnalysisInternal::countNrOfLowerBounds(e)
|
||||
}
|
||||
|
||||
float debugNrOfBounds(Expr e) {
|
||||
|
||||
@@ -245,6 +245,25 @@ trap_filename(
|
||||
string filename: string ref
|
||||
);
|
||||
|
||||
/**
|
||||
* Gives the tag name for `tag`.
|
||||
* For debugging only.
|
||||
*/
|
||||
tag_name(
|
||||
int tag: @tag,
|
||||
string name: string ref
|
||||
);
|
||||
|
||||
@trap_or_tag = @tag | @trap;
|
||||
|
||||
/**
|
||||
* Gives the name for the source file.
|
||||
*/
|
||||
source_file_name(
|
||||
int sf: @source_file,
|
||||
string name: string ref
|
||||
);
|
||||
|
||||
/**
|
||||
* In `build-mode: none` overlay mode, indicates that `source_file`
|
||||
* (`/path/to/foo.c`) uses the TRAP file `trap_file`; i.e. it is the
|
||||
@@ -252,16 +271,25 @@ trap_filename(
|
||||
* includes, or a template instantiation it transitively uses.
|
||||
*/
|
||||
source_file_uses_trap(
|
||||
string source_file: string ref,
|
||||
int source_file: @source_file ref,
|
||||
int trap_file: @trap ref
|
||||
);
|
||||
|
||||
/**
|
||||
* Holds if there is a definition of `element` in TRAP file `trap_file`.
|
||||
* In `build-mode: none` overlay mode, indicates that the TRAP file
|
||||
* `trap_file` uses tag `tag`.
|
||||
*/
|
||||
in_trap(
|
||||
trap_uses_tag(
|
||||
int trap_file: @trap ref,
|
||||
int tag: @tag ref
|
||||
);
|
||||
|
||||
/**
|
||||
* Holds if there is a definition of `element` in TRAP file or tag `t`.
|
||||
*/
|
||||
in_trap_or_tag(
|
||||
int element: @element ref,
|
||||
int trap_file: @trap ref
|
||||
int t: @trap_or_tag ref
|
||||
);
|
||||
|
||||
pch_uses(
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,11 @@
|
||||
class Element extends @element {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
class Trap extends @trap {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
from Element e, Trap trap
|
||||
where in_trap(e, trap)
|
||||
select e, trap
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,22 @@
|
||||
newtype TSourceFile = MkSourceFile(string name) { source_file_uses_trap(name, _) }
|
||||
|
||||
module FreshSourceFile = QlBuiltins::NewEntity<TSourceFile>;
|
||||
|
||||
class SourceFile extends FreshSourceFile::EntityId {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
class Trap extends @trap {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
query predicate mk_source_file_name(SourceFile source_file, string name) {
|
||||
source_file = FreshSourceFile::map(MkSourceFile(name))
|
||||
}
|
||||
|
||||
query predicate mk_source_file_uses_trap(SourceFile source_file, Trap trap) {
|
||||
exists(string name |
|
||||
source_file_uses_trap(name, trap) and
|
||||
mk_source_file_name(source_file, name)
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
description: Add source_file_name
|
||||
compatibility: backwards
|
||||
source_file_uses_trap.rel: run source_files.ql mk_source_file_uses_trap
|
||||
source_file_name.rel: run source_files.ql mk_source_file_name
|
||||
in_trap.rel: delete
|
||||
in_trap_or_tag.rel: run in_trap_or_tag.ql
|
||||
@@ -1,3 +1,11 @@
|
||||
## 1.5.13
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 1.5.12
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 1.5.11
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
@@ -218,7 +218,9 @@ where
|
||||
// only report if we cannot prove that the result of the
|
||||
// multiplication will be less (resp. greater) than the
|
||||
// maximum (resp. minimum) number we can compute.
|
||||
overflows(me, t1)
|
||||
overflows(me, t1) and
|
||||
// exclude cases where the expression type may not have been extracted accurately
|
||||
not me.getParent().(Call).getTarget().hasAmbiguousReturnType()
|
||||
select me,
|
||||
"Multiplication result may overflow '" + me.getType().toString() + "' before it is converted to '"
|
||||
+ me.getFullyConverted().getType().toString() + "'."
|
||||
|
||||
@@ -168,9 +168,11 @@ where
|
||||
formatOtherArgType(ffc, n, expected, arg, actual) and
|
||||
not actual.getUnspecifiedType().(IntegralType).getSize() = sizeof_IntType()
|
||||
) and
|
||||
// Exclude some cases where we're less confident the result is correct / clear / valuable
|
||||
not arg.isAffectedByMacro() and
|
||||
not arg.isFromUninstantiatedTemplate(_) and
|
||||
not actual.stripType() instanceof ErroneousType and
|
||||
not arg.getType().stripType().(RoutineType).getReturnType() instanceof ErroneousType and
|
||||
not arg.(Call).mayBeFromImplicitlyDeclaredFunction() and
|
||||
// Make sure that the format function definition is consistent
|
||||
count(ffc.getTarget().getFormatParameterIndex()) = 1
|
||||
|
||||
@@ -308,3 +308,37 @@ private module PossibleYearArithmeticOperationCheckConfig implements DataFlow::C
|
||||
|
||||
module PossibleYearArithmeticOperationCheckFlow =
|
||||
TaintTracking::Global<PossibleYearArithmeticOperationCheckConfig>;
|
||||
|
||||
/**
|
||||
* A time conversion function where either
|
||||
* 1) an incorrect leap year date would result in an error that can be checked from the return value or
|
||||
* 2) an incorrect leap year date is auto corrected (no checks required)
|
||||
*/
|
||||
class TimeConversionFunction extends Function {
|
||||
boolean autoLeapYearCorrecting;
|
||||
|
||||
TimeConversionFunction() {
|
||||
autoLeapYearCorrecting = false and
|
||||
(
|
||||
this.getName() =
|
||||
[
|
||||
"FileTimeToSystemTime", "SystemTimeToFileTime", "SystemTimeToTzSpecificLocalTime",
|
||||
"SystemTimeToTzSpecificLocalTimeEx", "TzSpecificLocalTimeToSystemTime",
|
||||
"TzSpecificLocalTimeToSystemTimeEx", "RtlLocalTimeToSystemTime",
|
||||
"RtlTimeToSecondsSince1970", "_mkgmtime", "SetSystemTime", "VarUdateFromDate", "from_tm"
|
||||
]
|
||||
or
|
||||
// Matches all forms of GetDateFormat, e.g. GetDateFormatA/W/Ex
|
||||
this.getName().matches("GetDateFormat%")
|
||||
)
|
||||
or
|
||||
autoLeapYearCorrecting = true and
|
||||
this.getName() =
|
||||
["mktime", "_mktime32", "_mktime64", "SystemTimeToVariantTime", "VariantTimeToSystemTime"]
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the function is expected to auto convert a bad leap year date.
|
||||
*/
|
||||
predicate isAutoLeapYearCorrecting() { autoLeapYearCorrecting = true }
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* @name Year field changed using an arithmetic operation without checking for leap year
|
||||
* @description A field that represents a year is being modified by an arithmetic operation, but no proper check for leap years can be detected afterwards.
|
||||
* @kind problem
|
||||
* @kind path-problem
|
||||
* @problem.severity warning
|
||||
* @id cpp/leap-year/unchecked-after-arithmetic-year-modification
|
||||
* @precision medium
|
||||
@@ -11,49 +11,844 @@
|
||||
|
||||
import cpp
|
||||
import LeapYear
|
||||
import semmle.code.cpp.controlflow.IRGuards
|
||||
|
||||
from Variable var, LeapYearFieldAccess yfa
|
||||
where
|
||||
exists(VariableAccess va |
|
||||
yfa.getQualifier() = va and
|
||||
var.getAnAccess() = va and
|
||||
// The year is modified with an arithmetic operation. Avoid values that are likely false positives
|
||||
yfa.isModifiedByArithmeticOperationNotForNormalization() and
|
||||
// Avoid false positives
|
||||
not (
|
||||
// If there is a local check for leap year after the modification
|
||||
exists(LeapYearFieldAccess yfacheck |
|
||||
yfacheck.getQualifier() = var.getAnAccess() and
|
||||
yfacheck.isUsedInCorrectLeapYearCheck() and
|
||||
yfacheck.getBasicBlock() = yfa.getBasicBlock().getASuccessor*()
|
||||
)
|
||||
/**
|
||||
* Functions whose operations should never be considered a
|
||||
* source or sink of a dangerous leap year operation.
|
||||
* The general concept is to add conversion functions
|
||||
* that convert one time type to another. Often
|
||||
* other ignorable operation heuristics will filter these,
|
||||
* but some cases, the simplest approach is to simply filter
|
||||
* the function entirely.
|
||||
* Note that flow through these functions should still be allowed
|
||||
* we just cannot start or end flow from an operation to a
|
||||
* year assignment in one of these functions.
|
||||
*/
|
||||
class IgnorableFunction extends Function {
|
||||
IgnorableFunction() {
|
||||
// arithmetic in known time conversion functions may look like dangerous operations
|
||||
// we assume all known time conversion functions are safe.
|
||||
this instanceof TimeConversionFunction
|
||||
or
|
||||
// Helper utility in postgres with string time conversions
|
||||
this.getName() = "DecodeISO8601Interval"
|
||||
or
|
||||
// helper utility for date conversions in qtbase
|
||||
this.getName() = "adjacentDay"
|
||||
or
|
||||
// Windows API function that does timezone conversions
|
||||
this.getName().matches("%SystemTimeToTzSpecificLocalTime%")
|
||||
or
|
||||
// Windows APIs that do time conversions
|
||||
this.getName().matches("%localtime%\\_s%")
|
||||
or
|
||||
// Windows APIs that do time conversions
|
||||
this.getName().matches("%SpecificLocalTimeToSystemTime%")
|
||||
or
|
||||
// postgres function for diffing timestamps, date for leap year
|
||||
// is not applicable.
|
||||
this.getName().toLowerCase().matches("%timestamp%age%")
|
||||
or
|
||||
// Reading byte streams often involves operations of some base, but that's
|
||||
// not a real source of leap year issues.
|
||||
this.getName().toLowerCase().matches("%read%bytes%")
|
||||
or
|
||||
// A postgres function for local time conversions
|
||||
// conversion operations (from one time structure to another) are generally ignorable
|
||||
this.getName() = "localsub"
|
||||
or
|
||||
// Indication of a calendar not applicable to
|
||||
// gregorian leap year, e.g., Hijri, Persian, Hebrew
|
||||
this.getName().toLowerCase().matches("%hijri%")
|
||||
or
|
||||
this.getFile().getBaseName().toLowerCase().matches("%hijri%")
|
||||
or
|
||||
this.getName().toLowerCase().matches("%persian%")
|
||||
or
|
||||
this.getFile().getBaseName().toLowerCase().matches("%persian%")
|
||||
or
|
||||
this.getName().toLowerCase().matches("%hebrew%")
|
||||
or
|
||||
this.getFile().getBaseName().toLowerCase().matches("%hebrew%")
|
||||
or
|
||||
// misc. from string/char converters heuristic
|
||||
this.getName()
|
||||
.toLowerCase()
|
||||
.matches(["%char%to%", "%string%to%", "%from%char%", "%from%string%"])
|
||||
or
|
||||
// boost's gregorian.cpp has year manipulations that are checked in complex ways.
|
||||
// ignore the entire file as a source or sink.
|
||||
this.getFile().getAbsolutePath().toLowerCase().matches("%boost%gregorian.cpp%")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The set of expressions which are ignorable; either because they seem to not be part of a year mutation,
|
||||
* or because they seem to be a conversion pattern of mapping date scalars.
|
||||
*/
|
||||
abstract class IgnorableOperation extends Expr { }
|
||||
|
||||
class IgnorableExprRem extends IgnorableOperation instanceof RemExpr { }
|
||||
|
||||
/**
|
||||
* An operation with 10, 100, 1000, 10000 as an operand is often a sign of conversion
|
||||
* or atoi.
|
||||
*/
|
||||
class IgnorableExpr10MultipleComponent extends IgnorableOperation {
|
||||
IgnorableExpr10MultipleComponent() {
|
||||
this.(Operation).getAnOperand().getValue().toInt() in [10, 100, 1000, 10000]
|
||||
or
|
||||
exists(AssignOperation a | a.getRValue() = this |
|
||||
a.getRValue().getValue().toInt() in [10, 100, 1000, 10000]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An operation involving a sub expression with char literal `48`, ignore as a likely string conversion. For example: `X - '0'`
|
||||
*/
|
||||
class IgnorableExpr48Mapping extends IgnorableOperation {
|
||||
IgnorableExpr48Mapping() {
|
||||
this.(SubExpr).getRightOperand().getValue().toInt() = 48
|
||||
or
|
||||
exists(AssignSubExpr e | e.getRValue() = this | e.getRValue().getValue().toInt() = 48)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A binary or arithmetic operation whereby one of the components is textual or a string.
|
||||
*/
|
||||
class IgnorableCharLiteralArithmetic extends IgnorableOperation {
|
||||
IgnorableCharLiteralArithmetic() {
|
||||
this.(BinaryArithmeticOperation).getAnOperand() instanceof TextLiteral
|
||||
or
|
||||
this instanceof TextLiteral and
|
||||
any(AssignArithmeticOperation arith).getRValue() = this
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constants often used in date conversions (from one date data type to another)
|
||||
* Numerous examples exist, like 1900 or 2000 that convert years from one
|
||||
* representation to another.
|
||||
* Also '0' is sometimes observed as an atoi style conversion.
|
||||
*/
|
||||
bindingset[c]
|
||||
predicate isLikelyConversionConstant(int c) {
|
||||
exists(int i | i = c.abs() |
|
||||
i =
|
||||
[
|
||||
146097, // days in 400-year Gregorian cycle
|
||||
36524, // days in 100-year Gregorian subcycle
|
||||
1461, // days in 4-year cycle (incl. 1 leap)
|
||||
32044, // Fliegel-van Flandern JDN epoch shift
|
||||
1721425, // JDN of 0001-01-01 (Gregorian)
|
||||
1721119, // alt epoch offset
|
||||
2400000, // MJD -> JDN conversion
|
||||
2400001, // alt MJD -> JDN conversion
|
||||
2141, // fixed-point month/day extraction
|
||||
65536, // observed in some conversions
|
||||
7834, // observed in some conversions
|
||||
256, // observed in some conversions
|
||||
292275056, // qdatetime.h Qt Core year range first year constant
|
||||
292278994, // qdatetime.h Qt Core year range last year constant
|
||||
1601, // Windows FILETIME epoch start year
|
||||
1970, // Unix epoch start year
|
||||
70, // Unix epoch start year short form
|
||||
1899, // Observed in uses with 1900 to address off by one scenarios
|
||||
1900, // Used when converting a 2 digit year
|
||||
2000, // Used when converting a 2 digit year
|
||||
1400, // Hijri base year, used when converting a 2 digit year
|
||||
1980, // FAT filesystem epoch start year
|
||||
227013, // constant observed for Hirji year conversion, and Hirji years are not applicable for gregorian leap year
|
||||
10631, // constant observed for Hirji year conversion, and Hirji years are not applicable for gregorian leap year,
|
||||
80, // 1980/01/01 is the start of the epoch on DOS
|
||||
0
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* An `isLikelyConversionConstant` constant indicates conversion that is ignorable, e.g.,
|
||||
* julian to gregorian conversion or conversions from linux time structs
|
||||
* that start at 1900, etc.
|
||||
*/
|
||||
class IgnorableConstantArithmetic extends IgnorableOperation {
|
||||
IgnorableConstantArithmetic() {
|
||||
exists(int i | isLikelyConversionConstant(i) |
|
||||
this.(Operation).getAnOperand().getValue().toInt() = i
|
||||
or
|
||||
// If there is a data flow from the variable that was modified to a function that seems to check for leap year
|
||||
exists(VariableAccess source, ChecksForLeapYearFunctionCall fc |
|
||||
source = var.getAnAccess() and
|
||||
LeapYearCheckFlow::flow(DataFlow::exprNode(source), DataFlow::exprNode(fc.getAnArgument()))
|
||||
)
|
||||
or
|
||||
// If there is a data flow from the field that was modified to a function that seems to check for leap year
|
||||
exists(VariableAccess vacheck, YearFieldAccess yfacheck, ChecksForLeapYearFunctionCall fc |
|
||||
vacheck = var.getAnAccess() and
|
||||
yfacheck.getQualifier() = vacheck and
|
||||
LeapYearCheckFlow::flow(DataFlow::exprNode(yfacheck), DataFlow::exprNode(fc.getAnArgument()))
|
||||
)
|
||||
or
|
||||
// If there is a successor or predecessor that sets the month = 1
|
||||
exists(MonthFieldAccess mfa, AssignExpr ae |
|
||||
mfa.getQualifier() = var.getAnAccess() and
|
||||
mfa.isModified() and
|
||||
(
|
||||
mfa.getBasicBlock() = yfa.getBasicBlock().getASuccessor*() or
|
||||
yfa.getBasicBlock() = mfa.getBasicBlock().getASuccessor+()
|
||||
) and
|
||||
ae = mfa.getEnclosingElement() and
|
||||
ae.getAnOperand().getValue().toInt() = 1
|
||||
exists(AssignArithmeticOperation a | this = a.getRValue() |
|
||||
a.getRValue().getValue().toInt() = i
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// If a unary minus assume it is some sort of conversion
|
||||
class IgnorableUnaryMinus extends IgnorableOperation {
|
||||
IgnorableUnaryMinus() {
|
||||
this instanceof UnaryMinusExpr
|
||||
or
|
||||
this.(Operation).getAnOperand() instanceof UnaryMinusExpr
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An argument to a function is ignorable if the function that is called is an ignored function
|
||||
*/
|
||||
class OperationAsArgToIgnorableFunction extends IgnorableOperation {
|
||||
OperationAsArgToIgnorableFunction() {
|
||||
exists(Call c |
|
||||
c.getAnArgument().getAChild*() = this and
|
||||
c.getTarget() instanceof IgnorableFunction
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A binary operation on two literals means the result is constant/known
|
||||
* and the operation is basically ignorable (it's not a real operation but
|
||||
* probably one visual simplicity what it means).
|
||||
*/
|
||||
class ConstantBinaryArithmeticOperation extends IgnorableOperation, BinaryArithmeticOperation {
|
||||
ConstantBinaryArithmeticOperation() {
|
||||
this.getLeftOperand() instanceof Literal and
|
||||
this.getRightOperand() instanceof Literal
|
||||
}
|
||||
}
|
||||
|
||||
class IgnorableBinaryBitwiseOperation extends IgnorableOperation instanceof BinaryBitwiseOperation {
|
||||
}
|
||||
|
||||
class IgnorableUnaryBitwiseOperation extends IgnorableOperation instanceof UnaryBitwiseOperation { }
|
||||
|
||||
class IgnorableAssignmentBitwiseOperation extends IgnorableOperation instanceof AssignBitwiseOperation
|
||||
{ }
|
||||
|
||||
/**
|
||||
* An arithmetic operation where one of the operands is a pointer or char type, ignore it
|
||||
*/
|
||||
class IgnorablePointerOrCharArithmetic extends IgnorableOperation {
|
||||
IgnorablePointerOrCharArithmetic() {
|
||||
this instanceof BinaryArithmeticOperation and
|
||||
exists(Expr op | op = this.(BinaryArithmeticOperation).getAnOperand() |
|
||||
op.getUnspecifiedType() instanceof PointerType
|
||||
or
|
||||
op.getUnspecifiedType() instanceof CharType
|
||||
or
|
||||
// Operations on calls to functions that accept char or char*
|
||||
op.(Call).getAnArgument().getUnspecifiedType().stripType() instanceof CharType
|
||||
or
|
||||
// Operations on calls to functions named like "strlen", "wcslen", etc
|
||||
// NOTE: workaround for cases where the wchar_t type is not a char, but an unsigned short
|
||||
// unclear if there is a best way to filter cases like these out based on type info.
|
||||
op.(Call).getTarget().getName().matches("%len%")
|
||||
)
|
||||
or
|
||||
exists(AssignArithmeticOperation a | a.getRValue() = this |
|
||||
exists(Expr op | op = a.getAnOperand() |
|
||||
op.getUnspecifiedType() instanceof PointerType
|
||||
or
|
||||
op.getUnspecifiedType() instanceof CharType
|
||||
or
|
||||
// Operations on calls to functions that accept char or char*
|
||||
op.(Call).getAnArgument().getUnspecifiedType().stripType() instanceof CharType
|
||||
)
|
||||
or
|
||||
// Operations on calls to functions named like "strlen", "wcslen", etc
|
||||
// for example `strlen(foo) + bar`
|
||||
this.(BinaryArithmeticOperation).getAnOperand().(Call).getTarget().getName().matches("%len%")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds for an expression that is an add or similar operation that could flow to a Year field.
|
||||
*/
|
||||
predicate isOperationSourceCandidate(Expr e) {
|
||||
not e instanceof IgnorableOperation and
|
||||
exists(Function f |
|
||||
f = e.getEnclosingFunction() and
|
||||
not f instanceof IgnorableFunction
|
||||
) and
|
||||
(
|
||||
e instanceof SubExpr
|
||||
or
|
||||
e instanceof AddExpr
|
||||
or
|
||||
e instanceof CrementOperation
|
||||
or
|
||||
e instanceof AssignSubExpr
|
||||
or
|
||||
e instanceof AssignAddExpr
|
||||
)
|
||||
select yfa,
|
||||
"Field $@ on variable $@ has been modified, but no appropriate check for LeapYear was found.",
|
||||
yfa.getTarget(), yfa.getTarget().toString(), var, var.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* A data flow that tracks an ignorable operation (such as a bitwise operation) to an operation source, so we may disqualify it.
|
||||
*/
|
||||
module IgnorableOperationToOperationSourceCandidateConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node n) { n.asExpr() instanceof IgnorableOperation }
|
||||
|
||||
predicate isSink(DataFlow::Node n) { isOperationSourceCandidate(n.asExpr()) }
|
||||
|
||||
// looking for sources and sinks in the same function
|
||||
DataFlow::FlowFeature getAFeature() {
|
||||
result instanceof DataFlow::FeatureEqualSourceSinkCallContext
|
||||
}
|
||||
}
|
||||
|
||||
module IgnorableOperationToOperationSourceCandidateFlow =
|
||||
TaintTracking::Global<IgnorableOperationToOperationSourceCandidateConfig>;
|
||||
|
||||
/**
|
||||
* The set of all expressions which is a candidate expression and also does not flow from to to some ignorable expression (eg. bitwise op)
|
||||
* ```
|
||||
* a = something <<< 2;
|
||||
* myDate.year = a + 1; // invalid
|
||||
* ...
|
||||
* a = someDate.year + 1;
|
||||
* myDate.year = a; // valid
|
||||
* ```
|
||||
*/
|
||||
class OperationSource extends Expr {
|
||||
OperationSource() {
|
||||
isOperationSourceCandidate(this) and
|
||||
// If the candidate came from an ignorable operation, ignore the candidate
|
||||
// NOTE: we cannot easily flow the candidate to an ignorable operation as that can
|
||||
// be tricky in practice, e.g., a mod operation on a year would be part of a leap year check
|
||||
// but a mod operation ending in a year is more indicative of something to ignore (a conversion)
|
||||
not exists(IgnorableOperationToOperationSourceCandidateFlow::PathNode sink |
|
||||
sink.getNode().asExpr() = this and
|
||||
sink.isSink()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class YearFieldAssignmentNode extends DataFlow::Node {
|
||||
YearFieldAccess access;
|
||||
|
||||
YearFieldAssignmentNode() {
|
||||
exists(Function f |
|
||||
f = this.getEnclosingCallable().getUnderlyingCallable() and not f instanceof IgnorableFunction
|
||||
) and
|
||||
(
|
||||
this.asDefinition().(Assignment).getLValue() = access
|
||||
or
|
||||
this.asDefinition().(CrementOperation).getOperand() = access
|
||||
or
|
||||
exists(Call c | c.getAnArgument() = access and this.asDefiningArgument() = access)
|
||||
or
|
||||
exists(Call c, AddressOfExpr aoe |
|
||||
c.getAnArgument() = aoe and
|
||||
aoe.getOperand() = access and
|
||||
this.asDefiningArgument() = aoe
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
YearFieldAccess getYearFieldAccess() { result = access }
|
||||
}
|
||||
|
||||
/**
|
||||
* A DataFlow configuration for identifying flows from an identified source
|
||||
* to the Year field of a date object.
|
||||
*/
|
||||
module OperationToYearAssignmentConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node n) { n.asExpr() instanceof OperationSource }
|
||||
|
||||
predicate isSink(DataFlow::Node n) {
|
||||
n instanceof YearFieldAssignmentNode and
|
||||
not isYearModifiedWithCheck(n) and
|
||||
not isControlledByMonthEqualityCheckNonFebruary(n.asExpr())
|
||||
}
|
||||
|
||||
predicate isBarrier(DataFlow::Node n) {
|
||||
exists(ArrayExpr arr | arr.getArrayOffset() = n.asExpr())
|
||||
or
|
||||
n.getType().getUnspecifiedType() instanceof PointerType
|
||||
or
|
||||
n.getType().getUnspecifiedType() instanceof CharType
|
||||
or
|
||||
// If a type resembles "string" ignore flow (likely string conversion, currently ignored)
|
||||
n.getType().getUnspecifiedType().stripType().getName().toLowerCase().matches("%string%")
|
||||
or
|
||||
n.asExpr() instanceof IgnorableOperation
|
||||
or
|
||||
// Flowing into variables that indicate likely non-gregorian years are barriers
|
||||
// e.g., names similar to hijri, persian, lunar, chinese, hebrew, etc.
|
||||
exists(Variable v |
|
||||
v.getName()
|
||||
.toLowerCase()
|
||||
.matches(["%hijri%", "%persian%", "%lunar%", "%chinese%", "%hebrew%"]) and
|
||||
v.getAnAccess() = [n.asIndirectExpr(), n.asExpr()]
|
||||
)
|
||||
or
|
||||
isLeapYearCheckSink(n)
|
||||
or
|
||||
// this is a bit of a hack to address cases where a year is normalized and checked, but the
|
||||
// normalized year is never itself assigned to the final year struct
|
||||
// isLeapYear(getCivilYear(year))
|
||||
// struct.year = year
|
||||
// This is assuming a user would have done this all on one line though.
|
||||
// setting a variable for the conversion and passing that separately would be more difficult to track
|
||||
// considering this approach good enough for current observed false positives
|
||||
exists(Expr arg |
|
||||
isLeapYearCheckCall(_, arg) and arg.getAChild*() = [n.asExpr(), n.asIndirectExpr()]
|
||||
)
|
||||
or
|
||||
// If as the flow progresses, the value holding a dangerous operation result
|
||||
// is apparently being passed by address to some function, it is more than likely
|
||||
// intended to be modified, and therefore, the definition is killed.
|
||||
exists(Call c | c.getAnArgument().(AddressOfExpr).getAnOperand() = n.asIndirectExpr())
|
||||
}
|
||||
|
||||
/** Block flow out of an operation source to get the "closest" operation to the sink */
|
||||
predicate isBarrierIn(DataFlow::Node n) { isSource(n) }
|
||||
|
||||
predicate isBarrierOut(DataFlow::Node n) { isSink(n) }
|
||||
}
|
||||
|
||||
module OperationToYearAssignmentFlow = TaintTracking::Global<OperationToYearAssignmentConfig>;
|
||||
|
||||
predicate isLeapYearCheckSink(DataFlow::Node sink) {
|
||||
exists(LeapYearGuardCondition lgc |
|
||||
lgc.checkedYearAccess() = [sink.asExpr(), sink.asIndirectExpr()]
|
||||
)
|
||||
or
|
||||
isLeapYearCheckCall(_, [sink.asExpr(), sink.asIndirectExpr()])
|
||||
}
|
||||
|
||||
predicate yearAssignmentToCheckCommonSteps(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
// flow from a YearFieldAccess to the qualifier
|
||||
node2.asExpr() = node1.asExpr().(YearFieldAccess).getQualifier*()
|
||||
or
|
||||
// getting the 'access' can be tricky at definitions (assignments especially)
|
||||
// as dataflow uses asDefinition not asExpr.
|
||||
// the YearFieldAssignmentNode holds the access in these cases
|
||||
node1.(YearFieldAssignmentNode).getYearFieldAccess().getQualifier() = node2.asExpr()
|
||||
or
|
||||
// flow from a year access qualifier to a year field
|
||||
exists(YearFieldAccess yfa | node2.asExpr() = yfa and node1.asExpr() = yfa.getQualifier())
|
||||
or
|
||||
node1.(YearFieldAssignmentNode).getYearFieldAccess().getQualifier() = node2.asExpr()
|
||||
or
|
||||
// Pass through any intermediate struct
|
||||
exists(Assignment a |
|
||||
a.getRValue() = node1.asExpr() and
|
||||
node2.asExpr() = a.getLValue().(YearFieldAccess).getQualifier*()
|
||||
)
|
||||
or
|
||||
// in cases of t.year = x and the value of x is checked, but the year t.year isn't directly checked
|
||||
// flow from a year assignment node to an RHS if it is an assignment
|
||||
// e.g.,
|
||||
// t.year = x;
|
||||
// isLeapYear(x);
|
||||
// --> at this point there is no flow of t.year to a check, but only its raw value
|
||||
// To detect the flow of 'x' to the isLeapYear check,
|
||||
// flow from t.year to 'x' (at assignment, t.year = x, flow to the RHS to track use-use flow of x)
|
||||
exists(YearFieldAssignmentNode yfan |
|
||||
node1 = yfan and
|
||||
node2.asExpr() = yfan.asDefinition().(Assignment).getRValue()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A flow configuration from a Year field access to some Leap year check or guard
|
||||
*/
|
||||
module YearAssignmentToLeapYearCheckConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof YearFieldAssignmentNode }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { isLeapYearCheckSink(sink) }
|
||||
|
||||
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
yearAssignmentToCheckCommonSteps(node1, node2)
|
||||
}
|
||||
|
||||
/**
|
||||
* Enforcing the check must occur in the same call context as the source,
|
||||
* i.e., do not return from the source function and check in a caller.
|
||||
*/
|
||||
DataFlow::FlowFeature getAFeature() { result instanceof DataFlow::FeatureHasSourceCallContext }
|
||||
}
|
||||
|
||||
module YearAssignmentToLeapYearCheckFlow =
|
||||
TaintTracking::Global<YearAssignmentToLeapYearCheckConfig>;
|
||||
|
||||
/** Does there exist a flow from the given YearFieldAccess to a Leap Year check or guard? */
|
||||
predicate isYearModifiedWithCheck(YearFieldAssignmentNode n) {
|
||||
exists(YearAssignmentToLeapYearCheckFlow::PathNode src |
|
||||
src.isSource() and
|
||||
src.getNode() = n
|
||||
)
|
||||
or
|
||||
// If the time flows to a time conversion whose value/result is checked,
|
||||
// assume the leap year is being handled.
|
||||
exists(YearAssignmentToCheckedTimeConversionFlow::PathNode timeQualSrc |
|
||||
timeQualSrc.isSource() and
|
||||
timeQualSrc.getNode() = n
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression which checks the value of a Month field `a->month == 1`.
|
||||
*/
|
||||
class MonthEqualityCheck extends EqualityOperation {
|
||||
MonthEqualityCheck() { this.getAnOperand() instanceof MonthFieldAccess }
|
||||
|
||||
Expr getExprCompared() {
|
||||
exists(Expr e |
|
||||
e = this.getAnOperand() and
|
||||
not e instanceof MonthFieldAccess and
|
||||
result = e
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
final class FinalMonthEqualityCheck = MonthEqualityCheck;
|
||||
|
||||
class MonthEqualityCheckGuard extends GuardCondition, FinalMonthEqualityCheck { }
|
||||
|
||||
/**
|
||||
* Verifies if the expression is guarded by a check on the Month property of a date struct, that is NOT February.
|
||||
*/
|
||||
bindingset[e]
|
||||
pragma[inline_late]
|
||||
predicate isControlledByMonthEqualityCheckNonFebruary(Expr e) {
|
||||
exists(MonthEqualityCheckGuard monthGuard, Expr compared |
|
||||
monthGuard.controls(e.getBasicBlock(), true) and
|
||||
compared = monthGuard.getExprCompared() and
|
||||
not compared.getValue().toInt() = 2
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Flow from a year field access to a time conversion function
|
||||
* that auto converts feb29 in non-leap year, or through a conversion function that doesn't
|
||||
* auto convert to a sanity check guard of the result for error conditions.
|
||||
*/
|
||||
module YearAssignmentToCheckedTimeConversionConfig implements DataFlow::StateConfigSig {
|
||||
// Flow state tracks if flow goes through a known time conversion function
|
||||
// see `TimeConversionFunction`.
|
||||
// A valid check with a time conversion function is either the case:
|
||||
// 1) the year flows into a time conversion function, and the time conversion function's result is checked or
|
||||
// 2) the year flows into a time conversion function that auto corrects for leap year, so no check is necessary.
|
||||
class FlowState = boolean;
|
||||
|
||||
predicate isSource(DataFlow::Node source, FlowState state) {
|
||||
source instanceof YearFieldAssignmentNode and
|
||||
state = false
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink, FlowState state) {
|
||||
// Case 1: Flow through a time conversion function that requires a check,
|
||||
// and we have arrived at a guard, implying the result was checked for possible error, including leap year error.
|
||||
// state = true indicates the flow went through a time conversion function
|
||||
state = true and
|
||||
(
|
||||
exists(IfStmt ifs | ifs.getCondition().getAChild*() = [sink.asExpr(), sink.asIndirectExpr()])
|
||||
or
|
||||
exists(ConditionalExpr ce |
|
||||
ce.getCondition().getAChild*() = [sink.asExpr(), sink.asIndirectExpr()]
|
||||
)
|
||||
or
|
||||
exists(Loop l | l.getCondition().getAChild*() = [sink.asExpr(), sink.asIndirectExpr()])
|
||||
)
|
||||
or
|
||||
// Case 2: Flow through a time conversion function that auto corrects for leap year, so no check is necessary.
|
||||
// state true or false, as flowing through a time conversion function is not necessary in this instance.
|
||||
state in [true, false] and
|
||||
exists(Call c, TimeConversionFunction f |
|
||||
f.isAutoLeapYearCorrecting() and
|
||||
c.getTarget() = f and
|
||||
c.getAnArgument().getAChild*() = [sink.asExpr(), sink.asIndirectExpr()]
|
||||
)
|
||||
}
|
||||
|
||||
predicate isAdditionalFlowStep(
|
||||
DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2
|
||||
) {
|
||||
state1 in [true, false] and
|
||||
state2 = true and
|
||||
exists(Call c |
|
||||
c.getTarget() instanceof TimeConversionFunction and
|
||||
c.getAnArgument().getAChild*() = [node1.asExpr(), node1.asIndirectExpr()] and
|
||||
node2.asExpr() = c
|
||||
)
|
||||
}
|
||||
|
||||
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
yearAssignmentToCheckCommonSteps(node1, node2)
|
||||
}
|
||||
|
||||
DataFlow::FlowFeature getAFeature() { result instanceof DataFlow::FeatureHasSourceCallContext }
|
||||
}
|
||||
|
||||
module YearAssignmentToCheckedTimeConversionFlow =
|
||||
DataFlow::GlobalWithState<YearAssignmentToCheckedTimeConversionConfig>;
|
||||
|
||||
/**
|
||||
* Finds flow from a parameter of a function to a leap year check.
|
||||
* This is necessary to handle for scenarios like this:
|
||||
*
|
||||
* year = DANGEROUS_OP // source
|
||||
* isLeap = isLeapYear(year);
|
||||
* // logic based on isLeap
|
||||
* struct.year = year; // sink
|
||||
*
|
||||
* In this case, we may flow a dangerous op to a year assignment, failing
|
||||
* to barrier the flow through a leap year check, as the leap year check
|
||||
* is nested, and dataflow does not progress down into the check and out.
|
||||
* Instead, the point of this flow is to detect isLeapYear's argument
|
||||
* is checked for leap year, making the isLeapYear call a barrier for
|
||||
* the dangerous flow if we flow through the parameter identified to
|
||||
* be checked.
|
||||
*/
|
||||
module ParameterToLeapYearCheckConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { exists(source.asParameter()) }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
exists(LeapYearGuardCondition lgc |
|
||||
lgc.checkedYearAccess() = [sink.asExpr(), sink.asIndirectExpr()]
|
||||
)
|
||||
}
|
||||
|
||||
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
// flow from a YearFieldAccess to the qualifier
|
||||
node2.asExpr() = node1.asExpr().(YearFieldAccess).getQualifier*()
|
||||
or
|
||||
// flow from a year access qualifier to a year field
|
||||
exists(YearFieldAccess yfa | node2.asExpr() = yfa and node1.asExpr() = yfa.getQualifier())
|
||||
}
|
||||
|
||||
/**
|
||||
* Enforcing the check must occur in the same call context as the source,
|
||||
* i.e., do not return from the source function and check in a caller.
|
||||
*/
|
||||
DataFlow::FlowFeature getAFeature() { result instanceof DataFlow::FeatureHasSourceCallContext }
|
||||
}
|
||||
|
||||
// NOTE: I do not believe taint flow is necessary here as we should
|
||||
// be flowing directyly from some parameter to a leap year check.
|
||||
module ParameterToLeapYearCheckFlow = DataFlow::Global<ParameterToLeapYearCheckConfig>;
|
||||
|
||||
predicate isLeapYearCheckCall(Call c, Expr arg) {
|
||||
exists(ParameterToLeapYearCheckFlow::PathNode src, Function f, int i |
|
||||
src.isSource() and
|
||||
f.getParameter(i) = src.getNode().asParameter() and
|
||||
c.getTarget() = f and
|
||||
c.getArgument(i) = arg
|
||||
)
|
||||
}
|
||||
|
||||
class LeapYearGuardCondition extends GuardCondition {
|
||||
Expr yearSinkDiv4;
|
||||
Expr yearSinkDiv100;
|
||||
Expr yearSinkDiv400;
|
||||
|
||||
LeapYearGuardCondition() {
|
||||
exists(
|
||||
LogicalAndExpr andExpr, LogicalOrExpr orExpr, GuardCondition div4Check,
|
||||
GuardCondition div100Check, GuardCondition div400Check, GuardValue gv
|
||||
|
|
||||
// canonical case:
|
||||
// form: `(year % 4 == 0) && (year % 100 != 0 || year % 400 == 0)`
|
||||
// `!((year % 4 == 0) && (year % 100 != 0 || year % 400 == 0))`
|
||||
// `!(year % 4) && (year % 100 || !(year % 400))`
|
||||
// Also accepting `((year & 3) == 0) && (year % 100 != 0 || year % 400 == 0)`
|
||||
// and `(year % 4 == 0) && (year % 100 > 0 || year % 400 == 0)`
|
||||
this = andExpr and
|
||||
andExpr.hasOperands(div4Check, orExpr) and
|
||||
orExpr.hasOperands(div100Check, div400Check) and
|
||||
(
|
||||
// year % 4 == 0
|
||||
exists(RemExpr e |
|
||||
div4Check.comparesEq(e, 0, true, gv) and
|
||||
e.getRightOperand().getValue().toInt() = 4 and
|
||||
yearSinkDiv4 = e.getLeftOperand()
|
||||
)
|
||||
or
|
||||
// year & 3 == 0
|
||||
exists(BitwiseAndExpr e |
|
||||
div4Check.comparesEq(e, 0, true, gv) and
|
||||
e.getRightOperand().getValue().toInt() = 3 and
|
||||
yearSinkDiv4 = e.getLeftOperand()
|
||||
)
|
||||
) and
|
||||
exists(RemExpr e |
|
||||
// year % 100 != 0 or year % 100 > 0
|
||||
(
|
||||
div100Check.comparesEq(e, 0, false, gv) or
|
||||
div100Check.comparesLt(e, 1, false, gv)
|
||||
) and
|
||||
e.getRightOperand().getValue().toInt() = 100 and
|
||||
yearSinkDiv100 = e.getLeftOperand()
|
||||
) and
|
||||
// year % 400 == 0
|
||||
exists(RemExpr e |
|
||||
div400Check.comparesEq(e, 0, true, gv) and
|
||||
e.getRightOperand().getValue().toInt() = 400 and
|
||||
yearSinkDiv400 = e.getLeftOperand()
|
||||
)
|
||||
or
|
||||
// Inverted logic case:
|
||||
// `year % 4 != 0 || (year % 100 == 0 && year % 400 != 0)`
|
||||
// or `year & 3 != 0 || (year % 100 == 0 && year % 400 != 0)`
|
||||
// also accepting `year % 4 > 0 || (year % 100 == 0 && year % 400 > 0)`
|
||||
this = orExpr and
|
||||
orExpr.hasOperands(div4Check, andExpr) and
|
||||
andExpr.hasOperands(div100Check, div400Check) and
|
||||
(
|
||||
// year % 4 != 0 or year % 4 > 0
|
||||
exists(RemExpr e |
|
||||
(
|
||||
div4Check.comparesEq(e, 0, false, gv)
|
||||
or
|
||||
div4Check.comparesLt(e, 1, false, gv)
|
||||
) and
|
||||
e.getRightOperand().getValue().toInt() = 4 and
|
||||
yearSinkDiv4 = e.getLeftOperand()
|
||||
)
|
||||
or
|
||||
// year & 3 != 0
|
||||
exists(BitwiseAndExpr e |
|
||||
div4Check.comparesEq(e, 0, false, gv) and
|
||||
e.getRightOperand().getValue().toInt() = 3 and
|
||||
yearSinkDiv4 = e.getLeftOperand()
|
||||
)
|
||||
) and
|
||||
// year % 100 == 0
|
||||
exists(RemExpr e |
|
||||
div100Check.comparesEq(e, 0, true, gv) and
|
||||
e.getRightOperand().getValue().toInt() = 100 and
|
||||
yearSinkDiv100 = e.getLeftOperand()
|
||||
) and
|
||||
// year % 400 != 0 or year % 400 > 0
|
||||
exists(RemExpr e |
|
||||
(
|
||||
div400Check.comparesEq(e, 0, false, gv)
|
||||
or
|
||||
div400Check.comparesLt(e, 1, false, gv)
|
||||
) and
|
||||
e.getRightOperand().getValue().toInt() = 400 and
|
||||
yearSinkDiv400 = e.getLeftOperand()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
Expr getYearSinkDiv4() { result = yearSinkDiv4 }
|
||||
|
||||
Expr getYearSinkDiv100() { result = yearSinkDiv100 }
|
||||
|
||||
Expr getYearSinkDiv400() { result = yearSinkDiv400 }
|
||||
|
||||
/**
|
||||
* Gets the variable access that is used in all 3 components of the leap year check
|
||||
* e.g., see getYearSinkDiv4/100/400..
|
||||
* If a field access is used, the qualifier and the field access are both returned
|
||||
* in checked condition.
|
||||
* NOTE: if the year is not checked using the same access in all 3 components, no result is returned.
|
||||
* The typical case observed is a consistent variable access is used. If not, this may indicate a bug.
|
||||
* We could check more accurately with a dataflow analysis, but this is likely sufficient for now.
|
||||
*/
|
||||
VariableAccess checkedYearAccess() {
|
||||
exists(Variable var |
|
||||
(
|
||||
this.getYearSinkDiv4().getAChild*() = var.getAnAccess() and
|
||||
this.getYearSinkDiv100().getAChild*() = var.getAnAccess() and
|
||||
this.getYearSinkDiv400().getAChild*() = var.getAnAccess() and
|
||||
result = var.getAnAccess() and
|
||||
(
|
||||
result = this.getYearSinkDiv4().getAChild*() or
|
||||
result = this.getYearSinkDiv100().getAChild*() or
|
||||
result = this.getYearSinkDiv400().getAChild*()
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A difficult case to detect is if a year modification is tied to a month or day modification
|
||||
* and the month or day is safe for leap year.
|
||||
* e.g.,
|
||||
* year++;
|
||||
* month = 1;
|
||||
* // alternative: day = 15;
|
||||
* ... values eventually used in the same time struct
|
||||
* If this is even more challenging if the struct the values end up in are not
|
||||
* local (set inter-procedurally).
|
||||
* This configuration looks for constants 1-31 flowing to a month or day assignment.
|
||||
* It is assumed a user of this flow will check if the month/day source and month/day sink
|
||||
* are in the same basic blocks as a year modification source and a year modification sink.
|
||||
* It is also assumed a user will check if the constant source is a value that is ignorable
|
||||
* e.g., if it is 2 and the sink is a month assignment, then it isn't ignorable or
|
||||
* if the value is < 27 and is a day assignment, it is likely ignorable
|
||||
*
|
||||
* Obviously this does not handle all conditions (e.g., the month set in another block).
|
||||
* It is meant to capture the most common cases of false positives.
|
||||
*/
|
||||
module CandidateConstantToDayOrMonthAssignmentConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
source.asExpr().getValue().toInt() in [1 .. 31] and
|
||||
(
|
||||
exists(Assignment a | a.getRValue() = source.asExpr())
|
||||
or
|
||||
exists(Call c | c.getAnArgument() = source.asExpr())
|
||||
)
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
exists(Assignment a |
|
||||
(a.getLValue() instanceof MonthFieldAccess or a.getLValue() instanceof DayFieldAccess) and
|
||||
a.getRValue() = sink.asExpr()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: only data flow here (no taint tracking) as we want the exact
|
||||
// constant flowing to the month assignment
|
||||
module CandidateConstantToDayOrMonthAssignmentFlow =
|
||||
DataFlow::Global<CandidateConstantToDayOrMonthAssignmentConfig>;
|
||||
|
||||
/**
|
||||
* Holds if value the assignment `a` resolves to (`dayOrMonthValSrcExpr`) doesn't represent February,
|
||||
* and/or if it represents a day, is a 'safe' day (meaning the 27th or prior).
|
||||
*/
|
||||
bindingset[dayOrMonthValSrcExpr]
|
||||
predicate isSafeValueForAssignmentOfMonthOrDayValue(Assignment a, Expr dayOrMonthValSrcExpr) {
|
||||
a.getLValue() instanceof MonthFieldAccess and
|
||||
dayOrMonthValSrcExpr.getValue().toInt() != 2
|
||||
or
|
||||
a.getLValue() instanceof DayFieldAccess and
|
||||
dayOrMonthValSrcExpr.getValue().toInt() <= 27
|
||||
}
|
||||
|
||||
import OperationToYearAssignmentFlow::PathGraph
|
||||
|
||||
from OperationToYearAssignmentFlow::PathNode src, OperationToYearAssignmentFlow::PathNode sink
|
||||
where
|
||||
OperationToYearAssignmentFlow::flowPath(src, sink) and
|
||||
// Check if a month is set in the same block as the year operation source
|
||||
// and the month value would indicate its set to any other month than february.
|
||||
// Finds if the source year node is in the same block as a source month block
|
||||
// and if the same for the sinks.
|
||||
not exists(DataFlow::Node dayOrMonthValSrc, DataFlow::Node dayOrMonthValSink, Assignment a |
|
||||
CandidateConstantToDayOrMonthAssignmentFlow::flow(dayOrMonthValSrc, dayOrMonthValSink) and
|
||||
a.getRValue() = dayOrMonthValSink.asExpr() and
|
||||
dayOrMonthValSink.getBasicBlock() = sink.getNode().getBasicBlock() and
|
||||
exists(IRBlock dayOrMonthValBB |
|
||||
dayOrMonthValBB = dayOrMonthValSrc.getBasicBlock() and
|
||||
// The source of the day is set in the same block as the source for the year
|
||||
// or the source for the day is set in the same block as the sink for the year
|
||||
dayOrMonthValBB in [
|
||||
src.getNode().getBasicBlock(),
|
||||
sink.getNode().getBasicBlock()
|
||||
]
|
||||
) and
|
||||
isSafeValueForAssignmentOfMonthOrDayValue(a, dayOrMonthValSrc.asExpr())
|
||||
)
|
||||
select sink, src, sink,
|
||||
"Year field has been modified, but no appropriate check for LeapYear was found."
|
||||
|
||||
@@ -44,23 +44,9 @@ class SafeTimeGatheringFunction extends Function {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This list of APIs should check for the return value to detect problems during the conversion.
|
||||
*/
|
||||
class TimeConversionFunction extends Function {
|
||||
TimeConversionFunction() {
|
||||
this.getQualifiedName() =
|
||||
[
|
||||
"FileTimeToSystemTime", "SystemTimeToFileTime", "SystemTimeToTzSpecificLocalTime",
|
||||
"SystemTimeToTzSpecificLocalTimeEx", "TzSpecificLocalTimeToSystemTime",
|
||||
"TzSpecificLocalTimeToSystemTimeEx", "RtlLocalTimeToSystemTime",
|
||||
"RtlTimeToSecondsSince1970", "_mkgmtime"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
from FunctionCall fcall, TimeConversionFunction trf, Variable var
|
||||
where
|
||||
not trf.isAutoLeapYearCorrecting() and
|
||||
fcall = trf.getACallToThisFunction() and
|
||||
fcall instanceof ExprInVoidContext and
|
||||
var.getUnderlyingType() instanceof UnpackedTimeType and
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils
|
||||
import semmle.code.cpp.ir.dataflow.DataFlow
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowNodes
|
||||
|
||||
/** Gets a loop that contains `e`. */
|
||||
Loop getAnEnclosingLoopOfExpr(Expr e) { result = getAnEnclosingLoopOfStmt(e.getEnclosingStmt()) }
|
||||
@@ -45,9 +46,9 @@ private Expr getExpr(DataFlow::Node node) {
|
||||
or
|
||||
result = node.asOperand().getUse().getAst()
|
||||
or
|
||||
result = node.(DataFlow::RawIndirectInstruction).getInstruction().getAst()
|
||||
result = node.(RawIndirectInstruction).getInstruction().getAst()
|
||||
or
|
||||
result = node.(DataFlow::RawIndirectOperand).getOperand().getUse().getAst()
|
||||
result = node.(RawIndirectOperand).getOperand().getUse().getAst()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -208,7 +209,7 @@ class LoopWithAlloca extends Stmt {
|
||||
this.conditionRequiresInequality(va, _, _) and
|
||||
DataFlow::localFlow(result, DataFlow::exprNode(va)) and
|
||||
// Phi nodes will be preceded by nodes that represent actual definitions
|
||||
not result instanceof DataFlow::SsaSynthNode and
|
||||
not result instanceof SsaSynthNode and
|
||||
// A source is outside the loop if it's not inside the loop
|
||||
not exists(Expr e | e = getExpr(result) | this = getAnEnclosingLoopOfExpr(e))
|
||||
)
|
||||
|
||||
@@ -16,17 +16,15 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.ir.IR
|
||||
import semmle.code.cpp.ir.dataflow.MustFlow
|
||||
import PathGraph
|
||||
import ReturnStackAllocatedMemory::PathGraph
|
||||
|
||||
/** Holds if `f` has a name that we interpret as evidence of intentionally returning the value of the stack pointer. */
|
||||
predicate intentionallyReturnsStackPointer(Function f) {
|
||||
f.getName().toLowerCase().matches(["%stack%", "%sp%"])
|
||||
}
|
||||
|
||||
class ReturnStackAllocatedMemoryConfig extends MustFlowConfiguration {
|
||||
ReturnStackAllocatedMemoryConfig() { this = "ReturnStackAllocatedMemoryConfig" }
|
||||
|
||||
override predicate isSource(Instruction source) {
|
||||
module ReturnStackAllocatedMemoryConfig implements MustFlow::ConfigSig {
|
||||
predicate isSource(Instruction source) {
|
||||
exists(Function func |
|
||||
// Rule out FPs caused by extraction errors.
|
||||
not func.hasErrors() and
|
||||
@@ -50,7 +48,7 @@ class ReturnStackAllocatedMemoryConfig extends MustFlowConfiguration {
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSink(Operand sink) {
|
||||
predicate isSink(Operand sink) {
|
||||
// Holds if `sink` is a node that represents the `StoreInstruction` that is subsequently used in
|
||||
// a `ReturnValueInstruction`.
|
||||
// We use the `StoreInstruction` instead of the instruction that defines the
|
||||
@@ -72,7 +70,7 @@ class ReturnStackAllocatedMemoryConfig extends MustFlowConfiguration {
|
||||
// int* px = id(&x);
|
||||
// }
|
||||
// ```
|
||||
override predicate allowInterproceduralFlow() { none() }
|
||||
predicate allowInterproceduralFlow() { none() }
|
||||
|
||||
/**
|
||||
* This configuration intentionally conflates addresses of fields and their object, and pointer offsets
|
||||
@@ -87,20 +85,22 @@ class ReturnStackAllocatedMemoryConfig extends MustFlowConfiguration {
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
override predicate isAdditionalFlowStep(Operand node1, Instruction node2) {
|
||||
predicate isAdditionalFlowStep(Operand node1, Instruction node2) {
|
||||
node2.(FieldAddressInstruction).getObjectAddressOperand() = node1
|
||||
or
|
||||
node2.(PointerOffsetInstruction).getLeftOperand() = node1
|
||||
}
|
||||
|
||||
override predicate isBarrier(Instruction n) { n.getResultType() instanceof ErroneousType }
|
||||
predicate isBarrier(Instruction n) { n.getResultType() instanceof ErroneousType }
|
||||
}
|
||||
|
||||
module ReturnStackAllocatedMemory = MustFlow::Global<ReturnStackAllocatedMemoryConfig>;
|
||||
|
||||
from
|
||||
MustFlowPathNode source, MustFlowPathNode sink, Instruction instr,
|
||||
ReturnStackAllocatedMemoryConfig conf
|
||||
ReturnStackAllocatedMemory::PathNode source, ReturnStackAllocatedMemory::PathNode sink,
|
||||
Instruction instr
|
||||
where
|
||||
conf.hasFlowPath(pragma[only_bind_into](source), pragma[only_bind_into](sink)) and
|
||||
ReturnStackAllocatedMemory::flowPath(pragma[only_bind_into](source), pragma[only_bind_into](sink)) and
|
||||
source.getInstruction() = instr
|
||||
select sink.getInstruction(), source, sink, "May return stack-allocated memory from $@.",
|
||||
instr.getAst(), instr.getAst().toString()
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.ir.IR
|
||||
import semmle.code.cpp.ir.dataflow.MustFlow
|
||||
import PathGraph
|
||||
import UninitializedLocal::PathGraph
|
||||
|
||||
/**
|
||||
* Auxiliary predicate: Types that don't require initialization
|
||||
@@ -70,25 +70,26 @@ predicate isSinkImpl(Instruction sink, VariableAccess va) {
|
||||
)
|
||||
}
|
||||
|
||||
class MustFlow extends MustFlowConfiguration {
|
||||
MustFlow() { this = "MustFlow" }
|
||||
|
||||
override predicate isSource(Instruction source) {
|
||||
module UninitializedLocalConfig implements MustFlow::ConfigSig {
|
||||
predicate isSource(Instruction source) {
|
||||
source instanceof UninitializedInstruction and
|
||||
exists(Type t | t = source.getResultType() | not allocatedType(t))
|
||||
}
|
||||
|
||||
override predicate isSink(Operand sink) { isSinkImpl(sink.getDef(), _) }
|
||||
predicate isSink(Operand sink) { isSinkImpl(sink.getDef(), _) }
|
||||
|
||||
override predicate allowInterproceduralFlow() { none() }
|
||||
predicate allowInterproceduralFlow() { none() }
|
||||
|
||||
override predicate isBarrier(Instruction instr) { instr instanceof ChiInstruction }
|
||||
predicate isBarrier(Instruction instr) { instr instanceof ChiInstruction }
|
||||
}
|
||||
|
||||
module UninitializedLocal = MustFlow::Global<UninitializedLocalConfig>;
|
||||
|
||||
from
|
||||
VariableAccess va, LocalVariable v, MustFlow conf, MustFlowPathNode source, MustFlowPathNode sink
|
||||
VariableAccess va, LocalVariable v, UninitializedLocal::PathNode source,
|
||||
UninitializedLocal::PathNode sink
|
||||
where
|
||||
conf.hasFlowPath(source, sink) and
|
||||
UninitializedLocal::flowPath(source, sink) and
|
||||
isSinkImpl(sink.getInstruction(), va) and
|
||||
v = va.getTarget()
|
||||
select va, source, sink, "The variable $@ may not be initialized at this access.", v, v.getName()
|
||||
|
||||
@@ -17,16 +17,16 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.ir.IR
|
||||
import semmle.code.cpp.ir.dataflow.MustFlow
|
||||
import PathGraph
|
||||
import UnsafeUseOfThis::PathGraph
|
||||
|
||||
class UnsafeUseOfThisConfig extends MustFlowConfiguration {
|
||||
UnsafeUseOfThisConfig() { this = "UnsafeUseOfThisConfig" }
|
||||
module UnsafeUseOfThisConfig implements MustFlow::ConfigSig {
|
||||
predicate isSource(Instruction source) { isSource(source, _, _) }
|
||||
|
||||
override predicate isSource(Instruction source) { isSource(source, _, _) }
|
||||
|
||||
override predicate isSink(Operand sink) { isSink(sink, _) }
|
||||
predicate isSink(Operand sink) { isSink(sink, _) }
|
||||
}
|
||||
|
||||
module UnsafeUseOfThis = MustFlow::Global<UnsafeUseOfThisConfig>;
|
||||
|
||||
/** Holds if `sink` is a `this` pointer used by the call instruction `call`. */
|
||||
predicate isSink(Operand sink, CallInstruction call) {
|
||||
exists(PureVirtualFunction func |
|
||||
@@ -66,19 +66,17 @@ predicate isSource(InitializeParameterInstruction source, string msg, Class c) {
|
||||
* - `msg` is a string describing whether `source` is from a constructor or destructor.
|
||||
*/
|
||||
predicate flows(
|
||||
MustFlowPathNode source, string msg, Class sourceClass, MustFlowPathNode sink,
|
||||
UnsafeUseOfThis::PathNode source, string msg, Class sourceClass, UnsafeUseOfThis::PathNode sink,
|
||||
CallInstruction call
|
||||
) {
|
||||
exists(UnsafeUseOfThisConfig conf |
|
||||
conf.hasFlowPath(source, sink) and
|
||||
isSource(source.getInstruction(), msg, sourceClass) and
|
||||
isSink(sink.getInstruction().getAUse(), call)
|
||||
)
|
||||
UnsafeUseOfThis::flowPath(source, sink) and
|
||||
isSource(source.getInstruction(), msg, sourceClass) and
|
||||
isSink(sink.getInstruction().getAUse(), call)
|
||||
}
|
||||
|
||||
from
|
||||
MustFlowPathNode source, MustFlowPathNode sink, CallInstruction call, string msg,
|
||||
Class sourceClass
|
||||
UnsafeUseOfThis::PathNode source, UnsafeUseOfThis::PathNode sink, CallInstruction call,
|
||||
string msg, Class sourceClass
|
||||
where
|
||||
flows(source, msg, sourceClass, sink, call) and
|
||||
// Only raise an alert if there is no override of the pure virtual function in any base class.
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* allows for a cross-site scripting vulnerability.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @security-severity 6.1
|
||||
* @security-severity 7.8
|
||||
* @precision high
|
||||
* @id cpp/cgi-xss
|
||||
* @tags security
|
||||
|
||||
@@ -23,13 +23,31 @@ import Flow::PathGraph
|
||||
|
||||
predicate isSource(FlowSource source, string sourceType) { sourceType = source.getSourceType() }
|
||||
|
||||
/**
|
||||
* Holds if `f` is a printf-like function or a (possibly nested) wrapper
|
||||
* that forwards a format-string parameter to one.
|
||||
*
|
||||
* Functions that *implement* printf-like behavior (e.g. a custom
|
||||
* `vsnprintf` variant) internally parse the caller-supplied format string
|
||||
* and build small, bounded, local format strings such as `"%d"` or `"%ld"`
|
||||
* for inner `sprintf` calls. Taint that reaches those inner calls via the
|
||||
* parsed format specifier is not exploitable, so sinks inside such
|
||||
* functions should be excluded.
|
||||
*/
|
||||
private predicate isPrintfImplementation(Function f) {
|
||||
f instanceof PrintfLikeFunction
|
||||
or
|
||||
exists(PrintfLikeFunction printf | printf.wrapperFunction(f, _, _))
|
||||
}
|
||||
|
||||
module Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node node) { isSource(node, _) }
|
||||
|
||||
predicate isSink(DataFlow::Node node) {
|
||||
exists(PrintfLikeFunction printf |
|
||||
printf.outermostWrapperFunctionCall([node.asExpr(), node.asIndirectExpr()], _)
|
||||
)
|
||||
) and
|
||||
not isPrintfImplementation([node.asExpr(), node.asIndirectExpr()].getEnclosingFunction())
|
||||
}
|
||||
|
||||
private predicate isArithmeticNonCharType(ArithmeticType type) {
|
||||
|
||||
@@ -18,7 +18,8 @@ import IncorrectPointerScalingCommon
|
||||
private predicate isCharSzPtrExpr(Expr e) {
|
||||
exists(PointerType pt | pt = e.getFullyConverted().getUnspecifiedType() |
|
||||
pt.getBaseType() instanceof CharType or
|
||||
pt.getBaseType() instanceof VoidType
|
||||
pt.getBaseType() instanceof VoidType or
|
||||
pt.getBaseType() instanceof ErroneousType // this could be char / void type in a successful compilation
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
25
cpp/ql/src/Telemetry/DatabaseQuality.qll
Normal file
25
cpp/ql/src/Telemetry/DatabaseQuality.qll
Normal file
@@ -0,0 +1,25 @@
|
||||
import cpp
|
||||
import codeql.util.ReportStats
|
||||
|
||||
module CallTargetStats implements StatsSig {
|
||||
private class RelevantCall extends Call {
|
||||
RelevantCall() { this.getFile() = any(File f | f.fromSource() and exists(f.getRelativePath())) }
|
||||
}
|
||||
|
||||
// We assume that calls with an implicit target are calls that could not be
|
||||
// resolved. This is accurate in the vast majority of cases, but is inaccurate
|
||||
// for calls that deliberately rely on implicitly declared functions.
|
||||
private predicate hasImplicitTarget(RelevantCall call) {
|
||||
call.getTarget().getADeclarationEntry().isImplicit()
|
||||
}
|
||||
|
||||
int getNumberOfOk() { result = count(RelevantCall call | not hasImplicitTarget(call)) }
|
||||
|
||||
int getNumberOfNotOk() { result = count(RelevantCall call | hasImplicitTarget(call)) }
|
||||
|
||||
string getOkText() { result = "calls with call target" }
|
||||
|
||||
string getNotOkText() { result = "calls with missing call target" }
|
||||
}
|
||||
|
||||
module CallTargetStatsReport = ReportStats<CallTargetStats>;
|
||||
25
cpp/ql/src/Telemetry/ExtractorInformation.ql
Normal file
25
cpp/ql/src/Telemetry/ExtractorInformation.ql
Normal file
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* @name C/C++ extraction information
|
||||
* @description Information about the extraction for a C/C++ database
|
||||
* @kind metric
|
||||
* @tags summary telemetry
|
||||
* @id cpp/telemetry/extraction-information
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import DatabaseQuality
|
||||
|
||||
from string key, float value
|
||||
where
|
||||
(
|
||||
CallTargetStatsReport::numberOfOk(key, value) or
|
||||
CallTargetStatsReport::numberOfNotOk(key, value) or
|
||||
CallTargetStatsReport::percentageOfOk(key, value)
|
||||
) and
|
||||
/* Infinity */
|
||||
value != 1.0 / 0.0 and
|
||||
/* -Infinity */
|
||||
value != -1.0 / 0.0 and
|
||||
/* NaN */
|
||||
value != 0.0 / 0.0
|
||||
select key, value
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Fixed an issue with the "Multiplication result converted to larger type" (`cpp/integer-multiplication-cast-to-long`) query causing false positive results in `build-mode: none` databases.
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: queryMetadata
|
||||
---
|
||||
* The `@security-severity` metadata of `cpp/cgi-xss` has been increased from 6.1 (medium) to 7.8 (high).
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Fixed an issue with the "Wrong type of arguments to formatting function" (`cpp/wrong-type-format-argument`) query causing false positive results in `build-mode: none` databases.
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Fixed an issue with the "Suspicious add with sizeof" (`cpp/suspicious-add-sizeof`) query causing false positive results in `build-mode: none` databases.
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Fixed an issue with the "Uncontrolled format string" (`cpp/tainted-format-string`) query involving certain kinds of formatting function implementations.
|
||||
3
cpp/ql/src/change-notes/released/1.5.12.md
Normal file
3
cpp/ql/src/change-notes/released/1.5.12.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 1.5.12
|
||||
|
||||
No user-facing changes.
|
||||
3
cpp/ql/src/change-notes/released/1.5.13.md
Normal file
3
cpp/ql/src/change-notes/released/1.5.13.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 1.5.13
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 1.5.11
|
||||
lastReleaseVersion: 1.5.13
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/cpp-queries
|
||||
version: 1.5.11
|
||||
version: 1.5.14-dev
|
||||
groups:
|
||||
- cpp
|
||||
- queries
|
||||
|
||||
@@ -8,6 +8,7 @@ private import semmle.code.cpp.dataflow.ExternalFlow as ExternalFlow
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowImplCommon as DataFlowImplCommon
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowImplSpecific
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate as DataFlowPrivate
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowNodes as DataFlowNodes
|
||||
private import semmle.code.cpp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
private import semmle.code.cpp.ir.dataflow.internal.TaintTrackingImplSpecific
|
||||
private import semmle.code.cpp.dataflow.new.TaintTracking as Tt
|
||||
@@ -403,7 +404,7 @@ private module SinkModelGeneratorInput implements SinkModelGeneratorInputSig {
|
||||
}
|
||||
|
||||
predicate apiSource(DataFlow::Node source) {
|
||||
DataFlowPrivate::nodeHasOperand(source, any(DataFlow::FieldAddress fa), 1)
|
||||
DataFlowPrivate::nodeHasOperand(source, any(DataFlowNodes::FieldAddress fa), 1)
|
||||
or
|
||||
source instanceof DataFlow::ParameterNode
|
||||
}
|
||||
@@ -416,7 +417,7 @@ private module SinkModelGeneratorInput implements SinkModelGeneratorInputSig {
|
||||
result = "Argument[" + DataFlow::repeatStars(indirectionIndex) + argumentIndex + "]"
|
||||
)
|
||||
or
|
||||
DataFlowPrivate::nodeHasOperand(source, any(DataFlow::FieldAddress fa), 1) and
|
||||
DataFlowPrivate::nodeHasOperand(source, any(DataFlowNodes::FieldAddress fa), 1) and
|
||||
result = qualifierString()
|
||||
}
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ public:
|
||||
{
|
||||
C *c = new C();
|
||||
B *b = B::make(c);
|
||||
sink(b->c); // $ast,ir
|
||||
sink(b->c); // $ ast,ir
|
||||
}
|
||||
|
||||
void f2()
|
||||
|
||||
@@ -26,9 +26,9 @@ public:
|
||||
|
||||
void func()
|
||||
{
|
||||
sink(s1); // $ast,ir
|
||||
sink(s1); // $ ast,ir
|
||||
sink(s2); // $ MISSING: ast,ir
|
||||
sink(s3); // $ast,ir
|
||||
sink(s3); // $ ast,ir
|
||||
sink(s4); // $ MISSING: ast,ir
|
||||
}
|
||||
};
|
||||
|
||||
@@ -19,7 +19,7 @@ public:
|
||||
};
|
||||
|
||||
static void sinkWrap(Box2* b2) {
|
||||
sink(b2->getBox1()->getElem()); // $ast,ir=28:15 ast,ir=35:15 ast,ir=42:15 ast,ir=49:15
|
||||
sink(b2->getBox1()->getElem()); // $ ast,ir=28:15 ast,ir=35:15 ast,ir=42:15 ast,ir=49:15
|
||||
}
|
||||
|
||||
Box2* boxfield;
|
||||
|
||||
@@ -48,25 +48,25 @@ struct S {
|
||||
void test_setDirectly() {
|
||||
S s;
|
||||
s.setDirectly(user_input());
|
||||
sink(s.getDirectly()); // $ast ir
|
||||
sink(s.getDirectly()); // $ ast ir
|
||||
}
|
||||
|
||||
void test_setIndirectly() {
|
||||
S s;
|
||||
s.setIndirectly(user_input());
|
||||
sink(s.getIndirectly()); // $ast ir
|
||||
sink(s.getIndirectly()); // $ ast ir
|
||||
}
|
||||
|
||||
void test_setThroughNonMember() {
|
||||
S s;
|
||||
s.setThroughNonMember(user_input());
|
||||
sink(s.getThroughNonMember()); // $ast ir
|
||||
sink(s.getThroughNonMember()); // $ ast ir
|
||||
}
|
||||
|
||||
void test_nonMemberSetA() {
|
||||
S s;
|
||||
nonMemberSetA(&s, user_input());
|
||||
sink(nonMemberGetA(&s)); // $ast,ir
|
||||
sink(nonMemberGetA(&s)); // $ ast,ir
|
||||
}
|
||||
|
||||
////////////////////
|
||||
@@ -112,7 +112,7 @@ void test_outer_with_ptr(Outer *pouter) {
|
||||
sink(outer.a); // $ ast,ir
|
||||
|
||||
sink(pouter->inner_nested.a); // $ ast,ir
|
||||
sink(pouter->inner_ptr->a); // $ast,ir
|
||||
sink(pouter->inner_ptr->a); // $ ast,ir
|
||||
sink(pouter->a); // $ ast,ir
|
||||
}
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ void single_field_test()
|
||||
A a;
|
||||
a.i = user_input();
|
||||
A a2 = a;
|
||||
sink(a2.i); //$ ast,ir
|
||||
sink(a2.i); // $ ast,ir
|
||||
}
|
||||
|
||||
struct C {
|
||||
@@ -81,7 +81,7 @@ struct C2
|
||||
|
||||
void m() {
|
||||
f2.f1 = user_input();
|
||||
sink(getf2f1()); //$ ast,ir
|
||||
sink(getf2f1()); // $ ast,ir
|
||||
}
|
||||
};
|
||||
|
||||
@@ -91,7 +91,7 @@ void single_field_test_typedef(A_typedef a)
|
||||
{
|
||||
a.i = user_input();
|
||||
A_typedef a2 = a;
|
||||
sink(a2.i); //$ ast,ir
|
||||
sink(a2.i); // $ ast,ir
|
||||
}
|
||||
|
||||
namespace TestAdditionalCallTargets {
|
||||
@@ -168,4 +168,4 @@ void test_union_with_two_instantiations_of_different_sizes() {
|
||||
sink(u_int.y); // $ MISSING: ir
|
||||
}
|
||||
|
||||
} // namespace Simple
|
||||
} // namespace Simple
|
||||
|
||||
@@ -12,14 +12,14 @@ struct Outer {
|
||||
};
|
||||
|
||||
void absink(struct AB *ab) {
|
||||
sink(ab->a); //$ ast,ir=20:20 ast,ir=27:7 ast,ir=40:20
|
||||
sink(ab->a); // $ ast,ir=20:20 ast,ir=27:7 ast,ir=40:20
|
||||
sink(ab->b); // no flow
|
||||
}
|
||||
|
||||
int struct_init(void) {
|
||||
struct AB ab = { user_input(), 0 };
|
||||
|
||||
sink(ab.a); //$ ast,ir
|
||||
sink(ab.a); // $ ast,ir
|
||||
sink(ab.b); // no flow
|
||||
absink(&ab);
|
||||
|
||||
@@ -28,9 +28,9 @@ int struct_init(void) {
|
||||
&ab,
|
||||
};
|
||||
|
||||
sink(outer.nestedAB.a); //$ ast,ir
|
||||
sink(outer.nestedAB.a); // $ ast,ir
|
||||
sink(outer.nestedAB.b); // no flow
|
||||
sink(outer.pointerAB->a); //$ ast,ir
|
||||
sink(outer.pointerAB->a); // $ ast,ir
|
||||
sink(outer.pointerAB->b); // no flow
|
||||
|
||||
absink(&outer.nestedAB);
|
||||
|
||||
@@ -15,7 +15,10 @@ predicate instructionGuardChecks(IRGuardCondition gc, Instruction checked, boole
|
||||
module BarrierGuard = DataFlow::InstructionBarrierGuard<instructionGuardChecks/3>;
|
||||
|
||||
predicate indirectBarrierGuard(DataFlow::Node node, string s) {
|
||||
node = BarrierGuard::getAnIndirectBarrierNode(_) and
|
||||
// This any(...) could technically be removed, but it helps us verify that we don't
|
||||
// accidentially change the API of this predicate (for instance, by having
|
||||
// the column be a unit parameter).
|
||||
node = BarrierGuard::getAnIndirectBarrierNode(any(int indirectionIndex)) and
|
||||
if node.isGLValue()
|
||||
then s = "glval<" + node.getType().toString().replaceAll(" ", "") + ">"
|
||||
else s = node.getType().toString().replaceAll(" ", "")
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import testModels
|
||||
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.DataFlowNodes
|
||||
|
||||
string describe(DataFlow::Node n) {
|
||||
n instanceof ParameterNode and result = "ParameterNode"
|
||||
|
||||
@@ -75,7 +75,7 @@ void test_sources() {
|
||||
int e = localMadSource();
|
||||
sink(e); // $ ir
|
||||
|
||||
sink(MyNamespace::namespaceLocalMadSource()); // $: ir
|
||||
sink(MyNamespace::namespaceLocalMadSource()); // $ ir
|
||||
sink(MyNamespace::namespaceLocalMadSourceVar); // $ ir
|
||||
sink(MyNamespace::MyNamespace2::namespace2LocalMadSource()); // $ ir
|
||||
sink(MyNamespace::localMadSource()); // $ (the MyNamespace version of this function is not a source)
|
||||
@@ -475,4 +475,4 @@ void test_receive_array() {
|
||||
int array[10] = {x};
|
||||
int y = receive_array(array);
|
||||
sink(y); // $ ir
|
||||
}
|
||||
}
|
||||
|
||||
@@ -450,7 +450,7 @@ void test_qualifiers()
|
||||
b.member = source();
|
||||
sink(b); // $ ir MISSING: ast
|
||||
sink(b.member); // $ ast,ir
|
||||
sink(b.getMember()); // $ MISSING: ir ast
|
||||
sink(b.getMember()); // $ MISSING: ir ast
|
||||
|
||||
c = new MyClass2(0);
|
||||
|
||||
@@ -865,4 +865,4 @@ void test_iconv(size_t size) {
|
||||
size_t size_out;
|
||||
iconv(0, &s, &size, &p, &size_out);
|
||||
sink(*p); // $ ast,ir
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,64 +24,64 @@ struct DerivedVI : virtual Base1 {
|
||||
};
|
||||
|
||||
void Locals() {
|
||||
Point pt = { //$ussa=pt
|
||||
1, //$ussa=pt[0..4)<int>
|
||||
2 //$ussa=pt[4..8)<int>
|
||||
Point pt = { // $ ussa=pt
|
||||
1, // $ ussa=pt[0..4)<int>
|
||||
2 // $ ussa=pt[4..8)<int>
|
||||
};
|
||||
int i = pt.x; //$ussa=pt[0..4)<int>
|
||||
i = pt.y; //$ussa=pt[4..8)<int>
|
||||
int i = pt.x; // $ ussa=pt[0..4)<int>
|
||||
i = pt.y; // $ ussa=pt[4..8)<int>
|
||||
int* p = &pt.x;
|
||||
i = *p; //$ussa=pt[0..4)<int>
|
||||
i = *p; // $ ussa=pt[0..4)<int>
|
||||
p = &pt.y;
|
||||
i = *p; //$ussa=pt[4..8)<int>
|
||||
i = *p; // $ ussa=pt[4..8)<int>
|
||||
}
|
||||
|
||||
void PointsTo(
|
||||
int a, //$raw=a
|
||||
Point& b, //$raw=b ussa=*b
|
||||
Point* c, //$raw=c ussa=*c
|
||||
int* d, //$raw=d ussa=*d
|
||||
DerivedSI* e, //$raw=e ussa=*e
|
||||
DerivedMI* f, //$raw=f ussa=*f
|
||||
DerivedVI* g //$raw=g ussa=*g
|
||||
int a, // $ raw=a
|
||||
Point& b, // $ raw=b ussa=*b
|
||||
Point* c, // $ raw=c ussa=*c
|
||||
int* d, // $ raw=d ussa=*d
|
||||
DerivedSI* e, // $ raw=e ussa=*e
|
||||
DerivedMI* f, // $ raw=f ussa=*f
|
||||
DerivedVI* g // $ raw=g ussa=*g
|
||||
) {
|
||||
|
||||
int i = a; //$raw=a
|
||||
i = *&a; //$raw=a
|
||||
i = *(&a + 0); //$raw=a
|
||||
i = b.x; //$raw=b ussa=*b[0..4)<int>
|
||||
i = b.y; //$raw=b ussa=*b[4..8)<int>
|
||||
i = c->x; //$raw=c ussa=*c[0..4)<int>
|
||||
i = c->y; //$raw=c ussa=*c[4..8)<int>
|
||||
i = *d; //$raw=d ussa=*d[0..4)<int>
|
||||
i = *(d + 0); //$raw=d ussa=*d[0..4)<int>
|
||||
i = d[5]; //$raw=d ussa=*d[20..24)<int>
|
||||
i = 5[d]; //$raw=d ussa=*d[20..24)<int>
|
||||
i = d[a]; //$raw=d raw=a ussa=*d[?..?)<int>
|
||||
i = a[d]; //$raw=d raw=a ussa=*d[?..?)<int>
|
||||
int i = a; // $ raw=a
|
||||
i = *&a; // $ raw=a
|
||||
i = *(&a + 0); // $ raw=a
|
||||
i = b.x; // $ raw=b ussa=*b[0..4)<int>
|
||||
i = b.y; // $ raw=b ussa=*b[4..8)<int>
|
||||
i = c->x; // $ raw=c ussa=*c[0..4)<int>
|
||||
i = c->y; // $ raw=c ussa=*c[4..8)<int>
|
||||
i = *d; // $ raw=d ussa=*d[0..4)<int>
|
||||
i = *(d + 0); // $ raw=d ussa=*d[0..4)<int>
|
||||
i = d[5]; // $ raw=d ussa=*d[20..24)<int>
|
||||
i = 5[d]; // $ raw=d ussa=*d[20..24)<int>
|
||||
i = d[a]; // $ raw=d raw=a ussa=*d[?..?)<int>
|
||||
i = a[d]; // $ raw=d raw=a ussa=*d[?..?)<int>
|
||||
|
||||
int* p = &b.x; //$raw=b
|
||||
i = *p; //$ussa=*b[0..4)<int>
|
||||
p = &b.y; //$raw=b
|
||||
i = *p; //$ussa=*b[4..8)<int>
|
||||
p = &c->x; //$raw=c
|
||||
i = *p; //$ussa=*c[0..4)<int>
|
||||
p = &c->y; //$raw=c
|
||||
i = *p; //$ussa=*c[4..8)<int>
|
||||
p = &d[5]; //$raw=d
|
||||
i = *p; //$ussa=*d[20..24)<int>
|
||||
p = &d[a]; //$raw=d raw=a
|
||||
i = *p; //$ussa=*d[?..?)<int>
|
||||
int* p = &b.x; // $ raw=b
|
||||
i = *p; // $ ussa=*b[0..4)<int>
|
||||
p = &b.y; // $ raw=b
|
||||
i = *p; // $ ussa=*b[4..8)<int>
|
||||
p = &c->x; // $ raw=c
|
||||
i = *p; // $ ussa=*c[0..4)<int>
|
||||
p = &c->y; // $ raw=c
|
||||
i = *p; // $ ussa=*c[4..8)<int>
|
||||
p = &d[5]; // $ raw=d
|
||||
i = *p; // $ ussa=*d[20..24)<int>
|
||||
p = &d[a]; // $ raw=d raw=a
|
||||
i = *p; // $ ussa=*d[?..?)<int>
|
||||
|
||||
Point* q = &c[a]; //$raw=c raw=a
|
||||
i = q->x; //$ussa=*c[?..?)<int>
|
||||
i = q->y; //$ussa=*c[?..?)<int>
|
||||
Point* q = &c[a]; // $ raw=c raw=a
|
||||
i = q->x; // $ ussa=*c[?..?)<int>
|
||||
i = q->y; // $ ussa=*c[?..?)<int>
|
||||
|
||||
i = e->b1; //$raw=e ussa=*e[0..4)<int>
|
||||
i = e->dsi; //$raw=e ussa=*e[4..8)<int>
|
||||
i = f->b1; //$raw=f ussa=*f[0..4)<int>
|
||||
i = f->b2; //$raw=f ussa=*f[4..8)<int>
|
||||
i = f->dmi; //$raw=f ussa=*f[8..12)<int>
|
||||
i = g->b1; //$raw=g ussa=*g[?..?)<int>
|
||||
i = g->dvi; //$raw=g ussa=*g[8..12)<int>
|
||||
}
|
||||
i = e->b1; // $ raw=e ussa=*e[0..4)<int>
|
||||
i = e->dsi; // $ raw=e ussa=*e[4..8)<int>
|
||||
i = f->b1; // $ raw=f ussa=*f[0..4)<int>
|
||||
i = f->b2; // $ raw=f ussa=*f[4..8)<int>
|
||||
i = f->dmi; // $ raw=f ussa=*f[8..12)<int>
|
||||
i = g->b1; // $ raw=g ussa=*g[?..?)<int>
|
||||
i = g->dvi; // $ raw=g ussa=*g[8..12)<int>
|
||||
}
|
||||
|
||||
@@ -10,24 +10,24 @@ struct S {
|
||||
|
||||
void unique_ptr_init(S s) {
|
||||
unique_ptr<S> p(new S); // MISSING: $ussa=dynamic{1}
|
||||
int i = (*p).x; //$ MISSING: ussa=dynamic{1}[0..4)<int>
|
||||
*p = s; //$ MISSING: ussa=dynamic{1}[0..4)<S>
|
||||
int i = (*p).x; // $ MISSING: ussa=dynamic{1}[0..4)<int>
|
||||
*p = s; // $ MISSING: ussa=dynamic{1}[0..4)<S>
|
||||
unique_ptr<S> q = std::move(p);
|
||||
*(q.get()) = s; //$ MISSING: ussa=dynamic{1}[0..4)<S>
|
||||
*(q.get()) = s; // $ MISSING: ussa=dynamic{1}[0..4)<S>
|
||||
shared_ptr<S> t(std::move(q));
|
||||
t->x = 5; //$ MISSING: ussa=dynamic{1}[0..4)<int>
|
||||
*t = s; //$ MISSING: ussa=dynamic{1}[0..4)<S>
|
||||
*(t.get()) = s; //$ MISSING: ussa=dynamic{1}[0..4)<S>
|
||||
t->x = 5; // $ MISSING: ussa=dynamic{1}[0..4)<int>
|
||||
*t = s; // $ MISSING: ussa=dynamic{1}[0..4)<S>
|
||||
*(t.get()) = s; // $ MISSING: ussa=dynamic{1}[0..4)<S>
|
||||
}
|
||||
|
||||
void shared_ptr_init(S s) {
|
||||
shared_ptr<S> p(new S); //$ MISSING: ussa=dynamic{1}
|
||||
int i = (*p).x; //$ MISSING: ussa=dynamic{1}[0..4)<int>
|
||||
*p = s; //$ MISSING: ussa=dynamic{1}[0..4)<S>
|
||||
shared_ptr<S> p(new S); // $ MISSING: ussa=dynamic{1}
|
||||
int i = (*p).x; // $ MISSING: ussa=dynamic{1}[0..4)<int>
|
||||
*p = s; // $ MISSING: ussa=dynamic{1}[0..4)<S>
|
||||
shared_ptr<S> q = std::move(p);
|
||||
*(q.get()) = s; //$ MISSING: ussa=dynamic{1}[0..4)<S>
|
||||
*(q.get()) = s; // $ MISSING: ussa=dynamic{1}[0..4)<S>
|
||||
shared_ptr<S> t(q);
|
||||
t->x = 5; //$ MISSING: ussa=dynamic{1}[0..4)<int>
|
||||
*t = s; //$ MISSING: ussa=dynamic{1}[0..4)<S>
|
||||
*(t.get()) = s; //$ MISSING: ussa=dynamic{1}[0..4)<S>
|
||||
t->x = 5; // $ MISSING: ussa=dynamic{1}[0..4)<int>
|
||||
*t = s; // $ MISSING: ussa=dynamic{1}[0..4)<S>
|
||||
*(t.get()) = s; // $ MISSING: ussa=dynamic{1}[0..4)<S>
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ int test4() {
|
||||
}
|
||||
range(total); // $ MISSING: range=>=0
|
||||
range(i); // $ range===2
|
||||
range(total + i); // $ range="<=Phi: i+2" MISSING: range===i+2 range=>=2 range=>=i+0
|
||||
range(total + i); // $ range="<=Phi: i+2" MISSING: range===i+2 range=>=2 range=>=i+0
|
||||
return total + i;
|
||||
}
|
||||
|
||||
@@ -210,7 +210,7 @@ int test14(int x) {
|
||||
int x3 = (int)(unsigned int)x;
|
||||
range(x3);
|
||||
char c0 = x;
|
||||
range(c0);
|
||||
range(c0);
|
||||
unsigned short s0 = x;
|
||||
range(s0);
|
||||
range(x0 + x1 + x2 + x3 + c0 + s0); // $ overflow=+ overflow=+-
|
||||
@@ -218,7 +218,7 @@ int test14(int x) {
|
||||
}
|
||||
|
||||
long long test15(long long x) {
|
||||
return (x > 0 && (range(x), x == (int)x)) ? // $ range=>=1
|
||||
return (x > 0 && (range(x), x == (int)x)) ? // $ range=>=1
|
||||
(range(x), x) : // $ range=>=1
|
||||
(range(x), -1);
|
||||
}
|
||||
@@ -228,7 +228,7 @@ int test_unary(int a) {
|
||||
int total = 0;
|
||||
|
||||
if (3 <= a && a <= 11) {
|
||||
range(a); // $ range=<=11 range=>=3
|
||||
range(a); // $ range=<=11 range=>=3
|
||||
int b = +a;
|
||||
range(b); // $ range=<=11 range=>=3
|
||||
int c = -a;
|
||||
@@ -384,7 +384,7 @@ int test_mult02(int a, int b) {
|
||||
total += r;
|
||||
range(total); // $ range=">=Phi: 0-143" range=">=Phi: 0-286"
|
||||
}
|
||||
range(total); // $range=">=Phi: 0-143" range=">=Phi: 0-286"
|
||||
range(total); // $ range=">=Phi: 0-143" range=">=Phi: 0-286"
|
||||
return total;
|
||||
}
|
||||
|
||||
@@ -467,7 +467,7 @@ int test_mult04(int a, int b) {
|
||||
range(a); // $ range=<=0 range=>=-17
|
||||
range(b); // $ range=<=0 range=>=-13
|
||||
int r = a*b; // 0 .. 221
|
||||
range(r); // $ range=<=221 range=>=0
|
||||
range(r); // $ range=<=221 range=>=0
|
||||
total += r;
|
||||
range(total); // $ range="<=Phi: - ...+221"
|
||||
}
|
||||
@@ -1030,7 +1030,7 @@ void test_negate_signed(int s) {
|
||||
}
|
||||
}
|
||||
|
||||
// By setting the guard after the use in another guard we
|
||||
// By setting the guard after the use in another guard we
|
||||
// don't get the useful information
|
||||
void test_guard_after_use(int pos, int size, int offset) {
|
||||
if (pos + offset >= size) { // $ overflow=+-
|
||||
@@ -1040,12 +1040,12 @@ void test_guard_after_use(int pos, int size, int offset) {
|
||||
return;
|
||||
}
|
||||
range(pos + 1); // $ overflow=+ range="==InitializeParameter: pos+1" MISSING: range="<=InitializeParameter: size-1"
|
||||
}
|
||||
}
|
||||
|
||||
int cond();
|
||||
|
||||
|
||||
// This is basically what we get when we have a loop that calls
|
||||
// This is basically what we get when we have a loop that calls
|
||||
// realloc in some iterations
|
||||
void alloc_in_loop(int origLen) {
|
||||
if (origLen <= 10) {
|
||||
@@ -1066,12 +1066,12 @@ void alloc_in_loop(int origLen) {
|
||||
}
|
||||
}
|
||||
|
||||
// This came from a case where it handled the leftovers before an unrolled loop
|
||||
// This came from a case where it handled the leftovers before an unrolled loop
|
||||
void mask_at_start(int len) {
|
||||
if (len < 0) {
|
||||
return;
|
||||
}
|
||||
int leftOver = len & 63;
|
||||
int leftOver = len & 63;
|
||||
for (int i = 0; i < leftOver; i++) {
|
||||
range(i); // $ range=<=62 range=>=0 range="<=Store: ... & ... | Store: leftOver-1" range="<=InitializeParameter: len-1"
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user