mirror of
https://github.com/github/codeql.git
synced 2026-07-06 12:05:31 +02:00
Compare commits
747 Commits
codeql-cli
...
calumgrant
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4788444cf4 | ||
|
|
991a337106 | ||
|
|
fb7bec75fd | ||
|
|
62a6a2479b | ||
|
|
c92be9fcd7 | ||
|
|
996811d918 | ||
|
|
179a7f3fdb | ||
|
|
0c164b0552 | ||
|
|
6b38ca3db9 | ||
|
|
4acc51bb7f | ||
|
|
4316ab6518 | ||
|
|
8d12d0d1ac | ||
|
|
2b052b18e2 | ||
|
|
213711029a | ||
|
|
7407b55f75 | ||
|
|
e950ddcb1c | ||
|
|
c4e2a52dc3 | ||
|
|
190c5ca9c0 | ||
|
|
3ae2652040 | ||
|
|
8b0293d189 | ||
|
|
3550e1f837 | ||
|
|
66e56e2e39 | ||
|
|
1bc6bc8049 | ||
|
|
ff379714b6 | ||
|
|
74b51313e0 | ||
|
|
8ae607cdce | ||
|
|
c743abad54 | ||
|
|
5a7174dcbb | ||
|
|
78d4745722 | ||
|
|
9aee2dc002 | ||
|
|
f5c654b669 | ||
|
|
6f67f9e887 | ||
|
|
f498e05099 | ||
|
|
613ccaac1d | ||
|
|
b2c64eabd4 | ||
|
|
4fbbda508b | ||
|
|
1129df9cb7 | ||
|
|
5928ede324 | ||
|
|
9cf0995720 | ||
|
|
6f5bdfba65 | ||
|
|
b5b5fef642 | ||
|
|
5c4eb3c943 | ||
|
|
1e54422662 | ||
|
|
c0d623c056 | ||
|
|
4905612905 | ||
|
|
d540675b9e | ||
|
|
1bc3f6b0e7 | ||
|
|
bf3dbc24de | ||
|
|
619913b553 | ||
|
|
7da7416bcd | ||
|
|
4b3e35ed52 | ||
|
|
f353065d26 | ||
|
|
0f864081cb | ||
|
|
90a152a2bc | ||
|
|
5cf7112d4c | ||
|
|
4567b17a58 | ||
|
|
7042f3222a | ||
|
|
c58971e632 | ||
|
|
11da42b049 | ||
|
|
f517c00658 | ||
|
|
0cc868c742 | ||
|
|
d202355b07 | ||
|
|
c80f48b23a | ||
|
|
0f2d0c098f | ||
|
|
621de2b977 | ||
|
|
ffe4c8c87b | ||
|
|
3f66b635a4 | ||
|
|
1978922d9e | ||
|
|
769d9317c7 | ||
|
|
1a0d66b339 | ||
|
|
987d5712b8 | ||
|
|
15a7c3faeb | ||
|
|
a650499a9c | ||
|
|
f931c8d836 | ||
|
|
8db9187698 | ||
|
|
e7f24318cd | ||
|
|
f977a4206a | ||
|
|
9907f0f827 | ||
|
|
39019b3b62 | ||
|
|
bbebdfea8d | ||
|
|
de5c69d0a1 | ||
|
|
09357e15b4 | ||
|
|
a992b67d97 | ||
|
|
c9f468578d | ||
|
|
00a940fd58 | ||
|
|
4b02f88089 | ||
|
|
af81698fa8 | ||
|
|
91f860ba4f | ||
|
|
8119a27540 | ||
|
|
a006c29a00 | ||
|
|
70cf16597b | ||
|
|
a078dcf1f2 | ||
|
|
eda815789b | ||
|
|
5ec3335b07 | ||
|
|
a74cf6501a | ||
|
|
84e412fe36 | ||
|
|
ba97c3623a | ||
|
|
54f2316d00 | ||
|
|
7828cb8f5a | ||
|
|
d82acf5866 | ||
|
|
855fe12c6c | ||
|
|
012b861ffb | ||
|
|
c4ae18649e | ||
|
|
c2ec1bf561 | ||
|
|
73b8314308 | ||
|
|
22c1d52381 | ||
|
|
91aa182d53 | ||
|
|
92df0a3d46 | ||
|
|
a30bac14e9 | ||
|
|
ab4b823c2e | ||
|
|
22aea47604 | ||
|
|
a87ceed361 | ||
|
|
bc1283c715 | ||
|
|
61c72361cd | ||
|
|
241f977488 | ||
|
|
8a22e2283c | ||
|
|
bebcd679a4 | ||
|
|
18f138e754 | ||
|
|
2451a6d3f6 | ||
|
|
454687d583 | ||
|
|
182325dc5e | ||
|
|
12494a0c5a | ||
|
|
358c7410c8 | ||
|
|
158dafa7d0 | ||
|
|
80364e9570 | ||
|
|
bddc69e409 | ||
|
|
bf2ae9890f | ||
|
|
c8fec336ce | ||
|
|
1a60c01723 | ||
|
|
01a6c5e82f | ||
|
|
60ee7fb0d3 | ||
|
|
1a20a624d6 | ||
|
|
c9b4e83e73 | ||
|
|
9d21e2cda3 | ||
|
|
cecaa0de3a | ||
|
|
6da0592ab6 | ||
|
|
d9019f9676 | ||
|
|
c166cb406a | ||
|
|
5893e38567 | ||
|
|
0a550bb919 | ||
|
|
8ace9da14a | ||
|
|
398b90a15d | ||
|
|
13a7d9acb6 | ||
|
|
c483a4bf04 | ||
|
|
0ba3cd96f5 | ||
|
|
2f7766a557 | ||
|
|
df24e5982a | ||
|
|
e8b9d7e6fa | ||
|
|
15667dcf1e | ||
|
|
1d8d45b3aa | ||
|
|
bfc95c6f13 | ||
|
|
d01d657f89 | ||
|
|
73d306c8c8 | ||
|
|
170e2231d4 | ||
|
|
0ecefd6a24 | ||
|
|
9534e56d1b | ||
|
|
b639f60fa6 | ||
|
|
1a4c07a1ac | ||
|
|
f1047606ad | ||
|
|
a7a12f17b5 | ||
|
|
daf19a2468 | ||
|
|
ad1188be1f | ||
|
|
a6ce24dc39 | ||
|
|
cc4f8e038d | ||
|
|
03cf9b702c | ||
|
|
a568873a8e | ||
|
|
92a5b6f6d6 | ||
|
|
e66cce7fe1 | ||
|
|
e198d0105f | ||
|
|
adeff8c602 | ||
|
|
e3b6ee1aaf | ||
|
|
a5fd433c20 | ||
|
|
26f9297d62 | ||
|
|
404b39b6e6 | ||
|
|
2cda1a207e | ||
|
|
78b8a9259a | ||
|
|
e483948c19 | ||
|
|
384649b336 | ||
|
|
d4d6b48f98 | ||
|
|
3d0c42f44d | ||
|
|
a88c007c05 | ||
|
|
a79105e2ae | ||
|
|
2848ccf0e2 | ||
|
|
08c0d8ec60 | ||
|
|
9aeebc6f39 | ||
|
|
a8201a19ae | ||
|
|
3a224871ee | ||
|
|
19e2af8a1d | ||
|
|
92699d18ac | ||
|
|
dcadda23cd | ||
|
|
f06c3fddd9 | ||
|
|
18fc92247b | ||
|
|
763d53afed | ||
|
|
78dc6502f5 | ||
|
|
b1329fd806 | ||
|
|
42a9962519 | ||
|
|
4378924785 | ||
|
|
20ea9255a1 | ||
|
|
ea3cc51286 | ||
|
|
47f679dc34 | ||
|
|
8dc9c95be4 | ||
|
|
2dd42f7aa5 | ||
|
|
2b7394cd42 | ||
|
|
499c4df79b | ||
|
|
a2994c073a | ||
|
|
8cc118f781 | ||
|
|
b008f98782 | ||
|
|
451f601a65 | ||
|
|
98d2c848bb | ||
|
|
74e446ea3b | ||
|
|
56dff8540f | ||
|
|
066f3b61a2 | ||
|
|
410543f26b | ||
|
|
e71cf0ff1d | ||
|
|
5dbb91f508 | ||
|
|
c4d33fbede | ||
|
|
e8d064e291 | ||
|
|
ea2b73bda2 | ||
|
|
62faab320b | ||
|
|
5076b1a214 | ||
|
|
b9a7f6a8f7 | ||
|
|
761f9cac97 | ||
|
|
e2a4c2aa1b | ||
|
|
6ffa821aa3 | ||
|
|
21ff705b73 | ||
|
|
1af3374322 | ||
|
|
1b22e0879a | ||
|
|
4617c055e2 | ||
|
|
a50584c665 | ||
|
|
44edff7661 | ||
|
|
9c823d708d | ||
|
|
c328957bf3 | ||
|
|
7e13c88f1d | ||
|
|
72493a6bd1 | ||
|
|
950e8c85e7 | ||
|
|
a158e40cc2 | ||
|
|
90bb0ba33f | ||
|
|
8f207e7a06 | ||
|
|
3c47c1137d | ||
|
|
d406646414 | ||
|
|
b32fb1d961 | ||
|
|
3f020a96e4 | ||
|
|
25b7214064 | ||
|
|
723850394d | ||
|
|
f1616d3c26 | ||
|
|
45167cf9f1 | ||
|
|
04c26056a1 | ||
|
|
4a85cc98e5 | ||
|
|
24da489bab | ||
|
|
c6ee25497e | ||
|
|
eb9c734120 | ||
|
|
332a3dbc61 | ||
|
|
253965096b | ||
|
|
964b3f2339 | ||
|
|
586e90043a | ||
|
|
ab7d30a464 | ||
|
|
38e1065b24 | ||
|
|
64b8f97d39 | ||
|
|
28c8611f93 | ||
|
|
b44ad2d39f | ||
|
|
8ea1d1376f | ||
|
|
7e10f212cd | ||
|
|
a1cb419902 | ||
|
|
7cf61ffb18 | ||
|
|
eb2e587637 | ||
|
|
c3caafcca1 | ||
|
|
b49a61705c | ||
|
|
32e8b5c667 | ||
|
|
145873f562 | ||
|
|
b7f0b7afb5 | ||
|
|
538d556465 | ||
|
|
04c0475251 | ||
|
|
100166fa53 | ||
|
|
d76182b945 | ||
|
|
829707528b | ||
|
|
cf134331a9 | ||
|
|
61cd93fcf3 | ||
|
|
e699b18928 | ||
|
|
1b1367eec0 | ||
|
|
58ef9f4e3e | ||
|
|
8814e6e2bd | ||
|
|
827d15aaa0 | ||
|
|
ab5e1f8a69 | ||
|
|
99335e5f29 | ||
|
|
49ffa8bf07 | ||
|
|
027e5e7291 | ||
|
|
904799b674 | ||
|
|
34c447939e | ||
|
|
869bf8afc6 | ||
|
|
79c6834aa3 | ||
|
|
f72afdc7cb | ||
|
|
e65a62c727 | ||
|
|
9105faa3fd | ||
|
|
faa2dcee24 | ||
|
|
a62ce4c51d | ||
|
|
5b572a2c2a | ||
|
|
ed0717af96 | ||
|
|
52717f8500 | ||
|
|
513a319149 | ||
|
|
b34fb7c26b | ||
|
|
1758a1e04b | ||
|
|
9456eaf5d3 | ||
|
|
372746ffd4 | ||
|
|
34f91f8129 | ||
|
|
65c654e946 | ||
|
|
10e1bfcee5 | ||
|
|
4d5f05a8dd | ||
|
|
1b9e8ae86f | ||
|
|
bbc359ebde | ||
|
|
76e740bc1d | ||
|
|
5eaaf02bf5 | ||
|
|
f125e732b0 | ||
|
|
c30ca4d9f6 | ||
|
|
0d814e0e20 | ||
|
|
f7e6bf7ef5 | ||
|
|
f8e17fe8cc | ||
|
|
76946184de | ||
|
|
e1c85e3581 | ||
|
|
8cc9838628 | ||
|
|
d5aa08dbf3 | ||
|
|
86e2b845f3 | ||
|
|
eab1569342 | ||
|
|
c816412795 | ||
|
|
84b7319688 | ||
|
|
f95ccd242f | ||
|
|
fcdac0aeb0 | ||
|
|
1322ce35d6 | ||
|
|
e857eedd4a | ||
|
|
462e564c19 | ||
|
|
e7092b403b | ||
|
|
d6acea1d0c | ||
|
|
52ceb7fb89 | ||
|
|
bcddef8f32 | ||
|
|
e721399eec | ||
|
|
674e65e134 | ||
|
|
4d54c09a6f | ||
|
|
b5d6bb9097 | ||
|
|
cfc419b639 | ||
|
|
304ad33837 | ||
|
|
231918f612 | ||
|
|
526204dc43 | ||
|
|
20a1e22c44 | ||
|
|
da93a08639 | ||
|
|
f1ab3f40f3 | ||
|
|
9aff22c664 | ||
|
|
4dfcdbccd7 | ||
|
|
aab5da0435 | ||
|
|
9e09c5a6cf | ||
|
|
279b2c7a95 | ||
|
|
811c175556 | ||
|
|
a51d24cbab | ||
|
|
d68d2cca41 | ||
|
|
0cbedfb28c | ||
|
|
49aba25852 | ||
|
|
53d4a10108 | ||
|
|
59fb9cc862 | ||
|
|
4f10cb5fa0 | ||
|
|
57ff30c5f3 | ||
|
|
1ccea884ff | ||
|
|
f6f594e4b5 | ||
|
|
fb74a2a170 | ||
|
|
4d42a88c3c | ||
|
|
9842445b52 | ||
|
|
694b3b0fce | ||
|
|
8cba06a25c | ||
|
|
088f8297b6 | ||
|
|
ab23d0ad23 | ||
|
|
1a53b923a6 | ||
|
|
944f8842b7 | ||
|
|
64145cf181 | ||
|
|
f00ad7c0e7 | ||
|
|
5e2e224c9c | ||
|
|
e8120afef9 | ||
|
|
8bb4193704 | ||
|
|
e359744b8b | ||
|
|
896fb87d1d | ||
|
|
724d026238 | ||
|
|
eaba798e34 | ||
|
|
52db1c1253 | ||
|
|
fa06d88642 | ||
|
|
24f8b5f203 | ||
|
|
7b89c6c7f7 | ||
|
|
9bc04e7591 | ||
|
|
2a40a2a57f | ||
|
|
c5403f4249 | ||
|
|
3fd6bc9b5c | ||
|
|
486226814a | ||
|
|
12c28f3f68 | ||
|
|
871fd9aba3 | ||
|
|
5b1b30a171 | ||
|
|
880d56c576 | ||
|
|
9c8945f626 | ||
|
|
6575927630 | ||
|
|
2dcb55cc42 | ||
|
|
23fbfcee27 | ||
|
|
5f4bc4197b | ||
|
|
65645821be | ||
|
|
0338ffd125 | ||
|
|
61580da14d | ||
|
|
3989717878 | ||
|
|
336c7de643 | ||
|
|
8b91914826 | ||
|
|
575b66a054 | ||
|
|
dd95a2abab | ||
|
|
aab43afd81 | ||
|
|
99940a6084 | ||
|
|
c11fac81fd | ||
|
|
85e71c30dc | ||
|
|
c529988b43 | ||
|
|
8e95395382 | ||
|
|
61fb89721a | ||
|
|
8198b1a6ef | ||
|
|
ab3d21ce7e | ||
|
|
9f27eb3eda | ||
|
|
efda63d37a | ||
|
|
4eea214cb4 | ||
|
|
b8f62ae4d5 | ||
|
|
0f7325ec68 | ||
|
|
5fe3ab7890 | ||
|
|
53c2d2f1e7 | ||
|
|
07d51a55fd | ||
|
|
41b95a1938 | ||
|
|
cfb0a862c1 | ||
|
|
9b23635d0a | ||
|
|
bbdc2752ac | ||
|
|
5c74bebe6c | ||
|
|
757cf8d43a | ||
|
|
b53fa0f7f3 | ||
|
|
d85f81d699 | ||
|
|
7c7bdb2242 | ||
|
|
92b3eda12d | ||
|
|
a8549d2e23 | ||
|
|
54c9aea251 | ||
|
|
e71d038d7f | ||
|
|
c123513311 | ||
|
|
bab2dd4427 | ||
|
|
73df4fa920 | ||
|
|
95ddd6ec74 | ||
|
|
51e7f3be1a | ||
|
|
5b184c179a | ||
|
|
cba4ba042c | ||
|
|
a33393d452 | ||
|
|
7cb8a6c52f | ||
|
|
6815bcaa80 | ||
|
|
105984f7de | ||
|
|
6233da3e40 | ||
|
|
e68165fcb7 | ||
|
|
cbc0261567 | ||
|
|
535e6db40f | ||
|
|
9b51e0e0ee | ||
|
|
30b5d5092d | ||
|
|
1dd3a0fd76 | ||
|
|
077a2e3479 | ||
|
|
ca9567debc | ||
|
|
5d5e31378b | ||
|
|
5cde3fa697 | ||
|
|
39a8b49222 | ||
|
|
b209fc67cb | ||
|
|
77128de105 | ||
|
|
7a1b85aa56 | ||
|
|
eb0621ab7a | ||
|
|
c0cf1c7c8c | ||
|
|
17990da205 | ||
|
|
d5475c4a89 | ||
|
|
471303bd7c | ||
|
|
2132c7bf96 | ||
|
|
7ca54a6f94 | ||
|
|
e8cb8b4f81 | ||
|
|
ba64cf3016 | ||
|
|
3c91333d0b | ||
|
|
d9e8e0e00a | ||
|
|
ff85db36e2 | ||
|
|
8f0b88497a | ||
|
|
31c427e64c | ||
|
|
e64a2d6c9c | ||
|
|
807e6795a7 | ||
|
|
880262d462 | ||
|
|
95ff5bae65 | ||
|
|
6cbe16e0c2 | ||
|
|
75615f2817 | ||
|
|
c07bf65eb6 | ||
|
|
8def1c2c13 | ||
|
|
1b90f22e84 | ||
|
|
ecdf62376d | ||
|
|
f5431abb10 | ||
|
|
73cc211779 | ||
|
|
08e08a2b3a | ||
|
|
657402b42f | ||
|
|
ecbf7aef18 | ||
|
|
669fc925e0 | ||
|
|
8a04840f93 | ||
|
|
f4e4e238ba | ||
|
|
8f682ef4e4 | ||
|
|
96d69ca49c | ||
|
|
8a261b7e7a | ||
|
|
daea674095 | ||
|
|
315f439135 | ||
|
|
06d8892e03 | ||
|
|
e22159ab5d | ||
|
|
355c7d9b41 | ||
|
|
00baccbc15 | ||
|
|
81dea9f89a | ||
|
|
76067cb12d | ||
|
|
e4cf7df38f | ||
|
|
b8b3689251 | ||
|
|
0693bf9e75 | ||
|
|
1aafc377ad | ||
|
|
abcd9165b4 | ||
|
|
0bc6934bfc | ||
|
|
4ae82ac215 | ||
|
|
ccad70897d | ||
|
|
318d954536 | ||
|
|
3239af9973 | ||
|
|
f7113e0105 | ||
|
|
ca2d94b297 | ||
|
|
2cff081f2b | ||
|
|
9055d9567a | ||
|
|
f99cb3f649 | ||
|
|
dfdd79d8cf | ||
|
|
8ec4f0b5bd | ||
|
|
b912918d8b | ||
|
|
ef88f3ed09 | ||
|
|
f5d4b2e6cd | ||
|
|
8b0f3af5b1 | ||
|
|
54793cfa01 | ||
|
|
d909f2bc4f | ||
|
|
7f27127301 | ||
|
|
520a2c96ff | ||
|
|
12b9b805e2 | ||
|
|
6eb07a7a7e | ||
|
|
9bfb189fa7 | ||
|
|
322fa36359 | ||
|
|
4a47e11a16 | ||
|
|
83249cd9c2 | ||
|
|
c6185b30ba | ||
|
|
9ce08c586c | ||
|
|
a8d3226e99 | ||
|
|
94212d103e | ||
|
|
608791fd7f | ||
|
|
7d92ec5ddf | ||
|
|
9157dee0db | ||
|
|
677520aa8e | ||
|
|
16dcc0969b | ||
|
|
09e59ccf44 | ||
|
|
a8f2cbc2b1 | ||
|
|
c00d0d302d | ||
|
|
5a7a1dc92e | ||
|
|
c480431ec0 | ||
|
|
f7fc2e0b00 | ||
|
|
397e641f2f | ||
|
|
00cbfaf40e | ||
|
|
42653b5fec | ||
|
|
58bbfe694f | ||
|
|
d9c7401ea2 | ||
|
|
ed7538d0b9 | ||
|
|
5b89bd23c7 | ||
|
|
b68abab12a | ||
|
|
d05c5e3d94 | ||
|
|
301a6cc191 | ||
|
|
acd0fa4b7b | ||
|
|
85a4dd0325 | ||
|
|
dc4604f5a5 | ||
|
|
40b6e1624f | ||
|
|
22e843abc6 | ||
|
|
3c70a2d7df | ||
|
|
541effb8cb | ||
|
|
248ffa15a2 | ||
|
|
072f19008a | ||
|
|
93988e5834 | ||
|
|
e0c2a43780 | ||
|
|
5843326b5c | ||
|
|
3a2b0a2feb | ||
|
|
fd55713006 | ||
|
|
c6372d5822 | ||
|
|
07dd6d5c8d | ||
|
|
708d12624f | ||
|
|
3eddd3114f | ||
|
|
61ce7252e6 | ||
|
|
b86aeb68ae | ||
|
|
cf025e1924 | ||
|
|
a200ced2d6 | ||
|
|
c04e59611b | ||
|
|
c5a87c95d8 | ||
|
|
4f22b91e73 | ||
|
|
797f675285 | ||
|
|
c32c810ae7 | ||
|
|
32fe084630 | ||
|
|
ba054bd428 | ||
|
|
0715c4ac5a | ||
|
|
99928b82ed | ||
|
|
377c6b4cc8 | ||
|
|
2939c89f7a | ||
|
|
bb6cc92728 | ||
|
|
d736426529 | ||
|
|
806d42852c | ||
|
|
2590d8a27f | ||
|
|
7df8b1ba51 | ||
|
|
676bcf39a5 | ||
|
|
94364f724e | ||
|
|
cb85a756a0 | ||
|
|
e7886d0e57 | ||
|
|
0fa5a1f274 | ||
|
|
15bb846a5f | ||
|
|
5c454bdd8c | ||
|
|
f194c70e8a | ||
|
|
179270ffc1 | ||
|
|
1f78882cdc | ||
|
|
f0f6c229f6 | ||
|
|
2f6dd2ab81 | ||
|
|
6ec223c515 | ||
|
|
401717d739 | ||
|
|
527409d05f | ||
|
|
d3d2e2188d | ||
|
|
141af7cc87 | ||
|
|
6e3dddede0 | ||
|
|
683fe26034 | ||
|
|
ea1b8a3999 | ||
|
|
5f0efc19fa | ||
|
|
0dfd336729 | ||
|
|
b0758fd109 | ||
|
|
50775d0c53 | ||
|
|
9874d40d29 | ||
|
|
044ee9b08a | ||
|
|
d66494dcb0 | ||
|
|
86d6b8ef21 | ||
|
|
0f387eeac2 | ||
|
|
3195f0c828 | ||
|
|
d98ccdfa06 | ||
|
|
c8b02241af | ||
|
|
146d84bbf8 | ||
|
|
862942f3d8 | ||
|
|
adc82b553b | ||
|
|
c2252e12ac | ||
|
|
bab6ecf3bb | ||
|
|
f95b33049e | ||
|
|
bfa189e2ac | ||
|
|
19b2e56d02 | ||
|
|
b754706e44 | ||
|
|
9db32f4d26 | ||
|
|
acb2bbb2a3 | ||
|
|
06f987ad58 | ||
|
|
925a2cca7e | ||
|
|
3ad9c026a5 | ||
|
|
7d9a68bf17 | ||
|
|
d7c784ef2f | ||
|
|
2a0459838b | ||
|
|
86d1e5b646 | ||
|
|
be9009d653 | ||
|
|
037211c4a4 | ||
|
|
8cba276b87 | ||
|
|
4ca8faa9c9 | ||
|
|
393f6b7666 | ||
|
|
98a6d0fa26 | ||
|
|
6b0e7961fa | ||
|
|
53f69d9966 | ||
|
|
8fb2faa89b | ||
|
|
2b935e575a | ||
|
|
ec4c820391 | ||
|
|
1dce2eb325 | ||
|
|
49e5f8a1a5 | ||
|
|
f3b27d611a | ||
|
|
f57ba3e642 | ||
|
|
d4a072818f | ||
|
|
eeef062f7c | ||
|
|
9d56f3eb68 | ||
|
|
cf8db4e425 | ||
|
|
daa31b5bb7 | ||
|
|
8636a50190 | ||
|
|
fa28d94363 | ||
|
|
dbbc944f32 | ||
|
|
a88ad62c00 | ||
|
|
3e9341ff8a | ||
|
|
b9984beb16 | ||
|
|
68d90918cf | ||
|
|
25ffcb2fde | ||
|
|
6021d9238c | ||
|
|
830b83f653 | ||
|
|
f85ee38e04 | ||
|
|
88e3227ed0 | ||
|
|
f77f91ef49 | ||
|
|
91bca4a2c3 | ||
|
|
2950890180 | ||
|
|
f98479dca3 | ||
|
|
78ddb998a2 | ||
|
|
5d946586b8 | ||
|
|
112992585a | ||
|
|
84d69566c9 | ||
|
|
ca7789d73c | ||
|
|
bed0d5678d | ||
|
|
6d3244d01c | ||
|
|
3ab73c8552 | ||
|
|
df10a7e7f0 | ||
|
|
b12f4d97f8 | ||
|
|
ee3085e15e | ||
|
|
1547cd0546 | ||
|
|
2c4d2d3069 | ||
|
|
67fb802f29 | ||
|
|
2097a001b9 | ||
|
|
53e96e5adf | ||
|
|
2e4e5ef480 | ||
|
|
3899f2cdf3 | ||
|
|
261cabde67 | ||
|
|
b24c6fd579 | ||
|
|
de2ee4d289 | ||
|
|
4cd3618dcd | ||
|
|
8768eb64e6 | ||
|
|
609bb762fe | ||
|
|
90017712a6 | ||
|
|
32859eb057 | ||
|
|
4198f61c16 | ||
|
|
6f73e9c3ba | ||
|
|
9053ceb3b7 | ||
|
|
e13050b64e | ||
|
|
6789273ab1 | ||
|
|
aff6f00450 | ||
|
|
5a49f6bb9b | ||
|
|
f5efddc011 | ||
|
|
e45268cd4d | ||
|
|
5bc21a6178 | ||
|
|
7d961e1af2 | ||
|
|
eef8137166 | ||
|
|
faaddd4dfe | ||
|
|
e81a4fc330 | ||
|
|
2c74dc23c9 | ||
|
|
a20ca78599 | ||
|
|
f1a7f0a7e8 | ||
|
|
d44c9d3e74 | ||
|
|
905fa10c88 | ||
|
|
4191b07b1f | ||
|
|
77dcd68a86 | ||
|
|
d06444e639 | ||
|
|
369bc50709 | ||
|
|
6f60eb9e1a | ||
|
|
845f384df6 | ||
|
|
516fdf627a | ||
|
|
c7a7594821 | ||
|
|
37af588492 | ||
|
|
9540c58c4a | ||
|
|
8a80a734d8 | ||
|
|
9e33b47cbd | ||
|
|
e2fe0e11f0 | ||
|
|
3bd45a8536 | ||
|
|
effb8024a4 | ||
|
|
c16a2827d7 | ||
|
|
796075f9dc | ||
|
|
307187f6c1 | ||
|
|
39c52c9ecf |
7
.bazelrc
7
.bazelrc
@@ -11,9 +11,14 @@ common --override_module=semmle_code=%workspace%/misc/bazel/semmle_code_stub
|
|||||||
build --repo_env=CC=clang --repo_env=CXX=clang++
|
build --repo_env=CC=clang --repo_env=CXX=clang++
|
||||||
|
|
||||||
build:linux --cxxopt=-std=c++20
|
build:linux --cxxopt=-std=c++20
|
||||||
build:macos --cxxopt=-std=c++20 --cpu=darwin_x86_64
|
# we currently cannot built the swift extractor for ARM
|
||||||
|
build:macos --cxxopt=-std=c++20 --copt=-arch --copt=x86_64 --linkopt=-arch --linkopt=x86_64
|
||||||
build:windows --cxxopt=/std:c++20 --cxxopt=/Zc:preprocessor
|
build:windows --cxxopt=/std:c++20 --cxxopt=/Zc:preprocessor
|
||||||
|
|
||||||
|
# this requires developer mode, but is required to have pack installer functioning
|
||||||
|
startup --windows_enable_symlinks
|
||||||
|
common --enable_runfiles
|
||||||
|
|
||||||
common --registry=file:///%workspace%/misc/bazel/registry
|
common --registry=file:///%workspace%/misc/bazel/registry
|
||||||
common --registry=https://bcr.bazel.build
|
common --registry=https://bcr.bazel.build
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
7.1.0
|
7.1.2
|
||||||
|
|||||||
5
.gitattributes
vendored
5
.gitattributes
vendored
@@ -73,3 +73,8 @@ python/ql/lib/semmle/python/frameworks/data/internal/subclass-capture/*.yml ling
|
|||||||
# auto-generated bazel lock file
|
# auto-generated bazel lock file
|
||||||
ruby/extractor/cargo-bazel-lock.json linguist-generated=true
|
ruby/extractor/cargo-bazel-lock.json linguist-generated=true
|
||||||
ruby/extractor/cargo-bazel-lock.json -merge
|
ruby/extractor/cargo-bazel-lock.json -merge
|
||||||
|
|
||||||
|
# auto-generated files for the C# build
|
||||||
|
csharp/paket.lock linguist-generated=true
|
||||||
|
# needs eol=crlf, as `paket` touches this file and saves it als crlf
|
||||||
|
csharp/.paket/Paket.Restore.targets linguist-generated=true eol=crlf
|
||||||
|
|||||||
2
.github/workflows/buildifier.yml
vendored
2
.github/workflows/buildifier.yml
vendored
@@ -24,5 +24,5 @@ jobs:
|
|||||||
extra_args: >
|
extra_args: >
|
||||||
buildifier --all-files 2>&1 ||
|
buildifier --all-files 2>&1 ||
|
||||||
(
|
(
|
||||||
echo -e "In order to format all bazel files, please run:\n bazel run //:buildifier"; exit 1
|
echo -e "In order to format all bazel files, please run:\n bazel run //misc/bazel:buildifier"; exit 1
|
||||||
)
|
)
|
||||||
|
|||||||
4
.github/workflows/codeql-analysis.yml
vendored
4
.github/workflows/codeql-analysis.yml
vendored
@@ -56,7 +56,9 @@ jobs:
|
|||||||
# uses a compiled language
|
# uses a compiled language
|
||||||
|
|
||||||
- run: |
|
- run: |
|
||||||
dotnet build csharp
|
cd csharp
|
||||||
|
dotnet tool restore
|
||||||
|
dotnet build .
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@main
|
uses: github/codeql-action/analyze@main
|
||||||
|
|||||||
3
.github/workflows/csharp-qltest.yml
vendored
3
.github/workflows/csharp-qltest.yml
vendored
@@ -81,10 +81,11 @@ jobs:
|
|||||||
dotnet-version: 8.0.101
|
dotnet-version: 8.0.101
|
||||||
- name: Extractor unit tests
|
- name: Extractor unit tests
|
||||||
run: |
|
run: |
|
||||||
|
dotnet tool restore
|
||||||
dotnet test -p:RuntimeFrameworkVersion=8.0.1 extractor/Semmle.Util.Tests
|
dotnet test -p:RuntimeFrameworkVersion=8.0.1 extractor/Semmle.Util.Tests
|
||||||
dotnet test -p:RuntimeFrameworkVersion=8.0.1 extractor/Semmle.Extraction.Tests
|
dotnet test -p:RuntimeFrameworkVersion=8.0.1 extractor/Semmle.Extraction.Tests
|
||||||
dotnet test -p:RuntimeFrameworkVersion=8.0.1 autobuilder/Semmle.Autobuild.CSharp.Tests
|
dotnet test -p:RuntimeFrameworkVersion=8.0.1 autobuilder/Semmle.Autobuild.CSharp.Tests
|
||||||
dotnet test -p:RuntimeFrameworkVersion=8.0.1 "${{ github.workspace }}/cpp/autobuilder/Semmle.Autobuild.Cpp.Tests"
|
dotnet test -p:RuntimeFrameworkVersion=8.0.1 autobuilder/Semmle.Autobuild.Cpp.Tests
|
||||||
shell: bash
|
shell: bash
|
||||||
stubgentest:
|
stubgentest:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|||||||
65
.github/workflows/go-tests-other-os.yml
vendored
65
.github/workflows/go-tests-other-os.yml
vendored
@@ -7,8 +7,6 @@ on:
|
|||||||
- .github/workflows/go-tests-other-os.yml
|
- .github/workflows/go-tests-other-os.yml
|
||||||
- .github/actions/**
|
- .github/actions/**
|
||||||
- codeql-workspace.yml
|
- codeql-workspace.yml
|
||||||
env:
|
|
||||||
GO_VERSION: '~1.22.0'
|
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
@@ -18,72 +16,17 @@ jobs:
|
|||||||
name: Test MacOS
|
name: Test MacOS
|
||||||
runs-on: macos-latest
|
runs-on: macos-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Set up Go ${{ env.GO_VERSION }}
|
|
||||||
uses: actions/setup-go@v5
|
|
||||||
with:
|
|
||||||
go-version: ${{ env.GO_VERSION }}
|
|
||||||
cache: false
|
|
||||||
id: go
|
|
||||||
|
|
||||||
- name: Check out code
|
- name: Check out code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
- name: Run tests
|
||||||
- name: Set up CodeQL CLI
|
uses: ./go/actions/test
|
||||||
uses: ./.github/actions/fetch-codeql
|
|
||||||
|
|
||||||
- name: Enable problem matchers in repository
|
|
||||||
shell: bash
|
|
||||||
run: 'find .github/problem-matchers -name \*.json -exec echo "::add-matcher::{}" \;'
|
|
||||||
|
|
||||||
- name: Build
|
|
||||||
run: |
|
|
||||||
cd go
|
|
||||||
make
|
|
||||||
|
|
||||||
- name: Cache compilation cache
|
|
||||||
id: query-cache
|
|
||||||
uses: ./.github/actions/cache-query-compilation
|
|
||||||
with:
|
|
||||||
key: go-qltest
|
|
||||||
- name: Test
|
|
||||||
run: |
|
|
||||||
cd go
|
|
||||||
make test cache="${{ steps.query-cache.outputs.cache-dir }}"
|
|
||||||
|
|
||||||
test-win:
|
test-win:
|
||||||
if: github.repository_owner == 'github'
|
if: github.repository_owner == 'github'
|
||||||
name: Test Windows
|
name: Test Windows
|
||||||
runs-on: windows-latest-xl
|
runs-on: windows-latest-xl
|
||||||
steps:
|
steps:
|
||||||
- name: Set up Go ${{ env.GO_VERSION }}
|
|
||||||
uses: actions/setup-go@v5
|
|
||||||
with:
|
|
||||||
go-version: ${{ env.GO_VERSION }}
|
|
||||||
cache: false
|
|
||||||
id: go
|
|
||||||
|
|
||||||
- name: Check out code
|
- name: Check out code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
- name: Run tests
|
||||||
- name: Set up CodeQL CLI
|
uses: ./go/actions/test
|
||||||
uses: ./.github/actions/fetch-codeql
|
|
||||||
|
|
||||||
- name: Enable problem matchers in repository
|
|
||||||
shell: bash
|
|
||||||
run: 'find .github/problem-matchers -name \*.json -exec echo "::add-matcher::{}" \;'
|
|
||||||
|
|
||||||
- name: Build
|
|
||||||
run: |
|
|
||||||
cd go
|
|
||||||
make
|
|
||||||
|
|
||||||
- name: Cache compilation cache
|
|
||||||
id: query-cache
|
|
||||||
uses: ./.github/actions/cache-query-compilation
|
|
||||||
with:
|
|
||||||
key: go-qltest
|
|
||||||
|
|
||||||
- name: Test
|
|
||||||
run: |
|
|
||||||
cd go
|
|
||||||
make test cache="${{ steps.query-cache.outputs.cache-dir }}"
|
|
||||||
|
|||||||
51
.github/workflows/go-tests.yml
vendored
51
.github/workflows/go-tests.yml
vendored
@@ -16,9 +16,6 @@ on:
|
|||||||
- .github/actions/**
|
- .github/actions/**
|
||||||
- codeql-workspace.yml
|
- codeql-workspace.yml
|
||||||
|
|
||||||
env:
|
|
||||||
GO_VERSION: '~1.22.0'
|
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|
||||||
@@ -28,51 +25,9 @@ jobs:
|
|||||||
name: Test Linux (Ubuntu)
|
name: Test Linux (Ubuntu)
|
||||||
runs-on: ubuntu-latest-xl
|
runs-on: ubuntu-latest-xl
|
||||||
steps:
|
steps:
|
||||||
- name: Set up Go ${{ env.GO_VERSION }}
|
|
||||||
uses: actions/setup-go@v5
|
|
||||||
with:
|
|
||||||
go-version: ${{ env.GO_VERSION }}
|
|
||||||
cache: false
|
|
||||||
id: go
|
|
||||||
|
|
||||||
- name: Check out code
|
- name: Check out code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
- name: Run tests
|
||||||
- name: Set up CodeQL CLI
|
uses: ./go/actions/test
|
||||||
uses: ./.github/actions/fetch-codeql
|
|
||||||
|
|
||||||
- name: Enable problem matchers in repository
|
|
||||||
shell: bash
|
|
||||||
run: 'find .github/problem-matchers -name \*.json -exec echo "::add-matcher::{}" \;'
|
|
||||||
|
|
||||||
- name: Build
|
|
||||||
run: |
|
|
||||||
cd go
|
|
||||||
make
|
|
||||||
|
|
||||||
- name: Check that all Go code is autoformatted
|
|
||||||
run: |
|
|
||||||
cd go
|
|
||||||
make check-formatting
|
|
||||||
|
|
||||||
- name: Compile qhelp files to markdown
|
|
||||||
run: |
|
|
||||||
cd go
|
|
||||||
env QHELP_OUT_DIR=qhelp-out make qhelp-to-markdown
|
|
||||||
|
|
||||||
- name: Upload qhelp markdown
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
with:
|
with:
|
||||||
name: qhelp-markdown
|
run-code-checks: true
|
||||||
path: go/qhelp-out/**/*.md
|
|
||||||
|
|
||||||
- name: Cache compilation cache
|
|
||||||
id: query-cache
|
|
||||||
uses: ./.github/actions/cache-query-compilation
|
|
||||||
with:
|
|
||||||
key: go-qltest
|
|
||||||
|
|
||||||
- name: Test
|
|
||||||
run: |
|
|
||||||
cd go
|
|
||||||
make test cache="${{ steps.query-cache.outputs.cache-dir }}"
|
|
||||||
|
|||||||
5
.lfsconfig
Normal file
5
.lfsconfig
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
[lfs]
|
||||||
|
# codeql is publicly forked by many users, and we don't want any LFS file polluting their working
|
||||||
|
# copies. We therefore exclude everything by default.
|
||||||
|
# For files required by bazel builds, use rules in `misc/bazel/lfs.bzl` to download them on demand.
|
||||||
|
fetchinclude = /nothing
|
||||||
@@ -26,9 +26,17 @@ repos:
|
|||||||
name: Format bazel files
|
name: Format bazel files
|
||||||
files: \.(bazel|bzl)
|
files: \.(bazel|bzl)
|
||||||
language: system
|
language: system
|
||||||
entry: bazel run //:buildifier
|
entry: bazel run //misc/bazel:buildifier
|
||||||
pass_filenames: false
|
pass_filenames: false
|
||||||
|
|
||||||
|
# DISABLED: can be enabled by copying this config and installing `pre-commit` with `--config` on the copy
|
||||||
|
# - id: go-gen
|
||||||
|
# name: Check checked in generated files in go
|
||||||
|
# files: ^go/.*
|
||||||
|
# language: system
|
||||||
|
# entry: bazel run //go:gen
|
||||||
|
# pass_filenames: false
|
||||||
|
|
||||||
- id: codeql-format
|
- id: codeql-format
|
||||||
name: Fix QL file formatting
|
name: Fix QL file formatting
|
||||||
files: \.qll?$
|
files: \.qll?$
|
||||||
|
|||||||
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"omnisharp.autoStart": false,
|
"omnisharp.autoStart": false,
|
||||||
"cmake.sourceDirectory": "${workspaceFolder}/swift",
|
"cmake.sourceDirectory": "${workspaceFolder}/swift",
|
||||||
"cmake.buildDirectory": "${workspaceFolder}/bazel-cmake-build"
|
"cmake.buildDirectory": "${workspaceFolder}/bazel-cmake-build",
|
||||||
|
"codeQL.githubDatabase.download": "never"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
load("@buildifier_prebuilt//:rules.bzl", "buildifier")
|
|
||||||
|
|
||||||
buildifier(
|
|
||||||
name = "buildifier",
|
|
||||||
exclude_patterns = [
|
|
||||||
"./.git/*",
|
|
||||||
],
|
|
||||||
lint_mode = "fix",
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
/cpp/ @github/codeql-c-analysis
|
/cpp/ @github/codeql-c-analysis
|
||||||
/cpp/autobuilder/ @github/codeql-c-extractor
|
|
||||||
/csharp/ @github/codeql-csharp
|
/csharp/ @github/codeql-csharp
|
||||||
|
/csharp/autobuilder/Semmle.Autobuild.Cpp @github/codeql-c-extractor
|
||||||
|
/csharp/autobuilder/Semmle.Autobuild.Cpp.Tests @github/codeql-c-extractor
|
||||||
/go/ @github/codeql-go
|
/go/ @github/codeql-go
|
||||||
/java/ @github/codeql-java
|
/java/ @github/codeql-java
|
||||||
/javascript/ @github/codeql-javascript
|
/javascript/ @github/codeql-javascript
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ We welcome contributions to our CodeQL libraries and queries. Got an idea for a
|
|||||||
|
|
||||||
There is lots of useful documentation to help you write queries, ranging from information about query file structure to tutorials for specific target languages. For more information on the documentation available, see [CodeQL queries](https://codeql.github.com/docs/writing-codeql-queries/codeql-queries) on [codeql.github.com](https://codeql.github.com).
|
There is lots of useful documentation to help you write queries, ranging from information about query file structure to tutorials for specific target languages. For more information on the documentation available, see [CodeQL queries](https://codeql.github.com/docs/writing-codeql-queries/codeql-queries) on [codeql.github.com](https://codeql.github.com).
|
||||||
|
|
||||||
|
Note that the CodeQL for Visual Studio Code documentation has been migrated to https://docs.github.com/en/code-security/codeql-for-vs-code/, but you can still contribute to it via a different repository. For more information, see [Contributing to GitHub Docs documentation](https://docs.github.com/en/contributing)."
|
||||||
|
|
||||||
## Change notes
|
## Change notes
|
||||||
|
|
||||||
Any nontrivial user-visible change to a query pack or library pack should have a change note. For details on how to add a change note for your change, see [this guide](docs/change-notes.md).
|
Any nontrivial user-visible change to a query pack or library pack should have a change note. For details on how to add a change note for your change, see [this guide](docs/change-notes.md).
|
||||||
@@ -43,7 +45,7 @@ If you have an idea for a query that you would like to share with other CodeQL u
|
|||||||
|
|
||||||
3. **Formatting**
|
3. **Formatting**
|
||||||
|
|
||||||
- The queries and libraries must be autoformatted, for example using the "Format Document" command in [CodeQL for Visual Studio Code](https://codeql.github.com/docs/codeql-for-visual-studio-code/about-codeql-for-visual-studio-code).
|
- The queries and libraries must be autoformatted, for example using the "Format Document" command in [CodeQL for Visual Studio Code](https://docs.github.com/en/code-security/codeql-for-vs-code/).
|
||||||
|
|
||||||
If you prefer, you can either:
|
If you prefer, you can either:
|
||||||
1. install the [pre-commit framework](https://pre-commit.com/) and install the configured hooks on this repo via `pre-commit install`, or
|
1. install the [pre-commit framework](https://pre-commit.com/) and install the configured hooks on this repo via `pre-commit install`, or
|
||||||
|
|||||||
@@ -13,7 +13,8 @@ local_path_override(
|
|||||||
|
|
||||||
# see https://registry.bazel.build/ for a list of available packages
|
# see https://registry.bazel.build/ for a list of available packages
|
||||||
|
|
||||||
bazel_dep(name = "platforms", version = "0.0.8")
|
bazel_dep(name = "platforms", version = "0.0.9")
|
||||||
|
bazel_dep(name = "rules_go", version = "0.47.0")
|
||||||
bazel_dep(name = "rules_pkg", version = "0.10.1")
|
bazel_dep(name = "rules_pkg", version = "0.10.1")
|
||||||
bazel_dep(name = "rules_nodejs", version = "6.0.3")
|
bazel_dep(name = "rules_nodejs", version = "6.0.3")
|
||||||
bazel_dep(name = "rules_python", version = "0.31.0")
|
bazel_dep(name = "rules_python", version = "0.31.0")
|
||||||
@@ -21,6 +22,7 @@ bazel_dep(name = "bazel_skylib", version = "1.5.0")
|
|||||||
bazel_dep(name = "abseil-cpp", version = "20240116.0", repo_name = "absl")
|
bazel_dep(name = "abseil-cpp", version = "20240116.0", repo_name = "absl")
|
||||||
bazel_dep(name = "nlohmann_json", version = "3.11.3", repo_name = "json")
|
bazel_dep(name = "nlohmann_json", version = "3.11.3", repo_name = "json")
|
||||||
bazel_dep(name = "fmt", version = "10.0.0")
|
bazel_dep(name = "fmt", version = "10.0.0")
|
||||||
|
bazel_dep(name = "gazelle", version = "0.36.0")
|
||||||
|
|
||||||
bazel_dep(name = "buildifier_prebuilt", version = "6.4.0", dev_dependency = True)
|
bazel_dep(name = "buildifier_prebuilt", version = "6.4.0", dev_dependency = True)
|
||||||
|
|
||||||
@@ -52,6 +54,9 @@ node.toolchain(
|
|||||||
)
|
)
|
||||||
use_repo(node, "nodejs", "nodejs_toolchains")
|
use_repo(node, "nodejs", "nodejs_toolchains")
|
||||||
|
|
||||||
|
go_sdk = use_extension("@rules_go//go:extensions.bzl", "go_sdk")
|
||||||
|
go_sdk.download(version = "1.22.2")
|
||||||
|
|
||||||
register_toolchains(
|
register_toolchains(
|
||||||
"@nodejs_toolchains//:all",
|
"@nodejs_toolchains//:all",
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ This open source repository contains the standard CodeQL libraries and queries t
|
|||||||
|
|
||||||
## How do I learn CodeQL and run queries?
|
## How do I learn CodeQL and run queries?
|
||||||
|
|
||||||
There is [extensive documentation](https://codeql.github.com/docs/) on getting started with writing CodeQL using the [CodeQL extension for Visual Studio Code](https://codeql.github.com/docs/codeql-for-visual-studio-code/) and the [CodeQL CLI](https://codeql.github.com/docs/codeql-cli/).
|
There is extensive documentation about the [CodeQL language](https://codeql.github.com/docs/), writing CodeQL using the [CodeQL extension for Visual Studio Code](https://docs.github.com/en/code-security/codeql-for-vs-code/) and using the [CodeQL CLI](https://docs.github.com/en/code-security/codeql-cli).
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
|
|||||||
13
cpp/autobuilder/.gitignore
vendored
13
cpp/autobuilder/.gitignore
vendored
@@ -1,13 +0,0 @@
|
|||||||
obj/
|
|
||||||
TestResults/
|
|
||||||
*.manifest
|
|
||||||
*.pdb
|
|
||||||
*.suo
|
|
||||||
*.mdb
|
|
||||||
*.vsmdi
|
|
||||||
csharp.log
|
|
||||||
**/bin/Debug
|
|
||||||
**/bin/Release
|
|
||||||
*.tlog
|
|
||||||
.vs
|
|
||||||
*.user
|
|
||||||
1
cpp/autobuilder/README.md
Normal file
1
cpp/autobuilder/README.md
Normal file
@@ -0,0 +1 @@
|
|||||||
|
The Windows autobuilder that used to live in this directory moved to `csharp/autobuilder/Semmle.Autobuild.Cpp`.
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<OutputType>Exe</OutputType>
|
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
|
||||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
|
||||||
<RuntimeIdentifiers>win-x64;linux-x64;osx-x64</RuntimeIdentifiers>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="System.IO.FileSystem" Version="4.3.0" />
|
|
||||||
<PackageReference Include="System.IO.FileSystem.Primitives" Version="4.3.0" />
|
|
||||||
<PackageReference Include="xunit" Version="2.6.2" />
|
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.4">
|
|
||||||
<PrivateAssets>all</PrivateAssets>
|
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
|
||||||
</PackageReference>
|
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\Semmle.Autobuild.Cpp\Semmle.Autobuild.Cpp.csproj" />
|
|
||||||
<ProjectReference Include="..\..\..\csharp\autobuilder\Semmle.Autobuild.Shared\Semmle.Autobuild.Shared.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
</Project>
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
using System.Reflection;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
// General Information about an assembly is controlled through the following
|
|
||||||
// set of attributes. Change these attribute values to modify the information
|
|
||||||
// associated with an assembly.
|
|
||||||
[assembly: AssemblyTitle("Semmle.Autobuild.Cpp")]
|
|
||||||
[assembly: AssemblyDescription("")]
|
|
||||||
[assembly: AssemblyConfiguration("")]
|
|
||||||
[assembly: AssemblyCompany("GitHub")]
|
|
||||||
[assembly: AssemblyProduct("CodeQL autobuilder for C++")]
|
|
||||||
[assembly: AssemblyCopyright("Copyright © GitHub 2020")]
|
|
||||||
[assembly: AssemblyTrademark("")]
|
|
||||||
[assembly: AssemblyCulture("")]
|
|
||||||
|
|
||||||
// Setting ComVisible to false makes the types in this assembly not visible
|
|
||||||
// to COM components. If you need to access a type in this assembly from
|
|
||||||
// COM, set the ComVisible attribute to true on that type.
|
|
||||||
[assembly: ComVisible(false)]
|
|
||||||
|
|
||||||
// Version information for an assembly consists of the following four values:
|
|
||||||
//
|
|
||||||
// Major Version
|
|
||||||
// Minor Version
|
|
||||||
// Build Number
|
|
||||||
// Revision
|
|
||||||
//
|
|
||||||
// You can specify all the values or you can default the Build and Revision Numbers
|
|
||||||
// by using the '*' as shown below:
|
|
||||||
// [assembly: AssemblyVersion("1.0.*")]
|
|
||||||
[assembly: AssemblyVersion("1.0.0.0")]
|
|
||||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
|
||||||
<AssemblyName>Semmle.Autobuild.Cpp</AssemblyName>
|
|
||||||
<RootNamespace>Semmle.Autobuild.Cpp</RootNamespace>
|
|
||||||
<ApplicationIcon />
|
|
||||||
<OutputType>Exe</OutputType>
|
|
||||||
<StartupObject />
|
|
||||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
|
||||||
<RuntimeIdentifiers>win-x64;linux-x64;osx-x64</RuntimeIdentifiers>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Folder Include="Properties\" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="Microsoft.Build" Version="17.8.3" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\..\..\csharp\extractor\Semmle.Util\Semmle.Util.csproj" />
|
|
||||||
<ProjectReference Include="..\..\..\csharp\autobuilder\Semmle.Autobuild.Shared\Semmle.Autobuild.Shared.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
description: Revert support for repeated initializers, which are allowed in C with designated initializers.
|
description: Revert support for repeated initializers, which are allowed in C with designated initializers.
|
||||||
compatibility: full
|
compatibility: full
|
||||||
aggregate_field_init.rel: reorder aggregate_field_init.rel (int aggregate, int initializer, int field, int position) aggregate initializer field
|
aggregate_field_init.rel: reorder aggregate_field_init.rel (@aggregateliteral aggregate, @expr initializer, @membervariable field, int position) aggregate initializer field
|
||||||
aggregate_array_init.rel: reorder aggregate_array_init.rel (int aggregate, int initializer, int element_index, int position) aggregate initializer element_index
|
aggregate_array_init.rel: reorder aggregate_array_init.rel (@aggregateliteral aggregate, @expr initializer, int element_index, int position) aggregate initializer element_index
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
|
## 0.13.1
|
||||||
|
|
||||||
|
No user-facing changes.
|
||||||
|
|
||||||
## 0.13.0
|
## 0.13.0
|
||||||
|
|
||||||
### Breaking Changes
|
### Breaking Changes
|
||||||
|
|||||||
4
cpp/ql/lib/change-notes/2024-05-23-Version1.md
Normal file
4
cpp/ql/lib/change-notes/2024-05-23-Version1.md
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
category: breaking
|
||||||
|
---
|
||||||
|
* CodeQL package management is now generally available, and all GitHub-produced CodeQL packages have had their version numbers increased to 1.0.0.
|
||||||
3
cpp/ql/lib/change-notes/released/0.13.1.md
Normal file
3
cpp/ql/lib/change-notes/released/0.13.1.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
## 0.13.1
|
||||||
|
|
||||||
|
No user-facing changes.
|
||||||
@@ -1,2 +1,2 @@
|
|||||||
---
|
---
|
||||||
lastReleaseVersion: 0.13.0
|
lastReleaseVersion: 0.13.1
|
||||||
|
|||||||
88
cpp/ql/lib/experimental/buildless/ASTSig.qll
Normal file
88
cpp/ql/lib/experimental/buildless/ASTSig.qll
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
import cpp
|
||||||
|
|
||||||
|
/*
|
||||||
|
The syntax of a C++ program.
|
||||||
|
*/
|
||||||
|
signature module BuildlessASTSig
|
||||||
|
{
|
||||||
|
class Node;
|
||||||
|
predicate nodeLocation(Node node, Location location);
|
||||||
|
|
||||||
|
// Parent/child relationship between AST nodes
|
||||||
|
predicate edge(Node parent, int index, Node child);
|
||||||
|
|
||||||
|
// Include graph
|
||||||
|
predicate userInclude(Node include, string path);
|
||||||
|
predicate systemInclude(Node include, string path);
|
||||||
|
|
||||||
|
// Namespaces
|
||||||
|
predicate namespace(Node ns);
|
||||||
|
predicate namespaceName(Node ns, string name);
|
||||||
|
predicate namespaceMember(Node ns, Node member);
|
||||||
|
|
||||||
|
// Functions
|
||||||
|
predicate function(Node fn);
|
||||||
|
predicate functionBody(Node fn, Node body);
|
||||||
|
predicate functionReturn(Node fn, Node returnType);
|
||||||
|
predicate functionName(Node fn, string name);
|
||||||
|
predicate functionParameter(Node fn, int i, Node parameterDecl);
|
||||||
|
predicate functionDefinition(Node fn); // If a definition as opposed to a declaration
|
||||||
|
|
||||||
|
// Statements
|
||||||
|
predicate stmt(Node node);
|
||||||
|
predicate blockStmt(Node stmt);
|
||||||
|
predicate blockMember(Node stmt, int index, Node child);
|
||||||
|
predicate ifStmt(Node stmt, Node condition, Node thenBranch);
|
||||||
|
predicate ifStmt(Node tmt, Node condition, Node thenBranch, Node elseBranch);
|
||||||
|
predicate whileStmt(Node stmt, Node condition, Node body);
|
||||||
|
predicate doWhileStmt(Node stmt, Node condition, Node body);
|
||||||
|
predicate forStmt(Node stmt, Node init, Node condition, Node update, Node body);
|
||||||
|
predicate exprStmt(Node stmt, Node expr);
|
||||||
|
predicate returnStmt(Node stmt, Node expr);
|
||||||
|
predicate returnVoidStmt(Node stmt);
|
||||||
|
// etc
|
||||||
|
|
||||||
|
// Types
|
||||||
|
predicate type(Node type);
|
||||||
|
predicate ptrType(Node type, Node element);
|
||||||
|
predicate refType(Node type, Node element);
|
||||||
|
predicate constType(Node type, Node element);
|
||||||
|
predicate rvalueRefType(Node type, Node element);
|
||||||
|
predicate arrayType(Node type, Node element, Node size);
|
||||||
|
predicate arrayType(Node type, Node element);
|
||||||
|
predicate typename(Node node, string name); // Any named type, including built-in types
|
||||||
|
predicate templated(Node node);
|
||||||
|
predicate typeDefinition(Node node); // If a definition as opposed to a declaration
|
||||||
|
|
||||||
|
predicate classOrStructDefinition(Node node);
|
||||||
|
predicate classMember(Node classOrStruct, int child, Node member);
|
||||||
|
|
||||||
|
// Templates
|
||||||
|
predicate templateParameter(Node node, int i, Node parameter);
|
||||||
|
predicate typeParameter(Node templateParameter, Node type, Node parameter);
|
||||||
|
predicate typeParameterDefault(Node templateParameter, Node defaultTypeOrValue);
|
||||||
|
|
||||||
|
// Declarations
|
||||||
|
predicate variableDeclaration(Node decl);
|
||||||
|
predicate variableDeclarationType(Node decl, Node type);
|
||||||
|
predicate variableDeclarationEntry(Node decl, int index, Node entry);
|
||||||
|
predicate variableDeclarationEntryInitializer(Node entry, Node initializer);
|
||||||
|
predicate variableName(Node entry, string name);
|
||||||
|
predicate ptrEntry(Node entry, Node element);
|
||||||
|
predicate refEntry(Node entry, Node element);
|
||||||
|
predicate rvalueRefEntry(Node entry, Node element);
|
||||||
|
predicate arrayEntry(Node entry, Node element); // ?? Size
|
||||||
|
|
||||||
|
// Expressions
|
||||||
|
predicate expression(Node node);
|
||||||
|
predicate prefixExpr(Node expr, string operator, Node operand);
|
||||||
|
predicate postfixExpr(Node expr, Node operand, string operator);
|
||||||
|
predicate binaryExpr(Node expr, Node lhs, string operator, Node rhs);
|
||||||
|
predicate castExpr(Node expr, Node type, Node operand);
|
||||||
|
predicate callExpr(Node call);
|
||||||
|
predicate callArgument(Node call, int i, Node arg);
|
||||||
|
predicate callReceiver(Node call, Node receiver);
|
||||||
|
predicate accessExpr(Node expr, string name);
|
||||||
|
predicate literal(Node expr, string value);
|
||||||
|
predicate stringLiteral(Node expr, string value);
|
||||||
|
}
|
||||||
304
cpp/ql/lib/experimental/buildless/CompiledAST.qll
Normal file
304
cpp/ql/lib/experimental/buildless/CompiledAST.qll
Normal file
@@ -0,0 +1,304 @@
|
|||||||
|
import cpp
|
||||||
|
import ASTSig
|
||||||
|
|
||||||
|
module CompiledAST implements BuildlessASTSig {
|
||||||
|
private class SourceLocation extends Location {
|
||||||
|
SourceLocation() { not this.hasLocationInfo(_, 0, 0, 0, 0) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private Type reachableType(Type type) {
|
||||||
|
result = type or
|
||||||
|
result = reachableType(type).stripTopLevelSpecifiers() or
|
||||||
|
result = reachableType(type).(PointerType).getBaseType() or
|
||||||
|
result = reachableType(type).(ReferenceType).getBaseType()
|
||||||
|
}
|
||||||
|
|
||||||
|
private class SourceDeclEntry extends DeclarationEntry {
|
||||||
|
SourceDeclEntry() {
|
||||||
|
not this.isAffectedByMacro() and
|
||||||
|
not this.isFromTemplateInstantiation(_) and
|
||||||
|
not this.isInMacroExpansion() and
|
||||||
|
not this.getDeclaration().isInMacroExpansion() and
|
||||||
|
this.getLocation() instanceof SourceLocation and
|
||||||
|
not this.(FunctionDeclarationEntry).getDeclaration().isCompilerGenerated()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private newtype TNode =
|
||||||
|
// TFunction(SourceLocation loc) { exists(Function f | f.getLocation() = loc) } or
|
||||||
|
TStatement(SourceLocation loc) { exists(Stmt s | s.getLocation() = loc) } or
|
||||||
|
TDeclaration(SourceDeclEntry decl) or
|
||||||
|
TExpression(SourceLocation loc) { exists(Expr e | e.getLocation() = loc) } or
|
||||||
|
TFunctionCallName(SourceLocation loc) { exists(FunctionCall c | c.getLocation() = loc) } or
|
||||||
|
TDeclarationType(SourceDeclEntry decl, Type type) { type = reachableType(decl.getType()) } or
|
||||||
|
TNamespaceDeclaration(NamespaceDeclarationEntry ns) { any() } or
|
||||||
|
TInclude(Include i)
|
||||||
|
|
||||||
|
class Node extends TNode {
|
||||||
|
string toString() { result = "node" }
|
||||||
|
|
||||||
|
SourceLocation getLocation() {
|
||||||
|
this = TStatement(result) or
|
||||||
|
result = this.getDeclaration().getLocation() or
|
||||||
|
this = TExpression(result) or
|
||||||
|
this = TFunctionCallName(result) or
|
||||||
|
result = this.getVariableDeclaration().getLocation() or
|
||||||
|
result = this.getNamespaceDeclaration().getLocation() or
|
||||||
|
result = this.getInclude().getLocation()
|
||||||
|
}
|
||||||
|
|
||||||
|
Include getInclude() { this = TInclude(result) }
|
||||||
|
|
||||||
|
Stmt getStmt() { this = TStatement(result.getLocation()) }
|
||||||
|
|
||||||
|
Function getFunction() {
|
||||||
|
result = this.getDeclaration().getDeclaration() and
|
||||||
|
not result.isFromTemplateInstantiation(_) and
|
||||||
|
not result.isCompilerGenerated()
|
||||||
|
}
|
||||||
|
|
||||||
|
SourceDeclEntry getDeclaration() { this = TDeclaration(result) }
|
||||||
|
|
||||||
|
NamespaceDeclarationEntry getNamespaceDeclaration() { this = TNamespaceDeclaration(result) }
|
||||||
|
|
||||||
|
Type getType() { this = TDeclarationType(_, result) }
|
||||||
|
|
||||||
|
SourceDeclEntry getVariableDeclaration() { this = TDeclarationType(result, _) }
|
||||||
|
|
||||||
|
Expr getExpr() { this = TExpression(result.getLocation()) }
|
||||||
|
|
||||||
|
FunctionCall getFunctionCallName() { this = TFunctionCallName(result.getLocation()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate nodeLocation(Node node, Location location) { location = node.getLocation() }
|
||||||
|
|
||||||
|
// Include graph
|
||||||
|
predicate userInclude(Node include, string path) {
|
||||||
|
exists(string head | head = include.getInclude().getHead() |
|
||||||
|
path = head.substring(1, head.length() - 1) and head.charAt(0) = "\""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate systemInclude(Node include, string path) {
|
||||||
|
exists(string head | head = include.getInclude().getHead() |
|
||||||
|
path = head.substring(1, head.length() - 1) and head.charAt(0) = "<"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Functions
|
||||||
|
predicate function(Node fn) { exists(fn.getFunction()) }
|
||||||
|
|
||||||
|
predicate functionBody(Node fn, Node body) { body.getStmt() = fn.getFunction().getBlock() }
|
||||||
|
|
||||||
|
predicate functionReturn(Node fn, Node returnType) {
|
||||||
|
returnType.getVariableDeclaration() = fn.getDeclaration() and
|
||||||
|
fn.getDeclaration().(FunctionDeclarationEntry).getDeclaration().getType() = returnType.getType()
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate functionName(Node fn, string name) { name = fn.getFunction().getName() }
|
||||||
|
|
||||||
|
predicate functionParameter(Node fn, int i, Node parameterDecl) {
|
||||||
|
functionParameter0(fn, i, parameterDecl) and
|
||||||
|
not exists(Node param2 | functionParameter0(fn, i, param2) |
|
||||||
|
param2.getLocation().getStartLine() < parameterDecl.getLocation().getStartLine()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pragma[nomagic]
|
||||||
|
private predicate functionParameter0(Node fn, int i, Node parameterDecl) {
|
||||||
|
fn.getFunction().getParameter(i).getADeclarationEntry() = parameterDecl.getDeclaration() and
|
||||||
|
fn.getLocation().getFile() = parameterDecl.getLocation().getFile() and
|
||||||
|
fn.getLocation().getStartLine() <= parameterDecl.getLocation().getStartLine()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Statements
|
||||||
|
predicate stmt(Node node) { exists(node.getStmt()) }
|
||||||
|
|
||||||
|
predicate blockStmt(Node stmt) { stmt.getStmt() instanceof BlockStmt }
|
||||||
|
|
||||||
|
predicate blockMember(Node stmt, int index, Node child) {
|
||||||
|
child.getStmt() = stmt.getStmt().(BlockStmt).getChild(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate ifStmt(Node stmt, Node condition, Node thenBranch) { none() }
|
||||||
|
|
||||||
|
predicate ifStmt(Node tmt, Node condition, Node thenBranch, Node elseBranch) { none() }
|
||||||
|
|
||||||
|
predicate whileStmt(Node stmt, Node condition, Node body) { none() }
|
||||||
|
|
||||||
|
predicate doWhileStmt(Node stmt, Node condition, Node body) { none() }
|
||||||
|
|
||||||
|
predicate forStmt(Node stmt, Node init, Node condition, Node update, Node body) { none() }
|
||||||
|
|
||||||
|
predicate exprStmt(Node stmt, Node expr) { none() }
|
||||||
|
|
||||||
|
predicate returnStmt(Node stmt, Node expr) { none() }
|
||||||
|
|
||||||
|
predicate returnVoidStmt(Node stmt) { none() }
|
||||||
|
|
||||||
|
// etc
|
||||||
|
// Types
|
||||||
|
predicate ptrType(Node node, Node element) {
|
||||||
|
exists(PointerType type, SourceDeclEntry e |
|
||||||
|
node = TDeclarationType(e, type) and
|
||||||
|
element = TDeclarationType(e, type.getBaseType())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate refType(Node node, Node element) {
|
||||||
|
exists(ReferenceType type, SourceDeclEntry e |
|
||||||
|
node = TDeclarationType(e, type) and
|
||||||
|
element = TDeclarationType(e, type.getBaseType())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate rvalueRefType(Node type, Node element) { none() }
|
||||||
|
|
||||||
|
predicate arrayType(Node type, Node element, Node size) { none() }
|
||||||
|
|
||||||
|
predicate arrayType(Node type, Node element) { none() }
|
||||||
|
|
||||||
|
predicate typename(Node node, string name) {
|
||||||
|
exists(Class c | c = node.getDeclaration().getDeclaration() |
|
||||||
|
not c.isAnonymous() and c.getName() = name
|
||||||
|
)
|
||||||
|
or
|
||||||
|
name = node.getType().getName()
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate templated(Node node) { none() }
|
||||||
|
|
||||||
|
predicate classOrStructDefinition(Node node) {
|
||||||
|
node.getDeclaration().getDeclaration() instanceof Class
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate classMember(Node classOrStruct, int child, Node member) {
|
||||||
|
classOrStruct.getDeclaration().getDeclaration().(Class).getAMember() =
|
||||||
|
member.getDeclaration().getDeclaration() and
|
||||||
|
child = 0 and
|
||||||
|
classOrStruct.getLocation().getFile() = member.getLocation().getFile() // TODO: Disambiguate
|
||||||
|
// and not member.getDeclaration().getDeclaration() instanceof FriendDecl
|
||||||
|
}
|
||||||
|
|
||||||
|
// Templates
|
||||||
|
predicate templateParameter(Node node, int i, Node parameter) { none() }
|
||||||
|
|
||||||
|
predicate typeParameter(Node templateParameter, Node type, Node parameter) { none() }
|
||||||
|
|
||||||
|
predicate typeParameterDefault(Node templateParameter, Node defaultTypeOrValue) { none() }
|
||||||
|
|
||||||
|
// Declarations
|
||||||
|
predicate variableDeclaration(Node decl) {
|
||||||
|
decl.getDeclaration() instanceof VariableDeclarationEntry
|
||||||
|
}
|
||||||
|
|
||||||
|
pragma[nomagic]
|
||||||
|
private predicate variableDeclarationType2(Node decl, Node type) {
|
||||||
|
decl.getDeclaration() = type.getVariableDeclaration()
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate variableDeclarationType(Node decl, Node type) {
|
||||||
|
variableDeclarationType2(decl, type) and
|
||||||
|
type.getType() = decl.getDeclaration().getType()
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate variableDeclarationEntry(Node decl, int index, Node entry) { none() }
|
||||||
|
|
||||||
|
predicate variableDeclarationEntryInitializer(Node entry, Node initializer) { none() }
|
||||||
|
|
||||||
|
predicate variableName(Node decl, string name) {
|
||||||
|
decl.getDeclaration().(VariableDeclarationEntry).getName() = name
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate ptrEntry(Node entry, Node element) { none() }
|
||||||
|
|
||||||
|
predicate refEntry(Node entry, Node element) { none() }
|
||||||
|
|
||||||
|
predicate rvalueRefEntry(Node entry, Node element) { none() }
|
||||||
|
|
||||||
|
predicate arrayEntry(Node entry, Node element) { none() }
|
||||||
|
|
||||||
|
// Expressions
|
||||||
|
predicate expression(Node node) { exists(node.getExpr()) or exists(node.getFunctionCallName()) }
|
||||||
|
|
||||||
|
predicate prefixExpr(Node expr, string operator, Node operand) { none() }
|
||||||
|
|
||||||
|
predicate postfixExpr(Node expr, Node operand, string operator) { none() }
|
||||||
|
|
||||||
|
predicate binaryExpr(Node expr, Node lhs, string operator, Node rhs) { none() }
|
||||||
|
|
||||||
|
predicate castExpr(Node expr, Node type, Node operand) { none() }
|
||||||
|
|
||||||
|
predicate callExpr(Node call) { call.getExpr() instanceof Call }
|
||||||
|
|
||||||
|
predicate callArgument(Node call, int i, Node arg) {
|
||||||
|
arg.getExpr() = call.getExpr().(Call).getArgument(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate callReceiver(Node call, Node receiver) {
|
||||||
|
receiver.getFunctionCallName() = call.getExpr()
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate accessExpr(Node expr, string name) {
|
||||||
|
expr.getExpr().(VariableAccess).getTarget().getName() = name or
|
||||||
|
expr.getFunctionCallName().getTarget().getName() = name
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate literal(Node expr, string value) { expr.getExpr().(Literal).toString() = value }
|
||||||
|
|
||||||
|
predicate stringLiteral(Node expr, string value) {
|
||||||
|
expr.getExpr().(StringLiteral).getValue() = value
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate type(Node node) { node = TDeclarationType(_, _) }
|
||||||
|
|
||||||
|
predicate constType(Node node, Node element) {
|
||||||
|
exists(SpecifiedType type, SourceDeclEntry e |
|
||||||
|
node = TDeclarationType(e, type) and
|
||||||
|
element = TDeclarationType(e, type.getBaseType()) and
|
||||||
|
type.isConst()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate namespace(Node ns) { exists(ns.getNamespaceDeclaration()) }
|
||||||
|
|
||||||
|
predicate namespaceName(Node ns, string name) {
|
||||||
|
ns.getNamespaceDeclaration().getNamespace().getName() = name
|
||||||
|
}
|
||||||
|
|
||||||
|
pragma[nomagic]
|
||||||
|
private predicate namespaceNamespace(Node ns, Node child) {
|
||||||
|
ns.getNamespaceDeclaration().getNamespace().getAChildNamespace() =
|
||||||
|
child.getNamespaceDeclaration().getNamespace() and
|
||||||
|
ns.getLocation().getFile() = child.getLocation().getFile()
|
||||||
|
}
|
||||||
|
|
||||||
|
pragma[nomagic]
|
||||||
|
private predicate namespaceDecl(Node ns, Node child) {
|
||||||
|
child.getDeclaration().getDeclaration() =
|
||||||
|
ns.getNamespaceDeclaration().getNamespace().getADeclaration() and
|
||||||
|
ns.getLocation().getFile() = child.getLocation().getFile() and
|
||||||
|
ns.getLocation().getStartLine() <= child.getLocation().getStartLine()
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate namespaceMember(Node ns, Node member) {
|
||||||
|
namespaceNamespace(ns, member) or
|
||||||
|
namespaceDecl(ns, member)
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate edge(Node parent, int index, Node child) {
|
||||||
|
namespaceMember(parent, child) and index = 0
|
||||||
|
or
|
||||||
|
classMember(parent, index, child)
|
||||||
|
or
|
||||||
|
blockMember(parent, index, child)
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate typeDefinition(Node node) {
|
||||||
|
node.getDeclaration().(TypeDeclarationEntry).isDefinition()
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate functionDefinition(Node fn) {
|
||||||
|
fn.getDeclaration().(FunctionDeclarationEntry).isDefinition()
|
||||||
|
}
|
||||||
|
}
|
||||||
282
cpp/ql/lib/experimental/buildless/Model.qll
Normal file
282
cpp/ql/lib/experimental/buildless/Model.qll
Normal file
@@ -0,0 +1,282 @@
|
|||||||
|
import AST
|
||||||
|
|
||||||
|
module BuildlessModel<BuildlessASTSig Sig> {
|
||||||
|
module AST = BuildlessAST<Sig>;
|
||||||
|
|
||||||
|
private string getQualifiedName(AST::SourceNamespace ns) {
|
||||||
|
not exists(AST::SourceNamespace p | ns = p.getAChild()) and result = ns.getName()
|
||||||
|
or
|
||||||
|
exists(AST::SourceNamespace p | ns = p.getAChild() and ns != p |
|
||||||
|
result = getQualifiedName(p) + "::" + ns.getName()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private newtype TElement =
|
||||||
|
TNamespace(string fqn) { fqn = getQualifiedName(_) } or
|
||||||
|
TASTNode(AST::SourceElement node) { any() }
|
||||||
|
|
||||||
|
/*
|
||||||
|
Any compile-time concept that can be named.
|
||||||
|
*/
|
||||||
|
class Entity extends string
|
||||||
|
{
|
||||||
|
bindingset[this] Entity() { any() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
An entity that contains named members.
|
||||||
|
*/
|
||||||
|
class Scope extends Entity
|
||||||
|
{
|
||||||
|
bindingset[this] Scope() { any() }
|
||||||
|
|
||||||
|
Member getAMember() { result = this.getAMember(_) }
|
||||||
|
|
||||||
|
abstract Member getAMember(string name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
An entity that is a member of a scope.
|
||||||
|
*/
|
||||||
|
class Member extends Entity
|
||||||
|
{
|
||||||
|
bindingset[this] Member() { any() }
|
||||||
|
|
||||||
|
abstract string getName();
|
||||||
|
abstract Scope getParent();
|
||||||
|
}
|
||||||
|
|
||||||
|
class Namespace2 extends Member, Scope
|
||||||
|
{
|
||||||
|
AST::SourceNamespace ns;
|
||||||
|
|
||||||
|
Namespace2() { this = "::" + getQualifiedName(ns) }
|
||||||
|
|
||||||
|
AST::SourceNamespace getAstNode() { result = ns }
|
||||||
|
|
||||||
|
override Namespace2 getParent() { result.getAstNode() = ns.getParent() }
|
||||||
|
|
||||||
|
override string getName() { result = ns.getName() }
|
||||||
|
|
||||||
|
override Member getAMember(string name) {
|
||||||
|
result = this.getMemberNamespace(name)
|
||||||
|
// !! Types
|
||||||
|
}
|
||||||
|
|
||||||
|
Namespace2 getMemberNamespace(string name) {
|
||||||
|
result.getParent() = this and result.getName() = name
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class Type extends Member, Scope {
|
||||||
|
Type() { exists(SourceTypeDeclaration t | t.getMangledName() = this) }
|
||||||
|
|
||||||
|
AST::SourceType getAstNode() { result = this.getADeclaration().getSourceNode() }
|
||||||
|
|
||||||
|
// string toString() { result = "i am a type" }
|
||||||
|
override string getName() { result = this.getADeclaration().getName() }
|
||||||
|
|
||||||
|
Location getLocation() { result = this.getADefinition().getLocation() }
|
||||||
|
|
||||||
|
SourceTypeDeclaration getADeclaration() { result.getMangledName() = this }
|
||||||
|
|
||||||
|
SourceTypeDefinition getADefinition() { result.getMangledName() = this }
|
||||||
|
|
||||||
|
override Member getAMember(string name)
|
||||||
|
{
|
||||||
|
result = getMemberType(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
Type getMemberType(string name)
|
||||||
|
{
|
||||||
|
none()
|
||||||
|
}
|
||||||
|
|
||||||
|
Namespace2 getParentNamespace() {
|
||||||
|
result.getAstNode() = this.getADeclaration().getParentNamespace()
|
||||||
|
}
|
||||||
|
|
||||||
|
Type getParentType() { result.getADeclaration() = this.getADeclaration().getParentType() }
|
||||||
|
|
||||||
|
override Scope getParent() { result = this.getParentNamespace() or result = this.getParentType() }
|
||||||
|
|
||||||
|
string getFullyQualifiedName() { result = this.getADeclaration().getFullyQualifiedName() }
|
||||||
|
|
||||||
|
Field getAField() { result.getParentType() = this }
|
||||||
|
}
|
||||||
|
|
||||||
|
private Type lookupNameInType(Type type, string name)
|
||||||
|
{
|
||||||
|
// Is the name a member of this type?
|
||||||
|
|
||||||
|
// Is the name in the same scope as the type?
|
||||||
|
|
||||||
|
// Is the name in global scope?
|
||||||
|
|
||||||
|
// TODO!
|
||||||
|
none()
|
||||||
|
}
|
||||||
|
|
||||||
|
class Field extends string {
|
||||||
|
Type containingType;
|
||||||
|
SourceTypeDefinition containingTypeDef;
|
||||||
|
AST::SourceVariableDeclaration fieldDef;
|
||||||
|
|
||||||
|
Field() {
|
||||||
|
containingType.getADefinition() = containingTypeDef and
|
||||||
|
fieldDef = containingTypeDef.getAField() and
|
||||||
|
this = containingTypeDef.getMangledName() + "::" + fieldDef.getName()
|
||||||
|
}
|
||||||
|
|
||||||
|
Location getLocation() { result = fieldDef.getLocation() }
|
||||||
|
|
||||||
|
Type getParentType() { result = containingType }
|
||||||
|
|
||||||
|
string getName() { result = fieldDef.getName() }
|
||||||
|
|
||||||
|
// TODO: The type of the field
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class Element extends TElement {
|
||||||
|
string toString() { result = "element" }
|
||||||
|
}
|
||||||
|
|
||||||
|
class Namespace extends Element, TNamespace {
|
||||||
|
string getFullyQualifiedName() { this = TNamespace(result) }
|
||||||
|
|
||||||
|
override string toString() { result = "namespace " + this.getFullyQualifiedName() }
|
||||||
|
|
||||||
|
NamespaceDeclaration getADeclaration() { result.getNamespace() = this }
|
||||||
|
}
|
||||||
|
|
||||||
|
class SourceElement extends Element, TASTNode {
|
||||||
|
AST::SourceElement node;
|
||||||
|
|
||||||
|
SourceElement() { this = TASTNode(node) }
|
||||||
|
|
||||||
|
Location getLocation() { result = node.getLocation() }
|
||||||
|
|
||||||
|
AST::SourceElement getSourceNode() { result = node }
|
||||||
|
}
|
||||||
|
|
||||||
|
class SourceDeclaration extends SourceElement, TASTNode {
|
||||||
|
abstract SourceDeclaration getParent();
|
||||||
|
|
||||||
|
abstract string getName();
|
||||||
|
|
||||||
|
abstract string getMangledName();
|
||||||
|
|
||||||
|
abstract predicate isDefinition();
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class SourceDefinition extends SourceDeclaration { }
|
||||||
|
|
||||||
|
class NamespaceDeclaration extends SourceDeclaration {
|
||||||
|
AST::SourceNamespace ns;
|
||||||
|
|
||||||
|
NamespaceDeclaration() { ns = node }
|
||||||
|
|
||||||
|
override string getName() { result = ns.getName() }
|
||||||
|
|
||||||
|
Namespace getNamespace() { result.getFullyQualifiedName() = this.getFullyQualifiedName() }
|
||||||
|
|
||||||
|
override string toString() { result = "namespace " + this.getName() + " { ... }" }
|
||||||
|
|
||||||
|
override NamespaceDeclaration getParent() { result.getSourceNode() = node.getParent() }
|
||||||
|
|
||||||
|
string getFullyQualifiedName() {
|
||||||
|
if exists(this.getParent())
|
||||||
|
then result = this.getParent().getFullyQualifiedName() + "::" + this.getName()
|
||||||
|
else result = this.getName()
|
||||||
|
}
|
||||||
|
|
||||||
|
override string getMangledName() { result = "::" + this.getFullyQualifiedName() }
|
||||||
|
|
||||||
|
override predicate isDefinition() { any() }
|
||||||
|
}
|
||||||
|
|
||||||
|
class SourceTypeDeclaration extends SourceDeclaration {
|
||||||
|
AST::SourceTypeDefinition def;
|
||||||
|
|
||||||
|
SourceTypeDeclaration() { def = node }
|
||||||
|
|
||||||
|
override Location getLocation() { result = def.getLocation() }
|
||||||
|
|
||||||
|
override string toString() { result = "typename " + def.getName() }
|
||||||
|
|
||||||
|
string getFullyQualifiedName() {
|
||||||
|
if exists(this.getParentNamespace())
|
||||||
|
then result = this.getParentNamespace().getFullyQualifiedName() + "::" + this.getName()
|
||||||
|
else
|
||||||
|
if exists(this.getParentType())
|
||||||
|
then result = this.getParentType() + "::" + this.getName()
|
||||||
|
else result = this.getName()
|
||||||
|
}
|
||||||
|
|
||||||
|
NamespaceDeclaration getParentNamespace() { result.getSourceNode() = def.getParent() }
|
||||||
|
|
||||||
|
SourceTypeDeclaration getParentType() { result.getSourceNode() = def.getParent() }
|
||||||
|
|
||||||
|
override SourceDeclaration getParent() {
|
||||||
|
result = this.getParentNamespace() or result = this.getParentType()
|
||||||
|
}
|
||||||
|
|
||||||
|
override string getName() { result = def.getName() }
|
||||||
|
|
||||||
|
// Mangled name
|
||||||
|
override string getMangledName() {
|
||||||
|
if exists(this.getParent())
|
||||||
|
then result = this.getParent().getMangledName() + "::" + this.getName()
|
||||||
|
else result = "::" + this.getName()
|
||||||
|
}
|
||||||
|
|
||||||
|
override predicate isDefinition() { def.isDefinition() }
|
||||||
|
|
||||||
|
AST::SourceVariableDeclaration getAField() {
|
||||||
|
result = def.getAMember()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SourceTypeDefinition extends SourceTypeDeclaration, SourceDefinition {
|
||||||
|
SourceTypeDefinition() { this.isDefinition() }
|
||||||
|
}
|
||||||
|
|
||||||
|
class SourceFunctionDeclaration extends SourceDeclaration {
|
||||||
|
AST::SourceFunction fn;
|
||||||
|
|
||||||
|
SourceFunctionDeclaration() { fn = node }
|
||||||
|
|
||||||
|
SourceTypeDeclaration getParentType() { result.getSourceNode() = fn.getParent() }
|
||||||
|
|
||||||
|
NamespaceDeclaration getParentNamespace() { result.getSourceNode() = fn.getParent() }
|
||||||
|
|
||||||
|
override SourceDeclaration getParent() {
|
||||||
|
result = this.getParentType() or result = this.getParentNamespace()
|
||||||
|
}
|
||||||
|
|
||||||
|
override string toString() { result = fn.getName() + "()" }
|
||||||
|
|
||||||
|
override Location getLocation() { result = fn.getLocation() }
|
||||||
|
|
||||||
|
override string getName() { result = fn.getName() }
|
||||||
|
|
||||||
|
override string getMangledName() {
|
||||||
|
if exists(this.getParent())
|
||||||
|
then result = this.getParent().getMangledName() + "::" + this.getName() + "()"
|
||||||
|
else result = "::" + this.getName() + "()"
|
||||||
|
}
|
||||||
|
|
||||||
|
override predicate isDefinition() { fn.isDefinition() }
|
||||||
|
}
|
||||||
|
|
||||||
|
class SourceFunctionDefinition extends SourceFunctionDeclaration, SourceDefinition {
|
||||||
|
SourceFunctionDefinition() { this.isDefinition() }
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate invalidParent(SourceDeclaration decl) { decl.getParent+() = decl }
|
||||||
|
}
|
||||||
|
|
||||||
|
// For debugging in context
|
||||||
|
module TestModel = BuildlessModel<CompiledAST>;
|
||||||
0
cpp/ql/lib/experimental/buildless/README.md
Normal file
0
cpp/ql/lib/experimental/buildless/README.md
Normal file
46
cpp/ql/lib/experimental/buildless/TODO.md
Normal file
46
cpp/ql/lib/experimental/buildless/TODO.md
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
Next:
|
||||||
|
- [x] Namespaces
|
||||||
|
- [ ] Unnamed class/struct/union and name mangling
|
||||||
|
- [ ] Linker awareness is creating duplicates
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
- Resolve the type of a local variable
|
||||||
|
|
||||||
|
|
||||||
|
- [ ] Parent scopes - where are things declared
|
||||||
|
Parent class
|
||||||
|
Parent namespace
|
||||||
|
Parent function (for variable declarations and parameters)
|
||||||
|
Parent block (for blocks and statements)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
- [ ] Return type nodes
|
||||||
|
- [ ] Construction of types
|
||||||
|
- [ ] Construction of functions
|
||||||
|
- [ ] Construction of Variables
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Names:
|
||||||
|
- [x] Identify all function names
|
||||||
|
- [x] Identify all parameter names
|
||||||
|
- [x] Identify all variable declaration names
|
||||||
|
- [x] Identify variable accesses
|
||||||
|
|
||||||
|
Types:
|
||||||
|
- [x] Locate user type definitions and typedefs
|
||||||
|
- [x] Get the type of the parameter
|
||||||
|
- [ ] Assign types to variable accesses
|
||||||
|
|
||||||
|
Calls:
|
||||||
|
- [x] Identify function call expressions
|
||||||
|
- [x] Identify the "target" of a call in a trivial case.
|
||||||
|
|
||||||
|
Overloads:
|
||||||
|
|
||||||
|
Types:
|
||||||
|
- [ ] Identify return types
|
||||||
|
- [x] Identify parameter types and other declarations
|
||||||
196
cpp/ql/lib/experimental/buildless/ast.qll
Normal file
196
cpp/ql/lib/experimental/buildless/ast.qll
Normal file
@@ -0,0 +1,196 @@
|
|||||||
|
import CompiledAST
|
||||||
|
import ASTSig
|
||||||
|
|
||||||
|
module BuildlessAST<BuildlessASTSig AST> {
|
||||||
|
final class Node = AST::Node;
|
||||||
|
|
||||||
|
// Any node in the abstract syntax tree
|
||||||
|
class SourceElement extends Node {
|
||||||
|
Location getLocation() { AST::nodeLocation(this, result) }
|
||||||
|
|
||||||
|
string toString() { result = "element" }
|
||||||
|
|
||||||
|
SourceElement getParent() { AST::edge(result, _, this) }
|
||||||
|
|
||||||
|
SourceElement getChild(int i) { AST::edge(this, i, result) }
|
||||||
|
|
||||||
|
SourceElement getAChild() { result = this.getChild(_) }
|
||||||
|
}
|
||||||
|
|
||||||
|
bindingset[path]
|
||||||
|
private File getAnIncludeTarget(string path) {
|
||||||
|
exists(string p | p = result.toString() | path = p.suffix(p.length() - path.length()))
|
||||||
|
}
|
||||||
|
|
||||||
|
class Include extends SourceElement {
|
||||||
|
string path;
|
||||||
|
|
||||||
|
Include() { AST::userInclude(this, path) or AST::systemInclude(this, path) }
|
||||||
|
|
||||||
|
File getATarget() {
|
||||||
|
result = getAnIncludeTarget(path)
|
||||||
|
or
|
||||||
|
path.prefix(3) = "../" and result = getAnIncludeTarget(path.suffix(3))
|
||||||
|
or
|
||||||
|
path.prefix(2) = "./" and result = getAnIncludeTarget(path.suffix(2))
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate isSystemInclude() { AST::systemInclude(this, _) }
|
||||||
|
|
||||||
|
predicate isUserInclude() { AST::userInclude(this, _) }
|
||||||
|
|
||||||
|
override string toString() {
|
||||||
|
this.isSystemInclude() and result = "#include <" + path + ">"
|
||||||
|
or
|
||||||
|
this.isUserInclude() and result = "#include \"" + path + "\""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class SourceScope extends SourceElement { }
|
||||||
|
|
||||||
|
class SourceNamespace extends SourceScope, SourceDeclaration {
|
||||||
|
SourceNamespace() { AST::namespace(this) }
|
||||||
|
|
||||||
|
override string getName() { AST::namespaceName(this, result) }
|
||||||
|
|
||||||
|
override string toString() { result = "namespace " + this.getName() }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Any syntax node that is a declaration
|
||||||
|
abstract class SourceDeclaration extends SourceElement {
|
||||||
|
abstract string getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
// A syntax node that declares or defines a function
|
||||||
|
class SourceFunction extends SourceDeclaration {
|
||||||
|
SourceFunction() { AST::function(this) }
|
||||||
|
|
||||||
|
override string getName() { AST::functionName(this, result) }
|
||||||
|
|
||||||
|
override string toString() { result = this.getName() }
|
||||||
|
|
||||||
|
BlockStmt getBody() { AST::functionBody(this, result) }
|
||||||
|
|
||||||
|
SourceParameter getParameter(int i) { AST::functionParameter(this, i, result) }
|
||||||
|
|
||||||
|
SourceType getReturnType() { AST::functionReturn(this, result) }
|
||||||
|
|
||||||
|
predicate isDefinition() { AST::functionDefinition(this) }
|
||||||
|
}
|
||||||
|
|
||||||
|
// A syntax node that declares a variable (including fields and parameters)
|
||||||
|
class SourceVariableDeclaration extends SourceDeclaration {
|
||||||
|
SourceVariableDeclaration() { AST::variableDeclaration(this) }
|
||||||
|
|
||||||
|
override string getName() { AST::variableName(this, result) }
|
||||||
|
|
||||||
|
override string toString() { result = this.getName() }
|
||||||
|
|
||||||
|
SourceType getType() { AST::variableDeclarationType(this, result) }
|
||||||
|
}
|
||||||
|
|
||||||
|
// A syntax node that declares a parameter
|
||||||
|
class SourceParameter extends SourceVariableDeclaration {
|
||||||
|
SourceFunction fn;
|
||||||
|
int index;
|
||||||
|
|
||||||
|
SourceParameter() { AST::functionParameter(fn, index, this) }
|
||||||
|
}
|
||||||
|
|
||||||
|
class Stmt extends SourceElement {
|
||||||
|
Stmt() { AST::stmt(this) }
|
||||||
|
|
||||||
|
override string toString() { result = "stmt" }
|
||||||
|
}
|
||||||
|
|
||||||
|
class BlockStmt extends Stmt {
|
||||||
|
BlockStmt() { AST::blockStmt(this) }
|
||||||
|
|
||||||
|
override string toString() { result = "{ ... }" }
|
||||||
|
}
|
||||||
|
|
||||||
|
class Expr extends SourceElement {
|
||||||
|
Expr() { AST::expression(this) }
|
||||||
|
}
|
||||||
|
|
||||||
|
class AccessExpr extends Expr {
|
||||||
|
string identifier;
|
||||||
|
|
||||||
|
AccessExpr() { AST::accessExpr(this, identifier) }
|
||||||
|
|
||||||
|
string getName() { result = identifier }
|
||||||
|
|
||||||
|
override string toString() { result = this.getName() }
|
||||||
|
}
|
||||||
|
|
||||||
|
class CallExpr extends Expr {
|
||||||
|
CallExpr() { AST::callExpr(this) }
|
||||||
|
|
||||||
|
Expr getReceiver() { AST::callReceiver(this, result) }
|
||||||
|
|
||||||
|
Expr getArgument(int i) { AST::callArgument(this, i, result) }
|
||||||
|
|
||||||
|
override string toString() { result = "...(...)" }
|
||||||
|
}
|
||||||
|
|
||||||
|
class Literal extends Expr {
|
||||||
|
string value;
|
||||||
|
|
||||||
|
Literal() { AST::literal(this, value) }
|
||||||
|
|
||||||
|
override string toString() { result = value }
|
||||||
|
|
||||||
|
string getValue() { result = value }
|
||||||
|
}
|
||||||
|
|
||||||
|
class StringLiteral extends Literal {
|
||||||
|
StringLiteral() { AST::stringLiteral(this, _) }
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class SourceDefinition extends SourceDeclaration { }
|
||||||
|
|
||||||
|
class SourceTypeDefinition extends SourceDefinition {
|
||||||
|
SourceTypeDefinition() { AST::classOrStructDefinition(this) }
|
||||||
|
|
||||||
|
override string getName() { AST::typename(this, result) }
|
||||||
|
|
||||||
|
override string toString() { result = this.getName() }
|
||||||
|
|
||||||
|
SourceElement getAMember() { AST::classMember(this, _, result) }
|
||||||
|
|
||||||
|
predicate isDefinition() { AST::typeDefinition(this) }
|
||||||
|
}
|
||||||
|
|
||||||
|
// A node that contains a type of some kind
|
||||||
|
class SourceType extends SourceElement {
|
||||||
|
SourceType() { AST::type(this) }
|
||||||
|
|
||||||
|
override string toString() { AST::typename(this, result) }
|
||||||
|
}
|
||||||
|
|
||||||
|
class SourcePointer extends SourceType {
|
||||||
|
SourceType pointee;
|
||||||
|
|
||||||
|
SourcePointer() { AST::ptrType(this, pointee) }
|
||||||
|
|
||||||
|
SourceType getType() { result = pointee }
|
||||||
|
}
|
||||||
|
|
||||||
|
class SourceConst extends SourceType {
|
||||||
|
SourceType type;
|
||||||
|
|
||||||
|
SourceConst() { AST::constType(this, type) }
|
||||||
|
|
||||||
|
SourceType getType() { result = type }
|
||||||
|
}
|
||||||
|
|
||||||
|
class SourceReference extends SourceType {
|
||||||
|
SourceType type;
|
||||||
|
|
||||||
|
SourceReference() { AST::refType(this, type) }
|
||||||
|
|
||||||
|
SourceType getType() { result = type }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module TestAST = BuildlessAST<CompiledAST>;
|
||||||
3
cpp/ql/lib/experimental/buildless/checks.ql
Normal file
3
cpp/ql/lib/experimental/buildless/checks.ql
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import Model
|
||||||
|
|
||||||
|
select 1
|
||||||
31
cpp/ql/lib/experimental/buildless/identifiers.qll
Normal file
31
cpp/ql/lib/experimental/buildless/identifiers.qll
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import ast_sig
|
||||||
|
import ast
|
||||||
|
import compiled_ast // For debugging in context
|
||||||
|
|
||||||
|
module BuildlessIdentifiers<BuildlessASTSig A> {
|
||||||
|
module AST = Buildless<A>;
|
||||||
|
|
||||||
|
string getQualifiedName(AST::SourceNamespace ns) {
|
||||||
|
not exists(AST::SourceNamespace p | ns = p.getAChild()) and result = ns.getName()
|
||||||
|
or
|
||||||
|
exists(AST::SourceNamespace p | ns = p.getAChild() and ns !=p|
|
||||||
|
result = getQualifiedName(p) + "::" + ns.getName()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate namespace(string ns) {
|
||||||
|
ns = ["", getQualifiedName(_)]
|
||||||
|
}
|
||||||
|
|
||||||
|
// What are the identifiers in scope at a given point in the program?
|
||||||
|
// Give a potential object that the identifier refers to
|
||||||
|
AST::SourceDeclaration nameLookup(AST::SourceScope scope) {
|
||||||
|
result = scope
|
||||||
|
or
|
||||||
|
result = scope.(AST::SourceNamespace).getAChild()
|
||||||
|
or
|
||||||
|
result = scope.(AST::SourceTypeDefinition).getAMember()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module TestIdentifiers = BuildlessIdentifiers<CompiledAST>;
|
||||||
26
cpp/ql/lib/experimental/buildless/test_ast.ql
Normal file
26
cpp/ql/lib/experimental/buildless/test_ast.ql
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import AST
|
||||||
|
import types
|
||||||
|
|
||||||
|
query TestAST::SourceFunction lua_copy() { result.getName() = "lua_copy" }
|
||||||
|
|
||||||
|
query int lua_copy_count() { result = count(lua_copy()) }
|
||||||
|
|
||||||
|
query predicate variables(TestAST::SourceVariableDeclaration decl, TestAST::SourceType sourceType) {
|
||||||
|
sourceType = decl.getType()
|
||||||
|
}
|
||||||
|
|
||||||
|
query predicate naiveCallTargets(TestAST::CallExpr call, TestAST::SourceFunction target) {
|
||||||
|
call.getReceiver().(TestAST::AccessExpr).getName() = target.getName() and
|
||||||
|
target.getName() = "max"
|
||||||
|
}
|
||||||
|
|
||||||
|
query predicate fnParents(TestAST::SourceFunction fn, TestAST::SourceNamespace parent) {
|
||||||
|
parent = fn.getParent()
|
||||||
|
}
|
||||||
|
|
||||||
|
query predicate includes(TestAST::Include include, File target) { target = include.getATarget() }
|
||||||
|
|
||||||
|
query predicate unresolvedIncludes(TestAST::Include include) { not exists(include.getATarget()) }
|
||||||
|
|
||||||
|
from TestAST::SourceFunction fn
|
||||||
|
select fn, fn.getReturnType(), count(fn.getReturnType()), fn.getReturnType().getAQlClass()
|
||||||
13
cpp/ql/lib/experimental/buildless/test_model.ql
Normal file
13
cpp/ql/lib/experimental/buildless/test_model.ql
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import Model
|
||||||
|
|
||||||
|
query predicate multipleDefinitions(TestModel::Type type, TestModel::SourceTypeDefinition def1, TestModel::SourceTypeDefinition def2)
|
||||||
|
{
|
||||||
|
def1 = type.getADefinition() and
|
||||||
|
def2 = type.getADefinition() and
|
||||||
|
def1.getLocation() != def2.getLocation() and
|
||||||
|
def1 != def2
|
||||||
|
}
|
||||||
|
|
||||||
|
from TestModel::SourceDeclaration decl
|
||||||
|
where decl.getParent+()=decl
|
||||||
|
select decl, "This has an invalid parent $@", decl.getParent()
|
||||||
34
cpp/ql/lib/experimental/buildless/test_types.ql
Normal file
34
cpp/ql/lib/experimental/buildless/test_types.ql
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import types
|
||||||
|
|
||||||
|
query predicate constPointers(TestAST::SourceType t, TestAST::SourceConst c, TestAST::SourcePointer p)
|
||||||
|
{
|
||||||
|
p.getType() = c and
|
||||||
|
t = c.getType()
|
||||||
|
}
|
||||||
|
|
||||||
|
query predicate constRefs(TestAST::SourceType t, TestAST::SourceConst c, TestAST::SourceReference p)
|
||||||
|
{
|
||||||
|
p.getType() = c and
|
||||||
|
t = c.getType()
|
||||||
|
}
|
||||||
|
|
||||||
|
query predicate nestedNamespaces(TestAST::SourceNamespace parent, TestAST::SourceNamespace child)
|
||||||
|
{
|
||||||
|
child = parent.getAChild()
|
||||||
|
}
|
||||||
|
|
||||||
|
query predicate recursiveNamespace(TestAST::SourceNamespace ns, TestAST::SourceNamespace descendents)
|
||||||
|
{
|
||||||
|
ns = ns.getAChild+() and
|
||||||
|
descendents = ns.getAChild+()
|
||||||
|
}
|
||||||
|
|
||||||
|
query predicate usertypes(TestAST::SourceNamespace ns, TestAST::SourceTypeDefinition td)
|
||||||
|
{
|
||||||
|
td = ns.getAChild()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Let's try to resolve the type of a local variable
|
||||||
|
|
||||||
|
from TestTypes::Type t
|
||||||
|
select t
|
||||||
34
cpp/ql/lib/experimental/buildless/types.qll
Normal file
34
cpp/ql/lib/experimental/buildless/types.qll
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import AST
|
||||||
|
|
||||||
|
module BuildlessTypes<BuildlessASTSig Sig> {
|
||||||
|
module AST = BuildlessAST<Sig>;
|
||||||
|
|
||||||
|
private newtype TType =
|
||||||
|
TBuiltinType(string name) { name = ["int", "char"] }
|
||||||
|
or
|
||||||
|
TUserType(string fqn) { exists(AST::SourceTypeDefinition d | d.getName() = fqn) }
|
||||||
|
//or
|
||||||
|
//TPointerType(Type type) { exists(A::SourcePointer p | p.getType() = type) }
|
||||||
|
//or
|
||||||
|
//TConstType(Type type) { exists(A::SourceConst c | c.getType() = type) }
|
||||||
|
|
||||||
|
class Type extends TType {
|
||||||
|
string toString() { result = this.getName() }
|
||||||
|
|
||||||
|
abstract string getName();
|
||||||
|
Location getLocation() { none() }
|
||||||
|
}
|
||||||
|
|
||||||
|
class BuiltinType extends Type, TBuiltinType {
|
||||||
|
override string getName() { this = TBuiltinType(result) }
|
||||||
|
}
|
||||||
|
|
||||||
|
class UserType extends Type, TUserType
|
||||||
|
{
|
||||||
|
override string getName() { this = TUserType(result) }
|
||||||
|
|
||||||
|
override Location getLocation() { exists(AST::SourceTypeDefinition d | this.getName() = d.getName() | result = d.getLocation()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module TestTypes = BuildlessTypes<CompiledAST>;
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
name: codeql/cpp-all
|
name: codeql/cpp-all
|
||||||
version: 0.13.0
|
version: 1.0.0-dev
|
||||||
groups: cpp
|
groups: cpp
|
||||||
dbscheme: semmlecode.cpp.dbscheme
|
dbscheme: semmlecode.cpp.dbscheme
|
||||||
extractor: cpp
|
extractor: cpp
|
||||||
|
|||||||
@@ -565,7 +565,7 @@ class IRGuardCondition extends Instruction {
|
|||||||
/** Holds if (determined by this guard) `op == k` evaluates to `areEqual` if this expression evaluates to `value`. */
|
/** Holds if (determined by this guard) `op == k` evaluates to `areEqual` if this expression evaluates to `value`. */
|
||||||
cached
|
cached
|
||||||
predicate comparesEq(Operand op, int k, boolean areEqual, AbstractValue value) {
|
predicate comparesEq(Operand op, int k, boolean areEqual, AbstractValue value) {
|
||||||
compares_eq(this, op, k, areEqual, value)
|
unary_compares_eq(this, op, k, areEqual, false, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -586,7 +586,7 @@ class IRGuardCondition extends Instruction {
|
|||||||
cached
|
cached
|
||||||
predicate ensuresEq(Operand op, int k, IRBlock block, boolean areEqual) {
|
predicate ensuresEq(Operand op, int k, IRBlock block, boolean areEqual) {
|
||||||
exists(AbstractValue value |
|
exists(AbstractValue value |
|
||||||
compares_eq(this, op, k, areEqual, value) and this.valueControls(block, value)
|
unary_compares_eq(this, op, k, areEqual, false, value) and this.valueControls(block, value)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -611,7 +611,7 @@ class IRGuardCondition extends Instruction {
|
|||||||
cached
|
cached
|
||||||
predicate ensuresEqEdge(Operand op, int k, IRBlock pred, IRBlock succ, boolean areEqual) {
|
predicate ensuresEqEdge(Operand op, int k, IRBlock pred, IRBlock succ, boolean areEqual) {
|
||||||
exists(AbstractValue value |
|
exists(AbstractValue value |
|
||||||
compares_eq(this, op, k, areEqual, value) and
|
unary_compares_eq(this, op, k, areEqual, false, value) and
|
||||||
this.valueControlsEdge(pred, succ, value)
|
this.valueControlsEdge(pred, succ, value)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -737,26 +737,66 @@ private predicate compares_eq(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Holds if `op == k` is `areEqual` given that `test` is equal to `value`. */
|
/**
|
||||||
private predicate compares_eq(
|
* Holds if `op == k` is `areEqual` given that `test` is equal to `value`.
|
||||||
Instruction test, Operand op, int k, boolean areEqual, AbstractValue value
|
*
|
||||||
|
* Many internal predicates in this file have a `inNonZeroCase` column.
|
||||||
|
* Ideally, the `k` column would be a type such as `Option<int>::Option`, to
|
||||||
|
* represent whether we have a concrete value `k` such that `op == k`, or whether
|
||||||
|
* we only know that `op != 0`.
|
||||||
|
* However, cannot instantiate `Option` with an infinite type. Thus the boolean
|
||||||
|
* `inNonZeroCase` is used to distinquish the `Some` (where we have a concrete
|
||||||
|
* value `k`) and `None` cases (where we only know that `op != 0`).
|
||||||
|
*
|
||||||
|
* Thus, if `inNonZeroCase = true` then `op != 0` and the value of `k` is
|
||||||
|
* meaningless.
|
||||||
|
*
|
||||||
|
* To see why `inNonZeroCase` is needed consider the following C program:
|
||||||
|
* ```c
|
||||||
|
* char* p = ...;
|
||||||
|
* if(p) {
|
||||||
|
* use(p);
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
* in C++ there would be an int-to-bool conversion on `p`. However, since C
|
||||||
|
* does not have booleans there is no conversion. We want to be able to
|
||||||
|
* conclude that `p` is non-zero in the true branch, so we need to give `k`
|
||||||
|
* some value. However, simply setting `k = 1` would make the rest of the
|
||||||
|
* analysis think that `k == 1` holds inside the branch. So we distinquish
|
||||||
|
* between the above case and
|
||||||
|
* ```c
|
||||||
|
* if(p == 1) {
|
||||||
|
* use(p)
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
* by setting `inNonZeroCase` to `true` in the former case, but not in the
|
||||||
|
* latter.
|
||||||
|
*/
|
||||||
|
private predicate unary_compares_eq(
|
||||||
|
Instruction test, Operand op, int k, boolean areEqual, boolean inNonZeroCase, AbstractValue value
|
||||||
) {
|
) {
|
||||||
/* The simple case where the test *is* the comparison so areEqual = testIsTrue xor eq. */
|
/* The simple case where the test *is* the comparison so areEqual = testIsTrue xor eq. */
|
||||||
exists(AbstractValue v | simple_comparison_eq(test, op, k, v) |
|
exists(AbstractValue v | unary_simple_comparison_eq(test, op, k, inNonZeroCase, v) |
|
||||||
areEqual = true and value = v
|
areEqual = true and value = v
|
||||||
or
|
or
|
||||||
areEqual = false and value = v.getDualValue()
|
areEqual = false and value = v.getDualValue()
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
complex_eq(test, op, k, areEqual, value)
|
unary_complex_eq(test, op, k, areEqual, inNonZeroCase, value)
|
||||||
or
|
or
|
||||||
/* (x is true => (op == k)) => (!x is false => (op == k)) */
|
/* (x is true => (op == k)) => (!x is false => (op == k)) */
|
||||||
exists(AbstractValue dual | value = dual.getDualValue() |
|
exists(AbstractValue dual, boolean inNonZeroCase0 |
|
||||||
compares_eq(test.(LogicalNotInstruction).getUnary(), op, k, areEqual, dual)
|
value = dual.getDualValue() and
|
||||||
|
unary_compares_eq(test.(LogicalNotInstruction).getUnary(), op, k, inNonZeroCase0, areEqual, dual)
|
||||||
|
|
|
||||||
|
k = 0 and inNonZeroCase = inNonZeroCase0
|
||||||
|
or
|
||||||
|
k != 0 and inNonZeroCase = true
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
// ((test is `areEqual` => op == const + k2) and const == `k1`) =>
|
// ((test is `areEqual` => op == const + k2) and const == `k1`) =>
|
||||||
// test is `areEqual` => op == k1 + k2
|
// test is `areEqual` => op == k1 + k2
|
||||||
|
inNonZeroCase = false and
|
||||||
exists(int k1, int k2, ConstantInstruction const |
|
exists(int k1, int k2, ConstantInstruction const |
|
||||||
compares_eq(test, op, const.getAUse(), k2, areEqual, value) and
|
compares_eq(test, op, const.getAUse(), k2, areEqual, value) and
|
||||||
int_value(const) = k1 and
|
int_value(const) = k1 and
|
||||||
@@ -781,14 +821,53 @@ private predicate simple_comparison_eq(
|
|||||||
value.(BooleanValue).getValue() = false
|
value.(BooleanValue).getValue() = false
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Rearrange various simple comparisons into `op == k` form. */
|
/**
|
||||||
private predicate simple_comparison_eq(Instruction test, Operand op, int k, AbstractValue value) {
|
* Holds if `test` is an instruction that is part of test that eventually is
|
||||||
|
* used in a conditional branch.
|
||||||
|
*/
|
||||||
|
private predicate relevantUnaryComparison(Instruction test) {
|
||||||
|
not test instanceof CompareInstruction and
|
||||||
|
exists(IRType type, ConditionalBranchInstruction branch |
|
||||||
|
type instanceof IRAddressType or type instanceof IRIntegerType
|
||||||
|
|
|
||||||
|
type = test.getResultIRType() and
|
||||||
|
branch.getCondition() = test
|
||||||
|
)
|
||||||
|
or
|
||||||
|
exists(LogicalNotInstruction logicalNot |
|
||||||
|
relevantUnaryComparison(logicalNot) and
|
||||||
|
test = logicalNot.getUnary()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rearrange various simple comparisons into `op == k` form.
|
||||||
|
*/
|
||||||
|
private predicate unary_simple_comparison_eq(
|
||||||
|
Instruction test, Operand op, int k, boolean inNonZeroCase, AbstractValue value
|
||||||
|
) {
|
||||||
exists(SwitchInstruction switch, CaseEdge case |
|
exists(SwitchInstruction switch, CaseEdge case |
|
||||||
test = switch.getExpression() and
|
test = switch.getExpression() and
|
||||||
op.getDef() = test and
|
op.getDef() = test and
|
||||||
case = value.(MatchValue).getCase() and
|
case = value.(MatchValue).getCase() and
|
||||||
exists(switch.getSuccessor(case)) and
|
exists(switch.getSuccessor(case)) and
|
||||||
case.getValue().toInt() = k
|
case.getValue().toInt() = k and
|
||||||
|
inNonZeroCase = false
|
||||||
|
)
|
||||||
|
or
|
||||||
|
// There's no implicit CompareInstruction in files compiled as C since C
|
||||||
|
// doesn't have implicit boolean conversions. So instead we check whether
|
||||||
|
// there's a branch on a value of pointer or integer type.
|
||||||
|
relevantUnaryComparison(test) and
|
||||||
|
op.getDef() = test and
|
||||||
|
(
|
||||||
|
k = 1 and
|
||||||
|
value.(BooleanValue).getValue() = true and
|
||||||
|
inNonZeroCase = true
|
||||||
|
or
|
||||||
|
k = 0 and
|
||||||
|
value.(BooleanValue).getValue() = false and
|
||||||
|
inNonZeroCase = false
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -800,12 +879,12 @@ private predicate complex_eq(
|
|||||||
add_eq(cmp, left, right, k, areEqual, value)
|
add_eq(cmp, left, right, k, areEqual, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
private predicate complex_eq(
|
private predicate unary_complex_eq(
|
||||||
Instruction test, Operand op, int k, boolean areEqual, AbstractValue value
|
Instruction test, Operand op, int k, boolean areEqual, boolean inNonZeroCase, AbstractValue value
|
||||||
) {
|
) {
|
||||||
sub_eq(test, op, k, areEqual, value)
|
unary_sub_eq(test, op, k, areEqual, inNonZeroCase, value)
|
||||||
or
|
or
|
||||||
add_eq(test, op, k, areEqual, value)
|
unary_add_eq(test, op, k, areEqual, inNonZeroCase, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -1069,16 +1148,20 @@ private predicate sub_eq(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// op - x == c => op == (c+x)
|
// op - x == c => op == (c+x)
|
||||||
private predicate sub_eq(Instruction test, Operand op, int k, boolean areEqual, AbstractValue value) {
|
private predicate unary_sub_eq(
|
||||||
|
Instruction test, Operand op, int k, boolean areEqual, boolean inNonZeroCase, AbstractValue value
|
||||||
|
) {
|
||||||
|
inNonZeroCase = false and
|
||||||
exists(SubInstruction sub, int c, int x |
|
exists(SubInstruction sub, int c, int x |
|
||||||
compares_eq(test, sub.getAUse(), c, areEqual, value) and
|
unary_compares_eq(test, sub.getAUse(), c, areEqual, inNonZeroCase, value) and
|
||||||
op = sub.getLeftOperand() and
|
op = sub.getLeftOperand() and
|
||||||
x = int_value(sub.getRight()) and
|
x = int_value(sub.getRight()) and
|
||||||
k = c + x
|
k = c + x
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
|
inNonZeroCase = false and
|
||||||
exists(PointerSubInstruction sub, int c, int x |
|
exists(PointerSubInstruction sub, int c, int x |
|
||||||
compares_eq(test, sub.getAUse(), c, areEqual, value) and
|
unary_compares_eq(test, sub.getAUse(), c, areEqual, inNonZeroCase, value) and
|
||||||
op = sub.getLeftOperand() and
|
op = sub.getLeftOperand() and
|
||||||
x = int_value(sub.getRight()) and
|
x = int_value(sub.getRight()) and
|
||||||
k = c + x
|
k = c + x
|
||||||
@@ -1132,11 +1215,13 @@ private predicate add_eq(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// left + x == right + c => left == right + (c-x)
|
// left + x == right + c => left == right + (c-x)
|
||||||
private predicate add_eq(
|
private predicate unary_add_eq(
|
||||||
Instruction test, Operand left, int k, boolean areEqual, AbstractValue value
|
Instruction test, Operand left, int k, boolean areEqual, boolean inNonZeroCase,
|
||||||
|
AbstractValue value
|
||||||
) {
|
) {
|
||||||
|
inNonZeroCase = false and
|
||||||
exists(AddInstruction lhs, int c, int x |
|
exists(AddInstruction lhs, int c, int x |
|
||||||
compares_eq(test, lhs.getAUse(), c, areEqual, value) and
|
unary_compares_eq(test, lhs.getAUse(), c, areEqual, inNonZeroCase, value) and
|
||||||
(
|
(
|
||||||
left = lhs.getLeftOperand() and x = int_value(lhs.getRight())
|
left = lhs.getLeftOperand() and x = int_value(lhs.getRight())
|
||||||
or
|
or
|
||||||
@@ -1145,8 +1230,9 @@ private predicate add_eq(
|
|||||||
k = c - x
|
k = c - x
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
|
inNonZeroCase = false and
|
||||||
exists(PointerAddInstruction lhs, int c, int x |
|
exists(PointerAddInstruction lhs, int c, int x |
|
||||||
compares_eq(test, lhs.getAUse(), c, areEqual, value) and
|
unary_compares_eq(test, lhs.getAUse(), c, areEqual, inNonZeroCase, value) and
|
||||||
(
|
(
|
||||||
left = lhs.getLeftOperand() and x = int_value(lhs.getRight())
|
left = lhs.getLeftOperand() and x = int_value(lhs.getRight())
|
||||||
or
|
or
|
||||||
|
|||||||
@@ -1665,3 +1665,311 @@ class DataFlowSecondLevelScope extends TDataFlowSecondLevelScope {
|
|||||||
|
|
||||||
/** Gets the second-level scope containing the node `n`, if any. */
|
/** Gets the second-level scope containing the node `n`, if any. */
|
||||||
DataFlowSecondLevelScope getSecondLevelScope(Node n) { result.getANode() = n }
|
DataFlowSecondLevelScope getSecondLevelScope(Node n) { result.getANode() = n }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Module that defines flow through iterators.
|
||||||
|
* For example,
|
||||||
|
* ```cpp
|
||||||
|
* auto it = v.begin();
|
||||||
|
* *it = source();
|
||||||
|
* ...
|
||||||
|
* sink(v[0]);
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
module IteratorFlow {
|
||||||
|
private import codeql.ssa.Ssa as SsaImpl
|
||||||
|
private import semmle.code.cpp.models.interfaces.Iterator as Interface
|
||||||
|
private import semmle.code.cpp.models.implementations.Iterator as Impl
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A variable of some type that can produce an iterator.
|
||||||
|
*/
|
||||||
|
class SourceVariable extends Ssa::SourceVariable {
|
||||||
|
SourceVariable() {
|
||||||
|
exists(Interface::GetIteratorFunction gets, Cpp::FunctionInput input, int i |
|
||||||
|
input.isParameterDerefOrQualifierObject(i) and
|
||||||
|
gets.getsIterator(input, _)
|
||||||
|
|
|
||||||
|
this.getType().stripType() = gets.getParameter(i).getType().stripType()
|
||||||
|
or
|
||||||
|
i = -1 and
|
||||||
|
this.getType().stripType() = gets.getDeclaringType()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private module SsaInput implements SsaImpl::InputSig<Location> {
|
||||||
|
import Ssa::InputSigCommon
|
||||||
|
|
||||||
|
class SourceVariable = IteratorFlow::SourceVariable;
|
||||||
|
|
||||||
|
/** A call to function that dereferences an iterator. */
|
||||||
|
private class IteratorPointerDereferenceCall extends CallInstruction {
|
||||||
|
IteratorPointerDereferenceCall() {
|
||||||
|
this.getStaticCallTarget() instanceof Impl::IteratorPointerDereferenceOperator
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A call to a function that obtains an iterator. */
|
||||||
|
private class GetsIteratorCall extends CallInstruction {
|
||||||
|
GetsIteratorCall() { this.getStaticCallTarget() instanceof Impl::GetIteratorFunction }
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A call to `operator++` or `operator--` on an iterator. */
|
||||||
|
private class IteratorCrementCall extends CallInstruction {
|
||||||
|
IteratorCrementCall() { this.getStaticCallTarget() instanceof Impl::IteratorCrementOperator }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets an ultimate definition of `def`.
|
||||||
|
*
|
||||||
|
* Note: Unlike `def.getAnUltimateDefinition()` this predicate also
|
||||||
|
* traverses back through iterator increment and decrement operations.
|
||||||
|
*/
|
||||||
|
private Ssa::Def getAnUltimateDefinition(Ssa::Def def) {
|
||||||
|
result = def.getAnUltimateDefinition()
|
||||||
|
or
|
||||||
|
exists(IRBlock bb, int i, IteratorCrementCall crementCall, Ssa::SourceVariable sv |
|
||||||
|
crementCall = def.getValue().asInstruction().(StoreInstruction).getSourceValue() and
|
||||||
|
sv = def.getSourceVariable() and
|
||||||
|
bb.getInstruction(i) = crementCall and
|
||||||
|
Ssa::ssaDefReachesRead(sv, result.asDef(), bb, i)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if `write` is an instruction that writes to address `address`
|
||||||
|
*/
|
||||||
|
private predicate isIteratorWrite(Instruction write, Operand address) {
|
||||||
|
exists(Ssa::DefImpl writeDef, IRBlock bb, int i |
|
||||||
|
writeDef.hasIndexInBlock(bb, i, _) and
|
||||||
|
bb.getInstruction(i) = write and
|
||||||
|
address = writeDef.getAddressOperand()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if `writeToDeref` is a write to an iterator that was obtained
|
||||||
|
* by `beginCall`. That is, the following instruction sequence holds:
|
||||||
|
* ```cpp
|
||||||
|
* it = container.begin(); // or a similar iterator-obtaining function call
|
||||||
|
* ...
|
||||||
|
* *it = value;
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
private predicate isIteratorStoreInstruction(
|
||||||
|
GetsIteratorCall beginCall, Instruction writeToDeref
|
||||||
|
) {
|
||||||
|
exists(
|
||||||
|
StoreInstruction beginStore, IRBlock bbStar, int iStar, Ssa::Def def,
|
||||||
|
IteratorPointerDereferenceCall starCall, Ssa::Def ultimate, Operand address
|
||||||
|
|
|
||||||
|
isIteratorWrite(writeToDeref, address) and
|
||||||
|
operandForFullyConvertedCall(address, starCall) and
|
||||||
|
bbStar.getInstruction(iStar) = starCall and
|
||||||
|
Ssa::ssaDefReachesRead(_, def.asDef(), bbStar, iStar) and
|
||||||
|
ultimate = getAnUltimateDefinition*(def) and
|
||||||
|
beginStore = ultimate.getValue().asInstruction() and
|
||||||
|
operandForFullyConvertedCall(beginStore.getSourceValueOperand(), beginCall)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if `(bb, i)` contains a write to an iterator that may have been obtained
|
||||||
|
* by calling `begin` (or related functions) on the variable `v`.
|
||||||
|
*/
|
||||||
|
predicate variableWrite(IRBlock bb, int i, SourceVariable v, boolean certain) {
|
||||||
|
certain = false and
|
||||||
|
exists(GetsIteratorCall beginCall, Instruction writeToDeref, IRBlock bbQual, int iQual |
|
||||||
|
isIteratorStoreInstruction(beginCall, writeToDeref) and
|
||||||
|
bb.getInstruction(i) = writeToDeref and
|
||||||
|
bbQual.getInstruction(iQual) = beginCall and
|
||||||
|
Ssa::variableRead(bbQual, iQual, v, _)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Holds if `(bb, i)` reads the container variable `v`. */
|
||||||
|
predicate variableRead(IRBlock bb, int i, SourceVariable v, boolean certain) {
|
||||||
|
Ssa::variableRead(bb, i, v, certain)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private module IteratorSsa = SsaImpl::Make<Location, SsaInput>;
|
||||||
|
|
||||||
|
cached
|
||||||
|
private newtype TSsaDef =
|
||||||
|
TDef(IteratorSsa::DefinitionExt def) or
|
||||||
|
TPhi(PhiNode phi)
|
||||||
|
|
||||||
|
abstract private class SsaDef extends TSsaDef {
|
||||||
|
/** Gets a textual representation of this element. */
|
||||||
|
string toString() { none() }
|
||||||
|
|
||||||
|
/** Gets the underlying non-phi definition or use. */
|
||||||
|
IteratorSsa::DefinitionExt asDef() { none() }
|
||||||
|
|
||||||
|
/** Gets the underlying phi node. */
|
||||||
|
PhiNode asPhi() { none() }
|
||||||
|
|
||||||
|
/** Gets the location of this element. */
|
||||||
|
abstract Location getLocation();
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Def extends TDef, SsaDef {
|
||||||
|
IteratorSsa::DefinitionExt def;
|
||||||
|
|
||||||
|
Def() { this = TDef(def) }
|
||||||
|
|
||||||
|
final override IteratorSsa::DefinitionExt asDef() { result = def }
|
||||||
|
|
||||||
|
final override Location getLocation() { result = this.getImpl().getLocation() }
|
||||||
|
|
||||||
|
/** Gets the variable written to by this definition. */
|
||||||
|
final SourceVariable getSourceVariable() { result = def.getSourceVariable() }
|
||||||
|
|
||||||
|
override string toString() { result = def.toString() }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if this definition (or use) has index `index` in block `block`,
|
||||||
|
* and is a definition (or use) of the variable `sv`.
|
||||||
|
*/
|
||||||
|
predicate hasIndexInBlock(IRBlock block, int index, SourceVariable sv) {
|
||||||
|
def.definesAt(sv, block, index, _)
|
||||||
|
}
|
||||||
|
|
||||||
|
private Ssa::DefImpl getImpl() {
|
||||||
|
exists(IRBlock bb, int i |
|
||||||
|
this.hasIndexInBlock(bb, i, _) and
|
||||||
|
result.hasIndexInBlock(bb, i)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Gets the value written by this definition (i.e., the "right-hand side"). */
|
||||||
|
Node0Impl getValue() { result = this.getImpl().getValue() }
|
||||||
|
|
||||||
|
/** Gets the indirection index of this definition. */
|
||||||
|
int getIndirectionIndex() { result = this.getImpl().getIndirectionIndex() }
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Phi extends TPhi, SsaDef {
|
||||||
|
PhiNode phi;
|
||||||
|
|
||||||
|
Phi() { this = TPhi(phi) }
|
||||||
|
|
||||||
|
final override PhiNode asPhi() { result = phi }
|
||||||
|
|
||||||
|
final override Location getLocation() { result = phi.getBasicBlock().getLocation() }
|
||||||
|
|
||||||
|
override string toString() { result = phi.toString() }
|
||||||
|
|
||||||
|
SsaIteratorNode getNode() { result.getIteratorFlowNode() = phi }
|
||||||
|
}
|
||||||
|
|
||||||
|
private class PhiNode extends IteratorSsa::DefinitionExt {
|
||||||
|
PhiNode() {
|
||||||
|
this instanceof IteratorSsa::PhiNode or
|
||||||
|
this instanceof IteratorSsa::PhiReadNode
|
||||||
|
}
|
||||||
|
|
||||||
|
SsaIteratorNode getNode() { result.getIteratorFlowNode() = this }
|
||||||
|
}
|
||||||
|
|
||||||
|
cached
|
||||||
|
private module IteratorSsaCached {
|
||||||
|
cached
|
||||||
|
predicate adjacentDefRead(IRBlock bb1, int i1, SourceVariable sv, IRBlock bb2, int i2) {
|
||||||
|
IteratorSsa::adjacentDefReadExt(_, sv, bb1, i1, bb2, i2)
|
||||||
|
or
|
||||||
|
exists(PhiNode phi |
|
||||||
|
IteratorSsa::lastRefRedefExt(_, sv, bb1, i1, phi) and
|
||||||
|
phi.definesAt(sv, bb2, i2, _)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
cached
|
||||||
|
Node getAPriorDefinition(IteratorSsa::DefinitionExt next) {
|
||||||
|
exists(IRBlock bb, int i, SourceVariable sv, IteratorSsa::DefinitionExt def |
|
||||||
|
IteratorSsa::lastRefRedefExt(pragma[only_bind_into](def), pragma[only_bind_into](sv),
|
||||||
|
pragma[only_bind_into](bb), pragma[only_bind_into](i), next) and
|
||||||
|
nodeToDefOrUse(result, sv, bb, i, _)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The set of nodes necessary for iterator flow. */
|
||||||
|
class IteratorFlowNode instanceof PhiNode {
|
||||||
|
/** Gets a textual representation of this node. */
|
||||||
|
string toString() { result = super.toString() }
|
||||||
|
|
||||||
|
/** Gets the type of this node. */
|
||||||
|
DataFlowType getType() {
|
||||||
|
exists(Ssa::SourceVariable sv |
|
||||||
|
super.definesAt(sv, _, _, _) and
|
||||||
|
result = sv.getType()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Gets the `Declaration` that contains this block. */
|
||||||
|
Declaration getFunction() { result = super.getBasicBlock().getEnclosingFunction() }
|
||||||
|
|
||||||
|
/** Gets the locatino of this node. */
|
||||||
|
Location getLocation() { result = super.getBasicBlock().getLocation() }
|
||||||
|
}
|
||||||
|
|
||||||
|
private import IteratorSsaCached
|
||||||
|
|
||||||
|
private predicate defToNode(Node node, Def def, boolean uncertain) {
|
||||||
|
(
|
||||||
|
nodeHasOperand(node, def.getValue().asOperand(), def.getIndirectionIndex())
|
||||||
|
or
|
||||||
|
nodeHasInstruction(node, def.getValue().asInstruction(), def.getIndirectionIndex())
|
||||||
|
) and
|
||||||
|
uncertain = false
|
||||||
|
}
|
||||||
|
|
||||||
|
private predicate nodeToDefOrUse(
|
||||||
|
Node node, SourceVariable sv, IRBlock bb, int i, boolean uncertain
|
||||||
|
) {
|
||||||
|
exists(Def def |
|
||||||
|
def.hasIndexInBlock(bb, i, sv) and
|
||||||
|
defToNode(node, def, uncertain)
|
||||||
|
)
|
||||||
|
or
|
||||||
|
useToNode(bb, i, sv, node) and
|
||||||
|
uncertain = false
|
||||||
|
}
|
||||||
|
|
||||||
|
private predicate useToNode(IRBlock bb, int i, SourceVariable sv, Node nodeTo) {
|
||||||
|
exists(PhiNode phi |
|
||||||
|
phi.definesAt(sv, bb, i, _) and
|
||||||
|
nodeTo = phi.getNode()
|
||||||
|
)
|
||||||
|
or
|
||||||
|
exists(Ssa::UseImpl use |
|
||||||
|
use.hasIndexInBlock(bb, i, sv) and
|
||||||
|
nodeTo = use.getNode()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if `nodeFrom` flows to `nodeTo` in a single step.
|
||||||
|
*/
|
||||||
|
predicate localFlowStep(Node nodeFrom, Node nodeTo) {
|
||||||
|
exists(
|
||||||
|
Node nFrom, SourceVariable sv, IRBlock bb1, int i1, IRBlock bb2, int i2, boolean uncertain
|
||||||
|
|
|
||||||
|
adjacentDefRead(bb1, i1, sv, bb2, i2) and
|
||||||
|
nodeToDefOrUse(nFrom, sv, bb1, i1, uncertain) and
|
||||||
|
useToNode(bb2, i2, sv, nodeTo)
|
||||||
|
|
|
||||||
|
if uncertain = true
|
||||||
|
then
|
||||||
|
nodeFrom =
|
||||||
|
[
|
||||||
|
nFrom,
|
||||||
|
getAPriorDefinition(any(IteratorSsa::DefinitionExt next | next.definesAt(sv, bb1, i1, _)))
|
||||||
|
]
|
||||||
|
else nFrom = nodeFrom
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ private newtype TIRDataFlowNode =
|
|||||||
Ssa::isModifiableByCall(operand, indirectionIndex)
|
Ssa::isModifiableByCall(operand, indirectionIndex)
|
||||||
} or
|
} or
|
||||||
TSsaPhiNode(Ssa::PhiNode phi) or
|
TSsaPhiNode(Ssa::PhiNode phi) or
|
||||||
|
TSsaIteratorNode(IteratorFlow::IteratorFlowNode n) or
|
||||||
TRawIndirectOperand0(Node0Impl node, int indirectionIndex) {
|
TRawIndirectOperand0(Node0Impl node, int indirectionIndex) {
|
||||||
Ssa::hasRawIndirectOperand(node.asOperand(), indirectionIndex)
|
Ssa::hasRawIndirectOperand(node.asOperand(), indirectionIndex)
|
||||||
} or
|
} or
|
||||||
@@ -653,6 +654,30 @@ class SsaPhiNode extends Node, TSsaPhiNode {
|
|||||||
predicate isPhiRead() { phi.isPhiRead() }
|
predicate isPhiRead() { phi.isPhiRead() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* INTERNAL: do not use.
|
||||||
|
*
|
||||||
|
* Dataflow nodes necessary for iterator flow
|
||||||
|
*/
|
||||||
|
class SsaIteratorNode extends Node, TSsaIteratorNode {
|
||||||
|
IteratorFlow::IteratorFlowNode node;
|
||||||
|
|
||||||
|
SsaIteratorNode() { this = TSsaIteratorNode(node) }
|
||||||
|
|
||||||
|
/** Gets the phi node associated with this node. */
|
||||||
|
IteratorFlow::IteratorFlowNode getIteratorFlowNode() { result = node }
|
||||||
|
|
||||||
|
override Declaration getEnclosingCallable() { result = this.getFunction() }
|
||||||
|
|
||||||
|
override Declaration getFunction() { result = node.getFunction() }
|
||||||
|
|
||||||
|
override DataFlowType getType() { result = node.getType() }
|
||||||
|
|
||||||
|
final override Location getLocationImpl() { result = node.getLocation() }
|
||||||
|
|
||||||
|
override string toStringImpl() { result = node.toString() }
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* INTERNAL: do not use.
|
* INTERNAL: do not use.
|
||||||
*
|
*
|
||||||
@@ -1190,11 +1215,11 @@ class UninitializedNode extends Node {
|
|||||||
LocalVariable v;
|
LocalVariable v;
|
||||||
|
|
||||||
UninitializedNode() {
|
UninitializedNode() {
|
||||||
exists(Ssa::Def def |
|
exists(Ssa::Def def, Ssa::SourceVariable sv |
|
||||||
def.getIndirectionIndex() = 0 and
|
def.getIndirectionIndex() = 0 and
|
||||||
def.getValue().asInstruction() instanceof UninitializedInstruction and
|
def.getValue().asInstruction() instanceof UninitializedInstruction and
|
||||||
Ssa::nodeToDefOrUse(this, def, _) and
|
Ssa::defToNode(this, def, sv, _, _, _) and
|
||||||
v = def.getSourceVariable().getBaseVariable().(Ssa::BaseIRVariable).getIRVariable().getAst()
|
v = sv.getBaseVariable().(Ssa::BaseIRVariable).getIRVariable().getAst()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2151,6 +2176,8 @@ private module Cached {
|
|||||||
// Def-use/Use-use flow
|
// Def-use/Use-use flow
|
||||||
Ssa::ssaFlow(nodeFrom, nodeTo)
|
Ssa::ssaFlow(nodeFrom, nodeTo)
|
||||||
or
|
or
|
||||||
|
IteratorFlow::localFlowStep(nodeFrom, nodeTo)
|
||||||
|
or
|
||||||
// Operand -> Instruction flow
|
// Operand -> Instruction flow
|
||||||
simpleInstructionLocalFlowStep(nodeFrom.asOperand(), nodeTo.asInstruction())
|
simpleInstructionLocalFlowStep(nodeFrom.asOperand(), nodeTo.asInstruction())
|
||||||
or
|
or
|
||||||
|
|||||||
@@ -546,7 +546,7 @@ module ProductFlow {
|
|||||||
Flow1::PathGraph::edges(pred1, succ1, _, _) and
|
Flow1::PathGraph::edges(pred1, succ1, _, _) and
|
||||||
exists(ReturnKindExt returnKind |
|
exists(ReturnKindExt returnKind |
|
||||||
succ1.getNode() = returnKind.getAnOutNode(call) and
|
succ1.getNode() = returnKind.getAnOutNode(call) and
|
||||||
pred1.getNode().(ReturnNodeExt).getKind() = returnKind
|
paramReturnNode(_, pred1.asParameterReturnNode(), _, returnKind)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -574,7 +574,7 @@ module ProductFlow {
|
|||||||
Flow2::PathGraph::edges(pred2, succ2, _, _) and
|
Flow2::PathGraph::edges(pred2, succ2, _, _) and
|
||||||
exists(ReturnKindExt returnKind |
|
exists(ReturnKindExt returnKind |
|
||||||
succ2.getNode() = returnKind.getAnOutNode(call) and
|
succ2.getNode() = returnKind.getAnOutNode(call) and
|
||||||
pred2.getNode().(ReturnNodeExt).getKind() = returnKind
|
paramReturnNode(_, pred2.asParameterReturnNode(), _, returnKind)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -246,14 +246,6 @@ private module IteratorIndirections {
|
|||||||
baseType = super.getValueType()
|
baseType = super.getValueType()
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate isAdditionalDereference(Instruction deref, Operand address) {
|
|
||||||
exists(CallInstruction call |
|
|
||||||
operandForFullyConvertedCall(getAUse(deref), call) and
|
|
||||||
this = call.getStaticCallTarget().getClassAndName("operator*") and
|
|
||||||
address = call.getThisArgumentOperand()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override predicate isAdditionalWrite(Node0Impl value, Operand address, boolean certain) {
|
override predicate isAdditionalWrite(Node0Impl value, Operand address, boolean certain) {
|
||||||
exists(CallInstruction call | call.getArgumentOperand(0) = value.asOperand() |
|
exists(CallInstruction call | call.getArgumentOperand(0) = value.asOperand() |
|
||||||
this = call.getStaticCallTarget().getClassAndName("operator=") and
|
this = call.getStaticCallTarget().getClassAndName("operator=") and
|
||||||
@@ -262,16 +254,6 @@ private module IteratorIndirections {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate isAdditionalTaintStep(Node node1, Node node2) {
|
|
||||||
exists(CallInstruction call |
|
|
||||||
// Taint through `operator+=` and `operator-=` on iterators.
|
|
||||||
call.getStaticCallTarget() instanceof Iterator::IteratorAssignArithmeticOperator and
|
|
||||||
node2.(IndirectArgumentOutNode).getPreUpdateNode() = node1 and
|
|
||||||
node1.(IndirectOperand).hasOperandAndIndirectionIndex(call.getArgumentOperand(0), _) and
|
|
||||||
node1.getType().getUnspecifiedType() = this
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override predicate isAdditionalConversionFlow(Operand opFrom, Instruction instrTo) {
|
override predicate isAdditionalConversionFlow(Operand opFrom, Instruction instrTo) {
|
||||||
// This is a bit annoying: Consider the following snippet:
|
// This is a bit annoying: Consider the following snippet:
|
||||||
// ```
|
// ```
|
||||||
@@ -589,230 +571,6 @@ private class BaseCallInstruction extends BaseSourceVariableInstruction, CallIns
|
|||||||
|
|
||||||
cached
|
cached
|
||||||
private module Cached {
|
private module Cached {
|
||||||
private import semmle.code.cpp.models.interfaces.Iterator as Interfaces
|
|
||||||
private import semmle.code.cpp.models.implementations.Iterator as Iterator
|
|
||||||
private import semmle.code.cpp.models.interfaces.FunctionInputsAndOutputs as IO
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Holds if `next` is a instruction with a memory result that potentially
|
|
||||||
* updates the memory produced by `prev`.
|
|
||||||
*/
|
|
||||||
private predicate memorySucc(Instruction prev, Instruction next) {
|
|
||||||
prev = next.(ChiInstruction).getTotal()
|
|
||||||
or
|
|
||||||
// Phi inputs can be inexact.
|
|
||||||
prev = next.(PhiInstruction).getAnInputOperand().getAnyDef()
|
|
||||||
or
|
|
||||||
prev = next.(CopyInstruction).getSourceValue()
|
|
||||||
or
|
|
||||||
exists(ReadSideEffectInstruction read |
|
|
||||||
next = read.getPrimaryInstruction() and
|
|
||||||
isAdditionalConversionFlow(_, next) and
|
|
||||||
prev = read.getSideEffectOperand().getAnyDef()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Holds if `iteratorDerefAddress` is an address of an iterator dereference (i.e., `*it`)
|
|
||||||
* that is used for a write operation that writes the value `value`. The `memory` instruction
|
|
||||||
* represents the memory that the IR's SSA analysis determined was read by the call to `operator*`.
|
|
||||||
*
|
|
||||||
* The `numberOfLoads` integer represents the number of dereferences this write corresponds to
|
|
||||||
* on the underlying container that produced the iterator.
|
|
||||||
*/
|
|
||||||
private predicate isChiAfterIteratorDef(
|
|
||||||
Instruction memory, Operand iteratorDerefAddress, Node0Impl value, int numberOfLoads
|
|
||||||
) {
|
|
||||||
exists(
|
|
||||||
BaseSourceVariableInstruction iteratorBase, ReadSideEffectInstruction read,
|
|
||||||
Operand iteratorAddress
|
|
||||||
|
|
|
||||||
numberOfLoads >= 0 and
|
|
||||||
isDef(_, value, iteratorDerefAddress, iteratorBase, numberOfLoads + 2, 0) and
|
|
||||||
isUse(_, iteratorAddress, iteratorBase, numberOfLoads + 1, 0) and
|
|
||||||
iteratorBase.getResultType() instanceof Interfaces::Iterator and
|
|
||||||
isDereference(iteratorAddress.getDef(), read.getArgumentDef().getAUse(), _) and
|
|
||||||
memory = read.getSideEffectOperand().getAnyDef()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private predicate isSource(Instruction instr, Operand iteratorAddress, int numberOfLoads) {
|
|
||||||
getAUse(instr) = iteratorAddress and
|
|
||||||
exists(BaseSourceVariableInstruction iteratorBase |
|
|
||||||
iteratorBase.getResultType() instanceof Interfaces::Iterator and
|
|
||||||
not iteratorBase.getResultType() instanceof Cpp::PointerType and
|
|
||||||
isUse(_, iteratorAddress, iteratorBase, numberOfLoads - 1, 0)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private predicate isSink(Instruction instr, CallInstruction call) {
|
|
||||||
getAUse(instr).(ArgumentOperand).getCall() = call and
|
|
||||||
// Only include operations that may modify the object that the iterator points to.
|
|
||||||
// The following is a non-exhaustive list of things that may modify the value of the
|
|
||||||
// iterator, but never the value of what the iterator points to.
|
|
||||||
// The more things we can exclude here, the faster the small dataflow-like analysis
|
|
||||||
// done by `convertsIntoArgument` will converge.
|
|
||||||
not exists(Function f | f = call.getStaticCallTarget() |
|
|
||||||
f instanceof Iterator::IteratorCrementOperator or
|
|
||||||
f instanceof Iterator::IteratorBinaryArithmeticOperator or
|
|
||||||
f instanceof Iterator::IteratorAssignArithmeticOperator or
|
|
||||||
f instanceof Iterator::IteratorCrementMemberOperator or
|
|
||||||
f instanceof Iterator::IteratorBinaryArithmeticMemberOperator or
|
|
||||||
f instanceof Iterator::IteratorAssignArithmeticMemberOperator or
|
|
||||||
f instanceof Iterator::IteratorAssignmentMemberOperator
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private predicate convertsIntoArgumentFwd(Instruction instr) {
|
|
||||||
isSource(instr, _, _)
|
|
||||||
or
|
|
||||||
exists(Instruction prev | convertsIntoArgumentFwd(prev) |
|
|
||||||
conversionFlow(unique( | | getAUse(prev)), instr, false, _)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private predicate convertsIntoArgumentRev(Instruction instr) {
|
|
||||||
convertsIntoArgumentFwd(instr) and
|
|
||||||
(
|
|
||||||
isSink(instr, _)
|
|
||||||
or
|
|
||||||
exists(Instruction next | convertsIntoArgumentRev(next) |
|
|
||||||
conversionFlow(unique( | | getAUse(instr)), next, false, _)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private predicate convertsIntoArgument(
|
|
||||||
Operand iteratorAddress, CallInstruction call, int numberOfLoads
|
|
||||||
) {
|
|
||||||
exists(Instruction iteratorAddressDef |
|
|
||||||
isSource(iteratorAddressDef, iteratorAddress, numberOfLoads) and
|
|
||||||
isSink(iteratorAddressDef, call) and
|
|
||||||
convertsIntoArgumentRev(pragma[only_bind_into](iteratorAddressDef))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private predicate isChiAfterIteratorArgument(
|
|
||||||
Instruction memory, Operand iteratorAddress, int numberOfLoads
|
|
||||||
) {
|
|
||||||
// Ideally, `iteratorAddress` would be an `ArgumentOperand`, but there might be
|
|
||||||
// various conversions applied to it before it becomes an argument.
|
|
||||||
// So we do a small amount of flow to find the call that the iterator is passed to.
|
|
||||||
exists(CallInstruction call | convertsIntoArgument(iteratorAddress, call, numberOfLoads) |
|
|
||||||
exists(ReadSideEffectInstruction read |
|
|
||||||
read.getPrimaryInstruction() = call and
|
|
||||||
read.getSideEffectOperand().getAnyDef() = memory
|
|
||||||
)
|
|
||||||
or
|
|
||||||
exists(LoadInstruction load |
|
|
||||||
iteratorAddress.getDef() = load and
|
|
||||||
memory = load.getSourceValueOperand().getAnyDef()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Holds if `iterator` is a `StoreInstruction` that stores the result of some function
|
|
||||||
* returning an iterator into an address computed started at `containerBase`.
|
|
||||||
*
|
|
||||||
* For example, given a declaration like `std::vector<int>::iterator it = v.begin()`,
|
|
||||||
* the `iterator` will be the `StoreInstruction` generated by the write to `it`, and
|
|
||||||
* `containerBase` will be the address of `v`.
|
|
||||||
*/
|
|
||||||
private predicate isChiAfterBegin(
|
|
||||||
BaseSourceVariableInstruction containerBase, StoreInstruction iterator
|
|
||||||
) {
|
|
||||||
exists(
|
|
||||||
CallInstruction getIterator, Iterator::GetIteratorFunction getIteratorFunction,
|
|
||||||
IO::FunctionInput input, int i
|
|
||||||
|
|
|
||||||
getIterator = iterator.getSourceValue() and
|
|
||||||
getIteratorFunction = getIterator.getStaticCallTarget() and
|
|
||||||
getIteratorFunction.getsIterator(input, _) and
|
|
||||||
isDef(_, any(Node0Impl n | n.asInstruction() = iterator), _, _, 1, 0) and
|
|
||||||
input.isParameterDerefOrQualifierObject(i) and
|
|
||||||
isUse(_, getIterator.getArgumentOperand(i), containerBase, 0, 0)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Holds if `iteratorAddress` is an address of an iterator that is used for
|
|
||||||
* a read operation. The `memory` instruction represents the memory that
|
|
||||||
* the IR's SSA analysis determined was read by the call to `operator*`.
|
|
||||||
*
|
|
||||||
* Finally, the `numberOfLoads` integer represents the number of dereferences
|
|
||||||
* this read corresponds to on the underlying container that produced the iterator.
|
|
||||||
*/
|
|
||||||
private predicate isChiBeforeIteratorUse(
|
|
||||||
Operand iteratorAddress, Instruction memory, int numberOfLoads
|
|
||||||
) {
|
|
||||||
exists(
|
|
||||||
BaseSourceVariableInstruction iteratorBase, LoadInstruction load,
|
|
||||||
ReadSideEffectInstruction read, Operand iteratorDerefAddress
|
|
||||||
|
|
|
||||||
numberOfLoads >= 0 and
|
|
||||||
isUse(_, iteratorAddress, iteratorBase, numberOfLoads + 1, 0) and
|
|
||||||
isUse(_, iteratorDerefAddress, iteratorBase, numberOfLoads + 2, 0) and
|
|
||||||
iteratorBase.getResultType() instanceof Interfaces::Iterator and
|
|
||||||
load.getSourceAddressOperand() = iteratorDerefAddress and
|
|
||||||
read.getPrimaryInstruction() = load.getSourceAddress() and
|
|
||||||
memory = read.getSideEffectOperand().getAnyDef()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Holds if `iteratorDerefAddress` is an address of an iterator dereference (i.e., `*it`)
|
|
||||||
* that is used for a write operation that writes the value `value` to a container that
|
|
||||||
* created the iterator. `container` represents the base of the address of the container
|
|
||||||
* that was used to create the iterator.
|
|
||||||
*/
|
|
||||||
cached
|
|
||||||
predicate isIteratorDef(
|
|
||||||
BaseSourceVariableInstruction container, Operand iteratorDerefAddress, Node0Impl value,
|
|
||||||
int numberOfLoads, int indirectionIndex
|
|
||||||
) {
|
|
||||||
exists(Instruction memory, Instruction begin, int upper, int ind |
|
|
||||||
isChiAfterIteratorDef(memory, iteratorDerefAddress, value, numberOfLoads) and
|
|
||||||
memorySucc*(begin, memory) and
|
|
||||||
isChiAfterBegin(container, begin) and
|
|
||||||
upper = countIndirectionsForCppType(getResultLanguageType(container)) and
|
|
||||||
ind = numberOfLoads + [1 .. upper] and
|
|
||||||
indirectionIndex = ind - (numberOfLoads + 1)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Holds if `iteratorAddress` is an address of an iterator that is used for a
|
|
||||||
* read operation to read a value from a container that created the iterator.
|
|
||||||
* `container` represents the base of the address of the container that was used
|
|
||||||
* to create the iterator.
|
|
||||||
*/
|
|
||||||
cached
|
|
||||||
predicate isIteratorUse(
|
|
||||||
BaseSourceVariableInstruction container, Operand iteratorAddress, int numberOfLoads,
|
|
||||||
int indirectionIndex
|
|
||||||
) {
|
|
||||||
// Direct use
|
|
||||||
exists(Instruction begin, Instruction memory, int upper, int ind |
|
|
||||||
isChiBeforeIteratorUse(iteratorAddress, memory, numberOfLoads) and
|
|
||||||
memorySucc*(begin, memory) and
|
|
||||||
isChiAfterBegin(container, begin) and
|
|
||||||
upper = countIndirectionsForCppType(getResultLanguageType(container)) and
|
|
||||||
ind = numberOfLoads + [1 .. upper] and
|
|
||||||
indirectionIndex = ind - (numberOfLoads + 1)
|
|
||||||
)
|
|
||||||
or
|
|
||||||
// Use through function output
|
|
||||||
exists(Instruction memory, Instruction begin, int upper, int ind |
|
|
||||||
isChiAfterIteratorArgument(memory, iteratorAddress, numberOfLoads) and
|
|
||||||
memorySucc*(begin, memory) and
|
|
||||||
isChiAfterBegin(container, begin) and
|
|
||||||
upper = countIndirectionsForCppType(getResultLanguageType(container)) and
|
|
||||||
ind = numberOfLoads + [1 .. upper] and
|
|
||||||
indirectionIndex = ind - (numberOfLoads - 1)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Holds if `op` is the only use of its defining instruction, and that op is used in a conversation */
|
/** Holds if `op` is the only use of its defining instruction, and that op is used in a conversation */
|
||||||
private predicate isConversion(Operand op) {
|
private predicate isConversion(Operand op) {
|
||||||
exists(Instruction def, Operand use |
|
exists(Instruction def, Operand use |
|
||||||
|
|||||||
@@ -17,18 +17,11 @@ private import Imports::IRType
|
|||||||
* The variable may be a user-declared variable (`IRUserVariable`) or a temporary variable generated
|
* The variable may be a user-declared variable (`IRUserVariable`) or a temporary variable generated
|
||||||
* by the AST-to-IR translation (`IRTempVariable`).
|
* by the AST-to-IR translation (`IRTempVariable`).
|
||||||
*/
|
*/
|
||||||
class IRVariable extends TIRVariable {
|
abstract private class AbstractIRVariable extends TIRVariable {
|
||||||
Language::Declaration func;
|
Language::Declaration func;
|
||||||
|
|
||||||
IRVariable() {
|
|
||||||
this = TIRUserVariable(_, _, func) or
|
|
||||||
this = TIRTempVariable(func, _, _, _) or
|
|
||||||
this = TIRStringLiteral(func, _, _, _) or
|
|
||||||
this = TIRDynamicInitializationFlag(func, _, _)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Gets a textual representation of this element. */
|
/** Gets a textual representation of this element. */
|
||||||
string toString() { none() }
|
abstract string toString();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if this variable's value cannot be changed within a function. Currently used for string
|
* Holds if this variable's value cannot be changed within a function. Currently used for string
|
||||||
@@ -49,13 +42,13 @@ class IRVariable extends TIRVariable {
|
|||||||
/**
|
/**
|
||||||
* Gets the type of the variable.
|
* Gets the type of the variable.
|
||||||
*/
|
*/
|
||||||
Language::LanguageType getLanguageType() { none() }
|
abstract Language::LanguageType getLanguageType();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the AST node that declared this variable, or that introduced this
|
* Gets the AST node that declared this variable, or that introduced this
|
||||||
* variable as part of the AST-to-IR translation.
|
* variable as part of the AST-to-IR translation.
|
||||||
*/
|
*/
|
||||||
Language::AST getAst() { none() }
|
abstract Language::AST getAst();
|
||||||
|
|
||||||
/** DEPRECATED: Alias for getAst */
|
/** DEPRECATED: Alias for getAst */
|
||||||
deprecated Language::AST getAST() { result = this.getAst() }
|
deprecated Language::AST getAST() { result = this.getAst() }
|
||||||
@@ -64,7 +57,7 @@ class IRVariable extends TIRVariable {
|
|||||||
* Gets an identifier string for the variable. This identifier is unique
|
* Gets an identifier string for the variable. This identifier is unique
|
||||||
* within the function.
|
* within the function.
|
||||||
*/
|
*/
|
||||||
string getUniqueId() { none() }
|
abstract string getUniqueId();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the source location of this variable.
|
* Gets the source location of this variable.
|
||||||
@@ -74,7 +67,7 @@ class IRVariable extends TIRVariable {
|
|||||||
/**
|
/**
|
||||||
* Gets the IR for the function that references this variable.
|
* Gets the IR for the function that references this variable.
|
||||||
*/
|
*/
|
||||||
final IRFunction getEnclosingIRFunction() { result.getFunction() = func }
|
final IRFunction getEnclosingIRFunction() { result.getFunction() = this.getEnclosingFunction() }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the function that references this variable.
|
* Gets the function that references this variable.
|
||||||
@@ -82,10 +75,18 @@ class IRVariable extends TIRVariable {
|
|||||||
final Language::Declaration getEnclosingFunction() { result = func }
|
final Language::Declaration getEnclosingFunction() { result = func }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A variable referenced by the IR for a function.
|
||||||
|
*
|
||||||
|
* The variable may be a user-declared variable (`IRUserVariable`) or a temporary variable generated
|
||||||
|
* by the AST-to-IR translation (`IRTempVariable`).
|
||||||
|
*/
|
||||||
|
final class IRVariable = AbstractIRVariable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A user-declared variable referenced by the IR for a function.
|
* A user-declared variable referenced by the IR for a function.
|
||||||
*/
|
*/
|
||||||
class IRUserVariable extends IRVariable, TIRUserVariable {
|
class IRUserVariable extends AbstractIRVariable, TIRUserVariable {
|
||||||
Language::Variable var;
|
Language::Variable var;
|
||||||
Language::LanguageType type;
|
Language::LanguageType type;
|
||||||
|
|
||||||
@@ -114,26 +115,29 @@ class IRUserVariable extends IRVariable, TIRUserVariable {
|
|||||||
* A variable (user-declared or temporary) that is allocated on the stack. This includes all
|
* A variable (user-declared or temporary) that is allocated on the stack. This includes all
|
||||||
* parameters, non-static local variables, and temporary variables.
|
* parameters, non-static local variables, and temporary variables.
|
||||||
*/
|
*/
|
||||||
class IRAutomaticVariable extends IRVariable {
|
abstract private class AbstractIRAutomaticVariable extends AbstractIRVariable { }
|
||||||
IRAutomaticVariable() {
|
|
||||||
exists(Language::Variable var |
|
/**
|
||||||
this = TIRUserVariable(var, _, func) and
|
* A variable (user-declared or temporary) that is allocated on the stack. This includes all
|
||||||
Language::isVariableAutomatic(var)
|
* parameters, non-static local variables, and temporary variables.
|
||||||
)
|
*/
|
||||||
or
|
final class IRAutomaticVariable = AbstractIRAutomaticVariable;
|
||||||
this = TIRTempVariable(func, _, _, _)
|
|
||||||
}
|
/**
|
||||||
|
* A user-declared variable that is allocated on the stack. This includes all parameters and
|
||||||
|
* non-static local variables.
|
||||||
|
*/
|
||||||
|
private class AbstractIRAutomaticUserVariable extends IRUserVariable, AbstractIRAutomaticVariable {
|
||||||
|
override Language::AutomaticVariable var;
|
||||||
|
|
||||||
|
final override Language::AutomaticVariable getVariable() { result = var }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A user-declared variable that is allocated on the stack. This includes all parameters and
|
* A user-declared variable that is allocated on the stack. This includes all parameters and
|
||||||
* non-static local variables.
|
* non-static local variables.
|
||||||
*/
|
*/
|
||||||
class IRAutomaticUserVariable extends IRUserVariable, IRAutomaticVariable {
|
final class IRAutomaticUserVariable = AbstractIRAutomaticUserVariable;
|
||||||
override Language::AutomaticVariable var;
|
|
||||||
|
|
||||||
final override Language::AutomaticVariable getVariable() { result = var }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A user-declared variable that is not allocated on the stack. This includes all global variables,
|
* A user-declared variable that is not allocated on the stack. This includes all global variables,
|
||||||
@@ -151,16 +155,10 @@ class IRStaticUserVariable extends IRUserVariable {
|
|||||||
* A variable that is not user-declared. This includes temporary variables generated as part of IR
|
* A variable that is not user-declared. This includes temporary variables generated as part of IR
|
||||||
* construction, as well as string literals.
|
* construction, as well as string literals.
|
||||||
*/
|
*/
|
||||||
class IRGeneratedVariable extends IRVariable {
|
abstract private class AbstractIRGeneratedVariable extends AbstractIRVariable {
|
||||||
Language::AST ast;
|
Language::AST ast;
|
||||||
Language::LanguageType type;
|
Language::LanguageType type;
|
||||||
|
|
||||||
IRGeneratedVariable() {
|
|
||||||
this = TIRTempVariable(func, ast, _, type) or
|
|
||||||
this = TIRStringLiteral(func, ast, type, _) or
|
|
||||||
this = TIRDynamicInitializationFlag(func, ast, type)
|
|
||||||
}
|
|
||||||
|
|
||||||
final override Language::LanguageType getLanguageType() { result = type }
|
final override Language::LanguageType getLanguageType() { result = type }
|
||||||
|
|
||||||
final override Language::AST getAst() { result = ast }
|
final override Language::AST getAst() { result = ast }
|
||||||
@@ -196,12 +194,20 @@ class IRGeneratedVariable extends IRVariable {
|
|||||||
string getBaseString() { none() }
|
string getBaseString() { none() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A variable that is not user-declared. This includes temporary variables generated as part of IR
|
||||||
|
* construction, as well as string literals.
|
||||||
|
*/
|
||||||
|
final class IRGeneratedVariable = AbstractIRGeneratedVariable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A temporary variable introduced by IR construction. The most common examples are the variable
|
* A temporary variable introduced by IR construction. The most common examples are the variable
|
||||||
* generated to hold the return value of a function, or the variable generated to hold the result of
|
* generated to hold the return value of a function, or the variable generated to hold the result of
|
||||||
* a condition operator (`a ? b : c`).
|
* a condition operator (`a ? b : c`).
|
||||||
*/
|
*/
|
||||||
class IRTempVariable extends IRGeneratedVariable, IRAutomaticVariable, TIRTempVariable {
|
class IRTempVariable extends AbstractIRGeneratedVariable, AbstractIRAutomaticVariable,
|
||||||
|
TIRTempVariable
|
||||||
|
{
|
||||||
TempVariableTag tag;
|
TempVariableTag tag;
|
||||||
|
|
||||||
IRTempVariable() { this = TIRTempVariable(func, ast, tag, type) }
|
IRTempVariable() { this = TIRTempVariable(func, ast, tag, type) }
|
||||||
@@ -241,7 +247,7 @@ class IRThrowVariable extends IRTempVariable {
|
|||||||
* A temporary variable generated to hold the contents of all arguments passed to the `...` of a
|
* A temporary variable generated to hold the contents of all arguments passed to the `...` of a
|
||||||
* function that accepts a variable number of arguments.
|
* function that accepts a variable number of arguments.
|
||||||
*/
|
*/
|
||||||
class IREllipsisVariable extends IRTempVariable, IRParameter {
|
class IREllipsisVariable extends IRTempVariable, AbstractIRParameter {
|
||||||
IREllipsisVariable() { tag = EllipsisTempVar() }
|
IREllipsisVariable() { tag = EllipsisTempVar() }
|
||||||
|
|
||||||
final override string toString() { result = "#ellipsis" }
|
final override string toString() { result = "#ellipsis" }
|
||||||
@@ -252,7 +258,7 @@ class IREllipsisVariable extends IRTempVariable, IRParameter {
|
|||||||
/**
|
/**
|
||||||
* A temporary variable generated to hold the `this` pointer.
|
* A temporary variable generated to hold the `this` pointer.
|
||||||
*/
|
*/
|
||||||
class IRThisVariable extends IRTempVariable, IRParameter {
|
class IRThisVariable extends IRTempVariable, AbstractIRParameter {
|
||||||
IRThisVariable() { tag = ThisTempVar() }
|
IRThisVariable() { tag = ThisTempVar() }
|
||||||
|
|
||||||
final override string toString() { result = "#this" }
|
final override string toString() { result = "#this" }
|
||||||
@@ -264,7 +270,7 @@ class IRThisVariable extends IRTempVariable, IRParameter {
|
|||||||
* A variable generated to represent the contents of a string literal. This variable acts much like
|
* A variable generated to represent the contents of a string literal. This variable acts much like
|
||||||
* a read-only global variable.
|
* a read-only global variable.
|
||||||
*/
|
*/
|
||||||
class IRStringLiteral extends IRGeneratedVariable, TIRStringLiteral {
|
class IRStringLiteral extends AbstractIRGeneratedVariable, TIRStringLiteral {
|
||||||
Language::StringLiteral literal;
|
Language::StringLiteral literal;
|
||||||
|
|
||||||
IRStringLiteral() { this = TIRStringLiteral(func, ast, type, literal) }
|
IRStringLiteral() { this = TIRStringLiteral(func, ast, type, literal) }
|
||||||
@@ -288,7 +294,7 @@ class IRStringLiteral extends IRGeneratedVariable, TIRStringLiteral {
|
|||||||
* used to model the runtime initialization of static local variables in C++, as well as static
|
* used to model the runtime initialization of static local variables in C++, as well as static
|
||||||
* fields in C#.
|
* fields in C#.
|
||||||
*/
|
*/
|
||||||
class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitializationFlag {
|
class IRDynamicInitializationFlag extends AbstractIRGeneratedVariable, TIRDynamicInitializationFlag {
|
||||||
Language::Variable var;
|
Language::Variable var;
|
||||||
|
|
||||||
IRDynamicInitializationFlag() {
|
IRDynamicInitializationFlag() {
|
||||||
@@ -314,24 +320,24 @@ class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitial
|
|||||||
* An IR variable which acts like a function parameter, including positional parameters and the
|
* An IR variable which acts like a function parameter, including positional parameters and the
|
||||||
* temporary variables generated for `this` and ellipsis parameters.
|
* temporary variables generated for `this` and ellipsis parameters.
|
||||||
*/
|
*/
|
||||||
class IRParameter extends IRAutomaticVariable {
|
abstract private class AbstractIRParameter extends AbstractIRAutomaticVariable {
|
||||||
IRParameter() {
|
|
||||||
this.(IRAutomaticUserVariable).getVariable() instanceof Language::Parameter
|
|
||||||
or
|
|
||||||
this = TIRTempVariable(_, _, ThisTempVar(), _)
|
|
||||||
or
|
|
||||||
this = TIRTempVariable(_, _, EllipsisTempVar(), _)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the zero-based index of this parameter. The `this` parameter has index -1.
|
* Gets the zero-based index of this parameter. The `this` parameter has index -1.
|
||||||
*/
|
*/
|
||||||
int getIndex() { none() }
|
int getIndex() { none() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An IR variable which acts like a function parameter, including positional parameters and the
|
||||||
|
* temporary variables generated for `this` and ellipsis parameters.
|
||||||
|
*/
|
||||||
|
final class IRParameter = AbstractIRParameter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An IR variable representing a positional parameter.
|
* An IR variable representing a positional parameter.
|
||||||
*/
|
*/
|
||||||
class IRPositionalParameter extends IRParameter, IRAutomaticUserVariable {
|
class IRPositionalParameter extends AbstractIRParameter, AbstractIRAutomaticUserVariable {
|
||||||
|
IRPositionalParameter() { this.getVariable() instanceof Language::Parameter }
|
||||||
|
|
||||||
final override int getIndex() { result = this.getVariable().(Language::Parameter).getIndex() }
|
final override int getIndex() { result = this.getVariable().(Language::Parameter).getIndex() }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,18 +17,11 @@ private import Imports::IRType
|
|||||||
* The variable may be a user-declared variable (`IRUserVariable`) or a temporary variable generated
|
* The variable may be a user-declared variable (`IRUserVariable`) or a temporary variable generated
|
||||||
* by the AST-to-IR translation (`IRTempVariable`).
|
* by the AST-to-IR translation (`IRTempVariable`).
|
||||||
*/
|
*/
|
||||||
class IRVariable extends TIRVariable {
|
abstract private class AbstractIRVariable extends TIRVariable {
|
||||||
Language::Declaration func;
|
Language::Declaration func;
|
||||||
|
|
||||||
IRVariable() {
|
|
||||||
this = TIRUserVariable(_, _, func) or
|
|
||||||
this = TIRTempVariable(func, _, _, _) or
|
|
||||||
this = TIRStringLiteral(func, _, _, _) or
|
|
||||||
this = TIRDynamicInitializationFlag(func, _, _)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Gets a textual representation of this element. */
|
/** Gets a textual representation of this element. */
|
||||||
string toString() { none() }
|
abstract string toString();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if this variable's value cannot be changed within a function. Currently used for string
|
* Holds if this variable's value cannot be changed within a function. Currently used for string
|
||||||
@@ -49,13 +42,13 @@ class IRVariable extends TIRVariable {
|
|||||||
/**
|
/**
|
||||||
* Gets the type of the variable.
|
* Gets the type of the variable.
|
||||||
*/
|
*/
|
||||||
Language::LanguageType getLanguageType() { none() }
|
abstract Language::LanguageType getLanguageType();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the AST node that declared this variable, or that introduced this
|
* Gets the AST node that declared this variable, or that introduced this
|
||||||
* variable as part of the AST-to-IR translation.
|
* variable as part of the AST-to-IR translation.
|
||||||
*/
|
*/
|
||||||
Language::AST getAst() { none() }
|
abstract Language::AST getAst();
|
||||||
|
|
||||||
/** DEPRECATED: Alias for getAst */
|
/** DEPRECATED: Alias for getAst */
|
||||||
deprecated Language::AST getAST() { result = this.getAst() }
|
deprecated Language::AST getAST() { result = this.getAst() }
|
||||||
@@ -64,7 +57,7 @@ class IRVariable extends TIRVariable {
|
|||||||
* Gets an identifier string for the variable. This identifier is unique
|
* Gets an identifier string for the variable. This identifier is unique
|
||||||
* within the function.
|
* within the function.
|
||||||
*/
|
*/
|
||||||
string getUniqueId() { none() }
|
abstract string getUniqueId();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the source location of this variable.
|
* Gets the source location of this variable.
|
||||||
@@ -74,7 +67,7 @@ class IRVariable extends TIRVariable {
|
|||||||
/**
|
/**
|
||||||
* Gets the IR for the function that references this variable.
|
* Gets the IR for the function that references this variable.
|
||||||
*/
|
*/
|
||||||
final IRFunction getEnclosingIRFunction() { result.getFunction() = func }
|
final IRFunction getEnclosingIRFunction() { result.getFunction() = this.getEnclosingFunction() }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the function that references this variable.
|
* Gets the function that references this variable.
|
||||||
@@ -82,10 +75,18 @@ class IRVariable extends TIRVariable {
|
|||||||
final Language::Declaration getEnclosingFunction() { result = func }
|
final Language::Declaration getEnclosingFunction() { result = func }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A variable referenced by the IR for a function.
|
||||||
|
*
|
||||||
|
* The variable may be a user-declared variable (`IRUserVariable`) or a temporary variable generated
|
||||||
|
* by the AST-to-IR translation (`IRTempVariable`).
|
||||||
|
*/
|
||||||
|
final class IRVariable = AbstractIRVariable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A user-declared variable referenced by the IR for a function.
|
* A user-declared variable referenced by the IR for a function.
|
||||||
*/
|
*/
|
||||||
class IRUserVariable extends IRVariable, TIRUserVariable {
|
class IRUserVariable extends AbstractIRVariable, TIRUserVariable {
|
||||||
Language::Variable var;
|
Language::Variable var;
|
||||||
Language::LanguageType type;
|
Language::LanguageType type;
|
||||||
|
|
||||||
@@ -114,26 +115,29 @@ class IRUserVariable extends IRVariable, TIRUserVariable {
|
|||||||
* A variable (user-declared or temporary) that is allocated on the stack. This includes all
|
* A variable (user-declared or temporary) that is allocated on the stack. This includes all
|
||||||
* parameters, non-static local variables, and temporary variables.
|
* parameters, non-static local variables, and temporary variables.
|
||||||
*/
|
*/
|
||||||
class IRAutomaticVariable extends IRVariable {
|
abstract private class AbstractIRAutomaticVariable extends AbstractIRVariable { }
|
||||||
IRAutomaticVariable() {
|
|
||||||
exists(Language::Variable var |
|
/**
|
||||||
this = TIRUserVariable(var, _, func) and
|
* A variable (user-declared or temporary) that is allocated on the stack. This includes all
|
||||||
Language::isVariableAutomatic(var)
|
* parameters, non-static local variables, and temporary variables.
|
||||||
)
|
*/
|
||||||
or
|
final class IRAutomaticVariable = AbstractIRAutomaticVariable;
|
||||||
this = TIRTempVariable(func, _, _, _)
|
|
||||||
}
|
/**
|
||||||
|
* A user-declared variable that is allocated on the stack. This includes all parameters and
|
||||||
|
* non-static local variables.
|
||||||
|
*/
|
||||||
|
private class AbstractIRAutomaticUserVariable extends IRUserVariable, AbstractIRAutomaticVariable {
|
||||||
|
override Language::AutomaticVariable var;
|
||||||
|
|
||||||
|
final override Language::AutomaticVariable getVariable() { result = var }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A user-declared variable that is allocated on the stack. This includes all parameters and
|
* A user-declared variable that is allocated on the stack. This includes all parameters and
|
||||||
* non-static local variables.
|
* non-static local variables.
|
||||||
*/
|
*/
|
||||||
class IRAutomaticUserVariable extends IRUserVariable, IRAutomaticVariable {
|
final class IRAutomaticUserVariable = AbstractIRAutomaticUserVariable;
|
||||||
override Language::AutomaticVariable var;
|
|
||||||
|
|
||||||
final override Language::AutomaticVariable getVariable() { result = var }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A user-declared variable that is not allocated on the stack. This includes all global variables,
|
* A user-declared variable that is not allocated on the stack. This includes all global variables,
|
||||||
@@ -151,16 +155,10 @@ class IRStaticUserVariable extends IRUserVariable {
|
|||||||
* A variable that is not user-declared. This includes temporary variables generated as part of IR
|
* A variable that is not user-declared. This includes temporary variables generated as part of IR
|
||||||
* construction, as well as string literals.
|
* construction, as well as string literals.
|
||||||
*/
|
*/
|
||||||
class IRGeneratedVariable extends IRVariable {
|
abstract private class AbstractIRGeneratedVariable extends AbstractIRVariable {
|
||||||
Language::AST ast;
|
Language::AST ast;
|
||||||
Language::LanguageType type;
|
Language::LanguageType type;
|
||||||
|
|
||||||
IRGeneratedVariable() {
|
|
||||||
this = TIRTempVariable(func, ast, _, type) or
|
|
||||||
this = TIRStringLiteral(func, ast, type, _) or
|
|
||||||
this = TIRDynamicInitializationFlag(func, ast, type)
|
|
||||||
}
|
|
||||||
|
|
||||||
final override Language::LanguageType getLanguageType() { result = type }
|
final override Language::LanguageType getLanguageType() { result = type }
|
||||||
|
|
||||||
final override Language::AST getAst() { result = ast }
|
final override Language::AST getAst() { result = ast }
|
||||||
@@ -196,12 +194,20 @@ class IRGeneratedVariable extends IRVariable {
|
|||||||
string getBaseString() { none() }
|
string getBaseString() { none() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A variable that is not user-declared. This includes temporary variables generated as part of IR
|
||||||
|
* construction, as well as string literals.
|
||||||
|
*/
|
||||||
|
final class IRGeneratedVariable = AbstractIRGeneratedVariable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A temporary variable introduced by IR construction. The most common examples are the variable
|
* A temporary variable introduced by IR construction. The most common examples are the variable
|
||||||
* generated to hold the return value of a function, or the variable generated to hold the result of
|
* generated to hold the return value of a function, or the variable generated to hold the result of
|
||||||
* a condition operator (`a ? b : c`).
|
* a condition operator (`a ? b : c`).
|
||||||
*/
|
*/
|
||||||
class IRTempVariable extends IRGeneratedVariable, IRAutomaticVariable, TIRTempVariable {
|
class IRTempVariable extends AbstractIRGeneratedVariable, AbstractIRAutomaticVariable,
|
||||||
|
TIRTempVariable
|
||||||
|
{
|
||||||
TempVariableTag tag;
|
TempVariableTag tag;
|
||||||
|
|
||||||
IRTempVariable() { this = TIRTempVariable(func, ast, tag, type) }
|
IRTempVariable() { this = TIRTempVariable(func, ast, tag, type) }
|
||||||
@@ -241,7 +247,7 @@ class IRThrowVariable extends IRTempVariable {
|
|||||||
* A temporary variable generated to hold the contents of all arguments passed to the `...` of a
|
* A temporary variable generated to hold the contents of all arguments passed to the `...` of a
|
||||||
* function that accepts a variable number of arguments.
|
* function that accepts a variable number of arguments.
|
||||||
*/
|
*/
|
||||||
class IREllipsisVariable extends IRTempVariable, IRParameter {
|
class IREllipsisVariable extends IRTempVariable, AbstractIRParameter {
|
||||||
IREllipsisVariable() { tag = EllipsisTempVar() }
|
IREllipsisVariable() { tag = EllipsisTempVar() }
|
||||||
|
|
||||||
final override string toString() { result = "#ellipsis" }
|
final override string toString() { result = "#ellipsis" }
|
||||||
@@ -252,7 +258,7 @@ class IREllipsisVariable extends IRTempVariable, IRParameter {
|
|||||||
/**
|
/**
|
||||||
* A temporary variable generated to hold the `this` pointer.
|
* A temporary variable generated to hold the `this` pointer.
|
||||||
*/
|
*/
|
||||||
class IRThisVariable extends IRTempVariable, IRParameter {
|
class IRThisVariable extends IRTempVariable, AbstractIRParameter {
|
||||||
IRThisVariable() { tag = ThisTempVar() }
|
IRThisVariable() { tag = ThisTempVar() }
|
||||||
|
|
||||||
final override string toString() { result = "#this" }
|
final override string toString() { result = "#this" }
|
||||||
@@ -264,7 +270,7 @@ class IRThisVariable extends IRTempVariable, IRParameter {
|
|||||||
* A variable generated to represent the contents of a string literal. This variable acts much like
|
* A variable generated to represent the contents of a string literal. This variable acts much like
|
||||||
* a read-only global variable.
|
* a read-only global variable.
|
||||||
*/
|
*/
|
||||||
class IRStringLiteral extends IRGeneratedVariable, TIRStringLiteral {
|
class IRStringLiteral extends AbstractIRGeneratedVariable, TIRStringLiteral {
|
||||||
Language::StringLiteral literal;
|
Language::StringLiteral literal;
|
||||||
|
|
||||||
IRStringLiteral() { this = TIRStringLiteral(func, ast, type, literal) }
|
IRStringLiteral() { this = TIRStringLiteral(func, ast, type, literal) }
|
||||||
@@ -288,7 +294,7 @@ class IRStringLiteral extends IRGeneratedVariable, TIRStringLiteral {
|
|||||||
* used to model the runtime initialization of static local variables in C++, as well as static
|
* used to model the runtime initialization of static local variables in C++, as well as static
|
||||||
* fields in C#.
|
* fields in C#.
|
||||||
*/
|
*/
|
||||||
class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitializationFlag {
|
class IRDynamicInitializationFlag extends AbstractIRGeneratedVariable, TIRDynamicInitializationFlag {
|
||||||
Language::Variable var;
|
Language::Variable var;
|
||||||
|
|
||||||
IRDynamicInitializationFlag() {
|
IRDynamicInitializationFlag() {
|
||||||
@@ -314,24 +320,24 @@ class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitial
|
|||||||
* An IR variable which acts like a function parameter, including positional parameters and the
|
* An IR variable which acts like a function parameter, including positional parameters and the
|
||||||
* temporary variables generated for `this` and ellipsis parameters.
|
* temporary variables generated for `this` and ellipsis parameters.
|
||||||
*/
|
*/
|
||||||
class IRParameter extends IRAutomaticVariable {
|
abstract private class AbstractIRParameter extends AbstractIRAutomaticVariable {
|
||||||
IRParameter() {
|
|
||||||
this.(IRAutomaticUserVariable).getVariable() instanceof Language::Parameter
|
|
||||||
or
|
|
||||||
this = TIRTempVariable(_, _, ThisTempVar(), _)
|
|
||||||
or
|
|
||||||
this = TIRTempVariable(_, _, EllipsisTempVar(), _)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the zero-based index of this parameter. The `this` parameter has index -1.
|
* Gets the zero-based index of this parameter. The `this` parameter has index -1.
|
||||||
*/
|
*/
|
||||||
int getIndex() { none() }
|
int getIndex() { none() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An IR variable which acts like a function parameter, including positional parameters and the
|
||||||
|
* temporary variables generated for `this` and ellipsis parameters.
|
||||||
|
*/
|
||||||
|
final class IRParameter = AbstractIRParameter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An IR variable representing a positional parameter.
|
* An IR variable representing a positional parameter.
|
||||||
*/
|
*/
|
||||||
class IRPositionalParameter extends IRParameter, IRAutomaticUserVariable {
|
class IRPositionalParameter extends AbstractIRParameter, AbstractIRAutomaticUserVariable {
|
||||||
|
IRPositionalParameter() { this.getVariable() instanceof Language::Parameter }
|
||||||
|
|
||||||
final override int getIndex() { result = this.getVariable().(Language::Parameter).getIndex() }
|
final override int getIndex() { result = this.getVariable().(Language::Parameter).getIndex() }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -830,6 +830,12 @@ newtype TTranslatedElement =
|
|||||||
not ignoreExpr(dc)
|
not ignoreExpr(dc)
|
||||||
)
|
)
|
||||||
} or
|
} or
|
||||||
|
// The set of destructors to invoke after a handler for a `try` statement. These
|
||||||
|
// need to be special cased because the destructors need to run following an
|
||||||
|
// `ExceptionEdge`, but not following a `GotoEdge` edge.
|
||||||
|
TTranslatedDestructorsAfterHandler(Handler handler) {
|
||||||
|
exists(handler.getAnImplicitDestructorCall())
|
||||||
|
} or
|
||||||
// A precise side effect of an argument to a `Call`
|
// A precise side effect of an argument to a `Call`
|
||||||
TTranslatedArgumentExprSideEffect(Call call, Expr expr, int n, SideEffectOpcode opcode) {
|
TTranslatedArgumentExprSideEffect(Call call, Expr expr, int n, SideEffectOpcode opcode) {
|
||||||
not ignoreExpr(expr) and
|
not ignoreExpr(expr) and
|
||||||
|
|||||||
@@ -1844,9 +1844,6 @@ class TranslatedAssignExpr extends TranslatedNonConstantExpr {
|
|||||||
child = this.getRightOperand() and
|
child = this.getRightOperand() and
|
||||||
result = this.getLeftOperand().getFirstInstruction(kind)
|
result = this.getLeftOperand().getFirstInstruction(kind)
|
||||||
or
|
or
|
||||||
child = this.getRightOperand() and
|
|
||||||
result = this.getLeftOperand().getFirstInstruction(kind)
|
|
||||||
or
|
|
||||||
kind instanceof GotoEdge and
|
kind instanceof GotoEdge and
|
||||||
child = this.getLeftOperand() and
|
child = this.getLeftOperand() and
|
||||||
result = this.getInstruction(AssignmentStoreTag())
|
result = this.getInstruction(AssignmentStoreTag())
|
||||||
|
|||||||
@@ -777,6 +777,72 @@ abstract class TranslatedHandler extends TranslatedStmt {
|
|||||||
TranslatedStmt getBlock() { result = getTranslatedStmt(stmt.getBlock()) }
|
TranslatedStmt getBlock() { result = getTranslatedStmt(stmt.getBlock()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The IR translation of the destructor calls of the parent `TranslatedCatchByTypeHandler`.
|
||||||
|
*
|
||||||
|
* This object does not itself generate the destructor calls. Instead, its
|
||||||
|
* children provide the actual calls.
|
||||||
|
*/
|
||||||
|
class TranslatedDestructorsAfterHandler extends TranslatedElement,
|
||||||
|
TTranslatedDestructorsAfterHandler
|
||||||
|
{
|
||||||
|
Handler handler;
|
||||||
|
|
||||||
|
TranslatedDestructorsAfterHandler() { this = TTranslatedDestructorsAfterHandler(handler) }
|
||||||
|
|
||||||
|
override string toString() { result = "Destructor calls after handler: " + handler }
|
||||||
|
|
||||||
|
private TranslatedCall getTranslatedImplicitDestructorCall(int id) {
|
||||||
|
result.getExpr() = handler.getImplicitDestructorCall(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
override Instruction getFirstInstruction(EdgeKind kind) {
|
||||||
|
result = this.getChild(0).getFirstInstruction(kind)
|
||||||
|
}
|
||||||
|
|
||||||
|
override Handler getAst() { result = handler }
|
||||||
|
|
||||||
|
override Instruction getInstructionSuccessorInternal(InstructionTag tag, EdgeKind kind) { none() }
|
||||||
|
|
||||||
|
override TranslatedElement getChild(int id) {
|
||||||
|
result = this.getTranslatedImplicitDestructorCall(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
override predicate handlesDestructorsExplicitly() { any() }
|
||||||
|
|
||||||
|
override Declaration getFunction() { result = handler.getEnclosingFunction() }
|
||||||
|
|
||||||
|
override Instruction getChildSuccessorInternal(TranslatedElement child, EdgeKind kind) {
|
||||||
|
exists(int id | child = this.getChild(id) |
|
||||||
|
// Transition to the next child, if any.
|
||||||
|
result = this.getChild(id + 1).getFirstInstruction(kind)
|
||||||
|
or
|
||||||
|
// And otherwise go to the next handler, if any.
|
||||||
|
not exists(this.getChild(id + 1)) and
|
||||||
|
result =
|
||||||
|
getTranslatedStmt(handler)
|
||||||
|
.getParent()
|
||||||
|
.(TranslatedTryStmt)
|
||||||
|
.getNextHandler(getTranslatedStmt(handler), kind)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override TranslatedElement getLastChild() {
|
||||||
|
result =
|
||||||
|
this.getTranslatedImplicitDestructorCall(max(int id |
|
||||||
|
exists(handler.getImplicitDestructorCall(id))
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
override Instruction getALastInstructionInternal() {
|
||||||
|
result = this.getLastChild().getALastInstruction()
|
||||||
|
}
|
||||||
|
|
||||||
|
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||||
|
none()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The IR translation of a C++ `catch` block that catches an exception with a
|
* The IR translation of a C++ `catch` block that catches an exception with a
|
||||||
* specific type (e.g. `catch (const std::exception&)`).
|
* specific type (e.g. `catch (const std::exception&)`).
|
||||||
@@ -790,10 +856,14 @@ class TranslatedCatchByTypeHandler extends TranslatedHandler {
|
|||||||
resultType = getVoidType()
|
resultType = getVoidType()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override predicate handlesDestructorsExplicitly() { any() }
|
||||||
|
|
||||||
override TranslatedElement getChildInternal(int id) {
|
override TranslatedElement getChildInternal(int id) {
|
||||||
result = super.getChildInternal(id)
|
result = super.getChildInternal(id)
|
||||||
or
|
or
|
||||||
id = 0 and result = this.getParameter()
|
id = 0 and result = this.getParameter()
|
||||||
|
or
|
||||||
|
id = 1 and result = this.getDestructors()
|
||||||
}
|
}
|
||||||
|
|
||||||
override Instruction getChildSuccessorInternal(TranslatedElement child, EdgeKind kind) {
|
override Instruction getChildSuccessorInternal(TranslatedElement child, EdgeKind kind) {
|
||||||
@@ -810,7 +880,9 @@ class TranslatedCatchByTypeHandler extends TranslatedHandler {
|
|||||||
result = this.getParameter().getFirstInstruction(kind)
|
result = this.getParameter().getFirstInstruction(kind)
|
||||||
or
|
or
|
||||||
kind instanceof ExceptionEdge and
|
kind instanceof ExceptionEdge and
|
||||||
result = this.getParent().(TranslatedTryStmt).getNextHandler(this, any(GotoEdge edge))
|
if exists(this.getDestructors())
|
||||||
|
then result = this.getDestructors().getFirstInstruction(any(GotoEdge edge))
|
||||||
|
else result = this.getParent().(TranslatedTryStmt).getNextHandler(this, any(GotoEdge edge))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -822,6 +894,8 @@ class TranslatedCatchByTypeHandler extends TranslatedHandler {
|
|||||||
private TranslatedParameter getParameter() {
|
private TranslatedParameter getParameter() {
|
||||||
result = getTranslatedParameter(stmt.getParameter())
|
result = getTranslatedParameter(stmt.getParameter())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private TranslatedDestructorsAfterHandler getDestructors() { result.getAst() = stmt }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -842,9 +916,7 @@ class TranslatedCatchAnyHandler extends TranslatedHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class TranslatedIfStmt extends TranslatedStmt, ConditionContext {
|
abstract class TranslatedIfLikeStmt extends TranslatedStmt, ConditionContext {
|
||||||
override IfStmt stmt;
|
|
||||||
|
|
||||||
override Instruction getFirstInstruction(EdgeKind kind) {
|
override Instruction getFirstInstruction(EdgeKind kind) {
|
||||||
if this.hasInitialization()
|
if this.hasInitialization()
|
||||||
then result = this.getInitialization().getFirstInstruction(kind)
|
then result = this.getInitialization().getFirstInstruction(kind)
|
||||||
@@ -857,6 +929,8 @@ class TranslatedIfStmt extends TranslatedStmt, ConditionContext {
|
|||||||
|
|
||||||
override TranslatedElement getLastChild() { result = this.getElse() or result = this.getThen() }
|
override TranslatedElement getLastChild() { result = this.getElse() or result = this.getThen() }
|
||||||
|
|
||||||
|
override predicate handlesDestructorsExplicitly() { any() }
|
||||||
|
|
||||||
override TranslatedElement getChildInternal(int id) {
|
override TranslatedElement getChildInternal(int id) {
|
||||||
id = 0 and result = this.getInitialization()
|
id = 0 and result = this.getInitialization()
|
||||||
or
|
or
|
||||||
@@ -867,25 +941,21 @@ class TranslatedIfStmt extends TranslatedStmt, ConditionContext {
|
|||||||
id = 3 and result = this.getElse()
|
id = 3 and result = this.getElse()
|
||||||
}
|
}
|
||||||
|
|
||||||
private predicate hasInitialization() { exists(stmt.getInitialization()) }
|
abstract predicate hasInitialization();
|
||||||
|
|
||||||
private TranslatedStmt getInitialization() {
|
abstract TranslatedStmt getInitialization();
|
||||||
result = getTranslatedStmt(stmt.getInitialization())
|
|
||||||
}
|
|
||||||
|
|
||||||
private TranslatedCondition getCondition() {
|
abstract TranslatedCondition getCondition();
|
||||||
result = getTranslatedCondition(stmt.getCondition().getFullyConverted())
|
|
||||||
}
|
|
||||||
|
|
||||||
private Instruction getFirstConditionInstruction(EdgeKind kind) {
|
private Instruction getFirstConditionInstruction(EdgeKind kind) {
|
||||||
result = this.getCondition().getFirstInstruction(kind)
|
result = this.getCondition().getFirstInstruction(kind)
|
||||||
}
|
}
|
||||||
|
|
||||||
private TranslatedStmt getThen() { result = getTranslatedStmt(stmt.getThen()) }
|
abstract TranslatedStmt getThen();
|
||||||
|
|
||||||
private TranslatedStmt getElse() { result = getTranslatedStmt(stmt.getElse()) }
|
abstract TranslatedStmt getElse();
|
||||||
|
|
||||||
private predicate hasElse() { exists(stmt.getElse()) }
|
abstract predicate hasElse();
|
||||||
|
|
||||||
override Instruction getInstructionSuccessorInternal(InstructionTag tag, EdgeKind kind) { none() }
|
override Instruction getInstructionSuccessorInternal(InstructionTag tag, EdgeKind kind) { none() }
|
||||||
|
|
||||||
@@ -898,7 +968,11 @@ class TranslatedIfStmt extends TranslatedStmt, ConditionContext {
|
|||||||
child = this.getCondition() and
|
child = this.getCondition() and
|
||||||
if this.hasElse()
|
if this.hasElse()
|
||||||
then result = this.getElse().getFirstInstruction(kind)
|
then result = this.getElse().getFirstInstruction(kind)
|
||||||
|
else (
|
||||||
|
if this.hasAnImplicitDestructorCall()
|
||||||
|
then result = this.getChild(this.getFirstDestructorCallIndex()).getFirstInstruction(kind)
|
||||||
else result = this.getParent().getChildSuccessor(this, kind)
|
else result = this.getParent().getChildSuccessor(this, kind)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override Instruction getChildSuccessorInternal(TranslatedElement child, EdgeKind kind) {
|
override Instruction getChildSuccessorInternal(TranslatedElement child, EdgeKind kind) {
|
||||||
@@ -906,7 +980,24 @@ class TranslatedIfStmt extends TranslatedStmt, ConditionContext {
|
|||||||
result = this.getFirstConditionInstruction(kind)
|
result = this.getFirstConditionInstruction(kind)
|
||||||
or
|
or
|
||||||
(child = this.getThen() or child = this.getElse()) and
|
(child = this.getThen() or child = this.getElse()) and
|
||||||
|
(
|
||||||
|
if this.hasAnImplicitDestructorCall()
|
||||||
|
then result = this.getChild(this.getFirstDestructorCallIndex()).getFirstInstruction(kind)
|
||||||
|
else result = this.getParent().getChildSuccessor(this, kind)
|
||||||
|
)
|
||||||
|
or
|
||||||
|
exists(int destructorId |
|
||||||
|
destructorId >= this.getFirstDestructorCallIndex() and
|
||||||
|
child = this.getChild(destructorId) and
|
||||||
|
result = this.getChild(destructorId + 1).getFirstInstruction(kind)
|
||||||
|
)
|
||||||
|
or
|
||||||
|
exists(int lastDestructorIndex |
|
||||||
|
lastDestructorIndex =
|
||||||
|
max(int n | exists(this.getChild(n)) and n >= this.getFirstDestructorCallIndex()) and
|
||||||
|
child = this.getChild(lastDestructorIndex) and
|
||||||
result = this.getParent().getChildSuccessor(this, kind)
|
result = this.getParent().getChildSuccessor(this, kind)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||||
@@ -914,76 +1005,44 @@ class TranslatedIfStmt extends TranslatedStmt, ConditionContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class TranslatedConstExprIfStmt extends TranslatedStmt, ConditionContext {
|
class TranslatedIfStmt extends TranslatedIfLikeStmt {
|
||||||
|
override IfStmt stmt;
|
||||||
|
|
||||||
|
override predicate hasInitialization() { exists(stmt.getInitialization()) }
|
||||||
|
|
||||||
|
override TranslatedStmt getInitialization() {
|
||||||
|
result = getTranslatedStmt(stmt.getInitialization())
|
||||||
|
}
|
||||||
|
|
||||||
|
override TranslatedCondition getCondition() {
|
||||||
|
result = getTranslatedCondition(stmt.getCondition().getFullyConverted())
|
||||||
|
}
|
||||||
|
|
||||||
|
override TranslatedStmt getThen() { result = getTranslatedStmt(stmt.getThen()) }
|
||||||
|
|
||||||
|
override TranslatedStmt getElse() { result = getTranslatedStmt(stmt.getElse()) }
|
||||||
|
|
||||||
|
override predicate hasElse() { exists(stmt.getElse()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
class TranslatedConstExprIfStmt extends TranslatedIfLikeStmt {
|
||||||
override ConstexprIfStmt stmt;
|
override ConstexprIfStmt stmt;
|
||||||
|
|
||||||
override Instruction getFirstInstruction(EdgeKind kind) {
|
override predicate hasInitialization() { exists(stmt.getInitialization()) }
|
||||||
if this.hasInitialization()
|
|
||||||
then result = this.getInitialization().getFirstInstruction(kind)
|
|
||||||
else result = this.getFirstConditionInstruction(kind)
|
|
||||||
}
|
|
||||||
|
|
||||||
override TranslatedElement getChildInternal(int id) {
|
override TranslatedStmt getInitialization() {
|
||||||
id = 0 and result = this.getInitialization()
|
|
||||||
or
|
|
||||||
id = 1 and result = this.getCondition()
|
|
||||||
or
|
|
||||||
id = 2 and result = this.getThen()
|
|
||||||
or
|
|
||||||
id = 3 and result = this.getElse()
|
|
||||||
}
|
|
||||||
|
|
||||||
private predicate hasInitialization() { exists(stmt.getInitialization()) }
|
|
||||||
|
|
||||||
private TranslatedStmt getInitialization() {
|
|
||||||
result = getTranslatedStmt(stmt.getInitialization())
|
result = getTranslatedStmt(stmt.getInitialization())
|
||||||
}
|
}
|
||||||
|
|
||||||
private TranslatedCondition getCondition() {
|
override TranslatedCondition getCondition() {
|
||||||
result = getTranslatedCondition(stmt.getCondition().getFullyConverted())
|
result = getTranslatedCondition(stmt.getCondition().getFullyConverted())
|
||||||
}
|
}
|
||||||
|
|
||||||
private Instruction getFirstConditionInstruction(EdgeKind kind) {
|
override TranslatedStmt getThen() { result = getTranslatedStmt(stmt.getThen()) }
|
||||||
result = this.getCondition().getFirstInstruction(kind)
|
|
||||||
}
|
|
||||||
|
|
||||||
private TranslatedStmt getThen() { result = getTranslatedStmt(stmt.getThen()) }
|
override TranslatedStmt getElse() { result = getTranslatedStmt(stmt.getElse()) }
|
||||||
|
|
||||||
private TranslatedStmt getElse() { result = getTranslatedStmt(stmt.getElse()) }
|
override predicate hasElse() { exists(stmt.getElse()) }
|
||||||
|
|
||||||
private predicate hasElse() { exists(stmt.getElse()) }
|
|
||||||
|
|
||||||
override Instruction getInstructionSuccessorInternal(InstructionTag tag, EdgeKind kind) { none() }
|
|
||||||
|
|
||||||
override Instruction getChildTrueSuccessor(TranslatedCondition child, EdgeKind kind) {
|
|
||||||
child = this.getCondition() and
|
|
||||||
result = this.getThen().getFirstInstruction(kind)
|
|
||||||
}
|
|
||||||
|
|
||||||
override Instruction getChildFalseSuccessor(TranslatedCondition child, EdgeKind kind) {
|
|
||||||
child = this.getCondition() and
|
|
||||||
if this.hasElse()
|
|
||||||
then result = this.getElse().getFirstInstruction(kind)
|
|
||||||
else result = this.getParent().getChildSuccessor(this, kind)
|
|
||||||
}
|
|
||||||
|
|
||||||
override Instruction getChildSuccessorInternal(TranslatedElement child, EdgeKind kind) {
|
|
||||||
child = this.getInitialization() and
|
|
||||||
result = this.getFirstConditionInstruction(kind)
|
|
||||||
or
|
|
||||||
(child = this.getThen() or child = this.getElse()) and
|
|
||||||
result = this.getParent().getChildSuccessor(this, kind)
|
|
||||||
}
|
|
||||||
|
|
||||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
|
||||||
none()
|
|
||||||
}
|
|
||||||
|
|
||||||
override Instruction getALastInstructionInternal() {
|
|
||||||
result = this.getThen().getALastInstruction()
|
|
||||||
or
|
|
||||||
result = this.getElse().getALastInstruction()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class TranslatedLoop extends TranslatedStmt, ConditionContext {
|
abstract class TranslatedLoop extends TranslatedStmt, ConditionContext {
|
||||||
@@ -1163,6 +1222,8 @@ class TranslatedForStmt extends TranslatedLoop {
|
|||||||
class TranslatedRangeBasedForStmt extends TranslatedStmt, ConditionContext {
|
class TranslatedRangeBasedForStmt extends TranslatedStmt, ConditionContext {
|
||||||
override RangeBasedForStmt stmt;
|
override RangeBasedForStmt stmt;
|
||||||
|
|
||||||
|
override predicate handlesDestructorsExplicitly() { any() }
|
||||||
|
|
||||||
override TranslatedElement getChildInternal(int id) {
|
override TranslatedElement getChildInternal(int id) {
|
||||||
id = 0 and result = this.getInitialization()
|
id = 0 and result = this.getInitialization()
|
||||||
or
|
or
|
||||||
@@ -1216,6 +1277,19 @@ class TranslatedRangeBasedForStmt extends TranslatedStmt, ConditionContext {
|
|||||||
or
|
or
|
||||||
child = this.getUpdate() and
|
child = this.getUpdate() and
|
||||||
result = this.getCondition().getFirstInstruction(kind)
|
result = this.getCondition().getFirstInstruction(kind)
|
||||||
|
or
|
||||||
|
exists(int destructorId |
|
||||||
|
destructorId >= this.getFirstDestructorCallIndex() and
|
||||||
|
child = this.getChild(destructorId) and
|
||||||
|
result = this.getChild(destructorId + 1).getFirstInstruction(kind)
|
||||||
|
)
|
||||||
|
or
|
||||||
|
exists(int lastDestructorIndex |
|
||||||
|
lastDestructorIndex =
|
||||||
|
max(int n | exists(this.getChild(n)) and n >= this.getFirstDestructorCallIndex()) and
|
||||||
|
child = this.getChild(lastDestructorIndex) and
|
||||||
|
result = this.getParent().getChildSuccessor(this, kind)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||||
@@ -1231,7 +1305,9 @@ class TranslatedRangeBasedForStmt extends TranslatedStmt, ConditionContext {
|
|||||||
|
|
||||||
override Instruction getChildFalseSuccessor(TranslatedCondition child, EdgeKind kind) {
|
override Instruction getChildFalseSuccessor(TranslatedCondition child, EdgeKind kind) {
|
||||||
child = this.getCondition() and
|
child = this.getCondition() and
|
||||||
result = this.getParent().getChildSuccessor(this, kind)
|
if this.hasAnImplicitDestructorCall()
|
||||||
|
then result = this.getChild(this.getFirstDestructorCallIndex()).getFirstInstruction(kind)
|
||||||
|
else result = this.getParent().getChildSuccessor(this, kind)
|
||||||
}
|
}
|
||||||
|
|
||||||
private TranslatedDeclStmt getRangeVariableDeclStmt() {
|
private TranslatedDeclStmt getRangeVariableDeclStmt() {
|
||||||
@@ -1276,6 +1352,11 @@ class TranslatedJumpStmt extends TranslatedStmt {
|
|||||||
override JumpStmt stmt;
|
override JumpStmt stmt;
|
||||||
|
|
||||||
override Instruction getFirstInstruction(EdgeKind kind) {
|
override Instruction getFirstInstruction(EdgeKind kind) {
|
||||||
|
// The first instruction is a destructor call, if any.
|
||||||
|
result = this.getChildInternal(0).getFirstInstruction(kind)
|
||||||
|
or
|
||||||
|
// Otherwise, the first (and only) instruction is a `NoOp`
|
||||||
|
not exists(this.getChildInternal(0)) and
|
||||||
result = this.getInstruction(OnlyInstructionTag()) and
|
result = this.getInstruction(OnlyInstructionTag()) and
|
||||||
kind instanceof GotoEdge
|
kind instanceof GotoEdge
|
||||||
}
|
}
|
||||||
@@ -1284,7 +1365,20 @@ class TranslatedJumpStmt extends TranslatedStmt {
|
|||||||
result = this.getInstruction(OnlyInstructionTag())
|
result = this.getInstruction(OnlyInstructionTag())
|
||||||
}
|
}
|
||||||
|
|
||||||
override TranslatedElement getChildInternal(int id) { none() }
|
private TranslatedCall getTranslatedImplicitDestructorCall(int id) {
|
||||||
|
result.getExpr() = stmt.getImplicitDestructorCall(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
override TranslatedElement getLastChild() {
|
||||||
|
result =
|
||||||
|
this.getTranslatedImplicitDestructorCall(max(int id |
|
||||||
|
exists(stmt.getImplicitDestructorCall(id))
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
override TranslatedElement getChildInternal(int id) {
|
||||||
|
result = this.getTranslatedImplicitDestructorCall(id)
|
||||||
|
}
|
||||||
|
|
||||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||||
tag = OnlyInstructionTag() and
|
tag = OnlyInstructionTag() and
|
||||||
@@ -1297,7 +1391,19 @@ class TranslatedJumpStmt extends TranslatedStmt {
|
|||||||
result = getTranslatedStmt(stmt.getTarget()).getFirstInstruction(kind)
|
result = getTranslatedStmt(stmt.getTarget()).getFirstInstruction(kind)
|
||||||
}
|
}
|
||||||
|
|
||||||
override Instruction getChildSuccessorInternal(TranslatedElement child, EdgeKind kind) { none() }
|
final override predicate handlesDestructorsExplicitly() { any() }
|
||||||
|
|
||||||
|
override Instruction getChildSuccessorInternal(TranslatedElement child, EdgeKind kind) {
|
||||||
|
exists(int id | child = this.getChildInternal(id) |
|
||||||
|
// Transition to the next destructor call, if any.
|
||||||
|
result = this.getChildInternal(id + 1).getFirstInstruction(kind)
|
||||||
|
or
|
||||||
|
// And otherwise, exit this element by flowing to the target of the jump.
|
||||||
|
not exists(this.getChildInternal(id + 1)) and
|
||||||
|
kind instanceof GotoEdge and
|
||||||
|
result = this.getInstruction(OnlyInstructionTag())
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private EdgeKind getCaseEdge(SwitchCase switchCase) {
|
private EdgeKind getCaseEdge(SwitchCase switchCase) {
|
||||||
|
|||||||
@@ -17,18 +17,11 @@ private import Imports::IRType
|
|||||||
* The variable may be a user-declared variable (`IRUserVariable`) or a temporary variable generated
|
* The variable may be a user-declared variable (`IRUserVariable`) or a temporary variable generated
|
||||||
* by the AST-to-IR translation (`IRTempVariable`).
|
* by the AST-to-IR translation (`IRTempVariable`).
|
||||||
*/
|
*/
|
||||||
class IRVariable extends TIRVariable {
|
abstract private class AbstractIRVariable extends TIRVariable {
|
||||||
Language::Declaration func;
|
Language::Declaration func;
|
||||||
|
|
||||||
IRVariable() {
|
|
||||||
this = TIRUserVariable(_, _, func) or
|
|
||||||
this = TIRTempVariable(func, _, _, _) or
|
|
||||||
this = TIRStringLiteral(func, _, _, _) or
|
|
||||||
this = TIRDynamicInitializationFlag(func, _, _)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Gets a textual representation of this element. */
|
/** Gets a textual representation of this element. */
|
||||||
string toString() { none() }
|
abstract string toString();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if this variable's value cannot be changed within a function. Currently used for string
|
* Holds if this variable's value cannot be changed within a function. Currently used for string
|
||||||
@@ -49,13 +42,13 @@ class IRVariable extends TIRVariable {
|
|||||||
/**
|
/**
|
||||||
* Gets the type of the variable.
|
* Gets the type of the variable.
|
||||||
*/
|
*/
|
||||||
Language::LanguageType getLanguageType() { none() }
|
abstract Language::LanguageType getLanguageType();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the AST node that declared this variable, or that introduced this
|
* Gets the AST node that declared this variable, or that introduced this
|
||||||
* variable as part of the AST-to-IR translation.
|
* variable as part of the AST-to-IR translation.
|
||||||
*/
|
*/
|
||||||
Language::AST getAst() { none() }
|
abstract Language::AST getAst();
|
||||||
|
|
||||||
/** DEPRECATED: Alias for getAst */
|
/** DEPRECATED: Alias for getAst */
|
||||||
deprecated Language::AST getAST() { result = this.getAst() }
|
deprecated Language::AST getAST() { result = this.getAst() }
|
||||||
@@ -64,7 +57,7 @@ class IRVariable extends TIRVariable {
|
|||||||
* Gets an identifier string for the variable. This identifier is unique
|
* Gets an identifier string for the variable. This identifier is unique
|
||||||
* within the function.
|
* within the function.
|
||||||
*/
|
*/
|
||||||
string getUniqueId() { none() }
|
abstract string getUniqueId();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the source location of this variable.
|
* Gets the source location of this variable.
|
||||||
@@ -74,7 +67,7 @@ class IRVariable extends TIRVariable {
|
|||||||
/**
|
/**
|
||||||
* Gets the IR for the function that references this variable.
|
* Gets the IR for the function that references this variable.
|
||||||
*/
|
*/
|
||||||
final IRFunction getEnclosingIRFunction() { result.getFunction() = func }
|
final IRFunction getEnclosingIRFunction() { result.getFunction() = this.getEnclosingFunction() }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the function that references this variable.
|
* Gets the function that references this variable.
|
||||||
@@ -82,10 +75,18 @@ class IRVariable extends TIRVariable {
|
|||||||
final Language::Declaration getEnclosingFunction() { result = func }
|
final Language::Declaration getEnclosingFunction() { result = func }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A variable referenced by the IR for a function.
|
||||||
|
*
|
||||||
|
* The variable may be a user-declared variable (`IRUserVariable`) or a temporary variable generated
|
||||||
|
* by the AST-to-IR translation (`IRTempVariable`).
|
||||||
|
*/
|
||||||
|
final class IRVariable = AbstractIRVariable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A user-declared variable referenced by the IR for a function.
|
* A user-declared variable referenced by the IR for a function.
|
||||||
*/
|
*/
|
||||||
class IRUserVariable extends IRVariable, TIRUserVariable {
|
class IRUserVariable extends AbstractIRVariable, TIRUserVariable {
|
||||||
Language::Variable var;
|
Language::Variable var;
|
||||||
Language::LanguageType type;
|
Language::LanguageType type;
|
||||||
|
|
||||||
@@ -114,26 +115,29 @@ class IRUserVariable extends IRVariable, TIRUserVariable {
|
|||||||
* A variable (user-declared or temporary) that is allocated on the stack. This includes all
|
* A variable (user-declared or temporary) that is allocated on the stack. This includes all
|
||||||
* parameters, non-static local variables, and temporary variables.
|
* parameters, non-static local variables, and temporary variables.
|
||||||
*/
|
*/
|
||||||
class IRAutomaticVariable extends IRVariable {
|
abstract private class AbstractIRAutomaticVariable extends AbstractIRVariable { }
|
||||||
IRAutomaticVariable() {
|
|
||||||
exists(Language::Variable var |
|
/**
|
||||||
this = TIRUserVariable(var, _, func) and
|
* A variable (user-declared or temporary) that is allocated on the stack. This includes all
|
||||||
Language::isVariableAutomatic(var)
|
* parameters, non-static local variables, and temporary variables.
|
||||||
)
|
*/
|
||||||
or
|
final class IRAutomaticVariable = AbstractIRAutomaticVariable;
|
||||||
this = TIRTempVariable(func, _, _, _)
|
|
||||||
}
|
/**
|
||||||
|
* A user-declared variable that is allocated on the stack. This includes all parameters and
|
||||||
|
* non-static local variables.
|
||||||
|
*/
|
||||||
|
private class AbstractIRAutomaticUserVariable extends IRUserVariable, AbstractIRAutomaticVariable {
|
||||||
|
override Language::AutomaticVariable var;
|
||||||
|
|
||||||
|
final override Language::AutomaticVariable getVariable() { result = var }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A user-declared variable that is allocated on the stack. This includes all parameters and
|
* A user-declared variable that is allocated on the stack. This includes all parameters and
|
||||||
* non-static local variables.
|
* non-static local variables.
|
||||||
*/
|
*/
|
||||||
class IRAutomaticUserVariable extends IRUserVariable, IRAutomaticVariable {
|
final class IRAutomaticUserVariable = AbstractIRAutomaticUserVariable;
|
||||||
override Language::AutomaticVariable var;
|
|
||||||
|
|
||||||
final override Language::AutomaticVariable getVariable() { result = var }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A user-declared variable that is not allocated on the stack. This includes all global variables,
|
* A user-declared variable that is not allocated on the stack. This includes all global variables,
|
||||||
@@ -151,16 +155,10 @@ class IRStaticUserVariable extends IRUserVariable {
|
|||||||
* A variable that is not user-declared. This includes temporary variables generated as part of IR
|
* A variable that is not user-declared. This includes temporary variables generated as part of IR
|
||||||
* construction, as well as string literals.
|
* construction, as well as string literals.
|
||||||
*/
|
*/
|
||||||
class IRGeneratedVariable extends IRVariable {
|
abstract private class AbstractIRGeneratedVariable extends AbstractIRVariable {
|
||||||
Language::AST ast;
|
Language::AST ast;
|
||||||
Language::LanguageType type;
|
Language::LanguageType type;
|
||||||
|
|
||||||
IRGeneratedVariable() {
|
|
||||||
this = TIRTempVariable(func, ast, _, type) or
|
|
||||||
this = TIRStringLiteral(func, ast, type, _) or
|
|
||||||
this = TIRDynamicInitializationFlag(func, ast, type)
|
|
||||||
}
|
|
||||||
|
|
||||||
final override Language::LanguageType getLanguageType() { result = type }
|
final override Language::LanguageType getLanguageType() { result = type }
|
||||||
|
|
||||||
final override Language::AST getAst() { result = ast }
|
final override Language::AST getAst() { result = ast }
|
||||||
@@ -196,12 +194,20 @@ class IRGeneratedVariable extends IRVariable {
|
|||||||
string getBaseString() { none() }
|
string getBaseString() { none() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A variable that is not user-declared. This includes temporary variables generated as part of IR
|
||||||
|
* construction, as well as string literals.
|
||||||
|
*/
|
||||||
|
final class IRGeneratedVariable = AbstractIRGeneratedVariable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A temporary variable introduced by IR construction. The most common examples are the variable
|
* A temporary variable introduced by IR construction. The most common examples are the variable
|
||||||
* generated to hold the return value of a function, or the variable generated to hold the result of
|
* generated to hold the return value of a function, or the variable generated to hold the result of
|
||||||
* a condition operator (`a ? b : c`).
|
* a condition operator (`a ? b : c`).
|
||||||
*/
|
*/
|
||||||
class IRTempVariable extends IRGeneratedVariable, IRAutomaticVariable, TIRTempVariable {
|
class IRTempVariable extends AbstractIRGeneratedVariable, AbstractIRAutomaticVariable,
|
||||||
|
TIRTempVariable
|
||||||
|
{
|
||||||
TempVariableTag tag;
|
TempVariableTag tag;
|
||||||
|
|
||||||
IRTempVariable() { this = TIRTempVariable(func, ast, tag, type) }
|
IRTempVariable() { this = TIRTempVariable(func, ast, tag, type) }
|
||||||
@@ -241,7 +247,7 @@ class IRThrowVariable extends IRTempVariable {
|
|||||||
* A temporary variable generated to hold the contents of all arguments passed to the `...` of a
|
* A temporary variable generated to hold the contents of all arguments passed to the `...` of a
|
||||||
* function that accepts a variable number of arguments.
|
* function that accepts a variable number of arguments.
|
||||||
*/
|
*/
|
||||||
class IREllipsisVariable extends IRTempVariable, IRParameter {
|
class IREllipsisVariable extends IRTempVariable, AbstractIRParameter {
|
||||||
IREllipsisVariable() { tag = EllipsisTempVar() }
|
IREllipsisVariable() { tag = EllipsisTempVar() }
|
||||||
|
|
||||||
final override string toString() { result = "#ellipsis" }
|
final override string toString() { result = "#ellipsis" }
|
||||||
@@ -252,7 +258,7 @@ class IREllipsisVariable extends IRTempVariable, IRParameter {
|
|||||||
/**
|
/**
|
||||||
* A temporary variable generated to hold the `this` pointer.
|
* A temporary variable generated to hold the `this` pointer.
|
||||||
*/
|
*/
|
||||||
class IRThisVariable extends IRTempVariable, IRParameter {
|
class IRThisVariable extends IRTempVariable, AbstractIRParameter {
|
||||||
IRThisVariable() { tag = ThisTempVar() }
|
IRThisVariable() { tag = ThisTempVar() }
|
||||||
|
|
||||||
final override string toString() { result = "#this" }
|
final override string toString() { result = "#this" }
|
||||||
@@ -264,7 +270,7 @@ class IRThisVariable extends IRTempVariable, IRParameter {
|
|||||||
* A variable generated to represent the contents of a string literal. This variable acts much like
|
* A variable generated to represent the contents of a string literal. This variable acts much like
|
||||||
* a read-only global variable.
|
* a read-only global variable.
|
||||||
*/
|
*/
|
||||||
class IRStringLiteral extends IRGeneratedVariable, TIRStringLiteral {
|
class IRStringLiteral extends AbstractIRGeneratedVariable, TIRStringLiteral {
|
||||||
Language::StringLiteral literal;
|
Language::StringLiteral literal;
|
||||||
|
|
||||||
IRStringLiteral() { this = TIRStringLiteral(func, ast, type, literal) }
|
IRStringLiteral() { this = TIRStringLiteral(func, ast, type, literal) }
|
||||||
@@ -288,7 +294,7 @@ class IRStringLiteral extends IRGeneratedVariable, TIRStringLiteral {
|
|||||||
* used to model the runtime initialization of static local variables in C++, as well as static
|
* used to model the runtime initialization of static local variables in C++, as well as static
|
||||||
* fields in C#.
|
* fields in C#.
|
||||||
*/
|
*/
|
||||||
class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitializationFlag {
|
class IRDynamicInitializationFlag extends AbstractIRGeneratedVariable, TIRDynamicInitializationFlag {
|
||||||
Language::Variable var;
|
Language::Variable var;
|
||||||
|
|
||||||
IRDynamicInitializationFlag() {
|
IRDynamicInitializationFlag() {
|
||||||
@@ -314,24 +320,24 @@ class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitial
|
|||||||
* An IR variable which acts like a function parameter, including positional parameters and the
|
* An IR variable which acts like a function parameter, including positional parameters and the
|
||||||
* temporary variables generated for `this` and ellipsis parameters.
|
* temporary variables generated for `this` and ellipsis parameters.
|
||||||
*/
|
*/
|
||||||
class IRParameter extends IRAutomaticVariable {
|
abstract private class AbstractIRParameter extends AbstractIRAutomaticVariable {
|
||||||
IRParameter() {
|
|
||||||
this.(IRAutomaticUserVariable).getVariable() instanceof Language::Parameter
|
|
||||||
or
|
|
||||||
this = TIRTempVariable(_, _, ThisTempVar(), _)
|
|
||||||
or
|
|
||||||
this = TIRTempVariable(_, _, EllipsisTempVar(), _)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the zero-based index of this parameter. The `this` parameter has index -1.
|
* Gets the zero-based index of this parameter. The `this` parameter has index -1.
|
||||||
*/
|
*/
|
||||||
int getIndex() { none() }
|
int getIndex() { none() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An IR variable which acts like a function parameter, including positional parameters and the
|
||||||
|
* temporary variables generated for `this` and ellipsis parameters.
|
||||||
|
*/
|
||||||
|
final class IRParameter = AbstractIRParameter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An IR variable representing a positional parameter.
|
* An IR variable representing a positional parameter.
|
||||||
*/
|
*/
|
||||||
class IRPositionalParameter extends IRParameter, IRAutomaticUserVariable {
|
class IRPositionalParameter extends AbstractIRParameter, AbstractIRAutomaticUserVariable {
|
||||||
|
IRPositionalParameter() { this.getVariable() instanceof Language::Parameter }
|
||||||
|
|
||||||
final override int getIndex() { result = this.getVariable().(Language::Parameter).getIndex() }
|
final override int getIndex() { result = this.getVariable().(Language::Parameter).getIndex() }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -560,7 +560,7 @@ private class IteratorAssignmentMemberOperatorModel extends IteratorAssignmentMe
|
|||||||
TaintFunction, SideEffectFunction, AliasFunction
|
TaintFunction, SideEffectFunction, AliasFunction
|
||||||
{
|
{
|
||||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||||
input.isParameterDeref(0) and
|
(input.isParameterDeref(0) or input.isParameter(0)) and
|
||||||
output.isQualifierObject()
|
output.isQualifierObject()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -579,17 +579,34 @@ private class IteratorAssignmentMemberOperatorModel extends IteratorAssignmentMe
|
|||||||
override predicate parameterEscapesOnlyViaReturn(int index) { index = -1 }
|
override predicate parameterEscapesOnlyViaReturn(int index) { index = -1 }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A `begin` member function, or a related function, that returns an iterator.
|
||||||
|
*/
|
||||||
|
class BeginFunction extends MemberFunction {
|
||||||
|
BeginFunction() {
|
||||||
|
this.hasName(["begin", "cbegin", "rbegin", "crbegin", "before_begin", "cbefore_begin"]) and
|
||||||
|
this.getType().getUnspecifiedType() instanceof Iterator
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An `end` member function, or a related function, that returns an iterator.
|
||||||
|
*/
|
||||||
|
class EndFunction extends MemberFunction {
|
||||||
|
EndFunction() {
|
||||||
|
this.hasName(["end", "cend", "rend", "crend"]) and
|
||||||
|
this.getType().getUnspecifiedType() instanceof Iterator
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A `begin` or `end` member function, or a related member function, that
|
* A `begin` or `end` member function, or a related member function, that
|
||||||
* returns an iterator.
|
* returns an iterator.
|
||||||
*/
|
*/
|
||||||
class BeginOrEndFunction extends MemberFunction {
|
class BeginOrEndFunction extends MemberFunction {
|
||||||
BeginOrEndFunction() {
|
BeginOrEndFunction() {
|
||||||
this.hasName([
|
this instanceof BeginFunction or
|
||||||
"begin", "cbegin", "rbegin", "crbegin", "end", "cend", "rend", "crend", "before_begin",
|
this instanceof EndFunction
|
||||||
"cbefore_begin"
|
|
||||||
]) and
|
|
||||||
this.getType().getUnspecifiedType() instanceof Iterator
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
description: Removed unused column from the `folders` and `files` relations
|
description: Removed unused column from the `folders` and `files` relations
|
||||||
compatibility: full
|
compatibility: full
|
||||||
files.rel: reorder files.rel (int id, string name, string simple, string ext, int fromSource) id name
|
files.rel: reorder files.rel (@file id, string name, string simple, string ext, int fromSource) id name
|
||||||
folders.rel: reorder folders.rel (int id, string name, string simple) id name
|
folders.rel: reorder folders.rel (@folder id, string name, string simple) id name
|
||||||
@@ -1,3 +1,9 @@
|
|||||||
|
## 0.9.12
|
||||||
|
|
||||||
|
### New Queries
|
||||||
|
|
||||||
|
* Added a new query, `cpp/iterator-to-expired-container`, to detect the creation of iterators owned by a temporary objects that are about to be destroyed.
|
||||||
|
|
||||||
## 0.9.11
|
## 0.9.11
|
||||||
|
|
||||||
### Minor Analysis Improvements
|
### Minor Analysis Improvements
|
||||||
|
|||||||
@@ -14,13 +14,32 @@ the program, or security vulnerabilities, by allowing an attacker to overwrite a
|
|||||||
</overview>
|
</overview>
|
||||||
<recommendation>
|
<recommendation>
|
||||||
<p>
|
<p>
|
||||||
Ensure that all execution paths deallocate the allocated memory at most once. If possible, reassign
|
Ensure that all execution paths deallocate the allocated memory at most once. In complex cases it may
|
||||||
the pointer to a null value after deallocating it. This will prevent double-free vulnerabilities since
|
help to reassign a pointer to a null value after deallocating it. This will prevent double-free vulnerabilities
|
||||||
most deallocation functions will perform a null-pointer check before attempting to deallocate the memory.
|
since most deallocation functions will perform a null-pointer check before attempting to deallocate memory.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
</recommendation>
|
</recommendation>
|
||||||
<example><sample src="DoubleFree.cpp" />
|
<example>
|
||||||
|
<p>
|
||||||
|
In the following example, <code>buff</code> is allocated and then freed twice:
|
||||||
|
</p>
|
||||||
|
<sample src="DoubleFreeBad.cpp" />
|
||||||
|
<p>
|
||||||
|
Reviewing the code above, the issue can be fixed by simply deleting the additional call to
|
||||||
|
<code>free(buff)</code>.
|
||||||
|
</p>
|
||||||
|
<sample src="DoubleFreeGood.cpp" />
|
||||||
|
<p>
|
||||||
|
In the next example, <code>task</code> may be deleted twice, if an exception occurs inside the <code>try</code>
|
||||||
|
block after the first <code>delete</code>:
|
||||||
|
</p>
|
||||||
|
<sample src="DoubleFreeBad2.cpp" />
|
||||||
|
<p>
|
||||||
|
The problem can be solved by assigning a null value to the pointer after the first <code>delete</code>, as
|
||||||
|
calling <code>delete</code> a second time on the null pointer is harmless.
|
||||||
|
</p>
|
||||||
|
<sample src="DoubleFreeGood2.cpp" />
|
||||||
</example>
|
</example>
|
||||||
<references>
|
<references>
|
||||||
|
|
||||||
|
|||||||
16
cpp/ql/src/Critical/DoubleFreeBad2.cpp
Normal file
16
cpp/ql/src/Critical/DoubleFreeBad2.cpp
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
void g() {
|
||||||
|
MyTask *task = nullptr;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
task = new MyTask;
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
delete task;
|
||||||
|
|
||||||
|
...
|
||||||
|
} catch (...) {
|
||||||
|
delete task; // BAD: potential double-free
|
||||||
|
}
|
||||||
|
}
|
||||||
7
cpp/ql/src/Critical/DoubleFreeGood.cpp
Normal file
7
cpp/ql/src/Critical/DoubleFreeGood.cpp
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
int* f() {
|
||||||
|
int *buff = malloc(SIZE*sizeof(int));
|
||||||
|
do_stuff(buff);
|
||||||
|
free(buff); // GOOD: buff is only freed once.
|
||||||
|
int *new_buffer = malloc(SIZE*sizeof(int));
|
||||||
|
return new_buffer;
|
||||||
|
}
|
||||||
17
cpp/ql/src/Critical/DoubleFreeGood2.cpp
Normal file
17
cpp/ql/src/Critical/DoubleFreeGood2.cpp
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
void g() {
|
||||||
|
MyTask *task = nullptr;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
task = new MyTask;
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
delete task;
|
||||||
|
task = nullptr;
|
||||||
|
|
||||||
|
...
|
||||||
|
} catch (...) {
|
||||||
|
delete task; // GOOD: harmless if task is NULL
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -54,6 +54,7 @@ predicate undefinedLocalUse(VariableAccess va) {
|
|||||||
// it is hard to tell when a struct or array has been initialized, so we
|
// it is hard to tell when a struct or array has been initialized, so we
|
||||||
// ignore them
|
// ignore them
|
||||||
not isAggregateType(lv.getUnderlyingType()) and
|
not isAggregateType(lv.getUnderlyingType()) and
|
||||||
|
not lv.isStatic() and // static variables are initialized to zero or null by default
|
||||||
not lv.getType().hasName("va_list") and
|
not lv.getType().hasName("va_list") and
|
||||||
va = lv.getAnAccess() and
|
va = lv.getAnAccess() and
|
||||||
noDefPath(lv, va) and
|
noDefPath(lv, va) and
|
||||||
@@ -70,7 +71,8 @@ predicate uninitialisedGlobal(GlobalVariable gv) {
|
|||||||
va = gv.getAnAccess() and
|
va = gv.getAnAccess() and
|
||||||
va.isRValue() and
|
va.isRValue() and
|
||||||
not gv.hasInitializer() and
|
not gv.hasInitializer() and
|
||||||
not gv.hasSpecifier("extern")
|
not gv.hasSpecifier("extern") and
|
||||||
|
not gv.isStatic() // static variables are initialized to zero or null by default
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ in the previous example, one solution is to make the log message a trailing argu
|
|||||||
<p>An alternative solution is to allow <code>log_with_timestamp</code> to accept format arguments:</p>
|
<p>An alternative solution is to allow <code>log_with_timestamp</code> to accept format arguments:</p>
|
||||||
<sample src="NonConstantFormat-2-good.c" />
|
<sample src="NonConstantFormat-2-good.c" />
|
||||||
<p>In this formulation, the non-constant format string to <code>printf</code> has been replaced with
|
<p>In this formulation, the non-constant format string to <code>printf</code> has been replaced with
|
||||||
a non-constant format string to <code>vprintf</code>. Semmle will no longer consider the body of
|
a non-constant format string to <code>vprintf</code>. The analysis will no longer consider the body of
|
||||||
<code>log_with_timestamp</code> to be a problem, and will instead check that every call to
|
<code>log_with_timestamp</code> to be a problem, and will instead check that every call to
|
||||||
<code>log_with_timestamp</code> passes a constant format string.</p>
|
<code>log_with_timestamp</code> passes a constant format string.</p>
|
||||||
|
|
||||||
|
|||||||
@@ -22,10 +22,8 @@ function.
|
|||||||
</example>
|
</example>
|
||||||
<references>
|
<references>
|
||||||
|
|
||||||
<li>cplusplus.com: <a href="http://www.tutorialspoint.com/cplusplus/cpp_functions.htm">C++ Functions</a>.</li>
|
<li>CERT C Coding Standard: <a href="https://wiki.sei.cmu.edu/confluence/display/c/FIO47-C.+Use+valid+format+strings">FIO47-C. Use valid format strings</a>.</li>
|
||||||
<li>Microsoft C Runtime Library Reference: <a href="https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/printf-printf-l-wprintf-wprintf-l">printf, wprintf</a>.</li>
|
<li>Microsoft C Runtime Library Reference: <a href="https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/printf-printf-l-wprintf-wprintf-l">printf, wprintf</a>.</li>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</references>
|
</references>
|
||||||
</qhelp>
|
</qhelp>
|
||||||
|
|||||||
@@ -30,11 +30,8 @@ function.
|
|||||||
</example>
|
</example>
|
||||||
<references>
|
<references>
|
||||||
|
|
||||||
<li>CERT C Coding
|
<li>CERT C Coding Standard: <a href="https://wiki.sei.cmu.edu/confluence/display/c/FIO47-C.+Use+valid+format+strings">FIO47-C. Use valid format strings</a>.</li>
|
||||||
Standard: <a href="https://www.securecoding.cert.org/confluence/display/c/FIO30-C.+Exclude+user+input+from+format+strings">FIO30-C. Exclude user input from format strings</a>.</li>
|
|
||||||
<li>cplusplus.com: <a href="http://www.tutorialspoint.com/cplusplus/cpp_functions.htm">C++ Functions</a>.</li>
|
|
||||||
<li>Microsoft C Runtime Library Reference: <a href="https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/printf-printf-l-wprintf-wprintf-l">printf, wprintf</a>.</li>
|
<li>Microsoft C Runtime Library Reference: <a href="https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/printf-printf-l-wprintf-wprintf-l">printf, wprintf</a>.</li>
|
||||||
|
|
||||||
|
|
||||||
</references>
|
</references>
|
||||||
</qhelp>
|
</qhelp>
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
int main() {
|
|
||||||
printf("%s\n", 42); //printf will treat 42 as a char*, will most likely segfault
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -15,18 +15,22 @@ the function.
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
</recommendation>
|
</recommendation>
|
||||||
<example><sample src="WrongTypeFormatArguments.cpp" />
|
<example>
|
||||||
|
|
||||||
|
<p>In the following example, the wrong format specifier is given for an integer format argument:</p>
|
||||||
|
|
||||||
|
<sample src="WrongTypeFormatArgumentsBad.cpp" />
|
||||||
|
|
||||||
|
<p>The corrected version uses <code>%i</code> as the format specifier for the integer format argument:</p>
|
||||||
|
|
||||||
|
<sample src="WrongTypeFormatArgumentsGood.cpp" />
|
||||||
|
|
||||||
</example>
|
</example>
|
||||||
<references>
|
<references>
|
||||||
|
|
||||||
<li>CERT C Coding
|
<li>Microsoft Learn: <a href="https://learn.microsoft.com/en-us/cpp/c-runtime-library/format-specification-syntax-printf-and-wprintf-functions?view=msvc-170">Format specification syntax: printf and wprintf functions</a>.</li>
|
||||||
Standard: <a href="https://www.securecoding.cert.org/confluence/display/c/FIO30-C.+Exclude+user+input+from+format+strings">FIO30-C. Exclude user input from format strings</a>.</li>
|
<li>cplusplus.com:<a href="https://cplusplus.com/reference/cstdio/printf/"></a>printf</li>
|
||||||
<li>cplusplus.com: <a href="http://www.tutorialspoint.com/cplusplus/cpp_functions.htm">C++ Functions</a>.</li>
|
<li>CERT C Coding Standard: <a href="https://wiki.sei.cmu.edu/confluence/display/c/FIO47-C.+Use+valid+format+strings">FIO47-C. Use valid format strings</a>.</li>
|
||||||
<li>CRT Alphabetical Function Reference: <a href="https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/printf-printf-l-wprintf-wprintf-l">printf, _printf_l, wprintf, _wprintf_l</a>.</li>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</references>
|
</references>
|
||||||
</qhelp>
|
</qhelp>
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
int main() {
|
||||||
|
printf("%s\n", 42); // BAD: printf will treat 42 as a char*, will most likely segfault
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
int main() {
|
||||||
|
printf("%i\n", 42); // GOOD: printf will treat 42 as an int
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
void f_warning(int i)
|
void f_warning(int i)
|
||||||
{
|
{
|
||||||
// The usage of the logical not operator in this case is unlikely to be correct
|
// BAD: the usage of the logical not operator in this case is unlikely to be correct
|
||||||
// as the output is being used as an operator for a bit-wise and operation
|
// as the output is being used as an operator for a bit-wise and operation
|
||||||
if (i & !FLAGS)
|
if (i & !FLAGS)
|
||||||
{
|
{
|
||||||
@@ -10,10 +10,9 @@ void f_warning(int i)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void f_fixed(int i)
|
void f_fixed(int i)
|
||||||
{
|
{
|
||||||
if (i & ~FLAGS) // Changing the logical not operator for the bit-wise not operator would fix this logic
|
if (i & ~FLAGS) // GOOD: Changing the logical not operator for the bit-wise not operator would fix this logic
|
||||||
{
|
{
|
||||||
// code
|
// code
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,13 @@
|
|||||||
<p>Carefully inspect the flagged expressions. Consider the intent in the code logic, and decide whether it is necessary to change the not operator.</p>
|
<p>Carefully inspect the flagged expressions. Consider the intent in the code logic, and decide whether it is necessary to change the not operator.</p>
|
||||||
</recommendation>
|
</recommendation>
|
||||||
|
|
||||||
<example><sample src="IncorrectNotOperatorUsage.cpp" /></example>
|
<example>
|
||||||
|
<p>Here is an example of this issue and how it can be fixed:</p>
|
||||||
|
|
||||||
|
<sample src="IncorrectNotOperatorUsage.cpp" />
|
||||||
|
|
||||||
|
<p>In other cases, particularly when the expressions have <code>bool</code> type, the fix may instead be of the form <code>a && !b</code>.</p>
|
||||||
|
</example>
|
||||||
|
|
||||||
<references>
|
<references>
|
||||||
<li>
|
<li>
|
||||||
|
|||||||
@@ -37,6 +37,19 @@ class AllocaCall extends FunctionCall {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets an expression associated with a dataflow node.
|
||||||
|
*/
|
||||||
|
private Expr getExpr(DataFlow::Node node) {
|
||||||
|
result = node.asInstruction().getAst()
|
||||||
|
or
|
||||||
|
result = node.asOperand().getUse().getAst()
|
||||||
|
or
|
||||||
|
result = node.(DataFlow::RawIndirectInstruction).getInstruction().getAst()
|
||||||
|
or
|
||||||
|
result = node.(DataFlow::RawIndirectOperand).getOperand().getUse().getAst()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A loop that contains an `alloca` call.
|
* A loop that contains an `alloca` call.
|
||||||
*/
|
*/
|
||||||
@@ -185,19 +198,6 @@ class LoopWithAlloca extends Stmt {
|
|||||||
not this.conditionReachesWithoutUpdate(var, this.(Loop).getCondition())
|
not this.conditionReachesWithoutUpdate(var, this.(Loop).getCondition())
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets an expression associated with a dataflow node.
|
|
||||||
*/
|
|
||||||
private Expr getExpr(DataFlow::Node node) {
|
|
||||||
result = node.asInstruction().getAst()
|
|
||||||
or
|
|
||||||
result = node.asOperand().getUse().getAst()
|
|
||||||
or
|
|
||||||
result = node.(DataFlow::RawIndirectInstruction).getInstruction().getAst()
|
|
||||||
or
|
|
||||||
result = node.(DataFlow::RawIndirectOperand).getOperand().getUse().getAst()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a definition that may be the most recent definition of the
|
* Gets a definition that may be the most recent definition of the
|
||||||
* controlling variable `var` before this loop.
|
* controlling variable `var` before this loop.
|
||||||
@@ -210,7 +210,7 @@ class LoopWithAlloca extends Stmt {
|
|||||||
// Phi nodes will be preceded by nodes that represent actual definitions
|
// Phi nodes will be preceded by nodes that represent actual definitions
|
||||||
not result instanceof DataFlow::SsaPhiNode and
|
not result instanceof DataFlow::SsaPhiNode and
|
||||||
// A source is outside the loop if it's not inside the loop
|
// A source is outside the loop if it's not inside the loop
|
||||||
not exists(Expr e | e = this.getExpr(result) | this = getAnEnclosingLoopOfExpr(e))
|
not exists(Expr e | e = getExpr(result) | this = getAnEnclosingLoopOfExpr(e))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -221,9 +221,9 @@ class LoopWithAlloca extends Stmt {
|
|||||||
private int getAControllingVarInitialValue(Variable var, DataFlow::Node source) {
|
private int getAControllingVarInitialValue(Variable var, DataFlow::Node source) {
|
||||||
source = this.getAPrecedingDef(var) and
|
source = this.getAPrecedingDef(var) and
|
||||||
(
|
(
|
||||||
result = this.getExpr(source).getValue().toInt()
|
result = getExpr(source).getValue().toInt()
|
||||||
or
|
or
|
||||||
result = this.getExpr(source).(Assignment).getRValue().getValue().toInt()
|
result = getExpr(source).(Assignment).getRValue().getValue().toInt()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ class SnprintfSizeExpr extends BufferAccess, FunctionCall {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class MemcmpSizeExpr extends BufferAccess, FunctionCall {
|
class MemcmpSizeExpr extends BufferAccess, FunctionCall {
|
||||||
MemcmpSizeExpr() { this.getTarget().hasName("Memcmp") }
|
MemcmpSizeExpr() { this.getTarget().hasName("memcmp") }
|
||||||
|
|
||||||
override Expr getPointer() {
|
override Expr getPointer() {
|
||||||
result = this.getArgument(0) or
|
result = this.getArgument(0) or
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
strncpy(dest, src, sizeof(src)); //wrong: size of dest should be used
|
|
||||||
strncpy(dest, src, strlen(src)); //wrong: size of dest should be used
|
|
||||||
@@ -12,14 +12,20 @@ or equal to the size of the destination buffer. Calls of the form <code>strncpy(
|
|||||||
not the source buffer.</p>
|
not the source buffer.</p>
|
||||||
|
|
||||||
</recommendation>
|
</recommendation>
|
||||||
<example><sample src="StrncpyFlippedArgs.cpp" />
|
|
||||||
|
|
||||||
|
<example>
|
||||||
|
<p>In the following examples, the size of the source buffer is incorrectly used as a parameter to <code>strncpy</code>:</p>
|
||||||
|
|
||||||
|
<sample src="StrncpyFlippedArgsBad.cpp" />
|
||||||
|
|
||||||
|
<p>The corrected version uses the size of the destination buffer, or a variable containing the size of the destination buffer as the size parameter to <code>strncpy</code>:</p>
|
||||||
|
|
||||||
|
<sample src="StrncpyFlippedArgsGood.cpp" />
|
||||||
</example>
|
</example>
|
||||||
|
|
||||||
<references>
|
<references>
|
||||||
|
|
||||||
<li>cplusplus.com: <a href="http://www.cplusplus.com/reference/clibrary/cstring/strncpy/">strncpy</a>.</li>
|
<li>cplusplus.com: <a href="https://cplusplus.com/reference/cstring/strncpy/">strncpy</a>.</li>
|
||||||
<li>
|
<li>
|
||||||
I. Gerg. <em>An Overview and Example of the Buffer-Overflow Exploit</em>. IANewsletter vol 7 no 4. 2005.
|
I. Gerg. <em>An Overview and Example of the Buffer-Overflow Exploit</em>. IANewsletter vol 7 no 4. 2005.
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
char src[256];
|
||||||
|
char dest1[128];
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
strncpy(dest1, src, sizeof(src)); // wrong: size of dest should be used
|
||||||
|
|
||||||
|
char *dest2 = (char *)malloc(sz1 + sz2 + sz3);
|
||||||
|
strncpy(dest2, src, strlen(src)); // wrong: size of dest should be used
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
char src[256];
|
||||||
|
char dest1[128];
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
strncpy(dest1, src, sizeof(dest1)); // correct
|
||||||
|
|
||||||
|
size_t destSize = sz1 + sz2 + sz3;
|
||||||
|
char *dest2 = (char *)malloc(destSize);
|
||||||
|
strncpy(dest2, src, destSize); // correct
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
int main(int argc, char** argv) {
|
|
||||||
char *userAndFile = argv[2];
|
|
||||||
|
|
||||||
{
|
|
||||||
char fileBuffer[FILENAME_MAX] = "/home/";
|
|
||||||
char *fileName = fileBuffer;
|
|
||||||
size_t len = strlen(fileName);
|
|
||||||
strncat(fileName+len, userAndFile, FILENAME_MAX-len-1);
|
|
||||||
// BAD: a string from the user is used in a filename
|
|
||||||
fopen(fileName, "wb+");
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
char fileBuffer[FILENAME_MAX] = "/home/";
|
|
||||||
char *fileName = fileBuffer;
|
|
||||||
size_t len = strlen(fileName);
|
|
||||||
// GOOD: use a fixed file
|
|
||||||
char* fixed = "jim/file.txt";
|
|
||||||
strncat(fileName+len, fixed, FILENAME_MAX-len-1);
|
|
||||||
fopen(fileName, "wb+");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -7,32 +7,53 @@
|
|||||||
can result in sensitive information being revealed or deleted, or an attacker being able to influence
|
can result in sensitive information being revealed or deleted, or an attacker being able to influence
|
||||||
behavior by modifying unexpected files.</p>
|
behavior by modifying unexpected files.</p>
|
||||||
|
|
||||||
<p>Paths that are naively constructed from data controlled by a user may contain unexpected special characters,
|
<p>Paths that are naively constructed from data controlled by a user may be absolute paths, or may contain
|
||||||
such as "..". Such a path may potentially point to any directory on the filesystem.</p>
|
unexpected special characters such as "..". Such a path could point anywhere on the file system.</p>
|
||||||
|
|
||||||
</overview>
|
</overview>
|
||||||
<recommendation>
|
<recommendation>
|
||||||
|
|
||||||
<p>Validate user input before using it to construct a filepath. Ideally, follow these rules:</p>
|
<p>Validate user input before using it to construct a file path.</p>
|
||||||
|
|
||||||
<ul>
|
<p>Common validation methods include checking that the normalized path is relative and does not contain
|
||||||
<li>Do not allow more than a single "." character.</li>
|
any ".." components, or checking that the path is contained within a safe folder. The method you should use depends
|
||||||
<li>Do not allow directory separators such as "/" or "\" (depending on the filesystem).</li>
|
on how the path is used in the application, and whether the path should be a single path component.
|
||||||
<li>Do not rely on simply replacing problematic sequences such as "../". For example, after applying this filter to
|
</p>
|
||||||
".../...//" the resulting string would still be "../".</li>
|
|
||||||
<li>Ideally use a whitelist of known good patterns.</li>
|
<p>If the path should be a single path component (such as a file name), you can check for the existence
|
||||||
</ul>
|
of any path separators ("/" or "\"), or ".." sequences in the input, and reject the input if any are found.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Note that removing "../" sequences is <i>not</i> sufficient, since the input could still contain a path separator
|
||||||
|
followed by "..". For example, the input ".../...//" would still result in the string "../" if only "../" sequences
|
||||||
|
are removed.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>Finally, the simplest (but most restrictive) option is to use an allow list of safe patterns and make sure that
|
||||||
|
the user input matches one of these patterns.</p>
|
||||||
|
|
||||||
</recommendation>
|
</recommendation>
|
||||||
<example>
|
<example>
|
||||||
|
|
||||||
<p>In this example, a username and file are read from the arguments to main and then used to access a file in the
|
<p>In this example, a file name is read from a user and then used to access a file.
|
||||||
user's home directory. However, a malicious user could enter a filename which contains special
|
However, a malicious user could enter a file name anywhere on the file system,
|
||||||
characters. For example, the string "../../etc/passwd" will result in the code reading the file located at
|
such as "/etc/passwd" or "../../../etc/passwd".</p>
|
||||||
"/home/[user]/../../etc/passwd", which is the system's password file. This could potentially allow them to
|
|
||||||
access all the system's passwords.</p>
|
|
||||||
|
|
||||||
<sample src="TaintedPath.c" />
|
<sample src="examples/TaintedPath.c" />
|
||||||
|
|
||||||
|
<p>
|
||||||
|
If the input should only be a file name, you can check that it doesn't contain any path separators or ".." sequences.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<sample src="examples/TaintedPathNormalize.c" />
|
||||||
|
|
||||||
|
<p>
|
||||||
|
If the input should be within a specific directory, you can check that the resolved path
|
||||||
|
is still contained within that directory.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<sample src="examples/TaintedPathFolder.c" />
|
||||||
|
|
||||||
</example>
|
</example>
|
||||||
<references>
|
<references>
|
||||||
@@ -41,6 +62,7 @@ access all the system's passwords.</p>
|
|||||||
OWASP:
|
OWASP:
|
||||||
<a href="https://owasp.org/www-community/attacks/Path_Traversal">Path Traversal</a>.
|
<a href="https://owasp.org/www-community/attacks/Path_Traversal">Path Traversal</a>.
|
||||||
</li>
|
</li>
|
||||||
|
<li>Linux man pages: <a href="https://man7.org/linux/man-pages/man3/realpath.3.html">realpath(3)</a>.</li>
|
||||||
|
|
||||||
</references>
|
</references>
|
||||||
</qhelp>
|
</qhelp>
|
||||||
|
|||||||
10
cpp/ql/src/Security/CWE/CWE-022/examples/TaintedPath.c
Normal file
10
cpp/ql/src/Security/CWE/CWE-022/examples/TaintedPath.c
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
int main(int argc, char** argv) {
|
||||||
|
char *userAndFile = argv[2];
|
||||||
|
|
||||||
|
{
|
||||||
|
char fileBuffer[PATH_MAX];
|
||||||
|
snprintf(fileBuffer, sizeof(fileBuffer), "/home/%s", userAndFile);
|
||||||
|
// BAD: a string from the user is used in a filename
|
||||||
|
fopen(fileBuffer, "wb+");
|
||||||
|
}
|
||||||
|
}
|
||||||
28
cpp/ql/src/Security/CWE/CWE-022/examples/TaintedPathFolder.c
Normal file
28
cpp/ql/src/Security/CWE/CWE-022/examples/TaintedPathFolder.c
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
char *userAndFile = argv[2];
|
||||||
|
const char *baseDir = "/home/user/public/";
|
||||||
|
char fullPath[PATH_MAX];
|
||||||
|
|
||||||
|
// Attempt to concatenate the base directory and the user-supplied path
|
||||||
|
snprintf(fullPath, sizeof(fullPath), "%s%s", baseDir, userAndFile);
|
||||||
|
|
||||||
|
// Resolve the absolute path, normalizing any ".." or "."
|
||||||
|
char *resolvedPath = realpath(fullPath, NULL);
|
||||||
|
if (resolvedPath == NULL) {
|
||||||
|
perror("Error resolving path");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the resolved path starts with the base directory
|
||||||
|
if (strncmp(baseDir, resolvedPath, strlen(baseDir)) != 0) {
|
||||||
|
free(resolvedPath);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GOOD: Path is within the intended directory
|
||||||
|
FILE *file = fopen(resolvedPath, "wb+");
|
||||||
|
free(resolvedPath);
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
char *fileName = argv[2];
|
||||||
|
// Check for invalid sequences in the user input
|
||||||
|
if (strstr(fileName , "..") || strchr(fileName , '/') || strchr(fileName , '\\')) {
|
||||||
|
printf("Invalid filename.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
char fileBuffer[PATH_MAX];
|
||||||
|
snprintf(fileBuffer, sizeof(fileBuffer), "/home/user/files/%s", fileName);
|
||||||
|
// GOOD: We know that the filename is safe and stays within the public folder
|
||||||
|
FILE *file = fopen(fileBuffer, "wb+");
|
||||||
|
}
|
||||||
@@ -30,6 +30,12 @@ This is because the temporary container is not bound to a rvalue reference.
|
|||||||
</p>
|
</p>
|
||||||
<sample src="IteratorToExpiredContainerExtendedLifetime.cpp" />
|
<sample src="IteratorToExpiredContainerExtendedLifetime.cpp" />
|
||||||
|
|
||||||
|
<p>
|
||||||
|
To fix <code>lifetime_of_temp_not_extended</code>, consider rewriting the code so that the lifetime of the temporary object is extended.
|
||||||
|
In <code>fixed_lifetime_of_temp_not_extended</code>, the lifetime of the temporary object has been extended by storing it in an rvalue reference.
|
||||||
|
</p>
|
||||||
|
<sample src="IteratorToExpiredContainerExtendedLifetime-fixed.cpp" />
|
||||||
|
|
||||||
</example>
|
</example>
|
||||||
<references>
|
<references>
|
||||||
|
|
||||||
@@ -2,9 +2,10 @@
|
|||||||
* @name Iterator to expired container
|
* @name Iterator to expired container
|
||||||
* @description Using an iterator owned by a container whose lifetime has expired may lead to unexpected behavior.
|
* @description Using an iterator owned by a container whose lifetime has expired may lead to unexpected behavior.
|
||||||
* @kind problem
|
* @kind problem
|
||||||
* @precision high
|
* @precision medium
|
||||||
* @id cpp/iterator-to-expired-container
|
* @id cpp/iterator-to-expired-container
|
||||||
* @problem.severity warning
|
* @problem.severity warning
|
||||||
|
* @security-severity 8.8
|
||||||
* @tags reliability
|
* @tags reliability
|
||||||
* security
|
* security
|
||||||
* external/cwe/cwe-416
|
* external/cwe/cwe-416
|
||||||
@@ -61,14 +62,38 @@ DataFlow::Node getADestroyedNode(DataFlow::Node n) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
predicate destroyedToBeginSink(DataFlow::Node sink, FunctionCall fc) {
|
predicate destroyedToBeginSink(DataFlow::Node sink) {
|
||||||
exists(CallInstruction call |
|
exists(CallInstruction call |
|
||||||
call = sink.asOperand().(ThisArgumentOperand).getCall() and
|
call = sink.asOperand().(ThisArgumentOperand).getCall() and
|
||||||
fc = call.getUnconvertedResultExpression() and
|
|
||||||
call.getStaticCallTarget() instanceof BeginOrEndFunction
|
call.getStaticCallTarget() instanceof BeginOrEndFunction
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if `node1` is the node corresponding to a qualifier of a destructor
|
||||||
|
* call and `node2` is a node that is destroyed as a result of `node1` being
|
||||||
|
* destroyed.
|
||||||
|
*/
|
||||||
|
private predicate qualifierToDestroyed(DataFlow::Node node1, DataFlow::Node node2) {
|
||||||
|
tempToDestructorSink(node1, _) and
|
||||||
|
node2 = getADestroyedNode(node1)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A configuration to track flow from a destroyed node to a qualifier of
|
||||||
|
* a `begin` or `end` function call.
|
||||||
|
*
|
||||||
|
* This configuration exists to prevent a cartesian product between all sinks and
|
||||||
|
* all states in `Config::isSink`.
|
||||||
|
*/
|
||||||
|
module Config0 implements DataFlow::ConfigSig {
|
||||||
|
predicate isSource(DataFlow::Node source) { qualifierToDestroyed(_, source) }
|
||||||
|
|
||||||
|
predicate isSink(DataFlow::Node sink) { destroyedToBeginSink(sink) }
|
||||||
|
}
|
||||||
|
|
||||||
|
module Flow0 = DataFlow::Global<Config0>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A configuration to track flow from a temporary variable to the qualifier of
|
* A configuration to track flow from a temporary variable to the qualifier of
|
||||||
* a destructor call, and subsequently to a qualifier of a call to `begin` or
|
* a destructor call, and subsequently to a qualifier of a call to `begin` or
|
||||||
@@ -78,12 +103,15 @@ module Config implements DataFlow::StateConfigSig {
|
|||||||
newtype FlowState =
|
newtype FlowState =
|
||||||
additional TempToDestructor() or
|
additional TempToDestructor() or
|
||||||
additional DestroyedToBegin(DataFlow::Node n) {
|
additional DestroyedToBegin(DataFlow::Node n) {
|
||||||
exists(DataFlow::Node thisOperand |
|
any(Flow0::PathNode pn | pn.isSource()).getNode() = n
|
||||||
tempToDestructorSink(thisOperand, _) and
|
|
||||||
n = getADestroyedNode(thisOperand)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if `sink` is a qualifier to a call to `begin`, and `mid` is an
|
||||||
|
* object that is destroyed.
|
||||||
|
*/
|
||||||
|
private predicate relevant(DataFlow::Node mid, DataFlow::Node sink) { Flow0::flow(mid, sink) }
|
||||||
|
|
||||||
predicate isSource(DataFlow::Node source, FlowState state) {
|
predicate isSource(DataFlow::Node source, FlowState state) {
|
||||||
source.asInstruction().(VariableAddressInstruction).getIRVariable() instanceof IRTempVariable and
|
source.asInstruction().(VariableAddressInstruction).getIRVariable() instanceof IRTempVariable and
|
||||||
state = TempToDestructor()
|
state = TempToDestructor()
|
||||||
@@ -92,16 +120,16 @@ module Config implements DataFlow::StateConfigSig {
|
|||||||
predicate isAdditionalFlowStep(
|
predicate isAdditionalFlowStep(
|
||||||
DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2
|
DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2
|
||||||
) {
|
) {
|
||||||
tempToDestructorSink(node1, _) and
|
|
||||||
state1 = TempToDestructor() and
|
state1 = TempToDestructor() and
|
||||||
state2 = DestroyedToBegin(node2) and
|
state2 = DestroyedToBegin(node2) and
|
||||||
node2 = getADestroyedNode(node1)
|
qualifierToDestroyed(node1, node2)
|
||||||
}
|
}
|
||||||
|
|
||||||
predicate isSink(DataFlow::Node sink, FlowState state) {
|
predicate isSink(DataFlow::Node sink, FlowState state) {
|
||||||
// Note: This is a non-trivial cartesian product!
|
exists(DataFlow::Node mid |
|
||||||
// Hopefully, both of these sets are quite small in practice
|
relevant(mid, sink) and
|
||||||
destroyedToBeginSink(sink, _) and state instanceof DestroyedToBegin
|
state = DestroyedToBegin(mid)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
DataFlow::FlowFeature getAFeature() {
|
DataFlow::FlowFeature getAFeature() {
|
||||||
@@ -121,9 +149,9 @@ module Config implements DataFlow::StateConfigSig {
|
|||||||
|
|
||||||
module Flow = DataFlow::GlobalWithState<Config>;
|
module Flow = DataFlow::GlobalWithState<Config>;
|
||||||
|
|
||||||
from Flow::PathNode source, Flow::PathNode sink, FunctionCall beginOrEnd, DataFlow::Node mid
|
from Flow::PathNode source, Flow::PathNode sink, DataFlow::Node mid
|
||||||
where
|
where
|
||||||
Flow::flowPath(source, sink) and
|
Flow::flowPath(source, sink) and
|
||||||
destroyedToBeginSink(sink.getNode(), beginOrEnd) and
|
destroyedToBeginSink(sink.getNode()) and
|
||||||
sink.getState() = Config::DestroyedToBegin(mid)
|
sink.getState() = Config::DestroyedToBegin(mid)
|
||||||
select mid, "This object is destroyed before $@ is called.", beginOrEnd, beginOrEnd.toString()
|
select mid, "This object is destroyed at the end of the full-expression."
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
void fixed_lifetime_of_temp_not_extended() {
|
||||||
|
auto&& v = get_vector();
|
||||||
|
for(auto x : log_and_return_argument(v)) {
|
||||||
|
use(x); // GOOD: The lifetime of the container returned by `get_vector()` has been extended to the lifetime of `v`.
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,6 +8,12 @@
|
|||||||
When the <code>std::string</code> object is destroyed, the pointer returned by <code>c_str</code> is no
|
When the <code>std::string</code> object is destroyed, the pointer returned by <code>c_str</code> is no
|
||||||
longer valid. If the pointer is used after the <code>std::string</code> object is destroyed, then the behavior is undefined.
|
longer valid. If the pointer is used after the <code>std::string</code> object is destroyed, then the behavior is undefined.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<p>Typically, this problem occurs when a <code>std::string</code> is returned by a function call (or overloaded operator)
|
||||||
|
by value, and the result is not immediately stored in a variable by value or reference in a way that extends the lifetime of
|
||||||
|
the temporary object. The resulting temporary <code>std::string</code> object is destroyed at the end of the containing expression
|
||||||
|
statement, along with any memory returned by a call to <code>c_str</code>.
|
||||||
|
</p>
|
||||||
</overview>
|
</overview>
|
||||||
|
|
||||||
<recommendation>
|
<recommendation>
|
||||||
@@ -39,6 +45,8 @@ points to valid memory.
|
|||||||
<references>
|
<references>
|
||||||
|
|
||||||
<li><a href="https://wiki.sei.cmu.edu/confluence/display/cplusplus/MEM50-CPP.+Do+not+access+freed+memory">MEM50-CPP. Do not access freed memory</a>.</li>
|
<li><a href="https://wiki.sei.cmu.edu/confluence/display/cplusplus/MEM50-CPP.+Do+not+access+freed+memory">MEM50-CPP. Do not access freed memory</a>.</li>
|
||||||
|
<li>Microsoft Learn: <a href="https://learn.microsoft.com/en-us/cpp/cpp/temporary-objects?view=msvc-170">Temporary objects</a>.</li>
|
||||||
|
<li>cppreference.com: <a href="https://en.cppreference.com/w/cpp/language/reference_initialization#Lifetime_of_a_temporary">Lifetime of a temporary</a>.</li>
|
||||||
|
|
||||||
</references>
|
</references>
|
||||||
</qhelp>
|
</qhelp>
|
||||||
|
|||||||
@@ -23,4 +23,5 @@ where
|
|||||||
(c.getTarget() instanceof StdStringCStr or c.getTarget() instanceof StdStringData) and
|
(c.getTarget() instanceof StdStringCStr or c.getTarget() instanceof StdStringData) and
|
||||||
isTemporary(c.getQualifier().getFullyConverted())
|
isTemporary(c.getQualifier().getFullyConverted())
|
||||||
select c,
|
select c,
|
||||||
"The underlying string object is destroyed after the call to '" + c.getTarget() + "' returns."
|
"The underlying temporary string object is destroyed after the call to '" + c.getTarget() +
|
||||||
|
"' returns."
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ where
|
|||||||
outlivesFullExpr(c) and
|
outlivesFullExpr(c) and
|
||||||
not c.isFromUninstantiatedTemplate(_) and
|
not c.isFromUninstantiatedTemplate(_) and
|
||||||
isUniquePointerDerefFunction(c.getTarget()) and
|
isUniquePointerDerefFunction(c.getTarget()) and
|
||||||
|
// Exclude cases where the pointer is implicitly converted to a non-pointer type
|
||||||
|
not c.getActualType() instanceof IntegralType and
|
||||||
isTemporary(c.getQualifier().getFullyConverted())
|
isTemporary(c.getQualifier().getFullyConverted())
|
||||||
select c,
|
select c,
|
||||||
"The underlying unique pointer object is destroyed after the call to '" + c.getTarget() +
|
"The underlying unique pointer object is destroyed after the call to '" + c.getTarget() +
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
category: minorAnalysis
|
||||||
|
---
|
||||||
|
* The "Variable not initialized before use" query (`cpp/not-initialised`) no longer reports an alert on static variables.
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
category: minorAnalysis
|
||||||
|
---
|
||||||
|
* The "Use of unique pointer after lifetime ends" query (`cpp/use-of-unique-pointer-after-lifetime-ends`) no longer reports an alert when the pointer is converted to a boolean
|
||||||
4
cpp/ql/src/change-notes/2024-05-23-Version1.md
Normal file
4
cpp/ql/src/change-notes/2024-05-23-Version1.md
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
category: breaking
|
||||||
|
---
|
||||||
|
* CodeQL package management is now generally available, and all GitHub-produced CodeQL packages have had their version numbers increased to 1.0.0.
|
||||||
5
cpp/ql/src/change-notes/released/0.9.12.md
Normal file
5
cpp/ql/src/change-notes/released/0.9.12.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
## 0.9.12
|
||||||
|
|
||||||
|
### New Queries
|
||||||
|
|
||||||
|
* Added a new query, `cpp/iterator-to-expired-container`, to detect the creation of iterators owned by a temporary objects that are about to be destroyed.
|
||||||
@@ -1,2 +1,2 @@
|
|||||||
---
|
---
|
||||||
lastReleaseVersion: 0.9.11
|
lastReleaseVersion: 0.9.12
|
||||||
|
|||||||
11
cpp/ql/src/experimental/Best Practices/GuardedFree.cpp
Normal file
11
cpp/ql/src/experimental/Best Practices/GuardedFree.cpp
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
void test()
|
||||||
|
{
|
||||||
|
char *foo = malloc(100);
|
||||||
|
|
||||||
|
// BAD
|
||||||
|
if (foo)
|
||||||
|
free(foo);
|
||||||
|
|
||||||
|
// GOOD
|
||||||
|
free(foo);
|
||||||
|
}
|
||||||
18
cpp/ql/src/experimental/Best Practices/GuardedFree.qhelp
Normal file
18
cpp/ql/src/experimental/Best Practices/GuardedFree.qhelp
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<!DOCTYPE qhelp SYSTEM "qhelp.dtd">
|
||||||
|
<qhelp>
|
||||||
|
<overview>
|
||||||
|
<p>The <code>free</code> function, which deallocates heap memory, may accept a NULL pointer and take no action. Therefore, it is unnecessary to check its argument for the value of NULL before a function call to <code>free</code>. As such, these guards may hinder performance and readability.</p>
|
||||||
|
</overview>
|
||||||
|
<recommendation>
|
||||||
|
<p>A function call to <code>free</code> should not depend upon the value of its argument. Delete the <code>if</code> condition preceeding a function call to <code>free</code> when its only purpose is to check the value of the pointer to be freed.</p>
|
||||||
|
</recommendation>
|
||||||
|
<example>
|
||||||
|
<sample src = "GuardedFree.cpp" />
|
||||||
|
</example>
|
||||||
|
<references>
|
||||||
|
<li>
|
||||||
|
The Open Group Base Specifications Issue 7, 2018 Edition:
|
||||||
|
<a href="https://pubs.opengroup.org/onlinepubs/9699919799/functions/free.html">free - free allocated memory</a>
|
||||||
|
</li>
|
||||||
|
</references>
|
||||||
|
</qhelp>
|
||||||
26
cpp/ql/src/experimental/Best Practices/GuardedFree.ql
Normal file
26
cpp/ql/src/experimental/Best Practices/GuardedFree.ql
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
/**
|
||||||
|
* @name Guarded Free
|
||||||
|
* @description NULL-condition guards before function calls to the memory-deallocation
|
||||||
|
* function free(3) are unnecessary, because passing NULL to free(3) is a no-op.
|
||||||
|
* @kind problem
|
||||||
|
* @problem.severity recommendation
|
||||||
|
* @precision very-high
|
||||||
|
* @id cpp/guarded-free
|
||||||
|
* @tags maintainability
|
||||||
|
* readability
|
||||||
|
* experimental
|
||||||
|
*/
|
||||||
|
|
||||||
|
import cpp
|
||||||
|
import semmle.code.cpp.controlflow.Guards
|
||||||
|
|
||||||
|
class FreeCall extends FunctionCall {
|
||||||
|
FreeCall() { this.getTarget().hasGlobalName("free") }
|
||||||
|
}
|
||||||
|
|
||||||
|
from GuardCondition gc, FreeCall fc, Variable v, BasicBlock bb
|
||||||
|
where
|
||||||
|
gc.ensuresEq(v.getAnAccess(), 0, bb, false) and
|
||||||
|
fc.getArgument(0) = v.getAnAccess() and
|
||||||
|
bb = fc.getEnclosingStmt()
|
||||||
|
select gc, "unnecessary NULL check before call to $@", fc, "free"
|
||||||
23
cpp/ql/src/experimental/Likely Bugs/DerefNullResult.cpp
Normal file
23
cpp/ql/src/experimental/Likely Bugs/DerefNullResult.cpp
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
char * create (int arg) {
|
||||||
|
if (arg > 42) {
|
||||||
|
// this function may return NULL
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
char * r = malloc(arg);
|
||||||
|
snprintf(r, arg -1, "Hello");
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
void process(char *str) {
|
||||||
|
// str is dereferenced
|
||||||
|
if (str[0] == 'H') {
|
||||||
|
printf("Hello H\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void test(int arg) {
|
||||||
|
// first function returns a pointer that may be NULL
|
||||||
|
char *str = create(arg);
|
||||||
|
// str is not checked for nullness before being passed to process function
|
||||||
|
process(str);
|
||||||
|
}
|
||||||
26
cpp/ql/src/experimental/Likely Bugs/DerefNullResult.qhelp
Normal file
26
cpp/ql/src/experimental/Likely Bugs/DerefNullResult.qhelp
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<!DOCTYPE qhelp PUBLIC
|
||||||
|
"-//Semmle//qhelp//EN"
|
||||||
|
"qhelp.dtd">
|
||||||
|
<qhelp>
|
||||||
|
|
||||||
|
<overview>
|
||||||
|
<p>This rule finds a dereference of a function parameter, whose value comes from another function call that may return NULL, without checks in the meantime.</p>
|
||||||
|
</overview>
|
||||||
|
|
||||||
|
<recommendation>
|
||||||
|
<p>A check should be added between the return of the function which may return NULL, and its use by the function dereferencing ths pointer.</p>
|
||||||
|
</recommendation>
|
||||||
|
|
||||||
|
<example>
|
||||||
|
<sample src="DerefNullResult.cpp" />
|
||||||
|
</example>
|
||||||
|
|
||||||
|
<references>
|
||||||
|
<li>
|
||||||
|
<a href="https://www.owasp.org/index.php/Null_Dereference">
|
||||||
|
Null Dereference
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</references>
|
||||||
|
|
||||||
|
</qhelp>
|
||||||
34
cpp/ql/src/experimental/Likely Bugs/DerefNullResult.ql
Normal file
34
cpp/ql/src/experimental/Likely Bugs/DerefNullResult.ql
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
/**
|
||||||
|
* @name Null dereference from a function result
|
||||||
|
* @description A function parameter is dereferenced,
|
||||||
|
* while it comes from a function that may return NULL,
|
||||||
|
* and is not checked for nullness by the caller.
|
||||||
|
* @kind problem
|
||||||
|
* @id cpp/deref-null-result
|
||||||
|
* @problem.severity recommendation
|
||||||
|
* @tags reliability
|
||||||
|
* security
|
||||||
|
* external/cwe/cwe-476
|
||||||
|
*/
|
||||||
|
|
||||||
|
import cpp
|
||||||
|
import semmle.code.cpp.dataflow.new.DataFlow
|
||||||
|
|
||||||
|
from Function nuller, Parameter pd, FunctionCall fc, Variable v
|
||||||
|
where
|
||||||
|
mayReturnNull(nuller) and
|
||||||
|
functionDereferences(pd.getFunction(), pd.getIndex()) and
|
||||||
|
// there is a function call which will deref parameter pd
|
||||||
|
fc.getTarget() = pd.getFunction() and
|
||||||
|
// the parameter pd comes from a variable v
|
||||||
|
DataFlow::localFlow(DataFlow::exprNode(v.getAnAccess()),
|
||||||
|
DataFlow::exprNode(fc.getArgument(pd.getIndex()))) and
|
||||||
|
// this variable v was assigned by a call to the nuller function
|
||||||
|
unique( | | v.getAnAssignedValue()) = nuller.getACallToThisFunction() and
|
||||||
|
// this variable v is not accessed for an operation (check for NULLness)
|
||||||
|
not exists(VariableAccess vc |
|
||||||
|
vc.getTarget() = v and
|
||||||
|
(vc.getParent() instanceof Operation or vc.getParent() instanceof IfStmt)
|
||||||
|
)
|
||||||
|
select fc, "This function call may deref $@ when it can be NULL from $@", v, v.getName(), nuller,
|
||||||
|
nuller.getName()
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
name: codeql/cpp-queries
|
name: codeql/cpp-queries
|
||||||
version: 0.9.11
|
version: 1.0.0-dev
|
||||||
groups:
|
groups:
|
||||||
- cpp
|
- cpp
|
||||||
- queries
|
- queries
|
||||||
|
|||||||
@@ -1,55 +1,55 @@
|
|||||||
edges
|
edges
|
||||||
| test.cpp:34:10:34:12 | buf | test.cpp:34:5:34:24 | access to array | provenance | |
|
| test.cpp:34:10:34:12 | buf | test.cpp:34:5:34:24 | access to array | provenance | Config |
|
||||||
| test.cpp:35:10:35:12 | buf | test.cpp:35:5:35:22 | access to array | provenance | |
|
| test.cpp:35:10:35:12 | buf | test.cpp:35:5:35:22 | access to array | provenance | Config |
|
||||||
| test.cpp:36:10:36:12 | buf | test.cpp:36:5:36:24 | access to array | provenance | |
|
| test.cpp:36:10:36:12 | buf | test.cpp:36:5:36:24 | access to array | provenance | Config |
|
||||||
| test.cpp:39:14:39:16 | buf | test.cpp:39:9:39:19 | access to array | provenance | |
|
| test.cpp:39:14:39:16 | buf | test.cpp:39:9:39:19 | access to array | provenance | Config |
|
||||||
| test.cpp:43:14:43:16 | buf | test.cpp:43:9:43:19 | access to array | provenance | |
|
| test.cpp:43:14:43:16 | buf | test.cpp:43:9:43:19 | access to array | provenance | Config |
|
||||||
| test.cpp:48:10:48:12 | buf | test.cpp:48:5:48:24 | access to array | provenance | |
|
| test.cpp:48:10:48:12 | buf | test.cpp:48:5:48:24 | access to array | provenance | Config |
|
||||||
| test.cpp:49:10:49:12 | buf | test.cpp:49:5:49:22 | access to array | provenance | |
|
| test.cpp:49:10:49:12 | buf | test.cpp:49:5:49:22 | access to array | provenance | Config |
|
||||||
| test.cpp:50:10:50:12 | buf | test.cpp:50:5:50:24 | access to array | provenance | |
|
| test.cpp:50:10:50:12 | buf | test.cpp:50:5:50:24 | access to array | provenance | Config |
|
||||||
| test.cpp:53:14:53:16 | buf | test.cpp:53:9:53:19 | access to array | provenance | |
|
| test.cpp:53:14:53:16 | buf | test.cpp:53:9:53:19 | access to array | provenance | Config |
|
||||||
| test.cpp:57:14:57:16 | buf | test.cpp:57:9:57:19 | access to array | provenance | |
|
| test.cpp:57:14:57:16 | buf | test.cpp:57:9:57:19 | access to array | provenance | Config |
|
||||||
| test.cpp:61:14:61:16 | buf | test.cpp:61:9:61:19 | access to array | provenance | |
|
| test.cpp:61:14:61:16 | buf | test.cpp:61:9:61:19 | access to array | provenance | Config |
|
||||||
| test.cpp:70:33:70:33 | p | test.cpp:71:5:71:17 | access to array | provenance | |
|
| test.cpp:70:33:70:33 | p | test.cpp:71:5:71:17 | access to array | provenance | Config |
|
||||||
| test.cpp:70:33:70:33 | p | test.cpp:72:5:72:15 | access to array | provenance | |
|
| test.cpp:70:33:70:33 | p | test.cpp:72:5:72:15 | access to array | provenance | Config |
|
||||||
| test.cpp:76:26:76:46 | & ... | test.cpp:66:32:66:32 | p | provenance | |
|
| test.cpp:76:26:76:46 | & ... | test.cpp:66:32:66:32 | p | provenance | |
|
||||||
| test.cpp:76:32:76:34 | buf | test.cpp:76:26:76:46 | & ... | provenance | |
|
| test.cpp:76:32:76:34 | buf | test.cpp:76:26:76:46 | & ... | provenance | Config |
|
||||||
| test.cpp:77:26:77:44 | & ... | test.cpp:66:32:66:32 | p | provenance | |
|
| test.cpp:77:26:77:44 | & ... | test.cpp:66:32:66:32 | p | provenance | |
|
||||||
| test.cpp:77:32:77:34 | buf | test.cpp:77:26:77:44 | & ... | provenance | |
|
| test.cpp:77:32:77:34 | buf | test.cpp:77:26:77:44 | & ... | provenance | Config |
|
||||||
| test.cpp:79:27:79:34 | buf | test.cpp:70:33:70:33 | p | provenance | |
|
| test.cpp:79:27:79:34 | buf | test.cpp:70:33:70:33 | p | provenance | |
|
||||||
| test.cpp:79:32:79:34 | buf | test.cpp:79:27:79:34 | buf | provenance | |
|
| test.cpp:79:32:79:34 | buf | test.cpp:79:27:79:34 | buf | provenance | |
|
||||||
| test.cpp:85:21:85:36 | buf | test.cpp:87:5:87:31 | access to array | provenance | |
|
| test.cpp:85:21:85:36 | buf | test.cpp:87:5:87:31 | access to array | provenance | Config |
|
||||||
| test.cpp:85:21:85:36 | buf | test.cpp:88:5:88:27 | access to array | provenance | |
|
| test.cpp:85:21:85:36 | buf | test.cpp:88:5:88:27 | access to array | provenance | Config |
|
||||||
| test.cpp:85:34:85:36 | buf | test.cpp:85:21:85:36 | buf | provenance | |
|
| test.cpp:85:34:85:36 | buf | test.cpp:85:21:85:36 | buf | provenance | |
|
||||||
| test.cpp:96:13:96:15 | arr | test.cpp:96:13:96:18 | access to array | provenance | |
|
| test.cpp:96:13:96:15 | arr | test.cpp:96:13:96:18 | access to array | provenance | Config |
|
||||||
| test.cpp:111:17:111:19 | arr | test.cpp:111:17:111:22 | access to array | provenance | |
|
| test.cpp:111:17:111:19 | arr | test.cpp:111:17:111:22 | access to array | provenance | Config |
|
||||||
| test.cpp:111:17:111:19 | arr | test.cpp:115:35:115:40 | access to array | provenance | |
|
| test.cpp:111:17:111:19 | arr | test.cpp:115:35:115:40 | access to array | provenance | Config |
|
||||||
| test.cpp:111:17:111:19 | arr | test.cpp:119:17:119:22 | access to array | provenance | |
|
| test.cpp:111:17:111:19 | arr | test.cpp:119:17:119:22 | access to array | provenance | Config |
|
||||||
| test.cpp:115:35:115:37 | arr | test.cpp:111:17:111:22 | access to array | provenance | |
|
| test.cpp:115:35:115:37 | arr | test.cpp:111:17:111:22 | access to array | provenance | Config |
|
||||||
| test.cpp:115:35:115:37 | arr | test.cpp:115:35:115:40 | access to array | provenance | |
|
| test.cpp:115:35:115:37 | arr | test.cpp:115:35:115:40 | access to array | provenance | Config |
|
||||||
| test.cpp:115:35:115:37 | arr | test.cpp:119:17:119:22 | access to array | provenance | |
|
| test.cpp:115:35:115:37 | arr | test.cpp:119:17:119:22 | access to array | provenance | Config |
|
||||||
| test.cpp:119:17:119:19 | arr | test.cpp:111:17:111:22 | access to array | provenance | |
|
| test.cpp:119:17:119:19 | arr | test.cpp:111:17:111:22 | access to array | provenance | Config |
|
||||||
| test.cpp:119:17:119:19 | arr | test.cpp:115:35:115:40 | access to array | provenance | |
|
| test.cpp:119:17:119:19 | arr | test.cpp:115:35:115:40 | access to array | provenance | Config |
|
||||||
| test.cpp:119:17:119:19 | arr | test.cpp:119:17:119:22 | access to array | provenance | |
|
| test.cpp:119:17:119:19 | arr | test.cpp:119:17:119:22 | access to array | provenance | Config |
|
||||||
| test.cpp:128:9:128:11 | arr | test.cpp:128:9:128:14 | access to array | provenance | |
|
| test.cpp:128:9:128:11 | arr | test.cpp:128:9:128:14 | access to array | provenance | Config |
|
||||||
| test.cpp:134:25:134:27 | arr | test.cpp:136:9:136:16 | ... += ... | provenance | |
|
| test.cpp:134:25:134:27 | arr | test.cpp:136:9:136:16 | ... += ... | provenance | Config |
|
||||||
| test.cpp:136:9:136:16 | ... += ... | test.cpp:136:9:136:16 | ... += ... | provenance | |
|
| test.cpp:136:9:136:16 | ... += ... | test.cpp:136:9:136:16 | ... += ... | provenance | |
|
||||||
| test.cpp:136:9:136:16 | ... += ... | test.cpp:138:13:138:15 | arr | provenance | |
|
| test.cpp:136:9:136:16 | ... += ... | test.cpp:138:13:138:15 | arr | provenance | |
|
||||||
| test.cpp:143:18:143:21 | asdf | test.cpp:134:25:134:27 | arr | provenance | |
|
| test.cpp:143:18:143:21 | asdf | test.cpp:134:25:134:27 | arr | provenance | |
|
||||||
| test.cpp:143:18:143:21 | asdf | test.cpp:143:18:143:21 | asdf | provenance | |
|
| test.cpp:143:18:143:21 | asdf | test.cpp:143:18:143:21 | asdf | provenance | |
|
||||||
| test.cpp:146:26:146:26 | *p | test.cpp:147:4:147:9 | -- ... | provenance | |
|
| test.cpp:146:26:146:26 | *p | test.cpp:147:4:147:9 | -- ... | provenance | |
|
||||||
| test.cpp:156:12:156:14 | buf | test.cpp:156:12:156:18 | ... + ... | provenance | |
|
| test.cpp:156:12:156:14 | buf | test.cpp:156:12:156:18 | ... + ... | provenance | Config |
|
||||||
| test.cpp:156:12:156:18 | ... + ... | test.cpp:156:12:156:18 | ... + ... | provenance | |
|
| test.cpp:156:12:156:18 | ... + ... | test.cpp:156:12:156:18 | ... + ... | provenance | |
|
||||||
| test.cpp:156:12:156:18 | ... + ... | test.cpp:158:17:158:18 | *& ... | provenance | |
|
| test.cpp:156:12:156:18 | ... + ... | test.cpp:158:17:158:18 | *& ... | provenance | |
|
||||||
| test.cpp:158:17:158:18 | *& ... | test.cpp:146:26:146:26 | *p | provenance | |
|
| test.cpp:158:17:158:18 | *& ... | test.cpp:146:26:146:26 | *p | provenance | |
|
||||||
| test.cpp:218:16:218:28 | buffer | test.cpp:220:5:220:11 | access to array | provenance | |
|
| test.cpp:218:16:218:28 | buffer | test.cpp:220:5:220:11 | access to array | provenance | Config |
|
||||||
| test.cpp:218:16:218:28 | buffer | test.cpp:221:5:221:11 | access to array | provenance | |
|
| test.cpp:218:16:218:28 | buffer | test.cpp:221:5:221:11 | access to array | provenance | Config |
|
||||||
| test.cpp:218:23:218:28 | buffer | test.cpp:218:16:218:28 | buffer | provenance | |
|
| test.cpp:218:23:218:28 | buffer | test.cpp:218:16:218:28 | buffer | provenance | |
|
||||||
| test.cpp:229:17:229:29 | array | test.cpp:231:5:231:10 | access to array | provenance | |
|
| test.cpp:229:17:229:29 | array | test.cpp:231:5:231:10 | access to array | provenance | Config |
|
||||||
| test.cpp:229:17:229:29 | array | test.cpp:232:5:232:10 | access to array | provenance | |
|
| test.cpp:229:17:229:29 | array | test.cpp:232:5:232:10 | access to array | provenance | Config |
|
||||||
| test.cpp:229:25:229:29 | array | test.cpp:229:17:229:29 | array | provenance | |
|
| test.cpp:229:25:229:29 | array | test.cpp:229:17:229:29 | array | provenance | |
|
||||||
| test.cpp:245:30:245:30 | p | test.cpp:261:27:261:30 | access to array | provenance | |
|
| test.cpp:245:30:245:30 | p | test.cpp:261:27:261:30 | access to array | provenance | Config |
|
||||||
| test.cpp:245:30:245:30 | p | test.cpp:261:27:261:30 | access to array | provenance | |
|
| test.cpp:245:30:245:30 | p | test.cpp:261:27:261:30 | access to array | provenance | Config |
|
||||||
| test.cpp:274:14:274:20 | buffer3 | test.cpp:245:30:245:30 | p | provenance | |
|
| test.cpp:274:14:274:20 | buffer3 | test.cpp:245:30:245:30 | p | provenance | |
|
||||||
| test.cpp:274:14:274:20 | buffer3 | test.cpp:274:14:274:20 | buffer3 | provenance | |
|
| test.cpp:274:14:274:20 | buffer3 | test.cpp:274:14:274:20 | buffer3 | provenance | |
|
||||||
| test.cpp:277:35:277:35 | p | test.cpp:278:14:278:14 | p | provenance | |
|
| test.cpp:277:35:277:35 | p | test.cpp:278:14:278:14 | p | provenance | |
|
||||||
@@ -60,21 +60,21 @@ edges
|
|||||||
| test.cpp:286:19:286:25 | buffer2 | test.cpp:286:19:286:25 | buffer2 | provenance | |
|
| test.cpp:286:19:286:25 | buffer2 | test.cpp:286:19:286:25 | buffer2 | provenance | |
|
||||||
| test.cpp:289:19:289:25 | buffer3 | test.cpp:277:35:277:35 | p | provenance | |
|
| test.cpp:289:19:289:25 | buffer3 | test.cpp:277:35:277:35 | p | provenance | |
|
||||||
| test.cpp:289:19:289:25 | buffer3 | test.cpp:289:19:289:25 | buffer3 | provenance | |
|
| test.cpp:289:19:289:25 | buffer3 | test.cpp:289:19:289:25 | buffer3 | provenance | |
|
||||||
| test.cpp:292:25:292:27 | arr | test.cpp:299:16:299:21 | access to array | provenance | |
|
| test.cpp:292:25:292:27 | arr | test.cpp:299:16:299:21 | access to array | provenance | Config |
|
||||||
| test.cpp:292:25:292:27 | arr | test.cpp:299:16:299:21 | access to array | provenance | |
|
| test.cpp:292:25:292:27 | arr | test.cpp:299:16:299:21 | access to array | provenance | Config |
|
||||||
| test.cpp:306:20:306:23 | arr1 | test.cpp:292:25:292:27 | arr | provenance | |
|
| test.cpp:306:20:306:23 | arr1 | test.cpp:292:25:292:27 | arr | provenance | |
|
||||||
| test.cpp:306:20:306:23 | arr1 | test.cpp:306:20:306:23 | arr1 | provenance | |
|
| test.cpp:306:20:306:23 | arr1 | test.cpp:306:20:306:23 | arr1 | provenance | |
|
||||||
| test.cpp:309:20:309:23 | arr2 | test.cpp:292:25:292:27 | arr | provenance | |
|
| test.cpp:309:20:309:23 | arr2 | test.cpp:292:25:292:27 | arr | provenance | |
|
||||||
| test.cpp:309:20:309:23 | arr2 | test.cpp:309:20:309:23 | arr2 | provenance | |
|
| test.cpp:309:20:309:23 | arr2 | test.cpp:309:20:309:23 | arr2 | provenance | |
|
||||||
| test.cpp:319:13:319:27 | ... = ... | test.cpp:325:24:325:26 | end | provenance | |
|
| test.cpp:319:13:319:27 | ... = ... | test.cpp:325:24:325:26 | end | provenance | |
|
||||||
| test.cpp:319:19:319:22 | temp | test.cpp:319:19:319:27 | ... + ... | provenance | |
|
| test.cpp:319:19:319:22 | temp | test.cpp:319:19:319:27 | ... + ... | provenance | Config |
|
||||||
| test.cpp:319:19:319:22 | temp | test.cpp:324:23:324:32 | ... + ... | provenance | |
|
| test.cpp:319:19:319:22 | temp | test.cpp:324:23:324:32 | ... + ... | provenance | Config |
|
||||||
| test.cpp:319:19:319:27 | ... + ... | test.cpp:319:13:319:27 | ... = ... | provenance | |
|
| test.cpp:319:19:319:27 | ... + ... | test.cpp:319:13:319:27 | ... = ... | provenance | |
|
||||||
| test.cpp:322:13:322:27 | ... = ... | test.cpp:325:24:325:26 | end | provenance | |
|
| test.cpp:322:13:322:27 | ... = ... | test.cpp:325:24:325:26 | end | provenance | |
|
||||||
| test.cpp:322:19:322:22 | temp | test.cpp:322:19:322:27 | ... + ... | provenance | |
|
| test.cpp:322:19:322:22 | temp | test.cpp:322:19:322:27 | ... + ... | provenance | Config |
|
||||||
| test.cpp:322:19:322:22 | temp | test.cpp:324:23:324:32 | ... + ... | provenance | |
|
| test.cpp:322:19:322:22 | temp | test.cpp:324:23:324:32 | ... + ... | provenance | Config |
|
||||||
| test.cpp:322:19:322:27 | ... + ... | test.cpp:322:13:322:27 | ... = ... | provenance | |
|
| test.cpp:322:19:322:27 | ... + ... | test.cpp:322:13:322:27 | ... = ... | provenance | |
|
||||||
| test.cpp:324:23:324:26 | temp | test.cpp:324:23:324:32 | ... + ... | provenance | |
|
| test.cpp:324:23:324:26 | temp | test.cpp:324:23:324:32 | ... + ... | provenance | Config |
|
||||||
| test.cpp:324:23:324:32 | ... + ... | test.cpp:324:23:324:32 | ... + ... | provenance | |
|
| test.cpp:324:23:324:32 | ... + ... | test.cpp:324:23:324:32 | ... + ... | provenance | |
|
||||||
| test.cpp:324:23:324:32 | ... + ... | test.cpp:325:15:325:19 | temp2 | provenance | |
|
| test.cpp:324:23:324:32 | ... + ... | test.cpp:325:15:325:19 | temp2 | provenance | |
|
||||||
nodes
|
nodes
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user