mirror of
https://github.com/github/codeql.git
synced 2026-05-26 09:01:22 +02:00
Compare commits
888 Commits
mbg/csharp
...
dbartol/in
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0ad333e296 | ||
|
|
208ae192e4 | ||
|
|
86b61d712c | ||
|
|
d537f86324 | ||
|
|
9d05d94f49 | ||
|
|
ac85b6e74f | ||
|
|
0cadf4d94a | ||
|
|
b8fb4b9b0f | ||
|
|
d824695da2 | ||
|
|
7c15527300 | ||
|
|
282b5d4836 | ||
|
|
f2dff092dc | ||
|
|
d2647850d2 | ||
|
|
d146d816a9 | ||
|
|
e69e90db4a | ||
|
|
75746cbacc | ||
|
|
ee01e9ab35 | ||
|
|
2c63dbad67 | ||
|
|
f1094cd3d6 | ||
|
|
f3c7aed1f9 | ||
|
|
e00c41c6e2 | ||
|
|
a63739915d | ||
|
|
2c1c41d8a3 | ||
|
|
d47659b48e | ||
|
|
ebab6ecc30 | ||
|
|
74cc1a42d0 | ||
|
|
880f948763 | ||
|
|
406d02253d | ||
|
|
3a04e42ae0 | ||
|
|
3e9924fcd2 | ||
|
|
647bd44666 | ||
|
|
f9bb0df6a2 | ||
|
|
13c2ef8c20 | ||
|
|
f35fb13723 | ||
|
|
9d3863eccc | ||
|
|
bce1f29a7e | ||
|
|
86a06bde72 | ||
|
|
e907d685f4 | ||
|
|
ae10e6e08f | ||
|
|
3fea9e4d0b | ||
|
|
2e86bbd6cd | ||
|
|
a9e5b34ad6 | ||
|
|
d02a50a504 | ||
|
|
1d0b3d4112 | ||
|
|
3ff60e076c | ||
|
|
be8f04a997 | ||
|
|
48f889b055 | ||
|
|
8aa9207281 | ||
|
|
a13b6ed230 | ||
|
|
b3a49ab143 | ||
|
|
eec1e9ffcd | ||
|
|
7585a3862f | ||
|
|
58602927bd | ||
|
|
404ead8a18 | ||
|
|
b3ef1e9372 | ||
|
|
64f13fa08f | ||
|
|
9f798902bd | ||
|
|
e999d33332 | ||
|
|
720eed398b | ||
|
|
3376d2aa12 | ||
|
|
dffde8f8b8 | ||
|
|
08419b77af | ||
|
|
bc9942eb75 | ||
|
|
a6e9d111a5 | ||
|
|
96639c594f | ||
|
|
fe34ec1378 | ||
|
|
5de0eae992 | ||
|
|
a90f4915a7 | ||
|
|
0de4259bff | ||
|
|
ecf5591bc6 | ||
|
|
913ff201f1 | ||
|
|
11932a54e8 | ||
|
|
9bd3347a3c | ||
|
|
623f6ff701 | ||
|
|
deb43c21e6 | ||
|
|
6408d7cbbe | ||
|
|
b3b5c2c767 | ||
|
|
4b814ec71c | ||
|
|
ca8e013618 | ||
|
|
5bd530f570 | ||
|
|
b14b95cd79 | ||
|
|
abf3abdc65 | ||
|
|
bea7c43584 | ||
|
|
e8a7139020 | ||
|
|
f811436cff | ||
|
|
fbe9823a42 | ||
|
|
1454890a58 | ||
|
|
1394abcf98 | ||
|
|
1f63c5d5e4 | ||
|
|
604d5f0c71 | ||
|
|
fd43ba0827 | ||
|
|
6dcc884fe1 | ||
|
|
eefc4866ea | ||
|
|
aaeb8a0aa0 | ||
|
|
959f93a766 | ||
|
|
a391c01d36 | ||
|
|
bc106873c1 | ||
|
|
7e4f77bfb3 | ||
|
|
a6509c7a37 | ||
|
|
769f9051af | ||
|
|
6fbc8261f2 | ||
|
|
770924455e | ||
|
|
ce96f009b7 | ||
|
|
819e0dfbf0 | ||
|
|
110cb64b7a | ||
|
|
4c329b7b62 | ||
|
|
30163e4f60 | ||
|
|
c132891669 | ||
|
|
feb7c49006 | ||
|
|
32e8b130ad | ||
|
|
a9d2b936af | ||
|
|
d953ad63fe | ||
|
|
d74da30fc7 | ||
|
|
8ab3f39b5e | ||
|
|
5ff7a898a6 | ||
|
|
decd5c1ae7 | ||
|
|
54b6c6f8f7 | ||
|
|
dbfc256f40 | ||
|
|
91ce88e2d9 | ||
|
|
de1ecf943e | ||
|
|
cc608f764d | ||
|
|
dd0723c36b | ||
|
|
bdd56f1b6e | ||
|
|
8dd99b951b | ||
|
|
08557974ae | ||
|
|
04f422ea5d | ||
|
|
999b12f78b | ||
|
|
7ee7a0df34 | ||
|
|
2c93ab99d8 | ||
|
|
d814e15a2f | ||
|
|
f19e48d473 | ||
|
|
4de31e8577 | ||
|
|
f3272239bd | ||
|
|
cd9927cf66 | ||
|
|
0e5dcddb98 | ||
|
|
5c59692b3a | ||
|
|
e070bd512e | ||
|
|
194edd76e5 | ||
|
|
a7e276343e | ||
|
|
f53c31398c | ||
|
|
15bd825805 | ||
|
|
6f23111320 | ||
|
|
983b84276b | ||
|
|
1a71c3f174 | ||
|
|
5792b4d363 | ||
|
|
d96dbea7be | ||
|
|
136769d647 | ||
|
|
fcd14a78ab | ||
|
|
a5051655a1 | ||
|
|
70b85a3e00 | ||
|
|
41b2273dee | ||
|
|
e2479940d2 | ||
|
|
6a5d6eb5c2 | ||
|
|
d001cc40d3 | ||
|
|
00450d10bb | ||
|
|
58c1518fbd | ||
|
|
97462a3fa0 | ||
|
|
c57fcfb8fb | ||
|
|
705691b096 | ||
|
|
64f23ebb4d | ||
|
|
df45ba0476 | ||
|
|
f54b02edb3 | ||
|
|
0c95ab2cdc | ||
|
|
4fbc747f93 | ||
|
|
fd8f7e071b | ||
|
|
fae4a8f37b | ||
|
|
8b6047dfd1 | ||
|
|
81e71c4669 | ||
|
|
6e75df4088 | ||
|
|
060c37b6a2 | ||
|
|
163bb2b94d | ||
|
|
714b61b63e | ||
|
|
219bac74bf | ||
|
|
de84dddfc1 | ||
|
|
e0a49e2999 | ||
|
|
e44aca0b33 | ||
|
|
41a53ec109 | ||
|
|
e4837f7da9 | ||
|
|
5461f94c6c | ||
|
|
41dd63adc7 | ||
|
|
7c0e89ffdd | ||
|
|
da273269cb | ||
|
|
f53a05bf13 | ||
|
|
a0fca20f0d | ||
|
|
c380ecbbbc | ||
|
|
6c1ebd999e | ||
|
|
25e6b976c8 | ||
|
|
7512d81331 | ||
|
|
6ee231fac5 | ||
|
|
0d1be2294c | ||
|
|
8d666d00c2 | ||
|
|
9a5dbd078e | ||
|
|
8398ee43b3 | ||
|
|
a964f536e8 | ||
|
|
278f90e5fa | ||
|
|
cfe2a76431 | ||
|
|
bd47c4f9ec | ||
|
|
3734a544bc | ||
|
|
e834f9302e | ||
|
|
e80ff4efba | ||
|
|
071517c74b | ||
|
|
bfe42a656c | ||
|
|
384e7c7a80 | ||
|
|
e65d7224db | ||
|
|
eada3b91df | ||
|
|
c82b4638c6 | ||
|
|
a1fab31bfc | ||
|
|
9c3d141c9c | ||
|
|
7ab3bb1239 | ||
|
|
fe995dd99b | ||
|
|
025cd34dab | ||
|
|
2d95b6a049 | ||
|
|
c97dccf0de | ||
|
|
ab58d4c11f | ||
|
|
c67bfff33b | ||
|
|
4bfcc31ef0 | ||
|
|
59eea2a4a3 | ||
|
|
b6eeac5bc8 | ||
|
|
d9732361fc | ||
|
|
fbde174a89 | ||
|
|
50ff82fe51 | ||
|
|
7ddd08fb1e | ||
|
|
f6e7837cb8 | ||
|
|
0c900e4b8a | ||
|
|
d4269251be | ||
|
|
7b8ac609f7 | ||
|
|
c2f6ee320f | ||
|
|
e931d59bea | ||
|
|
e2acc26419 | ||
|
|
1b2ba15753 | ||
|
|
7bd0f53df9 | ||
|
|
998827f491 | ||
|
|
286a56c9b3 | ||
|
|
1e64748ffe | ||
|
|
955cc5a1be | ||
|
|
289f921171 | ||
|
|
a836444bc6 | ||
|
|
00f0879ff5 | ||
|
|
cce3728edf | ||
|
|
52e4076684 | ||
|
|
faecf44870 | ||
|
|
7f06109a4f | ||
|
|
0e45c9982b | ||
|
|
674799af8c | ||
|
|
d6712b2111 | ||
|
|
1a70297662 | ||
|
|
32a699e34a | ||
|
|
6eea906bbf | ||
|
|
64dd8b9488 | ||
|
|
746c2d1fca | ||
|
|
f07f0888aa | ||
|
|
393a0759db | ||
|
|
8aa80882ea | ||
|
|
8065714ebe | ||
|
|
eef3dc81df | ||
|
|
698dfa46fc | ||
|
|
77d9bac52d | ||
|
|
0c19da926c | ||
|
|
83569911ae | ||
|
|
ec7e486a3b | ||
|
|
159d8e978c | ||
|
|
4a6bceccf5 | ||
|
|
3640b6d3a8 | ||
|
|
711d7057f7 | ||
|
|
08c658e66b | ||
|
|
a5d229903d | ||
|
|
8d97fe9ed3 | ||
|
|
21d5fa836b | ||
|
|
ef97e539ec | ||
|
|
5ad7ed49dd | ||
|
|
730eae9521 | ||
|
|
9cf2acface | ||
|
|
cf64e0e85f | ||
|
|
7649772935 | ||
|
|
348165205c | ||
|
|
a816b813d1 | ||
|
|
d25a312557 | ||
|
|
d89b8ba446 | ||
|
|
d627358f7e | ||
|
|
079451142e | ||
|
|
67a07e98a2 | ||
|
|
b3fa844322 | ||
|
|
a82aaea514 | ||
|
|
13483be5ed | ||
|
|
250a0a71e1 | ||
|
|
3ee14f914b | ||
|
|
f0bb25bfce | ||
|
|
e8f1f364c5 | ||
|
|
cb53ff70a6 | ||
|
|
84a61d1e02 | ||
|
|
59402eb754 | ||
|
|
eadc605c0e | ||
|
|
a40a578576 | ||
|
|
2931e5dea8 | ||
|
|
03ba7ea851 | ||
|
|
7819a7d2bc | ||
|
|
5ef71f9d28 | ||
|
|
293f791611 | ||
|
|
6e744093e2 | ||
|
|
de97ae38dc | ||
|
|
38fe9b71b9 | ||
|
|
f87b307ddb | ||
|
|
bdda0f574b | ||
|
|
30ad113d4c | ||
|
|
dd3e357ad3 | ||
|
|
a731a82a10 | ||
|
|
c98e0fa0b4 | ||
|
|
8096f86224 | ||
|
|
942cd7c275 | ||
|
|
2bbeb7383f | ||
|
|
1f77f77153 | ||
|
|
c7b41ca470 | ||
|
|
55003300fe | ||
|
|
e5ce27fbf9 | ||
|
|
31fa230c0d | ||
|
|
4ee13a3234 | ||
|
|
791f5913d2 | ||
|
|
f19f7967c2 | ||
|
|
32d95834d1 | ||
|
|
55da3257bf | ||
|
|
a2248cb5a0 | ||
|
|
fadd23a2a0 | ||
|
|
060cd9fada | ||
|
|
48ca1d0b72 | ||
|
|
ae0b4970ac | ||
|
|
da43a61506 | ||
|
|
db5bd98781 | ||
|
|
820de5d36f | ||
|
|
eeb9a88c3a | ||
|
|
f680a2ecbf | ||
|
|
57c8f5111d | ||
|
|
bfd430b446 | ||
|
|
24c9a516c9 | ||
|
|
cacae957b5 | ||
|
|
3f905f2675 | ||
|
|
695160d480 | ||
|
|
f6e05836f1 | ||
|
|
2ecc8a5abe | ||
|
|
540ce1f0db | ||
|
|
882e909862 | ||
|
|
5fb5f1b23b | ||
|
|
3ea167cadf | ||
|
|
2d1088e923 | ||
|
|
0eaad4136e | ||
|
|
4d51e4fed0 | ||
|
|
10cd6328dc | ||
|
|
8ec5b5b7fa | ||
|
|
8fcf00b73d | ||
|
|
fa416564c7 | ||
|
|
d3d712fbff | ||
|
|
5f4e8e3e6a | ||
|
|
f03e90f894 | ||
|
|
839b88a4bc | ||
|
|
b606271a61 | ||
|
|
01b20b3a26 | ||
|
|
c448481bf7 | ||
|
|
12f78dbed4 | ||
|
|
cd5a46123e | ||
|
|
2d6cdff14b | ||
|
|
0ec4df28f5 | ||
|
|
3f589722c2 | ||
|
|
618b608962 | ||
|
|
9fc119cc55 | ||
|
|
6bfa08c5cc | ||
|
|
63d3b3ff2a | ||
|
|
0d6f17ec90 | ||
|
|
3bf3876c14 | ||
|
|
17c550bc88 | ||
|
|
7ab0f88f78 | ||
|
|
ebf0bb889b | ||
|
|
e5be8ab1e5 | ||
|
|
30cbc91092 | ||
|
|
6f2407412e | ||
|
|
8253f2d343 | ||
|
|
7fe1a9431c | ||
|
|
53aa34bdd3 | ||
|
|
af612a12de | ||
|
|
4363a8ea30 | ||
|
|
e65ba13da4 | ||
|
|
661160a98e | ||
|
|
ce937e78c0 | ||
|
|
22c6c53993 | ||
|
|
a247a8b3ea | ||
|
|
e68bb53a6b | ||
|
|
619266d04b | ||
|
|
ce0f2b1788 | ||
|
|
b0cb65403b | ||
|
|
4cb5bea2c6 | ||
|
|
29ee1bda50 | ||
|
|
6adc04eca0 | ||
|
|
e7f85673e9 | ||
|
|
71d0a2378b | ||
|
|
2d6f3ed6c2 | ||
|
|
c84d88f5aa | ||
|
|
8308c661b4 | ||
|
|
a63a4c29e2 | ||
|
|
5a6b94eda2 | ||
|
|
0714310661 | ||
|
|
263b208282 | ||
|
|
7ce5c0d55d | ||
|
|
c88f52c63e | ||
|
|
b6d97b07bf | ||
|
|
67e7b8fc23 | ||
|
|
5391b13db9 | ||
|
|
0f8a12f3ac | ||
|
|
94d80b0e87 | ||
|
|
86fd21de45 | ||
|
|
13bdd9c0c6 | ||
|
|
57c5d5f2c7 | ||
|
|
0c39d1e5ca | ||
|
|
2eb2e11ef7 | ||
|
|
fb57914751 | ||
|
|
858aa9ae63 | ||
|
|
cc0b8bbebb | ||
|
|
07098bf8bf | ||
|
|
2edccec693 | ||
|
|
c28f51f820 | ||
|
|
05a4fdf6d8 | ||
|
|
a4c9120a9a | ||
|
|
2c5239ff7b | ||
|
|
a7a10de9ea | ||
|
|
8d28253175 | ||
|
|
01a2e74df7 | ||
|
|
b6a9f87238 | ||
|
|
4907e5754f | ||
|
|
2a41e6ae66 | ||
|
|
4fe4dfbf83 | ||
|
|
c0cc1c3fd5 | ||
|
|
3f805d3456 | ||
|
|
137b2c9ef9 | ||
|
|
cbb2fb9968 | ||
|
|
4d3b05e041 | ||
|
|
eea02e1ac1 | ||
|
|
a39a6ea648 | ||
|
|
ce02de48a0 | ||
|
|
f2b311a008 | ||
|
|
ce6366f023 | ||
|
|
0d1e061f5a | ||
|
|
63690066c5 | ||
|
|
b054b9c5cd | ||
|
|
2288eab0fd | ||
|
|
78a802359e | ||
|
|
50935899fa | ||
|
|
05b5aea477 | ||
|
|
856b50735d | ||
|
|
d839b4038e | ||
|
|
cc75a1a97e | ||
|
|
35beadc3bb | ||
|
|
4408482699 | ||
|
|
d4e6e77200 | ||
|
|
c4cf2febfb | ||
|
|
af12affc36 | ||
|
|
0affd898de | ||
|
|
9ec479a2a0 | ||
|
|
4f0e17bf97 | ||
|
|
686f7ccd5a | ||
|
|
9731989421 | ||
|
|
51599b3cae | ||
|
|
aa09361a42 | ||
|
|
47930f94e2 | ||
|
|
d4b4d22378 | ||
|
|
d2d91cfb29 | ||
|
|
8836cbae5b | ||
|
|
d34d7e51e5 | ||
|
|
e169702165 | ||
|
|
af61b45785 | ||
|
|
d589b324c9 | ||
|
|
b342e93989 | ||
|
|
b875d30bc6 | ||
|
|
24fa2be7b3 | ||
|
|
d2d7ed7bae | ||
|
|
549fb0324b | ||
|
|
462da63970 | ||
|
|
fe5fbaa142 | ||
|
|
620c69df12 | ||
|
|
37999eaea0 | ||
|
|
f4b13e0955 | ||
|
|
87b1c3eaa8 | ||
|
|
7f96fe725b | ||
|
|
1264029282 | ||
|
|
7a55b003d2 | ||
|
|
38194c6ae7 | ||
|
|
f454151e7a | ||
|
|
3af085afcb | ||
|
|
8d9060f1f9 | ||
|
|
c715de2a10 | ||
|
|
5fdc293d82 | ||
|
|
16e817c814 | ||
|
|
4faede0e2c | ||
|
|
7f9b856a21 | ||
|
|
f649def3f8 | ||
|
|
a928f4c9ef | ||
|
|
f96d6accbb | ||
|
|
959237e8d2 | ||
|
|
2963dc1cb1 | ||
|
|
b3f92fcf0f | ||
|
|
fc9e63275f | ||
|
|
094a2b0c46 | ||
|
|
88810420b1 | ||
|
|
c460eae2e1 | ||
|
|
f6ce27075c | ||
|
|
7ce230af77 | ||
|
|
c2efb4d232 | ||
|
|
87f0f1fb33 | ||
|
|
c6dc55ef9b | ||
|
|
2f8d71762c | ||
|
|
b6ec9464eb | ||
|
|
0469df1c18 | ||
|
|
a96145a4ac | ||
|
|
d5b0ad6bfc | ||
|
|
2f797fffc1 | ||
|
|
075a83c987 | ||
|
|
8dd0bdbdb0 | ||
|
|
5845528bae | ||
|
|
3014f207f3 | ||
|
|
484f761c6d | ||
|
|
d93d22ba3e | ||
|
|
1e5b235f4b | ||
|
|
85c7116e8f | ||
|
|
04b84320c9 | ||
|
|
3906a1923b | ||
|
|
f9c724d9a3 | ||
|
|
9c202f508f | ||
|
|
7bb806563f | ||
|
|
d90d895944 | ||
|
|
2a9133aae0 | ||
|
|
d628905156 | ||
|
|
a4c075f03b | ||
|
|
b4f6d519db | ||
|
|
b36d4931f2 | ||
|
|
d2f8cb6920 | ||
|
|
31f3504113 | ||
|
|
354a12c906 | ||
|
|
1db24dd28d | ||
|
|
4e16bb65e3 | ||
|
|
16ba4652af | ||
|
|
f6b9ca3da6 | ||
|
|
da4a059388 | ||
|
|
52e6e1dece | ||
|
|
575ac46bf3 | ||
|
|
b952f619db | ||
|
|
057e810122 | ||
|
|
9d64c0a023 | ||
|
|
1e4caca23a | ||
|
|
2cb4a554ea | ||
|
|
09df318e9e | ||
|
|
a806569b5f | ||
|
|
5a8b900394 | ||
|
|
64abf5b163 | ||
|
|
b951bf0f8f | ||
|
|
a5bb093935 | ||
|
|
7437de2909 | ||
|
|
a54ca38e31 | ||
|
|
e948e22186 | ||
|
|
2203eb6607 | ||
|
|
759854991a | ||
|
|
c11218fcde | ||
|
|
899f35a59b | ||
|
|
1d4e9745a3 | ||
|
|
2591460731 | ||
|
|
9e462866a0 | ||
|
|
ba0be2fd9f | ||
|
|
4ba5059064 | ||
|
|
b01a45f7be | ||
|
|
bda46b3748 | ||
|
|
ffef81bc47 | ||
|
|
0a3f958849 | ||
|
|
7a81f06e84 | ||
|
|
cc036c188f | ||
|
|
748a345e7f | ||
|
|
73d877e3a5 | ||
|
|
b9200c92d5 | ||
|
|
3dfe0adf0a | ||
|
|
3482158fee | ||
|
|
996eb6c63c | ||
|
|
378484ec01 | ||
|
|
7bd28183ba | ||
|
|
168202dbf7 | ||
|
|
8522eddf5a | ||
|
|
5f33827eca | ||
|
|
b023c6bb23 | ||
|
|
1a83b712a0 | ||
|
|
9a8b3bad31 | ||
|
|
ecdeb9a970 | ||
|
|
5bd7589109 | ||
|
|
faf9fd6253 | ||
|
|
ffbaef8771 | ||
|
|
e1aef3127c | ||
|
|
8f15abc285 | ||
|
|
350fb89ebf | ||
|
|
9aa9187dd7 | ||
|
|
4719fd5235 | ||
|
|
12930c68fb | ||
|
|
981c976754 | ||
|
|
6b851d0529 | ||
|
|
7439de37a3 | ||
|
|
9d6098af15 | ||
|
|
e4c211df2d | ||
|
|
946e301ed6 | ||
|
|
825628675e | ||
|
|
20ce4cdf91 | ||
|
|
d108185ec7 | ||
|
|
9e96d6182d | ||
|
|
30952f6a24 | ||
|
|
316eecc064 | ||
|
|
559c799309 | ||
|
|
77250af444 | ||
|
|
431738175d | ||
|
|
09a7573163 | ||
|
|
ae774a6b95 | ||
|
|
ad8e82ac65 | ||
|
|
0a6f914bfc | ||
|
|
0aed890b15 | ||
|
|
8e05fdb369 | ||
|
|
c5350ca6a0 | ||
|
|
cf094c2f4f | ||
|
|
848b24cfe4 | ||
|
|
ef44cb86c2 | ||
|
|
e9ebba3350 | ||
|
|
d228cf0e7b | ||
|
|
bce83bfc4e | ||
|
|
0a2c7d062c | ||
|
|
6bbc4f4a48 | ||
|
|
33c506d7fe | ||
|
|
5bddfc0d79 | ||
|
|
47a06d2824 | ||
|
|
7fcc548665 | ||
|
|
187cfd7be7 | ||
|
|
b53963a791 | ||
|
|
968fff29ac | ||
|
|
eb31160ae0 | ||
|
|
702b10ff96 | ||
|
|
0e1dcc8062 | ||
|
|
136b5d189c | ||
|
|
ce8a84abb3 | ||
|
|
88338bdfcf | ||
|
|
41ea71c31c | ||
|
|
1a27a069ac | ||
|
|
24891c3f43 | ||
|
|
be359a32c0 | ||
|
|
22202afbf5 | ||
|
|
7cc7675cdc | ||
|
|
1b45c5f96a | ||
|
|
9de8d5c501 | ||
|
|
7ecc3466cf | ||
|
|
9573395237 | ||
|
|
587b4fee9d | ||
|
|
e48c93a3b5 | ||
|
|
2b47e150c6 | ||
|
|
ec7b406cc9 | ||
|
|
e8db563e98 | ||
|
|
6a91e85981 | ||
|
|
3648f26cca | ||
|
|
ee62f2a223 | ||
|
|
8c224429b3 | ||
|
|
bfe9ae22ad | ||
|
|
b1dcb01ed7 | ||
|
|
ec297fb838 | ||
|
|
9f9c486268 | ||
|
|
7fb9db49be | ||
|
|
ecbcee5bc8 | ||
|
|
79b77b01fd | ||
|
|
9dbea539ed | ||
|
|
3f9deb66ce | ||
|
|
da6bbbbc5e | ||
|
|
ae998583be | ||
|
|
f628152be1 | ||
|
|
39d44adbc5 | ||
|
|
a892ae8764 | ||
|
|
2aace0df2c | ||
|
|
cdd9567062 | ||
|
|
cc0371648b | ||
|
|
a83879fa42 | ||
|
|
cd24405a22 | ||
|
|
8b01dfe696 | ||
|
|
0f93e5c907 | ||
|
|
83d751b287 | ||
|
|
e86e3ec3ec | ||
|
|
e453c7a36e | ||
|
|
05cd2518a4 | ||
|
|
273af3cbf1 | ||
|
|
0addae81cd | ||
|
|
949b61c635 | ||
|
|
88a1eead03 | ||
|
|
df7a4ac093 | ||
|
|
1d80e94bb4 | ||
|
|
c09ed10d33 | ||
|
|
33649ed7d3 | ||
|
|
a7aa1a7d8b | ||
|
|
eddc2f3038 | ||
|
|
45f69be94c | ||
|
|
df526552a6 | ||
|
|
2de2887ebb | ||
|
|
4ace171447 | ||
|
|
81de93da2d | ||
|
|
c06f7259cf | ||
|
|
a36afc6bff | ||
|
|
73b93be313 | ||
|
|
526b913f7d | ||
|
|
cb47bdd9fd | ||
|
|
f94ca0e087 | ||
|
|
78b7e12b87 | ||
|
|
5d417d7a69 | ||
|
|
ef110e77ff | ||
|
|
31b4dda7bd | ||
|
|
22b04af0fa | ||
|
|
bb256514c0 | ||
|
|
3b78860d8d | ||
|
|
c18cfa7a7a | ||
|
|
4075f693bd | ||
|
|
973aad5c17 | ||
|
|
628f92a9fb | ||
|
|
3be0b3e6c7 | ||
|
|
69155cb691 | ||
|
|
b10ed976cc | ||
|
|
18dea55071 | ||
|
|
a161dddbbf | ||
|
|
8722fb2cf5 | ||
|
|
ad522651ec | ||
|
|
2999243e34 | ||
|
|
b2091e8632 | ||
|
|
cb1dacfef1 | ||
|
|
9e7b73ab4a | ||
|
|
9f9ffef697 | ||
|
|
de70cbfee9 | ||
|
|
2ad61df9df | ||
|
|
52bf39bcf9 | ||
|
|
a3285653ae | ||
|
|
b9c9f65f0f | ||
|
|
1169c1b898 | ||
|
|
f235b991db | ||
|
|
7c6918de98 | ||
|
|
f814ce7f10 | ||
|
|
0a1097d735 | ||
|
|
4fd6ac5657 | ||
|
|
36d0903733 | ||
|
|
9f85175ee1 | ||
|
|
5ae3228928 | ||
|
|
68f0997d87 | ||
|
|
589f7bdbcc | ||
|
|
e71fbb1def | ||
|
|
c563ed3635 | ||
|
|
43adfe8893 | ||
|
|
145dea0e9b | ||
|
|
2ef13d1df7 | ||
|
|
4c94ff7deb | ||
|
|
223eeb6921 | ||
|
|
d9fab8afd9 | ||
|
|
6fa5fdfeb2 | ||
|
|
ac2174db13 | ||
|
|
30bdd25228 | ||
|
|
9e4cab74ec | ||
|
|
349c5cd800 | ||
|
|
623372238d | ||
|
|
cfb8f282d1 | ||
|
|
6a68e7936f | ||
|
|
98285393fe | ||
|
|
4731f9222c | ||
|
|
a2ce51fdf7 | ||
|
|
320b5ca01a | ||
|
|
fc3d6a1847 | ||
|
|
7658df9ebd | ||
|
|
23d6eb67c3 | ||
|
|
d1274e2769 | ||
|
|
231e2a8df3 | ||
|
|
24542ec84a | ||
|
|
d361053e10 | ||
|
|
1e00e15c7b | ||
|
|
ffbe1e065a | ||
|
|
7251f2fd3d | ||
|
|
bfba95f9f7 | ||
|
|
3c49447165 | ||
|
|
1e14af6e64 | ||
|
|
b9bcff40c5 | ||
|
|
f1b85d7ebf | ||
|
|
aa385a293d | ||
|
|
b12955e220 | ||
|
|
29f4b26280 | ||
|
|
70a9e49039 | ||
|
|
a9173727cf | ||
|
|
4f2c2e6d5e | ||
|
|
3b1b8cc720 | ||
|
|
3e5c66e932 | ||
|
|
235a06903e | ||
|
|
2cebd5c51d | ||
|
|
b8fab9ae79 | ||
|
|
1f43a1a924 | ||
|
|
a270f318e9 | ||
|
|
2acda03518 | ||
|
|
3d94286785 | ||
|
|
16565401c7 | ||
|
|
7408931565 | ||
|
|
2b37ebd7ed | ||
|
|
0c7f57e0c4 | ||
|
|
f534768715 | ||
|
|
e0a6c16228 | ||
|
|
1a1f078684 | ||
|
|
c999704d1e | ||
|
|
a51ac7b4e7 | ||
|
|
d8e96ef12a | ||
|
|
d0a00254ad | ||
|
|
0e33f4da6b | ||
|
|
bd301768ea | ||
|
|
0d27d63984 | ||
|
|
4c308d998e | ||
|
|
af6a87e54d | ||
|
|
d7804f5473 | ||
|
|
ceb6706c73 | ||
|
|
4ab5066ed2 | ||
|
|
f19b381e3e | ||
|
|
74f9b322a8 | ||
|
|
b305e15e11 | ||
|
|
89c5e3df1f | ||
|
|
5732c3bca0 | ||
|
|
bd549e527c | ||
|
|
fc2013334f | ||
|
|
b95163cfe4 | ||
|
|
2617e6d7c6 | ||
|
|
84af725ef1 | ||
|
|
b42e81c32d | ||
|
|
ae01201c43 | ||
|
|
1d1161771a | ||
|
|
96425a1787 | ||
|
|
c87b516130 | ||
|
|
f4915073c8 | ||
|
|
5d5c64a58b | ||
|
|
18802a2883 | ||
|
|
30f15473db | ||
|
|
80ef3b39ff | ||
|
|
1b50168d08 | ||
|
|
aa8214addf | ||
|
|
18d3801c92 | ||
|
|
f3be58e2ba | ||
|
|
20b188ff42 | ||
|
|
f6ff9c9c66 | ||
|
|
9888de8acb | ||
|
|
42e8ec32d7 | ||
|
|
3261612a8c | ||
|
|
2098489bb0 | ||
|
|
fafc0b5575 | ||
|
|
172261495f | ||
|
|
a334dc9b2b | ||
|
|
b7e42e805b | ||
|
|
24cb36a1e2 | ||
|
|
20bd30060f | ||
|
|
b4c8a52117 | ||
|
|
2fc7e6159e | ||
|
|
e2a0d62adf | ||
|
|
25a1148e04 | ||
|
|
8e2d34f0b6 | ||
|
|
e43422a090 | ||
|
|
562f052eb0 | ||
|
|
bc9cd63ef0 | ||
|
|
de89b4c69f | ||
|
|
fc9f239a3b | ||
|
|
adeb69e396 | ||
|
|
4bcba16748 | ||
|
|
2ba94f7c89 | ||
|
|
8a125d1ae5 | ||
|
|
3befa1cd96 | ||
|
|
557b94cd83 | ||
|
|
1bd48f8d02 | ||
|
|
c8f81bc6b8 | ||
|
|
39b268194a | ||
|
|
2ad3119fab | ||
|
|
21acefb0b4 | ||
|
|
23b07e5589 | ||
|
|
b85d3bc829 | ||
|
|
a4d434ee05 | ||
|
|
75de0f5c65 | ||
|
|
a7d6f4ff19 | ||
|
|
500004dbe0 | ||
|
|
ccea372fd0 | ||
|
|
2f0c7729ff | ||
|
|
5cd6dafe27 | ||
|
|
e7b0536896 | ||
|
|
b1f93b07c9 | ||
|
|
00a25dbe45 | ||
|
|
01a81a9c56 | ||
|
|
7ad781e91c | ||
|
|
49f39d7602 | ||
|
|
3ec60f11bb | ||
|
|
134cc6291c | ||
|
|
d62ae96763 | ||
|
|
1c51ad8d26 | ||
|
|
4c5953fce0 | ||
|
|
373c849b18 | ||
|
|
41cbef81ec |
21
.git-blame-ignore-revs
Normal file
21
.git-blame-ignore-revs
Normal file
@@ -0,0 +1,21 @@
|
||||
# .git-blame-ignore-revs
|
||||
# Auto-formatted Java
|
||||
730eae952139209fe9fdf598541d608f4c0c0c84
|
||||
# Auto-formatted C#
|
||||
5ad7ed49dd3de03ec6dcfcb6848758a6a987e11c
|
||||
# Auto-formatted C/C++
|
||||
ef97e539ec1971494d4bba5cafe82e00bc8217ac
|
||||
# Auto-formatted Python
|
||||
21d5fa836b3a7d020ba45e8b8168b145a9772131
|
||||
# Auto-formatted JavaScript
|
||||
8d97fe9ed327a9546ff2eaf515cf0f5214deddd9
|
||||
# Auto-formatted Ruby
|
||||
a5d229903d2f12d45f2c2c38822f1d0e7504ae7f
|
||||
# Auto-formatted Go
|
||||
08c658e66bf867090033ea096e244a93d46c0aa7
|
||||
# Auto-formatted Swift
|
||||
711d7057f79fb7d72fc3b35e010bd018f9009169
|
||||
# Auto-formatted shared ql packs
|
||||
3640b6d3a8ce9edf8e1d3ed106fe8526cf255bc0
|
||||
# Auto-formatted taint tracking files
|
||||
159d8e978c51959b380838c080d891b66e763b19
|
||||
4
.github/workflows/go-tests-other-os.yml
vendored
4
.github/workflows/go-tests-other-os.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- name: Set up Go 1.20
|
||||
uses: actions/setup-go@v3
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: 1.20.0
|
||||
id: go
|
||||
@@ -48,7 +48,7 @@ jobs:
|
||||
runs-on: windows-latest-xl
|
||||
steps:
|
||||
- name: Set up Go 1.20
|
||||
uses: actions/setup-go@v3
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: 1.20.0
|
||||
id: go
|
||||
|
||||
2
.github/workflows/go-tests.yml
vendored
2
.github/workflows/go-tests.yml
vendored
@@ -21,7 +21,7 @@ jobs:
|
||||
runs-on: ubuntu-latest-xl
|
||||
steps:
|
||||
- name: Set up Go 1.20
|
||||
uses: actions/setup-go@v3
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: 1.20.0
|
||||
id: go
|
||||
|
||||
63
.github/workflows/ruby-build.yml
vendored
63
.github/workflows/ruby-build.yml
vendored
@@ -55,12 +55,12 @@ jobs:
|
||||
id: cache-extractor
|
||||
with:
|
||||
path: |
|
||||
ruby/target/release/ruby-autobuilder
|
||||
ruby/target/release/ruby-autobuilder.exe
|
||||
ruby/target/release/ruby-extractor
|
||||
ruby/target/release/ruby-extractor.exe
|
||||
ruby/ql/lib/codeql/ruby/ast/internal/TreeSitter.qll
|
||||
key: ${{ runner.os }}-${{ steps.os_version.outputs.version }}-ruby-extractor-${{ hashFiles('ruby/rust-toolchain.toml', 'ruby/**/Cargo.lock') }}--${{ hashFiles('ruby/**/*.rs') }}
|
||||
ruby/extractor/target/release/autobuilder
|
||||
ruby/extractor/target/release/autobuilder.exe
|
||||
ruby/extractor/target/release/extractor
|
||||
ruby/extractor/target/release/extractor.exe
|
||||
ruby/extractor/ql/lib/codeql/ruby/ast/internal/TreeSitter.qll
|
||||
key: ${{ runner.os }}-${{ steps.os_version.outputs.version }}-ruby-extractor-${{ hashFiles('ruby/extractor/rust-toolchain.toml', 'ruby/extractor/Cargo.lock') }}--${{ hashFiles('ruby/extractor/**/*.rs') }}
|
||||
- uses: actions/cache@v3
|
||||
if: steps.cache-extractor.outputs.cache-hit != 'true'
|
||||
with:
|
||||
@@ -68,22 +68,22 @@ jobs:
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
ruby/target
|
||||
key: ${{ runner.os }}-${{ steps.os_version.outputs.version }}-ruby-rust-cargo-${{ hashFiles('ruby/rust-toolchain.toml', 'ruby/**/Cargo.lock') }}
|
||||
key: ${{ runner.os }}-${{ steps.os_version.outputs.version }}-ruby-rust-cargo-${{ hashFiles('ruby/extractor/rust-toolchain.toml', 'ruby/extractor/**/Cargo.lock') }}
|
||||
- name: Check formatting
|
||||
if: steps.cache-extractor.outputs.cache-hit != 'true'
|
||||
run: cargo fmt --all -- --check
|
||||
run: cd extractor && cargo fmt --all -- --check
|
||||
- name: Build
|
||||
if: steps.cache-extractor.outputs.cache-hit != 'true'
|
||||
run: cargo build --verbose
|
||||
run: cd extractor && cargo build --verbose
|
||||
- name: Run tests
|
||||
if: steps.cache-extractor.outputs.cache-hit != 'true'
|
||||
run: cargo test --verbose
|
||||
run: cd extractor && cargo test --verbose
|
||||
- name: Release build
|
||||
if: steps.cache-extractor.outputs.cache-hit != 'true'
|
||||
run: cargo build --release
|
||||
run: cd extractor && cargo build --release
|
||||
- name: Generate dbscheme
|
||||
if: ${{ matrix.os == 'ubuntu-latest' && steps.cache-extractor.outputs.cache-hit != 'true'}}
|
||||
run: target/release/ruby-generator --dbscheme ql/lib/ruby.dbscheme --library ql/lib/codeql/ruby/ast/internal/TreeSitter.qll
|
||||
run: extractor/target/release/generator --dbscheme ql/lib/ruby.dbscheme --library ql/lib/codeql/ruby/ast/internal/TreeSitter.qll
|
||||
- uses: actions/upload-artifact@v3
|
||||
if: ${{ matrix.os == 'ubuntu-latest' }}
|
||||
with:
|
||||
@@ -98,10 +98,10 @@ jobs:
|
||||
with:
|
||||
name: extractor-${{ matrix.os }}
|
||||
path: |
|
||||
ruby/target/release/ruby-autobuilder
|
||||
ruby/target/release/ruby-autobuilder.exe
|
||||
ruby/target/release/ruby-extractor
|
||||
ruby/target/release/ruby-extractor.exe
|
||||
ruby/extractor/target/release/autobuilder
|
||||
ruby/extractor/target/release/autobuilder.exe
|
||||
ruby/extractor/target/release/extractor
|
||||
ruby/extractor/target/release/extractor.exe
|
||||
retention-days: 1
|
||||
compile-queries:
|
||||
runs-on: ubuntu-latest-xl
|
||||
@@ -116,21 +116,22 @@ jobs:
|
||||
key: ruby-build
|
||||
- name: Build Query Pack
|
||||
run: |
|
||||
rm -rf target/packs
|
||||
codeql pack create ../misc/suite-helpers --output target/packs
|
||||
codeql pack create ../shared/regex --output target/packs
|
||||
codeql pack create ../shared/ssa --output target/packs
|
||||
codeql pack create ../shared/tutorial --output target/packs
|
||||
codeql pack create ql/lib --output target/packs
|
||||
codeql pack create -j0 ql/src --output target/packs --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}"
|
||||
PACK_FOLDER=$(readlink -f target/packs/codeql/ruby-queries/*)
|
||||
PACKS=${{ runner.temp }}/query-packs
|
||||
rm -rf $PACKS
|
||||
codeql pack create ../misc/suite-helpers --output "$PACKS"
|
||||
codeql pack create ../shared/regex --output "$PACKS"
|
||||
codeql pack create ../shared/ssa --output "$PACKS"
|
||||
codeql pack create ../shared/tutorial --output "$PACKS"
|
||||
codeql pack create ql/lib --output "$PACKS"
|
||||
codeql pack create -j0 ql/src --output "$PACKS" --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}"
|
||||
PACK_FOLDER=$(readlink -f "$PACKS"/codeql/ruby-queries/*)
|
||||
codeql generate query-help --format=sarifv2.1.0 --output="${PACK_FOLDER}/rules.sarif" ql/src
|
||||
(cd ql/src; find queries \( -name '*.qhelp' -o -name '*.rb' -o -name '*.erb' \) -exec bash -c 'mkdir -p "'"${PACK_FOLDER}"'/$(dirname "{}")"' \; -exec cp "{}" "${PACK_FOLDER}/{}" \;)
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: codeql-ruby-queries
|
||||
path: |
|
||||
ruby/target/packs/*
|
||||
${{ runner.temp }}/query-packs/*
|
||||
retention-days: 1
|
||||
|
||||
package:
|
||||
@@ -158,12 +159,12 @@ jobs:
|
||||
mkdir -p ruby
|
||||
cp -r codeql-extractor.yml tools ql/lib/ruby.dbscheme.stats ruby/
|
||||
mkdir -p ruby/tools/{linux64,osx64,win64}
|
||||
cp linux64/ruby-autobuilder ruby/tools/linux64/autobuilder
|
||||
cp osx64/ruby-autobuilder ruby/tools/osx64/autobuilder
|
||||
cp win64/ruby-autobuilder.exe ruby/tools/win64/autobuilder.exe
|
||||
cp linux64/ruby-extractor ruby/tools/linux64/extractor
|
||||
cp osx64/ruby-extractor ruby/tools/osx64/extractor
|
||||
cp win64/ruby-extractor.exe ruby/tools/win64/extractor.exe
|
||||
cp linux64/autobuilder ruby/tools/linux64/autobuilder
|
||||
cp osx64/autobuilder ruby/tools/osx64/autobuilder
|
||||
cp win64/autobuilder.exe ruby/tools/win64/autobuilder.exe
|
||||
cp linux64/extractor ruby/tools/linux64/extractor
|
||||
cp osx64/extractor ruby/tools/osx64/extractor
|
||||
cp win64/extractor.exe ruby/tools/win64/extractor.exe
|
||||
chmod +x ruby/tools/{linux64,osx64}/{autobuilder,extractor}
|
||||
zip -rq codeql-ruby.zip ruby
|
||||
- uses: actions/upload-artifact@v3
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
/csharp/ @github/codeql-csharp
|
||||
/go/ @github/codeql-go
|
||||
/java/ @github/codeql-java
|
||||
/javascript/ @github/codeql-dynamic
|
||||
/python/ @github/codeql-dynamic
|
||||
/ruby/ @github/codeql-dynamic
|
||||
/javascript/ @github/codeql-javascript
|
||||
/python/ @github/codeql-python
|
||||
/ruby/ @github/codeql-ruby
|
||||
/swift/ @github/codeql-swift
|
||||
/misc/codegen/ @github/codeql-swift
|
||||
/java/kotlin-extractor/ @github/codeql-kotlin
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlow.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlow.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlow.qll",
|
||||
"cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlow.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlow.qll",
|
||||
"go/ql/lib/semmle/go/dataflow/internal/DataFlow.qll",
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlow.qll",
|
||||
@@ -14,7 +13,6 @@
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll",
|
||||
"cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll",
|
||||
"go/ql/lib/semmle/go/dataflow/internal/DataFlowImpl.qll",
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl.qll",
|
||||
@@ -28,8 +26,6 @@
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl4.qll",
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl5.qll",
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl6.qll",
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForSerializability.qll",
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForOnActivityResult.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl1.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll",
|
||||
@@ -39,10 +35,6 @@
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll",
|
||||
"cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl1.qll",
|
||||
"cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll",
|
||||
"cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll",
|
||||
"cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl1.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll",
|
||||
@@ -67,7 +59,6 @@
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll",
|
||||
"cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll",
|
||||
"go/ql/lib/semmle/go/dataflow/internal/DataFlowImplCommon.qll",
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll",
|
||||
@@ -77,7 +68,6 @@
|
||||
"TaintTracking Java/C++/C#/Go/Python/Ruby/Swift": [
|
||||
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTracking.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/tainttracking1/TaintTracking.qll",
|
||||
"cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/tainttracking1/TaintTracking.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking1/TaintTracking.qll",
|
||||
"go/ql/lib/semmle/go/dataflow/internal/tainttracking1/TaintTracking.qll",
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/tainttracking1/TaintTracking.qll",
|
||||
@@ -91,9 +81,6 @@
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/tainttracking2/TaintTrackingImpl.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/tainttracking3/TaintTrackingImpl.qll",
|
||||
"cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
|
||||
"cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/tainttracking2/TaintTrackingImpl.qll",
|
||||
"cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/tainttracking3/TaintTrackingImpl.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking3/TaintTrackingImpl.qll",
|
||||
@@ -115,7 +102,6 @@
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplConsistency.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplConsistency.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll",
|
||||
"cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplConsistency.qll",
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplConsistency.qll",
|
||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplConsistency.qll",
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## 0.5.4
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.5.3
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: breaking
|
||||
---
|
||||
* The `semmle.code.cpp.commons.Buffer` and `semmle.code.cpp.commons.NullTermination` libraries no longer expose `semmle.code.cpp.dataflow.DataFlow`. Please import `semmle.code.cpp.dataflow.DataFlow` directly.
|
||||
12
cpp/ql/lib/change-notes/2023-03-03-delete-deps.md
Normal file
12
cpp/ql/lib/change-notes/2023-03-03-delete-deps.md
Normal file
@@ -0,0 +1,12 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Deleted the deprecated `hasGeneratedCopyConstructor` and `hasGeneratedCopyAssignmentOperator` predicates from the `Folder` class.
|
||||
* Deleted the deprecated `getPath` and `getFolder` predicates from the `XmlFile` class.
|
||||
* Deleted the deprecated `getMustlockFunction`, `getTrylockFunction`, `getLockFunction`, and `getUnlockFunction` predicates from the `MutexType` class.
|
||||
* Deleted the deprecated `getPosInBasicBlock` predicate from the `SubBasicBlock` class.
|
||||
* Deleted the deprecated `getExpr` predicate from the `PointerDereferenceExpr` class.
|
||||
* Deleted the deprecated `getUseInstruction` and `getDefinitionInstruction` predicates from the `Operand` class.
|
||||
* Deleted the deprecated `isInParameter`, `isInParameterPointer`, and `isInQualifier` predicates from the `FunctionInput` class.
|
||||
* Deleted the deprecated `isOutParameterPointer`, `isOutQualifier`, `isOutReturnValue`, and `isOutReturnPointer` predicate from the `FunctionOutput` class.
|
||||
* Deleted the deprecated 3-argument `isGuardPhi` predicate from the `RangeSsaDefinition` class.
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: deprecated
|
||||
---
|
||||
* The `WriteConfig` taint tracking configuration has been deprecated. Please use `WriteFlow`.
|
||||
4
cpp/ql/lib/change-notes/2023-03-13-mergepathgraph.md
Normal file
4
cpp/ql/lib/change-notes/2023-03-13-mergepathgraph.md
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: feature
|
||||
---
|
||||
* Added support for merging two `PathGraph`s via disjoint union to allow results from multiple data flow computations in a single `path-problem` query.
|
||||
11
cpp/ql/lib/change-notes/2023-03-16-use-use-flow.md
Normal file
11
cpp/ql/lib/change-notes/2023-03-16-use-use-flow.md
Normal file
@@ -0,0 +1,11 @@
|
||||
---
|
||||
category: majorAnalysis
|
||||
---
|
||||
* A new C/C++ dataflow library (`semmle.code.cpp.dataflow.new.DataFlow`) has been added.
|
||||
The new library behaves much more like the dataflow library of other CodeQL supported
|
||||
languages by following use-use dataflow paths instead of def-use dataflow paths.
|
||||
The new library also better supports dataflow through indirections, and new predicates
|
||||
such as `Node::asIndirectExpr` have been added to facilitate working with indirections.
|
||||
|
||||
The `semmle.code.cpp.ir.dataflow.DataFlow` library is now identical to the new
|
||||
`semmle.code.cpp.dataflow.new.DataFlow` library.
|
||||
3
cpp/ql/lib/change-notes/released/0.5.4.md
Normal file
3
cpp/ql/lib/change-notes/released/0.5.4.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.5.4
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.5.3
|
||||
lastReleaseVersion: 0.5.4
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import experimental.semmle.code.cpp.ir.dataflow.DataFlow
|
||||
import experimental.semmle.code.cpp.ir.dataflow.DataFlow2
|
||||
import semmle.code.cpp.ir.dataflow.DataFlow
|
||||
import semmle.code.cpp.ir.dataflow.DataFlow2
|
||||
|
||||
module ProductFlow {
|
||||
abstract class Configuration extends string {
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
/**
|
||||
* Provides a predicate for non-contextual virtual dispatch and function
|
||||
* pointer resolution.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
private import semmle.code.cpp.ir.ValueNumbering
|
||||
private import internal.DataFlowDispatch
|
||||
private import semmle.code.cpp.ir.IR
|
||||
|
||||
/**
|
||||
* Resolve potential target function(s) for `call`.
|
||||
*
|
||||
* If `call` is a call through a function pointer (`ExprCall`) or its target is
|
||||
* a virtual member function, simple data flow analysis is performed in order
|
||||
* to identify the possible target(s).
|
||||
*/
|
||||
Function resolveCall(Call call) {
|
||||
exists(CallInstruction callInstruction |
|
||||
callInstruction.getAst() = call and
|
||||
result = viableCallable(callInstruction)
|
||||
)
|
||||
}
|
||||
@@ -1,245 +0,0 @@
|
||||
/**
|
||||
* Provides an implementation of global (interprocedural) data flow. This file
|
||||
* re-exports the local (intraprocedural) data flow analysis from
|
||||
* `DataFlowImplSpecific::Public` and adds a global analysis, mainly exposed
|
||||
* through the `Make` and `MakeWithState` modules.
|
||||
*/
|
||||
|
||||
private import DataFlowImplCommon
|
||||
private import DataFlowImplSpecific::Private
|
||||
import DataFlowImplSpecific::Public
|
||||
import DataFlowImplCommonPublic
|
||||
private import DataFlowImpl
|
||||
|
||||
/** An input configuration for data flow. */
|
||||
signature module ConfigSig {
|
||||
/**
|
||||
* Holds if `source` is a relevant data flow source.
|
||||
*/
|
||||
predicate isSource(Node source);
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant data flow sink.
|
||||
*/
|
||||
predicate isSink(Node sink);
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited. This completely removes
|
||||
* `node` from the data flow graph.
|
||||
*/
|
||||
default predicate isBarrier(Node node) { none() }
|
||||
|
||||
/** Holds if data flow into `node` is prohibited. */
|
||||
default predicate isBarrierIn(Node node) { none() }
|
||||
|
||||
/** Holds if data flow out of `node` is prohibited. */
|
||||
default predicate isBarrierOut(Node node) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
|
||||
*/
|
||||
default predicate isAdditionalFlowStep(Node node1, Node node2) { none() }
|
||||
|
||||
/**
|
||||
* Holds if an arbitrary number of implicit read steps of content `c` may be
|
||||
* taken at `node`.
|
||||
*/
|
||||
default predicate allowImplicitRead(Node node, ContentSet c) { none() }
|
||||
|
||||
/**
|
||||
* Gets the virtual dispatch branching limit when calculating field flow.
|
||||
* This can be overridden to a smaller value to improve performance (a
|
||||
* value of 0 disables field flow), or a larger value to get more results.
|
||||
*/
|
||||
default int fieldFlowBranchLimit() { result = 2 }
|
||||
|
||||
/**
|
||||
* Gets a data flow configuration feature to add restrictions to the set of
|
||||
* valid flow paths.
|
||||
*
|
||||
* - `FeatureHasSourceCallContext`:
|
||||
* Assume that sources have some existing call context to disallow
|
||||
* conflicting return-flow directly following the source.
|
||||
* - `FeatureHasSinkCallContext`:
|
||||
* Assume that sinks have some existing call context to disallow
|
||||
* conflicting argument-to-parameter flow directly preceding the sink.
|
||||
* - `FeatureEqualSourceSinkCallContext`:
|
||||
* Implies both of the above and additionally ensures that the entire flow
|
||||
* path preserves the call context.
|
||||
*
|
||||
* These features are generally not relevant for typical end-to-end data flow
|
||||
* queries, but should only be used for constructing paths that need to
|
||||
* somehow be pluggable in another path context.
|
||||
*/
|
||||
default FlowFeature getAFeature() { none() }
|
||||
|
||||
/** Holds if sources should be grouped in the result of `hasFlowPath`. */
|
||||
default predicate sourceGrouping(Node source, string sourceGroup) { none() }
|
||||
|
||||
/** Holds if sinks should be grouped in the result of `hasFlowPath`. */
|
||||
default predicate sinkGrouping(Node sink, string sinkGroup) { none() }
|
||||
|
||||
/**
|
||||
* Holds if hidden nodes should be included in the data flow graph.
|
||||
*
|
||||
* This feature should only be used for debugging or when the data flow graph
|
||||
* is not visualized (as it is in a `path-problem` query).
|
||||
*/
|
||||
default predicate includeHiddenNodes() { none() }
|
||||
}
|
||||
|
||||
/** An input configuration for data flow using flow state. */
|
||||
signature module StateConfigSig {
|
||||
bindingset[this]
|
||||
class FlowState;
|
||||
|
||||
/**
|
||||
* Holds if `source` is a relevant data flow source with the given initial
|
||||
* `state`.
|
||||
*/
|
||||
predicate isSource(Node source, FlowState state);
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant data flow sink accepting `state`.
|
||||
*/
|
||||
predicate isSink(Node sink, FlowState state);
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited. This completely removes
|
||||
* `node` from the data flow graph.
|
||||
*/
|
||||
default predicate isBarrier(Node node) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited when the flow state is
|
||||
* `state`.
|
||||
*/
|
||||
predicate isBarrier(Node node, FlowState state);
|
||||
|
||||
/** Holds if data flow into `node` is prohibited. */
|
||||
default predicate isBarrierIn(Node node) { none() }
|
||||
|
||||
/** Holds if data flow out of `node` is prohibited. */
|
||||
default predicate isBarrierOut(Node node) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
|
||||
*/
|
||||
default predicate isAdditionalFlowStep(Node node1, Node node2) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
|
||||
* This step is only applicable in `state1` and updates the flow state to `state2`.
|
||||
*/
|
||||
predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2);
|
||||
|
||||
/**
|
||||
* Holds if an arbitrary number of implicit read steps of content `c` may be
|
||||
* taken at `node`.
|
||||
*/
|
||||
default predicate allowImplicitRead(Node node, ContentSet c) { none() }
|
||||
|
||||
/**
|
||||
* Gets the virtual dispatch branching limit when calculating field flow.
|
||||
* This can be overridden to a smaller value to improve performance (a
|
||||
* value of 0 disables field flow), or a larger value to get more results.
|
||||
*/
|
||||
default int fieldFlowBranchLimit() { result = 2 }
|
||||
|
||||
/**
|
||||
* Gets a data flow configuration feature to add restrictions to the set of
|
||||
* valid flow paths.
|
||||
*
|
||||
* - `FeatureHasSourceCallContext`:
|
||||
* Assume that sources have some existing call context to disallow
|
||||
* conflicting return-flow directly following the source.
|
||||
* - `FeatureHasSinkCallContext`:
|
||||
* Assume that sinks have some existing call context to disallow
|
||||
* conflicting argument-to-parameter flow directly preceding the sink.
|
||||
* - `FeatureEqualSourceSinkCallContext`:
|
||||
* Implies both of the above and additionally ensures that the entire flow
|
||||
* path preserves the call context.
|
||||
*
|
||||
* These features are generally not relevant for typical end-to-end data flow
|
||||
* queries, but should only be used for constructing paths that need to
|
||||
* somehow be pluggable in another path context.
|
||||
*/
|
||||
default FlowFeature getAFeature() { none() }
|
||||
|
||||
/** Holds if sources should be grouped in the result of `hasFlowPath`. */
|
||||
default predicate sourceGrouping(Node source, string sourceGroup) { none() }
|
||||
|
||||
/** Holds if sinks should be grouped in the result of `hasFlowPath`. */
|
||||
default predicate sinkGrouping(Node sink, string sinkGroup) { none() }
|
||||
|
||||
/**
|
||||
* Holds if hidden nodes should be included in the data flow graph.
|
||||
*
|
||||
* This feature should only be used for debugging or when the data flow graph
|
||||
* is not visualized (as it is in a `path-problem` query).
|
||||
*/
|
||||
default predicate includeHiddenNodes() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev`
|
||||
* measured in approximate number of interprocedural steps.
|
||||
*/
|
||||
signature int explorationLimitSig();
|
||||
|
||||
/**
|
||||
* The output of a data flow computation.
|
||||
*/
|
||||
signature module DataFlowSig {
|
||||
/**
|
||||
* A `Node` augmented with a call context (except for sinks) and an access path.
|
||||
* Only those `PathNode`s that are reachable from a source, and which can reach a sink, are generated.
|
||||
*/
|
||||
class PathNode;
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `source` to `sink`.
|
||||
*
|
||||
* The corresponding paths are generated from the end-points and the graph
|
||||
* included in the module `PathGraph`.
|
||||
*/
|
||||
predicate hasFlowPath(PathNode source, PathNode sink);
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `source` to `sink`.
|
||||
*/
|
||||
predicate hasFlow(Node source, Node sink);
|
||||
|
||||
/**
|
||||
* Holds if data can flow from some source to `sink`.
|
||||
*/
|
||||
predicate hasFlowTo(Node sink);
|
||||
|
||||
/**
|
||||
* Holds if data can flow from some source to `sink`.
|
||||
*/
|
||||
predicate hasFlowToExpr(DataFlowExpr sink);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a standard data flow computation.
|
||||
*/
|
||||
module Make<ConfigSig Config> implements DataFlowSig {
|
||||
private module C implements FullStateConfigSig {
|
||||
import DefaultState<Config>
|
||||
import Config
|
||||
}
|
||||
|
||||
import Impl<C>
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a data flow computation using flow state.
|
||||
*/
|
||||
module MakeWithState<StateConfigSig Config> implements DataFlowSig {
|
||||
private module C implements FullStateConfigSig {
|
||||
import Config
|
||||
}
|
||||
|
||||
import Impl<C>
|
||||
}
|
||||
@@ -1,273 +0,0 @@
|
||||
private import cpp
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import experimental.semmle.code.cpp.ir.dataflow.DataFlow
|
||||
private import experimental.semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate
|
||||
private import experimental.semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
|
||||
private import DataFlowImplCommon as DataFlowImplCommon
|
||||
|
||||
/**
|
||||
* Gets a function that might be called by `call`.
|
||||
*/
|
||||
cached
|
||||
Function viableCallable(CallInstruction call) {
|
||||
DataFlowImplCommon::forceCachingInSameStage() and
|
||||
result = call.getStaticCallTarget()
|
||||
or
|
||||
// If the target of the call does not have a body in the snapshot, it might
|
||||
// be because the target is just a header declaration, and the real target
|
||||
// will be determined at run time when the caller and callee are linked
|
||||
// together by the operating system's dynamic linker. In case a _unique_
|
||||
// function with the right signature is present in the database, we return
|
||||
// that as a potential callee.
|
||||
exists(string qualifiedName, int nparams |
|
||||
callSignatureWithoutBody(qualifiedName, nparams, call) and
|
||||
functionSignatureWithBody(qualifiedName, nparams, result) and
|
||||
strictcount(Function other | functionSignatureWithBody(qualifiedName, nparams, other)) = 1
|
||||
)
|
||||
or
|
||||
// Virtual dispatch
|
||||
result = call.(VirtualDispatch::DataSensitiveCall).resolve()
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides virtual dispatch support compatible with the original
|
||||
* implementation of `semmle.code.cpp.security.TaintTracking`.
|
||||
*/
|
||||
private module VirtualDispatch {
|
||||
/** A call that may dispatch differently depending on the qualifier value. */
|
||||
abstract class DataSensitiveCall extends DataFlowCall {
|
||||
/**
|
||||
* Gets the node whose value determines the target of this call. This node
|
||||
* could be the qualifier of a virtual dispatch or the function-pointer
|
||||
* expression in a call to a function pointer. What they have in common is
|
||||
* that we need to find out which data flows there, and then it's up to the
|
||||
* `resolve` predicate to stitch that information together and resolve the
|
||||
* call.
|
||||
*/
|
||||
abstract DataFlow::Node getDispatchValue();
|
||||
|
||||
/** Gets a candidate target for this call. */
|
||||
abstract Function resolve();
|
||||
|
||||
/**
|
||||
* Whether `src` can flow to this call.
|
||||
*
|
||||
* Searches backwards from `getDispatchValue()` to `src`. The `allowFromArg`
|
||||
* parameter is true when the search is allowed to continue backwards into
|
||||
* a parameter; non-recursive callers should pass `_` for `allowFromArg`.
|
||||
*/
|
||||
predicate flowsFrom(DataFlow::Node src, boolean allowFromArg) {
|
||||
src = this.getDispatchValue() and allowFromArg = true
|
||||
or
|
||||
exists(DataFlow::Node other, boolean allowOtherFromArg |
|
||||
this.flowsFrom(other, allowOtherFromArg)
|
||||
|
|
||||
// Call argument
|
||||
exists(DataFlowCall call, Position i |
|
||||
other
|
||||
.(DataFlow::ParameterNode)
|
||||
.isParameterOf(pragma[only_bind_into](call).getStaticCallTarget(), i) and
|
||||
src.(ArgumentNode).argumentOf(call, pragma[only_bind_into](pragma[only_bind_out](i)))
|
||||
) and
|
||||
allowOtherFromArg = true and
|
||||
allowFromArg = true
|
||||
or
|
||||
// Call return
|
||||
exists(DataFlowCall call, ReturnKind returnKind |
|
||||
other = getAnOutNode(call, returnKind) and
|
||||
returnNodeWithKindAndEnclosingCallable(src, returnKind, call.getStaticCallTarget())
|
||||
) and
|
||||
allowFromArg = false
|
||||
or
|
||||
// Local flow
|
||||
DataFlow::localFlowStep(src, other) and
|
||||
allowFromArg = allowOtherFromArg
|
||||
or
|
||||
// Flow from global variable to load.
|
||||
exists(LoadInstruction load, GlobalOrNamespaceVariable var |
|
||||
var = src.asVariable() and
|
||||
other.asInstruction() = load and
|
||||
addressOfGlobal(load.getSourceAddress(), var) and
|
||||
// The `allowFromArg` concept doesn't play a role when `src` is a
|
||||
// global variable, so we just set it to a single arbitrary value for
|
||||
// performance.
|
||||
allowFromArg = true
|
||||
)
|
||||
or
|
||||
// Flow from store to global variable.
|
||||
exists(StoreInstruction store, GlobalOrNamespaceVariable var |
|
||||
var = other.asVariable() and
|
||||
store = src.asInstruction() and
|
||||
storeIntoGlobal(store, var) and
|
||||
// Setting `allowFromArg` to `true` like in the base case means we
|
||||
// treat a store to a global variable like the dispatch itself: flow
|
||||
// may come from anywhere.
|
||||
allowFromArg = true
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate storeIntoGlobal(StoreInstruction store, GlobalOrNamespaceVariable var) {
|
||||
addressOfGlobal(store.getDestinationAddress(), var)
|
||||
}
|
||||
|
||||
/** Holds if `addressInstr` is an instruction that produces the address of `var`. */
|
||||
private predicate addressOfGlobal(Instruction addressInstr, GlobalOrNamespaceVariable var) {
|
||||
// Access directly to the global variable
|
||||
addressInstr.(VariableAddressInstruction).getAstVariable() = var
|
||||
or
|
||||
// Access to a field on a global union
|
||||
exists(FieldAddressInstruction fa |
|
||||
fa = addressInstr and
|
||||
fa.getObjectAddress().(VariableAddressInstruction).getAstVariable() = var and
|
||||
fa.getField().getDeclaringType() instanceof Union
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A ReturnNode with its ReturnKind and its enclosing callable.
|
||||
*
|
||||
* Used to fix a join ordering issue in flowsFrom.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate returnNodeWithKindAndEnclosingCallable(
|
||||
ReturnNode node, ReturnKind kind, DataFlowCallable callable
|
||||
) {
|
||||
node.getKind() = kind and
|
||||
node.getEnclosingCallable() = callable
|
||||
}
|
||||
|
||||
/** Call through a function pointer. */
|
||||
private class DataSensitiveExprCall extends DataSensitiveCall {
|
||||
DataSensitiveExprCall() { not exists(this.getStaticCallTarget()) }
|
||||
|
||||
override DataFlow::Node getDispatchValue() { result.asInstruction() = this.getCallTarget() }
|
||||
|
||||
override Function resolve() {
|
||||
exists(FunctionInstruction fi |
|
||||
this.flowsFrom(DataFlow::instructionNode(fi), _) and
|
||||
result = fi.getFunctionSymbol()
|
||||
) and
|
||||
(
|
||||
this.getNumberOfArguments() <= result.getEffectiveNumberOfParameters() and
|
||||
this.getNumberOfArguments() >= result.getEffectiveNumberOfParameters()
|
||||
or
|
||||
result.isVarargs()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Call to a virtual function. */
|
||||
private class DataSensitiveOverriddenFunctionCall extends DataSensitiveCall {
|
||||
DataSensitiveOverriddenFunctionCall() {
|
||||
exists(this.getStaticCallTarget().(VirtualFunction).getAnOverridingFunction())
|
||||
}
|
||||
|
||||
override DataFlow::Node getDispatchValue() { result.asInstruction() = this.getThisArgument() }
|
||||
|
||||
override MemberFunction resolve() {
|
||||
exists(Class overridingClass |
|
||||
this.overrideMayAffectCall(overridingClass, result) and
|
||||
this.hasFlowFromCastFrom(overridingClass)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `this` is a virtual function call whose static target is
|
||||
* overridden by `overridingFunction` in `overridingClass`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate overrideMayAffectCall(Class overridingClass, MemberFunction overridingFunction) {
|
||||
overridingFunction.getAnOverriddenFunction+() = this.getStaticCallTarget().(VirtualFunction) and
|
||||
overridingFunction.getDeclaringType() = overridingClass
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the qualifier of `this` has flow from an upcast from
|
||||
* `derivedClass`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate hasFlowFromCastFrom(Class derivedClass) {
|
||||
exists(ConvertToBaseInstruction toBase |
|
||||
this.flowsFrom(DataFlow::instructionNode(toBase), _) and
|
||||
derivedClass = toBase.getDerivedClass()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` is a function with a body that has name `qualifiedName` and
|
||||
* `nparams` parameter count. See `functionSignature`.
|
||||
*/
|
||||
private predicate functionSignatureWithBody(string qualifiedName, int nparams, Function f) {
|
||||
functionSignature(f, qualifiedName, nparams) and
|
||||
exists(f.getBlock())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the target of `call` is a function _with no definition_ that has
|
||||
* name `qualifiedName` and `nparams` parameter count. See `functionSignature`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate callSignatureWithoutBody(string qualifiedName, int nparams, CallInstruction call) {
|
||||
exists(Function target |
|
||||
target = call.getStaticCallTarget() and
|
||||
not exists(target.getBlock()) and
|
||||
functionSignature(target, qualifiedName, nparams)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` has name `qualifiedName` and `nparams` parameter count. This is
|
||||
* an approximation of its signature for the purpose of matching functions that
|
||||
* might be the same across link targets.
|
||||
*/
|
||||
private predicate functionSignature(Function f, string qualifiedName, int nparams) {
|
||||
qualifiedName = f.getQualifiedName() and
|
||||
nparams = f.getNumberOfParameters() and
|
||||
not f.isStatic()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the set of viable implementations that can be called by `call`
|
||||
* might be improved by knowing the call context.
|
||||
*/
|
||||
predicate mayBenefitFromCallContext(CallInstruction call, Function f) {
|
||||
mayBenefitFromCallContext(call, f, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `call` is a call through a function pointer, and the pointer
|
||||
* value is given as the `arg`'th argument to `f`.
|
||||
*/
|
||||
private predicate mayBenefitFromCallContext(
|
||||
VirtualDispatch::DataSensitiveCall call, Function f, int arg
|
||||
) {
|
||||
f = pragma[only_bind_out](call).getEnclosingCallable() and
|
||||
exists(InitializeParameterInstruction init |
|
||||
not exists(call.getStaticCallTarget()) and
|
||||
init.getEnclosingFunction() = f and
|
||||
call.flowsFrom(DataFlow::instructionNode(init), _) and
|
||||
init.getParameter().getIndex() = arg
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a viable dispatch target of `call` in the context `ctx`. This is
|
||||
* restricted to those `call`s for which a context might make a difference.
|
||||
*/
|
||||
Function viableImplInCallContext(CallInstruction call, CallInstruction ctx) {
|
||||
result = viableCallable(call) and
|
||||
exists(int i, Function f |
|
||||
mayBenefitFromCallContext(pragma[only_bind_into](call), f, i) and
|
||||
f = ctx.getStaticCallTarget() and
|
||||
result = ctx.getArgument(i).getUnconvertedResultExpression().(FunctionAccess).getTarget()
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if arguments at position `apos` match parameters at position `ppos`. */
|
||||
pragma[inline]
|
||||
predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos) { ppos = apos }
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,396 +0,0 @@
|
||||
/**
|
||||
* DEPRECATED: Use `Make` and `MakeWithState` instead.
|
||||
*
|
||||
* Provides a `Configuration` class backwards-compatible interface to the data
|
||||
* flow library.
|
||||
*/
|
||||
|
||||
private import DataFlowImplCommon
|
||||
private import DataFlowImplSpecific::Private
|
||||
import DataFlowImplSpecific::Public
|
||||
private import DataFlowImpl
|
||||
import DataFlowImplCommonPublic
|
||||
import FlowStateString
|
||||
|
||||
/**
|
||||
* A configuration of interprocedural data flow analysis. This defines
|
||||
* sources, sinks, and any other configurable aspect of the analysis. Each
|
||||
* use of the global data flow library must define its own unique extension
|
||||
* of this abstract class. To create a configuration, extend this class with
|
||||
* a subclass whose characteristic predicate is a unique singleton string.
|
||||
* For example, write
|
||||
*
|
||||
* ```ql
|
||||
* class MyAnalysisConfiguration extends DataFlow::Configuration {
|
||||
* MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" }
|
||||
* // Override `isSource` and `isSink`.
|
||||
* // Optionally override `isBarrier`.
|
||||
* // Optionally override `isAdditionalFlowStep`.
|
||||
* }
|
||||
* ```
|
||||
* Conceptually, this defines a graph where the nodes are `DataFlow::Node`s and
|
||||
* the edges are those data-flow steps that preserve the value of the node
|
||||
* along with any additional edges defined by `isAdditionalFlowStep`.
|
||||
* Specifying nodes in `isBarrier` will remove those nodes from the graph, and
|
||||
* specifying nodes in `isBarrierIn` and/or `isBarrierOut` will remove in-going
|
||||
* and/or out-going edges from those nodes, respectively.
|
||||
*
|
||||
* Then, to query whether there is flow between some `source` and `sink`,
|
||||
* write
|
||||
*
|
||||
* ```ql
|
||||
* exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink))
|
||||
* ```
|
||||
*
|
||||
* Multiple configurations can coexist, but two classes extending
|
||||
* `DataFlow::Configuration` should never depend on each other. One of them
|
||||
* should instead depend on a `DataFlow2::Configuration`, a
|
||||
* `DataFlow3::Configuration`, or a `DataFlow4::Configuration`.
|
||||
*/
|
||||
abstract class Configuration extends string {
|
||||
bindingset[this]
|
||||
Configuration() { any() }
|
||||
|
||||
/**
|
||||
* Holds if `source` is a relevant data flow source.
|
||||
*/
|
||||
predicate isSource(Node source) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `source` is a relevant data flow source with the given initial
|
||||
* `state`.
|
||||
*/
|
||||
predicate isSource(Node source, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant data flow sink.
|
||||
*/
|
||||
predicate isSink(Node sink) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant data flow sink accepting `state`.
|
||||
*/
|
||||
predicate isSink(Node sink, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited. This completely removes
|
||||
* `node` from the data flow graph.
|
||||
*/
|
||||
predicate isBarrier(Node node) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited when the flow state is
|
||||
* `state`.
|
||||
*/
|
||||
predicate isBarrier(Node node, FlowState state) { none() }
|
||||
|
||||
/** Holds if data flow into `node` is prohibited. */
|
||||
predicate isBarrierIn(Node node) { none() }
|
||||
|
||||
/** Holds if data flow out of `node` is prohibited. */
|
||||
predicate isBarrierOut(Node node) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if data flow through nodes guarded by `guard` is prohibited.
|
||||
*/
|
||||
deprecated predicate isBarrierGuard(BarrierGuard guard) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if data flow through nodes guarded by `guard` is prohibited when
|
||||
* the flow state is `state`
|
||||
*/
|
||||
deprecated predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
|
||||
*/
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
|
||||
* This step is only applicable in `state1` and updates the flow state to `state2`.
|
||||
*/
|
||||
predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an arbitrary number of implicit read steps of content `c` may be
|
||||
* taken at `node`.
|
||||
*/
|
||||
predicate allowImplicitRead(Node node, ContentSet c) { none() }
|
||||
|
||||
/**
|
||||
* Gets the virtual dispatch branching limit when calculating field flow.
|
||||
* This can be overridden to a smaller value to improve performance (a
|
||||
* value of 0 disables field flow), or a larger value to get more results.
|
||||
*/
|
||||
int fieldFlowBranchLimit() { result = 2 }
|
||||
|
||||
/**
|
||||
* Gets a data flow configuration feature to add restrictions to the set of
|
||||
* valid flow paths.
|
||||
*
|
||||
* - `FeatureHasSourceCallContext`:
|
||||
* Assume that sources have some existing call context to disallow
|
||||
* conflicting return-flow directly following the source.
|
||||
* - `FeatureHasSinkCallContext`:
|
||||
* Assume that sinks have some existing call context to disallow
|
||||
* conflicting argument-to-parameter flow directly preceding the sink.
|
||||
* - `FeatureEqualSourceSinkCallContext`:
|
||||
* Implies both of the above and additionally ensures that the entire flow
|
||||
* path preserves the call context.
|
||||
*
|
||||
* These features are generally not relevant for typical end-to-end data flow
|
||||
* queries, but should only be used for constructing paths that need to
|
||||
* somehow be pluggable in another path context.
|
||||
*/
|
||||
FlowFeature getAFeature() { none() }
|
||||
|
||||
/** Holds if sources should be grouped in the result of `hasFlowPath`. */
|
||||
predicate sourceGrouping(Node source, string sourceGroup) { none() }
|
||||
|
||||
/** Holds if sinks should be grouped in the result of `hasFlowPath`. */
|
||||
predicate sinkGrouping(Node sink, string sinkGroup) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `source` to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlow(Node source, Node sink) { hasFlow(source, sink, this) }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `source` to `sink` for this configuration.
|
||||
*
|
||||
* The corresponding paths are generated from the end-points and the graph
|
||||
* included in the module `PathGraph`.
|
||||
*/
|
||||
predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlowTo(Node sink) { hasFlowTo(sink, this) }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlowToExpr(DataFlowExpr sink) { this.hasFlowTo(exprNode(sink)) }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `FlowExploration<explorationLimit>` instead.
|
||||
*
|
||||
* Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev`
|
||||
* measured in approximate number of interprocedural steps.
|
||||
*/
|
||||
deprecated int explorationLimit() { none() }
|
||||
|
||||
/**
|
||||
* Holds if hidden nodes should be included in the data flow graph.
|
||||
*
|
||||
* This feature should only be used for debugging or when the data flow graph
|
||||
* is not visualized (for example in a `path-problem` query).
|
||||
*/
|
||||
predicate includeHiddenNodes() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* This class exists to prevent mutual recursion between the user-overridden
|
||||
* member predicates of `Configuration` and the rest of the data-flow library.
|
||||
* Good performance cannot be guaranteed in the presence of such recursion, so
|
||||
* it should be replaced by using more than one copy of the data flow library.
|
||||
*/
|
||||
abstract private class ConfigurationRecursionPrevention extends Configuration {
|
||||
bindingset[this]
|
||||
ConfigurationRecursionPrevention() { any() }
|
||||
|
||||
override predicate hasFlow(Node source, Node sink) {
|
||||
strictcount(Node n | this.isSource(n)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSource(n, _)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSink(n)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSink(n, _)) < 0
|
||||
or
|
||||
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0
|
||||
or
|
||||
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, _, n2, _)) < 0
|
||||
or
|
||||
super.hasFlow(source, sink)
|
||||
}
|
||||
}
|
||||
|
||||
/** A bridge class to access the deprecated `isBarrierGuard`. */
|
||||
private class BarrierGuardGuardedNodeBridge extends Unit {
|
||||
abstract predicate guardedNode(Node n, Configuration config);
|
||||
|
||||
abstract predicate guardedNode(Node n, FlowState state, Configuration config);
|
||||
}
|
||||
|
||||
private class BarrierGuardGuardedNode extends BarrierGuardGuardedNodeBridge {
|
||||
deprecated override predicate guardedNode(Node n, Configuration config) {
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
}
|
||||
|
||||
deprecated override predicate guardedNode(Node n, FlowState state, Configuration config) {
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g, state) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private FlowState relevantState(Configuration config) {
|
||||
config.isSource(_, result) or
|
||||
config.isSink(_, result) or
|
||||
config.isBarrier(_, result) or
|
||||
config.isAdditionalFlowStep(_, result, _, _) or
|
||||
config.isAdditionalFlowStep(_, _, _, result)
|
||||
}
|
||||
|
||||
private newtype TConfigState =
|
||||
TMkConfigState(Configuration config, FlowState state) {
|
||||
state = relevantState(config) or state instanceof FlowStateEmpty
|
||||
}
|
||||
|
||||
private Configuration getConfig(TConfigState state) { state = TMkConfigState(result, _) }
|
||||
|
||||
private FlowState getState(TConfigState state) { state = TMkConfigState(_, result) }
|
||||
|
||||
private predicate singleConfiguration() { 1 = strictcount(Configuration c) }
|
||||
|
||||
private module Config implements FullStateConfigSig {
|
||||
class FlowState = TConfigState;
|
||||
|
||||
predicate isSource(Node source, FlowState state) {
|
||||
getConfig(state).isSource(source, getState(state))
|
||||
or
|
||||
getConfig(state).isSource(source) and getState(state) instanceof FlowStateEmpty
|
||||
}
|
||||
|
||||
predicate isSink(Node sink, FlowState state) {
|
||||
getConfig(state).isSink(sink, getState(state))
|
||||
or
|
||||
getConfig(state).isSink(sink) and getState(state) instanceof FlowStateEmpty
|
||||
}
|
||||
|
||||
predicate isBarrier(Node node) { none() }
|
||||
|
||||
predicate isBarrier(Node node, FlowState state) {
|
||||
getConfig(state).isBarrier(node, getState(state)) or
|
||||
getConfig(state).isBarrier(node) or
|
||||
any(BarrierGuardGuardedNodeBridge b).guardedNode(node, getState(state), getConfig(state)) or
|
||||
any(BarrierGuardGuardedNodeBridge b).guardedNode(node, getConfig(state))
|
||||
}
|
||||
|
||||
predicate isBarrierIn(Node node) { any(Configuration config).isBarrierIn(node) }
|
||||
|
||||
predicate isBarrierOut(Node node) { any(Configuration config).isBarrierOut(node) }
|
||||
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2) {
|
||||
singleConfiguration() and
|
||||
any(Configuration config).isAdditionalFlowStep(node1, node2)
|
||||
}
|
||||
|
||||
predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) {
|
||||
getConfig(state1).isAdditionalFlowStep(node1, getState(state1), node2, getState(state2)) and
|
||||
getConfig(state2) = getConfig(state1)
|
||||
or
|
||||
not singleConfiguration() and
|
||||
getConfig(state1).isAdditionalFlowStep(node1, node2) and
|
||||
state2 = state1
|
||||
}
|
||||
|
||||
predicate allowImplicitRead(Node node, ContentSet c) {
|
||||
any(Configuration config).allowImplicitRead(node, c)
|
||||
}
|
||||
|
||||
int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) }
|
||||
|
||||
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
||||
|
||||
predicate sourceGrouping(Node source, string sourceGroup) {
|
||||
any(Configuration config).sourceGrouping(source, sourceGroup)
|
||||
}
|
||||
|
||||
predicate sinkGrouping(Node sink, string sinkGroup) {
|
||||
any(Configuration config).sinkGrouping(sink, sinkGroup)
|
||||
}
|
||||
|
||||
predicate includeHiddenNodes() { any(Configuration config).includeHiddenNodes() }
|
||||
}
|
||||
|
||||
private import Impl<Config> as I
|
||||
import I
|
||||
|
||||
/**
|
||||
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
|
||||
* Only those `PathNode`s that are reachable from a source, and which can reach a sink, are generated.
|
||||
*/
|
||||
class PathNode instanceof I::PathNode {
|
||||
/** Gets a textual representation of this element. */
|
||||
final string toString() { result = super.toString() }
|
||||
|
||||
/**
|
||||
* Gets a textual representation of this element, including a textual
|
||||
* representation of the call context.
|
||||
*/
|
||||
final string toStringWithContext() { result = super.toStringWithContext() }
|
||||
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||
*/
|
||||
final predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
|
||||
/** Gets the underlying `Node`. */
|
||||
final Node getNode() { result = super.getNode() }
|
||||
|
||||
/** Gets the `FlowState` of this node. */
|
||||
final FlowState getState() { result = getState(super.getState()) }
|
||||
|
||||
/** Gets the associated configuration. */
|
||||
final Configuration getConfiguration() { result = getConfig(super.getState()) }
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
final PathNode getASuccessor() { result = super.getASuccessor() }
|
||||
|
||||
/** Holds if this node is a source. */
|
||||
final predicate isSource() { super.isSource() }
|
||||
|
||||
/** Holds if this node is a grouping of source nodes. */
|
||||
final predicate isSourceGroup(string group) { super.isSourceGroup(group) }
|
||||
|
||||
/** Holds if this node is a grouping of sink nodes. */
|
||||
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
|
||||
}
|
||||
|
||||
private predicate hasFlow(Node source, Node sink, Configuration config) {
|
||||
exists(PathNode source0, PathNode sink0 |
|
||||
hasFlowPath(source0, sink0, config) and
|
||||
source0.getNode() = source and
|
||||
sink0.getNode() = sink
|
||||
)
|
||||
}
|
||||
|
||||
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
|
||||
hasFlowPath(source, sink) and source.getConfiguration() = config
|
||||
}
|
||||
|
||||
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }
|
||||
|
||||
predicate flowsTo = hasFlow/3;
|
||||
@@ -1,396 +0,0 @@
|
||||
/**
|
||||
* DEPRECATED: Use `Make` and `MakeWithState` instead.
|
||||
*
|
||||
* Provides a `Configuration` class backwards-compatible interface to the data
|
||||
* flow library.
|
||||
*/
|
||||
|
||||
private import DataFlowImplCommon
|
||||
private import DataFlowImplSpecific::Private
|
||||
import DataFlowImplSpecific::Public
|
||||
private import DataFlowImpl
|
||||
import DataFlowImplCommonPublic
|
||||
import FlowStateString
|
||||
|
||||
/**
|
||||
* A configuration of interprocedural data flow analysis. This defines
|
||||
* sources, sinks, and any other configurable aspect of the analysis. Each
|
||||
* use of the global data flow library must define its own unique extension
|
||||
* of this abstract class. To create a configuration, extend this class with
|
||||
* a subclass whose characteristic predicate is a unique singleton string.
|
||||
* For example, write
|
||||
*
|
||||
* ```ql
|
||||
* class MyAnalysisConfiguration extends DataFlow::Configuration {
|
||||
* MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" }
|
||||
* // Override `isSource` and `isSink`.
|
||||
* // Optionally override `isBarrier`.
|
||||
* // Optionally override `isAdditionalFlowStep`.
|
||||
* }
|
||||
* ```
|
||||
* Conceptually, this defines a graph where the nodes are `DataFlow::Node`s and
|
||||
* the edges are those data-flow steps that preserve the value of the node
|
||||
* along with any additional edges defined by `isAdditionalFlowStep`.
|
||||
* Specifying nodes in `isBarrier` will remove those nodes from the graph, and
|
||||
* specifying nodes in `isBarrierIn` and/or `isBarrierOut` will remove in-going
|
||||
* and/or out-going edges from those nodes, respectively.
|
||||
*
|
||||
* Then, to query whether there is flow between some `source` and `sink`,
|
||||
* write
|
||||
*
|
||||
* ```ql
|
||||
* exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink))
|
||||
* ```
|
||||
*
|
||||
* Multiple configurations can coexist, but two classes extending
|
||||
* `DataFlow::Configuration` should never depend on each other. One of them
|
||||
* should instead depend on a `DataFlow2::Configuration`, a
|
||||
* `DataFlow3::Configuration`, or a `DataFlow4::Configuration`.
|
||||
*/
|
||||
abstract class Configuration extends string {
|
||||
bindingset[this]
|
||||
Configuration() { any() }
|
||||
|
||||
/**
|
||||
* Holds if `source` is a relevant data flow source.
|
||||
*/
|
||||
predicate isSource(Node source) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `source` is a relevant data flow source with the given initial
|
||||
* `state`.
|
||||
*/
|
||||
predicate isSource(Node source, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant data flow sink.
|
||||
*/
|
||||
predicate isSink(Node sink) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant data flow sink accepting `state`.
|
||||
*/
|
||||
predicate isSink(Node sink, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited. This completely removes
|
||||
* `node` from the data flow graph.
|
||||
*/
|
||||
predicate isBarrier(Node node) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited when the flow state is
|
||||
* `state`.
|
||||
*/
|
||||
predicate isBarrier(Node node, FlowState state) { none() }
|
||||
|
||||
/** Holds if data flow into `node` is prohibited. */
|
||||
predicate isBarrierIn(Node node) { none() }
|
||||
|
||||
/** Holds if data flow out of `node` is prohibited. */
|
||||
predicate isBarrierOut(Node node) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if data flow through nodes guarded by `guard` is prohibited.
|
||||
*/
|
||||
deprecated predicate isBarrierGuard(BarrierGuard guard) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if data flow through nodes guarded by `guard` is prohibited when
|
||||
* the flow state is `state`
|
||||
*/
|
||||
deprecated predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
|
||||
*/
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
|
||||
* This step is only applicable in `state1` and updates the flow state to `state2`.
|
||||
*/
|
||||
predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an arbitrary number of implicit read steps of content `c` may be
|
||||
* taken at `node`.
|
||||
*/
|
||||
predicate allowImplicitRead(Node node, ContentSet c) { none() }
|
||||
|
||||
/**
|
||||
* Gets the virtual dispatch branching limit when calculating field flow.
|
||||
* This can be overridden to a smaller value to improve performance (a
|
||||
* value of 0 disables field flow), or a larger value to get more results.
|
||||
*/
|
||||
int fieldFlowBranchLimit() { result = 2 }
|
||||
|
||||
/**
|
||||
* Gets a data flow configuration feature to add restrictions to the set of
|
||||
* valid flow paths.
|
||||
*
|
||||
* - `FeatureHasSourceCallContext`:
|
||||
* Assume that sources have some existing call context to disallow
|
||||
* conflicting return-flow directly following the source.
|
||||
* - `FeatureHasSinkCallContext`:
|
||||
* Assume that sinks have some existing call context to disallow
|
||||
* conflicting argument-to-parameter flow directly preceding the sink.
|
||||
* - `FeatureEqualSourceSinkCallContext`:
|
||||
* Implies both of the above and additionally ensures that the entire flow
|
||||
* path preserves the call context.
|
||||
*
|
||||
* These features are generally not relevant for typical end-to-end data flow
|
||||
* queries, but should only be used for constructing paths that need to
|
||||
* somehow be pluggable in another path context.
|
||||
*/
|
||||
FlowFeature getAFeature() { none() }
|
||||
|
||||
/** Holds if sources should be grouped in the result of `hasFlowPath`. */
|
||||
predicate sourceGrouping(Node source, string sourceGroup) { none() }
|
||||
|
||||
/** Holds if sinks should be grouped in the result of `hasFlowPath`. */
|
||||
predicate sinkGrouping(Node sink, string sinkGroup) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `source` to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlow(Node source, Node sink) { hasFlow(source, sink, this) }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `source` to `sink` for this configuration.
|
||||
*
|
||||
* The corresponding paths are generated from the end-points and the graph
|
||||
* included in the module `PathGraph`.
|
||||
*/
|
||||
predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlowTo(Node sink) { hasFlowTo(sink, this) }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlowToExpr(DataFlowExpr sink) { this.hasFlowTo(exprNode(sink)) }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `FlowExploration<explorationLimit>` instead.
|
||||
*
|
||||
* Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev`
|
||||
* measured in approximate number of interprocedural steps.
|
||||
*/
|
||||
deprecated int explorationLimit() { none() }
|
||||
|
||||
/**
|
||||
* Holds if hidden nodes should be included in the data flow graph.
|
||||
*
|
||||
* This feature should only be used for debugging or when the data flow graph
|
||||
* is not visualized (for example in a `path-problem` query).
|
||||
*/
|
||||
predicate includeHiddenNodes() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* This class exists to prevent mutual recursion between the user-overridden
|
||||
* member predicates of `Configuration` and the rest of the data-flow library.
|
||||
* Good performance cannot be guaranteed in the presence of such recursion, so
|
||||
* it should be replaced by using more than one copy of the data flow library.
|
||||
*/
|
||||
abstract private class ConfigurationRecursionPrevention extends Configuration {
|
||||
bindingset[this]
|
||||
ConfigurationRecursionPrevention() { any() }
|
||||
|
||||
override predicate hasFlow(Node source, Node sink) {
|
||||
strictcount(Node n | this.isSource(n)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSource(n, _)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSink(n)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSink(n, _)) < 0
|
||||
or
|
||||
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0
|
||||
or
|
||||
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, _, n2, _)) < 0
|
||||
or
|
||||
super.hasFlow(source, sink)
|
||||
}
|
||||
}
|
||||
|
||||
/** A bridge class to access the deprecated `isBarrierGuard`. */
|
||||
private class BarrierGuardGuardedNodeBridge extends Unit {
|
||||
abstract predicate guardedNode(Node n, Configuration config);
|
||||
|
||||
abstract predicate guardedNode(Node n, FlowState state, Configuration config);
|
||||
}
|
||||
|
||||
private class BarrierGuardGuardedNode extends BarrierGuardGuardedNodeBridge {
|
||||
deprecated override predicate guardedNode(Node n, Configuration config) {
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
}
|
||||
|
||||
deprecated override predicate guardedNode(Node n, FlowState state, Configuration config) {
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g, state) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private FlowState relevantState(Configuration config) {
|
||||
config.isSource(_, result) or
|
||||
config.isSink(_, result) or
|
||||
config.isBarrier(_, result) or
|
||||
config.isAdditionalFlowStep(_, result, _, _) or
|
||||
config.isAdditionalFlowStep(_, _, _, result)
|
||||
}
|
||||
|
||||
private newtype TConfigState =
|
||||
TMkConfigState(Configuration config, FlowState state) {
|
||||
state = relevantState(config) or state instanceof FlowStateEmpty
|
||||
}
|
||||
|
||||
private Configuration getConfig(TConfigState state) { state = TMkConfigState(result, _) }
|
||||
|
||||
private FlowState getState(TConfigState state) { state = TMkConfigState(_, result) }
|
||||
|
||||
private predicate singleConfiguration() { 1 = strictcount(Configuration c) }
|
||||
|
||||
private module Config implements FullStateConfigSig {
|
||||
class FlowState = TConfigState;
|
||||
|
||||
predicate isSource(Node source, FlowState state) {
|
||||
getConfig(state).isSource(source, getState(state))
|
||||
or
|
||||
getConfig(state).isSource(source) and getState(state) instanceof FlowStateEmpty
|
||||
}
|
||||
|
||||
predicate isSink(Node sink, FlowState state) {
|
||||
getConfig(state).isSink(sink, getState(state))
|
||||
or
|
||||
getConfig(state).isSink(sink) and getState(state) instanceof FlowStateEmpty
|
||||
}
|
||||
|
||||
predicate isBarrier(Node node) { none() }
|
||||
|
||||
predicate isBarrier(Node node, FlowState state) {
|
||||
getConfig(state).isBarrier(node, getState(state)) or
|
||||
getConfig(state).isBarrier(node) or
|
||||
any(BarrierGuardGuardedNodeBridge b).guardedNode(node, getState(state), getConfig(state)) or
|
||||
any(BarrierGuardGuardedNodeBridge b).guardedNode(node, getConfig(state))
|
||||
}
|
||||
|
||||
predicate isBarrierIn(Node node) { any(Configuration config).isBarrierIn(node) }
|
||||
|
||||
predicate isBarrierOut(Node node) { any(Configuration config).isBarrierOut(node) }
|
||||
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2) {
|
||||
singleConfiguration() and
|
||||
any(Configuration config).isAdditionalFlowStep(node1, node2)
|
||||
}
|
||||
|
||||
predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) {
|
||||
getConfig(state1).isAdditionalFlowStep(node1, getState(state1), node2, getState(state2)) and
|
||||
getConfig(state2) = getConfig(state1)
|
||||
or
|
||||
not singleConfiguration() and
|
||||
getConfig(state1).isAdditionalFlowStep(node1, node2) and
|
||||
state2 = state1
|
||||
}
|
||||
|
||||
predicate allowImplicitRead(Node node, ContentSet c) {
|
||||
any(Configuration config).allowImplicitRead(node, c)
|
||||
}
|
||||
|
||||
int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) }
|
||||
|
||||
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
||||
|
||||
predicate sourceGrouping(Node source, string sourceGroup) {
|
||||
any(Configuration config).sourceGrouping(source, sourceGroup)
|
||||
}
|
||||
|
||||
predicate sinkGrouping(Node sink, string sinkGroup) {
|
||||
any(Configuration config).sinkGrouping(sink, sinkGroup)
|
||||
}
|
||||
|
||||
predicate includeHiddenNodes() { any(Configuration config).includeHiddenNodes() }
|
||||
}
|
||||
|
||||
private import Impl<Config> as I
|
||||
import I
|
||||
|
||||
/**
|
||||
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
|
||||
* Only those `PathNode`s that are reachable from a source, and which can reach a sink, are generated.
|
||||
*/
|
||||
class PathNode instanceof I::PathNode {
|
||||
/** Gets a textual representation of this element. */
|
||||
final string toString() { result = super.toString() }
|
||||
|
||||
/**
|
||||
* Gets a textual representation of this element, including a textual
|
||||
* representation of the call context.
|
||||
*/
|
||||
final string toStringWithContext() { result = super.toStringWithContext() }
|
||||
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||
*/
|
||||
final predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
|
||||
/** Gets the underlying `Node`. */
|
||||
final Node getNode() { result = super.getNode() }
|
||||
|
||||
/** Gets the `FlowState` of this node. */
|
||||
final FlowState getState() { result = getState(super.getState()) }
|
||||
|
||||
/** Gets the associated configuration. */
|
||||
final Configuration getConfiguration() { result = getConfig(super.getState()) }
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
final PathNode getASuccessor() { result = super.getASuccessor() }
|
||||
|
||||
/** Holds if this node is a source. */
|
||||
final predicate isSource() { super.isSource() }
|
||||
|
||||
/** Holds if this node is a grouping of source nodes. */
|
||||
final predicate isSourceGroup(string group) { super.isSourceGroup(group) }
|
||||
|
||||
/** Holds if this node is a grouping of sink nodes. */
|
||||
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
|
||||
}
|
||||
|
||||
private predicate hasFlow(Node source, Node sink, Configuration config) {
|
||||
exists(PathNode source0, PathNode sink0 |
|
||||
hasFlowPath(source0, sink0, config) and
|
||||
source0.getNode() = source and
|
||||
sink0.getNode() = sink
|
||||
)
|
||||
}
|
||||
|
||||
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
|
||||
hasFlowPath(source, sink) and source.getConfiguration() = config
|
||||
}
|
||||
|
||||
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }
|
||||
|
||||
predicate flowsTo = hasFlow/3;
|
||||
@@ -1,396 +0,0 @@
|
||||
/**
|
||||
* DEPRECATED: Use `Make` and `MakeWithState` instead.
|
||||
*
|
||||
* Provides a `Configuration` class backwards-compatible interface to the data
|
||||
* flow library.
|
||||
*/
|
||||
|
||||
private import DataFlowImplCommon
|
||||
private import DataFlowImplSpecific::Private
|
||||
import DataFlowImplSpecific::Public
|
||||
private import DataFlowImpl
|
||||
import DataFlowImplCommonPublic
|
||||
import FlowStateString
|
||||
|
||||
/**
|
||||
* A configuration of interprocedural data flow analysis. This defines
|
||||
* sources, sinks, and any other configurable aspect of the analysis. Each
|
||||
* use of the global data flow library must define its own unique extension
|
||||
* of this abstract class. To create a configuration, extend this class with
|
||||
* a subclass whose characteristic predicate is a unique singleton string.
|
||||
* For example, write
|
||||
*
|
||||
* ```ql
|
||||
* class MyAnalysisConfiguration extends DataFlow::Configuration {
|
||||
* MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" }
|
||||
* // Override `isSource` and `isSink`.
|
||||
* // Optionally override `isBarrier`.
|
||||
* // Optionally override `isAdditionalFlowStep`.
|
||||
* }
|
||||
* ```
|
||||
* Conceptually, this defines a graph where the nodes are `DataFlow::Node`s and
|
||||
* the edges are those data-flow steps that preserve the value of the node
|
||||
* along with any additional edges defined by `isAdditionalFlowStep`.
|
||||
* Specifying nodes in `isBarrier` will remove those nodes from the graph, and
|
||||
* specifying nodes in `isBarrierIn` and/or `isBarrierOut` will remove in-going
|
||||
* and/or out-going edges from those nodes, respectively.
|
||||
*
|
||||
* Then, to query whether there is flow between some `source` and `sink`,
|
||||
* write
|
||||
*
|
||||
* ```ql
|
||||
* exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink))
|
||||
* ```
|
||||
*
|
||||
* Multiple configurations can coexist, but two classes extending
|
||||
* `DataFlow::Configuration` should never depend on each other. One of them
|
||||
* should instead depend on a `DataFlow2::Configuration`, a
|
||||
* `DataFlow3::Configuration`, or a `DataFlow4::Configuration`.
|
||||
*/
|
||||
abstract class Configuration extends string {
|
||||
bindingset[this]
|
||||
Configuration() { any() }
|
||||
|
||||
/**
|
||||
* Holds if `source` is a relevant data flow source.
|
||||
*/
|
||||
predicate isSource(Node source) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `source` is a relevant data flow source with the given initial
|
||||
* `state`.
|
||||
*/
|
||||
predicate isSource(Node source, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant data flow sink.
|
||||
*/
|
||||
predicate isSink(Node sink) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant data flow sink accepting `state`.
|
||||
*/
|
||||
predicate isSink(Node sink, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited. This completely removes
|
||||
* `node` from the data flow graph.
|
||||
*/
|
||||
predicate isBarrier(Node node) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited when the flow state is
|
||||
* `state`.
|
||||
*/
|
||||
predicate isBarrier(Node node, FlowState state) { none() }
|
||||
|
||||
/** Holds if data flow into `node` is prohibited. */
|
||||
predicate isBarrierIn(Node node) { none() }
|
||||
|
||||
/** Holds if data flow out of `node` is prohibited. */
|
||||
predicate isBarrierOut(Node node) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if data flow through nodes guarded by `guard` is prohibited.
|
||||
*/
|
||||
deprecated predicate isBarrierGuard(BarrierGuard guard) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if data flow through nodes guarded by `guard` is prohibited when
|
||||
* the flow state is `state`
|
||||
*/
|
||||
deprecated predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
|
||||
*/
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
|
||||
* This step is only applicable in `state1` and updates the flow state to `state2`.
|
||||
*/
|
||||
predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an arbitrary number of implicit read steps of content `c` may be
|
||||
* taken at `node`.
|
||||
*/
|
||||
predicate allowImplicitRead(Node node, ContentSet c) { none() }
|
||||
|
||||
/**
|
||||
* Gets the virtual dispatch branching limit when calculating field flow.
|
||||
* This can be overridden to a smaller value to improve performance (a
|
||||
* value of 0 disables field flow), or a larger value to get more results.
|
||||
*/
|
||||
int fieldFlowBranchLimit() { result = 2 }
|
||||
|
||||
/**
|
||||
* Gets a data flow configuration feature to add restrictions to the set of
|
||||
* valid flow paths.
|
||||
*
|
||||
* - `FeatureHasSourceCallContext`:
|
||||
* Assume that sources have some existing call context to disallow
|
||||
* conflicting return-flow directly following the source.
|
||||
* - `FeatureHasSinkCallContext`:
|
||||
* Assume that sinks have some existing call context to disallow
|
||||
* conflicting argument-to-parameter flow directly preceding the sink.
|
||||
* - `FeatureEqualSourceSinkCallContext`:
|
||||
* Implies both of the above and additionally ensures that the entire flow
|
||||
* path preserves the call context.
|
||||
*
|
||||
* These features are generally not relevant for typical end-to-end data flow
|
||||
* queries, but should only be used for constructing paths that need to
|
||||
* somehow be pluggable in another path context.
|
||||
*/
|
||||
FlowFeature getAFeature() { none() }
|
||||
|
||||
/** Holds if sources should be grouped in the result of `hasFlowPath`. */
|
||||
predicate sourceGrouping(Node source, string sourceGroup) { none() }
|
||||
|
||||
/** Holds if sinks should be grouped in the result of `hasFlowPath`. */
|
||||
predicate sinkGrouping(Node sink, string sinkGroup) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `source` to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlow(Node source, Node sink) { hasFlow(source, sink, this) }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `source` to `sink` for this configuration.
|
||||
*
|
||||
* The corresponding paths are generated from the end-points and the graph
|
||||
* included in the module `PathGraph`.
|
||||
*/
|
||||
predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlowTo(Node sink) { hasFlowTo(sink, this) }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlowToExpr(DataFlowExpr sink) { this.hasFlowTo(exprNode(sink)) }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `FlowExploration<explorationLimit>` instead.
|
||||
*
|
||||
* Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev`
|
||||
* measured in approximate number of interprocedural steps.
|
||||
*/
|
||||
deprecated int explorationLimit() { none() }
|
||||
|
||||
/**
|
||||
* Holds if hidden nodes should be included in the data flow graph.
|
||||
*
|
||||
* This feature should only be used for debugging or when the data flow graph
|
||||
* is not visualized (for example in a `path-problem` query).
|
||||
*/
|
||||
predicate includeHiddenNodes() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* This class exists to prevent mutual recursion between the user-overridden
|
||||
* member predicates of `Configuration` and the rest of the data-flow library.
|
||||
* Good performance cannot be guaranteed in the presence of such recursion, so
|
||||
* it should be replaced by using more than one copy of the data flow library.
|
||||
*/
|
||||
abstract private class ConfigurationRecursionPrevention extends Configuration {
|
||||
bindingset[this]
|
||||
ConfigurationRecursionPrevention() { any() }
|
||||
|
||||
override predicate hasFlow(Node source, Node sink) {
|
||||
strictcount(Node n | this.isSource(n)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSource(n, _)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSink(n)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSink(n, _)) < 0
|
||||
or
|
||||
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0
|
||||
or
|
||||
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, _, n2, _)) < 0
|
||||
or
|
||||
super.hasFlow(source, sink)
|
||||
}
|
||||
}
|
||||
|
||||
/** A bridge class to access the deprecated `isBarrierGuard`. */
|
||||
private class BarrierGuardGuardedNodeBridge extends Unit {
|
||||
abstract predicate guardedNode(Node n, Configuration config);
|
||||
|
||||
abstract predicate guardedNode(Node n, FlowState state, Configuration config);
|
||||
}
|
||||
|
||||
private class BarrierGuardGuardedNode extends BarrierGuardGuardedNodeBridge {
|
||||
deprecated override predicate guardedNode(Node n, Configuration config) {
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
}
|
||||
|
||||
deprecated override predicate guardedNode(Node n, FlowState state, Configuration config) {
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g, state) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private FlowState relevantState(Configuration config) {
|
||||
config.isSource(_, result) or
|
||||
config.isSink(_, result) or
|
||||
config.isBarrier(_, result) or
|
||||
config.isAdditionalFlowStep(_, result, _, _) or
|
||||
config.isAdditionalFlowStep(_, _, _, result)
|
||||
}
|
||||
|
||||
private newtype TConfigState =
|
||||
TMkConfigState(Configuration config, FlowState state) {
|
||||
state = relevantState(config) or state instanceof FlowStateEmpty
|
||||
}
|
||||
|
||||
private Configuration getConfig(TConfigState state) { state = TMkConfigState(result, _) }
|
||||
|
||||
private FlowState getState(TConfigState state) { state = TMkConfigState(_, result) }
|
||||
|
||||
private predicate singleConfiguration() { 1 = strictcount(Configuration c) }
|
||||
|
||||
private module Config implements FullStateConfigSig {
|
||||
class FlowState = TConfigState;
|
||||
|
||||
predicate isSource(Node source, FlowState state) {
|
||||
getConfig(state).isSource(source, getState(state))
|
||||
or
|
||||
getConfig(state).isSource(source) and getState(state) instanceof FlowStateEmpty
|
||||
}
|
||||
|
||||
predicate isSink(Node sink, FlowState state) {
|
||||
getConfig(state).isSink(sink, getState(state))
|
||||
or
|
||||
getConfig(state).isSink(sink) and getState(state) instanceof FlowStateEmpty
|
||||
}
|
||||
|
||||
predicate isBarrier(Node node) { none() }
|
||||
|
||||
predicate isBarrier(Node node, FlowState state) {
|
||||
getConfig(state).isBarrier(node, getState(state)) or
|
||||
getConfig(state).isBarrier(node) or
|
||||
any(BarrierGuardGuardedNodeBridge b).guardedNode(node, getState(state), getConfig(state)) or
|
||||
any(BarrierGuardGuardedNodeBridge b).guardedNode(node, getConfig(state))
|
||||
}
|
||||
|
||||
predicate isBarrierIn(Node node) { any(Configuration config).isBarrierIn(node) }
|
||||
|
||||
predicate isBarrierOut(Node node) { any(Configuration config).isBarrierOut(node) }
|
||||
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2) {
|
||||
singleConfiguration() and
|
||||
any(Configuration config).isAdditionalFlowStep(node1, node2)
|
||||
}
|
||||
|
||||
predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) {
|
||||
getConfig(state1).isAdditionalFlowStep(node1, getState(state1), node2, getState(state2)) and
|
||||
getConfig(state2) = getConfig(state1)
|
||||
or
|
||||
not singleConfiguration() and
|
||||
getConfig(state1).isAdditionalFlowStep(node1, node2) and
|
||||
state2 = state1
|
||||
}
|
||||
|
||||
predicate allowImplicitRead(Node node, ContentSet c) {
|
||||
any(Configuration config).allowImplicitRead(node, c)
|
||||
}
|
||||
|
||||
int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) }
|
||||
|
||||
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
||||
|
||||
predicate sourceGrouping(Node source, string sourceGroup) {
|
||||
any(Configuration config).sourceGrouping(source, sourceGroup)
|
||||
}
|
||||
|
||||
predicate sinkGrouping(Node sink, string sinkGroup) {
|
||||
any(Configuration config).sinkGrouping(sink, sinkGroup)
|
||||
}
|
||||
|
||||
predicate includeHiddenNodes() { any(Configuration config).includeHiddenNodes() }
|
||||
}
|
||||
|
||||
private import Impl<Config> as I
|
||||
import I
|
||||
|
||||
/**
|
||||
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
|
||||
* Only those `PathNode`s that are reachable from a source, and which can reach a sink, are generated.
|
||||
*/
|
||||
class PathNode instanceof I::PathNode {
|
||||
/** Gets a textual representation of this element. */
|
||||
final string toString() { result = super.toString() }
|
||||
|
||||
/**
|
||||
* Gets a textual representation of this element, including a textual
|
||||
* representation of the call context.
|
||||
*/
|
||||
final string toStringWithContext() { result = super.toStringWithContext() }
|
||||
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||
*/
|
||||
final predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
|
||||
/** Gets the underlying `Node`. */
|
||||
final Node getNode() { result = super.getNode() }
|
||||
|
||||
/** Gets the `FlowState` of this node. */
|
||||
final FlowState getState() { result = getState(super.getState()) }
|
||||
|
||||
/** Gets the associated configuration. */
|
||||
final Configuration getConfiguration() { result = getConfig(super.getState()) }
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
final PathNode getASuccessor() { result = super.getASuccessor() }
|
||||
|
||||
/** Holds if this node is a source. */
|
||||
final predicate isSource() { super.isSource() }
|
||||
|
||||
/** Holds if this node is a grouping of source nodes. */
|
||||
final predicate isSourceGroup(string group) { super.isSourceGroup(group) }
|
||||
|
||||
/** Holds if this node is a grouping of sink nodes. */
|
||||
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
|
||||
}
|
||||
|
||||
private predicate hasFlow(Node source, Node sink, Configuration config) {
|
||||
exists(PathNode source0, PathNode sink0 |
|
||||
hasFlowPath(source0, sink0, config) and
|
||||
source0.getNode() = source and
|
||||
sink0.getNode() = sink
|
||||
)
|
||||
}
|
||||
|
||||
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
|
||||
hasFlowPath(source, sink) and source.getConfiguration() = config
|
||||
}
|
||||
|
||||
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }
|
||||
|
||||
predicate flowsTo = hasFlow/3;
|
||||
@@ -1,396 +0,0 @@
|
||||
/**
|
||||
* DEPRECATED: Use `Make` and `MakeWithState` instead.
|
||||
*
|
||||
* Provides a `Configuration` class backwards-compatible interface to the data
|
||||
* flow library.
|
||||
*/
|
||||
|
||||
private import DataFlowImplCommon
|
||||
private import DataFlowImplSpecific::Private
|
||||
import DataFlowImplSpecific::Public
|
||||
private import DataFlowImpl
|
||||
import DataFlowImplCommonPublic
|
||||
import FlowStateString
|
||||
|
||||
/**
|
||||
* A configuration of interprocedural data flow analysis. This defines
|
||||
* sources, sinks, and any other configurable aspect of the analysis. Each
|
||||
* use of the global data flow library must define its own unique extension
|
||||
* of this abstract class. To create a configuration, extend this class with
|
||||
* a subclass whose characteristic predicate is a unique singleton string.
|
||||
* For example, write
|
||||
*
|
||||
* ```ql
|
||||
* class MyAnalysisConfiguration extends DataFlow::Configuration {
|
||||
* MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" }
|
||||
* // Override `isSource` and `isSink`.
|
||||
* // Optionally override `isBarrier`.
|
||||
* // Optionally override `isAdditionalFlowStep`.
|
||||
* }
|
||||
* ```
|
||||
* Conceptually, this defines a graph where the nodes are `DataFlow::Node`s and
|
||||
* the edges are those data-flow steps that preserve the value of the node
|
||||
* along with any additional edges defined by `isAdditionalFlowStep`.
|
||||
* Specifying nodes in `isBarrier` will remove those nodes from the graph, and
|
||||
* specifying nodes in `isBarrierIn` and/or `isBarrierOut` will remove in-going
|
||||
* and/or out-going edges from those nodes, respectively.
|
||||
*
|
||||
* Then, to query whether there is flow between some `source` and `sink`,
|
||||
* write
|
||||
*
|
||||
* ```ql
|
||||
* exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink))
|
||||
* ```
|
||||
*
|
||||
* Multiple configurations can coexist, but two classes extending
|
||||
* `DataFlow::Configuration` should never depend on each other. One of them
|
||||
* should instead depend on a `DataFlow2::Configuration`, a
|
||||
* `DataFlow3::Configuration`, or a `DataFlow4::Configuration`.
|
||||
*/
|
||||
abstract class Configuration extends string {
|
||||
bindingset[this]
|
||||
Configuration() { any() }
|
||||
|
||||
/**
|
||||
* Holds if `source` is a relevant data flow source.
|
||||
*/
|
||||
predicate isSource(Node source) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `source` is a relevant data flow source with the given initial
|
||||
* `state`.
|
||||
*/
|
||||
predicate isSource(Node source, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant data flow sink.
|
||||
*/
|
||||
predicate isSink(Node sink) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant data flow sink accepting `state`.
|
||||
*/
|
||||
predicate isSink(Node sink, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited. This completely removes
|
||||
* `node` from the data flow graph.
|
||||
*/
|
||||
predicate isBarrier(Node node) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited when the flow state is
|
||||
* `state`.
|
||||
*/
|
||||
predicate isBarrier(Node node, FlowState state) { none() }
|
||||
|
||||
/** Holds if data flow into `node` is prohibited. */
|
||||
predicate isBarrierIn(Node node) { none() }
|
||||
|
||||
/** Holds if data flow out of `node` is prohibited. */
|
||||
predicate isBarrierOut(Node node) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if data flow through nodes guarded by `guard` is prohibited.
|
||||
*/
|
||||
deprecated predicate isBarrierGuard(BarrierGuard guard) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if data flow through nodes guarded by `guard` is prohibited when
|
||||
* the flow state is `state`
|
||||
*/
|
||||
deprecated predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
|
||||
*/
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
|
||||
* This step is only applicable in `state1` and updates the flow state to `state2`.
|
||||
*/
|
||||
predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an arbitrary number of implicit read steps of content `c` may be
|
||||
* taken at `node`.
|
||||
*/
|
||||
predicate allowImplicitRead(Node node, ContentSet c) { none() }
|
||||
|
||||
/**
|
||||
* Gets the virtual dispatch branching limit when calculating field flow.
|
||||
* This can be overridden to a smaller value to improve performance (a
|
||||
* value of 0 disables field flow), or a larger value to get more results.
|
||||
*/
|
||||
int fieldFlowBranchLimit() { result = 2 }
|
||||
|
||||
/**
|
||||
* Gets a data flow configuration feature to add restrictions to the set of
|
||||
* valid flow paths.
|
||||
*
|
||||
* - `FeatureHasSourceCallContext`:
|
||||
* Assume that sources have some existing call context to disallow
|
||||
* conflicting return-flow directly following the source.
|
||||
* - `FeatureHasSinkCallContext`:
|
||||
* Assume that sinks have some existing call context to disallow
|
||||
* conflicting argument-to-parameter flow directly preceding the sink.
|
||||
* - `FeatureEqualSourceSinkCallContext`:
|
||||
* Implies both of the above and additionally ensures that the entire flow
|
||||
* path preserves the call context.
|
||||
*
|
||||
* These features are generally not relevant for typical end-to-end data flow
|
||||
* queries, but should only be used for constructing paths that need to
|
||||
* somehow be pluggable in another path context.
|
||||
*/
|
||||
FlowFeature getAFeature() { none() }
|
||||
|
||||
/** Holds if sources should be grouped in the result of `hasFlowPath`. */
|
||||
predicate sourceGrouping(Node source, string sourceGroup) { none() }
|
||||
|
||||
/** Holds if sinks should be grouped in the result of `hasFlowPath`. */
|
||||
predicate sinkGrouping(Node sink, string sinkGroup) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `source` to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlow(Node source, Node sink) { hasFlow(source, sink, this) }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `source` to `sink` for this configuration.
|
||||
*
|
||||
* The corresponding paths are generated from the end-points and the graph
|
||||
* included in the module `PathGraph`.
|
||||
*/
|
||||
predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlowTo(Node sink) { hasFlowTo(sink, this) }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
*/
|
||||
predicate hasFlowToExpr(DataFlowExpr sink) { this.hasFlowTo(exprNode(sink)) }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `FlowExploration<explorationLimit>` instead.
|
||||
*
|
||||
* Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev`
|
||||
* measured in approximate number of interprocedural steps.
|
||||
*/
|
||||
deprecated int explorationLimit() { none() }
|
||||
|
||||
/**
|
||||
* Holds if hidden nodes should be included in the data flow graph.
|
||||
*
|
||||
* This feature should only be used for debugging or when the data flow graph
|
||||
* is not visualized (for example in a `path-problem` query).
|
||||
*/
|
||||
predicate includeHiddenNodes() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* This class exists to prevent mutual recursion between the user-overridden
|
||||
* member predicates of `Configuration` and the rest of the data-flow library.
|
||||
* Good performance cannot be guaranteed in the presence of such recursion, so
|
||||
* it should be replaced by using more than one copy of the data flow library.
|
||||
*/
|
||||
abstract private class ConfigurationRecursionPrevention extends Configuration {
|
||||
bindingset[this]
|
||||
ConfigurationRecursionPrevention() { any() }
|
||||
|
||||
override predicate hasFlow(Node source, Node sink) {
|
||||
strictcount(Node n | this.isSource(n)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSource(n, _)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSink(n)) < 0
|
||||
or
|
||||
strictcount(Node n | this.isSink(n, _)) < 0
|
||||
or
|
||||
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0
|
||||
or
|
||||
strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, _, n2, _)) < 0
|
||||
or
|
||||
super.hasFlow(source, sink)
|
||||
}
|
||||
}
|
||||
|
||||
/** A bridge class to access the deprecated `isBarrierGuard`. */
|
||||
private class BarrierGuardGuardedNodeBridge extends Unit {
|
||||
abstract predicate guardedNode(Node n, Configuration config);
|
||||
|
||||
abstract predicate guardedNode(Node n, FlowState state, Configuration config);
|
||||
}
|
||||
|
||||
private class BarrierGuardGuardedNode extends BarrierGuardGuardedNodeBridge {
|
||||
deprecated override predicate guardedNode(Node n, Configuration config) {
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
}
|
||||
|
||||
deprecated override predicate guardedNode(Node n, FlowState state, Configuration config) {
|
||||
exists(BarrierGuard g |
|
||||
config.isBarrierGuard(g, state) and
|
||||
n = g.getAGuardedNode()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private FlowState relevantState(Configuration config) {
|
||||
config.isSource(_, result) or
|
||||
config.isSink(_, result) or
|
||||
config.isBarrier(_, result) or
|
||||
config.isAdditionalFlowStep(_, result, _, _) or
|
||||
config.isAdditionalFlowStep(_, _, _, result)
|
||||
}
|
||||
|
||||
private newtype TConfigState =
|
||||
TMkConfigState(Configuration config, FlowState state) {
|
||||
state = relevantState(config) or state instanceof FlowStateEmpty
|
||||
}
|
||||
|
||||
private Configuration getConfig(TConfigState state) { state = TMkConfigState(result, _) }
|
||||
|
||||
private FlowState getState(TConfigState state) { state = TMkConfigState(_, result) }
|
||||
|
||||
private predicate singleConfiguration() { 1 = strictcount(Configuration c) }
|
||||
|
||||
private module Config implements FullStateConfigSig {
|
||||
class FlowState = TConfigState;
|
||||
|
||||
predicate isSource(Node source, FlowState state) {
|
||||
getConfig(state).isSource(source, getState(state))
|
||||
or
|
||||
getConfig(state).isSource(source) and getState(state) instanceof FlowStateEmpty
|
||||
}
|
||||
|
||||
predicate isSink(Node sink, FlowState state) {
|
||||
getConfig(state).isSink(sink, getState(state))
|
||||
or
|
||||
getConfig(state).isSink(sink) and getState(state) instanceof FlowStateEmpty
|
||||
}
|
||||
|
||||
predicate isBarrier(Node node) { none() }
|
||||
|
||||
predicate isBarrier(Node node, FlowState state) {
|
||||
getConfig(state).isBarrier(node, getState(state)) or
|
||||
getConfig(state).isBarrier(node) or
|
||||
any(BarrierGuardGuardedNodeBridge b).guardedNode(node, getState(state), getConfig(state)) or
|
||||
any(BarrierGuardGuardedNodeBridge b).guardedNode(node, getConfig(state))
|
||||
}
|
||||
|
||||
predicate isBarrierIn(Node node) { any(Configuration config).isBarrierIn(node) }
|
||||
|
||||
predicate isBarrierOut(Node node) { any(Configuration config).isBarrierOut(node) }
|
||||
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2) {
|
||||
singleConfiguration() and
|
||||
any(Configuration config).isAdditionalFlowStep(node1, node2)
|
||||
}
|
||||
|
||||
predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) {
|
||||
getConfig(state1).isAdditionalFlowStep(node1, getState(state1), node2, getState(state2)) and
|
||||
getConfig(state2) = getConfig(state1)
|
||||
or
|
||||
not singleConfiguration() and
|
||||
getConfig(state1).isAdditionalFlowStep(node1, node2) and
|
||||
state2 = state1
|
||||
}
|
||||
|
||||
predicate allowImplicitRead(Node node, ContentSet c) {
|
||||
any(Configuration config).allowImplicitRead(node, c)
|
||||
}
|
||||
|
||||
int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) }
|
||||
|
||||
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
||||
|
||||
predicate sourceGrouping(Node source, string sourceGroup) {
|
||||
any(Configuration config).sourceGrouping(source, sourceGroup)
|
||||
}
|
||||
|
||||
predicate sinkGrouping(Node sink, string sinkGroup) {
|
||||
any(Configuration config).sinkGrouping(sink, sinkGroup)
|
||||
}
|
||||
|
||||
predicate includeHiddenNodes() { any(Configuration config).includeHiddenNodes() }
|
||||
}
|
||||
|
||||
private import Impl<Config> as I
|
||||
import I
|
||||
|
||||
/**
|
||||
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
|
||||
* Only those `PathNode`s that are reachable from a source, and which can reach a sink, are generated.
|
||||
*/
|
||||
class PathNode instanceof I::PathNode {
|
||||
/** Gets a textual representation of this element. */
|
||||
final string toString() { result = super.toString() }
|
||||
|
||||
/**
|
||||
* Gets a textual representation of this element, including a textual
|
||||
* representation of the call context.
|
||||
*/
|
||||
final string toStringWithContext() { result = super.toStringWithContext() }
|
||||
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||
*/
|
||||
final predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
|
||||
/** Gets the underlying `Node`. */
|
||||
final Node getNode() { result = super.getNode() }
|
||||
|
||||
/** Gets the `FlowState` of this node. */
|
||||
final FlowState getState() { result = getState(super.getState()) }
|
||||
|
||||
/** Gets the associated configuration. */
|
||||
final Configuration getConfiguration() { result = getConfig(super.getState()) }
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
final PathNode getASuccessor() { result = super.getASuccessor() }
|
||||
|
||||
/** Holds if this node is a source. */
|
||||
final predicate isSource() { super.isSource() }
|
||||
|
||||
/** Holds if this node is a grouping of source nodes. */
|
||||
final predicate isSourceGroup(string group) { super.isSourceGroup(group) }
|
||||
|
||||
/** Holds if this node is a grouping of sink nodes. */
|
||||
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
|
||||
}
|
||||
|
||||
private predicate hasFlow(Node source, Node sink, Configuration config) {
|
||||
exists(PathNode source0, PathNode sink0 |
|
||||
hasFlowPath(source0, sink0, config) and
|
||||
source0.getNode() = source and
|
||||
sink0.getNode() = sink
|
||||
)
|
||||
}
|
||||
|
||||
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
|
||||
hasFlowPath(source, sink) and source.getConfiguration() = config
|
||||
}
|
||||
|
||||
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }
|
||||
|
||||
predicate flowsTo = hasFlow/3;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,278 +0,0 @@
|
||||
/**
|
||||
* Provides consistency queries for checking invariants in the language-specific
|
||||
* data-flow classes and predicates.
|
||||
*/
|
||||
|
||||
private import DataFlowImplSpecific::Private
|
||||
private import DataFlowImplSpecific::Public
|
||||
private import tainttracking1.TaintTrackingParameter::Private
|
||||
private import tainttracking1.TaintTrackingParameter::Public
|
||||
|
||||
module Consistency {
|
||||
private newtype TConsistencyConfiguration = MkConsistencyConfiguration()
|
||||
|
||||
/** A class for configuring the consistency queries. */
|
||||
class ConsistencyConfiguration extends TConsistencyConfiguration {
|
||||
string toString() { none() }
|
||||
|
||||
/** Holds if `n` should be excluded from the consistency test `uniqueEnclosingCallable`. */
|
||||
predicate uniqueEnclosingCallableExclude(Node n) { none() }
|
||||
|
||||
/** Holds if `n` should be excluded from the consistency test `uniqueNodeLocation`. */
|
||||
predicate uniqueNodeLocationExclude(Node n) { none() }
|
||||
|
||||
/** Holds if `n` should be excluded from the consistency test `missingLocation`. */
|
||||
predicate missingLocationExclude(Node n) { none() }
|
||||
|
||||
/** Holds if `n` should be excluded from the consistency test `postWithInFlow`. */
|
||||
predicate postWithInFlowExclude(Node n) { none() }
|
||||
|
||||
/** Holds if `n` should be excluded from the consistency test `argHasPostUpdate`. */
|
||||
predicate argHasPostUpdateExclude(ArgumentNode n) { none() }
|
||||
|
||||
/** Holds if `n` should be excluded from the consistency test `reverseRead`. */
|
||||
predicate reverseReadExclude(Node n) { none() }
|
||||
|
||||
/** Holds if `n` should be excluded from the consistency test `postHasUniquePre`. */
|
||||
predicate postHasUniquePreExclude(PostUpdateNode n) { none() }
|
||||
|
||||
/** Holds if `n` should be excluded from the consistency test `uniquePostUpdate`. */
|
||||
predicate uniquePostUpdateExclude(Node n) { none() }
|
||||
|
||||
/** Holds if `(call, ctx)` should be excluded from the consistency test `viableImplInCallContextTooLargeExclude`. */
|
||||
predicate viableImplInCallContextTooLargeExclude(
|
||||
DataFlowCall call, DataFlowCall ctx, DataFlowCallable callable
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
/** Holds if `(c, pos, p)` should be excluded from the consistency test `uniqueParameterNodeAtPosition`. */
|
||||
predicate uniqueParameterNodeAtPositionExclude(DataFlowCallable c, ParameterPosition pos, Node p) {
|
||||
none()
|
||||
}
|
||||
|
||||
/** Holds if `(c, pos, p)` should be excluded from the consistency test `uniqueParameterNodePosition`. */
|
||||
predicate uniqueParameterNodePositionExclude(DataFlowCallable c, ParameterPosition pos, Node p) {
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
private class RelevantNode extends Node {
|
||||
RelevantNode() {
|
||||
this instanceof ArgumentNode or
|
||||
this instanceof ParameterNode or
|
||||
this instanceof ReturnNode or
|
||||
this = getAnOutNode(_, _) or
|
||||
simpleLocalFlowStep(this, _) or
|
||||
simpleLocalFlowStep(_, this) or
|
||||
jumpStep(this, _) or
|
||||
jumpStep(_, this) or
|
||||
storeStep(this, _, _) or
|
||||
storeStep(_, _, this) or
|
||||
readStep(this, _, _) or
|
||||
readStep(_, _, this) or
|
||||
defaultAdditionalTaintStep(this, _) or
|
||||
defaultAdditionalTaintStep(_, this)
|
||||
}
|
||||
}
|
||||
|
||||
query predicate uniqueEnclosingCallable(Node n, string msg) {
|
||||
exists(int c |
|
||||
n instanceof RelevantNode and
|
||||
c = count(nodeGetEnclosingCallable(n)) and
|
||||
c != 1 and
|
||||
not any(ConsistencyConfiguration conf).uniqueEnclosingCallableExclude(n) and
|
||||
msg = "Node should have one enclosing callable but has " + c + "."
|
||||
)
|
||||
}
|
||||
|
||||
query predicate uniqueType(Node n, string msg) {
|
||||
exists(int c |
|
||||
n instanceof RelevantNode and
|
||||
c = count(getNodeType(n)) and
|
||||
c != 1 and
|
||||
msg = "Node should have one type but has " + c + "."
|
||||
)
|
||||
}
|
||||
|
||||
query predicate uniqueNodeLocation(Node n, string msg) {
|
||||
exists(int c |
|
||||
c =
|
||||
count(string filepath, int startline, int startcolumn, int endline, int endcolumn |
|
||||
n.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
) and
|
||||
c != 1 and
|
||||
not any(ConsistencyConfiguration conf).uniqueNodeLocationExclude(n) and
|
||||
msg = "Node should have one location but has " + c + "."
|
||||
)
|
||||
}
|
||||
|
||||
query predicate missingLocation(string msg) {
|
||||
exists(int c |
|
||||
c =
|
||||
strictcount(Node n |
|
||||
not n.hasLocationInfo(_, _, _, _, _) and
|
||||
not any(ConsistencyConfiguration conf).missingLocationExclude(n)
|
||||
) and
|
||||
msg = "Nodes without location: " + c
|
||||
)
|
||||
}
|
||||
|
||||
query predicate uniqueNodeToString(Node n, string msg) {
|
||||
exists(int c |
|
||||
c = count(n.toString()) and
|
||||
c != 1 and
|
||||
msg = "Node should have one toString but has " + c + "."
|
||||
)
|
||||
}
|
||||
|
||||
query predicate missingToString(string msg) {
|
||||
exists(int c |
|
||||
c = strictcount(Node n | not exists(n.toString())) and
|
||||
msg = "Nodes without toString: " + c
|
||||
)
|
||||
}
|
||||
|
||||
query predicate parameterCallable(ParameterNode p, string msg) {
|
||||
exists(DataFlowCallable c | isParameterNode(p, c, _) and c != nodeGetEnclosingCallable(p)) and
|
||||
msg = "Callable mismatch for parameter."
|
||||
}
|
||||
|
||||
query predicate localFlowIsLocal(Node n1, Node n2, string msg) {
|
||||
simpleLocalFlowStep(n1, n2) and
|
||||
nodeGetEnclosingCallable(n1) != nodeGetEnclosingCallable(n2) and
|
||||
msg = "Local flow step does not preserve enclosing callable."
|
||||
}
|
||||
|
||||
query predicate readStepIsLocal(Node n1, Node n2, string msg) {
|
||||
readStep(n1, _, n2) and
|
||||
nodeGetEnclosingCallable(n1) != nodeGetEnclosingCallable(n2) and
|
||||
msg = "Read step does not preserve enclosing callable."
|
||||
}
|
||||
|
||||
query predicate storeStepIsLocal(Node n1, Node n2, string msg) {
|
||||
storeStep(n1, _, n2) and
|
||||
nodeGetEnclosingCallable(n1) != nodeGetEnclosingCallable(n2) and
|
||||
msg = "Store step does not preserve enclosing callable."
|
||||
}
|
||||
|
||||
private DataFlowType typeRepr() { result = getNodeType(_) }
|
||||
|
||||
query predicate compatibleTypesReflexive(DataFlowType t, string msg) {
|
||||
t = typeRepr() and
|
||||
not compatibleTypes(t, t) and
|
||||
msg = "Type compatibility predicate is not reflexive."
|
||||
}
|
||||
|
||||
query predicate unreachableNodeCCtx(Node n, DataFlowCall call, string msg) {
|
||||
isUnreachableInCall(n, call) and
|
||||
exists(DataFlowCallable c |
|
||||
c = nodeGetEnclosingCallable(n) and
|
||||
not viableCallable(call) = c
|
||||
) and
|
||||
msg = "Call context for isUnreachableInCall is inconsistent with call graph."
|
||||
}
|
||||
|
||||
query predicate localCallNodes(DataFlowCall call, Node n, string msg) {
|
||||
(
|
||||
n = getAnOutNode(call, _) and
|
||||
msg = "OutNode and call does not share enclosing callable."
|
||||
or
|
||||
n.(ArgumentNode).argumentOf(call, _) and
|
||||
msg = "ArgumentNode and call does not share enclosing callable."
|
||||
) and
|
||||
nodeGetEnclosingCallable(n) != call.getEnclosingCallable()
|
||||
}
|
||||
|
||||
// This predicate helps the compiler forget that in some languages
|
||||
// it is impossible for a result of `getPreUpdateNode` to be an
|
||||
// instance of `PostUpdateNode`.
|
||||
private Node getPre(PostUpdateNode n) {
|
||||
result = n.getPreUpdateNode()
|
||||
or
|
||||
none()
|
||||
}
|
||||
|
||||
query predicate postIsNotPre(PostUpdateNode n, string msg) {
|
||||
getPre(n) = n and
|
||||
msg = "PostUpdateNode should not equal its pre-update node."
|
||||
}
|
||||
|
||||
query predicate postHasUniquePre(PostUpdateNode n, string msg) {
|
||||
not any(ConsistencyConfiguration conf).postHasUniquePreExclude(n) and
|
||||
exists(int c |
|
||||
c = count(n.getPreUpdateNode()) and
|
||||
c != 1 and
|
||||
msg = "PostUpdateNode should have one pre-update node but has " + c + "."
|
||||
)
|
||||
}
|
||||
|
||||
query predicate uniquePostUpdate(Node n, string msg) {
|
||||
not any(ConsistencyConfiguration conf).uniquePostUpdateExclude(n) and
|
||||
1 < strictcount(PostUpdateNode post | post.getPreUpdateNode() = n) and
|
||||
msg = "Node has multiple PostUpdateNodes."
|
||||
}
|
||||
|
||||
query predicate postIsInSameCallable(PostUpdateNode n, string msg) {
|
||||
nodeGetEnclosingCallable(n) != nodeGetEnclosingCallable(n.getPreUpdateNode()) and
|
||||
msg = "PostUpdateNode does not share callable with its pre-update node."
|
||||
}
|
||||
|
||||
private predicate hasPost(Node n) { exists(PostUpdateNode post | post.getPreUpdateNode() = n) }
|
||||
|
||||
query predicate reverseRead(Node n, string msg) {
|
||||
exists(Node n2 | readStep(n, _, n2) and hasPost(n2) and not hasPost(n)) and
|
||||
not any(ConsistencyConfiguration conf).reverseReadExclude(n) and
|
||||
msg = "Origin of readStep is missing a PostUpdateNode."
|
||||
}
|
||||
|
||||
query predicate argHasPostUpdate(ArgumentNode n, string msg) {
|
||||
not hasPost(n) and
|
||||
not any(ConsistencyConfiguration c).argHasPostUpdateExclude(n) and
|
||||
msg = "ArgumentNode is missing PostUpdateNode."
|
||||
}
|
||||
|
||||
// This predicate helps the compiler forget that in some languages
|
||||
// it is impossible for a `PostUpdateNode` to be the target of
|
||||
// `simpleLocalFlowStep`.
|
||||
private predicate isPostUpdateNode(Node n) { n instanceof PostUpdateNode or none() }
|
||||
|
||||
query predicate postWithInFlow(Node n, string msg) {
|
||||
isPostUpdateNode(n) and
|
||||
not clearsContent(n, _) and
|
||||
simpleLocalFlowStep(_, n) and
|
||||
not any(ConsistencyConfiguration c).postWithInFlowExclude(n) and
|
||||
msg = "PostUpdateNode should not be the target of local flow."
|
||||
}
|
||||
|
||||
query predicate viableImplInCallContextTooLarge(
|
||||
DataFlowCall call, DataFlowCall ctx, DataFlowCallable callable
|
||||
) {
|
||||
callable = viableImplInCallContext(call, ctx) and
|
||||
not callable = viableCallable(call) and
|
||||
not any(ConsistencyConfiguration c).viableImplInCallContextTooLargeExclude(call, ctx, callable)
|
||||
}
|
||||
|
||||
query predicate uniqueParameterNodeAtPosition(
|
||||
DataFlowCallable c, ParameterPosition pos, Node p, string msg
|
||||
) {
|
||||
not any(ConsistencyConfiguration conf).uniqueParameterNodeAtPositionExclude(c, pos, p) and
|
||||
isParameterNode(p, c, pos) and
|
||||
not exists(unique(Node p0 | isParameterNode(p0, c, pos))) and
|
||||
msg = "Parameters with overlapping positions."
|
||||
}
|
||||
|
||||
query predicate uniqueParameterNodePosition(
|
||||
DataFlowCallable c, ParameterPosition pos, Node p, string msg
|
||||
) {
|
||||
not any(ConsistencyConfiguration conf).uniqueParameterNodePositionExclude(c, pos, p) and
|
||||
isParameterNode(p, c, pos) and
|
||||
not exists(unique(ParameterPosition pos0 | isParameterNode(p, c, pos0))) and
|
||||
msg = "Parameter node with multiple positions."
|
||||
}
|
||||
|
||||
query predicate uniqueContentApprox(Content c, string msg) {
|
||||
not exists(unique(ContentApprox approx | approx = getContentApprox(c))) and
|
||||
msg = "Non-unique content approximation."
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
/**
|
||||
* Provides IR-specific definitions for use in the data flow library.
|
||||
*/
|
||||
module Private {
|
||||
import DataFlowPrivate
|
||||
import DataFlowDispatch
|
||||
}
|
||||
|
||||
module Public {
|
||||
import DataFlowUtil
|
||||
}
|
||||
@@ -1,576 +0,0 @@
|
||||
private import cpp as Cpp
|
||||
private import DataFlowUtil
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import DataFlowDispatch
|
||||
private import DataFlowImplConsistency
|
||||
private import semmle.code.cpp.ir.internal.IRCppLanguage
|
||||
private import SsaInternals as Ssa
|
||||
|
||||
/** Gets the callable in which this node occurs. */
|
||||
DataFlowCallable nodeGetEnclosingCallable(Node n) { result = n.getEnclosingCallable() }
|
||||
|
||||
/** Holds if `p` is a `ParameterNode` of `c` with position `pos`. */
|
||||
predicate isParameterNode(ParameterNode p, DataFlowCallable c, ParameterPosition pos) {
|
||||
p.isParameterOf(c, pos)
|
||||
}
|
||||
|
||||
/** Holds if `arg` is an `ArgumentNode` of `c` with position `pos`. */
|
||||
predicate isArgumentNode(ArgumentNode arg, DataFlowCall c, ArgumentPosition pos) {
|
||||
arg.argumentOf(c, pos)
|
||||
}
|
||||
|
||||
/**
|
||||
* A data flow node that occurs as the argument of a call and is passed as-is
|
||||
* to the callable. Instance arguments (`this` pointer) and read side effects
|
||||
* on parameters are also included.
|
||||
*/
|
||||
abstract class ArgumentNode extends Node {
|
||||
/**
|
||||
* Holds if this argument occurs at the given position in the given call.
|
||||
* The instance argument is considered to have index `-1`.
|
||||
*/
|
||||
abstract predicate argumentOf(DataFlowCall call, ArgumentPosition pos);
|
||||
|
||||
/** Gets the call in which this node is an argument. */
|
||||
DataFlowCall getCall() { this.argumentOf(result, _) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A data flow node that occurs as the argument to a call, or an
|
||||
* implicit `this` pointer argument.
|
||||
*/
|
||||
private class PrimaryArgumentNode extends ArgumentNode, OperandNode {
|
||||
override ArgumentOperand op;
|
||||
|
||||
PrimaryArgumentNode() { exists(CallInstruction call | op = call.getAnArgumentOperand()) }
|
||||
|
||||
override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
|
||||
op = call.getArgumentOperand(pos.(DirectPosition).getIndex())
|
||||
}
|
||||
|
||||
override string toStringImpl() { result = argumentOperandToString(op) }
|
||||
}
|
||||
|
||||
private string argumentOperandToString(ArgumentOperand op) {
|
||||
exists(Expr unconverted |
|
||||
unconverted = op.getDef().getUnconvertedResultExpression() and
|
||||
result = unconverted.toString()
|
||||
)
|
||||
or
|
||||
// Certain instructions don't map to an unconverted result expression. For these cases
|
||||
// we fall back to a simpler naming scheme. This can happen in IR-generated constructors.
|
||||
not exists(op.getDef().getUnconvertedResultExpression()) and
|
||||
(
|
||||
result = "Argument " + op.(PositionalArgumentOperand).getIndex()
|
||||
or
|
||||
op instanceof ThisArgumentOperand and result = "Argument this"
|
||||
)
|
||||
}
|
||||
|
||||
private class SideEffectArgumentNode extends ArgumentNode, SideEffectOperandNode {
|
||||
override predicate argumentOf(DataFlowCall dfCall, ArgumentPosition pos) {
|
||||
this.getCallInstruction() = dfCall and
|
||||
pos.(IndirectionPosition).getArgumentIndex() = this.getArgumentIndex() and
|
||||
pos.(IndirectionPosition).getIndirectionIndex() = super.getIndirectionIndex()
|
||||
}
|
||||
|
||||
override string toStringImpl() {
|
||||
result = argumentOperandToString(this.getAddressOperand()) + " indirection"
|
||||
}
|
||||
}
|
||||
|
||||
/** A parameter position represented by an integer. */
|
||||
class ParameterPosition = Position;
|
||||
|
||||
/** An argument position represented by an integer. */
|
||||
class ArgumentPosition = Position;
|
||||
|
||||
class Position extends TPosition {
|
||||
abstract string toString();
|
||||
}
|
||||
|
||||
class DirectPosition extends Position, TDirectPosition {
|
||||
int index;
|
||||
|
||||
DirectPosition() { this = TDirectPosition(index) }
|
||||
|
||||
override string toString() { if index = -1 then result = "this" else result = index.toString() }
|
||||
|
||||
int getIndex() { result = index }
|
||||
}
|
||||
|
||||
class IndirectionPosition extends Position, TIndirectionPosition {
|
||||
int argumentIndex;
|
||||
int indirectionIndex;
|
||||
|
||||
IndirectionPosition() { this = TIndirectionPosition(argumentIndex, indirectionIndex) }
|
||||
|
||||
override string toString() {
|
||||
if argumentIndex = -1
|
||||
then if indirectionIndex > 0 then result = "this indirection" else result = "this"
|
||||
else
|
||||
if indirectionIndex > 0
|
||||
then result = argumentIndex.toString() + " indirection"
|
||||
else result = argumentIndex.toString()
|
||||
}
|
||||
|
||||
int getArgumentIndex() { result = argumentIndex }
|
||||
|
||||
int getIndirectionIndex() { result = indirectionIndex }
|
||||
}
|
||||
|
||||
newtype TPosition =
|
||||
TDirectPosition(int index) { exists(any(CallInstruction c).getArgument(index)) } or
|
||||
TIndirectionPosition(int argumentIndex, int indirectionIndex) {
|
||||
hasOperandAndIndex(_, any(CallInstruction call).getArgumentOperand(argumentIndex),
|
||||
indirectionIndex)
|
||||
}
|
||||
|
||||
private newtype TReturnKind =
|
||||
TNormalReturnKind(int index) {
|
||||
exists(IndirectReturnNode return |
|
||||
return.getAddressOperand() = any(ReturnValueInstruction r).getReturnAddressOperand() and
|
||||
index = return.getIndirectionIndex() - 1 // We subtract one because the return loads the value.
|
||||
)
|
||||
} or
|
||||
TIndirectReturnKind(int argumentIndex, int indirectionIndex) {
|
||||
exists(IndirectReturnNode return, ReturnIndirectionInstruction returnInd |
|
||||
returnInd.hasIndex(argumentIndex) and
|
||||
return.getAddressOperand() = returnInd.getSourceAddressOperand() and
|
||||
indirectionIndex = return.getIndirectionIndex()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A return kind. A return kind describes how a value can be returned
|
||||
* from a callable. For C++, this is simply a function return.
|
||||
*/
|
||||
class ReturnKind extends TReturnKind {
|
||||
/** Gets a textual representation of this return kind. */
|
||||
abstract string toString();
|
||||
}
|
||||
|
||||
private class NormalReturnKind extends ReturnKind, TNormalReturnKind {
|
||||
int index;
|
||||
|
||||
NormalReturnKind() { this = TNormalReturnKind(index) }
|
||||
|
||||
override string toString() { result = "indirect return" }
|
||||
}
|
||||
|
||||
private class IndirectReturnKind extends ReturnKind, TIndirectReturnKind {
|
||||
int argumentIndex;
|
||||
int indirectionIndex;
|
||||
|
||||
IndirectReturnKind() { this = TIndirectReturnKind(argumentIndex, indirectionIndex) }
|
||||
|
||||
override string toString() { result = "indirect outparam[" + argumentIndex.toString() + "]" }
|
||||
}
|
||||
|
||||
/** A data flow node that occurs as the result of a `ReturnStmt`. */
|
||||
class ReturnNode extends Node instanceof IndirectReturnNode {
|
||||
/** Gets the kind of this returned value. */
|
||||
abstract ReturnKind getKind();
|
||||
}
|
||||
|
||||
/**
|
||||
* This predicate represents an annoying hack that we have to do. We use the
|
||||
* `ReturnIndirectionInstruction` to determine which variables need flow back
|
||||
* out of a function. However, the IR will unconditionally create those for a
|
||||
* variable passed to a function even though the variable was never updated by
|
||||
* the function. And if a function has too many `ReturnNode`s the dataflow
|
||||
* library lowers its precision for that function by disabling field flow.
|
||||
*
|
||||
* So we those eliminate `ReturnNode`s that would have otherwise been created
|
||||
* by this unconditional `ReturnIndirectionInstruction` by requiring that there
|
||||
* must exist an SSA definition of the IR variable in the function.
|
||||
*/
|
||||
private predicate hasNonInitializeParameterDef(IRVariable v) {
|
||||
exists(Ssa::Def def |
|
||||
not def.getDefiningInstruction() instanceof InitializeParameterInstruction and
|
||||
v = def.getSourceVariable().getBaseVariable().(Ssa::BaseIRVariable).getIRVariable()
|
||||
)
|
||||
}
|
||||
|
||||
class ReturnIndirectionNode extends IndirectReturnNode, ReturnNode {
|
||||
override ReturnKind getKind() {
|
||||
exists(int argumentIndex, ReturnIndirectionInstruction returnInd |
|
||||
returnInd.hasIndex(argumentIndex) and
|
||||
this.getAddressOperand() = returnInd.getSourceAddressOperand() and
|
||||
result = TIndirectReturnKind(argumentIndex, this.getIndirectionIndex()) and
|
||||
hasNonInitializeParameterDef(returnInd.getIRVariable())
|
||||
)
|
||||
or
|
||||
this.getAddressOperand() = any(ReturnValueInstruction r).getReturnAddressOperand() and
|
||||
result = TNormalReturnKind(this.getIndirectionIndex() - 1)
|
||||
}
|
||||
}
|
||||
|
||||
private Operand fullyConvertedCallStep(Operand op) {
|
||||
not exists(getANonConversionUse(op)) and
|
||||
exists(Instruction instr |
|
||||
conversionFlow(op, instr, _) and
|
||||
result = getAUse(instr)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the instruction that uses this operand, if the instruction is not
|
||||
* ignored for dataflow purposes.
|
||||
*/
|
||||
private Instruction getUse(Operand op) {
|
||||
result = op.getUse() and
|
||||
not Ssa::ignoreOperand(op)
|
||||
}
|
||||
|
||||
/** Gets a use of the instruction `instr` that is not ignored for dataflow purposes. */
|
||||
Operand getAUse(Instruction instr) {
|
||||
result = instr.getAUse() and
|
||||
not Ssa::ignoreOperand(result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a use of `operand` that is:
|
||||
* - not ignored for dataflow purposes, and
|
||||
* - not a conversion-like instruction.
|
||||
*/
|
||||
private Instruction getANonConversionUse(Operand operand) {
|
||||
result = getUse(operand) and
|
||||
not conversionFlow(_, result, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the operand that represents the first use of the value of `call` following
|
||||
* a sequence of conversion-like instructions.
|
||||
*/
|
||||
predicate operandForfullyConvertedCall(Operand operand, CallInstruction call) {
|
||||
exists(getANonConversionUse(operand)) and
|
||||
(
|
||||
operand = getAUse(call)
|
||||
or
|
||||
operand = fullyConvertedCallStep*(getAUse(call))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the instruction that represents the first use of the value of `call` following
|
||||
* a sequence of conversion-like instructions.
|
||||
*
|
||||
* This predicate only holds if there is no suitable operand (i.e., no operand of a non-
|
||||
* conversion instruction) to use to represent the value of `call` after conversions.
|
||||
*/
|
||||
predicate instructionForfullyConvertedCall(Instruction instr, CallInstruction call) {
|
||||
not operandForfullyConvertedCall(_, call) and
|
||||
(
|
||||
// If there is no use of the call then we pick the call instruction
|
||||
not exists(getAUse(call)) and
|
||||
instr = call
|
||||
or
|
||||
// Otherwise, flow to the first non-conversion use.
|
||||
exists(Operand operand | operand = fullyConvertedCallStep*(getAUse(call)) |
|
||||
instr = getANonConversionUse(operand)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `node` represents the output node for `call`. */
|
||||
private predicate simpleOutNode(Node node, CallInstruction call) {
|
||||
operandForfullyConvertedCall(node.asOperand(), call)
|
||||
or
|
||||
instructionForfullyConvertedCall(node.asInstruction(), call)
|
||||
}
|
||||
|
||||
/** A data flow node that represents the output of a call. */
|
||||
class OutNode extends Node {
|
||||
OutNode() {
|
||||
// Return values not hidden behind indirections
|
||||
simpleOutNode(this, _)
|
||||
or
|
||||
// Return values hidden behind indirections
|
||||
this instanceof IndirectReturnOutNode
|
||||
or
|
||||
// Modified arguments hidden behind indirections
|
||||
this instanceof IndirectArgumentOutNode
|
||||
}
|
||||
|
||||
/** Gets the underlying call. */
|
||||
abstract DataFlowCall getCall();
|
||||
|
||||
abstract ReturnKind getReturnKind();
|
||||
}
|
||||
|
||||
private class DirectCallOutNode extends OutNode {
|
||||
CallInstruction call;
|
||||
|
||||
DirectCallOutNode() { simpleOutNode(this, call) }
|
||||
|
||||
override DataFlowCall getCall() { result = call }
|
||||
|
||||
override ReturnKind getReturnKind() { result = TNormalReturnKind(0) }
|
||||
}
|
||||
|
||||
private class IndirectCallOutNode extends OutNode, IndirectReturnOutNode {
|
||||
override DataFlowCall getCall() { result = this.getCallInstruction() }
|
||||
|
||||
override ReturnKind getReturnKind() { result = TNormalReturnKind(this.getIndirectionIndex()) }
|
||||
}
|
||||
|
||||
private class SideEffectOutNode extends OutNode, IndirectArgumentOutNode {
|
||||
override DataFlowCall getCall() { result = this.getCallInstruction() }
|
||||
|
||||
override ReturnKind getReturnKind() {
|
||||
result = TIndirectReturnKind(this.getArgumentIndex(), this.getIndirectionIndex())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a node that can read the value returned from `call` with return kind
|
||||
* `kind`.
|
||||
*/
|
||||
OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) {
|
||||
result.getCall() = call and
|
||||
result.getReturnKind() = kind
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` in a way that loses the
|
||||
* calling context. For example, this would happen with flow through a
|
||||
* global or static variable.
|
||||
*/
|
||||
predicate jumpStep(Node n1, Node n2) {
|
||||
exists(Cpp::GlobalOrNamespaceVariable v |
|
||||
v =
|
||||
n1.asInstruction()
|
||||
.(StoreInstruction)
|
||||
.getResultAddress()
|
||||
.(VariableAddressInstruction)
|
||||
.getAstVariable() and
|
||||
v = n2.asVariable()
|
||||
or
|
||||
v =
|
||||
n2.asInstruction()
|
||||
.(LoadInstruction)
|
||||
.getSourceAddress()
|
||||
.(VariableAddressInstruction)
|
||||
.getAstVariable() and
|
||||
v = n1.asVariable()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` via an assignment to `f`.
|
||||
* Thus, `node2` references an object with a field `f` that contains the
|
||||
* value of `node1`.
|
||||
*/
|
||||
predicate storeStep(Node node1, Content c, PostFieldUpdateNode node2) {
|
||||
exists(int indirectionIndex1, int numberOfLoads, StoreInstruction store |
|
||||
nodeHasInstruction(node1, store, pragma[only_bind_into](indirectionIndex1)) and
|
||||
node2.getIndirectionIndex() = 1 and
|
||||
numberOfLoadsFromOperand(node2.getFieldAddress(), store.getDestinationAddressOperand(),
|
||||
numberOfLoads)
|
||||
|
|
||||
exists(FieldContent fc | fc = c |
|
||||
fc.getField() = node2.getUpdatedField() and
|
||||
fc.getIndirectionIndex() = 1 + indirectionIndex1 + numberOfLoads
|
||||
)
|
||||
or
|
||||
exists(UnionContent uc | uc = c |
|
||||
uc.getAField() = node2.getUpdatedField() and
|
||||
uc.getIndirectionIndex() = 1 + indirectionIndex1 + numberOfLoads
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `operandFrom` flows to `operandTo` using a sequence of conversion-like
|
||||
* operations and exactly `n` `LoadInstruction` operations.
|
||||
*/
|
||||
private predicate numberOfLoadsFromOperandRec(Operand operandFrom, Operand operandTo, int ind) {
|
||||
exists(LoadInstruction load | load.getSourceAddressOperand() = operandFrom |
|
||||
operandTo = operandFrom and ind = 0
|
||||
or
|
||||
numberOfLoadsFromOperand(load.getAUse(), operandTo, ind - 1)
|
||||
)
|
||||
or
|
||||
exists(Operand op, Instruction instr |
|
||||
instr = op.getDef() and
|
||||
conversionFlow(operandFrom, instr, _) and
|
||||
numberOfLoadsFromOperand(op, operandTo, ind)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `operandFrom` flows to `operandTo` using a sequence of conversion-like
|
||||
* operations and exactly `n` `LoadInstruction` operations.
|
||||
*/
|
||||
private predicate numberOfLoadsFromOperand(Operand operandFrom, Operand operandTo, int n) {
|
||||
numberOfLoadsFromOperandRec(operandFrom, operandTo, n)
|
||||
or
|
||||
not any(LoadInstruction load).getSourceAddressOperand() = operandFrom and
|
||||
not conversionFlow(operandFrom, _, _) and
|
||||
operandFrom = operandTo and
|
||||
n = 0
|
||||
}
|
||||
|
||||
// Needed to join on both an operand and an index at the same time.
|
||||
pragma[noinline]
|
||||
predicate nodeHasOperand(Node node, Operand operand, int indirectionIndex) {
|
||||
node.asOperand() = operand and indirectionIndex = 0
|
||||
or
|
||||
hasOperandAndIndex(node, operand, indirectionIndex)
|
||||
}
|
||||
|
||||
// Needed to join on both an instruction and an index at the same time.
|
||||
pragma[noinline]
|
||||
predicate nodeHasInstruction(Node node, Instruction instr, int indirectionIndex) {
|
||||
node.asInstruction() = instr and indirectionIndex = 0
|
||||
or
|
||||
hasInstructionAndIndex(node, instr, indirectionIndex)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` via a read of `f`.
|
||||
* Thus, `node1` references an object with a field `f` whose value ends up in
|
||||
* `node2`.
|
||||
*/
|
||||
predicate readStep(Node node1, Content c, Node node2) {
|
||||
exists(FieldAddress fa1, Operand operand, int numberOfLoads, int indirectionIndex2 |
|
||||
nodeHasOperand(node2, operand, indirectionIndex2) and
|
||||
nodeHasOperand(node1, fa1.getObjectAddressOperand(), _) and
|
||||
numberOfLoadsFromOperand(fa1, operand, numberOfLoads)
|
||||
|
|
||||
exists(FieldContent fc | fc = c |
|
||||
fc.getField() = fa1.getField() and
|
||||
fc.getIndirectionIndex() = indirectionIndex2 + numberOfLoads
|
||||
)
|
||||
or
|
||||
exists(UnionContent uc | uc = c |
|
||||
uc.getAField() = fa1.getField() and
|
||||
uc.getIndirectionIndex() = indirectionIndex2 + numberOfLoads
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if values stored inside content `c` are cleared at node `n`.
|
||||
*/
|
||||
predicate clearsContent(Node n, Content c) {
|
||||
none() // stub implementation
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the value that is being tracked is expected to be stored inside content `c`
|
||||
* at node `n`.
|
||||
*/
|
||||
predicate expectsContent(Node n, ContentSet c) { none() }
|
||||
|
||||
/** Gets the type of `n` used for type pruning. */
|
||||
DataFlowType getNodeType(Node n) {
|
||||
suppressUnusedNode(n) and
|
||||
result instanceof VoidType // stub implementation
|
||||
}
|
||||
|
||||
/** Gets a string representation of a type returned by `getNodeType`. */
|
||||
string ppReprType(DataFlowType t) { none() } // stub implementation
|
||||
|
||||
/**
|
||||
* Holds if `t1` and `t2` are compatible, that is, whether data can flow from
|
||||
* a node of type `t1` to a node of type `t2`.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate compatibleTypes(DataFlowType t1, DataFlowType t2) {
|
||||
any() // stub implementation
|
||||
}
|
||||
|
||||
private predicate suppressUnusedNode(Node n) { any() }
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Java QL library compatibility wrappers
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/** A node that performs a type cast. */
|
||||
class CastNode extends Node {
|
||||
CastNode() { none() } // stub implementation
|
||||
}
|
||||
|
||||
/**
|
||||
* A function that may contain code or a variable that may contain itself. When
|
||||
* flow crosses from one _enclosing callable_ to another, the interprocedural
|
||||
* data-flow library discards call contexts and inserts a node in the big-step
|
||||
* relation used for human-readable path explanations.
|
||||
*/
|
||||
class DataFlowCallable = Cpp::Declaration;
|
||||
|
||||
class DataFlowExpr = Expr;
|
||||
|
||||
class DataFlowType = Type;
|
||||
|
||||
/** A function call relevant for data flow. */
|
||||
class DataFlowCall extends CallInstruction {
|
||||
Function getEnclosingCallable() { result = this.getEnclosingFunction() }
|
||||
}
|
||||
|
||||
predicate isUnreachableInCall(Node n, DataFlowCall call) { none() } // stub implementation
|
||||
|
||||
int accessPathLimit() { result = 5 }
|
||||
|
||||
/**
|
||||
* Holds if access paths with `c` at their head always should be tracked at high
|
||||
* precision. This disables adaptive access path precision for such access paths.
|
||||
*/
|
||||
predicate forceHighPrecision(Content c) { none() }
|
||||
|
||||
/** The unit type. */
|
||||
private newtype TUnit = TMkUnit()
|
||||
|
||||
/** The trivial type with a single element. */
|
||||
class Unit extends TUnit {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = "unit" }
|
||||
}
|
||||
|
||||
/** Holds if `n` should be hidden from path explanations. */
|
||||
predicate nodeIsHidden(Node n) { n instanceof OperandNode and not n instanceof ArgumentNode }
|
||||
|
||||
class LambdaCallKind = Unit;
|
||||
|
||||
/** Holds if `creation` is an expression that creates a lambda of kind `kind` for `c`. */
|
||||
predicate lambdaCreation(Node creation, LambdaCallKind kind, DataFlowCallable c) { none() }
|
||||
|
||||
/** Holds if `call` is a lambda call of kind `kind` where `receiver` is the lambda expression. */
|
||||
predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) { none() }
|
||||
|
||||
/** Extra data-flow steps needed for lambda flow analysis. */
|
||||
predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preservesValue) { none() }
|
||||
|
||||
/**
|
||||
* Holds if flow is allowed to pass from parameter `p` and back to itself as a
|
||||
* side-effect, resulting in a summary from `p` to itself.
|
||||
*
|
||||
* One example would be to allow flow like `p.foo = p.bar;`, which is disallowed
|
||||
* by default as a heuristic.
|
||||
*/
|
||||
predicate allowParameterReturnInSelf(ParameterNode p) { none() }
|
||||
|
||||
/** An approximated `Content`. */
|
||||
class ContentApprox = Unit;
|
||||
|
||||
/** Gets an approximated value for content `c`. */
|
||||
pragma[inline]
|
||||
ContentApprox getContentApprox(Content c) { any() }
|
||||
|
||||
private class MyConsistencyConfiguration extends Consistency::ConsistencyConfiguration {
|
||||
override predicate argHasPostUpdateExclude(ArgumentNode n) {
|
||||
// The rules for whether an IR argument gets a post-update node are too
|
||||
// complex to model here.
|
||||
any()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an additional term that is added to the `join` and `branch` computations to reflect
|
||||
* an additional forward or backwards branching factor that is not taken into account
|
||||
* when calculating the (virtual) dispatch cost.
|
||||
*
|
||||
* Argument `arg` is part of a path from a source to a sink, and `p` is the target parameter.
|
||||
*/
|
||||
int getAdditionalFlowIntoCallNodeTerm(ArgumentNode arg, ParameterNode p) { none() }
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,93 +0,0 @@
|
||||
/**
|
||||
* Provides predicates for mapping the `FunctionInput` and `FunctionOutput`
|
||||
* classes used in function models to the corresponding instructions.
|
||||
*/
|
||||
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import experimental.semmle.code.cpp.ir.dataflow.DataFlow
|
||||
private import experimental.semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
|
||||
private import SsaInternals as Ssa
|
||||
|
||||
/**
|
||||
* Gets the instruction that goes into `input` for `call`.
|
||||
*/
|
||||
DataFlow::Node callInput(CallInstruction call, FunctionInput input) {
|
||||
// An argument or qualifier
|
||||
exists(int index |
|
||||
result.asOperand() = call.getArgumentOperand(index) and
|
||||
input.isParameterOrQualifierAddress(index)
|
||||
)
|
||||
or
|
||||
// A value pointed to by an argument or qualifier
|
||||
exists(int index, int indirectionIndex |
|
||||
hasOperandAndIndex(result, call.getArgumentOperand(index), indirectionIndex) and
|
||||
input.isParameterDerefOrQualifierObject(index, indirectionIndex)
|
||||
)
|
||||
or
|
||||
exists(int ind |
|
||||
result = getIndirectReturnOutNode(call, ind) and
|
||||
input.isReturnValueDeref(ind)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the instruction that holds the `output` for `call`.
|
||||
*/
|
||||
Node callOutput(CallInstruction call, FunctionOutput output) {
|
||||
// The return value
|
||||
result.asInstruction() = call and
|
||||
output.isReturnValue()
|
||||
or
|
||||
// The side effect of a call on the value pointed to by an argument or qualifier
|
||||
exists(int index, int indirectionIndex |
|
||||
result.(IndirectArgumentOutNode).getArgumentIndex() = index and
|
||||
result.(IndirectArgumentOutNode).getIndirectionIndex() = indirectionIndex and
|
||||
result.(IndirectArgumentOutNode).getCallInstruction() = call and
|
||||
output.isParameterDerefOrQualifierObject(index, indirectionIndex)
|
||||
)
|
||||
or
|
||||
exists(int ind |
|
||||
result = getIndirectReturnOutNode(call, ind) and
|
||||
output.isReturnValueDeref(ind)
|
||||
)
|
||||
}
|
||||
|
||||
DataFlow::Node callInput(CallInstruction call, FunctionInput input, int d) {
|
||||
exists(DataFlow::Node n | n = callInput(call, input) and d > 0 |
|
||||
// An argument or qualifier
|
||||
hasOperandAndIndex(result, n.asOperand(), d)
|
||||
or
|
||||
exists(Operand operand, int indirectionIndex |
|
||||
// A value pointed to by an argument or qualifier
|
||||
hasOperandAndIndex(n, operand, indirectionIndex) and
|
||||
hasOperandAndIndex(result, operand, indirectionIndex + d)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private IndirectReturnOutNode getIndirectReturnOutNode(CallInstruction call, int d) {
|
||||
result.getCallInstruction() = call and
|
||||
result.getIndirectionIndex() = d
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the instruction that holds the `output` for `call`.
|
||||
*/
|
||||
bindingset[d]
|
||||
Node callOutput(CallInstruction call, FunctionOutput output, int d) {
|
||||
exists(DataFlow::Node n | n = callOutput(call, output) and d > 0 |
|
||||
// The return value
|
||||
result = getIndirectReturnOutNode(n.asInstruction(), d)
|
||||
or
|
||||
// If there isn't an indirect out node for the call with indirection `d` then
|
||||
// we conflate this with the underlying `CallInstruction`.
|
||||
not exists(getIndirectReturnOutNode(call, d)) and
|
||||
n.asInstruction() = result.asInstruction()
|
||||
or
|
||||
// The side effect of a call on the value pointed to by an argument or qualifier
|
||||
exists(Operand operand, int indirectionIndex |
|
||||
Ssa::outNodeHasAddressAndIndex(n, operand, indirectionIndex) and
|
||||
Ssa::outNodeHasAddressAndIndex(result, operand, indirectionIndex + d)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -1,136 +0,0 @@
|
||||
private import cpp
|
||||
// The `ValueNumbering` library has to be imported right after `cpp` to ensure
|
||||
// that the cached IR gets the same checksum here as it does in queries that use
|
||||
// `ValueNumbering` without `DataFlow`.
|
||||
private import semmle.code.cpp.ir.ValueNumbering
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import semmle.code.cpp.ir.dataflow.DataFlow
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
|
||||
private import PrintIRUtilities
|
||||
|
||||
/**
|
||||
* Gets the local dataflow from other nodes in the same function to this node.
|
||||
*/
|
||||
private string getFromFlow(DataFlow::Node useNode, int order1, int order2) {
|
||||
exists(DataFlow::Node defNode, string prefix |
|
||||
(
|
||||
simpleLocalFlowStep(defNode, useNode) and prefix = ""
|
||||
or
|
||||
any(DataFlow::Configuration cfg).isAdditionalFlowStep(defNode, useNode) and
|
||||
defNode.getEnclosingCallable() = useNode.getEnclosingCallable() and
|
||||
prefix = "+"
|
||||
) and
|
||||
if defNode.asInstruction() = useNode.asOperand().getAnyDef()
|
||||
then
|
||||
// Shorthand for flow from the def of this operand.
|
||||
result = prefix + "def" and
|
||||
order1 = -1 and
|
||||
order2 = 0
|
||||
else
|
||||
if defNode.asOperand().getUse() = useNode.asInstruction()
|
||||
then
|
||||
// Shorthand for flow from an operand of this instruction
|
||||
result = prefix + defNode.asOperand().getDumpId() and
|
||||
order1 = -1 and
|
||||
order2 = defNode.asOperand().getDumpSortOrder()
|
||||
else result = prefix + nodeId(defNode, order1, order2)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the local dataflow from this node to other nodes in the same function.
|
||||
*/
|
||||
private string getToFlow(DataFlow::Node defNode, int order1, int order2) {
|
||||
exists(DataFlow::Node useNode, string prefix |
|
||||
(
|
||||
simpleLocalFlowStep(defNode, useNode) and prefix = ""
|
||||
or
|
||||
any(DataFlow::Configuration cfg).isAdditionalFlowStep(defNode, useNode) and
|
||||
defNode.getEnclosingCallable() = useNode.getEnclosingCallable() and
|
||||
prefix = "+"
|
||||
) and
|
||||
if useNode.asInstruction() = defNode.asOperand().getUse()
|
||||
then
|
||||
// Shorthand for flow to this operand's instruction.
|
||||
result = prefix + "result" and
|
||||
order1 = -1 and
|
||||
order2 = 0
|
||||
else result = prefix + nodeId(useNode, order1, order2)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the properties of the dataflow node `node`.
|
||||
*/
|
||||
private string getNodeProperty(DataFlow::Node node, string key) {
|
||||
// List dataflow into and out of this node. Flow into this node is printed as `src->@`, and flow
|
||||
// out of this node is printed as `@->dest`.
|
||||
key = "flow" and
|
||||
result =
|
||||
strictconcat(string flow, boolean to, int order1, int order2 |
|
||||
flow = getFromFlow(node, order1, order2) + "->@" and to = false
|
||||
or
|
||||
flow = "@->" + getToFlow(node, order1, order2) and to = true
|
||||
|
|
||||
flow, ", " order by to, order1, order2, flow
|
||||
)
|
||||
or
|
||||
// Is this node a dataflow sink?
|
||||
key = "sink" and
|
||||
any(DataFlow::Configuration cfg).isSink(node) and
|
||||
result = "true"
|
||||
or
|
||||
// Is this node a dataflow source?
|
||||
key = "source" and
|
||||
any(DataFlow::Configuration cfg).isSource(node) and
|
||||
result = "true"
|
||||
or
|
||||
// Is this node a dataflow barrier, and if so, what kind?
|
||||
key = "barrier" and
|
||||
result =
|
||||
strictconcat(string kind |
|
||||
any(DataFlow::Configuration cfg).isBarrier(node) and kind = "full"
|
||||
or
|
||||
any(DataFlow::Configuration cfg).isBarrierIn(node) and kind = "in"
|
||||
or
|
||||
any(DataFlow::Configuration cfg).isBarrierOut(node) and kind = "out"
|
||||
|
|
||||
kind, ", "
|
||||
)
|
||||
// or
|
||||
// // Is there partial flow from a source to this node?
|
||||
// // This property will only be emitted if partial flow is enabled by overriding
|
||||
// // `DataFlow::Configuration::explorationLimit()`.
|
||||
// key = "pflow" and
|
||||
// result =
|
||||
// strictconcat(DataFlow::PartialPathNode sourceNode, DataFlow::PartialPathNode destNode, int dist,
|
||||
// int order1, int order2 |
|
||||
// any(DataFlow::Configuration cfg).hasPartialFlow(sourceNode, destNode, dist) and
|
||||
// destNode.getNode() = node and
|
||||
// // Only print flow from a source in the same function.
|
||||
// sourceNode.getNode().getEnclosingCallable() = node.getEnclosingCallable()
|
||||
// |
|
||||
// nodeId(sourceNode.getNode(), order1, order2) + "+" + dist.toString(), ", "
|
||||
// order by
|
||||
// order1, order2, dist desc
|
||||
// )
|
||||
}
|
||||
|
||||
/**
|
||||
* Property provider for local IR dataflow.
|
||||
*/
|
||||
class LocalFlowPropertyProvider extends IRPropertyProvider {
|
||||
override string getOperandProperty(Operand operand, string key) {
|
||||
exists(DataFlow::Node node |
|
||||
operand = node.asOperand() and
|
||||
result = getNodeProperty(node, key)
|
||||
)
|
||||
}
|
||||
|
||||
override string getInstructionProperty(Instruction instruction, string key) {
|
||||
exists(DataFlow::Node node |
|
||||
instruction = node.asInstruction() and
|
||||
result = getNodeProperty(node, key)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/**
|
||||
* Print the dataflow local store steps in IR dumps.
|
||||
*/
|
||||
|
||||
private import cpp
|
||||
// The `ValueNumbering` library has to be imported right after `cpp` to ensure
|
||||
// that the cached IR gets the same checksum here as it does in queries that use
|
||||
// `ValueNumbering` without `DataFlow`.
|
||||
private import semmle.code.cpp.ir.ValueNumbering
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import semmle.code.cpp.ir.dataflow.DataFlow
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate
|
||||
private import PrintIRUtilities
|
||||
|
||||
/**
|
||||
* Property provider for local IR dataflow store steps.
|
||||
*/
|
||||
class LocalFlowPropertyProvider extends IRPropertyProvider {
|
||||
override string getInstructionProperty(Instruction instruction, string key) {
|
||||
exists(DataFlow::Node objectNode, Content content |
|
||||
key = "content[" + content.toString() + "]" and
|
||||
instruction = objectNode.asInstruction() and
|
||||
result =
|
||||
strictconcat(string element, DataFlow::Node fieldNode |
|
||||
storeStep(fieldNode, content, objectNode) and
|
||||
element = nodeId(fieldNode, _, _)
|
||||
|
|
||||
element, ", "
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
/**
|
||||
* Shared utilities used when printing dataflow annotations in IR dumps.
|
||||
*/
|
||||
|
||||
private import cpp
|
||||
// The `ValueNumbering` library has to be imported right after `cpp` to ensure
|
||||
// that the cached IR gets the same checksum here as it does in queries that use
|
||||
// `ValueNumbering` without `DataFlow`.
|
||||
private import semmle.code.cpp.ir.ValueNumbering
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import semmle.code.cpp.ir.dataflow.DataFlow
|
||||
|
||||
/**
|
||||
* Gets a short ID for an IR dataflow node.
|
||||
* - For `Instruction`s, this is just the result ID of the instruction (e.g. `m128`).
|
||||
* - For `Operand`s, this is the label of the operand, prefixed with the result ID of the
|
||||
* instruction and a dot (e.g. `m128.left`).
|
||||
* - For `Variable`s, this is the qualified name of the variable.
|
||||
*/
|
||||
string nodeId(DataFlow::Node node, int order1, int order2) {
|
||||
exists(Instruction instruction | instruction = node.asInstruction() |
|
||||
result = instruction.getResultId() and
|
||||
order1 = instruction.getBlock().getDisplayIndex() and
|
||||
order2 = instruction.getDisplayIndexInBlock()
|
||||
)
|
||||
or
|
||||
exists(Operand operand, Instruction instruction |
|
||||
operand = node.asOperand() and
|
||||
instruction = operand.getUse()
|
||||
|
|
||||
result = instruction.getResultId() + "." + operand.getDumpId() and
|
||||
order1 = instruction.getBlock().getDisplayIndex() and
|
||||
order2 = instruction.getDisplayIndexInBlock()
|
||||
)
|
||||
or
|
||||
result = "var(" + node.asVariable().getQualifiedName() + ")" and
|
||||
order1 = 1000000 and
|
||||
order2 = 0
|
||||
}
|
||||
@@ -1,552 +0,0 @@
|
||||
private import codeql.ssa.Ssa as SsaImplCommon
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import DataFlowUtil
|
||||
private import DataFlowImplCommon as DataFlowImplCommon
|
||||
private import semmle.code.cpp.models.interfaces.Allocation as Alloc
|
||||
private import semmle.code.cpp.models.interfaces.DataFlow as DataFlow
|
||||
private import semmle.code.cpp.ir.internal.IRCppLanguage
|
||||
private import DataFlowPrivate
|
||||
private import ssa0.SsaInternals as SsaInternals0
|
||||
import SsaInternalsCommon
|
||||
|
||||
private module SourceVariables {
|
||||
int getMaxIndirectionForIRVariable(IRVariable var) {
|
||||
exists(Type type, boolean isGLValue |
|
||||
var.getLanguageType().hasType(type, isGLValue) and
|
||||
if isGLValue = true
|
||||
then result = 1 + getMaxIndirectionsForType(type)
|
||||
else result = getMaxIndirectionsForType(type)
|
||||
)
|
||||
}
|
||||
|
||||
class BaseSourceVariable = SsaInternals0::BaseSourceVariable;
|
||||
|
||||
class BaseIRVariable = SsaInternals0::BaseIRVariable;
|
||||
|
||||
class BaseCallVariable = SsaInternals0::BaseCallVariable;
|
||||
|
||||
cached
|
||||
private newtype TSourceVariable =
|
||||
TSourceIRVariable(BaseIRVariable baseVar, int ind) {
|
||||
ind = [0 .. getMaxIndirectionForIRVariable(baseVar.getIRVariable())]
|
||||
} or
|
||||
TCallVariable(AllocationInstruction call, int ind) {
|
||||
ind = [0 .. countIndirectionsForCppType(getResultLanguageType(call))]
|
||||
}
|
||||
|
||||
abstract class SourceVariable extends TSourceVariable {
|
||||
int ind;
|
||||
|
||||
bindingset[ind]
|
||||
SourceVariable() { any() }
|
||||
|
||||
abstract string toString();
|
||||
|
||||
int getIndirection() { result = ind }
|
||||
|
||||
abstract BaseSourceVariable getBaseVariable();
|
||||
}
|
||||
|
||||
class SourceIRVariable extends SourceVariable, TSourceIRVariable {
|
||||
BaseIRVariable var;
|
||||
|
||||
SourceIRVariable() { this = TSourceIRVariable(var, ind) }
|
||||
|
||||
IRVariable getIRVariable() { result = var.getIRVariable() }
|
||||
|
||||
override BaseIRVariable getBaseVariable() { result.getIRVariable() = this.getIRVariable() }
|
||||
|
||||
override string toString() {
|
||||
ind = 0 and
|
||||
result = this.getIRVariable().toString()
|
||||
or
|
||||
ind > 0 and
|
||||
result = this.getIRVariable().toString() + " indirection"
|
||||
}
|
||||
}
|
||||
|
||||
class CallVariable extends SourceVariable, TCallVariable {
|
||||
AllocationInstruction call;
|
||||
|
||||
CallVariable() { this = TCallVariable(call, ind) }
|
||||
|
||||
AllocationInstruction getCall() { result = call }
|
||||
|
||||
override BaseCallVariable getBaseVariable() { result.getCallInstruction() = call }
|
||||
|
||||
override string toString() {
|
||||
ind = 0 and
|
||||
result = "Call"
|
||||
or
|
||||
ind > 0 and
|
||||
result = "Call indirection"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
import SourceVariables
|
||||
|
||||
predicate hasIndirectOperand(Operand op, int indirectionIndex) {
|
||||
exists(CppType type, int m |
|
||||
not ignoreOperand(op) and
|
||||
type = getLanguageType(op) and
|
||||
m = countIndirectionsForCppType(type) and
|
||||
indirectionIndex = [1 .. m]
|
||||
)
|
||||
}
|
||||
|
||||
predicate hasIndirectInstruction(Instruction instr, int indirectionIndex) {
|
||||
exists(CppType type, int m |
|
||||
not ignoreInstruction(instr) and
|
||||
type = getResultLanguageType(instr) and
|
||||
m = countIndirectionsForCppType(type) and
|
||||
indirectionIndex = [1 .. m]
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
private newtype TDefOrUseImpl =
|
||||
TDefImpl(Operand address, int indirectionIndex) {
|
||||
isDef(_, _, address, _, _, indirectionIndex) and
|
||||
// We only include the definition if the SSA pruning stage
|
||||
// concluded that the definition is live after the write.
|
||||
any(SsaInternals0::Def def).getAddressOperand() = address
|
||||
} or
|
||||
TUseImpl(Operand operand, int indirectionIndex) {
|
||||
isUse(_, operand, _, _, indirectionIndex) and
|
||||
not isDef(_, _, operand, _, _, _)
|
||||
}
|
||||
|
||||
abstract private class DefOrUseImpl extends TDefOrUseImpl {
|
||||
/** Gets a textual representation of this element. */
|
||||
abstract string toString();
|
||||
|
||||
/** Gets the block of this definition or use. */
|
||||
abstract IRBlock getBlock();
|
||||
|
||||
/** Holds if this definition or use has index `index` in block `block`. */
|
||||
abstract predicate hasIndexInBlock(IRBlock block, int index);
|
||||
|
||||
final predicate hasIndexInBlock(IRBlock block, int index, SourceVariable sv) {
|
||||
this.hasIndexInBlock(block, index) and
|
||||
sv = this.getSourceVariable()
|
||||
}
|
||||
|
||||
/** Gets the location of this element. */
|
||||
abstract Cpp::Location getLocation();
|
||||
|
||||
/**
|
||||
* Gets the index (i.e., the number of loads required) of this
|
||||
* definition or use.
|
||||
*
|
||||
* Note that this is _not_ the definition's (or use's) index in
|
||||
* the enclosing basic block. To obtain this index, use
|
||||
* `DefOrUseImpl::hasIndexInBlock/2` or `DefOrUseImpl::hasIndexInBlock/3`.
|
||||
*/
|
||||
abstract int getIndirectionIndex();
|
||||
|
||||
/**
|
||||
* Gets the instruction that computes the base of this definition or use.
|
||||
* This is always a `VariableAddressInstruction` or an `AllocationInstruction`.
|
||||
*/
|
||||
abstract Instruction getBase();
|
||||
|
||||
final BaseSourceVariable getBaseSourceVariable() {
|
||||
exists(IRVariable var |
|
||||
result.(BaseIRVariable).getIRVariable() = var and
|
||||
instructionHasIRVariable(this.getBase(), var)
|
||||
)
|
||||
or
|
||||
result.(BaseCallVariable).getCallInstruction() = this.getBase()
|
||||
}
|
||||
|
||||
/** Gets the variable that is defined or used. */
|
||||
final SourceVariable getSourceVariable() {
|
||||
exists(BaseSourceVariable v, int ind |
|
||||
sourceVariableHasBaseAndIndex(result, v, ind) and
|
||||
defOrUseHasSourceVariable(this, v, ind)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate instructionHasIRVariable(VariableAddressInstruction vai, IRVariable var) {
|
||||
vai.getIRVariable() = var
|
||||
}
|
||||
|
||||
private predicate defOrUseHasSourceVariable(DefOrUseImpl defOrUse, BaseSourceVariable bv, int ind) {
|
||||
defHasSourceVariable(defOrUse, bv, ind)
|
||||
or
|
||||
useHasSourceVariable(defOrUse, bv, ind)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate defHasSourceVariable(DefImpl def, BaseSourceVariable bv, int ind) {
|
||||
bv = def.getBaseSourceVariable() and
|
||||
ind = def.getIndirection()
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate useHasSourceVariable(UseImpl use, BaseSourceVariable bv, int ind) {
|
||||
bv = use.getBaseSourceVariable() and
|
||||
ind = use.getIndirection()
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate sourceVariableHasBaseAndIndex(SourceVariable v, BaseSourceVariable bv, int ind) {
|
||||
v.getBaseVariable() = bv and
|
||||
v.getIndirection() = ind
|
||||
}
|
||||
|
||||
class DefImpl extends DefOrUseImpl, TDefImpl {
|
||||
Operand address;
|
||||
int ind;
|
||||
|
||||
DefImpl() { this = TDefImpl(address, ind) }
|
||||
|
||||
override Instruction getBase() { isDef(_, _, address, result, _, _) }
|
||||
|
||||
Operand getAddressOperand() { result = address }
|
||||
|
||||
int getIndirection() { isDef(_, _, address, _, result, ind) }
|
||||
|
||||
override int getIndirectionIndex() { result = ind }
|
||||
|
||||
Instruction getDefiningInstruction() { isDef(_, result, address, _, _, _) }
|
||||
|
||||
override string toString() { result = "DefImpl" }
|
||||
|
||||
override IRBlock getBlock() { result = this.getDefiningInstruction().getBlock() }
|
||||
|
||||
override Cpp::Location getLocation() { result = this.getDefiningInstruction().getLocation() }
|
||||
|
||||
final override predicate hasIndexInBlock(IRBlock block, int index) {
|
||||
this.getDefiningInstruction() = block.getInstruction(index)
|
||||
}
|
||||
|
||||
predicate isCertain() { isDef(true, _, address, _, _, ind) }
|
||||
}
|
||||
|
||||
class UseImpl extends DefOrUseImpl, TUseImpl {
|
||||
Operand operand;
|
||||
int ind;
|
||||
|
||||
UseImpl() { this = TUseImpl(operand, ind) }
|
||||
|
||||
Operand getOperand() { result = operand }
|
||||
|
||||
override string toString() { result = "UseImpl" }
|
||||
|
||||
final override predicate hasIndexInBlock(IRBlock block, int index) {
|
||||
operand.getUse() = block.getInstruction(index)
|
||||
}
|
||||
|
||||
final override IRBlock getBlock() { result = operand.getUse().getBlock() }
|
||||
|
||||
final override Cpp::Location getLocation() { result = operand.getLocation() }
|
||||
|
||||
final int getIndirection() { isUse(_, operand, _, result, ind) }
|
||||
|
||||
override int getIndirectionIndex() { result = ind }
|
||||
|
||||
override Instruction getBase() { isUse(_, operand, result, _, ind) }
|
||||
|
||||
predicate isCertain() { isUse(true, operand, _, _, ind) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `defOrUse1` is a definition which is first read by `use`,
|
||||
* or if `defOrUse1` is a use and `use` is a next subsequent use.
|
||||
*
|
||||
* In both cases, `use` can either be an explicit use written in the
|
||||
* source file, or it can be a phi node as computed by the SSA library.
|
||||
*/
|
||||
predicate adjacentDefRead(DefOrUse defOrUse1, UseOrPhi use) {
|
||||
exists(IRBlock bb1, int i1, SourceVariable v |
|
||||
defOrUse1.asDefOrUse().hasIndexInBlock(bb1, i1, v)
|
||||
|
|
||||
exists(IRBlock bb2, int i2 |
|
||||
adjacentDefRead(_, pragma[only_bind_into](bb1), pragma[only_bind_into](i1),
|
||||
pragma[only_bind_into](bb2), pragma[only_bind_into](i2))
|
||||
|
|
||||
use.asDefOrUse().(UseImpl).hasIndexInBlock(bb2, i2, v)
|
||||
)
|
||||
or
|
||||
exists(PhiNode phi |
|
||||
lastRefRedef(_, bb1, i1, phi) and
|
||||
use.asPhi() = phi and
|
||||
phi.getSourceVariable() = pragma[only_bind_into](v)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate useToNode(UseOrPhi use, Node nodeTo) {
|
||||
exists(UseImpl useImpl |
|
||||
useImpl = use.asDefOrUse() and
|
||||
nodeHasOperand(nodeTo, useImpl.getOperand(), useImpl.getIndirectionIndex())
|
||||
)
|
||||
or
|
||||
nodeTo.(SsaPhiNode).getPhiNode() = use.asPhi()
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
predicate outNodeHasAddressAndIndex(
|
||||
IndirectArgumentOutNode out, Operand address, int indirectionIndex
|
||||
) {
|
||||
out.getAddressOperand() = address and
|
||||
out.getIndirectionIndex() = indirectionIndex
|
||||
}
|
||||
|
||||
private predicate defToNode(Node nodeFrom, Def def) {
|
||||
nodeHasInstruction(nodeFrom, def.getDefiningInstruction(), def.getIndirectionIndex())
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* Holds if `nodeFrom` is the node that correspond to the definition or use `defOrUse`.
|
||||
*/
|
||||
predicate nodeToDefOrUse(Node nodeFrom, SsaDefOrUse defOrUse) {
|
||||
// Node -> Def
|
||||
defToNode(nodeFrom, defOrUse)
|
||||
or
|
||||
// Node -> Use
|
||||
useToNode(defOrUse, nodeFrom)
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a single conversion-like step from `nFrom` to `nTo`. This relation
|
||||
* only holds when there is no use-use relation out of `nTo`.
|
||||
*/
|
||||
private predicate indirectConversionFlowStep(Node nFrom, Node nTo) {
|
||||
not exists(UseOrPhi defOrUse |
|
||||
nodeToDefOrUse(nTo, defOrUse) and
|
||||
adjacentDefRead(defOrUse, _)
|
||||
) and
|
||||
exists(Operand op1, Operand op2, int indirectionIndex, Instruction instr |
|
||||
hasOperandAndIndex(nFrom, op1, pragma[only_bind_into](indirectionIndex)) and
|
||||
hasOperandAndIndex(nTo, op2, pragma[only_bind_into](indirectionIndex)) and
|
||||
instr = op2.getDef() and
|
||||
conversionFlow(op1, instr, _)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* The reason for this predicate is a bit annoying:
|
||||
* We cannot mark a `PointerArithmeticInstruction` that computes an offset based on some SSA
|
||||
* variable `x` as a use of `x` since this creates taint-flow in the following example:
|
||||
* ```c
|
||||
* int x = array[source]
|
||||
* sink(*array)
|
||||
* ```
|
||||
* This is because `source` would flow from the operand of `PointerArithmeticInstruction` to the
|
||||
* result of the instruction, and into the `IndirectOperand` that represents the value of `*array`.
|
||||
* Then, via use-use flow, flow will arrive at `*array` in `sink(*array)`.
|
||||
*
|
||||
* So this predicate recurses back along conversions and `PointerArithmeticInstruction`s to find the
|
||||
* first use that has provides use-use flow, and uses that target as the target of the `nodeFrom`.
|
||||
*/
|
||||
private predicate adjustForPointerArith(Node nodeFrom, UseOrPhi use) {
|
||||
nodeFrom = any(PostUpdateNode pun).getPreUpdateNode() and
|
||||
exists(DefOrUse defOrUse, Node adjusted |
|
||||
indirectConversionFlowStep*(adjusted, nodeFrom) and
|
||||
nodeToDefOrUse(adjusted, defOrUse) and
|
||||
adjacentDefRead(defOrUse, use)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if there is def-use or use-use flow from `nodeFrom` to `nodeTo`. */
|
||||
predicate ssaFlow(Node nodeFrom, Node nodeTo) {
|
||||
// `nodeFrom = any(PostUpdateNode pun).getPreUpdateNode()` is implied by adjustedForPointerArith.
|
||||
exists(UseOrPhi use |
|
||||
adjustForPointerArith(nodeFrom, use) and
|
||||
useToNode(use, nodeTo)
|
||||
)
|
||||
or
|
||||
not nodeFrom = any(PostUpdateNode pun).getPreUpdateNode() and
|
||||
exists(DefOrUse defOrUse1, UseOrPhi use |
|
||||
nodeToDefOrUse(nodeFrom, defOrUse1) and
|
||||
adjacentDefRead(defOrUse1, use) and
|
||||
useToNode(use, nodeTo)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `nodeTo` receives flow from the phi node `nodeFrom`. */
|
||||
predicate fromPhiNode(SsaPhiNode nodeFrom, Node nodeTo) {
|
||||
exists(PhiNode phi, SourceVariable sv, IRBlock bb1, int i1, UseOrPhi use |
|
||||
phi = nodeFrom.getPhiNode() and
|
||||
phi.definesAt(sv, bb1, i1) and
|
||||
useToNode(use, nodeTo)
|
||||
|
|
||||
exists(IRBlock bb2, int i2 |
|
||||
use.asDefOrUse().hasIndexInBlock(bb2, i2, sv) and
|
||||
adjacentDefRead(phi, bb1, i1, bb2, i2)
|
||||
)
|
||||
or
|
||||
exists(PhiNode phiTo |
|
||||
lastRefRedef(phi, _, _, phiTo) and
|
||||
nodeTo.(SsaPhiNode).getPhiNode() = phiTo
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private SsaInternals0::SourceVariable getOldSourceVariable(SourceVariable v) {
|
||||
v.getBaseVariable().(BaseIRVariable).getIRVariable() =
|
||||
result.getBaseVariable().(SsaInternals0::BaseIRVariable).getIRVariable()
|
||||
or
|
||||
v.getBaseVariable().(BaseCallVariable).getCallInstruction() =
|
||||
result.getBaseVariable().(SsaInternals0::BaseCallVariable).getCallInstruction()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there is a write at index `i` in basic block `bb` to variable `v` that's
|
||||
* subsequently read (as determined by the SSA pruning stage).
|
||||
*/
|
||||
private predicate variableWriteCand(IRBlock bb, int i, SourceVariable v) {
|
||||
exists(SsaInternals0::Def def, SsaInternals0::SourceVariable v0 |
|
||||
def.asDefOrUse().hasIndexInBlock(bb, i, v0) and
|
||||
v0 = getOldSourceVariable(v)
|
||||
)
|
||||
}
|
||||
|
||||
private module SsaInput implements SsaImplCommon::InputSig {
|
||||
import InputSigCommon
|
||||
import SourceVariables
|
||||
|
||||
/**
|
||||
* Holds if the `i`'th write in block `bb` writes to the variable `v`.
|
||||
* `certain` is `true` if the write is guaranteed to overwrite the entire variable.
|
||||
*/
|
||||
predicate variableWrite(IRBlock bb, int i, SourceVariable v, boolean certain) {
|
||||
DataFlowImplCommon::forceCachingInSameStage() and
|
||||
variableWriteCand(bb, i, v) and
|
||||
exists(DefImpl def | def.hasIndexInBlock(bb, i, v) |
|
||||
if def.isCertain() then certain = true else certain = false
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the `i`'th read in block `bb` reads to the variable `v`.
|
||||
* `certain` is `true` if the read is guaranteed. For C++, this is always the case.
|
||||
*/
|
||||
predicate variableRead(IRBlock bb, int i, SourceVariable v, boolean certain) {
|
||||
exists(UseImpl use | use.hasIndexInBlock(bb, i, v) |
|
||||
if use.isCertain() then certain = true else certain = false
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The final SSA predicates used for dataflow purposes.
|
||||
*/
|
||||
cached
|
||||
module SsaCached {
|
||||
/**
|
||||
* Holds if `def` is accessed at index `i1` in basic block `bb1` (either a read
|
||||
* or a write), `def` is read at index `i2` in basic block `bb2`, and there is a
|
||||
* path between them without any read of `def`.
|
||||
*/
|
||||
cached
|
||||
predicate adjacentDefRead(Definition def, IRBlock bb1, int i1, IRBlock bb2, int i2) {
|
||||
SsaImpl::adjacentDefRead(def, bb1, i1, bb2, i2)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the node at index `i` in `bb` is a last reference to SSA definition
|
||||
* `def`. The reference is last because it can reach another write `next`,
|
||||
* without passing through another read or write.
|
||||
*/
|
||||
cached
|
||||
predicate lastRefRedef(Definition def, IRBlock bb, int i, Definition next) {
|
||||
SsaImpl::lastRefRedef(def, bb, i, next)
|
||||
}
|
||||
}
|
||||
|
||||
cached
|
||||
private newtype TSsaDefOrUse =
|
||||
TDefOrUse(DefOrUseImpl defOrUse) {
|
||||
defOrUse instanceof UseImpl
|
||||
or
|
||||
// Like in the pruning stage, we only include definition that's live after the
|
||||
// write as the final definitions computed by SSA.
|
||||
exists(Definition def, SourceVariable sv, IRBlock bb, int i |
|
||||
def.definesAt(sv, bb, i) and
|
||||
defOrUse.(DefImpl).hasIndexInBlock(bb, i, sv)
|
||||
)
|
||||
} or
|
||||
TPhi(PhiNode phi)
|
||||
|
||||
abstract private class SsaDefOrUse extends TSsaDefOrUse {
|
||||
string toString() { none() }
|
||||
|
||||
DefOrUseImpl asDefOrUse() { none() }
|
||||
|
||||
PhiNode asPhi() { none() }
|
||||
|
||||
abstract Location getLocation();
|
||||
}
|
||||
|
||||
class DefOrUse extends TDefOrUse, SsaDefOrUse {
|
||||
DefOrUseImpl defOrUse;
|
||||
|
||||
DefOrUse() { this = TDefOrUse(defOrUse) }
|
||||
|
||||
final override DefOrUseImpl asDefOrUse() { result = defOrUse }
|
||||
|
||||
final override Location getLocation() { result = defOrUse.getLocation() }
|
||||
|
||||
final SourceVariable getSourceVariable() { result = defOrUse.getSourceVariable() }
|
||||
|
||||
override string toString() { result = defOrUse.toString() }
|
||||
}
|
||||
|
||||
class Phi extends TPhi, SsaDefOrUse {
|
||||
PhiNode phi;
|
||||
|
||||
Phi() { this = TPhi(phi) }
|
||||
|
||||
final override PhiNode asPhi() { result = phi }
|
||||
|
||||
final override Location getLocation() { result = phi.getBasicBlock().getLocation() }
|
||||
|
||||
override string toString() { result = "Phi" }
|
||||
}
|
||||
|
||||
class UseOrPhi extends SsaDefOrUse {
|
||||
UseOrPhi() {
|
||||
this.asDefOrUse() instanceof UseImpl
|
||||
or
|
||||
this instanceof Phi
|
||||
}
|
||||
|
||||
final override Location getLocation() {
|
||||
result = this.asDefOrUse().getLocation() or result = this.(Phi).getLocation()
|
||||
}
|
||||
}
|
||||
|
||||
class Def extends DefOrUse {
|
||||
override DefImpl defOrUse;
|
||||
|
||||
Operand getAddressOperand() { result = defOrUse.getAddressOperand() }
|
||||
|
||||
Instruction getAddress() { result = this.getAddressOperand().getDef() }
|
||||
|
||||
/**
|
||||
* This predicate ensures that joins go from `defOrUse` to the result
|
||||
* instead of the other way around.
|
||||
*/
|
||||
pragma[inline]
|
||||
int getIndirectionIndex() {
|
||||
pragma[only_bind_into](result) = pragma[only_bind_out](defOrUse).getIndirectionIndex()
|
||||
}
|
||||
|
||||
Instruction getDefiningInstruction() { result = defOrUse.getDefiningInstruction() }
|
||||
}
|
||||
|
||||
private module SsaImpl = SsaImplCommon::Make<SsaInput>;
|
||||
|
||||
class PhiNode = SsaImpl::PhiNode;
|
||||
|
||||
class Definition = SsaImpl::Definition;
|
||||
|
||||
import SsaCached
|
||||
@@ -1,270 +0,0 @@
|
||||
import cpp as Cpp
|
||||
import semmle.code.cpp.ir.IR
|
||||
import semmle.code.cpp.ir.internal.IRCppLanguage
|
||||
private import semmle.code.cpp.ir.implementation.raw.internal.SideEffects as SideEffects
|
||||
private import DataFlowImplCommon as DataFlowImplCommon
|
||||
private import DataFlowUtil
|
||||
|
||||
/**
|
||||
* Holds if `operand` is an operand that is not used by the dataflow library.
|
||||
* Ignored operands are not recognizd as uses by SSA, and they don't have a
|
||||
* corresponding `(Indirect)OperandNode`.
|
||||
*/
|
||||
predicate ignoreOperand(Operand operand) {
|
||||
operand = any(Instruction instr | ignoreInstruction(instr)).getAnOperand() or
|
||||
operand = any(Instruction instr | ignoreInstruction(instr)).getAUse() or
|
||||
operand instanceof MemoryOperand
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` is an instruction that is not used by the dataflow library.
|
||||
* Ignored instructions are not recognized as reads/writes by SSA, and they
|
||||
* don't have a corresponding `(Indirect)InstructionNode`.
|
||||
*/
|
||||
predicate ignoreInstruction(Instruction instr) {
|
||||
DataFlowImplCommon::forceCachingInSameStage() and
|
||||
(
|
||||
instr instanceof WriteSideEffectInstruction or
|
||||
instr instanceof PhiInstruction or
|
||||
instr instanceof ReadSideEffectInstruction or
|
||||
instr instanceof ChiInstruction or
|
||||
instr instanceof InitializeIndirectionInstruction
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the C++ type of `this` in the member function `f`.
|
||||
* The result is a glvalue if `isGLValue` is true, and
|
||||
* a prvalue if `isGLValue` is false.
|
||||
*/
|
||||
bindingset[isGLValue]
|
||||
private CppType getThisType(Cpp::MemberFunction f, boolean isGLValue) {
|
||||
result.hasType(f.getTypeOfThis(), isGLValue)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the C++ type of the instruction `i`.
|
||||
*
|
||||
* This is equivalent to `i.getResultLanguageType()` with the exception
|
||||
* of instructions that directly references a `this` IRVariable. In this
|
||||
* case, `i.getResultLanguageType()` gives an unknown type, whereas the
|
||||
* predicate gives the expected type (i.e., a potentially cv-qualified
|
||||
* type `A*` where `A` is the declaring type of the member function that
|
||||
* contains `i`).
|
||||
*/
|
||||
cached
|
||||
CppType getResultLanguageType(Instruction i) {
|
||||
if i.(VariableAddressInstruction).getIRVariable() instanceof IRThisVariable
|
||||
then
|
||||
if i.isGLValue()
|
||||
then result = getThisType(i.getEnclosingFunction(), true)
|
||||
else result = getThisType(i.getEnclosingFunction(), false)
|
||||
else result = i.getResultLanguageType()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the C++ type of the operand `operand`.
|
||||
* This is equivalent to the type of the operand's defining instruction.
|
||||
*
|
||||
* See `getResultLanguageType` for a description of this behavior.
|
||||
*/
|
||||
CppType getLanguageType(Operand operand) { result = getResultLanguageType(operand.getDef()) }
|
||||
|
||||
/**
|
||||
* Gets the maximum number of indirections a glvalue of type `type` can have.
|
||||
* For example:
|
||||
* - If `type = int`, the result is 1
|
||||
* - If `type = MyStruct`, the result is 1
|
||||
* - If `type = char*`, the result is 2
|
||||
*/
|
||||
int getMaxIndirectionsForType(Type type) {
|
||||
result = countIndirectionsForCppType(getTypeForGLValue(type))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the maximum number of indirections a value of type `type` can have.
|
||||
*
|
||||
* Note that this predicate is intended to be called on unspecified types
|
||||
* (i.e., `countIndirections(e.getUnspecifiedType())`).
|
||||
*/
|
||||
private int countIndirections(Type t) {
|
||||
result =
|
||||
1 +
|
||||
countIndirections([t.(Cpp::PointerType).getBaseType(), t.(Cpp::ReferenceType).getBaseType()])
|
||||
or
|
||||
not t instanceof Cpp::PointerType and
|
||||
not t instanceof Cpp::ReferenceType and
|
||||
result = 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the maximum number of indirections a value of C++
|
||||
* type `langType` can have.
|
||||
*/
|
||||
int countIndirectionsForCppType(LanguageType langType) {
|
||||
exists(Type type | langType.hasType(type, true) |
|
||||
result = 1 + countIndirections(type.getUnspecifiedType())
|
||||
)
|
||||
or
|
||||
exists(Type type | langType.hasType(type, false) |
|
||||
result = countIndirections(type.getUnspecifiedType())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A `CallInstruction` that calls an allocation function such
|
||||
* as `malloc` or `operator new`.
|
||||
*/
|
||||
class AllocationInstruction extends CallInstruction {
|
||||
AllocationInstruction() { this.getStaticCallTarget() instanceof Cpp::AllocationFunction }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `i` is a base instruction that starts a sequence of uses
|
||||
* of some variable that SSA can handle.
|
||||
*
|
||||
* This is either when `i` is a `VariableAddressInstruction` or when
|
||||
* `i` is a fresh allocation produced by an `AllocationInstruction`.
|
||||
*/
|
||||
private predicate isSourceVariableBase(Instruction i) {
|
||||
i instanceof VariableAddressInstruction or i instanceof AllocationInstruction
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the value pointed to by `operand` can potentially be
|
||||
* modified be the caller.
|
||||
*/
|
||||
predicate isModifiableByCall(ArgumentOperand operand) {
|
||||
exists(CallInstruction call, int index, CppType type |
|
||||
type = getLanguageType(operand) and
|
||||
call.getArgumentOperand(index) = operand and
|
||||
if index = -1
|
||||
then not call.getStaticCallTarget() instanceof Cpp::ConstMemberFunction
|
||||
else not SideEffects::isConstPointerLike(any(Type t | type.hasType(t, _)))
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
private module Cached {
|
||||
/**
|
||||
* Holds if `op` is a use of an SSA variable rooted at `base` with `ind` number
|
||||
* of indirections.
|
||||
*
|
||||
* `certain` is `true` if the operand is guaranteed to read the variable, and
|
||||
* `indirectionIndex` specifies the number of loads required to read the variable.
|
||||
*/
|
||||
cached
|
||||
predicate isUse(boolean certain, Operand op, Instruction base, int ind, int indirectionIndex) {
|
||||
not ignoreOperand(op) and
|
||||
certain = true and
|
||||
exists(LanguageType type, int m, int ind0 |
|
||||
type = getLanguageType(op) and
|
||||
m = countIndirectionsForCppType(type) and
|
||||
isUseImpl(op, base, ind0) and
|
||||
ind = ind0 + [0 .. m] and
|
||||
indirectionIndex = ind - ind0
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `operand` is a use of an SSA variable rooted at `base`, and the
|
||||
* path from `base` to `operand` passes through `ind` load-like instructions.
|
||||
*/
|
||||
private predicate isUseImpl(Operand operand, Instruction base, int ind) {
|
||||
DataFlowImplCommon::forceCachingInSameStage() and
|
||||
ind = 0 and
|
||||
operand.getDef() = base and
|
||||
isSourceVariableBase(base)
|
||||
or
|
||||
exists(Operand mid, Instruction instr |
|
||||
isUseImpl(mid, base, ind) and
|
||||
instr = operand.getDef() and
|
||||
conversionFlow(mid, instr, false)
|
||||
)
|
||||
or
|
||||
exists(int ind0 |
|
||||
isUseImpl(operand.getDef().(LoadInstruction).getSourceAddressOperand(), base, ind0)
|
||||
or
|
||||
isUseImpl(operand.getDef().(InitializeParameterInstruction).getAnOperand(), base, ind0)
|
||||
|
|
||||
ind0 = ind - 1
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `address` is an address of an SSA variable rooted at `base`,
|
||||
* and `instr` is a definition of the SSA variable with `ind` number of indirections.
|
||||
*
|
||||
* `certain` is `true` if `instr` is guaranteed to write to the variable, and
|
||||
* `indirectionIndex` specifies the number of loads required to read the variable
|
||||
* after the write operation.
|
||||
*/
|
||||
cached
|
||||
predicate isDef(
|
||||
boolean certain, Instruction instr, Operand address, Instruction base, int ind,
|
||||
int indirectionIndex
|
||||
) {
|
||||
certain = true and
|
||||
exists(int ind0, CppType type, int m |
|
||||
address =
|
||||
[
|
||||
instr.(StoreInstruction).getDestinationAddressOperand(),
|
||||
instr.(InitializeParameterInstruction).getAnOperand(),
|
||||
instr.(InitializeDynamicAllocationInstruction).getAllocationAddressOperand(),
|
||||
instr.(UninitializedInstruction).getAnOperand()
|
||||
]
|
||||
|
|
||||
isDefImpl(address, base, ind0) and
|
||||
type = getLanguageType(address) and
|
||||
m = countIndirectionsForCppType(type) and
|
||||
ind = ind0 + [1 .. m] and
|
||||
indirectionIndex = ind - (ind0 + 1)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `address` is a use of an SSA variable rooted at `base`, and the
|
||||
* path from `base` to `address` passes through `ind` load-like instructions.
|
||||
*
|
||||
* Note: Unlike `isUseImpl`, this predicate recurses through pointer-arithmetic
|
||||
* instructions.
|
||||
*/
|
||||
private predicate isDefImpl(Operand address, Instruction base, int ind) {
|
||||
DataFlowImplCommon::forceCachingInSameStage() and
|
||||
ind = 0 and
|
||||
address.getDef() = base and
|
||||
isSourceVariableBase(base)
|
||||
or
|
||||
exists(Operand mid, Instruction instr |
|
||||
isDefImpl(mid, base, ind) and
|
||||
instr = address.getDef() and
|
||||
conversionFlow(mid, instr, _)
|
||||
)
|
||||
or
|
||||
exists(int ind0 |
|
||||
isDefImpl(address.getDef().(LoadInstruction).getSourceAddressOperand(), base, ind0)
|
||||
or
|
||||
isDefImpl(address.getDef().(InitializeParameterInstruction).getAnOperand(), base, ind0)
|
||||
|
|
||||
ind0 = ind - 1
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
import Cached
|
||||
|
||||
/**
|
||||
* Inputs to the shared SSA library's parameterized module that is shared
|
||||
* between the SSA pruning stage, and the final SSA stage.
|
||||
*/
|
||||
module InputSigCommon {
|
||||
class BasicBlock = IRBlock;
|
||||
|
||||
BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) { result.immediatelyDominates(bb) }
|
||||
|
||||
BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.getASuccessor() }
|
||||
|
||||
class ExitBasicBlock extends IRBlock {
|
||||
ExitBasicBlock() { this.getLastInstruction() instanceof ExitFunctionInstruction }
|
||||
}
|
||||
}
|
||||
@@ -1,208 +0,0 @@
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import experimental.semmle.code.cpp.ir.dataflow.DataFlow
|
||||
private import ModelUtil
|
||||
private import semmle.code.cpp.models.interfaces.DataFlow
|
||||
private import semmle.code.cpp.models.interfaces.SideEffect
|
||||
private import DataFlowUtil
|
||||
private import DataFlowPrivate
|
||||
private import semmle.code.cpp.models.Models
|
||||
|
||||
/**
|
||||
* Holds if taint propagates from `nodeFrom` to `nodeTo` in exactly one local
|
||||
* (intra-procedural) step.
|
||||
*/
|
||||
predicate localTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
DataFlow::localFlowStep(nodeFrom, nodeTo)
|
||||
or
|
||||
localAdditionalTaintStep(nodeFrom, nodeTo)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint can flow in one local step from `nodeFrom` to `nodeTo` excluding
|
||||
* local data flow steps. That is, `nodeFrom` and `nodeTo` are likely to represent
|
||||
* different objects.
|
||||
*/
|
||||
cached
|
||||
predicate localAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
operandToInstructionTaintStep(nodeFrom.asOperand(), nodeTo.asInstruction())
|
||||
or
|
||||
modeledTaintStep(nodeFrom, nodeTo)
|
||||
or
|
||||
// Flow from `op` to `*op`.
|
||||
exists(Operand operand, int indirectionIndex |
|
||||
nodeHasOperand(nodeFrom, operand, indirectionIndex) and
|
||||
nodeHasOperand(nodeTo, operand, indirectionIndex - 1)
|
||||
)
|
||||
or
|
||||
// Flow from `instr` to `*instr`.
|
||||
exists(Instruction instr, int indirectionIndex |
|
||||
nodeHasInstruction(nodeFrom, instr, indirectionIndex) and
|
||||
nodeHasInstruction(nodeTo, instr, indirectionIndex - 1)
|
||||
)
|
||||
or
|
||||
// Flow from (the indirection of) an operand of a pointer arithmetic instruction to the
|
||||
// indirection of the pointer arithmetic instruction. This provides flow from `source`
|
||||
// in `x[source]` to the result of the associated load instruction.
|
||||
exists(PointerArithmeticInstruction pai, int indirectionIndex |
|
||||
nodeHasOperand(nodeFrom, pai.getAnOperand(), pragma[only_bind_into](indirectionIndex)) and
|
||||
hasInstructionAndIndex(nodeTo, pai, indirectionIndex + 1)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint propagates from `nodeFrom` to `nodeTo` in exactly one local
|
||||
* (intra-procedural) step.
|
||||
*/
|
||||
private predicate operandToInstructionTaintStep(Operand opFrom, Instruction instrTo) {
|
||||
// Taint can flow through expressions that alter the value but preserve
|
||||
// more than one bit of it _or_ expressions that follow data through
|
||||
// pointer indirections.
|
||||
instrTo.getAnOperand() = opFrom and
|
||||
(
|
||||
instrTo instanceof ArithmeticInstruction
|
||||
or
|
||||
instrTo instanceof BitwiseInstruction
|
||||
or
|
||||
instrTo instanceof PointerArithmeticInstruction
|
||||
)
|
||||
or
|
||||
// The `CopyInstruction` case is also present in non-taint data flow, but
|
||||
// that uses `getDef` rather than `getAnyDef`. For taint, we want flow
|
||||
// from a definition of `myStruct` to a `myStruct.myField` expression.
|
||||
instrTo.(LoadInstruction).getSourceAddressOperand() = opFrom
|
||||
or
|
||||
// Unary instructions tend to preserve enough information in practice that we
|
||||
// want taint to flow through.
|
||||
// The exception is `FieldAddressInstruction`. Together with the rules below for
|
||||
// `LoadInstruction`s and `ChiInstruction`s, flow through `FieldAddressInstruction`
|
||||
// could cause flow into one field to come out an unrelated field.
|
||||
// This would happen across function boundaries, where the IR would not be able to
|
||||
// match loads to stores.
|
||||
instrTo.(UnaryInstruction).getUnaryOperand() = opFrom and
|
||||
(
|
||||
not instrTo instanceof FieldAddressInstruction
|
||||
or
|
||||
instrTo.(FieldAddressInstruction).getField().getDeclaringType() instanceof Union
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint may propagate from `source` to `sink` in zero or more local
|
||||
* (intra-procedural) steps.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate localTaint(DataFlow::Node source, DataFlow::Node sink) { localTaintStep*(source, sink) }
|
||||
|
||||
/**
|
||||
* Holds if taint can flow from `i1` to `i2` in zero or more
|
||||
* local (intra-procedural) steps.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate localInstructionTaint(Instruction i1, Instruction i2) {
|
||||
localTaint(DataFlow::instructionNode(i1), DataFlow::instructionNode(i2))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint can flow from `e1` to `e2` in zero or more
|
||||
* local (intra-procedural) steps.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate localExprTaint(Expr e1, Expr e2) {
|
||||
localTaint(DataFlow::exprNode(e1), DataFlow::exprNode(e2))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the additional step from `src` to `sink` should be included in all
|
||||
* global taint flow configurations.
|
||||
*/
|
||||
predicate defaultAdditionalTaintStep(DataFlow::Node src, DataFlow::Node sink) {
|
||||
localAdditionalTaintStep(src, sink)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if default `TaintTracking::Configuration`s should allow implicit reads
|
||||
* of `c` at sinks and inputs to additional taint steps.
|
||||
*/
|
||||
bindingset[node]
|
||||
predicate defaultImplicitTaintRead(DataFlow::Node node, DataFlow::Content c) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `node` should be a sanitizer in all global taint flow configurations
|
||||
* but not in local taint.
|
||||
*/
|
||||
predicate defaultTaintSanitizer(DataFlow::Node node) { none() }
|
||||
|
||||
/**
|
||||
* Holds if taint can flow from `instrIn` to `instrOut` through a call to a
|
||||
* modeled function.
|
||||
*/
|
||||
predicate modeledTaintStep(DataFlow::Node nodeIn, DataFlow::Node nodeOut) {
|
||||
// Normal taint steps
|
||||
exists(CallInstruction call, TaintFunction func, FunctionInput modelIn, FunctionOutput modelOut |
|
||||
call.getStaticCallTarget() = func and
|
||||
func.hasTaintFlow(modelIn, modelOut)
|
||||
|
|
||||
(
|
||||
nodeIn = callInput(call, modelIn)
|
||||
or
|
||||
exists(int n |
|
||||
modelIn.isParameterDerefOrQualifierObject(n) and
|
||||
if n = -1
|
||||
then nodeIn = callInput(call, any(InQualifierAddress inQualifier))
|
||||
else nodeIn = callInput(call, any(InParameter inParam | inParam.getIndex() = n))
|
||||
)
|
||||
) and
|
||||
nodeOut = callOutput(call, modelOut)
|
||||
or
|
||||
exists(int d |
|
||||
nodeIn = callInput(call, modelIn, d)
|
||||
or
|
||||
exists(int n |
|
||||
d = 1 and
|
||||
modelIn.isParameterDerefOrQualifierObject(n) and
|
||||
if n = -1
|
||||
then nodeIn = callInput(call, any(InQualifierAddress inQualifier))
|
||||
else nodeIn = callInput(call, any(InParameter inParam | inParam.getIndex() = n))
|
||||
)
|
||||
|
|
||||
call.getStaticCallTarget() = func and
|
||||
func.hasTaintFlow(modelIn, modelOut) and
|
||||
nodeOut = callOutput(call, modelOut, d)
|
||||
)
|
||||
)
|
||||
or
|
||||
// Taint flow from one argument to another and data flow from an argument to a
|
||||
// return value. This happens in functions like `strcat` and `memcpy`. We
|
||||
// could model this flow in two separate steps, but that would add reverse
|
||||
// flow from the write side-effect to the call instruction, which may not be
|
||||
// desirable.
|
||||
exists(
|
||||
CallInstruction call, Function func, FunctionInput modelIn, OutParameterDeref modelMidOut,
|
||||
int indexMid, InParameter modelMidIn, OutReturnValue modelOut
|
||||
|
|
||||
nodeIn = callInput(call, modelIn) and
|
||||
nodeOut = callOutput(call, modelOut) and
|
||||
call.getStaticCallTarget() = func and
|
||||
func.(TaintFunction).hasTaintFlow(modelIn, modelMidOut) and
|
||||
func.(DataFlowFunction).hasDataFlow(modelMidIn, modelOut) and
|
||||
modelMidOut.isParameterDeref(indexMid) and
|
||||
modelMidIn.isParameter(indexMid)
|
||||
)
|
||||
or
|
||||
// Taint flow from a pointer argument to an output, when the model specifies flow from the deref
|
||||
// to that output, but the deref is not modeled in the IR for the caller.
|
||||
exists(
|
||||
CallInstruction call, DataFlow::SideEffectOperandNode indirectArgument, Function func,
|
||||
FunctionInput modelIn, FunctionOutput modelOut
|
||||
|
|
||||
indirectArgument = callInput(call, modelIn) and
|
||||
indirectArgument.getAddressOperand() = nodeIn.asOperand() and
|
||||
call.getStaticCallTarget() = func and
|
||||
(
|
||||
func.(DataFlowFunction).hasDataFlow(modelIn, modelOut)
|
||||
or
|
||||
func.(TaintFunction).hasTaintFlow(modelIn, modelOut)
|
||||
) and
|
||||
nodeOut = callOutput(call, modelOut)
|
||||
)
|
||||
}
|
||||
@@ -1,314 +0,0 @@
|
||||
/**
|
||||
* This module defines an initial SSA pruning stage that doesn't take
|
||||
* indirections into account.
|
||||
*/
|
||||
|
||||
private import codeql.ssa.Ssa as SsaImplCommon
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import experimental.semmle.code.cpp.ir.dataflow.internal.DataFlowImplCommon as DataFlowImplCommon
|
||||
private import semmle.code.cpp.models.interfaces.Allocation as Alloc
|
||||
private import semmle.code.cpp.models.interfaces.DataFlow as DataFlow
|
||||
private import semmle.code.cpp.ir.implementation.raw.internal.SideEffects as SideEffects
|
||||
private import semmle.code.cpp.ir.internal.IRCppLanguage
|
||||
private import experimental.semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate
|
||||
private import experimental.semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
|
||||
private import experimental.semmle.code.cpp.ir.dataflow.internal.SsaInternalsCommon
|
||||
|
||||
private module SourceVariables {
|
||||
newtype TBaseSourceVariable =
|
||||
// Each IR variable gets its own source variable
|
||||
TBaseIRVariable(IRVariable var) or
|
||||
// Each allocation gets its own source variable
|
||||
TBaseCallVariable(AllocationInstruction call)
|
||||
|
||||
abstract class BaseSourceVariable extends TBaseSourceVariable {
|
||||
abstract string toString();
|
||||
|
||||
abstract DataFlowType getType();
|
||||
}
|
||||
|
||||
class BaseIRVariable extends BaseSourceVariable, TBaseIRVariable {
|
||||
IRVariable var;
|
||||
|
||||
IRVariable getIRVariable() { result = var }
|
||||
|
||||
BaseIRVariable() { this = TBaseIRVariable(var) }
|
||||
|
||||
override string toString() { result = var.toString() }
|
||||
|
||||
override DataFlowType getType() { result = var.getType() }
|
||||
}
|
||||
|
||||
class BaseCallVariable extends BaseSourceVariable, TBaseCallVariable {
|
||||
AllocationInstruction call;
|
||||
|
||||
BaseCallVariable() { this = TBaseCallVariable(call) }
|
||||
|
||||
AllocationInstruction getCallInstruction() { result = call }
|
||||
|
||||
override string toString() { result = call.toString() }
|
||||
|
||||
override DataFlowType getType() { result = call.getResultType() }
|
||||
}
|
||||
|
||||
private newtype TSourceVariable =
|
||||
TSourceIRVariable(BaseIRVariable baseVar) or
|
||||
TCallVariable(AllocationInstruction call)
|
||||
|
||||
abstract class SourceVariable extends TSourceVariable {
|
||||
abstract string toString();
|
||||
|
||||
abstract BaseSourceVariable getBaseVariable();
|
||||
}
|
||||
|
||||
class SourceIRVariable extends SourceVariable, TSourceIRVariable {
|
||||
BaseIRVariable var;
|
||||
|
||||
SourceIRVariable() { this = TSourceIRVariable(var) }
|
||||
|
||||
IRVariable getIRVariable() { result = var.getIRVariable() }
|
||||
|
||||
override BaseIRVariable getBaseVariable() { result.getIRVariable() = this.getIRVariable() }
|
||||
|
||||
override string toString() { result = this.getIRVariable().toString() }
|
||||
}
|
||||
|
||||
class CallVariable extends SourceVariable, TCallVariable {
|
||||
AllocationInstruction call;
|
||||
|
||||
CallVariable() { this = TCallVariable(call) }
|
||||
|
||||
AllocationInstruction getCall() { result = call }
|
||||
|
||||
override BaseCallVariable getBaseVariable() { result.getCallInstruction() = call }
|
||||
|
||||
override string toString() { result = "Call" }
|
||||
}
|
||||
}
|
||||
|
||||
import SourceVariables
|
||||
|
||||
private newtype TDefOrUseImpl =
|
||||
TDefImpl(Operand address) { isDef(_, _, address, _, _, _) } or
|
||||
TUseImpl(Operand operand) {
|
||||
isUse(_, operand, _, _, _) and
|
||||
not isDef(_, _, operand, _, _, _)
|
||||
}
|
||||
|
||||
abstract private class DefOrUseImpl extends TDefOrUseImpl {
|
||||
/** Gets a textual representation of this element. */
|
||||
abstract string toString();
|
||||
|
||||
/** Gets the block of this definition or use. */
|
||||
abstract IRBlock getBlock();
|
||||
|
||||
/** Holds if this definition or use has index `index` in block `block`. */
|
||||
abstract predicate hasIndexInBlock(IRBlock block, int index);
|
||||
|
||||
final predicate hasIndexInBlock(IRBlock block, int index, SourceVariable sv) {
|
||||
this.hasIndexInBlock(block, index) and
|
||||
sv = this.getSourceVariable()
|
||||
}
|
||||
|
||||
/** Gets the location of this element. */
|
||||
abstract Cpp::Location getLocation();
|
||||
|
||||
abstract Instruction getBase();
|
||||
|
||||
final BaseSourceVariable getBaseSourceVariable() {
|
||||
exists(IRVariable var |
|
||||
result.(BaseIRVariable).getIRVariable() = var and
|
||||
instructionHasIRVariable(this.getBase(), var)
|
||||
)
|
||||
or
|
||||
result.(BaseCallVariable).getCallInstruction() = this.getBase()
|
||||
}
|
||||
|
||||
/** Gets the variable that is defined or used. */
|
||||
final SourceVariable getSourceVariable() {
|
||||
exists(BaseSourceVariable v |
|
||||
sourceVariableHasBaseAndIndex(result, v) and
|
||||
defOrUseHasSourceVariable(this, v)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate instructionHasIRVariable(VariableAddressInstruction vai, IRVariable var) {
|
||||
vai.getIRVariable() = var
|
||||
}
|
||||
|
||||
private predicate defOrUseHasSourceVariable(DefOrUseImpl defOrUse, BaseSourceVariable bv) {
|
||||
defHasSourceVariable(defOrUse, bv)
|
||||
or
|
||||
useHasSourceVariable(defOrUse, bv)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate defHasSourceVariable(DefImpl def, BaseSourceVariable bv) {
|
||||
bv = def.getBaseSourceVariable()
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate useHasSourceVariable(UseImpl use, BaseSourceVariable bv) {
|
||||
bv = use.getBaseSourceVariable()
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate sourceVariableHasBaseAndIndex(SourceVariable v, BaseSourceVariable bv) {
|
||||
v.getBaseVariable() = bv
|
||||
}
|
||||
|
||||
class DefImpl extends DefOrUseImpl, TDefImpl {
|
||||
Operand address;
|
||||
|
||||
DefImpl() { this = TDefImpl(address) }
|
||||
|
||||
override Instruction getBase() { isDef(_, _, address, result, _, _) }
|
||||
|
||||
Operand getAddressOperand() { result = address }
|
||||
|
||||
Instruction getDefiningInstruction() { isDef(_, result, address, _, _, _) }
|
||||
|
||||
override string toString() { result = address.toString() }
|
||||
|
||||
override IRBlock getBlock() { result = this.getDefiningInstruction().getBlock() }
|
||||
|
||||
override Cpp::Location getLocation() { result = this.getDefiningInstruction().getLocation() }
|
||||
|
||||
final override predicate hasIndexInBlock(IRBlock block, int index) {
|
||||
this.getDefiningInstruction() = block.getInstruction(index)
|
||||
}
|
||||
|
||||
predicate isCertain() { isDef(true, _, address, _, _, _) }
|
||||
}
|
||||
|
||||
class UseImpl extends DefOrUseImpl, TUseImpl {
|
||||
Operand operand;
|
||||
|
||||
UseImpl() { this = TUseImpl(operand) }
|
||||
|
||||
Operand getOperand() { result = operand }
|
||||
|
||||
override string toString() { result = operand.toString() }
|
||||
|
||||
final override predicate hasIndexInBlock(IRBlock block, int index) {
|
||||
operand.getUse() = block.getInstruction(index)
|
||||
}
|
||||
|
||||
final override IRBlock getBlock() { result = operand.getUse().getBlock() }
|
||||
|
||||
final override Cpp::Location getLocation() { result = operand.getLocation() }
|
||||
|
||||
override Instruction getBase() { isUse(_, operand, result, _, _) }
|
||||
|
||||
predicate isCertain() { isUse(true, operand, _, _, _) }
|
||||
}
|
||||
|
||||
private module SsaInput implements SsaImplCommon::InputSig {
|
||||
import InputSigCommon
|
||||
import SourceVariables
|
||||
|
||||
/**
|
||||
* Holds if the `i`'th write in block `bb` writes to the variable `v`.
|
||||
* `certain` is `true` if the write is guaranteed to overwrite the entire variable.
|
||||
*/
|
||||
predicate variableWrite(IRBlock bb, int i, SourceVariable v, boolean certain) {
|
||||
DataFlowImplCommon::forceCachingInSameStage() and
|
||||
exists(DefImpl def | def.hasIndexInBlock(bb, i, v) |
|
||||
if def.isCertain() then certain = true else certain = false
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the `i`'th read in block `bb` reads to the variable `v`.
|
||||
* `certain` is `true` if the read is guaranteed.
|
||||
*/
|
||||
predicate variableRead(IRBlock bb, int i, SourceVariable v, boolean certain) {
|
||||
exists(UseImpl use | use.hasIndexInBlock(bb, i, v) |
|
||||
if use.isCertain() then certain = true else certain = false
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private newtype TSsaDefOrUse =
|
||||
TDefOrUse(DefOrUseImpl defOrUse) {
|
||||
defOrUse instanceof UseImpl
|
||||
or
|
||||
// If `defOrUse` is a definition we only include it if the
|
||||
// SSA library concludes that it's live after the write.
|
||||
exists(Definition def, SourceVariable sv, IRBlock bb, int i |
|
||||
def.definesAt(sv, bb, i) and
|
||||
defOrUse.(DefImpl).hasIndexInBlock(bb, i, sv)
|
||||
)
|
||||
} or
|
||||
TPhi(PhiNode phi)
|
||||
|
||||
abstract private class SsaDefOrUse extends TSsaDefOrUse {
|
||||
string toString() { result = "SsaDefOrUse" }
|
||||
|
||||
DefOrUseImpl asDefOrUse() { none() }
|
||||
|
||||
PhiNode asPhi() { none() }
|
||||
|
||||
abstract Location getLocation();
|
||||
}
|
||||
|
||||
class DefOrUse extends TDefOrUse, SsaDefOrUse {
|
||||
DefOrUseImpl defOrUse;
|
||||
|
||||
DefOrUse() { this = TDefOrUse(defOrUse) }
|
||||
|
||||
final override DefOrUseImpl asDefOrUse() { result = defOrUse }
|
||||
|
||||
final override Location getLocation() { result = defOrUse.getLocation() }
|
||||
|
||||
final SourceVariable getSourceVariable() { result = defOrUse.getSourceVariable() }
|
||||
}
|
||||
|
||||
class Phi extends TPhi, SsaDefOrUse {
|
||||
PhiNode phi;
|
||||
|
||||
Phi() { this = TPhi(phi) }
|
||||
|
||||
final override PhiNode asPhi() { result = phi }
|
||||
|
||||
final override Location getLocation() { result = phi.getBasicBlock().getLocation() }
|
||||
}
|
||||
|
||||
class UseOrPhi extends SsaDefOrUse {
|
||||
UseOrPhi() {
|
||||
this.asDefOrUse() instanceof UseImpl
|
||||
or
|
||||
this instanceof Phi
|
||||
}
|
||||
|
||||
final override Location getLocation() {
|
||||
result = this.asDefOrUse().getLocation() or result = this.(Phi).getLocation()
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = this.asDefOrUse().toString()
|
||||
or
|
||||
this instanceof Phi and
|
||||
result = "Phi"
|
||||
}
|
||||
}
|
||||
|
||||
class Def extends DefOrUse {
|
||||
override DefImpl defOrUse;
|
||||
|
||||
Operand getAddressOperand() { result = defOrUse.getAddressOperand() }
|
||||
|
||||
Instruction getAddress() { result = this.getAddressOperand().getDef() }
|
||||
|
||||
Instruction getDefiningInstruction() { result = defOrUse.getDefiningInstruction() }
|
||||
|
||||
override string toString() { result = this.asDefOrUse().toString() + " (def)" }
|
||||
}
|
||||
|
||||
private module SsaImpl = SsaImplCommon::Make<SsaInput>;
|
||||
|
||||
class PhiNode = SsaImpl::PhiNode;
|
||||
|
||||
class Definition = SsaImpl::Definition;
|
||||
@@ -1,63 +0,0 @@
|
||||
/**
|
||||
* Provides classes for performing local (intra-procedural) and
|
||||
* global (inter-procedural) taint-tracking analyses.
|
||||
*/
|
||||
|
||||
import TaintTrackingParameter::Public
|
||||
private import TaintTrackingParameter::Private
|
||||
|
||||
private module AddTaintDefaults<DataFlowInternal::FullStateConfigSig Config> implements
|
||||
DataFlowInternal::FullStateConfigSig {
|
||||
import Config
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) {
|
||||
Config::isBarrier(node) or defaultTaintSanitizer(node)
|
||||
}
|
||||
|
||||
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
Config::isAdditionalFlowStep(node1, node2) or
|
||||
defaultAdditionalTaintStep(node1, node2)
|
||||
}
|
||||
|
||||
predicate allowImplicitRead(DataFlow::Node node, DataFlow::ContentSet c) {
|
||||
Config::allowImplicitRead(node, c)
|
||||
or
|
||||
(
|
||||
Config::isSink(node, _) or
|
||||
Config::isAdditionalFlowStep(node, _) or
|
||||
Config::isAdditionalFlowStep(node, _, _, _)
|
||||
) and
|
||||
defaultImplicitTaintRead(node, c)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a standard taint tracking computation.
|
||||
*/
|
||||
module Make<DataFlow::ConfigSig Config> implements DataFlow::DataFlowSig {
|
||||
private module Config0 implements DataFlowInternal::FullStateConfigSig {
|
||||
import DataFlowInternal::DefaultState<Config>
|
||||
import Config
|
||||
}
|
||||
|
||||
private module C implements DataFlowInternal::FullStateConfigSig {
|
||||
import AddTaintDefaults<Config0>
|
||||
}
|
||||
|
||||
import DataFlowInternal::Impl<C>
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a taint tracking computation using flow state.
|
||||
*/
|
||||
module MakeWithState<DataFlow::StateConfigSig Config> implements DataFlow::DataFlowSig {
|
||||
private module Config0 implements DataFlowInternal::FullStateConfigSig {
|
||||
import Config
|
||||
}
|
||||
|
||||
private module C implements DataFlowInternal::FullStateConfigSig {
|
||||
import AddTaintDefaults<Config0>
|
||||
}
|
||||
|
||||
import DataFlowInternal::Impl<C>
|
||||
}
|
||||
@@ -1,191 +0,0 @@
|
||||
/**
|
||||
* Provides an implementation of global (interprocedural) taint tracking.
|
||||
* This file re-exports the local (intraprocedural) taint-tracking analysis
|
||||
* from `TaintTrackingParameter::Public` and adds a global analysis, mainly
|
||||
* exposed through the `Configuration` class. For some languages, this file
|
||||
* exists in several identical copies, allowing queries to use multiple
|
||||
* `Configuration` classes that depend on each other without introducing
|
||||
* mutual recursion among those configurations.
|
||||
*/
|
||||
|
||||
import TaintTrackingParameter::Public
|
||||
private import TaintTrackingParameter::Private
|
||||
|
||||
/**
|
||||
* A configuration of interprocedural taint tracking analysis. This defines
|
||||
* sources, sinks, and any other configurable aspect of the analysis. Each
|
||||
* use of the taint tracking library must define its own unique extension of
|
||||
* this abstract class.
|
||||
*
|
||||
* A taint-tracking configuration is a special data flow configuration
|
||||
* (`DataFlow::Configuration`) that allows for flow through nodes that do not
|
||||
* necessarily preserve values but are still relevant from a taint tracking
|
||||
* perspective. (For example, string concatenation, where one of the operands
|
||||
* is tainted.)
|
||||
*
|
||||
* To create a configuration, extend this class with a subclass whose
|
||||
* characteristic predicate is a unique singleton string. For example, write
|
||||
*
|
||||
* ```ql
|
||||
* class MyAnalysisConfiguration extends TaintTracking::Configuration {
|
||||
* MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" }
|
||||
* // Override `isSource` and `isSink`.
|
||||
* // Optionally override `isSanitizer`.
|
||||
* // Optionally override `isSanitizerIn`.
|
||||
* // Optionally override `isSanitizerOut`.
|
||||
* // Optionally override `isSanitizerGuard`.
|
||||
* // Optionally override `isAdditionalTaintStep`.
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Then, to query whether there is flow between some `source` and `sink`,
|
||||
* write
|
||||
*
|
||||
* ```ql
|
||||
* exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink))
|
||||
* ```
|
||||
*
|
||||
* Multiple configurations can coexist, but it is unsupported to depend on
|
||||
* another `TaintTracking::Configuration` or a `DataFlow::Configuration` in the
|
||||
* overridden predicates that define sources, sinks, or additional steps.
|
||||
* Instead, the dependency should go to a `TaintTracking2::Configuration` or a
|
||||
* `DataFlow2::Configuration`, `DataFlow3::Configuration`, etc.
|
||||
*/
|
||||
abstract class Configuration extends DataFlow::Configuration {
|
||||
bindingset[this]
|
||||
Configuration() { any() }
|
||||
|
||||
/**
|
||||
* Holds if `source` is a relevant taint source.
|
||||
*
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate isSource(DataFlow::Node source) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `source` is a relevant taint source with the given initial
|
||||
* `state`.
|
||||
*
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate isSource(DataFlow::Node source, DataFlow::FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant taint sink
|
||||
*
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate isSink(DataFlow::Node sink) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant taint sink accepting `state`.
|
||||
*
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate isSink(DataFlow::Node sink, DataFlow::FlowState state) { none() }
|
||||
|
||||
/** Holds if the node `node` is a taint sanitizer. */
|
||||
predicate isSanitizer(DataFlow::Node node) { none() }
|
||||
|
||||
final override predicate isBarrier(DataFlow::Node node) {
|
||||
this.isSanitizer(node) or
|
||||
defaultTaintSanitizer(node)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the node `node` is a taint sanitizer when the flow state is
|
||||
* `state`.
|
||||
*/
|
||||
predicate isSanitizer(DataFlow::Node node, DataFlow::FlowState state) { none() }
|
||||
|
||||
final override predicate isBarrier(DataFlow::Node node, DataFlow::FlowState state) {
|
||||
this.isSanitizer(node, state)
|
||||
}
|
||||
|
||||
/** Holds if taint propagation into `node` is prohibited. */
|
||||
predicate isSanitizerIn(DataFlow::Node node) { none() }
|
||||
|
||||
final override predicate isBarrierIn(DataFlow::Node node) { this.isSanitizerIn(node) }
|
||||
|
||||
/** Holds if taint propagation out of `node` is prohibited. */
|
||||
predicate isSanitizerOut(DataFlow::Node node) { none() }
|
||||
|
||||
final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if taint propagation through nodes guarded by `guard` is prohibited.
|
||||
*/
|
||||
deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
|
||||
|
||||
deprecated final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
|
||||
this.isSanitizerGuard(guard)
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if taint propagation through nodes guarded by `guard` is prohibited
|
||||
* when the flow state is `state`.
|
||||
*/
|
||||
deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) {
|
||||
none()
|
||||
}
|
||||
|
||||
deprecated final override predicate isBarrierGuard(
|
||||
DataFlow::BarrierGuard guard, DataFlow::FlowState state
|
||||
) {
|
||||
this.isSanitizerGuard(guard, state)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint may propagate from `node1` to `node2` in addition to the normal data-flow and taint steps.
|
||||
*/
|
||||
predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { none() }
|
||||
|
||||
final override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
this.isAdditionalTaintStep(node1, node2) or
|
||||
defaultAdditionalTaintStep(node1, node2)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint may propagate from `node1` to `node2` in addition to the normal data-flow and taint steps.
|
||||
* This step is only applicable in `state1` and updates the flow state to `state2`.
|
||||
*/
|
||||
predicate isAdditionalTaintStep(
|
||||
DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
|
||||
DataFlow::FlowState state2
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
final override predicate isAdditionalFlowStep(
|
||||
DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
|
||||
DataFlow::FlowState state2
|
||||
) {
|
||||
this.isAdditionalTaintStep(node1, state1, node2, state2)
|
||||
}
|
||||
|
||||
override predicate allowImplicitRead(DataFlow::Node node, DataFlow::ContentSet c) {
|
||||
(
|
||||
this.isSink(node) or
|
||||
this.isSink(node, _) or
|
||||
this.isAdditionalTaintStep(node, _) or
|
||||
this.isAdditionalTaintStep(node, _, _, _)
|
||||
) and
|
||||
defaultImplicitTaintRead(node, c)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint may flow from `source` to `sink` for this configuration.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate hasFlow(DataFlow::Node source, DataFlow::Node sink) {
|
||||
super.hasFlow(source, sink)
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
import experimental.semmle.code.cpp.ir.dataflow.internal.TaintTrackingUtil as Public
|
||||
|
||||
module Private {
|
||||
import experimental.semmle.code.cpp.ir.dataflow.DataFlow::DataFlow as DataFlow
|
||||
import experimental.semmle.code.cpp.ir.dataflow.internal.DataFlowImpl as DataFlowInternal
|
||||
}
|
||||
@@ -1,191 +0,0 @@
|
||||
/**
|
||||
* Provides an implementation of global (interprocedural) taint tracking.
|
||||
* This file re-exports the local (intraprocedural) taint-tracking analysis
|
||||
* from `TaintTrackingParameter::Public` and adds a global analysis, mainly
|
||||
* exposed through the `Configuration` class. For some languages, this file
|
||||
* exists in several identical copies, allowing queries to use multiple
|
||||
* `Configuration` classes that depend on each other without introducing
|
||||
* mutual recursion among those configurations.
|
||||
*/
|
||||
|
||||
import TaintTrackingParameter::Public
|
||||
private import TaintTrackingParameter::Private
|
||||
|
||||
/**
|
||||
* A configuration of interprocedural taint tracking analysis. This defines
|
||||
* sources, sinks, and any other configurable aspect of the analysis. Each
|
||||
* use of the taint tracking library must define its own unique extension of
|
||||
* this abstract class.
|
||||
*
|
||||
* A taint-tracking configuration is a special data flow configuration
|
||||
* (`DataFlow::Configuration`) that allows for flow through nodes that do not
|
||||
* necessarily preserve values but are still relevant from a taint tracking
|
||||
* perspective. (For example, string concatenation, where one of the operands
|
||||
* is tainted.)
|
||||
*
|
||||
* To create a configuration, extend this class with a subclass whose
|
||||
* characteristic predicate is a unique singleton string. For example, write
|
||||
*
|
||||
* ```ql
|
||||
* class MyAnalysisConfiguration extends TaintTracking::Configuration {
|
||||
* MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" }
|
||||
* // Override `isSource` and `isSink`.
|
||||
* // Optionally override `isSanitizer`.
|
||||
* // Optionally override `isSanitizerIn`.
|
||||
* // Optionally override `isSanitizerOut`.
|
||||
* // Optionally override `isSanitizerGuard`.
|
||||
* // Optionally override `isAdditionalTaintStep`.
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Then, to query whether there is flow between some `source` and `sink`,
|
||||
* write
|
||||
*
|
||||
* ```ql
|
||||
* exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink))
|
||||
* ```
|
||||
*
|
||||
* Multiple configurations can coexist, but it is unsupported to depend on
|
||||
* another `TaintTracking::Configuration` or a `DataFlow::Configuration` in the
|
||||
* overridden predicates that define sources, sinks, or additional steps.
|
||||
* Instead, the dependency should go to a `TaintTracking2::Configuration` or a
|
||||
* `DataFlow2::Configuration`, `DataFlow3::Configuration`, etc.
|
||||
*/
|
||||
abstract class Configuration extends DataFlow::Configuration {
|
||||
bindingset[this]
|
||||
Configuration() { any() }
|
||||
|
||||
/**
|
||||
* Holds if `source` is a relevant taint source.
|
||||
*
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate isSource(DataFlow::Node source) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `source` is a relevant taint source with the given initial
|
||||
* `state`.
|
||||
*
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate isSource(DataFlow::Node source, DataFlow::FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant taint sink
|
||||
*
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate isSink(DataFlow::Node sink) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant taint sink accepting `state`.
|
||||
*
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate isSink(DataFlow::Node sink, DataFlow::FlowState state) { none() }
|
||||
|
||||
/** Holds if the node `node` is a taint sanitizer. */
|
||||
predicate isSanitizer(DataFlow::Node node) { none() }
|
||||
|
||||
final override predicate isBarrier(DataFlow::Node node) {
|
||||
this.isSanitizer(node) or
|
||||
defaultTaintSanitizer(node)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the node `node` is a taint sanitizer when the flow state is
|
||||
* `state`.
|
||||
*/
|
||||
predicate isSanitizer(DataFlow::Node node, DataFlow::FlowState state) { none() }
|
||||
|
||||
final override predicate isBarrier(DataFlow::Node node, DataFlow::FlowState state) {
|
||||
this.isSanitizer(node, state)
|
||||
}
|
||||
|
||||
/** Holds if taint propagation into `node` is prohibited. */
|
||||
predicate isSanitizerIn(DataFlow::Node node) { none() }
|
||||
|
||||
final override predicate isBarrierIn(DataFlow::Node node) { this.isSanitizerIn(node) }
|
||||
|
||||
/** Holds if taint propagation out of `node` is prohibited. */
|
||||
predicate isSanitizerOut(DataFlow::Node node) { none() }
|
||||
|
||||
final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if taint propagation through nodes guarded by `guard` is prohibited.
|
||||
*/
|
||||
deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
|
||||
|
||||
deprecated final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
|
||||
this.isSanitizerGuard(guard)
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if taint propagation through nodes guarded by `guard` is prohibited
|
||||
* when the flow state is `state`.
|
||||
*/
|
||||
deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) {
|
||||
none()
|
||||
}
|
||||
|
||||
deprecated final override predicate isBarrierGuard(
|
||||
DataFlow::BarrierGuard guard, DataFlow::FlowState state
|
||||
) {
|
||||
this.isSanitizerGuard(guard, state)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint may propagate from `node1` to `node2` in addition to the normal data-flow and taint steps.
|
||||
*/
|
||||
predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { none() }
|
||||
|
||||
final override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
this.isAdditionalTaintStep(node1, node2) or
|
||||
defaultAdditionalTaintStep(node1, node2)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint may propagate from `node1` to `node2` in addition to the normal data-flow and taint steps.
|
||||
* This step is only applicable in `state1` and updates the flow state to `state2`.
|
||||
*/
|
||||
predicate isAdditionalTaintStep(
|
||||
DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
|
||||
DataFlow::FlowState state2
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
final override predicate isAdditionalFlowStep(
|
||||
DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
|
||||
DataFlow::FlowState state2
|
||||
) {
|
||||
this.isAdditionalTaintStep(node1, state1, node2, state2)
|
||||
}
|
||||
|
||||
override predicate allowImplicitRead(DataFlow::Node node, DataFlow::ContentSet c) {
|
||||
(
|
||||
this.isSink(node) or
|
||||
this.isSink(node, _) or
|
||||
this.isAdditionalTaintStep(node, _) or
|
||||
this.isAdditionalTaintStep(node, _, _, _)
|
||||
) and
|
||||
defaultImplicitTaintRead(node, c)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint may flow from `source` to `sink` for this configuration.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate hasFlow(DataFlow::Node source, DataFlow::Node sink) {
|
||||
super.hasFlow(source, sink)
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
import experimental.semmle.code.cpp.ir.dataflow.internal.TaintTrackingUtil as Public
|
||||
|
||||
module Private {
|
||||
import experimental.semmle.code.cpp.ir.dataflow.DataFlow2::DataFlow2 as DataFlow
|
||||
}
|
||||
@@ -1,191 +0,0 @@
|
||||
/**
|
||||
* Provides an implementation of global (interprocedural) taint tracking.
|
||||
* This file re-exports the local (intraprocedural) taint-tracking analysis
|
||||
* from `TaintTrackingParameter::Public` and adds a global analysis, mainly
|
||||
* exposed through the `Configuration` class. For some languages, this file
|
||||
* exists in several identical copies, allowing queries to use multiple
|
||||
* `Configuration` classes that depend on each other without introducing
|
||||
* mutual recursion among those configurations.
|
||||
*/
|
||||
|
||||
import TaintTrackingParameter::Public
|
||||
private import TaintTrackingParameter::Private
|
||||
|
||||
/**
|
||||
* A configuration of interprocedural taint tracking analysis. This defines
|
||||
* sources, sinks, and any other configurable aspect of the analysis. Each
|
||||
* use of the taint tracking library must define its own unique extension of
|
||||
* this abstract class.
|
||||
*
|
||||
* A taint-tracking configuration is a special data flow configuration
|
||||
* (`DataFlow::Configuration`) that allows for flow through nodes that do not
|
||||
* necessarily preserve values but are still relevant from a taint tracking
|
||||
* perspective. (For example, string concatenation, where one of the operands
|
||||
* is tainted.)
|
||||
*
|
||||
* To create a configuration, extend this class with a subclass whose
|
||||
* characteristic predicate is a unique singleton string. For example, write
|
||||
*
|
||||
* ```ql
|
||||
* class MyAnalysisConfiguration extends TaintTracking::Configuration {
|
||||
* MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" }
|
||||
* // Override `isSource` and `isSink`.
|
||||
* // Optionally override `isSanitizer`.
|
||||
* // Optionally override `isSanitizerIn`.
|
||||
* // Optionally override `isSanitizerOut`.
|
||||
* // Optionally override `isSanitizerGuard`.
|
||||
* // Optionally override `isAdditionalTaintStep`.
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Then, to query whether there is flow between some `source` and `sink`,
|
||||
* write
|
||||
*
|
||||
* ```ql
|
||||
* exists(MyAnalysisConfiguration cfg | cfg.hasFlow(source, sink))
|
||||
* ```
|
||||
*
|
||||
* Multiple configurations can coexist, but it is unsupported to depend on
|
||||
* another `TaintTracking::Configuration` or a `DataFlow::Configuration` in the
|
||||
* overridden predicates that define sources, sinks, or additional steps.
|
||||
* Instead, the dependency should go to a `TaintTracking2::Configuration` or a
|
||||
* `DataFlow2::Configuration`, `DataFlow3::Configuration`, etc.
|
||||
*/
|
||||
abstract class Configuration extends DataFlow::Configuration {
|
||||
bindingset[this]
|
||||
Configuration() { any() }
|
||||
|
||||
/**
|
||||
* Holds if `source` is a relevant taint source.
|
||||
*
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate isSource(DataFlow::Node source) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `source` is a relevant taint source with the given initial
|
||||
* `state`.
|
||||
*
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate isSource(DataFlow::Node source, DataFlow::FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant taint sink
|
||||
*
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate isSink(DataFlow::Node sink) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant taint sink accepting `state`.
|
||||
*
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate isSink(DataFlow::Node sink, DataFlow::FlowState state) { none() }
|
||||
|
||||
/** Holds if the node `node` is a taint sanitizer. */
|
||||
predicate isSanitizer(DataFlow::Node node) { none() }
|
||||
|
||||
final override predicate isBarrier(DataFlow::Node node) {
|
||||
this.isSanitizer(node) or
|
||||
defaultTaintSanitizer(node)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the node `node` is a taint sanitizer when the flow state is
|
||||
* `state`.
|
||||
*/
|
||||
predicate isSanitizer(DataFlow::Node node, DataFlow::FlowState state) { none() }
|
||||
|
||||
final override predicate isBarrier(DataFlow::Node node, DataFlow::FlowState state) {
|
||||
this.isSanitizer(node, state)
|
||||
}
|
||||
|
||||
/** Holds if taint propagation into `node` is prohibited. */
|
||||
predicate isSanitizerIn(DataFlow::Node node) { none() }
|
||||
|
||||
final override predicate isBarrierIn(DataFlow::Node node) { this.isSanitizerIn(node) }
|
||||
|
||||
/** Holds if taint propagation out of `node` is prohibited. */
|
||||
predicate isSanitizerOut(DataFlow::Node node) { none() }
|
||||
|
||||
final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if taint propagation through nodes guarded by `guard` is prohibited.
|
||||
*/
|
||||
deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
|
||||
|
||||
deprecated final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
|
||||
this.isSanitizerGuard(guard)
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if taint propagation through nodes guarded by `guard` is prohibited
|
||||
* when the flow state is `state`.
|
||||
*/
|
||||
deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) {
|
||||
none()
|
||||
}
|
||||
|
||||
deprecated final override predicate isBarrierGuard(
|
||||
DataFlow::BarrierGuard guard, DataFlow::FlowState state
|
||||
) {
|
||||
this.isSanitizerGuard(guard, state)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint may propagate from `node1` to `node2` in addition to the normal data-flow and taint steps.
|
||||
*/
|
||||
predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { none() }
|
||||
|
||||
final override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
this.isAdditionalTaintStep(node1, node2) or
|
||||
defaultAdditionalTaintStep(node1, node2)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint may propagate from `node1` to `node2` in addition to the normal data-flow and taint steps.
|
||||
* This step is only applicable in `state1` and updates the flow state to `state2`.
|
||||
*/
|
||||
predicate isAdditionalTaintStep(
|
||||
DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
|
||||
DataFlow::FlowState state2
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
final override predicate isAdditionalFlowStep(
|
||||
DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
|
||||
DataFlow::FlowState state2
|
||||
) {
|
||||
this.isAdditionalTaintStep(node1, state1, node2, state2)
|
||||
}
|
||||
|
||||
override predicate allowImplicitRead(DataFlow::Node node, DataFlow::ContentSet c) {
|
||||
(
|
||||
this.isSink(node) or
|
||||
this.isSink(node, _) or
|
||||
this.isAdditionalTaintStep(node, _) or
|
||||
this.isAdditionalTaintStep(node, _, _, _)
|
||||
) and
|
||||
defaultImplicitTaintRead(node, c)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint may flow from `source` to `sink` for this configuration.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate hasFlow(DataFlow::Node source, DataFlow::Node sink) {
|
||||
super.hasFlow(source, sink)
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
import experimental.semmle.code.cpp.ir.dataflow.internal.TaintTrackingUtil as Public
|
||||
|
||||
module Private {
|
||||
import experimental.semmle.code.cpp.ir.dataflow.DataFlow3::DataFlow3 as DataFlow
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.dataflow.TaintTracking
|
||||
import semmle.code.cpp.ir.dataflow.TaintTracking
|
||||
import semmle.code.cpp.security.PrivateData
|
||||
import semmle.code.cpp.security.FileWrite
|
||||
import semmle.code.cpp.security.BufferWrite
|
||||
@@ -36,7 +36,7 @@ module PrivateCleartextWrite {
|
||||
}
|
||||
}
|
||||
|
||||
class WriteConfig extends TaintTracking::Configuration {
|
||||
deprecated class WriteConfig extends TaintTracking::Configuration {
|
||||
WriteConfig() { this = "Write configuration" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
@@ -46,6 +46,16 @@ module PrivateCleartextWrite {
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
}
|
||||
|
||||
private module WriteConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
}
|
||||
|
||||
module WriteFlow = TaintTracking::Make<WriteConfig>;
|
||||
|
||||
class PrivateDataSource extends Source {
|
||||
PrivateDataSource() { this.getExpr() instanceof PrivateDataExpr }
|
||||
}
|
||||
|
||||
@@ -936,6 +936,15 @@ module RangeStage<DeltaSig D, BoundSig<D> Bounds, LangSig<D> LangParam, UtilSig<
|
||||
bounded(cast.getOperand(), b, delta, upper, fromBackEdge, origdelta, reason)
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes a normal form of `x` where -0.0 has changed to +0.0. This can be
|
||||
* needed on the lesser side of a floating-point comparison or on both sides of
|
||||
* a floating point equality because QL does not follow IEEE in floating-point
|
||||
* comparisons but instead defines -0.0 to be less than and distinct from 0.0.
|
||||
*/
|
||||
bindingset[x]
|
||||
private float normalizeFloatUp(float x) { result = x + 0.0 }
|
||||
|
||||
/**
|
||||
* Holds if `b + delta` is a valid bound for `e`.
|
||||
* - `upper = true` : `e <= b + delta`
|
||||
@@ -1020,6 +1029,15 @@ module RangeStage<DeltaSig D, BoundSig<D> Bounds, LangSig<D> LangParam, UtilSig<
|
||||
or
|
||||
upper = false and delta = D::fromFloat(D::toFloat(d1).minimum(D::toFloat(d2)))
|
||||
)
|
||||
or
|
||||
exists(SemExpr mid, D::Delta d, float f |
|
||||
e.(SemNegateExpr).getOperand() = mid and
|
||||
b instanceof SemZeroBound and
|
||||
bounded(mid, b, d, upper.booleanNot(), fromBackEdge, origdelta, reason) and
|
||||
f = normalizeFloatUp(-D::toFloat(d)) and
|
||||
delta = D::fromFloat(f) and
|
||||
if semPositive(e) then f >= 0 else any()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
/**
|
||||
* Wrapper for the semantic range analysis library that mimics the
|
||||
* interface of the simple range analysis library.
|
||||
*/
|
||||
|
||||
private import cpp
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import experimental.semmle.code.cpp.semantic.SemanticBound
|
||||
private import experimental.semmle.code.cpp.semantic.SemanticExprSpecific
|
||||
private import RangeAnalysis
|
||||
|
||||
/**
|
||||
* Gets the lower bound of the expression.
|
||||
*
|
||||
* Note: expressions in C/C++ are often implicitly or explicitly cast to a
|
||||
* different result type. Such casts can cause the value of the expression
|
||||
* to overflow or to be truncated. This predicate computes the lower bound
|
||||
* of the expression without including the effect of the casts. To compute
|
||||
* the lower bound of the expression after all the casts have been applied,
|
||||
* call `lowerBound` like this:
|
||||
*
|
||||
* `lowerBound(expr.getFullyConverted())`
|
||||
*/
|
||||
float lowerBound(Expr expr) {
|
||||
exists(Instruction i, SemBound b | i.getAst() = expr and b instanceof SemZeroBound |
|
||||
semBounded(getSemanticExpr(i), b, result, false, _)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the upper bound of the expression.
|
||||
*
|
||||
* Note: expressions in C/C++ are often implicitly or explicitly cast to a
|
||||
* different result type. Such casts can cause the value of the expression
|
||||
* to overflow or to be truncated. This predicate computes the upper bound
|
||||
* of the expression without including the effect of the casts. To compute
|
||||
* the upper bound of the expression after all the casts have been applied,
|
||||
* call `upperBound` like this:
|
||||
*
|
||||
* `upperBound(expr.getFullyConverted())`
|
||||
*/
|
||||
float upperBound(Expr expr) {
|
||||
exists(Instruction i, SemBound b | i.getAst() = expr and b instanceof SemZeroBound |
|
||||
semBounded(getSemanticExpr(i), b, result, true, _)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the upper bound of `expr` may have been widened. This means the
|
||||
* upper bound is in practice likely to be overly wide.
|
||||
*/
|
||||
predicate upperBoundMayBeWidened(Expr e) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `expr` has a provably empty range. For example:
|
||||
*
|
||||
* 10 < expr and expr < 5
|
||||
*
|
||||
* The range of an expression can only be empty if it can never be
|
||||
* executed. For example:
|
||||
*
|
||||
* ```cpp
|
||||
* if (10 < x) {
|
||||
* if (x < 5) {
|
||||
* // Unreachable code
|
||||
* return x; // x has an empty range: 10 < x && x < 5
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
predicate exprWithEmptyRange(Expr expr) { lowerBound(expr) > upperBound(expr) }
|
||||
|
||||
/** Holds if the definition might overflow negatively. */
|
||||
predicate defMightOverflowNegatively(RangeSsaDefinition def, StackVariable v) { none() }
|
||||
|
||||
/** Holds if the definition might overflow positively. */
|
||||
predicate defMightOverflowPositively(RangeSsaDefinition def, StackVariable v) { none() }
|
||||
|
||||
/**
|
||||
* Holds if the definition might overflow (either positively or
|
||||
* negatively).
|
||||
*/
|
||||
predicate defMightOverflow(RangeSsaDefinition def, StackVariable v) {
|
||||
defMightOverflowNegatively(def, v) or
|
||||
defMightOverflowPositively(def, v)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the expression might overflow negatively. This predicate
|
||||
* does not consider the possibility that the expression might overflow
|
||||
* due to a conversion.
|
||||
*/
|
||||
predicate exprMightOverflowNegatively(Expr expr) { none() }
|
||||
|
||||
/**
|
||||
* Holds if the expression might overflow negatively. Conversions
|
||||
* are also taken into account. For example the expression
|
||||
* `(int16)(x+y)` might overflow due to the `(int16)` cast, rather than
|
||||
* due to the addition.
|
||||
*/
|
||||
predicate convertedExprMightOverflowNegatively(Expr expr) {
|
||||
exprMightOverflowNegatively(expr) or
|
||||
convertedExprMightOverflowNegatively(expr.getConversion())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the expression might overflow positively. This predicate
|
||||
* does not consider the possibility that the expression might overflow
|
||||
* due to a conversion.
|
||||
*/
|
||||
predicate exprMightOverflowPositively(Expr expr) { none() }
|
||||
|
||||
/**
|
||||
* Holds if the expression might overflow positively. Conversions
|
||||
* are also taken into account. For example the expression
|
||||
* `(int16)(x+y)` might overflow due to the `(int16)` cast, rather than
|
||||
* due to the addition.
|
||||
*/
|
||||
predicate convertedExprMightOverflowPositively(Expr expr) {
|
||||
exprMightOverflowPositively(expr) or
|
||||
convertedExprMightOverflowPositively(expr.getConversion())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the expression might overflow (either positively or
|
||||
* negatively). The possibility that the expression might overflow
|
||||
* due to an implicit or explicit cast is also considered.
|
||||
*/
|
||||
predicate convertedExprMightOverflow(Expr expr) {
|
||||
convertedExprMightOverflowNegatively(expr) or
|
||||
convertedExprMightOverflowPositively(expr)
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/cpp-all
|
||||
version: 0.5.4-dev
|
||||
version: 0.5.5-dev
|
||||
groups: cpp
|
||||
dbscheme: semmlecode.cpp.dbscheme
|
||||
extractor: cpp
|
||||
|
||||
@@ -227,18 +227,6 @@ class Class extends UserType {
|
||||
result = this.accessOfBaseMember(member.getDeclaringType(), member.getASpecifier())
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: name changed to `hasImplicitCopyConstructor` to reflect that
|
||||
* `= default` members are no longer included.
|
||||
*/
|
||||
deprecated predicate hasGeneratedCopyConstructor() { this.hasImplicitCopyConstructor() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: name changed to `hasImplicitCopyAssignmentOperator` to
|
||||
* reflect that `= default` members are no longer included.
|
||||
*/
|
||||
deprecated predicate hasGeneratedCopyAssignmentOperator() { this.hasImplicitCopyConstructor() }
|
||||
|
||||
/**
|
||||
* Holds if this class, struct or union has an implicitly-declared copy
|
||||
* constructor that is not _deleted_. This predicate is more accurate than
|
||||
|
||||
@@ -619,11 +619,10 @@ private class DirectAccessHolder extends Element {
|
||||
/**
|
||||
* Like `couldAccessMember` but only contains derivations in which either
|
||||
* (5.2), (5.3) or (5.4) must be invoked. In other words, the `this`
|
||||
* parameter is not ignored. This restriction makes it feasible to fully
|
||||
* enumerate this predicate even on large code bases. We check for 11.4 as
|
||||
* part of (5.3), since this further limits the number of tuples produced by
|
||||
* this predicate.
|
||||
* parameter is not ignored. We check for 11.4 as part of (5.3), since
|
||||
* this further limits the number of tuples produced by this predicate.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate thisCouldAccessMember(Class memberClass, AccessSpecifier memberAccess, Class derived) {
|
||||
// Only (5.4) is recursive, and chains of invocations of (5.4) can always
|
||||
// be collapsed to one invocation by the transitivity of 11.2/4.
|
||||
@@ -665,7 +664,9 @@ private class DirectAccessHolder extends Element {
|
||||
// bypasses `p`. Then that path must be public, or we are in case 2.
|
||||
exists(AccessSpecifier public | public.hasName("public") |
|
||||
exists(Class between, Class p |
|
||||
between.accessOfBaseMember(memberClass, memberAccess).hasName("protected") and
|
||||
between
|
||||
.accessOfBaseMember(pragma[only_bind_into](memberClass), memberAccess)
|
||||
.hasName("protected") and
|
||||
this.isFriendOfOrEqualTo(p) and
|
||||
(
|
||||
// This is case 1 from above. If `p` derives privately from `between`
|
||||
|
||||
@@ -159,7 +159,8 @@ class NameQualifyingElement extends Element, @namequalifyingelement {
|
||||
* A special name-qualifying element. For example: `__super`.
|
||||
*/
|
||||
library class SpecialNameQualifyingElement extends NameQualifyingElement,
|
||||
@specialnamequalifyingelement {
|
||||
@specialnamequalifyingelement
|
||||
{
|
||||
/** Gets the name of this special qualifying element. */
|
||||
override string getName() { specialnamequalifyingelements(underlyingElement(this), result) }
|
||||
|
||||
|
||||
@@ -108,20 +108,6 @@ class XmlFile extends XmlParent, File {
|
||||
/** Gets the name of this XML file. */
|
||||
override string getName() { result = File.super.getAbsolutePath() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `getAbsolutePath()` instead.
|
||||
*
|
||||
* Gets the path of this XML file.
|
||||
*/
|
||||
deprecated string getPath() { result = this.getAbsolutePath() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `getParentContainer().getAbsolutePath()` instead.
|
||||
*
|
||||
* Gets the path of the folder that contains this XML file.
|
||||
*/
|
||||
deprecated string getFolder() { result = this.getParentContainer().getAbsolutePath() }
|
||||
|
||||
/** Gets the encoding of this XML file. */
|
||||
string getEncoding() { xmlEncoding(this, result) }
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.dataflow.DataFlow
|
||||
private import semmle.code.cpp.ir.dataflow.DataFlow
|
||||
|
||||
/**
|
||||
* Holds if `v` is a member variable of `c` that looks like it might be variable sized
|
||||
@@ -25,10 +25,12 @@ predicate memberMayBeVarSize(Class c, MemberVariable v) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the size in bytes of the buffer pointed to by an expression (if this can be determined).
|
||||
* Holds if `bufferExpr` is an allocation-like expression.
|
||||
*
|
||||
* This includes both actual allocations, as well as various operations that return a pointer to
|
||||
* stack-allocated objects.
|
||||
*/
|
||||
language[monotonicAggregates]
|
||||
int getBufferSize(Expr bufferExpr, Element why) {
|
||||
private int isSource(Expr bufferExpr, Element why) {
|
||||
exists(Variable bufferVar | bufferVar = bufferExpr.(VariableAccess).getTarget() |
|
||||
// buffer is a fixed size array
|
||||
result = bufferVar.getUnspecifiedType().(ArrayType).getSize() and
|
||||
@@ -46,42 +48,12 @@ int getBufferSize(Expr bufferExpr, Element why) {
|
||||
) and
|
||||
result = why.(Expr).getType().(ArrayType).getSize() and
|
||||
not exists(bufferVar.getUnspecifiedType().(ArrayType).getSize())
|
||||
or
|
||||
exists(Class parentClass, VariableAccess parentPtr, int bufferSize |
|
||||
// buffer is the parentPtr->bufferVar of a 'variable size struct'
|
||||
memberMayBeVarSize(parentClass, bufferVar) and
|
||||
why = bufferVar and
|
||||
parentPtr = bufferExpr.(VariableAccess).getQualifier() and
|
||||
parentPtr.getTarget().getUnspecifiedType().(PointerType).getBaseType() = parentClass and
|
||||
(
|
||||
if exists(bufferVar.getType().getSize())
|
||||
then bufferSize = bufferVar.getType().getSize()
|
||||
else bufferSize = 0
|
||||
) and
|
||||
result = getBufferSize(parentPtr, _) + bufferSize - parentClass.getSize()
|
||||
)
|
||||
)
|
||||
or
|
||||
// buffer is a fixed size dynamic allocation
|
||||
result = bufferExpr.(AllocationExpr).getSizeBytes() and
|
||||
why = bufferExpr
|
||||
or
|
||||
exists(DataFlow::ExprNode bufferExprNode |
|
||||
// dataflow (all sources must be the same size)
|
||||
bufferExprNode = DataFlow::exprNode(bufferExpr) and
|
||||
result =
|
||||
unique(Expr def |
|
||||
DataFlow::localFlowStep(DataFlow::exprNode(def), bufferExprNode)
|
||||
|
|
||||
getBufferSize(def, _)
|
||||
) and
|
||||
// find reason
|
||||
exists(Expr def | DataFlow::localFlowStep(DataFlow::exprNode(def), bufferExprNode) |
|
||||
why = def or
|
||||
exists(getBufferSize(def, why))
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(Type bufferType |
|
||||
// buffer is the address of a variable
|
||||
why = bufferExpr.(AddressOfExpr).getAddressable() and
|
||||
@@ -100,3 +72,30 @@ int getBufferSize(Expr bufferExpr, Element why) {
|
||||
result = bufferType.getSize()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the size in bytes of the buffer pointed to by an expression (if this can be determined).
|
||||
*/
|
||||
language[monotonicAggregates]
|
||||
int getBufferSize(Expr bufferExpr, Element why) {
|
||||
result = isSource(bufferExpr, why)
|
||||
or
|
||||
exists(Class parentClass, VariableAccess parentPtr, int bufferSize, Variable bufferVar |
|
||||
bufferVar = bufferExpr.(VariableAccess).getTarget() and
|
||||
// buffer is the parentPtr->bufferVar of a 'variable size struct'
|
||||
memberMayBeVarSize(parentClass, bufferVar) and
|
||||
why = bufferVar and
|
||||
parentPtr = bufferExpr.(VariableAccess).getQualifier() and
|
||||
parentPtr.getTarget().getUnspecifiedType().(PointerType).getBaseType() = parentClass and
|
||||
result = getBufferSize(parentPtr, _) + bufferSize - parentClass.getSize()
|
||||
|
|
||||
if exists(bufferVar.getType().getSize())
|
||||
then bufferSize = bufferVar.getType().getSize()
|
||||
else bufferSize = 0
|
||||
)
|
||||
or
|
||||
// dataflow (all sources must be the same size)
|
||||
result = unique(Expr def | DataFlow::localExprFlowStep(def, bufferExpr) | getBufferSize(def, _)) and
|
||||
// find reason
|
||||
exists(Expr def | DataFlow::localExprFlowStep(def, bufferExpr) | exists(getBufferSize(def, why)))
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import cpp
|
||||
private import semmle.code.cpp.models.interfaces.ArrayFunction
|
||||
private import semmle.code.cpp.models.implementations.Strcat
|
||||
import semmle.code.cpp.dataflow.DataFlow
|
||||
private import semmle.code.cpp.ir.dataflow.DataFlow
|
||||
|
||||
/**
|
||||
* Holds if the expression `e` assigns something including `va` to a
|
||||
|
||||
@@ -59,26 +59,6 @@ abstract class MutexType extends Type {
|
||||
* Gets a call that unlocks any mutex of this type.
|
||||
*/
|
||||
FunctionCall getUnlockAccess() { this.unlockAccess(result, _) }
|
||||
|
||||
/**
|
||||
* DEPRECATED: use mustlockAccess(fc, arg) instead.
|
||||
*/
|
||||
deprecated Function getMustlockFunction() { result = this.getMustlockAccess().getTarget() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: use trylockAccess(fc, arg) instead.
|
||||
*/
|
||||
deprecated Function getTrylockFunction() { result = this.getTrylockAccess().getTarget() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: use lockAccess(fc, arg) instead.
|
||||
*/
|
||||
deprecated Function getLockFunction() { result = this.getLockAccess().getTarget() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: use unlockAccess(fc, arg) instead.
|
||||
*/
|
||||
deprecated Function getUnlockFunction() { result = this.getUnlockAccess().getTarget() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -75,13 +75,6 @@ class SubBasicBlock extends ControlFlowNodeBase {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: use `getRankInBasicBlock` instead. Note that this predicate
|
||||
* returns a 0-based position, while `getRankInBasicBlock` returns a 1-based
|
||||
* position.
|
||||
*/
|
||||
deprecated int getPosInBasicBlock(BasicBlock bb) { result = this.getRankInBasicBlock(bb) - 1 }
|
||||
|
||||
pragma[noinline]
|
||||
private int getIndexInBasicBlock(BasicBlock bb) { this = bb.getNode(result) }
|
||||
|
||||
|
||||
@@ -19,6 +19,10 @@
|
||||
|
||||
import cpp
|
||||
|
||||
/**
|
||||
* Provides classes for performing local (intra-procedural) and
|
||||
* global (inter-procedural) data flow analyses.
|
||||
*/
|
||||
module DataFlow {
|
||||
import semmle.code.cpp.dataflow.internal.DataFlow
|
||||
import semmle.code.cpp.dataflow.internal.DataFlowImpl1
|
||||
|
||||
@@ -11,6 +11,10 @@
|
||||
|
||||
import cpp
|
||||
|
||||
/**
|
||||
* Provides classes for performing local (intra-procedural) and
|
||||
* global (inter-procedural) data flow analyses.
|
||||
*/
|
||||
module DataFlow2 {
|
||||
import semmle.code.cpp.dataflow.internal.DataFlowImpl2
|
||||
}
|
||||
|
||||
@@ -11,6 +11,10 @@
|
||||
|
||||
import cpp
|
||||
|
||||
/**
|
||||
* Provides classes for performing local (intra-procedural) and
|
||||
* global (inter-procedural) data flow analyses.
|
||||
*/
|
||||
module DataFlow3 {
|
||||
import semmle.code.cpp.dataflow.internal.DataFlowImpl3
|
||||
}
|
||||
|
||||
@@ -11,6 +11,10 @@
|
||||
|
||||
import cpp
|
||||
|
||||
/**
|
||||
* Provides classes for performing local (intra-procedural) and
|
||||
* global (inter-procedural) data flow analyses.
|
||||
*/
|
||||
module DataFlow4 {
|
||||
import semmle.code.cpp.dataflow.internal.DataFlowImpl4
|
||||
}
|
||||
|
||||
@@ -95,6 +95,11 @@ predicate stackPointerFlowsToUse(Expr use, Type useType, Expr source, boolean is
|
||||
cached
|
||||
private PointerType getExprPtrType(Expr use) { result = use.getUnspecifiedType() }
|
||||
|
||||
/**
|
||||
* Holds if `use` has type `useType` and `source` is an access to a stack variable
|
||||
* that flows to `use`. `isLocal` is `true` if `use` is accessed via a parameter, and
|
||||
* `false` otherwise.
|
||||
*/
|
||||
predicate stackReferenceFlowsToUse(Expr use, Type useType, Expr source, boolean isLocal) {
|
||||
// Stack variables
|
||||
exists(StackVariable var |
|
||||
|
||||
@@ -18,6 +18,10 @@
|
||||
import semmle.code.cpp.dataflow.DataFlow
|
||||
import semmle.code.cpp.dataflow.DataFlow2
|
||||
|
||||
/**
|
||||
* Provides classes for performing local (intra-procedural) and
|
||||
* global (inter-procedural) taint-tracking analyses.
|
||||
*/
|
||||
module TaintTracking {
|
||||
import semmle.code.cpp.dataflow.internal.tainttracking1.TaintTracking
|
||||
import semmle.code.cpp.dataflow.internal.tainttracking1.TaintTrackingImpl
|
||||
|
||||
@@ -10,6 +10,11 @@
|
||||
*
|
||||
* See `semmle.code.cpp.dataflow.TaintTracking` for the full documentation.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Provides classes for performing local (intra-procedural) and
|
||||
* global (inter-procedural) taint-tracking analyses.
|
||||
*/
|
||||
module TaintTracking2 {
|
||||
import semmle.code.cpp.dataflow.internal.tainttracking2.TaintTrackingImpl
|
||||
}
|
||||
|
||||
@@ -243,3 +243,111 @@ module MakeWithState<StateConfigSig Config> implements DataFlowSig {
|
||||
|
||||
import Impl<C>
|
||||
}
|
||||
|
||||
signature class PathNodeSig {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString();
|
||||
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
);
|
||||
|
||||
/** Gets the underlying `Node`. */
|
||||
Node getNode();
|
||||
}
|
||||
|
||||
signature module PathGraphSig<PathNodeSig PathNode> {
|
||||
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
|
||||
predicate edges(PathNode a, PathNode b);
|
||||
|
||||
/** Holds if `n` is a node in the graph of data flow path explanations. */
|
||||
predicate nodes(PathNode n, string key, string val);
|
||||
|
||||
/**
|
||||
* Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through
|
||||
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and
|
||||
* `ret -> out` is summarized as the edge `arg -> out`.
|
||||
*/
|
||||
predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a `PathGraph` from two `PathGraph`s by disjoint union.
|
||||
*/
|
||||
module MergePathGraph<
|
||||
PathNodeSig PathNode1, PathNodeSig PathNode2, PathGraphSig<PathNode1> Graph1,
|
||||
PathGraphSig<PathNode2> Graph2>
|
||||
{
|
||||
private newtype TPathNode =
|
||||
TPathNode1(PathNode1 p) or
|
||||
TPathNode2(PathNode2 p)
|
||||
|
||||
/** A node in a graph of path explanations that is formed by disjoint union of the two given graphs. */
|
||||
class PathNode extends TPathNode {
|
||||
/** Gets this as a projection on the first given `PathGraph`. */
|
||||
PathNode1 asPathNode1() { this = TPathNode1(result) }
|
||||
|
||||
/** Gets this as a projection on the second given `PathGraph`. */
|
||||
PathNode2 asPathNode2() { this = TPathNode2(result) }
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() {
|
||||
result = this.asPathNode1().toString() or
|
||||
result = this.asPathNode2().toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
this.asPathNode1().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) or
|
||||
this.asPathNode2().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
|
||||
/** Gets the underlying `Node`. */
|
||||
Node getNode() {
|
||||
result = this.asPathNode1().getNode() or
|
||||
result = this.asPathNode2().getNode()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the query predicates needed to include a graph in a path-problem query.
|
||||
*/
|
||||
module PathGraph implements PathGraphSig<PathNode> {
|
||||
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
|
||||
query predicate edges(PathNode a, PathNode b) {
|
||||
Graph1::edges(a.asPathNode1(), b.asPathNode1()) or
|
||||
Graph2::edges(a.asPathNode2(), b.asPathNode2())
|
||||
}
|
||||
|
||||
/** Holds if `n` is a node in the graph of data flow path explanations. */
|
||||
query predicate nodes(PathNode n, string key, string val) {
|
||||
Graph1::nodes(n.asPathNode1(), key, val) or
|
||||
Graph2::nodes(n.asPathNode2(), key, val)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through
|
||||
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and
|
||||
* `ret -> out` is summarized as the edge `arg -> out`.
|
||||
*/
|
||||
query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) {
|
||||
Graph1::subpaths(arg.asPathNode1(), par.asPathNode1(), ret.asPathNode1(), out.asPathNode1()) or
|
||||
Graph2::subpaths(arg.asPathNode2(), par.asPathNode2(), ret.asPathNode2(), out.asPathNode2())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
private import cpp
|
||||
private import semmle.code.cpp.dataflow.internal.DataFlowPrivate
|
||||
private import semmle.code.cpp.dataflow.internal.DataFlowUtil
|
||||
private import DataFlowPrivate
|
||||
private import DataFlowUtil
|
||||
|
||||
/**
|
||||
* Gets a function that might be called by `call`.
|
||||
|
||||
@@ -418,6 +418,10 @@ module Impl<FullStateConfigSig Config> {
|
||||
)
|
||||
}
|
||||
|
||||
private predicate sourceCallCtx(CallContext cc) {
|
||||
if hasSourceCallCtx() then cc instanceof CallContextSomeCall else cc instanceof CallContextAny
|
||||
}
|
||||
|
||||
private predicate hasSinkCallCtx() {
|
||||
exists(FlowFeature feature | feature = Config::getAFeature() |
|
||||
feature instanceof FeatureHasSinkCallContext or
|
||||
@@ -456,6 +460,7 @@ module Impl<FullStateConfigSig Config> {
|
||||
* The Boolean `cc` records whether the node is reached through an
|
||||
* argument in a call.
|
||||
*/
|
||||
pragma[assume_small_delta]
|
||||
private predicate fwdFlow(NodeEx node, Cc cc) {
|
||||
sourceNode(node, _) and
|
||||
if hasSourceCallCtx() then cc = true else cc = false
|
||||
@@ -2803,11 +2808,7 @@ module Impl<FullStateConfigSig Config> {
|
||||
// A PathNode is introduced by a source ...
|
||||
Stage5::revFlow(node, state) and
|
||||
sourceNode(node, state) and
|
||||
(
|
||||
if hasSourceCallCtx()
|
||||
then cc instanceof CallContextSomeCall
|
||||
else cc instanceof CallContextAny
|
||||
) and
|
||||
sourceCallCtx(cc) and
|
||||
sc instanceof SummaryCtxNone and
|
||||
ap = TAccessPathNil(node.getDataFlowType())
|
||||
or
|
||||
@@ -3156,7 +3157,7 @@ module Impl<FullStateConfigSig Config> {
|
||||
/**
|
||||
* Provides the query predicates needed to include a graph in a path-problem query.
|
||||
*/
|
||||
module PathGraph {
|
||||
module PathGraph implements PathGraphSig<PathNode> {
|
||||
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
|
||||
query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b }
|
||||
|
||||
@@ -3213,11 +3214,7 @@ module Impl<FullStateConfigSig Config> {
|
||||
|
||||
override predicate isSource() {
|
||||
sourceNode(node, state) and
|
||||
(
|
||||
if hasSourceCallCtx()
|
||||
then cc instanceof CallContextSomeCall
|
||||
else cc instanceof CallContextAny
|
||||
) and
|
||||
sourceCallCtx(cc) and
|
||||
sc instanceof SummaryCtxNone and
|
||||
ap = TAccessPathNil(node.getDataFlowType())
|
||||
}
|
||||
|
||||
@@ -182,6 +182,7 @@ private module LambdaFlow {
|
||||
boolean toJump, DataFlowCallOption lastCall
|
||||
) {
|
||||
revLambdaFlow0(lambdaCall, kind, node, t, toReturn, toJump, lastCall) and
|
||||
not expectsContent(node, _) and
|
||||
if castNode(node) or node instanceof ArgNode or node instanceof ReturnNode
|
||||
then compatibleTypes(t, getNodeDataFlowType(node))
|
||||
else any()
|
||||
|
||||
@@ -18,6 +18,9 @@ module Consistency {
|
||||
/** Holds if `n` should be excluded from the consistency test `uniqueEnclosingCallable`. */
|
||||
predicate uniqueEnclosingCallableExclude(Node n) { none() }
|
||||
|
||||
/** Holds if `call` should be excluded from the consistency test `uniqueCallEnclosingCallable`. */
|
||||
predicate uniqueCallEnclosingCallableExclude(DataFlowCall call) { none() }
|
||||
|
||||
/** Holds if `n` should be excluded from the consistency test `uniqueNodeLocation`. */
|
||||
predicate uniqueNodeLocationExclude(Node n) { none() }
|
||||
|
||||
@@ -86,6 +89,15 @@ module Consistency {
|
||||
)
|
||||
}
|
||||
|
||||
query predicate uniqueCallEnclosingCallable(DataFlowCall call, string msg) {
|
||||
exists(int c |
|
||||
c = count(call.getEnclosingCallable()) and
|
||||
c != 1 and
|
||||
not any(ConsistencyConfiguration conf).uniqueCallEnclosingCallableExclude(call) and
|
||||
msg = "Call should have one enclosing callable but has " + c + "."
|
||||
)
|
||||
}
|
||||
|
||||
query predicate uniqueType(Node n, string msg) {
|
||||
exists(int c |
|
||||
n instanceof RelevantNode and
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
*/
|
||||
|
||||
private import cpp
|
||||
private import semmle.code.cpp.dataflow.internal.FlowVar
|
||||
private import FlowVar
|
||||
private import semmle.code.cpp.models.interfaces.DataFlow
|
||||
private import semmle.code.cpp.controlflow.Guards
|
||||
private import semmle.code.cpp.dataflow.internal.AddressFlow
|
||||
private import AddressFlow
|
||||
|
||||
cached
|
||||
private newtype TNode =
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
|
||||
import cpp
|
||||
private import semmle.code.cpp.controlflow.SSA
|
||||
private import semmle.code.cpp.dataflow.internal.SubBasicBlocks
|
||||
private import semmle.code.cpp.dataflow.internal.AddressFlow
|
||||
private import SubBasicBlocks
|
||||
private import AddressFlow
|
||||
private import semmle.code.cpp.models.implementations.Iterator
|
||||
private import semmle.code.cpp.models.interfaces.PointerWrapper
|
||||
|
||||
|
||||
@@ -75,13 +75,6 @@ class SubBasicBlock extends ControlFlowNodeBase {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: use `getRankInBasicBlock` instead. Note that this predicate
|
||||
* returns a 0-based position, while `getRankInBasicBlock` returns a 1-based
|
||||
* position.
|
||||
*/
|
||||
deprecated int getPosInBasicBlock(BasicBlock bb) { result = this.getRankInBasicBlock(bb) - 1 }
|
||||
|
||||
pragma[noinline]
|
||||
private int getIndexInBasicBlock(BasicBlock bb) { this = bb.getNode(result) }
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ private import semmle.code.cpp.models.interfaces.Iterator
|
||||
private import semmle.code.cpp.models.interfaces.PointerWrapper
|
||||
|
||||
private module DataFlow {
|
||||
import semmle.code.cpp.dataflow.internal.DataFlowUtil
|
||||
import DataFlowUtil
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,7 +7,8 @@ import TaintTrackingParameter::Public
|
||||
private import TaintTrackingParameter::Private
|
||||
|
||||
private module AddTaintDefaults<DataFlowInternal::FullStateConfigSig Config> implements
|
||||
DataFlowInternal::FullStateConfigSig {
|
||||
DataFlowInternal::FullStateConfigSig
|
||||
{
|
||||
import Config
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) {
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
* results than the AST-based library in most scenarios.
|
||||
*
|
||||
* Unless configured otherwise, _flow_ means that the exact value of
|
||||
* the source may reach the sink. We do not track flow across pointer
|
||||
* dereferences or array indexing.
|
||||
* the source may reach the sink. To track flow where the exact value
|
||||
* may not be preserved, import `semmle.code.cpp.dataflow.new.TaintTracking`.
|
||||
*
|
||||
* To use global (interprocedural) data flow, extend the class
|
||||
* `DataFlow::Configuration` as documented on that class. To use local
|
||||
@@ -21,7 +21,11 @@
|
||||
|
||||
import cpp
|
||||
|
||||
/**
|
||||
* Provides classes for performing local (intra-procedural) and
|
||||
* global (inter-procedural) data flow analyses.
|
||||
*/
|
||||
module DataFlow {
|
||||
import experimental.semmle.code.cpp.ir.dataflow.internal.DataFlow
|
||||
import experimental.semmle.code.cpp.ir.dataflow.internal.DataFlowImpl1
|
||||
import semmle.code.cpp.ir.dataflow.internal.DataFlow
|
||||
import semmle.code.cpp.ir.dataflow.internal.DataFlowImpl1
|
||||
}
|
||||
@@ -6,11 +6,15 @@
|
||||
* `DataFlow2::Configuration`, a `DataFlow3::Configuration`, or a
|
||||
* `DataFlow4::Configuration`.
|
||||
*
|
||||
* See `semmle.code.cpp.ir.dataflow.DataFlow` for the full documentation.
|
||||
* See `semmle.code.cpp.dataflow.new.DataFlow` for the full documentation.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
/**
|
||||
* Provides classes for performing local (intra-procedural) and
|
||||
* global (inter-procedural) data flow analyses.
|
||||
*/
|
||||
module DataFlow2 {
|
||||
import experimental.semmle.code.cpp.ir.dataflow.internal.DataFlowImpl2
|
||||
import semmle.code.cpp.ir.dataflow.internal.DataFlowImpl2
|
||||
}
|
||||
@@ -6,11 +6,15 @@
|
||||
* `DataFlow2::Configuration`, a `DataFlow3::Configuration`, or a
|
||||
* `DataFlow4::Configuration`.
|
||||
*
|
||||
* See `semmle.code.cpp.ir.dataflow.DataFlow` for the full documentation.
|
||||
* See `semmle.code.cpp.dataflow.new.DataFlow` for the full documentation.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
/**
|
||||
* Provides classes for performing local (intra-procedural) and
|
||||
* global (inter-procedural) data flow analyses.
|
||||
*/
|
||||
module DataFlow3 {
|
||||
import experimental.semmle.code.cpp.ir.dataflow.internal.DataFlowImpl3
|
||||
import semmle.code.cpp.ir.dataflow.internal.DataFlowImpl3
|
||||
}
|
||||
@@ -6,11 +6,15 @@
|
||||
* `DataFlow2::Configuration`, a `DataFlow3::Configuration`, or a
|
||||
* `DataFlow4::Configuration`.
|
||||
*
|
||||
* See `semmle.code.cpp.ir.dataflow.DataFlow` for the full documentation.
|
||||
* See `semmle.code.cpp.dataflow.new.DataFlow` for the full documentation.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
/**
|
||||
* Provides classes for performing local (intra-procedural) and
|
||||
* global (inter-procedural) data flow analyses.
|
||||
*/
|
||||
module DataFlow4 {
|
||||
import experimental.semmle.code.cpp.ir.dataflow.internal.DataFlowImpl4
|
||||
import semmle.code.cpp.ir.dataflow.internal.DataFlowImpl4
|
||||
}
|
||||
@@ -15,10 +15,14 @@
|
||||
* `TaintTracking::localTaintStep` with arguments of type `DataFlow::Node`.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.ir.dataflow.DataFlow
|
||||
import semmle.code.cpp.ir.dataflow.DataFlow2
|
||||
import semmle.code.cpp.dataflow.new.DataFlow
|
||||
import semmle.code.cpp.dataflow.new.DataFlow2
|
||||
|
||||
/**
|
||||
* Provides classes for performing local (intra-procedural) and
|
||||
* global (inter-procedural) taint-tracking analyses.
|
||||
*/
|
||||
module TaintTracking {
|
||||
import experimental.semmle.code.cpp.ir.dataflow.internal.tainttracking1.TaintTracking
|
||||
import experimental.semmle.code.cpp.ir.dataflow.internal.tainttracking1.TaintTrackingImpl
|
||||
import semmle.code.cpp.ir.dataflow.internal.tainttracking1.TaintTracking
|
||||
import semmle.code.cpp.ir.dataflow.internal.tainttracking1.TaintTrackingImpl
|
||||
}
|
||||
@@ -8,8 +8,13 @@
|
||||
* `TaintTracking::Configuration` class extends `DataFlow::Configuration`, and
|
||||
* `TaintTracking2::Configuration` extends `DataFlow2::Configuration`.
|
||||
*
|
||||
* See `semmle.code.cpp.ir.dataflow.TaintTracking` for the full documentation.
|
||||
* See `semmle.code.cpp.dataflow.new.TaintTracking` for the full documentation.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Provides classes for performing local (intra-procedural) and
|
||||
* global (inter-procedural) taint-tracking analyses.
|
||||
*/
|
||||
module TaintTracking2 {
|
||||
import experimental.semmle.code.cpp.ir.dataflow.internal.tainttracking2.TaintTrackingImpl
|
||||
import semmle.code.cpp.ir.dataflow.internal.tainttracking2.TaintTrackingImpl
|
||||
}
|
||||
@@ -8,8 +8,13 @@
|
||||
* `TaintTracking::Configuration` class extends `DataFlow::Configuration`, and
|
||||
* `TaintTracking2::Configuration` extends `DataFlow2::Configuration`.
|
||||
*
|
||||
* See `semmle.code.cpp.ir.dataflow.TaintTracking` for the full documentation.
|
||||
* See `semmle.code.cpp.dataflow.new.TaintTracking` for the full documentation.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Provides classes for performing local (intra-procedural) and
|
||||
* global (inter-procedural) taint-tracking analyses.
|
||||
*/
|
||||
module TaintTracking3 {
|
||||
import experimental.semmle.code.cpp.ir.dataflow.internal.tainttracking3.TaintTrackingImpl
|
||||
import semmle.code.cpp.ir.dataflow.internal.tainttracking3.TaintTrackingImpl
|
||||
}
|
||||
@@ -569,7 +569,8 @@ class BuiltInOperationBuiltInAddressOf extends UnaryOperation, BuiltInOperation,
|
||||
* ```
|
||||
*/
|
||||
class BuiltInOperationIsTriviallyConstructible extends BuiltInOperation,
|
||||
@istriviallyconstructibleexpr {
|
||||
@istriviallyconstructibleexpr
|
||||
{
|
||||
override string toString() { result = "__is_trivially_constructible" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "BuiltInOperationIsTriviallyConstructible" }
|
||||
@@ -619,7 +620,8 @@ class BuiltInOperationIsNothrowDestructible extends BuiltInOperation, @isnothrow
|
||||
* bool v = __is_trivially_destructible(MyType);
|
||||
* ```
|
||||
*/
|
||||
class BuiltInOperationIsTriviallyDestructible extends BuiltInOperation, @istriviallydestructibleexpr {
|
||||
class BuiltInOperationIsTriviallyDestructible extends BuiltInOperation, @istriviallydestructibleexpr
|
||||
{
|
||||
override string toString() { result = "__is_trivially_destructible" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "BuiltInOperationIsTriviallyDestructible" }
|
||||
@@ -738,7 +740,8 @@ class BuiltInOperationIsLiteralType extends BuiltInOperation, @isliteraltypeexpr
|
||||
* ```
|
||||
*/
|
||||
class BuiltInOperationHasTrivialMoveConstructor extends BuiltInOperation,
|
||||
@hastrivialmoveconstructorexpr {
|
||||
@hastrivialmoveconstructorexpr
|
||||
{
|
||||
override string toString() { result = "__has_trivial_move_constructor" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "BuiltInOperationHasTrivialMoveConstructor" }
|
||||
@@ -1034,7 +1037,8 @@ class BuiltInOperationIsAggregate extends BuiltInOperation, @isaggregate {
|
||||
* ```
|
||||
*/
|
||||
class BuiltInOperationHasUniqueObjectRepresentations extends BuiltInOperation,
|
||||
@hasuniqueobjectrepresentations {
|
||||
@hasuniqueobjectrepresentations
|
||||
{
|
||||
override string toString() { result = "__has_unique_object_representations" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "BuiltInOperationHasUniqueObjectRepresentations" }
|
||||
@@ -1107,7 +1111,8 @@ class BuiltInOperationIsLayoutCompatible extends BuiltInOperation, @islayoutcomp
|
||||
* ```
|
||||
*/
|
||||
class BuiltInOperationIsPointerInterconvertibleBaseOf extends BuiltInOperation,
|
||||
@ispointerinterconvertiblebaseof {
|
||||
@ispointerinterconvertiblebaseof
|
||||
{
|
||||
override string toString() { result = "__is_pointer_interconvertible_base_of" }
|
||||
|
||||
override string getAPrimaryQlClass() {
|
||||
|
||||
@@ -719,13 +719,6 @@ class ReferenceToExpr extends Conversion, @reference_to {
|
||||
class PointerDereferenceExpr extends UnaryOperation, @indirect {
|
||||
override string getAPrimaryQlClass() { result = "PointerDereferenceExpr" }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use getOperand() instead.
|
||||
*
|
||||
* Gets the expression that is being dereferenced.
|
||||
*/
|
||||
deprecated Expr getExpr() { result = this.getOperand() }
|
||||
|
||||
override string getOperator() { result = "*" }
|
||||
|
||||
override int getPrecedence() { result = 16 }
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
* results than the AST-based library in most scenarios.
|
||||
*
|
||||
* Unless configured otherwise, _flow_ means that the exact value of
|
||||
* the source may reach the sink. We do not track flow across pointer
|
||||
* dereferences or array indexing.
|
||||
* the source may reach the sink. To track flow where the exact value
|
||||
* may not be preserved, import `semmle.code.cpp.ir.dataflow.TaintTracking`.
|
||||
*
|
||||
* To use global (interprocedural) data flow, extend the class
|
||||
* `DataFlow::Configuration` as documented on that class. To use local
|
||||
|
||||
@@ -243,3 +243,111 @@ module MakeWithState<StateConfigSig Config> implements DataFlowSig {
|
||||
|
||||
import Impl<C>
|
||||
}
|
||||
|
||||
signature class PathNodeSig {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString();
|
||||
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
);
|
||||
|
||||
/** Gets the underlying `Node`. */
|
||||
Node getNode();
|
||||
}
|
||||
|
||||
signature module PathGraphSig<PathNodeSig PathNode> {
|
||||
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
|
||||
predicate edges(PathNode a, PathNode b);
|
||||
|
||||
/** Holds if `n` is a node in the graph of data flow path explanations. */
|
||||
predicate nodes(PathNode n, string key, string val);
|
||||
|
||||
/**
|
||||
* Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through
|
||||
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and
|
||||
* `ret -> out` is summarized as the edge `arg -> out`.
|
||||
*/
|
||||
predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a `PathGraph` from two `PathGraph`s by disjoint union.
|
||||
*/
|
||||
module MergePathGraph<
|
||||
PathNodeSig PathNode1, PathNodeSig PathNode2, PathGraphSig<PathNode1> Graph1,
|
||||
PathGraphSig<PathNode2> Graph2>
|
||||
{
|
||||
private newtype TPathNode =
|
||||
TPathNode1(PathNode1 p) or
|
||||
TPathNode2(PathNode2 p)
|
||||
|
||||
/** A node in a graph of path explanations that is formed by disjoint union of the two given graphs. */
|
||||
class PathNode extends TPathNode {
|
||||
/** Gets this as a projection on the first given `PathGraph`. */
|
||||
PathNode1 asPathNode1() { this = TPathNode1(result) }
|
||||
|
||||
/** Gets this as a projection on the second given `PathGraph`. */
|
||||
PathNode2 asPathNode2() { this = TPathNode2(result) }
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() {
|
||||
result = this.asPathNode1().toString() or
|
||||
result = this.asPathNode2().toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
this.asPathNode1().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) or
|
||||
this.asPathNode2().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
|
||||
/** Gets the underlying `Node`. */
|
||||
Node getNode() {
|
||||
result = this.asPathNode1().getNode() or
|
||||
result = this.asPathNode2().getNode()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the query predicates needed to include a graph in a path-problem query.
|
||||
*/
|
||||
module PathGraph implements PathGraphSig<PathNode> {
|
||||
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
|
||||
query predicate edges(PathNode a, PathNode b) {
|
||||
Graph1::edges(a.asPathNode1(), b.asPathNode1()) or
|
||||
Graph2::edges(a.asPathNode2(), b.asPathNode2())
|
||||
}
|
||||
|
||||
/** Holds if `n` is a node in the graph of data flow path explanations. */
|
||||
query predicate nodes(PathNode n, string key, string val) {
|
||||
Graph1::nodes(n.asPathNode1(), key, val) or
|
||||
Graph2::nodes(n.asPathNode2(), key, val)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through
|
||||
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and
|
||||
* `ret -> out` is summarized as the edge `arg -> out`.
|
||||
*/
|
||||
query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) {
|
||||
Graph1::subpaths(arg.asPathNode1(), par.asPathNode1(), ret.asPathNode1(), out.asPathNode1()) or
|
||||
Graph2::subpaths(arg.asPathNode2(), par.asPathNode2(), ret.asPathNode2(), out.asPathNode2())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
private import cpp
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import semmle.code.cpp.ir.dataflow.DataFlow
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
|
||||
private import DataFlowPrivate
|
||||
private import DataFlowUtil
|
||||
private import DataFlowImplCommon as DataFlowImplCommon
|
||||
|
||||
/**
|
||||
@@ -143,7 +143,7 @@ private module VirtualDispatch {
|
||||
private class DataSensitiveExprCall extends DataSensitiveCall {
|
||||
DataSensitiveExprCall() { not exists(this.getStaticCallTarget()) }
|
||||
|
||||
override DataFlow::Node getDispatchValue() { result.asInstruction() = this.getCallTarget() }
|
||||
override DataFlow::Node getDispatchValue() { result.asOperand() = this.getCallTargetOperand() }
|
||||
|
||||
override Function resolve() {
|
||||
exists(FunctionInstruction fi |
|
||||
|
||||
@@ -418,6 +418,10 @@ module Impl<FullStateConfigSig Config> {
|
||||
)
|
||||
}
|
||||
|
||||
private predicate sourceCallCtx(CallContext cc) {
|
||||
if hasSourceCallCtx() then cc instanceof CallContextSomeCall else cc instanceof CallContextAny
|
||||
}
|
||||
|
||||
private predicate hasSinkCallCtx() {
|
||||
exists(FlowFeature feature | feature = Config::getAFeature() |
|
||||
feature instanceof FeatureHasSinkCallContext or
|
||||
@@ -456,6 +460,7 @@ module Impl<FullStateConfigSig Config> {
|
||||
* The Boolean `cc` records whether the node is reached through an
|
||||
* argument in a call.
|
||||
*/
|
||||
pragma[assume_small_delta]
|
||||
private predicate fwdFlow(NodeEx node, Cc cc) {
|
||||
sourceNode(node, _) and
|
||||
if hasSourceCallCtx() then cc = true else cc = false
|
||||
@@ -2803,11 +2808,7 @@ module Impl<FullStateConfigSig Config> {
|
||||
// A PathNode is introduced by a source ...
|
||||
Stage5::revFlow(node, state) and
|
||||
sourceNode(node, state) and
|
||||
(
|
||||
if hasSourceCallCtx()
|
||||
then cc instanceof CallContextSomeCall
|
||||
else cc instanceof CallContextAny
|
||||
) and
|
||||
sourceCallCtx(cc) and
|
||||
sc instanceof SummaryCtxNone and
|
||||
ap = TAccessPathNil(node.getDataFlowType())
|
||||
or
|
||||
@@ -3156,7 +3157,7 @@ module Impl<FullStateConfigSig Config> {
|
||||
/**
|
||||
* Provides the query predicates needed to include a graph in a path-problem query.
|
||||
*/
|
||||
module PathGraph {
|
||||
module PathGraph implements PathGraphSig<PathNode> {
|
||||
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
|
||||
query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b }
|
||||
|
||||
@@ -3213,11 +3214,7 @@ module Impl<FullStateConfigSig Config> {
|
||||
|
||||
override predicate isSource() {
|
||||
sourceNode(node, state) and
|
||||
(
|
||||
if hasSourceCallCtx()
|
||||
then cc instanceof CallContextSomeCall
|
||||
else cc instanceof CallContextAny
|
||||
) and
|
||||
sourceCallCtx(cc) and
|
||||
sc instanceof SummaryCtxNone and
|
||||
ap = TAccessPathNil(node.getDataFlowType())
|
||||
}
|
||||
|
||||
@@ -182,6 +182,7 @@ private module LambdaFlow {
|
||||
boolean toJump, DataFlowCallOption lastCall
|
||||
) {
|
||||
revLambdaFlow0(lambdaCall, kind, node, t, toReturn, toJump, lastCall) and
|
||||
not expectsContent(node, _) and
|
||||
if castNode(node) or node instanceof ArgNode or node instanceof ReturnNode
|
||||
then compatibleTypes(t, getNodeDataFlowType(node))
|
||||
else any()
|
||||
|
||||
@@ -18,6 +18,9 @@ module Consistency {
|
||||
/** Holds if `n` should be excluded from the consistency test `uniqueEnclosingCallable`. */
|
||||
predicate uniqueEnclosingCallableExclude(Node n) { none() }
|
||||
|
||||
/** Holds if `call` should be excluded from the consistency test `uniqueCallEnclosingCallable`. */
|
||||
predicate uniqueCallEnclosingCallableExclude(DataFlowCall call) { none() }
|
||||
|
||||
/** Holds if `n` should be excluded from the consistency test `uniqueNodeLocation`. */
|
||||
predicate uniqueNodeLocationExclude(Node n) { none() }
|
||||
|
||||
@@ -86,6 +89,15 @@ module Consistency {
|
||||
)
|
||||
}
|
||||
|
||||
query predicate uniqueCallEnclosingCallable(DataFlowCall call, string msg) {
|
||||
exists(int c |
|
||||
c = count(call.getEnclosingCallable()) and
|
||||
c != 1 and
|
||||
not any(ConsistencyConfiguration conf).uniqueCallEnclosingCallableExclude(call) and
|
||||
msg = "Call should have one enclosing callable but has " + c + "."
|
||||
)
|
||||
}
|
||||
|
||||
query predicate uniqueType(Node n, string msg) {
|
||||
exists(int c |
|
||||
n instanceof RelevantNode and
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -19,6 +19,7 @@ private import semmle.code.cpp.ir.dataflow.TaintTracking
|
||||
private import semmle.code.cpp.ir.dataflow.TaintTracking2
|
||||
private import semmle.code.cpp.ir.dataflow.TaintTracking3
|
||||
private import semmle.code.cpp.ir.dataflow.internal.ModelUtil
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate
|
||||
|
||||
/**
|
||||
* A predictable instruction is one where an external user can predict
|
||||
@@ -75,6 +76,20 @@ private DataFlow::Node getNodeForExpr(Expr node) {
|
||||
not argv(node.(VariableAccess).getTarget())
|
||||
}
|
||||
|
||||
private predicate conflatePointerAndPointee(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
// Flow from `op` to `*op`.
|
||||
exists(Operand operand, int indirectionIndex |
|
||||
nodeHasOperand(nodeFrom, operand, indirectionIndex) and
|
||||
nodeHasOperand(nodeTo, operand, indirectionIndex - 1)
|
||||
)
|
||||
or
|
||||
// Flow from `instr` to `*instr`.
|
||||
exists(Instruction instr, int indirectionIndex |
|
||||
nodeHasInstruction(nodeFrom, instr, indirectionIndex) and
|
||||
nodeHasInstruction(nodeTo, instr, indirectionIndex - 1)
|
||||
)
|
||||
}
|
||||
|
||||
private class DefaultTaintTrackingCfg extends TaintTracking::Configuration {
|
||||
DefaultTaintTrackingCfg() { this = "DefaultTaintTrackingCfg" }
|
||||
|
||||
@@ -85,6 +100,10 @@ private class DefaultTaintTrackingCfg extends TaintTracking::Configuration {
|
||||
override predicate isSanitizer(DataFlow::Node node) { nodeIsBarrier(node) }
|
||||
|
||||
override predicate isSanitizerIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
conflatePointerAndPointee(nodeFrom, nodeTo)
|
||||
}
|
||||
}
|
||||
|
||||
private class ToGlobalVarTaintTrackingCfg extends TaintTracking::Configuration {
|
||||
@@ -168,16 +187,18 @@ private predicate hasUpperBoundsCheck(Variable var) {
|
||||
private predicate nodeIsBarrierEqualityCandidate(
|
||||
DataFlow::Node node, Operand access, Variable checkedVar
|
||||
) {
|
||||
readsVariable(node.asInstruction(), checkedVar) and
|
||||
any(IRGuardCondition guard).ensuresEq(access, _, _, node.asInstruction().getBlock(), true)
|
||||
exists(Instruction instr | instr = node.asOperand().getDef() |
|
||||
readsVariable(instr, checkedVar) and
|
||||
any(IRGuardCondition guard).ensuresEq(access, _, _, instr.getBlock(), true)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
private module Cached {
|
||||
cached
|
||||
predicate nodeIsBarrier(DataFlow::Node node) {
|
||||
exists(Variable checkedVar |
|
||||
readsVariable(node.asInstruction(), checkedVar) and
|
||||
exists(Variable checkedVar, Instruction instr | instr = node.asOperand().getDef() |
|
||||
readsVariable(instr, checkedVar) and
|
||||
hasUpperBoundsCheck(checkedVar)
|
||||
)
|
||||
or
|
||||
@@ -284,7 +305,7 @@ private module Cached {
|
||||
cached
|
||||
predicate additionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) {
|
||||
exists(CallInstruction call, Function func, FunctionInput modelIn, FunctionOutput modelOut |
|
||||
n1.asOperand() = callInput(call, modelIn) and
|
||||
n1 = callInput(call, modelIn) and
|
||||
(
|
||||
func.(TaintFunction).hasTaintFlow(modelIn, modelOut)
|
||||
or
|
||||
@@ -415,6 +436,8 @@ module TaintedWithPath {
|
||||
}
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) {
|
||||
conflatePointerAndPointee(n1, n2)
|
||||
or
|
||||
// Steps into and out of global variables
|
||||
exists(TaintTrackingConfiguration cfg | cfg.taintThroughGlobals() |
|
||||
writesVariable(n1.asInstruction(), n2.asVariable().(GlobalOrNamespaceVariable))
|
||||
@@ -622,6 +645,8 @@ module TaintedWithPath {
|
||||
|
||||
private predicate isGlobalVariablePathNode(WrapPathNode n) {
|
||||
n.inner().getNode().asVariable() instanceof GlobalOrNamespaceVariable
|
||||
or
|
||||
n.inner().getNode().asIndirectVariable() instanceof GlobalOrNamespaceVariable
|
||||
}
|
||||
|
||||
private predicate edgesWithoutGlobals(PathNode a, PathNode b) {
|
||||
|
||||
@@ -5,41 +5,89 @@
|
||||
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import semmle.code.cpp.ir.dataflow.DataFlow
|
||||
private import DataFlowUtil
|
||||
private import SsaInternals as Ssa
|
||||
|
||||
/**
|
||||
* Gets the instruction that goes into `input` for `call`.
|
||||
*/
|
||||
Operand callInput(CallInstruction call, FunctionInput input) {
|
||||
DataFlow::Node callInput(CallInstruction call, FunctionInput input) {
|
||||
// An argument or qualifier
|
||||
exists(int index |
|
||||
result = call.getArgumentOperand(index) and
|
||||
result.asOperand() = call.getArgumentOperand(index) and
|
||||
input.isParameterOrQualifierAddress(index)
|
||||
)
|
||||
or
|
||||
// A value pointed to by an argument or qualifier
|
||||
exists(ReadSideEffectInstruction read |
|
||||
result = read.getSideEffectOperand() and
|
||||
read.getPrimaryInstruction() = call and
|
||||
input.isParameterDerefOrQualifierObject(read.getIndex())
|
||||
exists(int index, int indirectionIndex |
|
||||
hasOperandAndIndex(result, call.getArgumentOperand(index), indirectionIndex) and
|
||||
input.isParameterDerefOrQualifierObject(index, indirectionIndex)
|
||||
)
|
||||
or
|
||||
exists(int ind |
|
||||
result = getIndirectReturnOutNode(call, ind) and
|
||||
input.isReturnValueDeref(ind)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the instruction that holds the `output` for `call`.
|
||||
*/
|
||||
Instruction callOutput(CallInstruction call, FunctionOutput output) {
|
||||
Node callOutput(CallInstruction call, FunctionOutput output) {
|
||||
// The return value
|
||||
result = call and
|
||||
result.asInstruction() = call and
|
||||
output.isReturnValue()
|
||||
or
|
||||
// The side effect of a call on the value pointed to by an argument or qualifier
|
||||
exists(WriteSideEffectInstruction effect |
|
||||
result = effect and
|
||||
effect.getPrimaryInstruction() = call and
|
||||
output.isParameterDerefOrQualifierObject(effect.getIndex())
|
||||
exists(int index, int indirectionIndex |
|
||||
result.(IndirectArgumentOutNode).getArgumentIndex() = index and
|
||||
result.(IndirectArgumentOutNode).getIndirectionIndex() = indirectionIndex and
|
||||
result.(IndirectArgumentOutNode).getCallInstruction() = call and
|
||||
output.isParameterDerefOrQualifierObject(index, indirectionIndex)
|
||||
)
|
||||
or
|
||||
// TODO: modify this when we get return value dereferences
|
||||
result = call and
|
||||
output.isReturnValueDeref()
|
||||
exists(int ind |
|
||||
result = getIndirectReturnOutNode(call, ind) and
|
||||
output.isReturnValueDeref(ind)
|
||||
)
|
||||
}
|
||||
|
||||
DataFlow::Node callInput(CallInstruction call, FunctionInput input, int d) {
|
||||
exists(DataFlow::Node n | n = callInput(call, input) and d > 0 |
|
||||
// An argument or qualifier
|
||||
hasOperandAndIndex(result, n.asOperand(), d)
|
||||
or
|
||||
exists(Operand operand, int indirectionIndex |
|
||||
// A value pointed to by an argument or qualifier
|
||||
hasOperandAndIndex(n, operand, indirectionIndex) and
|
||||
hasOperandAndIndex(result, operand, indirectionIndex + d)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private IndirectReturnOutNode getIndirectReturnOutNode(CallInstruction call, int d) {
|
||||
result.getCallInstruction() = call and
|
||||
result.getIndirectionIndex() = d
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the instruction that holds the `output` for `call`.
|
||||
*/
|
||||
bindingset[d]
|
||||
Node callOutput(CallInstruction call, FunctionOutput output, int d) {
|
||||
exists(DataFlow::Node n | n = callOutput(call, output) and d > 0 |
|
||||
// The return value
|
||||
result = getIndirectReturnOutNode(n.asInstruction(), d)
|
||||
or
|
||||
// If there isn't an indirect out node for the call with indirection `d` then
|
||||
// we conflate this with the underlying `CallInstruction`.
|
||||
not exists(getIndirectReturnOutNode(call, d)) and
|
||||
n.asInstruction() = result.asInstruction()
|
||||
or
|
||||
// The side effect of a call on the value pointed to by an argument or qualifier
|
||||
exists(Operand operand, int indirectionIndex |
|
||||
Ssa::outNodeHasAddressAndIndex(n, operand, indirectionIndex) and
|
||||
Ssa::outNodeHasAddressAndIndex(result, operand, indirectionIndex + d)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,907 @@
|
||||
import cpp as Cpp
|
||||
import semmle.code.cpp.ir.IR
|
||||
import semmle.code.cpp.ir.internal.IRCppLanguage
|
||||
private import semmle.code.cpp.ir.implementation.raw.internal.SideEffects as SideEffects
|
||||
private import DataFlowImplCommon as DataFlowImplCommon
|
||||
private import DataFlowUtil
|
||||
private import semmle.code.cpp.models.interfaces.PointerWrapper
|
||||
private import DataFlowPrivate
|
||||
|
||||
/**
|
||||
* Holds if `operand` is an operand that is not used by the dataflow library.
|
||||
* Ignored operands are not recognized as uses by SSA, and they don't have a
|
||||
* corresponding `(Indirect)OperandNode`.
|
||||
*/
|
||||
predicate ignoreOperand(Operand operand) {
|
||||
operand = any(Instruction instr | ignoreInstruction(instr)).getAnOperand() or
|
||||
operand = any(Instruction instr | ignoreInstruction(instr)).getAUse() or
|
||||
operand instanceof MemoryOperand
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` is an instruction that is not used by the dataflow library.
|
||||
* Ignored instructions are not recognized as reads/writes by SSA, and they
|
||||
* don't have a corresponding `(Indirect)InstructionNode`.
|
||||
*/
|
||||
predicate ignoreInstruction(Instruction instr) {
|
||||
DataFlowImplCommon::forceCachingInSameStage() and
|
||||
(
|
||||
instr instanceof WriteSideEffectInstruction or
|
||||
instr instanceof PhiInstruction or
|
||||
instr instanceof ReadSideEffectInstruction or
|
||||
instr instanceof ChiInstruction or
|
||||
instr instanceof InitializeIndirectionInstruction or
|
||||
instr instanceof AliasedDefinitionInstruction or
|
||||
instr instanceof InitializeNonLocalInstruction or
|
||||
instr instanceof ReturnIndirectionInstruction
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the C++ type of `this` in the member function `f`.
|
||||
* The result is a glvalue if `isGLValue` is true, and
|
||||
* a prvalue if `isGLValue` is false.
|
||||
*/
|
||||
bindingset[isGLValue]
|
||||
private CppType getThisType(Cpp::MemberFunction f, boolean isGLValue) {
|
||||
result.hasType(f.getTypeOfThis(), isGLValue)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the C++ type of the instruction `i`.
|
||||
*
|
||||
* This is equivalent to `i.getResultLanguageType()` with the exception
|
||||
* of instructions that directly references a `this` IRVariable. In this
|
||||
* case, `i.getResultLanguageType()` gives an unknown type, whereas the
|
||||
* predicate gives the expected type (i.e., a potentially cv-qualified
|
||||
* type `A*` where `A` is the declaring type of the member function that
|
||||
* contains `i`).
|
||||
*/
|
||||
cached
|
||||
CppType getResultLanguageType(Instruction i) {
|
||||
if i.(VariableAddressInstruction).getIRVariable() instanceof IRThisVariable
|
||||
then
|
||||
if i.isGLValue()
|
||||
then result = getThisType(i.getEnclosingFunction(), true)
|
||||
else result = getThisType(i.getEnclosingFunction(), false)
|
||||
else result = i.getResultLanguageType()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the C++ type of the operand `operand`.
|
||||
* This is equivalent to the type of the operand's defining instruction.
|
||||
*
|
||||
* See `getResultLanguageType` for a description of this behavior.
|
||||
*/
|
||||
CppType getLanguageType(Operand operand) { result = getResultLanguageType(operand.getDef()) }
|
||||
|
||||
/**
|
||||
* Gets the maximum number of indirections a glvalue of type `type` can have.
|
||||
* For example:
|
||||
* - If `type = int`, the result is 1
|
||||
* - If `type = MyStruct`, the result is 1
|
||||
* - If `type = char*`, the result is 2
|
||||
*/
|
||||
int getMaxIndirectionsForType(Type type) {
|
||||
result = countIndirectionsForCppType(getTypeForGLValue(type))
|
||||
}
|
||||
|
||||
private class PointerOrArrayOrReferenceType extends Cpp::DerivedType {
|
||||
PointerOrArrayOrReferenceType() {
|
||||
this instanceof Cpp::PointerType
|
||||
or
|
||||
this instanceof Cpp::ArrayType
|
||||
or
|
||||
this instanceof Cpp::ReferenceType
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the maximum number of indirections a value of type `type` can have.
|
||||
*
|
||||
* Note that this predicate is intended to be called on unspecified types
|
||||
* (i.e., `countIndirections(e.getUnspecifiedType())`).
|
||||
*/
|
||||
private int countIndirections(Type t) {
|
||||
// We special case void pointers because we don't know how many indirections
|
||||
// they really have. In a Glorious Future we could do a pre-analysis to figure out
|
||||
// which kinds of values flows into the type and use the maximum number of
|
||||
// indirections flowinginto the type.
|
||||
if t instanceof Cpp::VoidPointerType
|
||||
then result = 2
|
||||
else (
|
||||
result = any(Indirection ind | ind.getType() = t).getNumberOfIndirections()
|
||||
or
|
||||
not exists(Indirection ind | ind.getType() = t) and
|
||||
result = 0
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the maximum number of indirections a value of C++
|
||||
* type `langType` can have.
|
||||
*/
|
||||
int countIndirectionsForCppType(LanguageType langType) {
|
||||
exists(Type type | langType.hasType(type, true) |
|
||||
result = 1 + countIndirections(type.getUnspecifiedType())
|
||||
)
|
||||
or
|
||||
exists(Type type | langType.hasType(type, false) |
|
||||
result = countIndirections(type.getUnspecifiedType())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A `CallInstruction` that calls an allocation function such
|
||||
* as `malloc` or `operator new`.
|
||||
*/
|
||||
class AllocationInstruction extends CallInstruction {
|
||||
AllocationInstruction() { this.getStaticCallTarget() instanceof Cpp::AllocationFunction }
|
||||
}
|
||||
|
||||
/**
|
||||
* An abstract class for handling indirections.
|
||||
*
|
||||
* Extend this class to make a type behave as a pointer for the
|
||||
* purposes of dataflow.
|
||||
*/
|
||||
abstract class Indirection extends Type {
|
||||
Type baseType;
|
||||
|
||||
/** Gets the type of this indirection. */
|
||||
final Type getType() { result = this }
|
||||
|
||||
/**
|
||||
* Gets the number of indirections supported by this type.
|
||||
*
|
||||
* For example, the number of indirections of a variable `p` of type
|
||||
* `int**` is `3` (i.e., `p`, `*p` and `**p`).
|
||||
*/
|
||||
abstract int getNumberOfIndirections();
|
||||
|
||||
/**
|
||||
* Holds if `deref` is an instruction that behaves as a `LoadInstruction`
|
||||
* that loads the value computed by `address`.
|
||||
*/
|
||||
predicate isAdditionalDereference(Instruction deref, Operand address) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `value` is written to the address computed by `address`.
|
||||
*
|
||||
* `certain` is `true` if this write is guaranteed to write to the address.
|
||||
*/
|
||||
predicate isAdditionalWrite(Node0Impl value, Operand address, boolean certain) { none() }
|
||||
|
||||
/**
|
||||
* Gets the base type of this indirection, after specifiers have been deeply
|
||||
* stripped and typedefs have been resolved.
|
||||
*
|
||||
* For example, the base type of `int*&` is `int*`, and the base type of `int*` is `int`.
|
||||
*/
|
||||
final Type getBaseType() { result = baseType }
|
||||
|
||||
/** Holds if there should be an additional taint step from `node1` to `node2`. */
|
||||
predicate isAdditionalTaintStep(Node node1, Node node2) { none() }
|
||||
|
||||
/**
|
||||
* Holds if the step from `opFrom` to `instrTo` should be considered a conversion
|
||||
* from `opFrom` to `instrTo`.
|
||||
*/
|
||||
predicate isAdditionalConversionFlow(Operand opFrom, Instruction instrTo) { none() }
|
||||
}
|
||||
|
||||
private class PointerOrArrayOrReferenceTypeIndirection extends Indirection instanceof PointerOrArrayOrReferenceType
|
||||
{
|
||||
PointerOrArrayOrReferenceTypeIndirection() {
|
||||
baseType = PointerOrArrayOrReferenceType.super.getBaseType()
|
||||
}
|
||||
|
||||
override int getNumberOfIndirections() {
|
||||
result = 1 + countIndirections(this.getBaseType().getUnspecifiedType())
|
||||
}
|
||||
}
|
||||
|
||||
private class PointerWrapperTypeIndirection extends Indirection instanceof PointerWrapper {
|
||||
PointerWrapperTypeIndirection() { baseType = PointerWrapper.super.getBaseType() }
|
||||
|
||||
override int getNumberOfIndirections() {
|
||||
result = 1 + countIndirections(this.getBaseType().getUnspecifiedType())
|
||||
}
|
||||
|
||||
override predicate isAdditionalDereference(Instruction deref, Operand address) {
|
||||
exists(CallInstruction call |
|
||||
operandForFullyConvertedCall(getAUse(deref), call) and
|
||||
this = call.getStaticCallTarget().getClassAndName("operator*") and
|
||||
address = call.getThisArgumentOperand()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private module IteratorIndirections {
|
||||
import semmle.code.cpp.models.interfaces.Iterator as Interfaces
|
||||
import semmle.code.cpp.models.implementations.Iterator as Iterator
|
||||
import semmle.code.cpp.models.implementations.StdContainer as StdContainer
|
||||
|
||||
class IteratorIndirection extends Indirection instanceof Interfaces::Iterator {
|
||||
IteratorIndirection() {
|
||||
not this instanceof PointerOrArrayOrReferenceTypeIndirection and
|
||||
baseType = super.getValueType()
|
||||
}
|
||||
|
||||
override int getNumberOfIndirections() {
|
||||
result = 1 + countIndirections(this.getBaseType().getUnspecifiedType())
|
||||
}
|
||||
|
||||
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) {
|
||||
exists(CallInstruction call | call.getArgumentOperand(0) = value.asOperand() |
|
||||
this = call.getStaticCallTarget().getClassAndName("operator=") and
|
||||
address = call.getThisArgumentOperand() and
|
||||
certain = false
|
||||
)
|
||||
}
|
||||
|
||||
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).getOperand() = call.getArgumentOperand(0) and
|
||||
node1.getType().getUnspecifiedType() = this
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isAdditionalConversionFlow(Operand opFrom, Instruction instrTo) {
|
||||
// This is a bit annoying: Consider the following snippet:
|
||||
// ```
|
||||
// struct MyIterator {
|
||||
// ...
|
||||
// insert_iterator_by_trait operator*();
|
||||
// insert_iterator_by_trait operator=(int x);
|
||||
// };
|
||||
// ...
|
||||
// MyIterator it;
|
||||
// ...
|
||||
// *it = source();
|
||||
// ```
|
||||
// The qualifier to `operator*` will undergo prvalue-to-xvalue conversion and a
|
||||
// temporary object will be created. Thus, the IR for the call to `operator=` will
|
||||
// look like (simplified):
|
||||
// ```
|
||||
// r1(glval<MyIterator>) = VariableAddress[it] :
|
||||
// r2(glval<unknown>) = FunctionAddress[operator*] :
|
||||
// r3(MyIterator) = Call[operator*] : func:r2, this:r1
|
||||
// r4(glval<MyIterator>) = VariableAddress[#temp] :
|
||||
// m1(MyIterator) = Store[#temp] : &:r4, r3
|
||||
// r5(glval<unknown>) = FunctionAddress[operator=] :
|
||||
// r6(glval<unknown>) = FunctionAddress[source] :
|
||||
// r7(int) = Call[source] : func:r6
|
||||
// r8(MyIterator) = Call[operator=] : func:r5, this:r4, 0:r7
|
||||
// ```
|
||||
// in order to properly recognize that the qualifier to the call to `operator=` accesses
|
||||
// `it` we look for the store that writes to the temporary object, and use the source value
|
||||
// of that store as the "address" to continue searching for the base variable `it`.
|
||||
exists(StoreInstruction store, VariableInstruction var |
|
||||
var = instrTo and
|
||||
var.getIRVariable() instanceof IRTempVariable and
|
||||
opFrom.getType() = this and
|
||||
store.getSourceValueOperand() = opFrom and
|
||||
store.getDestinationAddress() = var
|
||||
)
|
||||
or
|
||||
// A call to `operator++` or `operator--` is the iterator equivalent version of a
|
||||
// pointer arithmetic instruction.
|
||||
exists(CallInstruction call |
|
||||
instrTo = call and
|
||||
call.getStaticCallTarget() instanceof Iterator::IteratorCrementMemberOperator and
|
||||
opFrom = call.getThisArgumentOperand()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
predicate isDereference(Instruction deref, Operand address) {
|
||||
any(Indirection ind).isAdditionalDereference(deref, address)
|
||||
or
|
||||
deref.(LoadInstruction).getSourceAddressOperand() = address
|
||||
}
|
||||
|
||||
predicate isWrite(Node0Impl value, Operand address, boolean certain) {
|
||||
any(Indirection ind).isAdditionalWrite(value, address, certain)
|
||||
or
|
||||
certain = true and
|
||||
(
|
||||
exists(StoreInstruction store |
|
||||
value.asInstruction() = store and
|
||||
address = store.getDestinationAddressOperand()
|
||||
)
|
||||
or
|
||||
exists(InitializeParameterInstruction init |
|
||||
value.asInstruction() = init and
|
||||
address = init.getAnOperand()
|
||||
)
|
||||
or
|
||||
exists(InitializeDynamicAllocationInstruction init |
|
||||
value.asInstruction() = init and
|
||||
address = init.getAllocationAddressOperand()
|
||||
)
|
||||
or
|
||||
exists(UninitializedInstruction uninitialized |
|
||||
value.asInstruction() = uninitialized and
|
||||
address = uninitialized.getAnOperand()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
predicate isAdditionalConversionFlow(Operand opFrom, Instruction instrTo) {
|
||||
any(Indirection ind).isAdditionalConversionFlow(opFrom, instrTo)
|
||||
}
|
||||
|
||||
newtype TBaseSourceVariable =
|
||||
// Each IR variable gets its own source variable
|
||||
TBaseIRVariable(IRVariable var) or
|
||||
// Each allocation gets its own source variable
|
||||
TBaseCallVariable(AllocationInstruction call)
|
||||
|
||||
abstract class BaseSourceVariable extends TBaseSourceVariable {
|
||||
/** Gets a textual representation of this element. */
|
||||
abstract string toString();
|
||||
|
||||
/** Gets the type of this base source variable. */
|
||||
abstract DataFlowType getType();
|
||||
}
|
||||
|
||||
class BaseIRVariable extends BaseSourceVariable, TBaseIRVariable {
|
||||
IRVariable var;
|
||||
|
||||
IRVariable getIRVariable() { result = var }
|
||||
|
||||
BaseIRVariable() { this = TBaseIRVariable(var) }
|
||||
|
||||
override string toString() { result = var.toString() }
|
||||
|
||||
override DataFlowType getType() { result = var.getType() }
|
||||
}
|
||||
|
||||
class BaseCallVariable extends BaseSourceVariable, TBaseCallVariable {
|
||||
AllocationInstruction call;
|
||||
|
||||
BaseCallVariable() { this = TBaseCallVariable(call) }
|
||||
|
||||
AllocationInstruction getCallInstruction() { result = call }
|
||||
|
||||
override string toString() { result = call.toString() }
|
||||
|
||||
override DataFlowType getType() { result = call.getResultType() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the value pointed to by `operand` can potentially be
|
||||
* modified be the caller.
|
||||
*/
|
||||
predicate isModifiableByCall(ArgumentOperand operand, int indirectionIndex) {
|
||||
exists(CallInstruction call, int index, CppType type |
|
||||
indirectionIndex = [1 .. countIndirectionsForCppType(type)] and
|
||||
type = getLanguageType(operand) and
|
||||
call.getArgumentOperand(index) = operand and
|
||||
if index = -1
|
||||
then
|
||||
// A qualifier is "modifiable" if:
|
||||
// 1. the member function is not const specified, or
|
||||
// 2. the member function is `const` specified, but returns a pointer or reference
|
||||
// type that is non-const.
|
||||
//
|
||||
// To see why this is necessary, consider the following function:
|
||||
// ```
|
||||
// struct C {
|
||||
// void* data_;
|
||||
// void* data() const { return data; }
|
||||
// };
|
||||
// ...
|
||||
// C c;
|
||||
// memcpy(c.data(), source, 16)
|
||||
// ```
|
||||
// the data pointed to by `c.data_` is potentially modified by the call to `memcpy` even though
|
||||
// `C::data` has a const specifier. So we further place the restriction that the type returned
|
||||
// by `call` should not be of the form `const T*` (for some deeply const type `T`).
|
||||
if call.getStaticCallTarget() instanceof Cpp::ConstMemberFunction
|
||||
then
|
||||
exists(PointerOrArrayOrReferenceType resultType |
|
||||
resultType = call.getResultType() and
|
||||
not resultType.isDeeplyConstBelow()
|
||||
)
|
||||
else any()
|
||||
else
|
||||
// An argument is modifiable if it's a non-const pointer or reference type.
|
||||
isModifiableAt(type, indirectionIndex)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `t` is a pointer or reference type that supports at least `indirectionIndex` number
|
||||
* of indirections, and the `indirectionIndex` indirection cannot be modfiied by passing a
|
||||
* value of `t` to a function.
|
||||
*/
|
||||
private predicate isModifiableAtImpl(CppType cppType, int indirectionIndex) {
|
||||
indirectionIndex = [1 .. countIndirectionsForCppType(cppType)] and
|
||||
(
|
||||
exists(Type pointerType, Type base, Type t |
|
||||
pointerType = t.getUnderlyingType() and
|
||||
pointerType = any(Indirection ind).getUnderlyingType() and
|
||||
cppType.hasType(t, _) and
|
||||
base = getTypeImpl(pointerType, indirectionIndex)
|
||||
|
|
||||
// The value cannot be modified if it has a const specifier,
|
||||
not base.isConst()
|
||||
or
|
||||
// but in the case of a class type, it may be the case that
|
||||
// one of the members was modified.
|
||||
exists(base.stripType().(Cpp::Class).getAField())
|
||||
)
|
||||
or
|
||||
// If the `indirectionIndex`'th dereference of a type can be modified
|
||||
// then so can the `indirectionIndex + 1`'th dereference.
|
||||
isModifiableAtImpl(cppType, indirectionIndex - 1)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `t` is a type with at least `indirectionIndex` number of indirections,
|
||||
* and the `indirectionIndex` indirection can be modified by passing a value of
|
||||
* type `t` to a function function.
|
||||
*/
|
||||
bindingset[indirectionIndex]
|
||||
predicate isModifiableAt(CppType cppType, int indirectionIndex) {
|
||||
isModifiableAtImpl(cppType, indirectionIndex)
|
||||
or
|
||||
exists(PointerWrapper pw, Type t |
|
||||
cppType.hasType(t, _) and
|
||||
t.stripType() = pw and
|
||||
not pw.pointsToConst()
|
||||
)
|
||||
}
|
||||
|
||||
abstract class BaseSourceVariableInstruction extends Instruction {
|
||||
/** Gets the base source variable accessed by this instruction. */
|
||||
abstract BaseSourceVariable getBaseSourceVariable();
|
||||
}
|
||||
|
||||
private class BaseIRVariableInstruction extends BaseSourceVariableInstruction,
|
||||
VariableAddressInstruction
|
||||
{
|
||||
override BaseIRVariable getBaseSourceVariable() { result.getIRVariable() = this.getIRVariable() }
|
||||
}
|
||||
|
||||
private class BaseAllocationInstruction extends BaseSourceVariableInstruction, AllocationInstruction
|
||||
{
|
||||
override BaseCallVariable getBaseSourceVariable() { result.getCallInstruction() = this }
|
||||
}
|
||||
|
||||
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, _)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[assume_small_delta]
|
||||
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 */
|
||||
private predicate isConversion(Operand op) {
|
||||
exists(Instruction def, Operand use |
|
||||
def = op.getDef() and
|
||||
use = unique( | | getAUse(def)) and
|
||||
conversionFlow(use, _, false, false)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `op` is a use of an SSA variable rooted at `base` with `ind` number
|
||||
* of indirections.
|
||||
*
|
||||
* `certain` is `true` if the operand is guaranteed to read the variable, and
|
||||
* `indirectionIndex` specifies the number of loads required to read the variable.
|
||||
*/
|
||||
cached
|
||||
predicate isUse(
|
||||
boolean certain, Operand op, BaseSourceVariableInstruction base, int ind, int indirectionIndex
|
||||
) {
|
||||
not ignoreOperand(op) and
|
||||
certain = true and
|
||||
exists(LanguageType type, int upper, int ind0 |
|
||||
type = getLanguageType(op) and
|
||||
upper = countIndirectionsForCppType(type) and
|
||||
isUseImpl(op, base, ind0) and
|
||||
// Don't count every conversion as their own use. Instead, only the first
|
||||
// use (i.e., before any conversions are applied) will count as a use.
|
||||
not isConversion(op) and
|
||||
ind = ind0 + [0 .. upper] and
|
||||
indirectionIndex = ind - ind0
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the underlying IR has a suitable instruction to represent a value
|
||||
* that would otherwise need to be represented by a dedicated `OperandNode` value.
|
||||
*
|
||||
* Such operands do not create new `OperandNode` values, but are
|
||||
* instead associated with the instruction returned by this predicate.
|
||||
*/
|
||||
cached
|
||||
Instruction getIRRepresentationOfOperand(Operand operand) {
|
||||
operand = unique( | | getAUse(result))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the underlying IR has a suitable operand to represent a value
|
||||
* that would otherwise need to be represented by a dedicated `RawIndirectOperand` value.
|
||||
*
|
||||
* Such operands do not create new `RawIndirectOperand` values, but are
|
||||
* instead associated with the operand returned by this predicate.
|
||||
*/
|
||||
cached
|
||||
Operand getIRRepresentationOfIndirectOperand(Operand operand, int indirectionIndex) {
|
||||
exists(Instruction load |
|
||||
isDereference(load, operand) and
|
||||
result = unique( | | getAUse(load)) and
|
||||
isUseImpl(operand, _, indirectionIndex - 1)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the underlying IR has a suitable instruction to represent a value
|
||||
* that would otherwise need to be represented by a dedicated `RawIndirectInstruction` value.
|
||||
*
|
||||
* Such instructions do not create new `RawIndirectOperand` values, but are
|
||||
* instead associated with the instruction returned by this predicate.
|
||||
*/
|
||||
cached
|
||||
Instruction getIRRepresentationOfIndirectInstruction(Instruction instr, int indirectionIndex) {
|
||||
exists(Instruction load, Operand address |
|
||||
address.getDef() = instr and
|
||||
isDereference(load, address) and
|
||||
isUseImpl(address, _, indirectionIndex - 1) and
|
||||
result = instr
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `operand` is a use of an SSA variable rooted at `base`, and the
|
||||
* path from `base` to `operand` passes through `ind` load-like instructions.
|
||||
*/
|
||||
private predicate isUseImpl(Operand operand, BaseSourceVariableInstruction base, int ind) {
|
||||
DataFlowImplCommon::forceCachingInSameStage() and
|
||||
ind = 0 and
|
||||
operand = base.getAUse()
|
||||
or
|
||||
exists(Operand mid, Instruction instr |
|
||||
isUseImpl(mid, base, ind) and
|
||||
instr = operand.getDef() and
|
||||
conversionFlow(mid, instr, false, _)
|
||||
)
|
||||
or
|
||||
exists(int ind0 |
|
||||
exists(Operand address |
|
||||
isDereference(operand.getDef(), address) and
|
||||
isUseImpl(address, base, ind0)
|
||||
)
|
||||
or
|
||||
isUseImpl(operand.getDef().(InitializeParameterInstruction).getAnOperand(), base, ind0)
|
||||
|
|
||||
ind0 = ind - 1
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `address` is an address of an SSA variable rooted at `base`,
|
||||
* and `instr` is a definition of the SSA variable with `ind` number of indirections.
|
||||
*
|
||||
* `certain` is `true` if `instr` is guaranteed to write to the variable, and
|
||||
* `indirectionIndex` specifies the number of loads required to read the variable
|
||||
* after the write operation.
|
||||
*/
|
||||
cached
|
||||
predicate isDef(
|
||||
boolean certain, Node0Impl value, Operand address, BaseSourceVariableInstruction base, int ind,
|
||||
int indirectionIndex
|
||||
) {
|
||||
exists(
|
||||
boolean writeIsCertain, boolean addressIsCertain, int ind0, CppType type, int lower, int upper
|
||||
|
|
||||
isWrite(value, address, writeIsCertain) and
|
||||
isDefImpl(address, base, ind0, addressIsCertain) and
|
||||
certain = writeIsCertain.booleanAnd(addressIsCertain) and
|
||||
type = getLanguageType(address) and
|
||||
upper = countIndirectionsForCppType(type) and
|
||||
ind = ind0 + [lower .. upper] and
|
||||
indirectionIndex = ind - (ind0 + lower) and
|
||||
(if type.hasType(any(Cpp::ArrayType arrayType), true) then lower = 0 else lower = 1)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the address computed by `operand` is guaranteed to write
|
||||
* to a specific address.
|
||||
*/
|
||||
private predicate isCertainAddress(Operand operand) {
|
||||
operand.getDef() instanceof VariableAddressInstruction
|
||||
or
|
||||
operand.getType() instanceof Cpp::ReferenceType
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `address` is a use of an SSA variable rooted at `base`, and the
|
||||
* path from `base` to `address` passes through `ind` load-like instructions.
|
||||
*
|
||||
* Note: Unlike `isUseImpl`, this predicate recurses through pointer-arithmetic
|
||||
* instructions.
|
||||
*/
|
||||
private predicate isDefImpl(
|
||||
Operand operand, BaseSourceVariableInstruction base, int ind, boolean certain
|
||||
) {
|
||||
DataFlowImplCommon::forceCachingInSameStage() and
|
||||
ind = 0 and
|
||||
operand = base.getAUse() and
|
||||
(if isCertainAddress(operand) then certain = true else certain = false)
|
||||
or
|
||||
exists(Operand mid, Instruction instr, boolean certain0, boolean isPointerArith |
|
||||
isDefImpl(mid, base, ind, certain0) and
|
||||
instr = operand.getDef() and
|
||||
conversionFlow(mid, instr, isPointerArith, _) and
|
||||
if isPointerArith = true then certain = false else certain = certain0
|
||||
)
|
||||
or
|
||||
exists(Operand address, boolean certain0 |
|
||||
isDereference(operand.getDef(), address) and
|
||||
isDefImpl(address, base, ind - 1, certain0)
|
||||
|
|
||||
if isCertainAddress(operand) then certain = certain0 else certain = false
|
||||
)
|
||||
or
|
||||
isDefImpl(operand.getDef().(InitializeParameterInstruction).getAnOperand(), base, ind - 1, _) and
|
||||
certain = true
|
||||
}
|
||||
}
|
||||
|
||||
import Cached
|
||||
|
||||
/**
|
||||
* Inputs to the shared SSA library's parameterized module that is shared
|
||||
* between the SSA pruning stage, and the final SSA stage.
|
||||
*/
|
||||
module InputSigCommon {
|
||||
class BasicBlock = IRBlock;
|
||||
|
||||
BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) { result.immediatelyDominates(bb) }
|
||||
|
||||
BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.getASuccessor() }
|
||||
|
||||
class ExitBasicBlock extends IRBlock {
|
||||
ExitBasicBlock() { this.getLastInstruction() instanceof ExitFunctionInstruction }
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,9 @@ private import semmle.code.cpp.ir.dataflow.DataFlow
|
||||
private import ModelUtil
|
||||
private import semmle.code.cpp.models.interfaces.DataFlow
|
||||
private import semmle.code.cpp.models.interfaces.SideEffect
|
||||
private import DataFlowUtil
|
||||
private import DataFlowPrivate
|
||||
private import SsaInternals as Ssa
|
||||
|
||||
/**
|
||||
* Holds if taint propagates from `nodeFrom` to `nodeTo` in exactly one local
|
||||
@@ -23,27 +26,17 @@ cached
|
||||
predicate localAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
operandToInstructionTaintStep(nodeFrom.asOperand(), nodeTo.asInstruction())
|
||||
or
|
||||
instructionToOperandTaintStep(nodeFrom.asInstruction(), nodeTo.asOperand())
|
||||
}
|
||||
|
||||
private predicate instructionToOperandTaintStep(Instruction fromInstr, Operand toOperand) {
|
||||
// Propagate flow from the definition of an operand to the operand, even when the overlap is inexact.
|
||||
// We only do this in certain cases:
|
||||
// 1. The instruction's result must not be conflated, and
|
||||
// 2. The instruction's result type is one the types where we expect element-to-object flow. Currently
|
||||
// this is array types and union types. This matches the other two cases of element-to-object flow in
|
||||
// `DefaultTaintTracking`.
|
||||
toOperand.getAnyDef() = fromInstr and
|
||||
not fromInstr.isResultConflated() and
|
||||
(
|
||||
fromInstr.getResultType() instanceof ArrayType or
|
||||
fromInstr.getResultType() instanceof Union
|
||||
modeledTaintStep(nodeFrom, nodeTo)
|
||||
or
|
||||
// Flow from (the indirection of) an operand of a pointer arithmetic instruction to the
|
||||
// indirection of the pointer arithmetic instruction. This provides flow from `source`
|
||||
// in `x[source]` to the result of the associated load instruction.
|
||||
exists(PointerArithmeticInstruction pai, int indirectionIndex |
|
||||
nodeHasOperand(nodeFrom, pai.getAnOperand(), pragma[only_bind_into](indirectionIndex)) and
|
||||
hasInstructionAndIndex(nodeTo, pai, indirectionIndex + 1)
|
||||
)
|
||||
or
|
||||
exists(ReadSideEffectInstruction readInstr |
|
||||
fromInstr = readInstr.getArgumentDef() and
|
||||
toOperand = readInstr.getSideEffectOperand()
|
||||
)
|
||||
any(Ssa::Indirection ind).isAdditionalTaintStep(nodeFrom, nodeTo)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -61,13 +54,11 @@ private predicate operandToInstructionTaintStep(Operand opFrom, Instruction inst
|
||||
instrTo instanceof BitwiseInstruction
|
||||
or
|
||||
instrTo instanceof PointerArithmeticInstruction
|
||||
or
|
||||
// The `CopyInstruction` case is also present in non-taint data flow, but
|
||||
// that uses `getDef` rather than `getAnyDef`. For taint, we want flow
|
||||
// from a definition of `myStruct` to a `myStruct.myField` expression.
|
||||
instrTo instanceof CopyInstruction
|
||||
)
|
||||
or
|
||||
// Taint flow from an address to its dereference.
|
||||
Ssa::isDereference(instrTo, opFrom)
|
||||
or
|
||||
// Unary instructions tend to preserve enough information in practice that we
|
||||
// want taint to flow through.
|
||||
// The exception is `FieldAddressInstruction`. Together with the rules below for
|
||||
@@ -81,40 +72,6 @@ private predicate operandToInstructionTaintStep(Operand opFrom, Instruction inst
|
||||
or
|
||||
instrTo.(FieldAddressInstruction).getField().getDeclaringType() instanceof Union
|
||||
)
|
||||
or
|
||||
// Flow from an element to an array or union that contains it.
|
||||
instrTo.(ChiInstruction).getPartialOperand() = opFrom and
|
||||
not instrTo.isResultConflated() and
|
||||
exists(Type t | instrTo.getResultLanguageType().hasType(t, false) |
|
||||
t instanceof Union
|
||||
or
|
||||
t instanceof ArrayType
|
||||
)
|
||||
or
|
||||
// Until we have flow through indirections across calls, we'll take flow out
|
||||
// of the indirection and into the argument.
|
||||
// When we get proper flow through indirections across calls, this code can be
|
||||
// moved to `adjusedSink` or possibly into the `DataFlow::ExprNode` class.
|
||||
exists(ReadSideEffectInstruction read |
|
||||
read.getSideEffectOperand() = opFrom and
|
||||
read.getArgumentDef() = instrTo
|
||||
)
|
||||
or
|
||||
// Until we have from through indirections across calls, we'll take flow out
|
||||
// of the parameter and into its indirection.
|
||||
// `InitializeIndirectionInstruction` only has a single operand: the address of the
|
||||
// value whose indirection we are initializing. When initializing an indirection of a parameter `p`,
|
||||
// the IR looks like this:
|
||||
// ```
|
||||
// m1 = InitializeParameter[p] : &r1
|
||||
// r2 = Load[p] : r2, m1
|
||||
// m3 = InitializeIndirection[p] : &r2
|
||||
// ```
|
||||
// So by having flow from `r2` to `m3` we're enabling flow from `m1` to `m3`. This relies on the
|
||||
// `LoadOperand`'s overlap being exact.
|
||||
instrTo.(InitializeIndirectionInstruction).getAnOperand() = opFrom
|
||||
or
|
||||
modeledTaintStep(opFrom, instrTo)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -164,16 +121,18 @@ predicate defaultImplicitTaintRead(DataFlow::Node node, DataFlow::Content c) { n
|
||||
predicate defaultTaintSanitizer(DataFlow::Node node) { none() }
|
||||
|
||||
/**
|
||||
* Holds if taint can flow from `instrIn` to `instrOut` through a call to a
|
||||
* Holds if taint can flow from `nodeIn` to `nodeOut` through a call to a
|
||||
* modeled function.
|
||||
*/
|
||||
predicate modeledTaintStep(Operand nodeIn, Instruction nodeOut) {
|
||||
predicate modeledTaintStep(DataFlow::Node nodeIn, DataFlow::Node nodeOut) {
|
||||
// Normal taint steps
|
||||
exists(CallInstruction call, TaintFunction func, FunctionInput modelIn, FunctionOutput modelOut |
|
||||
call.getStaticCallTarget() = func and
|
||||
func.hasTaintFlow(modelIn, modelOut)
|
||||
|
|
||||
nodeIn = callInput(call, modelIn) and
|
||||
nodeOut = callOutput(call, modelOut)
|
||||
nodeIn = callInput(call, modelIn) and nodeOut = callOutput(call, modelOut)
|
||||
or
|
||||
exists(int d | nodeIn = callInput(call, modelIn, d) and nodeOut = callOutput(call, modelOut, d))
|
||||
)
|
||||
or
|
||||
// Taint flow from one argument to another and data flow from an argument to a
|
||||
@@ -197,12 +156,11 @@ predicate modeledTaintStep(Operand nodeIn, Instruction nodeOut) {
|
||||
// Taint flow from a pointer argument to an output, when the model specifies flow from the deref
|
||||
// to that output, but the deref is not modeled in the IR for the caller.
|
||||
exists(
|
||||
CallInstruction call, ReadSideEffectInstruction read, Function func, FunctionInput modelIn,
|
||||
FunctionOutput modelOut
|
||||
CallInstruction call, DataFlow::SideEffectOperandNode indirectArgument, Function func,
|
||||
FunctionInput modelIn, FunctionOutput modelOut
|
||||
|
|
||||
read.getSideEffectOperand() = callInput(call, modelIn) and
|
||||
read.getArgumentDef() = nodeIn.getDef() and
|
||||
not read.getSideEffect().isResultModeled() and
|
||||
indirectArgument = callInput(call, modelIn) and
|
||||
indirectArgument.getAddressOperand() = nodeIn.asOperand() and
|
||||
call.getStaticCallTarget() = func and
|
||||
(
|
||||
func.(DataFlowFunction).hasDataFlow(modelIn, modelOut)
|
||||
|
||||
@@ -0,0 +1,311 @@
|
||||
/**
|
||||
* This module defines an initial SSA pruning stage that doesn't take
|
||||
* indirections into account.
|
||||
*/
|
||||
|
||||
private import codeql.ssa.Ssa as SsaImplCommon
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowImplCommon as DataFlowImplCommon
|
||||
private import semmle.code.cpp.models.interfaces.Allocation as Alloc
|
||||
private import semmle.code.cpp.models.interfaces.DataFlow as DataFlow
|
||||
private import semmle.code.cpp.ir.implementation.raw.internal.SideEffects as SideEffects
|
||||
private import semmle.code.cpp.ir.internal.IRCppLanguage
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
|
||||
private import semmle.code.cpp.ir.dataflow.internal.SsaInternalsCommon
|
||||
|
||||
private module SourceVariables {
|
||||
class SourceVariable instanceof BaseSourceVariable {
|
||||
string toString() { result = BaseSourceVariable.super.toString() }
|
||||
|
||||
BaseSourceVariable getBaseVariable() { result = this }
|
||||
}
|
||||
|
||||
class SourceIRVariable = BaseIRVariable;
|
||||
|
||||
class CallVariable = BaseCallVariable;
|
||||
}
|
||||
|
||||
import SourceVariables
|
||||
|
||||
private newtype TDefOrUseImpl =
|
||||
TDefImpl(Operand address) { isDef(_, _, address, _, _, _) } or
|
||||
TUseImpl(Operand operand) {
|
||||
isUse(_, operand, _, _, _) and
|
||||
not isDef(true, _, operand, _, _, _)
|
||||
} or
|
||||
TIteratorDef(BaseSourceVariableInstruction container, Operand iteratorAddress) {
|
||||
isIteratorDef(container, iteratorAddress, _, _, _)
|
||||
} or
|
||||
TIteratorUse(BaseSourceVariableInstruction container, Operand iteratorAddress) {
|
||||
isIteratorUse(container, iteratorAddress, _, _)
|
||||
} or
|
||||
TFinalParameterUse(Parameter p) {
|
||||
any(Indirection indirection).getType() = p.getUnspecifiedType()
|
||||
}
|
||||
|
||||
abstract private class DefOrUseImpl extends TDefOrUseImpl {
|
||||
/** Gets a textual representation of this element. */
|
||||
abstract string toString();
|
||||
|
||||
/** Gets the block of this definition or use. */
|
||||
final IRBlock getBlock() { this.hasIndexInBlock(result, _) }
|
||||
|
||||
/** Holds if this definition or use has index `index` in block `block`. */
|
||||
abstract predicate hasIndexInBlock(IRBlock block, int index);
|
||||
|
||||
final predicate hasIndexInBlock(IRBlock block, int index, SourceVariable sv) {
|
||||
this.hasIndexInBlock(block, index) and
|
||||
sv = this.getSourceVariable()
|
||||
}
|
||||
|
||||
/** Gets the location of this element. */
|
||||
abstract Cpp::Location getLocation();
|
||||
|
||||
abstract BaseSourceVariableInstruction getBase();
|
||||
|
||||
final BaseSourceVariable getBaseSourceVariable() {
|
||||
result = this.getBase().getBaseSourceVariable()
|
||||
}
|
||||
|
||||
/** Gets the variable that is defined or used. */
|
||||
final SourceVariable getSourceVariable() {
|
||||
result.getBaseVariable() = this.getBaseSourceVariable()
|
||||
}
|
||||
|
||||
abstract predicate isCertain();
|
||||
}
|
||||
|
||||
abstract class DefImpl extends DefOrUseImpl {
|
||||
Operand address;
|
||||
|
||||
Operand getAddressOperand() { result = address }
|
||||
|
||||
abstract Node0Impl getValue();
|
||||
|
||||
override string toString() { result = address.toString() }
|
||||
|
||||
override Cpp::Location getLocation() { result = this.getAddressOperand().getLocation() }
|
||||
|
||||
final override predicate hasIndexInBlock(IRBlock block, int index) {
|
||||
this.getAddressOperand().getUse() = block.getInstruction(index)
|
||||
}
|
||||
}
|
||||
|
||||
private class DirectDef extends DefImpl, TDefImpl {
|
||||
DirectDef() { this = TDefImpl(address) }
|
||||
|
||||
override BaseSourceVariableInstruction getBase() { isDef(_, _, address, result, _, _) }
|
||||
|
||||
override Node0Impl getValue() { isDef(_, result, address, _, _, _) }
|
||||
|
||||
override predicate isCertain() { isDef(true, _, address, _, _, _) }
|
||||
}
|
||||
|
||||
private class IteratorDef extends DefImpl, TIteratorDef {
|
||||
BaseSourceVariableInstruction container;
|
||||
|
||||
IteratorDef() { this = TIteratorDef(container, address) }
|
||||
|
||||
override BaseSourceVariableInstruction getBase() { result = container }
|
||||
|
||||
override Node0Impl getValue() { isIteratorDef(_, address, result, _, _) }
|
||||
|
||||
override predicate isCertain() { none() }
|
||||
}
|
||||
|
||||
abstract class UseImpl extends DefOrUseImpl { }
|
||||
|
||||
abstract private class OperandBasedUse extends UseImpl {
|
||||
Operand operand;
|
||||
|
||||
override string toString() { result = operand.toString() }
|
||||
|
||||
final override predicate hasIndexInBlock(IRBlock block, int index) {
|
||||
operand.getUse() = block.getInstruction(index)
|
||||
}
|
||||
|
||||
final override Cpp::Location getLocation() { result = operand.getLocation() }
|
||||
}
|
||||
|
||||
private class DirectUse extends OperandBasedUse, TUseImpl {
|
||||
DirectUse() { this = TUseImpl(operand) }
|
||||
|
||||
override BaseSourceVariableInstruction getBase() { isUse(_, operand, result, _, _) }
|
||||
|
||||
override predicate isCertain() { isUse(true, operand, _, _, _) }
|
||||
}
|
||||
|
||||
private class IteratorUse extends OperandBasedUse, TIteratorUse {
|
||||
BaseSourceVariableInstruction container;
|
||||
|
||||
IteratorUse() { this = TIteratorUse(container, operand) }
|
||||
|
||||
override BaseSourceVariableInstruction getBase() { result = container }
|
||||
|
||||
override predicate isCertain() { none() }
|
||||
}
|
||||
|
||||
private class FinalParameterUse extends UseImpl, TFinalParameterUse {
|
||||
Parameter p;
|
||||
|
||||
FinalParameterUse() { this = TFinalParameterUse(p) }
|
||||
|
||||
override string toString() { result = p.toString() }
|
||||
|
||||
final override predicate hasIndexInBlock(IRBlock block, int index) {
|
||||
// Ideally, this should always be a `ReturnInstruction`, but if
|
||||
// someone forgets to write a `return` statement in a function
|
||||
// with a non-void return type we generate an `UnreachedInstruction`.
|
||||
// In this case we still want to generate flow out of such functions
|
||||
// if they write to a parameter. So we pick the index of the
|
||||
// `UnreachedInstruction` as the index of this use.
|
||||
// Note that a function may have both a `ReturnInstruction` and an
|
||||
// `UnreachedInstruction`. If that's the case this predicate will
|
||||
// return multiple results. I don't think this is detrimental to
|
||||
// performance, however.
|
||||
exists(Instruction return |
|
||||
return instanceof ReturnInstruction or
|
||||
return instanceof UnreachedInstruction
|
||||
|
|
||||
block.getInstruction(index) = return and
|
||||
return.getEnclosingFunction() = p.getFunction()
|
||||
)
|
||||
}
|
||||
|
||||
final override Cpp::Location getLocation() {
|
||||
// Parameters can have multiple locations. When there's a unique location we use
|
||||
// that one, but if multiple locations exist we default to an unknown location.
|
||||
result = unique( | | p.getLocation())
|
||||
or
|
||||
not exists(unique( | | p.getLocation())) and
|
||||
result instanceof UnknownDefaultLocation
|
||||
}
|
||||
|
||||
override BaseSourceVariableInstruction getBase() {
|
||||
exists(InitializeParameterInstruction init |
|
||||
init.getParameter() = p and
|
||||
// This is always a `VariableAddressInstruction`
|
||||
result = init.getAnOperand().getDef()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isCertain() { any() }
|
||||
}
|
||||
|
||||
private module SsaInput implements SsaImplCommon::InputSig {
|
||||
import InputSigCommon
|
||||
import SourceVariables
|
||||
|
||||
/**
|
||||
* Holds if the `i`'th write in block `bb` writes to the variable `v`.
|
||||
* `certain` is `true` if the write is guaranteed to overwrite the entire variable.
|
||||
*/
|
||||
predicate variableWrite(IRBlock bb, int i, SourceVariable v, boolean certain) {
|
||||
DataFlowImplCommon::forceCachingInSameStage() and
|
||||
exists(DefImpl def | def.hasIndexInBlock(bb, i, v) |
|
||||
if def.isCertain() then certain = true else certain = false
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the `i`'th read in block `bb` reads to the variable `v`.
|
||||
* `certain` is `true` if the read is guaranteed.
|
||||
*/
|
||||
predicate variableRead(IRBlock bb, int i, SourceVariable v, boolean certain) {
|
||||
exists(UseImpl use | use.hasIndexInBlock(bb, i, v) |
|
||||
if use.isCertain() then certain = true else certain = false
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private newtype TSsaDefOrUse =
|
||||
TDefOrUse(DefOrUseImpl defOrUse) {
|
||||
defOrUse instanceof UseImpl
|
||||
or
|
||||
// If `defOrUse` is a definition we only include it if the
|
||||
// SSA library concludes that it's live after the write.
|
||||
exists(DefinitionExt def, SourceVariable sv, IRBlock bb, int i |
|
||||
def.definesAt(sv, bb, i, _) and
|
||||
defOrUse.(DefImpl).hasIndexInBlock(bb, i, sv)
|
||||
)
|
||||
} or
|
||||
TPhi(PhiNode phi)
|
||||
|
||||
abstract private class SsaDefOrUse extends TSsaDefOrUse {
|
||||
string toString() { result = "SsaDefOrUse" }
|
||||
|
||||
DefOrUseImpl asDefOrUse() { none() }
|
||||
|
||||
PhiNode asPhi() { none() }
|
||||
|
||||
abstract Location getLocation();
|
||||
}
|
||||
|
||||
class DefOrUse extends TDefOrUse, SsaDefOrUse {
|
||||
DefOrUseImpl defOrUse;
|
||||
|
||||
DefOrUse() { this = TDefOrUse(defOrUse) }
|
||||
|
||||
final override DefOrUseImpl asDefOrUse() { result = defOrUse }
|
||||
|
||||
final override Location getLocation() { result = defOrUse.getLocation() }
|
||||
|
||||
final SourceVariable getSourceVariable() { result = defOrUse.getSourceVariable() }
|
||||
}
|
||||
|
||||
class Phi extends TPhi, SsaDefOrUse {
|
||||
PhiNode phi;
|
||||
|
||||
Phi() { this = TPhi(phi) }
|
||||
|
||||
final override PhiNode asPhi() { result = phi }
|
||||
|
||||
final override Location getLocation() { result = phi.getBasicBlock().getLocation() }
|
||||
}
|
||||
|
||||
class UseOrPhi extends SsaDefOrUse {
|
||||
UseOrPhi() {
|
||||
this.asDefOrUse() instanceof UseImpl
|
||||
or
|
||||
this instanceof Phi
|
||||
}
|
||||
|
||||
final override Location getLocation() {
|
||||
result = this.asDefOrUse().getLocation() or result = this.(Phi).getLocation()
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = this.asDefOrUse().toString()
|
||||
or
|
||||
this instanceof Phi and
|
||||
result = "Phi"
|
||||
}
|
||||
}
|
||||
|
||||
class Def extends DefOrUse {
|
||||
override DefImpl defOrUse;
|
||||
|
||||
Operand getAddressOperand() { result = defOrUse.getAddressOperand() }
|
||||
|
||||
Instruction getAddress() { result = this.getAddressOperand().getDef() }
|
||||
|
||||
Node0Impl getValue() { result = defOrUse.getValue() }
|
||||
|
||||
override string toString() { result = this.asDefOrUse().toString() }
|
||||
|
||||
BaseSourceVariableInstruction getBase() { result = defOrUse.getBase() }
|
||||
|
||||
predicate isIteratorDef() { defOrUse instanceof IteratorDef }
|
||||
}
|
||||
|
||||
private module SsaImpl = SsaImplCommon::Make<SsaInput>;
|
||||
|
||||
class PhiNode extends SsaImpl::DefinitionExt {
|
||||
PhiNode() {
|
||||
this instanceof SsaImpl::PhiNode or
|
||||
this instanceof SsaImpl::PhiReadNode
|
||||
}
|
||||
}
|
||||
|
||||
class DefinitionExt = SsaImpl::DefinitionExt;
|
||||
@@ -7,7 +7,8 @@ import TaintTrackingParameter::Public
|
||||
private import TaintTrackingParameter::Private
|
||||
|
||||
private module AddTaintDefaults<DataFlowInternal::FullStateConfigSig Config> implements
|
||||
DataFlowInternal::FullStateConfigSig {
|
||||
DataFlowInternal::FullStateConfigSig
|
||||
{
|
||||
import Config
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) {
|
||||
|
||||
@@ -1082,7 +1082,8 @@ module Opcode {
|
||||
* See the `CallSideEffectInstruction` documentation for more details.
|
||||
*/
|
||||
class CallSideEffect extends WriteSideEffectOpcode, EscapedWriteOpcode, MayWriteOpcode,
|
||||
ReadSideEffectOpcode, EscapedReadOpcode, MayReadOpcode, TCallSideEffect {
|
||||
ReadSideEffectOpcode, EscapedReadOpcode, MayReadOpcode, TCallSideEffect
|
||||
{
|
||||
final override string toString() { result = "CallSideEffect" }
|
||||
}
|
||||
|
||||
@@ -1092,7 +1093,8 @@ module Opcode {
|
||||
* See the `CallReadSideEffectInstruction` documentation for more details.
|
||||
*/
|
||||
class CallReadSideEffect extends ReadSideEffectOpcode, EscapedReadOpcode, MayReadOpcode,
|
||||
TCallReadSideEffect {
|
||||
TCallReadSideEffect
|
||||
{
|
||||
final override string toString() { result = "CallReadSideEffect" }
|
||||
}
|
||||
|
||||
@@ -1102,7 +1104,8 @@ module Opcode {
|
||||
* See the `IndirectReadSideEffectInstruction` documentation for more details.
|
||||
*/
|
||||
class IndirectReadSideEffect extends ReadSideEffectOpcode, IndirectReadOpcode,
|
||||
TIndirectReadSideEffect {
|
||||
TIndirectReadSideEffect
|
||||
{
|
||||
final override string toString() { result = "IndirectReadSideEffect" }
|
||||
}
|
||||
|
||||
@@ -1112,7 +1115,8 @@ module Opcode {
|
||||
* See the `IndirectMustWriteSideEffectInstruction` documentation for more details.
|
||||
*/
|
||||
class IndirectMustWriteSideEffect extends WriteSideEffectOpcode, IndirectWriteOpcode,
|
||||
TIndirectMustWriteSideEffect {
|
||||
TIndirectMustWriteSideEffect
|
||||
{
|
||||
final override string toString() { result = "IndirectMustWriteSideEffect" }
|
||||
}
|
||||
|
||||
@@ -1122,7 +1126,8 @@ module Opcode {
|
||||
* See the `IndirectMayWriteSideEffectInstruction` documentation for more details.
|
||||
*/
|
||||
class IndirectMayWriteSideEffect extends WriteSideEffectOpcode, IndirectWriteOpcode,
|
||||
MayWriteOpcode, TIndirectMayWriteSideEffect {
|
||||
MayWriteOpcode, TIndirectMayWriteSideEffect
|
||||
{
|
||||
final override string toString() { result = "IndirectMayWriteSideEffect" }
|
||||
}
|
||||
|
||||
@@ -1132,7 +1137,8 @@ module Opcode {
|
||||
* See the `BufferReadSideEffectInstruction` documentation for more details.
|
||||
*/
|
||||
class BufferReadSideEffect extends ReadSideEffectOpcode, UnsizedBufferReadOpcode,
|
||||
TBufferReadSideEffect {
|
||||
TBufferReadSideEffect
|
||||
{
|
||||
final override string toString() { result = "BufferReadSideEffect" }
|
||||
}
|
||||
|
||||
@@ -1142,7 +1148,8 @@ module Opcode {
|
||||
* See the `BufferMustWriteSideEffectInstruction` documentation for more details.
|
||||
*/
|
||||
class BufferMustWriteSideEffect extends WriteSideEffectOpcode, UnsizedBufferWriteOpcode,
|
||||
TBufferMustWriteSideEffect {
|
||||
TBufferMustWriteSideEffect
|
||||
{
|
||||
final override string toString() { result = "BufferMustWriteSideEffect" }
|
||||
}
|
||||
|
||||
@@ -1152,7 +1159,8 @@ module Opcode {
|
||||
* See the `BufferMayWriteSideEffectInstruction` documentation for more details.
|
||||
*/
|
||||
class BufferMayWriteSideEffect extends WriteSideEffectOpcode, UnsizedBufferWriteOpcode,
|
||||
MayWriteOpcode, TBufferMayWriteSideEffect {
|
||||
MayWriteOpcode, TBufferMayWriteSideEffect
|
||||
{
|
||||
final override string toString() { result = "BufferMayWriteSideEffect" }
|
||||
}
|
||||
|
||||
@@ -1162,7 +1170,8 @@ module Opcode {
|
||||
* See the `SizedBufferReadSideEffectInstruction` documentation for more details.
|
||||
*/
|
||||
class SizedBufferReadSideEffect extends ReadSideEffectOpcode, SizedBufferReadOpcode,
|
||||
TSizedBufferReadSideEffect {
|
||||
TSizedBufferReadSideEffect
|
||||
{
|
||||
final override string toString() { result = "SizedBufferReadSideEffect" }
|
||||
}
|
||||
|
||||
@@ -1172,7 +1181,8 @@ module Opcode {
|
||||
* See the `SizedBufferMustWriteSideEffectInstruction` documentation for more details.
|
||||
*/
|
||||
class SizedBufferMustWriteSideEffect extends WriteSideEffectOpcode, SizedBufferWriteOpcode,
|
||||
TSizedBufferMustWriteSideEffect {
|
||||
TSizedBufferMustWriteSideEffect
|
||||
{
|
||||
final override string toString() { result = "SizedBufferMustWriteSideEffect" }
|
||||
}
|
||||
|
||||
@@ -1182,7 +1192,8 @@ module Opcode {
|
||||
* See the `SizedBufferMayWriteSideEffectInstruction` documentation for more details.
|
||||
*/
|
||||
class SizedBufferMayWriteSideEffect extends WriteSideEffectOpcode, SizedBufferWriteOpcode,
|
||||
MayWriteOpcode, TSizedBufferMayWriteSideEffect {
|
||||
MayWriteOpcode, TSizedBufferMayWriteSideEffect
|
||||
{
|
||||
final override string toString() { result = "SizedBufferMayWriteSideEffect" }
|
||||
}
|
||||
|
||||
@@ -1192,7 +1203,8 @@ module Opcode {
|
||||
* See the `InitializeDynamicAllocationInstruction` documentation for more details.
|
||||
*/
|
||||
class InitializeDynamicAllocation extends SideEffectOpcode, EntireAllocationWriteOpcode,
|
||||
TInitializeDynamicAllocation {
|
||||
TInitializeDynamicAllocation
|
||||
{
|
||||
final override string toString() { result = "InitializeDynamicAllocation" }
|
||||
}
|
||||
|
||||
@@ -1221,7 +1233,8 @@ module Opcode {
|
||||
* See the `InlineAsmInstruction` documentation for more details.
|
||||
*/
|
||||
class InlineAsm extends Opcode, EscapedWriteOpcode, MayWriteOpcode, EscapedReadOpcode,
|
||||
MayReadOpcode, TInlineAsm {
|
||||
MayReadOpcode, TInlineAsm
|
||||
{
|
||||
final override string toString() { result = "InlineAsm" }
|
||||
|
||||
final override predicate hasOperandInternal(OperandTag tag) {
|
||||
|
||||
@@ -87,22 +87,6 @@ class Operand extends TStageOperand {
|
||||
this.getDefinitionOverlap() instanceof MustExactlyOverlap
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: renamed to `getUse`.
|
||||
*
|
||||
* Gets the `Instruction` that consumes this operand.
|
||||
*/
|
||||
deprecated final Instruction getUseInstruction() { result = this.getUse() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: use `getAnyDef` or `getDef`. The exact replacement for this
|
||||
* predicate is `getAnyDef`, but most uses of this predicate should probably
|
||||
* be replaced with `getDef`.
|
||||
*
|
||||
* Gets the `Instruction` whose result is the value of the operand.
|
||||
*/
|
||||
deprecated final Instruction getDefinitionInstruction() { result = this.getAnyDef() }
|
||||
|
||||
/**
|
||||
* Gets the overlap relationship between the operand's definition and its use.
|
||||
*/
|
||||
|
||||
@@ -246,7 +246,8 @@ class VariableMemoryLocation extends TVariableMemoryLocation, AllocationMemoryLo
|
||||
}
|
||||
|
||||
class EntireAllocationMemoryLocation extends TEntireAllocationMemoryLocation,
|
||||
AllocationMemoryLocation {
|
||||
AllocationMemoryLocation
|
||||
{
|
||||
EntireAllocationMemoryLocation() { this = TEntireAllocationMemoryLocation(var, isMayAccess) }
|
||||
|
||||
final override string toStringInternal() { result = var.toString() }
|
||||
|
||||
@@ -87,22 +87,6 @@ class Operand extends TStageOperand {
|
||||
this.getDefinitionOverlap() instanceof MustExactlyOverlap
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: renamed to `getUse`.
|
||||
*
|
||||
* Gets the `Instruction` that consumes this operand.
|
||||
*/
|
||||
deprecated final Instruction getUseInstruction() { result = this.getUse() }
|
||||
|
||||
/**
|
||||
* DEPRECATED: use `getAnyDef` or `getDef`. The exact replacement for this
|
||||
* predicate is `getAnyDef`, but most uses of this predicate should probably
|
||||
* be replaced with `getDef`.
|
||||
*
|
||||
* Gets the `Instruction` whose result is the value of the operand.
|
||||
*/
|
||||
deprecated final Instruction getDefinitionInstruction() { result = this.getAnyDef() }
|
||||
|
||||
/**
|
||||
* Gets the overlap relationship between the operand's definition and its use.
|
||||
*/
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user