mirror of
https://github.com/github/codeql.git
synced 2026-05-30 02:51:24 +02:00
Compare commits
798 Commits
mbg/add/de
...
smowton/ad
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
596c4fcac3 | ||
|
|
85ee4e6ca1 | ||
|
|
280bb6864f | ||
|
|
6897b20722 | ||
|
|
670ae6c84c | ||
|
|
5883957a67 | ||
|
|
81110b19e7 | ||
|
|
0d2474bd55 | ||
|
|
49bc524fd0 | ||
|
|
d684dbdf5c | ||
|
|
a6bc9fd10f | ||
|
|
fc49ede33d | ||
|
|
a2dac3a41e | ||
|
|
ba3d50a462 | ||
|
|
0edfc6e01e | ||
|
|
c7725ec37c | ||
|
|
78f15755d7 | ||
|
|
32494859cd | ||
|
|
d577eeeea8 | ||
|
|
9702ea02fb | ||
|
|
54c12cd715 | ||
|
|
35938067fe | ||
|
|
05d89b29e2 | ||
|
|
898a4006b0 | ||
|
|
9f9a51685b | ||
|
|
170c9af9e8 | ||
|
|
d862972d5e | ||
|
|
d5f4340cf5 | ||
|
|
f11f2cb1a0 | ||
|
|
2131b0f116 | ||
|
|
468b05ccda | ||
|
|
135c820a32 | ||
|
|
9cfeaeb18e | ||
|
|
5e694b5983 | ||
|
|
ef348453fe | ||
|
|
194c99c513 | ||
|
|
23626f2c69 | ||
|
|
9b89ded908 | ||
|
|
d39f37540e | ||
|
|
2d9975d73f | ||
|
|
01d8ad98f6 | ||
|
|
c526020fd4 | ||
|
|
ecbb96ffc1 | ||
|
|
2c08b95430 | ||
|
|
cabce5fb36 | ||
|
|
321a2f5a73 | ||
|
|
2ed8d5d798 | ||
|
|
6dcc0cc188 | ||
|
|
ccd465d669 | ||
|
|
2f622ad72c | ||
|
|
85b2642a5e | ||
|
|
51f11f19cc | ||
|
|
49eedde58a | ||
|
|
afe7872838 | ||
|
|
c1c0432c00 | ||
|
|
1a9dd48a88 | ||
|
|
8ee418405b | ||
|
|
cf29cde2e8 | ||
|
|
522a549d61 | ||
|
|
d2e7797485 | ||
|
|
1c81f8d8d5 | ||
|
|
00f323c8bd | ||
|
|
5d43c431c0 | ||
|
|
c68ac460c9 | ||
|
|
d37a10e4f1 | ||
|
|
59eb81b50a | ||
|
|
f5579d59f8 | ||
|
|
9f722a7e12 | ||
|
|
f2fded6486 | ||
|
|
5e023bf619 | ||
|
|
82f3c2f6d5 | ||
|
|
d9dc8e38f9 | ||
|
|
910a1f872d | ||
|
|
540a2a623e | ||
|
|
08e3431107 | ||
|
|
748637c2d8 | ||
|
|
e34d72aee9 | ||
|
|
64f58061b7 | ||
|
|
574d6d6119 | ||
|
|
b5147bbfb0 | ||
|
|
4789431d6e | ||
|
|
d588ee375b | ||
|
|
2c500142c7 | ||
|
|
b0c86d8e51 | ||
|
|
8e4190d84a | ||
|
|
b579e2e7ed | ||
|
|
27efb0d843 | ||
|
|
be168901d6 | ||
|
|
2bbd37f9ab | ||
|
|
29ccac8e93 | ||
|
|
3eea3b2f45 | ||
|
|
6b35098fb7 | ||
|
|
0a3295ef3f | ||
|
|
ae4f4d6df4 | ||
|
|
f7a1a4a9b7 | ||
|
|
38e906f854 | ||
|
|
c24302bec2 | ||
|
|
315a3a5ed3 | ||
|
|
38565407c5 | ||
|
|
86c021ef7e | ||
|
|
de652e1e27 | ||
|
|
3b5b121aeb | ||
|
|
fb8559f03a | ||
|
|
f0ac59be25 | ||
|
|
1b77f50fd7 | ||
|
|
4e93429026 | ||
|
|
82d0551215 | ||
|
|
d2140eb4b1 | ||
|
|
204766b967 | ||
|
|
b5e2e1e469 | ||
|
|
6fa2193602 | ||
|
|
cd5c0bec33 | ||
|
|
995efef5da | ||
|
|
5637d573c1 | ||
|
|
b171dc9b7b | ||
|
|
b886157f52 | ||
|
|
82c0449d56 | ||
|
|
6dbc59d5b5 | ||
|
|
23b02f4f27 | ||
|
|
731419fc80 | ||
|
|
96476cb50d | ||
|
|
5bb1319b0f | ||
|
|
407e4cdd07 | ||
|
|
7972db68bc | ||
|
|
8897f5bccc | ||
|
|
7dca1b4b06 | ||
|
|
5b31da44e1 | ||
|
|
012beebe3b | ||
|
|
e7ec5a69a3 | ||
|
|
bf8084080b | ||
|
|
ed1fe1447b | ||
|
|
a07c9ffbc5 | ||
|
|
8053b72901 | ||
|
|
80777b8c50 | ||
|
|
025cfe4064 | ||
|
|
27755d1551 | ||
|
|
9eed12af23 | ||
|
|
6b9cab23d4 | ||
|
|
fca249a62e | ||
|
|
64fb98e46f | ||
|
|
17244734d0 | ||
|
|
8f701cf1cb | ||
|
|
07c790a430 | ||
|
|
19db8e5d82 | ||
|
|
6cb69c907d | ||
|
|
8422df1c43 | ||
|
|
f31a7cf921 | ||
|
|
52f3a48638 | ||
|
|
e66a3c4d83 | ||
|
|
7c4b76b08b | ||
|
|
7883bff99e | ||
|
|
ef9b503f69 | ||
|
|
faca4b5b56 | ||
|
|
47d61e0b4d | ||
|
|
71a6b09bad | ||
|
|
07e4367151 | ||
|
|
1c135bd207 | ||
|
|
17e7f04adf | ||
|
|
bf332fa5e7 | ||
|
|
a9ba964be4 | ||
|
|
243b94b54a | ||
|
|
59e9d0828b | ||
|
|
d893768726 | ||
|
|
889eea92c2 | ||
|
|
16d7dc0853 | ||
|
|
83423854d2 | ||
|
|
3d59935156 | ||
|
|
fea755ecc8 | ||
|
|
8fb5c37ba8 | ||
|
|
995b7327fe | ||
|
|
21b51b48eb | ||
|
|
6bffb11749 | ||
|
|
6884db120a | ||
|
|
90c6771dd1 | ||
|
|
6e486d4347 | ||
|
|
5c8ef15d6f | ||
|
|
65f242cabe | ||
|
|
4c7cdc6245 | ||
|
|
c2d843f96b | ||
|
|
9d2b04928d | ||
|
|
6e7c7c245b | ||
|
|
a0d7043615 | ||
|
|
dea9adbad4 | ||
|
|
2b24870a3d | ||
|
|
15c58dee5f | ||
|
|
79d8444b94 | ||
|
|
ff4baf096f | ||
|
|
1c0494ec53 | ||
|
|
93e3c72c6a | ||
|
|
edfcc0cd6d | ||
|
|
5a57844dab | ||
|
|
d211decfb4 | ||
|
|
04f1fe523a | ||
|
|
c0aae3d68e | ||
|
|
f3f8f35069 | ||
|
|
a11756bad1 | ||
|
|
50c85f6e5b | ||
|
|
c4cb410970 | ||
|
|
1c7cae4620 | ||
|
|
d17383d98c | ||
|
|
2e20abca90 | ||
|
|
294f34bf07 | ||
|
|
a317f2bfe2 | ||
|
|
2653458a39 | ||
|
|
f8386e753a | ||
|
|
55090ecb65 | ||
|
|
bb716ddb80 | ||
|
|
95f994a82b | ||
|
|
7d674e7cdc | ||
|
|
7aca35d52b | ||
|
|
c49e9e8503 | ||
|
|
42a438cdcb | ||
|
|
f5ddbd6abb | ||
|
|
691188bc48 | ||
|
|
a453405365 | ||
|
|
2ae0c7e115 | ||
|
|
d3e06ee290 | ||
|
|
fef03a0806 | ||
|
|
194b754880 | ||
|
|
5e35785fd0 | ||
|
|
309807796c | ||
|
|
b80829a3a0 | ||
|
|
f5069ffc1f | ||
|
|
42411fd455 | ||
|
|
d9e4aafe3a | ||
|
|
5194108233 | ||
|
|
cd700dfe11 | ||
|
|
b2dd29ff05 | ||
|
|
fb670325d8 | ||
|
|
95e65347ca | ||
|
|
24be481574 | ||
|
|
ef72e222b0 | ||
|
|
cef7224739 | ||
|
|
a245977075 | ||
|
|
6e98c67869 | ||
|
|
26908ea281 | ||
|
|
d2bbb61885 | ||
|
|
7bf0e7ccc9 | ||
|
|
3f203eabec | ||
|
|
fae5a9a033 | ||
|
|
7a3beac494 | ||
|
|
e6c5975ed9 | ||
|
|
e7a48b4c98 | ||
|
|
2d578c1a73 | ||
|
|
73b171eb2b | ||
|
|
01307e1255 | ||
|
|
c145678323 | ||
|
|
1b6a50147a | ||
|
|
1e01049966 | ||
|
|
60e326b160 | ||
|
|
0e3e849ead | ||
|
|
aa633412f4 | ||
|
|
b789534b6c | ||
|
|
31ab22e3a0 | ||
|
|
155c1463ce | ||
|
|
8af2138ade | ||
|
|
f1ebaf1ae1 | ||
|
|
352d1a7e8c | ||
|
|
98923cee94 | ||
|
|
6869ad48e4 | ||
|
|
ae0d82efd8 | ||
|
|
65021e6ed9 | ||
|
|
880548bafc | ||
|
|
6bb54f07bf | ||
|
|
f54480b7c8 | ||
|
|
315ceb57e9 | ||
|
|
548ff47f03 | ||
|
|
f7ae4e894f | ||
|
|
1f4bd00993 | ||
|
|
25f0a13e15 | ||
|
|
c4dac31895 | ||
|
|
4d99cd1b7a | ||
|
|
0fa05d47e3 | ||
|
|
50a3c0d725 | ||
|
|
f388703a3d | ||
|
|
2976daa8eb | ||
|
|
c374a5301e | ||
|
|
cd6d00e760 | ||
|
|
a6b8ef310a | ||
|
|
35060659ee | ||
|
|
68504c097c | ||
|
|
b4382855fa | ||
|
|
6289ae329b | ||
|
|
c6c4975aa6 | ||
|
|
b7d0d28ef9 | ||
|
|
d64fd62194 | ||
|
|
eb9bee23a0 | ||
|
|
d47b3265c4 | ||
|
|
cd0d09d806 | ||
|
|
ef8e52a4b0 | ||
|
|
4ac9c875f1 | ||
|
|
e7264fb495 | ||
|
|
8acc09b551 | ||
|
|
1ea44374a4 | ||
|
|
c71bd4cad9 | ||
|
|
0a0cfc34e7 | ||
|
|
5a4d188255 | ||
|
|
d71eeb4c95 | ||
|
|
8f24b0b815 | ||
|
|
b1b3487028 | ||
|
|
38b8cf4fd4 | ||
|
|
3dfe18b565 | ||
|
|
7c1bfdbf41 | ||
|
|
4a6de3e444 | ||
|
|
bd129ede42 | ||
|
|
a0a742eb82 | ||
|
|
94c5d53192 | ||
|
|
22c4d975ad | ||
|
|
7f45e320d8 | ||
|
|
210d8529b6 | ||
|
|
91421528df | ||
|
|
cd8c40e063 | ||
|
|
4e29ff1d6e | ||
|
|
6ae10c5171 | ||
|
|
e31521bd14 | ||
|
|
86e2333e67 | ||
|
|
b189e5b365 | ||
|
|
3e1d49ad80 | ||
|
|
d958a62bf2 | ||
|
|
8f456295e0 | ||
|
|
cddc9db690 | ||
|
|
0bfe502bb0 | ||
|
|
f6bc88471a | ||
|
|
6620ba8cc8 | ||
|
|
b8c11de89c | ||
|
|
45e2a13c37 | ||
|
|
1ce28540fb | ||
|
|
b4e3554af7 | ||
|
|
bb3aa9e908 | ||
|
|
d6aad13a98 | ||
|
|
76db5f22b3 | ||
|
|
b33f5925bb | ||
|
|
e0bd210797 | ||
|
|
9eafee365f | ||
|
|
e222807693 | ||
|
|
4e7e70f981 | ||
|
|
fe0ae6bf0b | ||
|
|
ad663533c7 | ||
|
|
67fb56deb8 | ||
|
|
f618d53302 | ||
|
|
90d471b486 | ||
|
|
6dee013baa | ||
|
|
d53d275bba | ||
|
|
75940dc8b1 | ||
|
|
635c202ced | ||
|
|
6103c577b6 | ||
|
|
55c4643b20 | ||
|
|
cbf4197575 | ||
|
|
0f2cb440b0 | ||
|
|
5517cfa6c0 | ||
|
|
207ba86d51 | ||
|
|
3d04b267ef | ||
|
|
2f4cf592a7 | ||
|
|
ce8a20cfd1 | ||
|
|
758cb8b412 | ||
|
|
56f5214782 | ||
|
|
0963b6f221 | ||
|
|
ab276fc5d8 | ||
|
|
bfbe5bdfb8 | ||
|
|
ba56565125 | ||
|
|
e12e86b520 | ||
|
|
d165c4963d | ||
|
|
22eb619235 | ||
|
|
71f5c8aa88 | ||
|
|
045e6ef148 | ||
|
|
3d0c9c4642 | ||
|
|
594b7efb84 | ||
|
|
da5730706d | ||
|
|
b885249d9d | ||
|
|
e2e3667698 | ||
|
|
dab7970087 | ||
|
|
14a19d23a6 | ||
|
|
67257671ea | ||
|
|
d20d1e5e75 | ||
|
|
13f4a0e284 | ||
|
|
35a62018e4 | ||
|
|
1bd2dd0a6e | ||
|
|
eff763d127 | ||
|
|
5259d4af63 | ||
|
|
0a98559fcb | ||
|
|
346dd864b5 | ||
|
|
c5184d37e7 | ||
|
|
375403fb9d | ||
|
|
0eae638a93 | ||
|
|
84ce23249f | ||
|
|
6f807e9d43 | ||
|
|
75cd7a9ebc | ||
|
|
a710b723d1 | ||
|
|
cd24ec88d6 | ||
|
|
50291c7b7c | ||
|
|
05a943c9b5 | ||
|
|
5402f047bf | ||
|
|
2241252202 | ||
|
|
1d4b2ccab4 | ||
|
|
f375b0cc1b | ||
|
|
de5ffd5cfa | ||
|
|
d0cf709d2e | ||
|
|
91edeacb9f | ||
|
|
7eaef0cd3d | ||
|
|
b471926030 | ||
|
|
47702b9e14 | ||
|
|
083a3bae6e | ||
|
|
5c5ec8f66a | ||
|
|
1731d39119 | ||
|
|
818f02826c | ||
|
|
d9f8420c86 | ||
|
|
b63d0892ab | ||
|
|
309ab772da | ||
|
|
804d131d3b | ||
|
|
40eb422524 | ||
|
|
3006551eb1 | ||
|
|
5c7f7328ff | ||
|
|
c19ab7bc85 | ||
|
|
8957437a4c | ||
|
|
d63f161f06 | ||
|
|
c9aef4ac9f | ||
|
|
6ccfb4b4ba | ||
|
|
628230f14c | ||
|
|
569da2da60 | ||
|
|
c2b64d4545 | ||
|
|
f05da69392 | ||
|
|
ae408290dd | ||
|
|
20b9c60d58 | ||
|
|
c779b8f711 | ||
|
|
0d7b27d4e4 | ||
|
|
1142b77f8d | ||
|
|
c5b4e87f6d | ||
|
|
e8fe0b0213 | ||
|
|
607639c100 | ||
|
|
d7aea228ce | ||
|
|
e5f1fe86e4 | ||
|
|
544de5232c | ||
|
|
86e045916d | ||
|
|
9048d5d79b | ||
|
|
5bcb9b285a | ||
|
|
d7313082c9 | ||
|
|
9d17fae00c | ||
|
|
c0085cbb1a | ||
|
|
63a5f8965e | ||
|
|
3b31b50983 | ||
|
|
442c4e9a4e | ||
|
|
b075c55a60 | ||
|
|
136b6db2ad | ||
|
|
f3f7a89ef8 | ||
|
|
0def31a2ce | ||
|
|
bf023b0aed | ||
|
|
52cf27653f | ||
|
|
a3a68fe83d | ||
|
|
d2c458c066 | ||
|
|
cf7ebe2fa8 | ||
|
|
984124b3b5 | ||
|
|
48290c95a7 | ||
|
|
9507dc15fd | ||
|
|
c66569bd43 | ||
|
|
bc6f0c1622 | ||
|
|
1ee04dc020 | ||
|
|
cd770e0906 | ||
|
|
7c1435b7c6 | ||
|
|
3cb31ef030 | ||
|
|
3304e9f422 | ||
|
|
52ebf66d21 | ||
|
|
1576ee9410 | ||
|
|
8cc66172c3 | ||
|
|
52a117aaf5 | ||
|
|
63c139fdbe | ||
|
|
0f87eb45db | ||
|
|
93cce0f4c2 | ||
|
|
13bbee73d8 | ||
|
|
931173350f | ||
|
|
36e7235493 | ||
|
|
c89c449a2c | ||
|
|
d3cccca7f1 | ||
|
|
9a25de3ef1 | ||
|
|
378206ae7d | ||
|
|
f0d9dabca2 | ||
|
|
9b3c4e8db2 | ||
|
|
7541b01a86 | ||
|
|
58a87396ba | ||
|
|
4233c91a7e | ||
|
|
718663415b | ||
|
|
63334764d7 | ||
|
|
915d680fcc | ||
|
|
67e9841bf3 | ||
|
|
0e59257442 | ||
|
|
f3dca95958 | ||
|
|
c3dc9672f7 | ||
|
|
e0c8a8ecff | ||
|
|
5898615f5a | ||
|
|
1dc6640bc1 | ||
|
|
229c3e6e8f | ||
|
|
97db2c91f1 | ||
|
|
9780990836 | ||
|
|
434c5ea188 | ||
|
|
114d337210 | ||
|
|
0624324962 | ||
|
|
0cd50aac40 | ||
|
|
ae40b0aba2 | ||
|
|
4580b55673 | ||
|
|
210644e87d | ||
|
|
15121931b4 | ||
|
|
1c679378e7 | ||
|
|
99de397a5f | ||
|
|
7b0269c999 | ||
|
|
72c46c662c | ||
|
|
963407de4c | ||
|
|
cf7cd2b470 | ||
|
|
545ad0179b | ||
|
|
03ae2821c3 | ||
|
|
349a10c013 | ||
|
|
96e04e7f63 | ||
|
|
e97aee5d9d | ||
|
|
410609fed4 | ||
|
|
edb6325117 | ||
|
|
aa5c893d5e | ||
|
|
97bd91ed19 | ||
|
|
28bf0c9e03 | ||
|
|
ace7146164 | ||
|
|
8694119c3c | ||
|
|
4e67ec19d0 | ||
|
|
f8442ccb0e | ||
|
|
fef06679e5 | ||
|
|
3d9556e5a3 | ||
|
|
c310948521 | ||
|
|
479a9e4156 | ||
|
|
59b6d657cc | ||
|
|
7e5a9fbe2e | ||
|
|
179941daab | ||
|
|
33ae086861 | ||
|
|
d6ae5c898a | ||
|
|
a9b6a12317 | ||
|
|
85961f5dce | ||
|
|
8ec06d45e0 | ||
|
|
b22ccc114e | ||
|
|
0ac0277639 | ||
|
|
a407f0a4ac | ||
|
|
da4c178534 | ||
|
|
c1e6d4c82a | ||
|
|
c451fa8ad4 | ||
|
|
ea127c3d99 | ||
|
|
b5f849463b | ||
|
|
c06db6b67c | ||
|
|
fb0959bcea | ||
|
|
5f835da838 | ||
|
|
36a6ccba8b | ||
|
|
a76d47681d | ||
|
|
3716d67cc9 | ||
|
|
f291320655 | ||
|
|
d2824413db | ||
|
|
751ffbd9c8 | ||
|
|
7049532227 | ||
|
|
76afc2dcc3 | ||
|
|
30468dd419 | ||
|
|
ffbd201450 | ||
|
|
116d9667e7 | ||
|
|
a32363de79 | ||
|
|
7863bc2c99 | ||
|
|
1e63893411 | ||
|
|
3971cbf294 | ||
|
|
477a32831b | ||
|
|
a879fd519a | ||
|
|
a8effd1961 | ||
|
|
d7e656a32a | ||
|
|
43a63d6373 | ||
|
|
74f02cf855 | ||
|
|
b96540c937 | ||
|
|
91840c613e | ||
|
|
aed5ee4edc | ||
|
|
07578f11d4 | ||
|
|
ab12b6cc2b | ||
|
|
5c15ad412c | ||
|
|
665d40dc4b | ||
|
|
b61f515af2 | ||
|
|
805430983c | ||
|
|
d7aafbfe64 | ||
|
|
9c93402b26 | ||
|
|
157a228088 | ||
|
|
2d309bb8f8 | ||
|
|
b3a3b676ba | ||
|
|
f4e1867d28 | ||
|
|
0abeb831c7 | ||
|
|
663d091776 | ||
|
|
9cb5ff1cdc | ||
|
|
8e25cac653 | ||
|
|
7b6f202f23 | ||
|
|
63e2206d16 | ||
|
|
fc4b9827b9 | ||
|
|
9f7103c4fb | ||
|
|
a8ee878356 | ||
|
|
663112576a | ||
|
|
7a3898168f | ||
|
|
cde05e1190 | ||
|
|
c65780ee99 | ||
|
|
bdb205a318 | ||
|
|
4346a7f426 | ||
|
|
70d2a0df8a | ||
|
|
530b795eaa | ||
|
|
8a94cabdbf | ||
|
|
e99571baae | ||
|
|
dc02fa0386 | ||
|
|
b3d2e759a6 | ||
|
|
a5a459fe0a | ||
|
|
7456f3750d | ||
|
|
0c2ff98dc2 | ||
|
|
d5725255fe | ||
|
|
24e830f91d | ||
|
|
abe4d99e12 | ||
|
|
5a0cce2a18 | ||
|
|
2b0ecec0c8 | ||
|
|
dcfa0b38c1 | ||
|
|
2b996f11cc | ||
|
|
1e732ad4d7 | ||
|
|
d7763f236f | ||
|
|
b6034b4935 | ||
|
|
fb1f22144d | ||
|
|
0879f02db6 | ||
|
|
a423f5f695 | ||
|
|
edddaaa838 | ||
|
|
53b86fd53b | ||
|
|
180c3cee44 | ||
|
|
32847c125a | ||
|
|
8362caa9d9 | ||
|
|
8ec681e61c | ||
|
|
ebdea243b2 | ||
|
|
4607f5990e | ||
|
|
8df7d465cb | ||
|
|
77d98b217e | ||
|
|
d804acdef7 | ||
|
|
9b4b29cab7 | ||
|
|
208a728d39 | ||
|
|
37cdef7ab1 | ||
|
|
afb5dc7da3 | ||
|
|
2822c94aa7 | ||
|
|
6897fb46cb | ||
|
|
3c7f7511be | ||
|
|
e24e3bf13f | ||
|
|
df3dc6fadc | ||
|
|
e6446e501c | ||
|
|
893c8763bb | ||
|
|
9071acea01 | ||
|
|
03737543d4 | ||
|
|
8eeba92a47 | ||
|
|
56e5f01ce0 | ||
|
|
78d49e44b1 | ||
|
|
8d96bfe973 | ||
|
|
50b10be2db | ||
|
|
1c407a28cd | ||
|
|
adf905d838 | ||
|
|
1d57663343 | ||
|
|
32442a33de | ||
|
|
184c903ec7 | ||
|
|
501ea31c25 | ||
|
|
f2897f5bfc | ||
|
|
e4e5291511 | ||
|
|
6464135800 | ||
|
|
fc7c66dab2 | ||
|
|
e67b72d954 | ||
|
|
25354d2dd8 | ||
|
|
6a8b9fde78 | ||
|
|
d6b14a1395 | ||
|
|
2284127650 | ||
|
|
4bbc1dc734 | ||
|
|
cac6e946ab | ||
|
|
7f5df4fc0e | ||
|
|
c6835cd270 | ||
|
|
4f8ef13cd8 | ||
|
|
17218fa663 | ||
|
|
42259ef8d1 | ||
|
|
443d0f50c1 | ||
|
|
c9a600d496 | ||
|
|
4e4ee32dbc | ||
|
|
57f689401e | ||
|
|
2d92cee26a | ||
|
|
95f35196e4 | ||
|
|
5b8b9044a5 | ||
|
|
03b8e649f1 | ||
|
|
357c823b92 | ||
|
|
d03817ce2d | ||
|
|
277b5b483d | ||
|
|
950c4c811c | ||
|
|
9ee36215bd | ||
|
|
ee0811df26 | ||
|
|
f9215ec5ca | ||
|
|
72999c7af1 | ||
|
|
3c3442d8f0 | ||
|
|
abf0c0f296 | ||
|
|
5a51d718c6 | ||
|
|
91198524cd | ||
|
|
dcd082e955 | ||
|
|
807f87e01f | ||
|
|
40c2b3c43b | ||
|
|
1c910550e6 | ||
|
|
22316ee4fe | ||
|
|
2e3413c9b8 | ||
|
|
093ff4061d | ||
|
|
7644ecad52 | ||
|
|
0a91ee1019 | ||
|
|
df7f0cf9a9 | ||
|
|
c06b8a68e5 | ||
|
|
c663da5be6 | ||
|
|
4bd7e24b5f | ||
|
|
605c7113a2 | ||
|
|
e2240abc78 | ||
|
|
545c2f67e9 | ||
|
|
d345cec339 | ||
|
|
d113fb23c8 | ||
|
|
24c413fbf9 | ||
|
|
7d45ca6293 | ||
|
|
8f02463411 | ||
|
|
a2ac1384cb | ||
|
|
6b5cd9abc3 | ||
|
|
f9b775e4b8 | ||
|
|
ce2ba21240 | ||
|
|
414f18fc97 | ||
|
|
1c9545e49a | ||
|
|
256d8547c1 | ||
|
|
99e70e9a50 | ||
|
|
a3a3b46d54 | ||
|
|
5adf10fcba | ||
|
|
ca17c5b053 | ||
|
|
8d22fd25f1 | ||
|
|
db231a111c | ||
|
|
8a27660615 | ||
|
|
69ecbda133 | ||
|
|
7496b61b8d | ||
|
|
376d4e03a1 | ||
|
|
5deb16e58c | ||
|
|
2780d9a9a3 | ||
|
|
4e88b8453a | ||
|
|
f24fa402f3 | ||
|
|
4a1382925e | ||
|
|
ccbf1ca2a9 | ||
|
|
38c40a7192 | ||
|
|
8fee9cb0d5 | ||
|
|
c2035e85d2 | ||
|
|
0fd013f9fd | ||
|
|
eab270eb84 | ||
|
|
fc56c5a022 | ||
|
|
c8630bbe4f | ||
|
|
6fb014b34d | ||
|
|
13cb0ab554 | ||
|
|
2ecdfd1ff6 | ||
|
|
fedb98ddb5 | ||
|
|
cf4e37a0ab | ||
|
|
cb632b3534 | ||
|
|
e81c62e402 | ||
|
|
7c1fb5d4a6 | ||
|
|
4eee375296 | ||
|
|
13d6deb9c4 | ||
|
|
70075e2832 | ||
|
|
3c46b0cad1 | ||
|
|
fa86e75330 | ||
|
|
1b6317d584 | ||
|
|
e4dbf0acff | ||
|
|
39938b4dad | ||
|
|
29cf695b07 | ||
|
|
e18ceba49e | ||
|
|
031a910989 | ||
|
|
4a2472a078 | ||
|
|
e928777cb7 | ||
|
|
403f3caf4b | ||
|
|
ae5689b295 | ||
|
|
4c806a442a | ||
|
|
52d5578fb5 | ||
|
|
2dbb891942 | ||
|
|
9b38e1102a | ||
|
|
65c9d8cb78 | ||
|
|
a293239bd5 | ||
|
|
279ba60eb1 | ||
|
|
1132572620 | ||
|
|
a7e7334f0f | ||
|
|
55fad8ab23 | ||
|
|
7e5970f338 | ||
|
|
c660ea100b | ||
|
|
b39e2ef71c | ||
|
|
dd519cc9bf | ||
|
|
b4cd1ee34d | ||
|
|
7712ec2523 | ||
|
|
4d3a837310 | ||
|
|
2b4217b8a4 | ||
|
|
b16cecc8db | ||
|
|
62ea1f0a05 | ||
|
|
e25e192ef3 | ||
|
|
a8b0d298ff | ||
|
|
ad7b5ae7ed | ||
|
|
f1b63c4df3 | ||
|
|
0ab88c2e29 | ||
|
|
87944a3a75 | ||
|
|
25ceeaf241 | ||
|
|
4bc9096446 | ||
|
|
d865f2ecf5 | ||
|
|
e122f94c1c | ||
|
|
74ee101592 | ||
|
|
966be2727e | ||
|
|
3507cdc796 | ||
|
|
906f2f5e0f | ||
|
|
631b8fed30 | ||
|
|
bb0b0801dd | ||
|
|
5d288d321a |
2
.github/ISSUE_TEMPLATE/ql---general.md
vendored
2
.github/ISSUE_TEMPLATE/ql---general.md
vendored
@@ -10,5 +10,5 @@ assignees: ''
|
||||
**Description of the issue**
|
||||
|
||||
<!-- Please explain briefly what is the problem.
|
||||
If it is about an LGTM project, please include its URL.-->
|
||||
If it is about a GitHub project, please include its URL. -->
|
||||
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/ql--false-positive.md
vendored
2
.github/ISSUE_TEMPLATE/ql--false-positive.md
vendored
@@ -1,5 +1,5 @@
|
||||
---
|
||||
name: CodeQL False positive
|
||||
name: CodeQL false positive
|
||||
about: Report CodeQL alerts that you think should not have been detected (not applicable, not exploitable, etc.)
|
||||
title: False positive
|
||||
labels: false-positive
|
||||
|
||||
@@ -14,8 +14,7 @@ outputs:
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
# Cache the query compilation caches.
|
||||
# calculate the merge-base with main, in a way that works both on PRs and pushes to main.
|
||||
# calculate the merge-base with main, in a way that works both on PRs and pushes to main.
|
||||
- name: Calculate merge-base
|
||||
shell: bash
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
@@ -24,38 +23,33 @@ runs:
|
||||
run: |
|
||||
MERGE_BASE=$(git cat-file commit $GITHUB_SHA | grep '^parent ' | head -1 | cut -f 2 -d " ")
|
||||
echo "merge_base=$MERGE_BASE" >> $GITHUB_ENV
|
||||
- name: Read CodeQL query compilation - PR
|
||||
- name: Restore read-only cache (PR)
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
uses: erik-krogh/actions-cache@a88d0603fe5fb5606db9f002dfcadeb32b5f84c6
|
||||
with:
|
||||
path: '**/.cache'
|
||||
read-only: true
|
||||
key: codeql-compile-${{ inputs.key }}-pr-${{ github.sha }} # deliberately not using the `compile-compile-main` keys here.
|
||||
key: codeql-compile-${{ inputs.key }}-pr-${{ github.sha }}
|
||||
restore-keys: |
|
||||
codeql-compile-${{ inputs.key }}-${{ github.base_ref }}-${{ env.merge_base }}
|
||||
codeql-compile-${{ inputs.key }}-${{ github.base_ref }}-
|
||||
codeql-compile-${{ inputs.key }}-main-
|
||||
- name: Fill CodeQL query compilation cache - main
|
||||
- name: Fill cache (push)
|
||||
if: ${{ github.event_name != 'pull_request' }}
|
||||
uses: erik-krogh/actions-cache@a88d0603fe5fb5606db9f002dfcadeb32b5f84c6
|
||||
with:
|
||||
path: '**/.cache'
|
||||
key: codeql-compile-${{ inputs.key }}-${{ github.ref_name }}-${{ github.sha }} # just fill on main
|
||||
restore-keys: | # restore from another random commit, to speed up compilation.
|
||||
codeql-compile-${{ inputs.key }}-${{ github.ref_name }}-
|
||||
restore-keys: | # restore the latest cache if the exact cache is unavailable, to speed up compilation.
|
||||
codeql-compile-${{ inputs.key }}-${{ github.ref_name }}-
|
||||
codeql-compile-${{ inputs.key }}-main-
|
||||
- name: Fill compilation cache directory
|
||||
id: fill-compilation-dir
|
||||
shell: bash
|
||||
shell: bash
|
||||
run: |
|
||||
# Move all the existing cache into another folder, so we only preserve the cache for the current queries.
|
||||
mkdir -p ${COMBINED_CACHE_DIR}
|
||||
rm -f **/.cache/{lock,size} # -f to avoid errors if the cache is empty.
|
||||
# copy the contents of the .cache folders into the combined cache folder.
|
||||
cp -r **/.cache/* ${COMBINED_CACHE_DIR}/ || : # ignore missing files
|
||||
# clean up the .cache folders
|
||||
rm -rf **/.cache/*
|
||||
node $GITHUB_WORKSPACE/.github/actions/cache-query-compilation/move-caches.js ${COMBINED_CACHE_DIR}
|
||||
|
||||
echo "compdir=${COMBINED_CACHE_DIR}" >> $GITHUB_OUTPUT
|
||||
env:
|
||||
COMBINED_CACHE_DIR: ${{ github.workspace }}/compilation-dir
|
||||
env:
|
||||
COMBINED_CACHE_DIR: ${{ runner.temp }}/compilation-dir
|
||||
|
||||
75
.github/actions/cache-query-compilation/move-caches.js
vendored
Normal file
75
.github/actions/cache-query-compilation/move-caches.js
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
// # Move all the existing cache into another folder, so we only preserve the cache for the current queries.
|
||||
// mkdir -p ${COMBINED_CACHE_DIR}
|
||||
// rm -f **/.cache/{lock,size} # -f to avoid errors if the cache is empty.
|
||||
// # copy the contents of the .cache folders into the combined cache folder.
|
||||
// cp -r **/.cache/* ${COMBINED_CACHE_DIR}/ || : # ignore missing files
|
||||
// # clean up the .cache folders
|
||||
// rm -rf **/.cache/*
|
||||
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
// the first argv is the cache folder to create.
|
||||
const COMBINED_CACHE_DIR = process.argv[2];
|
||||
|
||||
function* walkCaches(dir) {
|
||||
const files = fs.readdirSync(dir, { withFileTypes: true });
|
||||
for (const file of files) {
|
||||
if (file.isDirectory()) {
|
||||
const filePath = path.join(dir, file.name);
|
||||
yield* walkCaches(filePath);
|
||||
if (file.name === ".cache") {
|
||||
yield filePath;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function copyDir(src, dest) {
|
||||
for await (const file of await fs.promises.readdir(src, { withFileTypes: true })) {
|
||||
const srcPath = path.join(src, file.name);
|
||||
const destPath = path.join(dest, file.name);
|
||||
if (file.isDirectory()) {
|
||||
if (!fs.existsSync(destPath)) {
|
||||
fs.mkdirSync(destPath);
|
||||
}
|
||||
await copyDir(srcPath, destPath);
|
||||
} else {
|
||||
await fs.promises.copyFile(srcPath, destPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const cacheDirs = [...walkCaches(".")];
|
||||
|
||||
for (const dir of cacheDirs) {
|
||||
console.log(`Found .cache dir at ${dir}`);
|
||||
}
|
||||
|
||||
// mkdir -p ${COMBINED_CACHE_DIR}
|
||||
fs.mkdirSync(COMBINED_CACHE_DIR, { recursive: true });
|
||||
|
||||
// rm -f **/.cache/{lock,size} # -f to avoid errors if the cache is empty.
|
||||
await Promise.all(
|
||||
cacheDirs.map((cacheDir) =>
|
||||
(async function () {
|
||||
await fs.promises.rm(path.join(cacheDir, "lock"), { force: true });
|
||||
await fs.promises.rm(path.join(cacheDir, "size"), { force: true });
|
||||
})()
|
||||
)
|
||||
);
|
||||
|
||||
// # copy the contents of the .cache folders into the combined cache folder.
|
||||
// cp -r **/.cache/* ${COMBINED_CACHE_DIR}/ || : # ignore missing files
|
||||
await Promise.all(
|
||||
cacheDirs.map((cacheDir) => copyDir(cacheDir, COMBINED_CACHE_DIR))
|
||||
);
|
||||
|
||||
// # clean up the .cache folders
|
||||
// rm -rf **/.cache/*
|
||||
await Promise.all(
|
||||
cacheDirs.map((cacheDir) => fs.promises.rm(cacheDir, { recursive: true }))
|
||||
);
|
||||
}
|
||||
main();
|
||||
26
.github/actions/find-latest-bundle/action.yml
vendored
Normal file
26
.github/actions/find-latest-bundle/action.yml
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
name: Find Latest CodeQL Bundle
|
||||
description: Finds the URL of the latest released version of the CodeQL bundle.
|
||||
outputs:
|
||||
url:
|
||||
description: The download URL of the latest CodeQL bundle release
|
||||
value: ${{ steps.find-latest.outputs.url }}
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Find Latest Release
|
||||
id: find-latest
|
||||
shell: pwsh
|
||||
run: |
|
||||
$Latest = gh release list --repo github/codeql-action --exclude-drafts --limit 1000 |
|
||||
ForEach-Object { $C = $_ -split "`t"; return @{ type = $C[1]; tag = $C[2]; } } |
|
||||
Where-Object { $_.type -eq 'Latest' }
|
||||
|
||||
$Tag = $Latest.tag
|
||||
if ($Tag -eq '') {
|
||||
throw 'Failed to find latest bundle release.'
|
||||
}
|
||||
|
||||
Write-Output "Latest bundle tag is '${Tag}'."
|
||||
"url=https://github.com/github/codeql-action/releases/download/${Tag}/codeql-bundle-linux64.tar.gz" >> $env:GITHUB_OUTPUT
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
2
.github/workflows/compile-queries.yml
vendored
2
.github/workflows/compile-queries.yml
vendored
@@ -35,5 +35,3 @@ jobs:
|
||||
if : ${{ github.event_name != 'pull_request' }}
|
||||
shell: bash
|
||||
run: codeql query compile -j0 */ql/{src,examples} --keep-going --warnings=error --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}"
|
||||
env:
|
||||
COMBINED_CACHE_DIR: ${{ github.workspace }}/compilation-dir
|
||||
2
.github/workflows/csharp-qltest.yml
vendored
2
.github/workflows/csharp-qltest.yml
vendored
@@ -67,7 +67,7 @@ jobs:
|
||||
mv "$CODEQL_PATH/csharp/tools/extractor-asp.jar" "${{ github.workspace }}/csharp/extractor-pack/tools"
|
||||
# Safe guard against using the bundled extractor
|
||||
rm -rf "$CODEQL_PATH/csharp"
|
||||
codeql test run --threads=0 --ram 52000 --slice ${{ matrix.slice }} --search-path "${{ github.workspace }}/csharp/extractor-pack" --check-databases --check-undefined-labels --check-repeated-labels --check-redefined-labels --consistency-queries ql/consistency-queries ql/test --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}"
|
||||
codeql test run --threads=0 --ram 50000 --slice ${{ matrix.slice }} --search-path "${{ github.workspace }}/csharp/extractor-pack" --check-databases --check-undefined-labels --check-repeated-labels --check-redefined-labels --consistency-queries ql/consistency-queries ql/test --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}"
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
unit-tests:
|
||||
|
||||
80
.github/workflows/go-tests-other-os.yml
vendored
Normal file
80
.github/workflows/go-tests-other-os.yml
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
name: "Go: Run Tests - Other OS"
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- "go/**"
|
||||
- "!go/ql/**" # don't run other-os if only ql/ files changed
|
||||
- .github/workflows/go-tests-other-os.yml
|
||||
- .github/actions/**
|
||||
- codeql-workspace.yml
|
||||
jobs:
|
||||
test-mac:
|
||||
name: Test MacOS
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- name: Set up Go 1.19
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.19
|
||||
id: go
|
||||
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set up CodeQL CLI
|
||||
uses: ./.github/actions/fetch-codeql
|
||||
|
||||
- name: Enable problem matchers in repository
|
||||
shell: bash
|
||||
run: 'find .github/problem-matchers -name \*.json -exec echo "::add-matcher::{}" \;'
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
cd go
|
||||
make
|
||||
|
||||
- name: Cache compilation cache
|
||||
id: query-cache
|
||||
uses: ./.github/actions/cache-query-compilation
|
||||
with:
|
||||
key: go-qltest
|
||||
- name: Test
|
||||
run: |
|
||||
cd go
|
||||
make test cache="${{ steps.query-cache.outputs.cache-dir }}"
|
||||
|
||||
test-win:
|
||||
name: Test Windows
|
||||
runs-on: windows-latest-xl
|
||||
steps:
|
||||
- name: Set up Go 1.19
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.19
|
||||
id: go
|
||||
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set up CodeQL CLI
|
||||
uses: ./.github/actions/fetch-codeql
|
||||
|
||||
- name: Enable problem matchers in repository
|
||||
shell: bash
|
||||
run: 'find .github/problem-matchers -name \*.json -exec echo "::add-matcher::{}" \;'
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
cd go
|
||||
make
|
||||
|
||||
- name: Cache compilation cache
|
||||
id: query-cache
|
||||
uses: ./.github/actions/cache-query-compilation
|
||||
with:
|
||||
key: go-qltest
|
||||
|
||||
- name: Test
|
||||
run: |
|
||||
cd go
|
||||
make test cache="${{ steps.query-cache.outputs.cache-dir }}"
|
||||
79
.github/workflows/go-tests.yml
vendored
79
.github/workflows/go-tests.yml
vendored
@@ -1,15 +1,24 @@
|
||||
name: "Go: Run Tests"
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- "go/**"
|
||||
- .github/workflows/go-tests.yml
|
||||
- .github/actions/**
|
||||
- codeql-workspace.yml
|
||||
branches:
|
||||
- main
|
||||
- "rc/*"
|
||||
pull_request:
|
||||
paths:
|
||||
- "go/**"
|
||||
- .github/workflows/go-tests.yml
|
||||
- .github/actions/fetch-codeql/action.yml
|
||||
- .github/actions/**
|
||||
- codeql-workspace.yml
|
||||
jobs:
|
||||
test-linux:
|
||||
name: Test Linux (Ubuntu)
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-latest-xl
|
||||
steps:
|
||||
- name: Set up Go 1.19
|
||||
uses: actions/setup-go@v3
|
||||
@@ -32,7 +41,7 @@ jobs:
|
||||
cd go
|
||||
make
|
||||
|
||||
- name: Check that all QL and Go code is autoformatted
|
||||
- name: Check that all Go code is autoformatted
|
||||
run: |
|
||||
cd go
|
||||
make check-formatting
|
||||
@@ -48,67 +57,13 @@ jobs:
|
||||
name: qhelp-markdown
|
||||
path: go/qhelp-out/**/*.md
|
||||
|
||||
- name: Test
|
||||
run: |
|
||||
cd go
|
||||
make test
|
||||
|
||||
test-mac:
|
||||
name: Test MacOS
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- name: Set up Go 1.19
|
||||
uses: actions/setup-go@v3
|
||||
- name: Cache compilation cache
|
||||
id: query-cache
|
||||
uses: ./.github/actions/cache-query-compilation
|
||||
with:
|
||||
go-version: 1.19
|
||||
id: go
|
||||
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set up CodeQL CLI
|
||||
uses: ./.github/actions/fetch-codeql
|
||||
|
||||
- name: Enable problem matchers in repository
|
||||
shell: bash
|
||||
run: 'find .github/problem-matchers -name \*.json -exec echo "::add-matcher::{}" \;'
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
cd go
|
||||
make
|
||||
key: go-qltest
|
||||
|
||||
- name: Test
|
||||
run: |
|
||||
cd go
|
||||
make test
|
||||
|
||||
test-win:
|
||||
name: Test Windows
|
||||
runs-on: windows-2019
|
||||
steps:
|
||||
- name: Set up Go 1.19
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.19
|
||||
id: go
|
||||
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set up CodeQL CLI
|
||||
uses: ./.github/actions/fetch-codeql
|
||||
|
||||
- name: Enable problem matchers in repository
|
||||
shell: bash
|
||||
run: 'find .github/problem-matchers -name \*.json -exec echo "::add-matcher::{}" \;'
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
cd go
|
||||
make
|
||||
|
||||
- name: Test
|
||||
run: |
|
||||
cd go
|
||||
make test
|
||||
make test cache="${{ steps.query-cache.outputs.cache-dir }}"
|
||||
|
||||
33
.github/workflows/js-ml-tests.yml
vendored
33
.github/workflows/js-ml-tests.yml
vendored
@@ -23,9 +23,9 @@ defaults:
|
||||
working-directory: javascript/ql/experimental/adaptivethreatmodeling
|
||||
|
||||
jobs:
|
||||
qlcompile:
|
||||
name: Check QL compilation
|
||||
runs-on: ubuntu-latest
|
||||
qltest:
|
||||
name: Test QL
|
||||
runs-on: ubuntu-latest-xl
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
@@ -33,36 +33,33 @@ jobs:
|
||||
|
||||
- name: Install pack dependencies
|
||||
run: |
|
||||
for pack in modelbuilding src; do
|
||||
for pack in modelbuilding src test; do
|
||||
codeql pack install --mode verify -- "${pack}"
|
||||
done
|
||||
|
||||
- name: Cache compilation cache
|
||||
id: query-cache
|
||||
uses: ./.github/actions/cache-query-compilation
|
||||
with:
|
||||
key: js-ml-test
|
||||
|
||||
- name: Check QL compilation
|
||||
run: |
|
||||
codeql query compile \
|
||||
--check-only \
|
||||
--ram 5120 \
|
||||
--ram 50000 \
|
||||
--additional-packs "${{ github.workspace }}" \
|
||||
--threads=0 \
|
||||
--compilation-cache "${{ steps.query-cache.outputs.cache-dir }}" \
|
||||
-- \
|
||||
lib modelbuilding src
|
||||
|
||||
qltest:
|
||||
name: Run QL tests
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: ./.github/actions/fetch-codeql
|
||||
|
||||
- name: Install pack dependencies
|
||||
run: codeql pack install -- test
|
||||
|
||||
- name: Run QL tests
|
||||
run: |
|
||||
codeql test run \
|
||||
--threads=0 \
|
||||
--ram 5120 \
|
||||
--ram 50000 \
|
||||
--additional-packs "${{ github.workspace }}" \
|
||||
--compilation-cache "${{ steps.query-cache.outputs.cache-dir }}" \
|
||||
-- \
|
||||
test
|
||||
test
|
||||
10
.github/workflows/mad_modelDiff.yml
vendored
10
.github/workflows/mad_modelDiff.yml
vendored
@@ -61,8 +61,8 @@ jobs:
|
||||
DATABASE=$2
|
||||
cd codeql-$QL_VARIANT
|
||||
SHORTNAME=`basename $DATABASE`
|
||||
python java/ql/src/utils/model-generator/GenerateFlowModel.py --with-summaries --with-sinks $DATABASE $MODELS/${SHORTNAME}.qll
|
||||
mv $MODELS/${SHORTNAME}.qll $MODELS/${SHORTNAME}Generated_${QL_VARIANT}.qll
|
||||
python java/ql/src/utils/model-generator/GenerateFlowModel.py --with-summaries --with-sinks $DATABASE ${SHORTNAME}.temp.model.yml
|
||||
mv java/ql/lib/ext/generated/${SHORTNAME}.temp.model.yml $MODELS/${SHORTNAME}Generated_${QL_VARIANT}.model.yml
|
||||
cd ..
|
||||
}
|
||||
|
||||
@@ -85,16 +85,16 @@ jobs:
|
||||
set -x
|
||||
MODELS=`pwd`/tmp-models
|
||||
ls -1 tmp-models/
|
||||
for m in $MODELS/*_main.qll ; do
|
||||
for m in $MODELS/*_main.model.yml ; do
|
||||
t="${m/main/"pr"}"
|
||||
basename=`basename $m`
|
||||
name="diff_${basename/_main.qll/""}"
|
||||
name="diff_${basename/_main.model.yml/""}"
|
||||
(diff -w -u $m $t | diff2html -i stdin -F $MODELS/$name.html) || true
|
||||
done
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: models
|
||||
path: tmp-models/*.qll
|
||||
path: tmp-models/*.model.yml
|
||||
retention-days: 20
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
|
||||
2
.github/workflows/mad_regenerate-models.yml
vendored
2
.github/workflows/mad_regenerate-models.yml
vendored
@@ -53,7 +53,7 @@ jobs:
|
||||
java/ql/src/utils/model-generator/RegenerateModels.py "${SLUG}" dbs/${SHORTNAME}
|
||||
- name: Stage changes
|
||||
run: |
|
||||
find java -name "*.qll" -print0 | xargs -0 git add
|
||||
find java -name "*.model.yml" -print0 | xargs -0 git add
|
||||
git status
|
||||
git diff --cached > models.patch
|
||||
- uses: actions/upload-artifact@v3
|
||||
|
||||
5
.github/workflows/ql-for-ql-build.yml
vendored
5
.github/workflows/ql-for-ql-build.yml
vendored
@@ -22,11 +22,15 @@ jobs:
|
||||
steps:
|
||||
### Build the queries ###
|
||||
- uses: actions/checkout@v3
|
||||
- name: Find latest bundle
|
||||
id: find-latest-bundle
|
||||
uses: ./.github/actions/find-latest-bundle
|
||||
- name: Find codeql
|
||||
id: find-codeql
|
||||
uses: github/codeql-action/init@77a8d2d10c0b403a8b4aadbd223dc489ecd22683
|
||||
with:
|
||||
languages: javascript # does not matter
|
||||
tools: ${{ steps.find-latest-bundle.outputs.url }}
|
||||
- name: Get CodeQL version
|
||||
id: get-codeql-version
|
||||
run: |
|
||||
@@ -138,6 +142,7 @@ jobs:
|
||||
languages: ql
|
||||
db-location: ${{ runner.temp }}/db
|
||||
config-file: ./ql-for-ql-config.yml
|
||||
tools: ${{ steps.find-latest-bundle.outputs.url }}
|
||||
- name: Move pack cache
|
||||
run: |
|
||||
cp -r ${PACK}/.cache ql/ql/src/.cache
|
||||
|
||||
18
.github/workflows/ruby-build.yml
vendored
18
.github/workflows/ruby-build.yml
vendored
@@ -48,7 +48,19 @@ jobs:
|
||||
run: |
|
||||
brew install gnu-tar
|
||||
echo "/usr/local/opt/gnu-tar/libexec/gnubin" >> $GITHUB_PATH
|
||||
- name: Cache entire extractor
|
||||
uses: actions/cache@v3
|
||||
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 }}-ruby-extractor-${{ hashFiles('ruby/rust-toolchain.toml', 'ruby/**/Cargo.lock') }}--${{ hashFiles('ruby/**/*.rs') }}
|
||||
- uses: actions/cache@v3
|
||||
if: steps.cache-extractor.outputs.cache-hit != 'true'
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
@@ -56,15 +68,19 @@ jobs:
|
||||
ruby/target
|
||||
key: ${{ runner.os }}-ruby-rust-cargo-${{ hashFiles('ruby/rust-toolchain.toml', 'ruby/**/Cargo.lock') }}
|
||||
- name: Check formatting
|
||||
if: steps.cache-extractor.outputs.cache-hit != 'true'
|
||||
run: cargo fmt --all -- --check
|
||||
- name: Build
|
||||
if: steps.cache-extractor.outputs.cache-hit != 'true'
|
||||
run: cargo build --verbose
|
||||
- name: Run tests
|
||||
if: steps.cache-extractor.outputs.cache-hit != 'true'
|
||||
run: cargo test --verbose
|
||||
- name: Release build
|
||||
if: steps.cache-extractor.outputs.cache-hit != 'true'
|
||||
run: cargo build --release
|
||||
- name: Generate dbscheme
|
||||
if: ${{ matrix.os == 'ubuntu-latest' }}
|
||||
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
|
||||
- uses: actions/upload-artifact@v3
|
||||
if: ${{ matrix.os == 'ubuntu-latest' }}
|
||||
|
||||
2
.github/workflows/ruby-qltest.yml
vendored
2
.github/workflows/ruby-qltest.yml
vendored
@@ -62,6 +62,6 @@ jobs:
|
||||
key: ruby-qltest
|
||||
- name: Run QL tests
|
||||
run: |
|
||||
codeql test run --threads=0 --ram 52000 --search-path "${{ github.workspace }}/ruby/extractor-pack" --check-databases --check-undefined-labels --check-unused-labels --check-repeated-labels --check-redefined-labels --check-use-before-definition --consistency-queries ql/consistency-queries ql/test --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}"
|
||||
codeql test run --threads=0 --ram 50000 --search-path "${{ github.workspace }}/ruby/extractor-pack" --check-databases --check-undefined-labels --check-unused-labels --check-repeated-labels --check-redefined-labels --check-use-before-definition --consistency-queries ql/consistency-queries ql/test --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}"
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
|
||||
75
.github/workflows/swift.yml
vendored
75
.github/workflows/swift.yml
vendored
@@ -7,96 +7,76 @@ on:
|
||||
- "misc/bazel/**"
|
||||
- "*.bazel*"
|
||||
- .github/workflows/swift.yml
|
||||
- .github/actions/fetch-codeql/action.yml
|
||||
- .github/actions/**
|
||||
- codeql-workspace.yml
|
||||
- .pre-commit-config.yaml
|
||||
- "!**/*.md"
|
||||
- "!**/*.qhelp"
|
||||
branches:
|
||||
- main
|
||||
- rc/*
|
||||
push:
|
||||
paths:
|
||||
- "swift/**"
|
||||
- "misc/bazel/**"
|
||||
- "*.bazel*"
|
||||
- .github/workflows/swift.yml
|
||||
- .github/actions/**
|
||||
- codeql-workspace.yml
|
||||
- "!**/*.md"
|
||||
- "!**/*.qhelp"
|
||||
branches:
|
||||
- main
|
||||
- rc/*
|
||||
|
||||
jobs:
|
||||
changes:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
codegen: ${{ steps.filter.outputs.codegen }}
|
||||
ql: ${{ steps.filter.outputs.ql }}
|
||||
steps:
|
||||
- uses: dorny/paths-filter@4512585405083f25c027a35db413c2b3b9006d50
|
||||
id: filter
|
||||
with:
|
||||
filters: |
|
||||
codegen:
|
||||
- '.github/workflows/swift.yml'
|
||||
- "misc/bazel/**"
|
||||
- "*.bazel*"
|
||||
- 'swift/actions/setup-env/**'
|
||||
- '.pre-commit-config.yaml'
|
||||
- 'swift/codegen/**'
|
||||
- 'swift/schema.py'
|
||||
- 'swift/**/*.dbscheme'
|
||||
- 'swift/ql/lib/codeql/swift/elements.qll'
|
||||
- 'swift/ql/lib/codeql/swift/elements/**'
|
||||
- 'swift/ql/lib/codeql/swift/generated/**'
|
||||
- 'swift/ql/test/extractor-tests/generated/**'
|
||||
- 'swift/ql/.generated.list'
|
||||
ql:
|
||||
- 'github/workflows/swift.yml'
|
||||
- 'swift/**/*.ql'
|
||||
- 'swift/**/*.qll'
|
||||
# not using a matrix as you cannot depend on a specific job in a matrix, and we want to start linux checks
|
||||
# without waiting for the macOS build
|
||||
build-and-test-macos:
|
||||
runs-on: macos-12-xl
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./swift/actions/create-extractor-pack
|
||||
- uses: ./swift/actions/run-quick-tests
|
||||
- uses: ./swift/actions/print-unextracted
|
||||
- uses: ./swift/actions/build-and-test
|
||||
build-and-test-linux:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-latest-xl
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./swift/actions/create-extractor-pack
|
||||
- uses: ./swift/actions/run-quick-tests
|
||||
- uses: ./swift/actions/print-unextracted
|
||||
- uses: ./swift/actions/build-and-test
|
||||
qltests-linux:
|
||||
needs: build-and-test-linux
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-latest-xl
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./swift/actions/run-ql-tests
|
||||
qltests-macos:
|
||||
if : ${{ github.event_name == 'pull_request' }}
|
||||
needs: build-and-test-macos
|
||||
runs-on: macos-12-xl
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
slice: ["1/2", "2/2"]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./swift/actions/run-ql-tests
|
||||
with:
|
||||
flags: --slice ${{ matrix.slice }}
|
||||
integration-tests-linux:
|
||||
needs: build-and-test-linux
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-latest-xl
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./swift/actions/run-integration-tests
|
||||
integration-tests-macos:
|
||||
if : ${{ github.event_name == 'pull_request' }}
|
||||
needs: build-and-test-macos
|
||||
runs-on: macos-12-xl
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./swift/actions/run-integration-tests
|
||||
codegen:
|
||||
if : ${{ github.event_name == 'pull_request' }}
|
||||
runs-on: ubuntu-latest
|
||||
needs: changes
|
||||
if: ${{ needs.changes.outputs.codegen == 'true' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./swift/actions/setup-env
|
||||
- uses: bazelbuild/setup-bazelisk@v2
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version-file: 'swift/.python-version'
|
||||
- uses: pre-commit/action@v3.0.0
|
||||
name: Check that python code is properly formatted
|
||||
with:
|
||||
@@ -114,6 +94,7 @@ jobs:
|
||||
name: swift-generated-cpp-files
|
||||
path: generated-cpp-files/**
|
||||
database-upgrade-scripts:
|
||||
if : ${{ github.event_name == 'pull_request' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
@@ -12,13 +12,6 @@
|
||||
# ML-powered queries
|
||||
/javascript/ql/experimental/adaptivethreatmodeling/ @github/codeql-ml-powered-queries-reviewers
|
||||
|
||||
# Notify members of codeql-go about PRs to the shared data-flow library files
|
||||
/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll @github/codeql-java @github/codeql-go
|
||||
/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll @github/codeql-java @github/codeql-go
|
||||
/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll @github/codeql-java @github/codeql-go
|
||||
/java/ql/src/semmle/code/java/dataflow/internal/tainttracking1/TaintTrackingImpl.qll @github/codeql-java @github/codeql-go
|
||||
/java/ql/src/semmle/code/java/dataflow/internal/tainttracking2/TaintTrackingImpl.qll @github/codeql-java @github/codeql-go
|
||||
|
||||
# CodeQL tools and associated docs
|
||||
/docs/codeql/codeql-cli/ @github/codeql-cli-reviewers
|
||||
/docs/codeql/codeql-for-visual-studio-code/ @github/codeql-vscode-reviewers
|
||||
|
||||
@@ -25,6 +25,7 @@ provide:
|
||||
- "misc/suite-helpers/qlpack.yml"
|
||||
- "ruby/extractor-pack/codeql-extractor.yml"
|
||||
- "swift/extractor-pack/codeql-extractor.yml"
|
||||
- "swift/integration-tests/qlpack.yml"
|
||||
- "ql/extractor-pack/codeql-extractor.yml"
|
||||
|
||||
versionPolicies:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"DataFlow Java/C++/C#/Python": [
|
||||
"DataFlow Java/C++/C#/Go/Python/Ruby/Swift": [
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl.qll",
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl2.qll",
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl3.qll",
|
||||
@@ -27,6 +27,8 @@
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplForContentDataFlow.qll",
|
||||
"go/ql/lib/semmle/go/dataflow/internal/DataFlowImpl.qll",
|
||||
"go/ql/lib/semmle/go/dataflow/internal/DataFlowImpl2.qll",
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl.qll",
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl2.qll",
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl3.qll",
|
||||
@@ -38,17 +40,18 @@
|
||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForPathname.qll",
|
||||
"swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImpl.qll"
|
||||
],
|
||||
"DataFlow Java/C++/C#/Python Common": [
|
||||
"DataFlow Java/C++/C#/Go/Python/Ruby/Swift Common": [
|
||||
"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",
|
||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplCommon.qll",
|
||||
"swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImplCommon.qll"
|
||||
],
|
||||
"TaintTracking::Configuration Java/C++/C#/Python": [
|
||||
"TaintTracking::Configuration Java/C++/C#/Go/Python/Ruby/Swift": [
|
||||
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
|
||||
@@ -62,6 +65,8 @@
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking3/TaintTrackingImpl.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking4/TaintTrackingImpl.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking5/TaintTrackingImpl.qll",
|
||||
"go/ql/lib/semmle/go/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
|
||||
"go/ql/lib/semmle/go/dataflow/internal/tainttracking2/TaintTrackingImpl.qll",
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/tainttracking2/TaintTrackingImpl.qll",
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/tainttracking3/TaintTrackingImpl.qll",
|
||||
@@ -72,7 +77,7 @@
|
||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
|
||||
"swift/ql/lib/codeql/swift/dataflow/internal/tainttracking1/TaintTrackingImpl.qll"
|
||||
],
|
||||
"DataFlow Java/C++/C#/Python Consistency checks": [
|
||||
"DataFlow Java/C++/C#/Python/Ruby/Swift Consistency checks": [
|
||||
"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",
|
||||
@@ -82,9 +87,10 @@
|
||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplConsistency.qll",
|
||||
"swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImplConsistency.qll"
|
||||
],
|
||||
"DataFlow Java/C#/Ruby/Python/Swift Flow Summaries": [
|
||||
"DataFlow Java/C#/Go/Ruby/Python/Swift Flow Summaries": [
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/FlowSummaryImpl.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll",
|
||||
"go/ql/lib/semmle/go/dataflow/internal/FlowSummaryImpl.qll",
|
||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/FlowSummaryImpl.qll",
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/FlowSummaryImpl.qll",
|
||||
"swift/ql/lib/codeql/swift/dataflow/internal/FlowSummaryImpl.qll"
|
||||
@@ -505,6 +511,7 @@
|
||||
],
|
||||
"AccessPathSyntax": [
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/AccessPathSyntax.qll",
|
||||
"go/ql/lib/semmle/go/dataflow/internal/AccessPathSyntax.qll",
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/AccessPathSyntax.qll",
|
||||
"javascript/ql/lib/semmle/javascript/frameworks/data/internal/AccessPathSyntax.qll",
|
||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/AccessPathSyntax.qll",
|
||||
@@ -573,5 +580,9 @@
|
||||
"IncompleteMultiCharacterSanitization JS/Ruby": [
|
||||
"javascript/ql/lib/semmle/javascript/security/IncompleteMultiCharacterSanitizationQuery.qll",
|
||||
"ruby/ql/lib/codeql/ruby/security/IncompleteMultiCharacterSanitizationQuery.qll"
|
||||
],
|
||||
"EncryptionKeySizes Python/Java": [
|
||||
"python/ql/lib/semmle/python/security/internal/EncryptionKeySizes.qll",
|
||||
"java/ql/lib/semmle/code/java/security/internal/EncryptionKeySizes.qll"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## 0.4.5
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.4.4
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
category: deprecated
|
||||
---
|
||||
|
||||
* Deprecated `semmle.code.cpp.ir.dataflow.DefaultTaintTracking`. Use `semmle.code.cpp.ir.dataflow.TaintTracking`.
|
||||
* Deprecated `semmle.code.cpp.security.TaintTrackingImpl`. Use `semmle.code.cpp.ir.dataflow.TaintTracking`.
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The `secure_getenv` and `_wgetenv` functions are now recognized as local flow sources.
|
||||
3
cpp/ql/lib/change-notes/released/0.4.5.md
Normal file
3
cpp/ql/lib/change-notes/released/0.4.5.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.4.5
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.4.4
|
||||
lastReleaseVersion: 0.4.5
|
||||
|
||||
@@ -12,8 +12,8 @@ import IDEContextual
|
||||
*
|
||||
* In some cases it is preferable to modify locations (the
|
||||
* `hasLocationInfo()` predicate) so that they are short, and
|
||||
* non-overlapping with other locations that might be highlighted in
|
||||
* the LGTM interface.
|
||||
* non-overlapping with other locations that might be reported as
|
||||
* code scanning alerts on GitHub.
|
||||
*
|
||||
* We need to give locations that may not be in the database, so
|
||||
* we use `hasLocationInfo()` rather than `getLocation()`.
|
||||
|
||||
@@ -70,7 +70,7 @@ abstract class Configuration extends string {
|
||||
/**
|
||||
* Holds if `sink` is a relevant data flow sink accepting `state`.
|
||||
*/
|
||||
predicate isSink(Node source, FlowState state) { none() }
|
||||
predicate isSink(Node sink, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited. This completely removes
|
||||
@@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
|
||||
}
|
||||
|
||||
ParameterPosition getPosition() { this.isParameterOf(_, result) }
|
||||
|
||||
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
|
||||
}
|
||||
|
||||
private class RetNodeEx extends NodeEx {
|
||||
@@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if flow from `p` to a return node of kind `kind` is allowed.
|
||||
*
|
||||
* We don't expect a parameter to return stored in itself, unless
|
||||
* explicitly allowed
|
||||
*/
|
||||
bindingset[p, kind]
|
||||
private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) {
|
||||
exists(ParameterPosition pos | p.isParameterOf(_, pos) |
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
allowParameterReturnInSelfCached(p.asNode())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if flow from a parameter at position `pos` inside `c` to a return node of
|
||||
* kind `kind` is allowed.
|
||||
*
|
||||
* We don't expect a parameter to return stored in itself, unless
|
||||
* explicitly allowed
|
||||
*/
|
||||
bindingset[c, pos, kind]
|
||||
private predicate parameterFlowThroughAllowed(
|
||||
DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
p.isParameterOf(c, pos) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
private module Stage1 implements StageSig {
|
||||
class Ap = Unit;
|
||||
|
||||
@@ -981,21 +1011,22 @@ private module Stage1 implements StageSig {
|
||||
* candidate for the origin of a summary.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||
exists(ReturnKindExt kind |
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
|
||||
exists(DataFlowCallable c, ReturnKindExt kind |
|
||||
throughFlowNodeCand(p, config) and
|
||||
returnFlowCallableNodeCand(c, kind, config) and
|
||||
p.getEnclosingCallable() = c and
|
||||
exists(ap) and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
|
||||
throughFlowNodeCand(ret, config) and
|
||||
kind = ret.getKind()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
|
||||
exists(ArgNodeEx arg, boolean toReturn |
|
||||
@@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1(
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand1(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config
|
||||
) {
|
||||
viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
|
||||
Stage1::revFlow(ret, config) and
|
||||
not outBarrier(ret, config) and
|
||||
not inBarrier(out, config)
|
||||
exists(ReturnPosition pos |
|
||||
viableReturnPosOutNodeCand1(call, pos, out, config) and
|
||||
pos = ret.getReturnPosition() and
|
||||
kind = pos.getKind() and
|
||||
Stage1::revFlow(ret, config) and
|
||||
not outBarrier(ret, config) and
|
||||
not inBarrier(out, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1(
|
||||
* edge in the graph of paths between sources and sinks that ignores call
|
||||
* contexts.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private int branch(NodeEx n1, Configuration conf) {
|
||||
result =
|
||||
strictcount(NodeEx n |
|
||||
flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
|
||||
flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) {
|
||||
* edge in the graph of paths between sources and sinks that ignores call
|
||||
* contexts.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private int join(NodeEx n2, Configuration conf) {
|
||||
result =
|
||||
strictcount(NodeEx n |
|
||||
flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
|
||||
flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand1(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
flowOutOfCallNodeCand1(call, ret, out, config) and
|
||||
flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and
|
||||
exists(int b, int j |
|
||||
b = branch(ret, config) and
|
||||
j = join(out, config) and
|
||||
b = branch(ret, pragma[only_bind_into](config)) and
|
||||
j = join(out, pragma[only_bind_into](config)) and
|
||||
if b.minimum(j) <= config.fieldFlowBranchLimit()
|
||||
then allowsFieldFlow = true
|
||||
else allowsFieldFlow = false
|
||||
@@ -1136,10 +1174,10 @@ pragma[nomagic]
|
||||
private predicate flowIntoCallNodeCand1(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowIntoCallNodeCand1(call, arg, p, config) and
|
||||
flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and
|
||||
exists(int b, int j |
|
||||
b = branch(arg, config) and
|
||||
j = join(p, config) and
|
||||
b = branch(arg, pragma[only_bind_into](config)) and
|
||||
j = join(p, pragma[only_bind_into](config)) and
|
||||
if b.minimum(j) <= config.fieldFlowBranchLimit()
|
||||
then allowsFieldFlow = true
|
||||
else allowsFieldFlow = false
|
||||
@@ -1156,7 +1194,9 @@ private signature module StageSig {
|
||||
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
|
||||
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
|
||||
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
|
||||
|
||||
predicate storeStepCand(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
|
||||
@@ -1222,7 +1262,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
);
|
||||
|
||||
predicate flowOutOfCall(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
);
|
||||
|
||||
predicate flowIntoCall(
|
||||
@@ -1247,14 +1288,14 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowThroughOutOfCall(
|
||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
|
||||
NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
|
||||
pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call)
|
||||
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call) and
|
||||
c = ret.getEnclosingCallable()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1262,29 +1303,32 @@ private module MkStage<StageSig PrevStage> {
|
||||
* configuration `config`.
|
||||
*
|
||||
* The call context `cc` records whether the node is reached through an
|
||||
* argument in a call, and if so, `argAp` records the access path of that
|
||||
* argument.
|
||||
* argument in a call, and if so, `summaryCtx` and `argAp` record the
|
||||
* corresponding parameter position and access path of that argument, respectively.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate fwdFlow(
|
||||
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow0(node, state, cc, argAp, ap, config) and
|
||||
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
|
||||
filter(node, state, ap, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlow0(
|
||||
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
sourceNode(node, state, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
argAp = apNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
ap = getApNil(node)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
|
||||
fwdFlow(mid, state0, cc, argAp, ap0, config) and
|
||||
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
|
||||
localCc = getLocalCc(mid, cc)
|
||||
|
|
||||
localStep(mid, state0, node, state, true, _, config, localCc) and
|
||||
@@ -1295,65 +1339,82 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
jumpStep(mid, node, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone()
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, ApNil nil |
|
||||
fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and
|
||||
additionalJumpStep(mid, node, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and
|
||||
additionalJumpStateStep(mid, state0, node, state, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(TypedContent tc, Ap ap0 |
|
||||
fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
|
||||
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
|
||||
ap = apCons(tc, ap0)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Ap ap0, Content c |
|
||||
fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
|
||||
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
|
||||
fwdFlowConsCand(ap0, c, ap, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(ApApprox apa |
|
||||
fwdFlowIn(_, node, state, _, cc, _, ap, config) and
|
||||
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
|
||||
apa = getApprox(ap) and
|
||||
if PrevStage::parameterMayFlowThrough(node, _, apa, config)
|
||||
then argAp = apSome(ap)
|
||||
else argAp = apNone()
|
||||
if PrevStage::parameterMayFlowThrough(node, apa, config)
|
||||
then (
|
||||
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
|
||||
argAp = apSome(ap)
|
||||
) else (
|
||||
summaryCtx = TParameterPositionNone() and argAp = apNone()
|
||||
)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
|
||||
exists(
|
||||
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
|
||||
DataFlowCallable inner
|
||||
|
|
||||
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
cc = getCallContextReturn(inner, call, innercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
or
|
||||
exists(DataFlowCall call, Ap argAp0 |
|
||||
fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, cc, argAp, argAp0, config)
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
|
||||
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowStore(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
|
||||
Configuration config
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
exists(DataFlowType contentType |
|
||||
fwdFlow(node1, state, cc, argAp, ap1, config) and
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
|
||||
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
|
||||
typecheckStore(ap1, contentType)
|
||||
)
|
||||
@@ -1366,7 +1427,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
|
||||
exists(TypedContent tc |
|
||||
fwdFlowStore(_, tail, tc, _, _, _, _, config) and
|
||||
fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and
|
||||
tc.getContent() = c and
|
||||
cons = apCons(tc, tail)
|
||||
)
|
||||
@@ -1374,21 +1435,21 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead(
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
|
||||
Configuration config
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
fwdFlow(node1, state, cc, argAp, ap, config) and
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
getHeadContent(ap) = c
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIn(
|
||||
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp,
|
||||
Ap ap, Configuration config
|
||||
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ArgNodeEx arg, boolean allowsFieldFlow |
|
||||
fwdFlow(arg, state, outercc, argAp, ap, config) and
|
||||
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
|
||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
@@ -1396,29 +1457,19 @@ private module MkStage<StageSig PrevStage> {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowOutNotFromArg(
|
||||
NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
|
||||
private predicate fwdFlowOutFromArg(
|
||||
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(
|
||||
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
|
||||
DataFlowCallable inner
|
||||
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
||||
|
|
||||
fwdFlow(ret, state, innercc, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
ccOut = getCallContextReturn(inner, call, innercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowOutFromArg(
|
||||
DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
|
||||
fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
|
||||
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
|
||||
config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1428,11 +1479,13 @@ private module MkStage<StageSig PrevStage> {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(
|
||||
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
ParameterPosition pos, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
|
||||
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
|
||||
exists(ParamNodeEx param |
|
||||
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
|
||||
pos = param.getPosition()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1440,27 +1493,40 @@ private module MkStage<StageSig PrevStage> {
|
||||
private predicate storeStepFwd(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
|
||||
) {
|
||||
fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
|
||||
fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and
|
||||
ap2 = apCons(tc, ap1) and
|
||||
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config)
|
||||
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config)
|
||||
}
|
||||
|
||||
private predicate readStepFwd(
|
||||
NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
|
||||
) {
|
||||
fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and
|
||||
fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and
|
||||
fwdFlowConsCand(ap1, c, ap2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
|
||||
exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
|
||||
fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
|
||||
private predicate returnFlowsThrough0(
|
||||
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
|
||||
ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(boolean allowsFieldFlow |
|
||||
fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
|
||||
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
|
||||
pragma[only_bind_into](config))
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnFlowsThrough(
|
||||
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c, ParameterPosition ppos |
|
||||
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
|
||||
p.isParameterOf(c, ppos) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1468,118 +1534,130 @@ private module MkStage<StageSig PrevStage> {
|
||||
private predicate flowThroughIntoCall(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
|
||||
PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
|
||||
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnNodeMayFlowThrough(
|
||||
RetNodeEx ret, FlowState state, Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
|
||||
exists(Ap argAp |
|
||||
flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
|
||||
returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` with access path `ap` is part of a path from a source to a
|
||||
* sink in the configuration `config`.
|
||||
*
|
||||
* The Boolean `toReturn` records whether the node must be returned from the
|
||||
* enclosing callable in order to reach a sink, and if so, `returnAp` records
|
||||
* the access path of the returned value.
|
||||
* The parameter `returnCtx` records whether (and how) the node must be returned
|
||||
* from the enclosing callable in order to reach a sink, and if so, `returnAp`
|
||||
* records the access path of the returned value.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate revFlow(
|
||||
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
revFlow0(node, state, toReturn, returnAp, ap, config) and
|
||||
fwdFlow(node, state, _, _, ap, config)
|
||||
revFlow0(node, state, returnCtx, returnAp, ap, config) and
|
||||
fwdFlow(node, state, _, _, _, ap, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlow0(
|
||||
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
fwdFlow(node, state, _, _, ap, config) and
|
||||
fwdFlow(node, state, _, _, _, ap, config) and
|
||||
sinkNode(node, state, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
(
|
||||
if hasSinkCallCtx(config)
|
||||
then returnCtx = TReturnCtxNoFlowThrough()
|
||||
else returnCtx = TReturnCtxNone()
|
||||
) and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0 |
|
||||
localStep(node, state, mid, state0, true, _, config, _) and
|
||||
revFlow(mid, state0, toReturn, returnAp, ap, config)
|
||||
revFlow(mid, state0, returnCtx, returnAp, ap, config)
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and
|
||||
revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
|
||||
revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
jumpStep(node, mid, config) and
|
||||
revFlow(mid, state, _, _, ap, config) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone()
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, ApNil nil |
|
||||
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
additionalJumpStep(node, mid, config) and
|
||||
revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
additionalJumpStateStep(node, state, mid, state0, config) and
|
||||
revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil,
|
||||
pragma[only_bind_into](config)) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Ap ap0, Content c |
|
||||
revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
|
||||
revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and
|
||||
revFlowConsCand(ap0, c, ap, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(NodeEx mid, Ap ap0 |
|
||||
revFlow(mid, state, toReturn, returnAp, ap0, config) and
|
||||
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
|
||||
readStepFwd(node, ap, _, mid, ap0, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
revFlowInNotToReturn(node, state, returnAp, ap, config) and
|
||||
toReturn = false
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
|
||||
flowIntoCall(_, node, p, allowsFieldFlow, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
returnCtx = TReturnCtxNone()
|
||||
)
|
||||
or
|
||||
exists(DataFlowCall call, Ap returnAp0 |
|
||||
revFlowInToReturn(call, node, state, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 |
|
||||
revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
revFlowOut(_, node, state, _, _, ap, config) and
|
||||
toReturn = true and
|
||||
if returnNodeMayFlowThrough(node, state, ap, config)
|
||||
then returnAp = apSome(ap)
|
||||
else returnAp = apNone()
|
||||
exists(ReturnKindExt kind |
|
||||
revFlowOut(_, node, kind, state, _, _, ap, config) and
|
||||
if returnFlowsThrough(node, kind, state, _, _, _, ap, config)
|
||||
then (
|
||||
returnCtx = TReturnCtxMaybeFlowThrough(kind) and
|
||||
returnAp = apSome(ap)
|
||||
) else (
|
||||
returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowStore(
|
||||
Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid,
|
||||
boolean toReturn, ApOption returnAp, Configuration config
|
||||
ReturnCtx returnCtx, ApOption returnAp, Configuration config
|
||||
) {
|
||||
revFlow(mid, state, toReturn, returnAp, ap0, config) and
|
||||
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
|
||||
storeStepFwd(node, ap, tc, mid, ap0, config) and
|
||||
tc.getContent() = c
|
||||
}
|
||||
@@ -1599,35 +1677,27 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowOut(
|
||||
DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx,
|
||||
ApOption returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(NodeEx out, boolean allowsFieldFlow |
|
||||
revFlow(out, state, toReturn, returnAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowInNotToReturn(
|
||||
ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, false, returnAp, ap, config) and
|
||||
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
|
||||
revFlow(out, state, returnCtx, returnAp, ap, config) and
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowInToReturn(
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, true, apSome(returnAp), ap, config) and
|
||||
revFlow(pragma[only_bind_into](p), state,
|
||||
TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
|
||||
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1638,11 +1708,12 @@ private module MkStage<StageSig PrevStage> {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate revFlowIsReturned(
|
||||
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(RetNodeEx ret, FlowState state, CcCall ccc |
|
||||
revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and
|
||||
fwdFlow(ret, state, ccc, apSome(_), ap, config) and
|
||||
revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and
|
||||
returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and
|
||||
matchesCall(ccc, call)
|
||||
)
|
||||
}
|
||||
@@ -1713,40 +1784,39 @@ private module MkStage<StageSig PrevStage> {
|
||||
validAp(ap, config)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate parameterFlow(
|
||||
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
|
||||
pragma[nomagic]
|
||||
private predicate parameterFlowsThroughRev(
|
||||
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
|
||||
) {
|
||||
revFlow(p, _, true, apSome(ap0), ap, config) and
|
||||
c = p.getEnclosingCallable()
|
||||
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
}
|
||||
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||
exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
|
||||
parameterFlow(p, ap, ap0, c, config) and
|
||||
c = ret.getEnclosingCallable() and
|
||||
revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_),
|
||||
pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and
|
||||
fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
pragma[nomagic]
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
|
||||
exists(RetNodeEx ret, ReturnKindExt kind |
|
||||
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
|
||||
parameterFlowsThroughRev(p, ap, kind, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
|
||||
exists(ParamNodeEx p, Ap ap |
|
||||
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
|
||||
parameterFlowsThroughRev(p, ap, kind, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
|
||||
exists(
|
||||
Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
|
||||
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
|
||||
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
||||
|
|
||||
revFlow(arg, state, toReturn, returnAp, ap, config) and
|
||||
revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
|
||||
revFlow(arg, state, returnCtx, returnAp, ap, config) and
|
||||
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1754,14 +1824,13 @@ private module MkStage<StageSig PrevStage> {
|
||||
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
|
||||
) {
|
||||
fwd = true and
|
||||
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and
|
||||
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and
|
||||
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
|
||||
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
|
||||
states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and
|
||||
states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and
|
||||
tuples =
|
||||
count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
|
||||
fwdFlow(n, state, cc, argAp, ap, config)
|
||||
)
|
||||
count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config))
|
||||
or
|
||||
fwd = false and
|
||||
nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
|
||||
@@ -1769,8 +1838,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
|
||||
states = count(FlowState state | revFlow(_, state, _, _, _, config)) and
|
||||
tuples =
|
||||
count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap |
|
||||
revFlow(n, state, b, retAp, ap, config)
|
||||
count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap |
|
||||
revFlow(n, state, returnCtx, retAp, ap, config)
|
||||
)
|
||||
}
|
||||
/* End: Stage logic. */
|
||||
@@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
|
||||
exists(lcc)
|
||||
}
|
||||
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
|
||||
|
||||
predicate flowIntoCall = flowIntoCallNodeCand1/5;
|
||||
|
||||
@@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand2(
|
||||
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
|
||||
flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and
|
||||
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
|
||||
Stage2::revFlowAlias(node1, pragma[only_bind_into](config))
|
||||
}
|
||||
@@ -2021,8 +2091,8 @@ private module LocalFlowBigStep {
|
||||
exists(NodeEx next | Stage2::revFlow(next, state, config) |
|
||||
jumpStep(node, next, config) or
|
||||
additionalJumpStep(node, next, config) or
|
||||
flowIntoCallNodeCand1(_, node, next, config) or
|
||||
flowOutOfCallNodeCand1(_, node, next, config) or
|
||||
flowIntoCallNodeCand2(_, node, next, _, config) or
|
||||
flowOutOfCallNodeCand2(_, node, _, next, _, config) or
|
||||
Stage2::storeStepCand(node, _, _, next, _, config) or
|
||||
Stage2::readStepCand(node, _, next, config)
|
||||
)
|
||||
@@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
|
||||
}
|
||||
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
|
||||
|
||||
predicate flowIntoCall = flowIntoCallNodeCand2/5;
|
||||
|
||||
@@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx(
|
||||
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
|
||||
) {
|
||||
exists(AccessPathFront apf |
|
||||
Stage3::revFlow(node, state, true, _, apf, config) and
|
||||
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
|
||||
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
|
||||
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
|
||||
config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
|
||||
pragma[nomagic]
|
||||
predicate flowOutOfCall(
|
||||
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
exists(FlowState state |
|
||||
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
|
||||
flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
|
||||
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
|
||||
pragma[only_bind_into](config))
|
||||
@@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeMayUseSummary0(
|
||||
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
|
||||
NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa,
|
||||
Configuration config
|
||||
) {
|
||||
exists(AccessPathApprox apa0 |
|
||||
Stage4::parameterMayFlowThrough(_, c, _, _) and
|
||||
Stage4::revFlow(n, state, true, _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
|
||||
n.getEnclosingCallable() = c
|
||||
c = n.getEnclosingCallable() and
|
||||
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
|
||||
TAccessPathApproxSome(apa), apa0, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2522,9 +2595,10 @@ pragma[nomagic]
|
||||
private predicate nodeMayUseSummary(
|
||||
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c |
|
||||
Stage4::parameterMayFlowThrough(_, c, apa, config) and
|
||||
nodeMayUseSummary0(n, c, state, apa, config)
|
||||
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
|
||||
Stage4::parameterMayFlowThrough(p, apa, config) and
|
||||
nodeMayUseSummary0(n, c, pos, state, apa, config) and
|
||||
p.isParameterOf(c, pos)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2532,7 +2606,7 @@ private newtype TSummaryCtx =
|
||||
TSummaryCtxNone() or
|
||||
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
|
||||
exists(Configuration config |
|
||||
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
|
||||
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
|
||||
Stage4::revFlow(p, state, _, config)
|
||||
)
|
||||
}
|
||||
@@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough(
|
||||
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
|
||||
AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos |
|
||||
exists(PathNodeMid mid, RetNodeEx ret |
|
||||
pathNode(mid, ret, state, cc, sc, ap, config, _) and
|
||||
kind = ret.getKind() and
|
||||
apa = ap.getApprox() and
|
||||
pos = sc.getParameterPos() and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
sc.getParamNode().allowParameterReturnInSelf()
|
||||
)
|
||||
parameterFlowThroughAllowed(sc.getParamNode(), kind)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ abstract class Configuration extends string {
|
||||
/**
|
||||
* Holds if `sink` is a relevant data flow sink accepting `state`.
|
||||
*/
|
||||
predicate isSink(Node source, FlowState state) { none() }
|
||||
predicate isSink(Node sink, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited. This completely removes
|
||||
@@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
|
||||
}
|
||||
|
||||
ParameterPosition getPosition() { this.isParameterOf(_, result) }
|
||||
|
||||
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
|
||||
}
|
||||
|
||||
private class RetNodeEx extends NodeEx {
|
||||
@@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if flow from `p` to a return node of kind `kind` is allowed.
|
||||
*
|
||||
* We don't expect a parameter to return stored in itself, unless
|
||||
* explicitly allowed
|
||||
*/
|
||||
bindingset[p, kind]
|
||||
private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) {
|
||||
exists(ParameterPosition pos | p.isParameterOf(_, pos) |
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
allowParameterReturnInSelfCached(p.asNode())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if flow from a parameter at position `pos` inside `c` to a return node of
|
||||
* kind `kind` is allowed.
|
||||
*
|
||||
* We don't expect a parameter to return stored in itself, unless
|
||||
* explicitly allowed
|
||||
*/
|
||||
bindingset[c, pos, kind]
|
||||
private predicate parameterFlowThroughAllowed(
|
||||
DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
p.isParameterOf(c, pos) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
private module Stage1 implements StageSig {
|
||||
class Ap = Unit;
|
||||
|
||||
@@ -981,21 +1011,22 @@ private module Stage1 implements StageSig {
|
||||
* candidate for the origin of a summary.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||
exists(ReturnKindExt kind |
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
|
||||
exists(DataFlowCallable c, ReturnKindExt kind |
|
||||
throughFlowNodeCand(p, config) and
|
||||
returnFlowCallableNodeCand(c, kind, config) and
|
||||
p.getEnclosingCallable() = c and
|
||||
exists(ap) and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
|
||||
throughFlowNodeCand(ret, config) and
|
||||
kind = ret.getKind()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
|
||||
exists(ArgNodeEx arg, boolean toReturn |
|
||||
@@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1(
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand1(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config
|
||||
) {
|
||||
viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
|
||||
Stage1::revFlow(ret, config) and
|
||||
not outBarrier(ret, config) and
|
||||
not inBarrier(out, config)
|
||||
exists(ReturnPosition pos |
|
||||
viableReturnPosOutNodeCand1(call, pos, out, config) and
|
||||
pos = ret.getReturnPosition() and
|
||||
kind = pos.getKind() and
|
||||
Stage1::revFlow(ret, config) and
|
||||
not outBarrier(ret, config) and
|
||||
not inBarrier(out, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1(
|
||||
* edge in the graph of paths between sources and sinks that ignores call
|
||||
* contexts.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private int branch(NodeEx n1, Configuration conf) {
|
||||
result =
|
||||
strictcount(NodeEx n |
|
||||
flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
|
||||
flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) {
|
||||
* edge in the graph of paths between sources and sinks that ignores call
|
||||
* contexts.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private int join(NodeEx n2, Configuration conf) {
|
||||
result =
|
||||
strictcount(NodeEx n |
|
||||
flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
|
||||
flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand1(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
flowOutOfCallNodeCand1(call, ret, out, config) and
|
||||
flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and
|
||||
exists(int b, int j |
|
||||
b = branch(ret, config) and
|
||||
j = join(out, config) and
|
||||
b = branch(ret, pragma[only_bind_into](config)) and
|
||||
j = join(out, pragma[only_bind_into](config)) and
|
||||
if b.minimum(j) <= config.fieldFlowBranchLimit()
|
||||
then allowsFieldFlow = true
|
||||
else allowsFieldFlow = false
|
||||
@@ -1136,10 +1174,10 @@ pragma[nomagic]
|
||||
private predicate flowIntoCallNodeCand1(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowIntoCallNodeCand1(call, arg, p, config) and
|
||||
flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and
|
||||
exists(int b, int j |
|
||||
b = branch(arg, config) and
|
||||
j = join(p, config) and
|
||||
b = branch(arg, pragma[only_bind_into](config)) and
|
||||
j = join(p, pragma[only_bind_into](config)) and
|
||||
if b.minimum(j) <= config.fieldFlowBranchLimit()
|
||||
then allowsFieldFlow = true
|
||||
else allowsFieldFlow = false
|
||||
@@ -1156,7 +1194,9 @@ private signature module StageSig {
|
||||
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
|
||||
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
|
||||
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
|
||||
|
||||
predicate storeStepCand(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
|
||||
@@ -1222,7 +1262,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
);
|
||||
|
||||
predicate flowOutOfCall(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
);
|
||||
|
||||
predicate flowIntoCall(
|
||||
@@ -1247,14 +1288,14 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowThroughOutOfCall(
|
||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
|
||||
NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
|
||||
pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call)
|
||||
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call) and
|
||||
c = ret.getEnclosingCallable()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1262,29 +1303,32 @@ private module MkStage<StageSig PrevStage> {
|
||||
* configuration `config`.
|
||||
*
|
||||
* The call context `cc` records whether the node is reached through an
|
||||
* argument in a call, and if so, `argAp` records the access path of that
|
||||
* argument.
|
||||
* argument in a call, and if so, `summaryCtx` and `argAp` record the
|
||||
* corresponding parameter position and access path of that argument, respectively.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate fwdFlow(
|
||||
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow0(node, state, cc, argAp, ap, config) and
|
||||
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
|
||||
filter(node, state, ap, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlow0(
|
||||
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
sourceNode(node, state, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
argAp = apNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
ap = getApNil(node)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
|
||||
fwdFlow(mid, state0, cc, argAp, ap0, config) and
|
||||
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
|
||||
localCc = getLocalCc(mid, cc)
|
||||
|
|
||||
localStep(mid, state0, node, state, true, _, config, localCc) and
|
||||
@@ -1295,65 +1339,82 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
jumpStep(mid, node, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone()
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, ApNil nil |
|
||||
fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and
|
||||
additionalJumpStep(mid, node, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and
|
||||
additionalJumpStateStep(mid, state0, node, state, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(TypedContent tc, Ap ap0 |
|
||||
fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
|
||||
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
|
||||
ap = apCons(tc, ap0)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Ap ap0, Content c |
|
||||
fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
|
||||
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
|
||||
fwdFlowConsCand(ap0, c, ap, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(ApApprox apa |
|
||||
fwdFlowIn(_, node, state, _, cc, _, ap, config) and
|
||||
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
|
||||
apa = getApprox(ap) and
|
||||
if PrevStage::parameterMayFlowThrough(node, _, apa, config)
|
||||
then argAp = apSome(ap)
|
||||
else argAp = apNone()
|
||||
if PrevStage::parameterMayFlowThrough(node, apa, config)
|
||||
then (
|
||||
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
|
||||
argAp = apSome(ap)
|
||||
) else (
|
||||
summaryCtx = TParameterPositionNone() and argAp = apNone()
|
||||
)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
|
||||
exists(
|
||||
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
|
||||
DataFlowCallable inner
|
||||
|
|
||||
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
cc = getCallContextReturn(inner, call, innercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
or
|
||||
exists(DataFlowCall call, Ap argAp0 |
|
||||
fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, cc, argAp, argAp0, config)
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
|
||||
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowStore(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
|
||||
Configuration config
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
exists(DataFlowType contentType |
|
||||
fwdFlow(node1, state, cc, argAp, ap1, config) and
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
|
||||
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
|
||||
typecheckStore(ap1, contentType)
|
||||
)
|
||||
@@ -1366,7 +1427,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
|
||||
exists(TypedContent tc |
|
||||
fwdFlowStore(_, tail, tc, _, _, _, _, config) and
|
||||
fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and
|
||||
tc.getContent() = c and
|
||||
cons = apCons(tc, tail)
|
||||
)
|
||||
@@ -1374,21 +1435,21 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead(
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
|
||||
Configuration config
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
fwdFlow(node1, state, cc, argAp, ap, config) and
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
getHeadContent(ap) = c
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIn(
|
||||
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp,
|
||||
Ap ap, Configuration config
|
||||
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ArgNodeEx arg, boolean allowsFieldFlow |
|
||||
fwdFlow(arg, state, outercc, argAp, ap, config) and
|
||||
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
|
||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
@@ -1396,29 +1457,19 @@ private module MkStage<StageSig PrevStage> {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowOutNotFromArg(
|
||||
NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
|
||||
private predicate fwdFlowOutFromArg(
|
||||
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(
|
||||
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
|
||||
DataFlowCallable inner
|
||||
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
||||
|
|
||||
fwdFlow(ret, state, innercc, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
ccOut = getCallContextReturn(inner, call, innercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowOutFromArg(
|
||||
DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
|
||||
fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
|
||||
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
|
||||
config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1428,11 +1479,13 @@ private module MkStage<StageSig PrevStage> {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(
|
||||
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
ParameterPosition pos, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
|
||||
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
|
||||
exists(ParamNodeEx param |
|
||||
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
|
||||
pos = param.getPosition()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1440,27 +1493,40 @@ private module MkStage<StageSig PrevStage> {
|
||||
private predicate storeStepFwd(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
|
||||
) {
|
||||
fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
|
||||
fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and
|
||||
ap2 = apCons(tc, ap1) and
|
||||
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config)
|
||||
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config)
|
||||
}
|
||||
|
||||
private predicate readStepFwd(
|
||||
NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
|
||||
) {
|
||||
fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and
|
||||
fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and
|
||||
fwdFlowConsCand(ap1, c, ap2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
|
||||
exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
|
||||
fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
|
||||
private predicate returnFlowsThrough0(
|
||||
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
|
||||
ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(boolean allowsFieldFlow |
|
||||
fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
|
||||
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
|
||||
pragma[only_bind_into](config))
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnFlowsThrough(
|
||||
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c, ParameterPosition ppos |
|
||||
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
|
||||
p.isParameterOf(c, ppos) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1468,118 +1534,130 @@ private module MkStage<StageSig PrevStage> {
|
||||
private predicate flowThroughIntoCall(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
|
||||
PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
|
||||
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnNodeMayFlowThrough(
|
||||
RetNodeEx ret, FlowState state, Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
|
||||
exists(Ap argAp |
|
||||
flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
|
||||
returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` with access path `ap` is part of a path from a source to a
|
||||
* sink in the configuration `config`.
|
||||
*
|
||||
* The Boolean `toReturn` records whether the node must be returned from the
|
||||
* enclosing callable in order to reach a sink, and if so, `returnAp` records
|
||||
* the access path of the returned value.
|
||||
* The parameter `returnCtx` records whether (and how) the node must be returned
|
||||
* from the enclosing callable in order to reach a sink, and if so, `returnAp`
|
||||
* records the access path of the returned value.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate revFlow(
|
||||
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
revFlow0(node, state, toReturn, returnAp, ap, config) and
|
||||
fwdFlow(node, state, _, _, ap, config)
|
||||
revFlow0(node, state, returnCtx, returnAp, ap, config) and
|
||||
fwdFlow(node, state, _, _, _, ap, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlow0(
|
||||
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
fwdFlow(node, state, _, _, ap, config) and
|
||||
fwdFlow(node, state, _, _, _, ap, config) and
|
||||
sinkNode(node, state, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
(
|
||||
if hasSinkCallCtx(config)
|
||||
then returnCtx = TReturnCtxNoFlowThrough()
|
||||
else returnCtx = TReturnCtxNone()
|
||||
) and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0 |
|
||||
localStep(node, state, mid, state0, true, _, config, _) and
|
||||
revFlow(mid, state0, toReturn, returnAp, ap, config)
|
||||
revFlow(mid, state0, returnCtx, returnAp, ap, config)
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and
|
||||
revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
|
||||
revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
jumpStep(node, mid, config) and
|
||||
revFlow(mid, state, _, _, ap, config) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone()
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, ApNil nil |
|
||||
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
additionalJumpStep(node, mid, config) and
|
||||
revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
additionalJumpStateStep(node, state, mid, state0, config) and
|
||||
revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil,
|
||||
pragma[only_bind_into](config)) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Ap ap0, Content c |
|
||||
revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
|
||||
revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and
|
||||
revFlowConsCand(ap0, c, ap, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(NodeEx mid, Ap ap0 |
|
||||
revFlow(mid, state, toReturn, returnAp, ap0, config) and
|
||||
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
|
||||
readStepFwd(node, ap, _, mid, ap0, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
revFlowInNotToReturn(node, state, returnAp, ap, config) and
|
||||
toReturn = false
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
|
||||
flowIntoCall(_, node, p, allowsFieldFlow, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
returnCtx = TReturnCtxNone()
|
||||
)
|
||||
or
|
||||
exists(DataFlowCall call, Ap returnAp0 |
|
||||
revFlowInToReturn(call, node, state, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 |
|
||||
revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
revFlowOut(_, node, state, _, _, ap, config) and
|
||||
toReturn = true and
|
||||
if returnNodeMayFlowThrough(node, state, ap, config)
|
||||
then returnAp = apSome(ap)
|
||||
else returnAp = apNone()
|
||||
exists(ReturnKindExt kind |
|
||||
revFlowOut(_, node, kind, state, _, _, ap, config) and
|
||||
if returnFlowsThrough(node, kind, state, _, _, _, ap, config)
|
||||
then (
|
||||
returnCtx = TReturnCtxMaybeFlowThrough(kind) and
|
||||
returnAp = apSome(ap)
|
||||
) else (
|
||||
returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowStore(
|
||||
Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid,
|
||||
boolean toReturn, ApOption returnAp, Configuration config
|
||||
ReturnCtx returnCtx, ApOption returnAp, Configuration config
|
||||
) {
|
||||
revFlow(mid, state, toReturn, returnAp, ap0, config) and
|
||||
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
|
||||
storeStepFwd(node, ap, tc, mid, ap0, config) and
|
||||
tc.getContent() = c
|
||||
}
|
||||
@@ -1599,35 +1677,27 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowOut(
|
||||
DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx,
|
||||
ApOption returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(NodeEx out, boolean allowsFieldFlow |
|
||||
revFlow(out, state, toReturn, returnAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowInNotToReturn(
|
||||
ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, false, returnAp, ap, config) and
|
||||
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
|
||||
revFlow(out, state, returnCtx, returnAp, ap, config) and
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowInToReturn(
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, true, apSome(returnAp), ap, config) and
|
||||
revFlow(pragma[only_bind_into](p), state,
|
||||
TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
|
||||
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1638,11 +1708,12 @@ private module MkStage<StageSig PrevStage> {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate revFlowIsReturned(
|
||||
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(RetNodeEx ret, FlowState state, CcCall ccc |
|
||||
revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and
|
||||
fwdFlow(ret, state, ccc, apSome(_), ap, config) and
|
||||
revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and
|
||||
returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and
|
||||
matchesCall(ccc, call)
|
||||
)
|
||||
}
|
||||
@@ -1713,40 +1784,39 @@ private module MkStage<StageSig PrevStage> {
|
||||
validAp(ap, config)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate parameterFlow(
|
||||
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
|
||||
pragma[nomagic]
|
||||
private predicate parameterFlowsThroughRev(
|
||||
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
|
||||
) {
|
||||
revFlow(p, _, true, apSome(ap0), ap, config) and
|
||||
c = p.getEnclosingCallable()
|
||||
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
}
|
||||
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||
exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
|
||||
parameterFlow(p, ap, ap0, c, config) and
|
||||
c = ret.getEnclosingCallable() and
|
||||
revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_),
|
||||
pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and
|
||||
fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
pragma[nomagic]
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
|
||||
exists(RetNodeEx ret, ReturnKindExt kind |
|
||||
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
|
||||
parameterFlowsThroughRev(p, ap, kind, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
|
||||
exists(ParamNodeEx p, Ap ap |
|
||||
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
|
||||
parameterFlowsThroughRev(p, ap, kind, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
|
||||
exists(
|
||||
Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
|
||||
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
|
||||
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
||||
|
|
||||
revFlow(arg, state, toReturn, returnAp, ap, config) and
|
||||
revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
|
||||
revFlow(arg, state, returnCtx, returnAp, ap, config) and
|
||||
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1754,14 +1824,13 @@ private module MkStage<StageSig PrevStage> {
|
||||
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
|
||||
) {
|
||||
fwd = true and
|
||||
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and
|
||||
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and
|
||||
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
|
||||
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
|
||||
states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and
|
||||
states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and
|
||||
tuples =
|
||||
count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
|
||||
fwdFlow(n, state, cc, argAp, ap, config)
|
||||
)
|
||||
count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config))
|
||||
or
|
||||
fwd = false and
|
||||
nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
|
||||
@@ -1769,8 +1838,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
|
||||
states = count(FlowState state | revFlow(_, state, _, _, _, config)) and
|
||||
tuples =
|
||||
count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap |
|
||||
revFlow(n, state, b, retAp, ap, config)
|
||||
count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap |
|
||||
revFlow(n, state, returnCtx, retAp, ap, config)
|
||||
)
|
||||
}
|
||||
/* End: Stage logic. */
|
||||
@@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
|
||||
exists(lcc)
|
||||
}
|
||||
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
|
||||
|
||||
predicate flowIntoCall = flowIntoCallNodeCand1/5;
|
||||
|
||||
@@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand2(
|
||||
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
|
||||
flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and
|
||||
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
|
||||
Stage2::revFlowAlias(node1, pragma[only_bind_into](config))
|
||||
}
|
||||
@@ -2021,8 +2091,8 @@ private module LocalFlowBigStep {
|
||||
exists(NodeEx next | Stage2::revFlow(next, state, config) |
|
||||
jumpStep(node, next, config) or
|
||||
additionalJumpStep(node, next, config) or
|
||||
flowIntoCallNodeCand1(_, node, next, config) or
|
||||
flowOutOfCallNodeCand1(_, node, next, config) or
|
||||
flowIntoCallNodeCand2(_, node, next, _, config) or
|
||||
flowOutOfCallNodeCand2(_, node, _, next, _, config) or
|
||||
Stage2::storeStepCand(node, _, _, next, _, config) or
|
||||
Stage2::readStepCand(node, _, next, config)
|
||||
)
|
||||
@@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
|
||||
}
|
||||
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
|
||||
|
||||
predicate flowIntoCall = flowIntoCallNodeCand2/5;
|
||||
|
||||
@@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx(
|
||||
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
|
||||
) {
|
||||
exists(AccessPathFront apf |
|
||||
Stage3::revFlow(node, state, true, _, apf, config) and
|
||||
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
|
||||
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
|
||||
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
|
||||
config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
|
||||
pragma[nomagic]
|
||||
predicate flowOutOfCall(
|
||||
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
exists(FlowState state |
|
||||
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
|
||||
flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
|
||||
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
|
||||
pragma[only_bind_into](config))
|
||||
@@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeMayUseSummary0(
|
||||
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
|
||||
NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa,
|
||||
Configuration config
|
||||
) {
|
||||
exists(AccessPathApprox apa0 |
|
||||
Stage4::parameterMayFlowThrough(_, c, _, _) and
|
||||
Stage4::revFlow(n, state, true, _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
|
||||
n.getEnclosingCallable() = c
|
||||
c = n.getEnclosingCallable() and
|
||||
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
|
||||
TAccessPathApproxSome(apa), apa0, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2522,9 +2595,10 @@ pragma[nomagic]
|
||||
private predicate nodeMayUseSummary(
|
||||
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c |
|
||||
Stage4::parameterMayFlowThrough(_, c, apa, config) and
|
||||
nodeMayUseSummary0(n, c, state, apa, config)
|
||||
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
|
||||
Stage4::parameterMayFlowThrough(p, apa, config) and
|
||||
nodeMayUseSummary0(n, c, pos, state, apa, config) and
|
||||
p.isParameterOf(c, pos)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2532,7 +2606,7 @@ private newtype TSummaryCtx =
|
||||
TSummaryCtxNone() or
|
||||
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
|
||||
exists(Configuration config |
|
||||
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
|
||||
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
|
||||
Stage4::revFlow(p, state, _, config)
|
||||
)
|
||||
}
|
||||
@@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough(
|
||||
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
|
||||
AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos |
|
||||
exists(PathNodeMid mid, RetNodeEx ret |
|
||||
pathNode(mid, ret, state, cc, sc, ap, config, _) and
|
||||
kind = ret.getKind() and
|
||||
apa = ap.getApprox() and
|
||||
pos = sc.getParameterPos() and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
sc.getParamNode().allowParameterReturnInSelf()
|
||||
)
|
||||
parameterFlowThroughAllowed(sc.getParamNode(), kind)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ abstract class Configuration extends string {
|
||||
/**
|
||||
* Holds if `sink` is a relevant data flow sink accepting `state`.
|
||||
*/
|
||||
predicate isSink(Node source, FlowState state) { none() }
|
||||
predicate isSink(Node sink, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited. This completely removes
|
||||
@@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
|
||||
}
|
||||
|
||||
ParameterPosition getPosition() { this.isParameterOf(_, result) }
|
||||
|
||||
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
|
||||
}
|
||||
|
||||
private class RetNodeEx extends NodeEx {
|
||||
@@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if flow from `p` to a return node of kind `kind` is allowed.
|
||||
*
|
||||
* We don't expect a parameter to return stored in itself, unless
|
||||
* explicitly allowed
|
||||
*/
|
||||
bindingset[p, kind]
|
||||
private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) {
|
||||
exists(ParameterPosition pos | p.isParameterOf(_, pos) |
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
allowParameterReturnInSelfCached(p.asNode())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if flow from a parameter at position `pos` inside `c` to a return node of
|
||||
* kind `kind` is allowed.
|
||||
*
|
||||
* We don't expect a parameter to return stored in itself, unless
|
||||
* explicitly allowed
|
||||
*/
|
||||
bindingset[c, pos, kind]
|
||||
private predicate parameterFlowThroughAllowed(
|
||||
DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
p.isParameterOf(c, pos) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
private module Stage1 implements StageSig {
|
||||
class Ap = Unit;
|
||||
|
||||
@@ -981,21 +1011,22 @@ private module Stage1 implements StageSig {
|
||||
* candidate for the origin of a summary.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||
exists(ReturnKindExt kind |
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
|
||||
exists(DataFlowCallable c, ReturnKindExt kind |
|
||||
throughFlowNodeCand(p, config) and
|
||||
returnFlowCallableNodeCand(c, kind, config) and
|
||||
p.getEnclosingCallable() = c and
|
||||
exists(ap) and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
|
||||
throughFlowNodeCand(ret, config) and
|
||||
kind = ret.getKind()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
|
||||
exists(ArgNodeEx arg, boolean toReturn |
|
||||
@@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1(
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand1(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config
|
||||
) {
|
||||
viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
|
||||
Stage1::revFlow(ret, config) and
|
||||
not outBarrier(ret, config) and
|
||||
not inBarrier(out, config)
|
||||
exists(ReturnPosition pos |
|
||||
viableReturnPosOutNodeCand1(call, pos, out, config) and
|
||||
pos = ret.getReturnPosition() and
|
||||
kind = pos.getKind() and
|
||||
Stage1::revFlow(ret, config) and
|
||||
not outBarrier(ret, config) and
|
||||
not inBarrier(out, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1(
|
||||
* edge in the graph of paths between sources and sinks that ignores call
|
||||
* contexts.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private int branch(NodeEx n1, Configuration conf) {
|
||||
result =
|
||||
strictcount(NodeEx n |
|
||||
flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
|
||||
flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) {
|
||||
* edge in the graph of paths between sources and sinks that ignores call
|
||||
* contexts.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private int join(NodeEx n2, Configuration conf) {
|
||||
result =
|
||||
strictcount(NodeEx n |
|
||||
flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
|
||||
flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand1(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
flowOutOfCallNodeCand1(call, ret, out, config) and
|
||||
flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and
|
||||
exists(int b, int j |
|
||||
b = branch(ret, config) and
|
||||
j = join(out, config) and
|
||||
b = branch(ret, pragma[only_bind_into](config)) and
|
||||
j = join(out, pragma[only_bind_into](config)) and
|
||||
if b.minimum(j) <= config.fieldFlowBranchLimit()
|
||||
then allowsFieldFlow = true
|
||||
else allowsFieldFlow = false
|
||||
@@ -1136,10 +1174,10 @@ pragma[nomagic]
|
||||
private predicate flowIntoCallNodeCand1(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowIntoCallNodeCand1(call, arg, p, config) and
|
||||
flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and
|
||||
exists(int b, int j |
|
||||
b = branch(arg, config) and
|
||||
j = join(p, config) and
|
||||
b = branch(arg, pragma[only_bind_into](config)) and
|
||||
j = join(p, pragma[only_bind_into](config)) and
|
||||
if b.minimum(j) <= config.fieldFlowBranchLimit()
|
||||
then allowsFieldFlow = true
|
||||
else allowsFieldFlow = false
|
||||
@@ -1156,7 +1194,9 @@ private signature module StageSig {
|
||||
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
|
||||
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
|
||||
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
|
||||
|
||||
predicate storeStepCand(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
|
||||
@@ -1222,7 +1262,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
);
|
||||
|
||||
predicate flowOutOfCall(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
);
|
||||
|
||||
predicate flowIntoCall(
|
||||
@@ -1247,14 +1288,14 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowThroughOutOfCall(
|
||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
|
||||
NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
|
||||
pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call)
|
||||
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call) and
|
||||
c = ret.getEnclosingCallable()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1262,29 +1303,32 @@ private module MkStage<StageSig PrevStage> {
|
||||
* configuration `config`.
|
||||
*
|
||||
* The call context `cc` records whether the node is reached through an
|
||||
* argument in a call, and if so, `argAp` records the access path of that
|
||||
* argument.
|
||||
* argument in a call, and if so, `summaryCtx` and `argAp` record the
|
||||
* corresponding parameter position and access path of that argument, respectively.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate fwdFlow(
|
||||
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow0(node, state, cc, argAp, ap, config) and
|
||||
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
|
||||
filter(node, state, ap, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlow0(
|
||||
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
sourceNode(node, state, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
argAp = apNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
ap = getApNil(node)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
|
||||
fwdFlow(mid, state0, cc, argAp, ap0, config) and
|
||||
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
|
||||
localCc = getLocalCc(mid, cc)
|
||||
|
|
||||
localStep(mid, state0, node, state, true, _, config, localCc) and
|
||||
@@ -1295,65 +1339,82 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
jumpStep(mid, node, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone()
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, ApNil nil |
|
||||
fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and
|
||||
additionalJumpStep(mid, node, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and
|
||||
additionalJumpStateStep(mid, state0, node, state, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(TypedContent tc, Ap ap0 |
|
||||
fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
|
||||
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
|
||||
ap = apCons(tc, ap0)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Ap ap0, Content c |
|
||||
fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
|
||||
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
|
||||
fwdFlowConsCand(ap0, c, ap, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(ApApprox apa |
|
||||
fwdFlowIn(_, node, state, _, cc, _, ap, config) and
|
||||
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
|
||||
apa = getApprox(ap) and
|
||||
if PrevStage::parameterMayFlowThrough(node, _, apa, config)
|
||||
then argAp = apSome(ap)
|
||||
else argAp = apNone()
|
||||
if PrevStage::parameterMayFlowThrough(node, apa, config)
|
||||
then (
|
||||
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
|
||||
argAp = apSome(ap)
|
||||
) else (
|
||||
summaryCtx = TParameterPositionNone() and argAp = apNone()
|
||||
)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
|
||||
exists(
|
||||
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
|
||||
DataFlowCallable inner
|
||||
|
|
||||
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
cc = getCallContextReturn(inner, call, innercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
or
|
||||
exists(DataFlowCall call, Ap argAp0 |
|
||||
fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, cc, argAp, argAp0, config)
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
|
||||
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowStore(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
|
||||
Configuration config
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
exists(DataFlowType contentType |
|
||||
fwdFlow(node1, state, cc, argAp, ap1, config) and
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
|
||||
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
|
||||
typecheckStore(ap1, contentType)
|
||||
)
|
||||
@@ -1366,7 +1427,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
|
||||
exists(TypedContent tc |
|
||||
fwdFlowStore(_, tail, tc, _, _, _, _, config) and
|
||||
fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and
|
||||
tc.getContent() = c and
|
||||
cons = apCons(tc, tail)
|
||||
)
|
||||
@@ -1374,21 +1435,21 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead(
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
|
||||
Configuration config
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
fwdFlow(node1, state, cc, argAp, ap, config) and
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
getHeadContent(ap) = c
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIn(
|
||||
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp,
|
||||
Ap ap, Configuration config
|
||||
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ArgNodeEx arg, boolean allowsFieldFlow |
|
||||
fwdFlow(arg, state, outercc, argAp, ap, config) and
|
||||
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
|
||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
@@ -1396,29 +1457,19 @@ private module MkStage<StageSig PrevStage> {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowOutNotFromArg(
|
||||
NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
|
||||
private predicate fwdFlowOutFromArg(
|
||||
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(
|
||||
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
|
||||
DataFlowCallable inner
|
||||
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
||||
|
|
||||
fwdFlow(ret, state, innercc, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
ccOut = getCallContextReturn(inner, call, innercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowOutFromArg(
|
||||
DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
|
||||
fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
|
||||
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
|
||||
config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1428,11 +1479,13 @@ private module MkStage<StageSig PrevStage> {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(
|
||||
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
ParameterPosition pos, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
|
||||
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
|
||||
exists(ParamNodeEx param |
|
||||
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
|
||||
pos = param.getPosition()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1440,27 +1493,40 @@ private module MkStage<StageSig PrevStage> {
|
||||
private predicate storeStepFwd(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
|
||||
) {
|
||||
fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
|
||||
fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and
|
||||
ap2 = apCons(tc, ap1) and
|
||||
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config)
|
||||
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config)
|
||||
}
|
||||
|
||||
private predicate readStepFwd(
|
||||
NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
|
||||
) {
|
||||
fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and
|
||||
fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and
|
||||
fwdFlowConsCand(ap1, c, ap2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
|
||||
exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
|
||||
fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
|
||||
private predicate returnFlowsThrough0(
|
||||
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
|
||||
ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(boolean allowsFieldFlow |
|
||||
fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
|
||||
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
|
||||
pragma[only_bind_into](config))
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnFlowsThrough(
|
||||
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c, ParameterPosition ppos |
|
||||
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
|
||||
p.isParameterOf(c, ppos) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1468,118 +1534,130 @@ private module MkStage<StageSig PrevStage> {
|
||||
private predicate flowThroughIntoCall(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
|
||||
PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
|
||||
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnNodeMayFlowThrough(
|
||||
RetNodeEx ret, FlowState state, Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
|
||||
exists(Ap argAp |
|
||||
flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
|
||||
returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` with access path `ap` is part of a path from a source to a
|
||||
* sink in the configuration `config`.
|
||||
*
|
||||
* The Boolean `toReturn` records whether the node must be returned from the
|
||||
* enclosing callable in order to reach a sink, and if so, `returnAp` records
|
||||
* the access path of the returned value.
|
||||
* The parameter `returnCtx` records whether (and how) the node must be returned
|
||||
* from the enclosing callable in order to reach a sink, and if so, `returnAp`
|
||||
* records the access path of the returned value.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate revFlow(
|
||||
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
revFlow0(node, state, toReturn, returnAp, ap, config) and
|
||||
fwdFlow(node, state, _, _, ap, config)
|
||||
revFlow0(node, state, returnCtx, returnAp, ap, config) and
|
||||
fwdFlow(node, state, _, _, _, ap, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlow0(
|
||||
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
fwdFlow(node, state, _, _, ap, config) and
|
||||
fwdFlow(node, state, _, _, _, ap, config) and
|
||||
sinkNode(node, state, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
(
|
||||
if hasSinkCallCtx(config)
|
||||
then returnCtx = TReturnCtxNoFlowThrough()
|
||||
else returnCtx = TReturnCtxNone()
|
||||
) and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0 |
|
||||
localStep(node, state, mid, state0, true, _, config, _) and
|
||||
revFlow(mid, state0, toReturn, returnAp, ap, config)
|
||||
revFlow(mid, state0, returnCtx, returnAp, ap, config)
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and
|
||||
revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
|
||||
revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
jumpStep(node, mid, config) and
|
||||
revFlow(mid, state, _, _, ap, config) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone()
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, ApNil nil |
|
||||
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
additionalJumpStep(node, mid, config) and
|
||||
revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
additionalJumpStateStep(node, state, mid, state0, config) and
|
||||
revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil,
|
||||
pragma[only_bind_into](config)) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Ap ap0, Content c |
|
||||
revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
|
||||
revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and
|
||||
revFlowConsCand(ap0, c, ap, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(NodeEx mid, Ap ap0 |
|
||||
revFlow(mid, state, toReturn, returnAp, ap0, config) and
|
||||
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
|
||||
readStepFwd(node, ap, _, mid, ap0, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
revFlowInNotToReturn(node, state, returnAp, ap, config) and
|
||||
toReturn = false
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
|
||||
flowIntoCall(_, node, p, allowsFieldFlow, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
returnCtx = TReturnCtxNone()
|
||||
)
|
||||
or
|
||||
exists(DataFlowCall call, Ap returnAp0 |
|
||||
revFlowInToReturn(call, node, state, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 |
|
||||
revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
revFlowOut(_, node, state, _, _, ap, config) and
|
||||
toReturn = true and
|
||||
if returnNodeMayFlowThrough(node, state, ap, config)
|
||||
then returnAp = apSome(ap)
|
||||
else returnAp = apNone()
|
||||
exists(ReturnKindExt kind |
|
||||
revFlowOut(_, node, kind, state, _, _, ap, config) and
|
||||
if returnFlowsThrough(node, kind, state, _, _, _, ap, config)
|
||||
then (
|
||||
returnCtx = TReturnCtxMaybeFlowThrough(kind) and
|
||||
returnAp = apSome(ap)
|
||||
) else (
|
||||
returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowStore(
|
||||
Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid,
|
||||
boolean toReturn, ApOption returnAp, Configuration config
|
||||
ReturnCtx returnCtx, ApOption returnAp, Configuration config
|
||||
) {
|
||||
revFlow(mid, state, toReturn, returnAp, ap0, config) and
|
||||
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
|
||||
storeStepFwd(node, ap, tc, mid, ap0, config) and
|
||||
tc.getContent() = c
|
||||
}
|
||||
@@ -1599,35 +1677,27 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowOut(
|
||||
DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx,
|
||||
ApOption returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(NodeEx out, boolean allowsFieldFlow |
|
||||
revFlow(out, state, toReturn, returnAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowInNotToReturn(
|
||||
ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, false, returnAp, ap, config) and
|
||||
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
|
||||
revFlow(out, state, returnCtx, returnAp, ap, config) and
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowInToReturn(
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, true, apSome(returnAp), ap, config) and
|
||||
revFlow(pragma[only_bind_into](p), state,
|
||||
TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
|
||||
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1638,11 +1708,12 @@ private module MkStage<StageSig PrevStage> {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate revFlowIsReturned(
|
||||
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(RetNodeEx ret, FlowState state, CcCall ccc |
|
||||
revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and
|
||||
fwdFlow(ret, state, ccc, apSome(_), ap, config) and
|
||||
revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and
|
||||
returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and
|
||||
matchesCall(ccc, call)
|
||||
)
|
||||
}
|
||||
@@ -1713,40 +1784,39 @@ private module MkStage<StageSig PrevStage> {
|
||||
validAp(ap, config)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate parameterFlow(
|
||||
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
|
||||
pragma[nomagic]
|
||||
private predicate parameterFlowsThroughRev(
|
||||
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
|
||||
) {
|
||||
revFlow(p, _, true, apSome(ap0), ap, config) and
|
||||
c = p.getEnclosingCallable()
|
||||
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
}
|
||||
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||
exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
|
||||
parameterFlow(p, ap, ap0, c, config) and
|
||||
c = ret.getEnclosingCallable() and
|
||||
revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_),
|
||||
pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and
|
||||
fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
pragma[nomagic]
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
|
||||
exists(RetNodeEx ret, ReturnKindExt kind |
|
||||
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
|
||||
parameterFlowsThroughRev(p, ap, kind, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
|
||||
exists(ParamNodeEx p, Ap ap |
|
||||
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
|
||||
parameterFlowsThroughRev(p, ap, kind, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
|
||||
exists(
|
||||
Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
|
||||
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
|
||||
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
||||
|
|
||||
revFlow(arg, state, toReturn, returnAp, ap, config) and
|
||||
revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
|
||||
revFlow(arg, state, returnCtx, returnAp, ap, config) and
|
||||
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1754,14 +1824,13 @@ private module MkStage<StageSig PrevStage> {
|
||||
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
|
||||
) {
|
||||
fwd = true and
|
||||
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and
|
||||
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and
|
||||
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
|
||||
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
|
||||
states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and
|
||||
states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and
|
||||
tuples =
|
||||
count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
|
||||
fwdFlow(n, state, cc, argAp, ap, config)
|
||||
)
|
||||
count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config))
|
||||
or
|
||||
fwd = false and
|
||||
nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
|
||||
@@ -1769,8 +1838,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
|
||||
states = count(FlowState state | revFlow(_, state, _, _, _, config)) and
|
||||
tuples =
|
||||
count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap |
|
||||
revFlow(n, state, b, retAp, ap, config)
|
||||
count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap |
|
||||
revFlow(n, state, returnCtx, retAp, ap, config)
|
||||
)
|
||||
}
|
||||
/* End: Stage logic. */
|
||||
@@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
|
||||
exists(lcc)
|
||||
}
|
||||
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
|
||||
|
||||
predicate flowIntoCall = flowIntoCallNodeCand1/5;
|
||||
|
||||
@@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand2(
|
||||
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
|
||||
flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and
|
||||
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
|
||||
Stage2::revFlowAlias(node1, pragma[only_bind_into](config))
|
||||
}
|
||||
@@ -2021,8 +2091,8 @@ private module LocalFlowBigStep {
|
||||
exists(NodeEx next | Stage2::revFlow(next, state, config) |
|
||||
jumpStep(node, next, config) or
|
||||
additionalJumpStep(node, next, config) or
|
||||
flowIntoCallNodeCand1(_, node, next, config) or
|
||||
flowOutOfCallNodeCand1(_, node, next, config) or
|
||||
flowIntoCallNodeCand2(_, node, next, _, config) or
|
||||
flowOutOfCallNodeCand2(_, node, _, next, _, config) or
|
||||
Stage2::storeStepCand(node, _, _, next, _, config) or
|
||||
Stage2::readStepCand(node, _, next, config)
|
||||
)
|
||||
@@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
|
||||
}
|
||||
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
|
||||
|
||||
predicate flowIntoCall = flowIntoCallNodeCand2/5;
|
||||
|
||||
@@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx(
|
||||
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
|
||||
) {
|
||||
exists(AccessPathFront apf |
|
||||
Stage3::revFlow(node, state, true, _, apf, config) and
|
||||
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
|
||||
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
|
||||
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
|
||||
config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
|
||||
pragma[nomagic]
|
||||
predicate flowOutOfCall(
|
||||
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
exists(FlowState state |
|
||||
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
|
||||
flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
|
||||
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
|
||||
pragma[only_bind_into](config))
|
||||
@@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeMayUseSummary0(
|
||||
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
|
||||
NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa,
|
||||
Configuration config
|
||||
) {
|
||||
exists(AccessPathApprox apa0 |
|
||||
Stage4::parameterMayFlowThrough(_, c, _, _) and
|
||||
Stage4::revFlow(n, state, true, _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
|
||||
n.getEnclosingCallable() = c
|
||||
c = n.getEnclosingCallable() and
|
||||
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
|
||||
TAccessPathApproxSome(apa), apa0, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2522,9 +2595,10 @@ pragma[nomagic]
|
||||
private predicate nodeMayUseSummary(
|
||||
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c |
|
||||
Stage4::parameterMayFlowThrough(_, c, apa, config) and
|
||||
nodeMayUseSummary0(n, c, state, apa, config)
|
||||
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
|
||||
Stage4::parameterMayFlowThrough(p, apa, config) and
|
||||
nodeMayUseSummary0(n, c, pos, state, apa, config) and
|
||||
p.isParameterOf(c, pos)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2532,7 +2606,7 @@ private newtype TSummaryCtx =
|
||||
TSummaryCtxNone() or
|
||||
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
|
||||
exists(Configuration config |
|
||||
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
|
||||
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
|
||||
Stage4::revFlow(p, state, _, config)
|
||||
)
|
||||
}
|
||||
@@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough(
|
||||
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
|
||||
AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos |
|
||||
exists(PathNodeMid mid, RetNodeEx ret |
|
||||
pathNode(mid, ret, state, cc, sc, ap, config, _) and
|
||||
kind = ret.getKind() and
|
||||
apa = ap.getApprox() and
|
||||
pos = sc.getParameterPos() and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
sc.getParamNode().allowParameterReturnInSelf()
|
||||
)
|
||||
parameterFlowThroughAllowed(sc.getParamNode(), kind)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ abstract class Configuration extends string {
|
||||
/**
|
||||
* Holds if `sink` is a relevant data flow sink accepting `state`.
|
||||
*/
|
||||
predicate isSink(Node source, FlowState state) { none() }
|
||||
predicate isSink(Node sink, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited. This completely removes
|
||||
@@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
|
||||
}
|
||||
|
||||
ParameterPosition getPosition() { this.isParameterOf(_, result) }
|
||||
|
||||
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
|
||||
}
|
||||
|
||||
private class RetNodeEx extends NodeEx {
|
||||
@@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if flow from `p` to a return node of kind `kind` is allowed.
|
||||
*
|
||||
* We don't expect a parameter to return stored in itself, unless
|
||||
* explicitly allowed
|
||||
*/
|
||||
bindingset[p, kind]
|
||||
private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) {
|
||||
exists(ParameterPosition pos | p.isParameterOf(_, pos) |
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
allowParameterReturnInSelfCached(p.asNode())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if flow from a parameter at position `pos` inside `c` to a return node of
|
||||
* kind `kind` is allowed.
|
||||
*
|
||||
* We don't expect a parameter to return stored in itself, unless
|
||||
* explicitly allowed
|
||||
*/
|
||||
bindingset[c, pos, kind]
|
||||
private predicate parameterFlowThroughAllowed(
|
||||
DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
p.isParameterOf(c, pos) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
private module Stage1 implements StageSig {
|
||||
class Ap = Unit;
|
||||
|
||||
@@ -981,21 +1011,22 @@ private module Stage1 implements StageSig {
|
||||
* candidate for the origin of a summary.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||
exists(ReturnKindExt kind |
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
|
||||
exists(DataFlowCallable c, ReturnKindExt kind |
|
||||
throughFlowNodeCand(p, config) and
|
||||
returnFlowCallableNodeCand(c, kind, config) and
|
||||
p.getEnclosingCallable() = c and
|
||||
exists(ap) and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
|
||||
throughFlowNodeCand(ret, config) and
|
||||
kind = ret.getKind()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
|
||||
exists(ArgNodeEx arg, boolean toReturn |
|
||||
@@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1(
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand1(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config
|
||||
) {
|
||||
viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
|
||||
Stage1::revFlow(ret, config) and
|
||||
not outBarrier(ret, config) and
|
||||
not inBarrier(out, config)
|
||||
exists(ReturnPosition pos |
|
||||
viableReturnPosOutNodeCand1(call, pos, out, config) and
|
||||
pos = ret.getReturnPosition() and
|
||||
kind = pos.getKind() and
|
||||
Stage1::revFlow(ret, config) and
|
||||
not outBarrier(ret, config) and
|
||||
not inBarrier(out, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1(
|
||||
* edge in the graph of paths between sources and sinks that ignores call
|
||||
* contexts.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private int branch(NodeEx n1, Configuration conf) {
|
||||
result =
|
||||
strictcount(NodeEx n |
|
||||
flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
|
||||
flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) {
|
||||
* edge in the graph of paths between sources and sinks that ignores call
|
||||
* contexts.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private int join(NodeEx n2, Configuration conf) {
|
||||
result =
|
||||
strictcount(NodeEx n |
|
||||
flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
|
||||
flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand1(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
flowOutOfCallNodeCand1(call, ret, out, config) and
|
||||
flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and
|
||||
exists(int b, int j |
|
||||
b = branch(ret, config) and
|
||||
j = join(out, config) and
|
||||
b = branch(ret, pragma[only_bind_into](config)) and
|
||||
j = join(out, pragma[only_bind_into](config)) and
|
||||
if b.minimum(j) <= config.fieldFlowBranchLimit()
|
||||
then allowsFieldFlow = true
|
||||
else allowsFieldFlow = false
|
||||
@@ -1136,10 +1174,10 @@ pragma[nomagic]
|
||||
private predicate flowIntoCallNodeCand1(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowIntoCallNodeCand1(call, arg, p, config) and
|
||||
flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and
|
||||
exists(int b, int j |
|
||||
b = branch(arg, config) and
|
||||
j = join(p, config) and
|
||||
b = branch(arg, pragma[only_bind_into](config)) and
|
||||
j = join(p, pragma[only_bind_into](config)) and
|
||||
if b.minimum(j) <= config.fieldFlowBranchLimit()
|
||||
then allowsFieldFlow = true
|
||||
else allowsFieldFlow = false
|
||||
@@ -1156,7 +1194,9 @@ private signature module StageSig {
|
||||
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
|
||||
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
|
||||
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
|
||||
|
||||
predicate storeStepCand(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
|
||||
@@ -1222,7 +1262,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
);
|
||||
|
||||
predicate flowOutOfCall(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
);
|
||||
|
||||
predicate flowIntoCall(
|
||||
@@ -1247,14 +1288,14 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowThroughOutOfCall(
|
||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
|
||||
NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
|
||||
pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call)
|
||||
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call) and
|
||||
c = ret.getEnclosingCallable()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1262,29 +1303,32 @@ private module MkStage<StageSig PrevStage> {
|
||||
* configuration `config`.
|
||||
*
|
||||
* The call context `cc` records whether the node is reached through an
|
||||
* argument in a call, and if so, `argAp` records the access path of that
|
||||
* argument.
|
||||
* argument in a call, and if so, `summaryCtx` and `argAp` record the
|
||||
* corresponding parameter position and access path of that argument, respectively.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate fwdFlow(
|
||||
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow0(node, state, cc, argAp, ap, config) and
|
||||
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
|
||||
filter(node, state, ap, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlow0(
|
||||
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
sourceNode(node, state, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
argAp = apNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
ap = getApNil(node)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
|
||||
fwdFlow(mid, state0, cc, argAp, ap0, config) and
|
||||
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
|
||||
localCc = getLocalCc(mid, cc)
|
||||
|
|
||||
localStep(mid, state0, node, state, true, _, config, localCc) and
|
||||
@@ -1295,65 +1339,82 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
jumpStep(mid, node, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone()
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, ApNil nil |
|
||||
fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and
|
||||
additionalJumpStep(mid, node, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and
|
||||
additionalJumpStateStep(mid, state0, node, state, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(TypedContent tc, Ap ap0 |
|
||||
fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
|
||||
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
|
||||
ap = apCons(tc, ap0)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Ap ap0, Content c |
|
||||
fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
|
||||
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
|
||||
fwdFlowConsCand(ap0, c, ap, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(ApApprox apa |
|
||||
fwdFlowIn(_, node, state, _, cc, _, ap, config) and
|
||||
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
|
||||
apa = getApprox(ap) and
|
||||
if PrevStage::parameterMayFlowThrough(node, _, apa, config)
|
||||
then argAp = apSome(ap)
|
||||
else argAp = apNone()
|
||||
if PrevStage::parameterMayFlowThrough(node, apa, config)
|
||||
then (
|
||||
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
|
||||
argAp = apSome(ap)
|
||||
) else (
|
||||
summaryCtx = TParameterPositionNone() and argAp = apNone()
|
||||
)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
|
||||
exists(
|
||||
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
|
||||
DataFlowCallable inner
|
||||
|
|
||||
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
cc = getCallContextReturn(inner, call, innercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
or
|
||||
exists(DataFlowCall call, Ap argAp0 |
|
||||
fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, cc, argAp, argAp0, config)
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
|
||||
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowStore(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
|
||||
Configuration config
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
exists(DataFlowType contentType |
|
||||
fwdFlow(node1, state, cc, argAp, ap1, config) and
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
|
||||
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
|
||||
typecheckStore(ap1, contentType)
|
||||
)
|
||||
@@ -1366,7 +1427,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
|
||||
exists(TypedContent tc |
|
||||
fwdFlowStore(_, tail, tc, _, _, _, _, config) and
|
||||
fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and
|
||||
tc.getContent() = c and
|
||||
cons = apCons(tc, tail)
|
||||
)
|
||||
@@ -1374,21 +1435,21 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead(
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
|
||||
Configuration config
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
fwdFlow(node1, state, cc, argAp, ap, config) and
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
getHeadContent(ap) = c
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIn(
|
||||
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp,
|
||||
Ap ap, Configuration config
|
||||
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ArgNodeEx arg, boolean allowsFieldFlow |
|
||||
fwdFlow(arg, state, outercc, argAp, ap, config) and
|
||||
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
|
||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
@@ -1396,29 +1457,19 @@ private module MkStage<StageSig PrevStage> {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowOutNotFromArg(
|
||||
NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
|
||||
private predicate fwdFlowOutFromArg(
|
||||
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(
|
||||
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
|
||||
DataFlowCallable inner
|
||||
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
||||
|
|
||||
fwdFlow(ret, state, innercc, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
ccOut = getCallContextReturn(inner, call, innercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowOutFromArg(
|
||||
DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
|
||||
fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
|
||||
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
|
||||
config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1428,11 +1479,13 @@ private module MkStage<StageSig PrevStage> {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(
|
||||
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
ParameterPosition pos, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
|
||||
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
|
||||
exists(ParamNodeEx param |
|
||||
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
|
||||
pos = param.getPosition()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1440,27 +1493,40 @@ private module MkStage<StageSig PrevStage> {
|
||||
private predicate storeStepFwd(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
|
||||
) {
|
||||
fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
|
||||
fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and
|
||||
ap2 = apCons(tc, ap1) and
|
||||
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config)
|
||||
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config)
|
||||
}
|
||||
|
||||
private predicate readStepFwd(
|
||||
NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
|
||||
) {
|
||||
fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and
|
||||
fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and
|
||||
fwdFlowConsCand(ap1, c, ap2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
|
||||
exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
|
||||
fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
|
||||
private predicate returnFlowsThrough0(
|
||||
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
|
||||
ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(boolean allowsFieldFlow |
|
||||
fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
|
||||
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
|
||||
pragma[only_bind_into](config))
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnFlowsThrough(
|
||||
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c, ParameterPosition ppos |
|
||||
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
|
||||
p.isParameterOf(c, ppos) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1468,118 +1534,130 @@ private module MkStage<StageSig PrevStage> {
|
||||
private predicate flowThroughIntoCall(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
|
||||
PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
|
||||
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnNodeMayFlowThrough(
|
||||
RetNodeEx ret, FlowState state, Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
|
||||
exists(Ap argAp |
|
||||
flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
|
||||
returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` with access path `ap` is part of a path from a source to a
|
||||
* sink in the configuration `config`.
|
||||
*
|
||||
* The Boolean `toReturn` records whether the node must be returned from the
|
||||
* enclosing callable in order to reach a sink, and if so, `returnAp` records
|
||||
* the access path of the returned value.
|
||||
* The parameter `returnCtx` records whether (and how) the node must be returned
|
||||
* from the enclosing callable in order to reach a sink, and if so, `returnAp`
|
||||
* records the access path of the returned value.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate revFlow(
|
||||
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
revFlow0(node, state, toReturn, returnAp, ap, config) and
|
||||
fwdFlow(node, state, _, _, ap, config)
|
||||
revFlow0(node, state, returnCtx, returnAp, ap, config) and
|
||||
fwdFlow(node, state, _, _, _, ap, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlow0(
|
||||
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
fwdFlow(node, state, _, _, ap, config) and
|
||||
fwdFlow(node, state, _, _, _, ap, config) and
|
||||
sinkNode(node, state, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
(
|
||||
if hasSinkCallCtx(config)
|
||||
then returnCtx = TReturnCtxNoFlowThrough()
|
||||
else returnCtx = TReturnCtxNone()
|
||||
) and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0 |
|
||||
localStep(node, state, mid, state0, true, _, config, _) and
|
||||
revFlow(mid, state0, toReturn, returnAp, ap, config)
|
||||
revFlow(mid, state0, returnCtx, returnAp, ap, config)
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and
|
||||
revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
|
||||
revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
jumpStep(node, mid, config) and
|
||||
revFlow(mid, state, _, _, ap, config) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone()
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, ApNil nil |
|
||||
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
additionalJumpStep(node, mid, config) and
|
||||
revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
additionalJumpStateStep(node, state, mid, state0, config) and
|
||||
revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil,
|
||||
pragma[only_bind_into](config)) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Ap ap0, Content c |
|
||||
revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
|
||||
revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and
|
||||
revFlowConsCand(ap0, c, ap, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(NodeEx mid, Ap ap0 |
|
||||
revFlow(mid, state, toReturn, returnAp, ap0, config) and
|
||||
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
|
||||
readStepFwd(node, ap, _, mid, ap0, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
revFlowInNotToReturn(node, state, returnAp, ap, config) and
|
||||
toReturn = false
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
|
||||
flowIntoCall(_, node, p, allowsFieldFlow, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
returnCtx = TReturnCtxNone()
|
||||
)
|
||||
or
|
||||
exists(DataFlowCall call, Ap returnAp0 |
|
||||
revFlowInToReturn(call, node, state, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 |
|
||||
revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
revFlowOut(_, node, state, _, _, ap, config) and
|
||||
toReturn = true and
|
||||
if returnNodeMayFlowThrough(node, state, ap, config)
|
||||
then returnAp = apSome(ap)
|
||||
else returnAp = apNone()
|
||||
exists(ReturnKindExt kind |
|
||||
revFlowOut(_, node, kind, state, _, _, ap, config) and
|
||||
if returnFlowsThrough(node, kind, state, _, _, _, ap, config)
|
||||
then (
|
||||
returnCtx = TReturnCtxMaybeFlowThrough(kind) and
|
||||
returnAp = apSome(ap)
|
||||
) else (
|
||||
returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowStore(
|
||||
Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid,
|
||||
boolean toReturn, ApOption returnAp, Configuration config
|
||||
ReturnCtx returnCtx, ApOption returnAp, Configuration config
|
||||
) {
|
||||
revFlow(mid, state, toReturn, returnAp, ap0, config) and
|
||||
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
|
||||
storeStepFwd(node, ap, tc, mid, ap0, config) and
|
||||
tc.getContent() = c
|
||||
}
|
||||
@@ -1599,35 +1677,27 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowOut(
|
||||
DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx,
|
||||
ApOption returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(NodeEx out, boolean allowsFieldFlow |
|
||||
revFlow(out, state, toReturn, returnAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowInNotToReturn(
|
||||
ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, false, returnAp, ap, config) and
|
||||
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
|
||||
revFlow(out, state, returnCtx, returnAp, ap, config) and
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowInToReturn(
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, true, apSome(returnAp), ap, config) and
|
||||
revFlow(pragma[only_bind_into](p), state,
|
||||
TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
|
||||
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1638,11 +1708,12 @@ private module MkStage<StageSig PrevStage> {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate revFlowIsReturned(
|
||||
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(RetNodeEx ret, FlowState state, CcCall ccc |
|
||||
revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and
|
||||
fwdFlow(ret, state, ccc, apSome(_), ap, config) and
|
||||
revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and
|
||||
returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and
|
||||
matchesCall(ccc, call)
|
||||
)
|
||||
}
|
||||
@@ -1713,40 +1784,39 @@ private module MkStage<StageSig PrevStage> {
|
||||
validAp(ap, config)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate parameterFlow(
|
||||
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
|
||||
pragma[nomagic]
|
||||
private predicate parameterFlowsThroughRev(
|
||||
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
|
||||
) {
|
||||
revFlow(p, _, true, apSome(ap0), ap, config) and
|
||||
c = p.getEnclosingCallable()
|
||||
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
}
|
||||
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||
exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
|
||||
parameterFlow(p, ap, ap0, c, config) and
|
||||
c = ret.getEnclosingCallable() and
|
||||
revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_),
|
||||
pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and
|
||||
fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
pragma[nomagic]
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
|
||||
exists(RetNodeEx ret, ReturnKindExt kind |
|
||||
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
|
||||
parameterFlowsThroughRev(p, ap, kind, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
|
||||
exists(ParamNodeEx p, Ap ap |
|
||||
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
|
||||
parameterFlowsThroughRev(p, ap, kind, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
|
||||
exists(
|
||||
Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
|
||||
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
|
||||
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
||||
|
|
||||
revFlow(arg, state, toReturn, returnAp, ap, config) and
|
||||
revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
|
||||
revFlow(arg, state, returnCtx, returnAp, ap, config) and
|
||||
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1754,14 +1824,13 @@ private module MkStage<StageSig PrevStage> {
|
||||
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
|
||||
) {
|
||||
fwd = true and
|
||||
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and
|
||||
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and
|
||||
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
|
||||
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
|
||||
states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and
|
||||
states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and
|
||||
tuples =
|
||||
count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
|
||||
fwdFlow(n, state, cc, argAp, ap, config)
|
||||
)
|
||||
count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config))
|
||||
or
|
||||
fwd = false and
|
||||
nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
|
||||
@@ -1769,8 +1838,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
|
||||
states = count(FlowState state | revFlow(_, state, _, _, _, config)) and
|
||||
tuples =
|
||||
count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap |
|
||||
revFlow(n, state, b, retAp, ap, config)
|
||||
count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap |
|
||||
revFlow(n, state, returnCtx, retAp, ap, config)
|
||||
)
|
||||
}
|
||||
/* End: Stage logic. */
|
||||
@@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
|
||||
exists(lcc)
|
||||
}
|
||||
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
|
||||
|
||||
predicate flowIntoCall = flowIntoCallNodeCand1/5;
|
||||
|
||||
@@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand2(
|
||||
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
|
||||
flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and
|
||||
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
|
||||
Stage2::revFlowAlias(node1, pragma[only_bind_into](config))
|
||||
}
|
||||
@@ -2021,8 +2091,8 @@ private module LocalFlowBigStep {
|
||||
exists(NodeEx next | Stage2::revFlow(next, state, config) |
|
||||
jumpStep(node, next, config) or
|
||||
additionalJumpStep(node, next, config) or
|
||||
flowIntoCallNodeCand1(_, node, next, config) or
|
||||
flowOutOfCallNodeCand1(_, node, next, config) or
|
||||
flowIntoCallNodeCand2(_, node, next, _, config) or
|
||||
flowOutOfCallNodeCand2(_, node, _, next, _, config) or
|
||||
Stage2::storeStepCand(node, _, _, next, _, config) or
|
||||
Stage2::readStepCand(node, _, next, config)
|
||||
)
|
||||
@@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
|
||||
}
|
||||
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
|
||||
|
||||
predicate flowIntoCall = flowIntoCallNodeCand2/5;
|
||||
|
||||
@@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx(
|
||||
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
|
||||
) {
|
||||
exists(AccessPathFront apf |
|
||||
Stage3::revFlow(node, state, true, _, apf, config) and
|
||||
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
|
||||
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
|
||||
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
|
||||
config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
|
||||
pragma[nomagic]
|
||||
predicate flowOutOfCall(
|
||||
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
exists(FlowState state |
|
||||
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
|
||||
flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
|
||||
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
|
||||
pragma[only_bind_into](config))
|
||||
@@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeMayUseSummary0(
|
||||
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
|
||||
NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa,
|
||||
Configuration config
|
||||
) {
|
||||
exists(AccessPathApprox apa0 |
|
||||
Stage4::parameterMayFlowThrough(_, c, _, _) and
|
||||
Stage4::revFlow(n, state, true, _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
|
||||
n.getEnclosingCallable() = c
|
||||
c = n.getEnclosingCallable() and
|
||||
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
|
||||
TAccessPathApproxSome(apa), apa0, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2522,9 +2595,10 @@ pragma[nomagic]
|
||||
private predicate nodeMayUseSummary(
|
||||
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c |
|
||||
Stage4::parameterMayFlowThrough(_, c, apa, config) and
|
||||
nodeMayUseSummary0(n, c, state, apa, config)
|
||||
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
|
||||
Stage4::parameterMayFlowThrough(p, apa, config) and
|
||||
nodeMayUseSummary0(n, c, pos, state, apa, config) and
|
||||
p.isParameterOf(c, pos)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2532,7 +2606,7 @@ private newtype TSummaryCtx =
|
||||
TSummaryCtxNone() or
|
||||
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
|
||||
exists(Configuration config |
|
||||
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
|
||||
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
|
||||
Stage4::revFlow(p, state, _, config)
|
||||
)
|
||||
}
|
||||
@@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough(
|
||||
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
|
||||
AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos |
|
||||
exists(PathNodeMid mid, RetNodeEx ret |
|
||||
pathNode(mid, ret, state, cc, sc, ap, config, _) and
|
||||
kind = ret.getKind() and
|
||||
apa = ap.getApprox() and
|
||||
pos = sc.getParameterPos() and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
sc.getParamNode().allowParameterReturnInSelf()
|
||||
)
|
||||
parameterFlowThroughAllowed(sc.getParamNode(), kind)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -915,6 +915,17 @@ private module Cached {
|
||||
TDataFlowCallNone() or
|
||||
TDataFlowCallSome(DataFlowCall call)
|
||||
|
||||
cached
|
||||
newtype TParameterPositionOption =
|
||||
TParameterPositionNone() or
|
||||
TParameterPositionSome(ParameterPosition pos)
|
||||
|
||||
cached
|
||||
newtype TReturnCtx =
|
||||
TReturnCtxNone() or
|
||||
TReturnCtxNoFlowThrough() or
|
||||
TReturnCtxMaybeFlowThrough(ReturnKindExt kind)
|
||||
|
||||
cached
|
||||
newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) }
|
||||
|
||||
@@ -1304,6 +1315,44 @@ class DataFlowCallOption extends TDataFlowCallOption {
|
||||
}
|
||||
}
|
||||
|
||||
/** An optional `ParameterPosition`. */
|
||||
class ParameterPositionOption extends TParameterPositionOption {
|
||||
string toString() {
|
||||
this = TParameterPositionNone() and
|
||||
result = "(none)"
|
||||
or
|
||||
exists(ParameterPosition pos |
|
||||
this = TParameterPositionSome(pos) and
|
||||
result = pos.toString()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A return context used to calculate flow summaries in reverse flow.
|
||||
*
|
||||
* The possible values are:
|
||||
*
|
||||
* - `TReturnCtxNone()`: no return flow.
|
||||
* - `TReturnCtxNoFlowThrough()`: return flow, but flow through is not possible.
|
||||
* - `TReturnCtxMaybeFlowThrough(ReturnKindExt kind)`: return flow, of kind `kind`, and
|
||||
* flow through may be possible.
|
||||
*/
|
||||
class ReturnCtx extends TReturnCtx {
|
||||
string toString() {
|
||||
this = TReturnCtxNone() and
|
||||
result = "(none)"
|
||||
or
|
||||
this = TReturnCtxNoFlowThrough() and
|
||||
result = "(no flow through)"
|
||||
or
|
||||
exists(ReturnKindExt kind |
|
||||
this = TReturnCtxMaybeFlowThrough(kind) and
|
||||
result = kind.toString()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** A `Content` tagged with the type of a containing object. */
|
||||
class TypedContent extends MkTypedContent {
|
||||
private Content c;
|
||||
|
||||
@@ -244,4 +244,20 @@ module Consistency {
|
||||
not callable = viableCallable(call) and
|
||||
not any(ConsistencyConfiguration c).viableImplInCallContextTooLargeExclude(call, ctx, callable)
|
||||
}
|
||||
|
||||
query predicate uniqueParameterNodeAtPosition(
|
||||
DataFlowCallable c, ParameterPosition pos, Node p, string msg
|
||||
) {
|
||||
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
|
||||
) {
|
||||
isParameterNode(p, c, pos) and
|
||||
not exists(unique(ParameterPosition pos0 | isParameterNode(p, c, pos0))) and
|
||||
msg = "Parameter node with multiple positions."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/cpp-all
|
||||
version: 0.4.5-dev
|
||||
version: 0.4.6-dev
|
||||
groups: cpp
|
||||
dbscheme: semmlecode.cpp.dbscheme
|
||||
extractor: cpp
|
||||
|
||||
@@ -70,7 +70,7 @@ abstract class Configuration extends string {
|
||||
/**
|
||||
* Holds if `sink` is a relevant data flow sink accepting `state`.
|
||||
*/
|
||||
predicate isSink(Node source, FlowState state) { none() }
|
||||
predicate isSink(Node sink, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited. This completely removes
|
||||
@@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
|
||||
}
|
||||
|
||||
ParameterPosition getPosition() { this.isParameterOf(_, result) }
|
||||
|
||||
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
|
||||
}
|
||||
|
||||
private class RetNodeEx extends NodeEx {
|
||||
@@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if flow from `p` to a return node of kind `kind` is allowed.
|
||||
*
|
||||
* We don't expect a parameter to return stored in itself, unless
|
||||
* explicitly allowed
|
||||
*/
|
||||
bindingset[p, kind]
|
||||
private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) {
|
||||
exists(ParameterPosition pos | p.isParameterOf(_, pos) |
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
allowParameterReturnInSelfCached(p.asNode())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if flow from a parameter at position `pos` inside `c` to a return node of
|
||||
* kind `kind` is allowed.
|
||||
*
|
||||
* We don't expect a parameter to return stored in itself, unless
|
||||
* explicitly allowed
|
||||
*/
|
||||
bindingset[c, pos, kind]
|
||||
private predicate parameterFlowThroughAllowed(
|
||||
DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
p.isParameterOf(c, pos) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
private module Stage1 implements StageSig {
|
||||
class Ap = Unit;
|
||||
|
||||
@@ -981,21 +1011,22 @@ private module Stage1 implements StageSig {
|
||||
* candidate for the origin of a summary.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||
exists(ReturnKindExt kind |
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
|
||||
exists(DataFlowCallable c, ReturnKindExt kind |
|
||||
throughFlowNodeCand(p, config) and
|
||||
returnFlowCallableNodeCand(c, kind, config) and
|
||||
p.getEnclosingCallable() = c and
|
||||
exists(ap) and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
|
||||
throughFlowNodeCand(ret, config) and
|
||||
kind = ret.getKind()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
|
||||
exists(ArgNodeEx arg, boolean toReturn |
|
||||
@@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1(
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand1(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config
|
||||
) {
|
||||
viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
|
||||
Stage1::revFlow(ret, config) and
|
||||
not outBarrier(ret, config) and
|
||||
not inBarrier(out, config)
|
||||
exists(ReturnPosition pos |
|
||||
viableReturnPosOutNodeCand1(call, pos, out, config) and
|
||||
pos = ret.getReturnPosition() and
|
||||
kind = pos.getKind() and
|
||||
Stage1::revFlow(ret, config) and
|
||||
not outBarrier(ret, config) and
|
||||
not inBarrier(out, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1(
|
||||
* edge in the graph of paths between sources and sinks that ignores call
|
||||
* contexts.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private int branch(NodeEx n1, Configuration conf) {
|
||||
result =
|
||||
strictcount(NodeEx n |
|
||||
flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
|
||||
flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) {
|
||||
* edge in the graph of paths between sources and sinks that ignores call
|
||||
* contexts.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private int join(NodeEx n2, Configuration conf) {
|
||||
result =
|
||||
strictcount(NodeEx n |
|
||||
flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
|
||||
flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand1(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
flowOutOfCallNodeCand1(call, ret, out, config) and
|
||||
flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and
|
||||
exists(int b, int j |
|
||||
b = branch(ret, config) and
|
||||
j = join(out, config) and
|
||||
b = branch(ret, pragma[only_bind_into](config)) and
|
||||
j = join(out, pragma[only_bind_into](config)) and
|
||||
if b.minimum(j) <= config.fieldFlowBranchLimit()
|
||||
then allowsFieldFlow = true
|
||||
else allowsFieldFlow = false
|
||||
@@ -1136,10 +1174,10 @@ pragma[nomagic]
|
||||
private predicate flowIntoCallNodeCand1(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowIntoCallNodeCand1(call, arg, p, config) and
|
||||
flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and
|
||||
exists(int b, int j |
|
||||
b = branch(arg, config) and
|
||||
j = join(p, config) and
|
||||
b = branch(arg, pragma[only_bind_into](config)) and
|
||||
j = join(p, pragma[only_bind_into](config)) and
|
||||
if b.minimum(j) <= config.fieldFlowBranchLimit()
|
||||
then allowsFieldFlow = true
|
||||
else allowsFieldFlow = false
|
||||
@@ -1156,7 +1194,9 @@ private signature module StageSig {
|
||||
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
|
||||
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
|
||||
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
|
||||
|
||||
predicate storeStepCand(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
|
||||
@@ -1222,7 +1262,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
);
|
||||
|
||||
predicate flowOutOfCall(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
);
|
||||
|
||||
predicate flowIntoCall(
|
||||
@@ -1247,14 +1288,14 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowThroughOutOfCall(
|
||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
|
||||
NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
|
||||
pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call)
|
||||
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call) and
|
||||
c = ret.getEnclosingCallable()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1262,29 +1303,32 @@ private module MkStage<StageSig PrevStage> {
|
||||
* configuration `config`.
|
||||
*
|
||||
* The call context `cc` records whether the node is reached through an
|
||||
* argument in a call, and if so, `argAp` records the access path of that
|
||||
* argument.
|
||||
* argument in a call, and if so, `summaryCtx` and `argAp` record the
|
||||
* corresponding parameter position and access path of that argument, respectively.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate fwdFlow(
|
||||
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow0(node, state, cc, argAp, ap, config) and
|
||||
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
|
||||
filter(node, state, ap, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlow0(
|
||||
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
sourceNode(node, state, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
argAp = apNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
ap = getApNil(node)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
|
||||
fwdFlow(mid, state0, cc, argAp, ap0, config) and
|
||||
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
|
||||
localCc = getLocalCc(mid, cc)
|
||||
|
|
||||
localStep(mid, state0, node, state, true, _, config, localCc) and
|
||||
@@ -1295,65 +1339,82 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
jumpStep(mid, node, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone()
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, ApNil nil |
|
||||
fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and
|
||||
additionalJumpStep(mid, node, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and
|
||||
additionalJumpStateStep(mid, state0, node, state, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(TypedContent tc, Ap ap0 |
|
||||
fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
|
||||
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
|
||||
ap = apCons(tc, ap0)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Ap ap0, Content c |
|
||||
fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
|
||||
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
|
||||
fwdFlowConsCand(ap0, c, ap, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(ApApprox apa |
|
||||
fwdFlowIn(_, node, state, _, cc, _, ap, config) and
|
||||
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
|
||||
apa = getApprox(ap) and
|
||||
if PrevStage::parameterMayFlowThrough(node, _, apa, config)
|
||||
then argAp = apSome(ap)
|
||||
else argAp = apNone()
|
||||
if PrevStage::parameterMayFlowThrough(node, apa, config)
|
||||
then (
|
||||
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
|
||||
argAp = apSome(ap)
|
||||
) else (
|
||||
summaryCtx = TParameterPositionNone() and argAp = apNone()
|
||||
)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
|
||||
exists(
|
||||
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
|
||||
DataFlowCallable inner
|
||||
|
|
||||
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
cc = getCallContextReturn(inner, call, innercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
or
|
||||
exists(DataFlowCall call, Ap argAp0 |
|
||||
fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, cc, argAp, argAp0, config)
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
|
||||
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowStore(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
|
||||
Configuration config
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
exists(DataFlowType contentType |
|
||||
fwdFlow(node1, state, cc, argAp, ap1, config) and
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
|
||||
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
|
||||
typecheckStore(ap1, contentType)
|
||||
)
|
||||
@@ -1366,7 +1427,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
|
||||
exists(TypedContent tc |
|
||||
fwdFlowStore(_, tail, tc, _, _, _, _, config) and
|
||||
fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and
|
||||
tc.getContent() = c and
|
||||
cons = apCons(tc, tail)
|
||||
)
|
||||
@@ -1374,21 +1435,21 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead(
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
|
||||
Configuration config
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
fwdFlow(node1, state, cc, argAp, ap, config) and
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
getHeadContent(ap) = c
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIn(
|
||||
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp,
|
||||
Ap ap, Configuration config
|
||||
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ArgNodeEx arg, boolean allowsFieldFlow |
|
||||
fwdFlow(arg, state, outercc, argAp, ap, config) and
|
||||
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
|
||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
@@ -1396,29 +1457,19 @@ private module MkStage<StageSig PrevStage> {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowOutNotFromArg(
|
||||
NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
|
||||
private predicate fwdFlowOutFromArg(
|
||||
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(
|
||||
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
|
||||
DataFlowCallable inner
|
||||
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
||||
|
|
||||
fwdFlow(ret, state, innercc, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
ccOut = getCallContextReturn(inner, call, innercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowOutFromArg(
|
||||
DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
|
||||
fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
|
||||
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
|
||||
config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1428,11 +1479,13 @@ private module MkStage<StageSig PrevStage> {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(
|
||||
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
ParameterPosition pos, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
|
||||
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
|
||||
exists(ParamNodeEx param |
|
||||
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
|
||||
pos = param.getPosition()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1440,27 +1493,40 @@ private module MkStage<StageSig PrevStage> {
|
||||
private predicate storeStepFwd(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
|
||||
) {
|
||||
fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
|
||||
fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and
|
||||
ap2 = apCons(tc, ap1) and
|
||||
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config)
|
||||
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config)
|
||||
}
|
||||
|
||||
private predicate readStepFwd(
|
||||
NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
|
||||
) {
|
||||
fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and
|
||||
fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and
|
||||
fwdFlowConsCand(ap1, c, ap2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
|
||||
exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
|
||||
fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
|
||||
private predicate returnFlowsThrough0(
|
||||
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
|
||||
ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(boolean allowsFieldFlow |
|
||||
fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
|
||||
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
|
||||
pragma[only_bind_into](config))
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnFlowsThrough(
|
||||
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c, ParameterPosition ppos |
|
||||
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
|
||||
p.isParameterOf(c, ppos) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1468,118 +1534,130 @@ private module MkStage<StageSig PrevStage> {
|
||||
private predicate flowThroughIntoCall(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
|
||||
PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
|
||||
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnNodeMayFlowThrough(
|
||||
RetNodeEx ret, FlowState state, Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
|
||||
exists(Ap argAp |
|
||||
flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
|
||||
returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` with access path `ap` is part of a path from a source to a
|
||||
* sink in the configuration `config`.
|
||||
*
|
||||
* The Boolean `toReturn` records whether the node must be returned from the
|
||||
* enclosing callable in order to reach a sink, and if so, `returnAp` records
|
||||
* the access path of the returned value.
|
||||
* The parameter `returnCtx` records whether (and how) the node must be returned
|
||||
* from the enclosing callable in order to reach a sink, and if so, `returnAp`
|
||||
* records the access path of the returned value.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate revFlow(
|
||||
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
revFlow0(node, state, toReturn, returnAp, ap, config) and
|
||||
fwdFlow(node, state, _, _, ap, config)
|
||||
revFlow0(node, state, returnCtx, returnAp, ap, config) and
|
||||
fwdFlow(node, state, _, _, _, ap, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlow0(
|
||||
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
fwdFlow(node, state, _, _, ap, config) and
|
||||
fwdFlow(node, state, _, _, _, ap, config) and
|
||||
sinkNode(node, state, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
(
|
||||
if hasSinkCallCtx(config)
|
||||
then returnCtx = TReturnCtxNoFlowThrough()
|
||||
else returnCtx = TReturnCtxNone()
|
||||
) and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0 |
|
||||
localStep(node, state, mid, state0, true, _, config, _) and
|
||||
revFlow(mid, state0, toReturn, returnAp, ap, config)
|
||||
revFlow(mid, state0, returnCtx, returnAp, ap, config)
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and
|
||||
revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
|
||||
revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
jumpStep(node, mid, config) and
|
||||
revFlow(mid, state, _, _, ap, config) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone()
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, ApNil nil |
|
||||
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
additionalJumpStep(node, mid, config) and
|
||||
revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
additionalJumpStateStep(node, state, mid, state0, config) and
|
||||
revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil,
|
||||
pragma[only_bind_into](config)) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Ap ap0, Content c |
|
||||
revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
|
||||
revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and
|
||||
revFlowConsCand(ap0, c, ap, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(NodeEx mid, Ap ap0 |
|
||||
revFlow(mid, state, toReturn, returnAp, ap0, config) and
|
||||
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
|
||||
readStepFwd(node, ap, _, mid, ap0, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
revFlowInNotToReturn(node, state, returnAp, ap, config) and
|
||||
toReturn = false
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
|
||||
flowIntoCall(_, node, p, allowsFieldFlow, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
returnCtx = TReturnCtxNone()
|
||||
)
|
||||
or
|
||||
exists(DataFlowCall call, Ap returnAp0 |
|
||||
revFlowInToReturn(call, node, state, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 |
|
||||
revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
revFlowOut(_, node, state, _, _, ap, config) and
|
||||
toReturn = true and
|
||||
if returnNodeMayFlowThrough(node, state, ap, config)
|
||||
then returnAp = apSome(ap)
|
||||
else returnAp = apNone()
|
||||
exists(ReturnKindExt kind |
|
||||
revFlowOut(_, node, kind, state, _, _, ap, config) and
|
||||
if returnFlowsThrough(node, kind, state, _, _, _, ap, config)
|
||||
then (
|
||||
returnCtx = TReturnCtxMaybeFlowThrough(kind) and
|
||||
returnAp = apSome(ap)
|
||||
) else (
|
||||
returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowStore(
|
||||
Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid,
|
||||
boolean toReturn, ApOption returnAp, Configuration config
|
||||
ReturnCtx returnCtx, ApOption returnAp, Configuration config
|
||||
) {
|
||||
revFlow(mid, state, toReturn, returnAp, ap0, config) and
|
||||
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
|
||||
storeStepFwd(node, ap, tc, mid, ap0, config) and
|
||||
tc.getContent() = c
|
||||
}
|
||||
@@ -1599,35 +1677,27 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowOut(
|
||||
DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx,
|
||||
ApOption returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(NodeEx out, boolean allowsFieldFlow |
|
||||
revFlow(out, state, toReturn, returnAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowInNotToReturn(
|
||||
ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, false, returnAp, ap, config) and
|
||||
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
|
||||
revFlow(out, state, returnCtx, returnAp, ap, config) and
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowInToReturn(
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, true, apSome(returnAp), ap, config) and
|
||||
revFlow(pragma[only_bind_into](p), state,
|
||||
TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
|
||||
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1638,11 +1708,12 @@ private module MkStage<StageSig PrevStage> {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate revFlowIsReturned(
|
||||
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(RetNodeEx ret, FlowState state, CcCall ccc |
|
||||
revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and
|
||||
fwdFlow(ret, state, ccc, apSome(_), ap, config) and
|
||||
revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and
|
||||
returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and
|
||||
matchesCall(ccc, call)
|
||||
)
|
||||
}
|
||||
@@ -1713,40 +1784,39 @@ private module MkStage<StageSig PrevStage> {
|
||||
validAp(ap, config)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate parameterFlow(
|
||||
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
|
||||
pragma[nomagic]
|
||||
private predicate parameterFlowsThroughRev(
|
||||
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
|
||||
) {
|
||||
revFlow(p, _, true, apSome(ap0), ap, config) and
|
||||
c = p.getEnclosingCallable()
|
||||
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
}
|
||||
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||
exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
|
||||
parameterFlow(p, ap, ap0, c, config) and
|
||||
c = ret.getEnclosingCallable() and
|
||||
revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_),
|
||||
pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and
|
||||
fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
pragma[nomagic]
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
|
||||
exists(RetNodeEx ret, ReturnKindExt kind |
|
||||
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
|
||||
parameterFlowsThroughRev(p, ap, kind, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
|
||||
exists(ParamNodeEx p, Ap ap |
|
||||
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
|
||||
parameterFlowsThroughRev(p, ap, kind, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
|
||||
exists(
|
||||
Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
|
||||
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
|
||||
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
||||
|
|
||||
revFlow(arg, state, toReturn, returnAp, ap, config) and
|
||||
revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
|
||||
revFlow(arg, state, returnCtx, returnAp, ap, config) and
|
||||
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1754,14 +1824,13 @@ private module MkStage<StageSig PrevStage> {
|
||||
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
|
||||
) {
|
||||
fwd = true and
|
||||
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and
|
||||
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and
|
||||
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
|
||||
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
|
||||
states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and
|
||||
states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and
|
||||
tuples =
|
||||
count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
|
||||
fwdFlow(n, state, cc, argAp, ap, config)
|
||||
)
|
||||
count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config))
|
||||
or
|
||||
fwd = false and
|
||||
nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
|
||||
@@ -1769,8 +1838,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
|
||||
states = count(FlowState state | revFlow(_, state, _, _, _, config)) and
|
||||
tuples =
|
||||
count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap |
|
||||
revFlow(n, state, b, retAp, ap, config)
|
||||
count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap |
|
||||
revFlow(n, state, returnCtx, retAp, ap, config)
|
||||
)
|
||||
}
|
||||
/* End: Stage logic. */
|
||||
@@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
|
||||
exists(lcc)
|
||||
}
|
||||
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
|
||||
|
||||
predicate flowIntoCall = flowIntoCallNodeCand1/5;
|
||||
|
||||
@@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand2(
|
||||
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
|
||||
flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and
|
||||
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
|
||||
Stage2::revFlowAlias(node1, pragma[only_bind_into](config))
|
||||
}
|
||||
@@ -2021,8 +2091,8 @@ private module LocalFlowBigStep {
|
||||
exists(NodeEx next | Stage2::revFlow(next, state, config) |
|
||||
jumpStep(node, next, config) or
|
||||
additionalJumpStep(node, next, config) or
|
||||
flowIntoCallNodeCand1(_, node, next, config) or
|
||||
flowOutOfCallNodeCand1(_, node, next, config) or
|
||||
flowIntoCallNodeCand2(_, node, next, _, config) or
|
||||
flowOutOfCallNodeCand2(_, node, _, next, _, config) or
|
||||
Stage2::storeStepCand(node, _, _, next, _, config) or
|
||||
Stage2::readStepCand(node, _, next, config)
|
||||
)
|
||||
@@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
|
||||
}
|
||||
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
|
||||
|
||||
predicate flowIntoCall = flowIntoCallNodeCand2/5;
|
||||
|
||||
@@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx(
|
||||
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
|
||||
) {
|
||||
exists(AccessPathFront apf |
|
||||
Stage3::revFlow(node, state, true, _, apf, config) and
|
||||
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
|
||||
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
|
||||
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
|
||||
config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
|
||||
pragma[nomagic]
|
||||
predicate flowOutOfCall(
|
||||
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
exists(FlowState state |
|
||||
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
|
||||
flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
|
||||
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
|
||||
pragma[only_bind_into](config))
|
||||
@@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeMayUseSummary0(
|
||||
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
|
||||
NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa,
|
||||
Configuration config
|
||||
) {
|
||||
exists(AccessPathApprox apa0 |
|
||||
Stage4::parameterMayFlowThrough(_, c, _, _) and
|
||||
Stage4::revFlow(n, state, true, _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
|
||||
n.getEnclosingCallable() = c
|
||||
c = n.getEnclosingCallable() and
|
||||
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
|
||||
TAccessPathApproxSome(apa), apa0, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2522,9 +2595,10 @@ pragma[nomagic]
|
||||
private predicate nodeMayUseSummary(
|
||||
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c |
|
||||
Stage4::parameterMayFlowThrough(_, c, apa, config) and
|
||||
nodeMayUseSummary0(n, c, state, apa, config)
|
||||
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
|
||||
Stage4::parameterMayFlowThrough(p, apa, config) and
|
||||
nodeMayUseSummary0(n, c, pos, state, apa, config) and
|
||||
p.isParameterOf(c, pos)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2532,7 +2606,7 @@ private newtype TSummaryCtx =
|
||||
TSummaryCtxNone() or
|
||||
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
|
||||
exists(Configuration config |
|
||||
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
|
||||
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
|
||||
Stage4::revFlow(p, state, _, config)
|
||||
)
|
||||
}
|
||||
@@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough(
|
||||
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
|
||||
AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos |
|
||||
exists(PathNodeMid mid, RetNodeEx ret |
|
||||
pathNode(mid, ret, state, cc, sc, ap, config, _) and
|
||||
kind = ret.getKind() and
|
||||
apa = ap.getApprox() and
|
||||
pos = sc.getParameterPos() and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
sc.getParamNode().allowParameterReturnInSelf()
|
||||
)
|
||||
parameterFlowThroughAllowed(sc.getParamNode(), kind)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ abstract class Configuration extends string {
|
||||
/**
|
||||
* Holds if `sink` is a relevant data flow sink accepting `state`.
|
||||
*/
|
||||
predicate isSink(Node source, FlowState state) { none() }
|
||||
predicate isSink(Node sink, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited. This completely removes
|
||||
@@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
|
||||
}
|
||||
|
||||
ParameterPosition getPosition() { this.isParameterOf(_, result) }
|
||||
|
||||
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
|
||||
}
|
||||
|
||||
private class RetNodeEx extends NodeEx {
|
||||
@@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if flow from `p` to a return node of kind `kind` is allowed.
|
||||
*
|
||||
* We don't expect a parameter to return stored in itself, unless
|
||||
* explicitly allowed
|
||||
*/
|
||||
bindingset[p, kind]
|
||||
private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) {
|
||||
exists(ParameterPosition pos | p.isParameterOf(_, pos) |
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
allowParameterReturnInSelfCached(p.asNode())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if flow from a parameter at position `pos` inside `c` to a return node of
|
||||
* kind `kind` is allowed.
|
||||
*
|
||||
* We don't expect a parameter to return stored in itself, unless
|
||||
* explicitly allowed
|
||||
*/
|
||||
bindingset[c, pos, kind]
|
||||
private predicate parameterFlowThroughAllowed(
|
||||
DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
p.isParameterOf(c, pos) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
private module Stage1 implements StageSig {
|
||||
class Ap = Unit;
|
||||
|
||||
@@ -981,21 +1011,22 @@ private module Stage1 implements StageSig {
|
||||
* candidate for the origin of a summary.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||
exists(ReturnKindExt kind |
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
|
||||
exists(DataFlowCallable c, ReturnKindExt kind |
|
||||
throughFlowNodeCand(p, config) and
|
||||
returnFlowCallableNodeCand(c, kind, config) and
|
||||
p.getEnclosingCallable() = c and
|
||||
exists(ap) and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
|
||||
throughFlowNodeCand(ret, config) and
|
||||
kind = ret.getKind()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
|
||||
exists(ArgNodeEx arg, boolean toReturn |
|
||||
@@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1(
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand1(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config
|
||||
) {
|
||||
viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
|
||||
Stage1::revFlow(ret, config) and
|
||||
not outBarrier(ret, config) and
|
||||
not inBarrier(out, config)
|
||||
exists(ReturnPosition pos |
|
||||
viableReturnPosOutNodeCand1(call, pos, out, config) and
|
||||
pos = ret.getReturnPosition() and
|
||||
kind = pos.getKind() and
|
||||
Stage1::revFlow(ret, config) and
|
||||
not outBarrier(ret, config) and
|
||||
not inBarrier(out, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1(
|
||||
* edge in the graph of paths between sources and sinks that ignores call
|
||||
* contexts.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private int branch(NodeEx n1, Configuration conf) {
|
||||
result =
|
||||
strictcount(NodeEx n |
|
||||
flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
|
||||
flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) {
|
||||
* edge in the graph of paths between sources and sinks that ignores call
|
||||
* contexts.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private int join(NodeEx n2, Configuration conf) {
|
||||
result =
|
||||
strictcount(NodeEx n |
|
||||
flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
|
||||
flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand1(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
flowOutOfCallNodeCand1(call, ret, out, config) and
|
||||
flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and
|
||||
exists(int b, int j |
|
||||
b = branch(ret, config) and
|
||||
j = join(out, config) and
|
||||
b = branch(ret, pragma[only_bind_into](config)) and
|
||||
j = join(out, pragma[only_bind_into](config)) and
|
||||
if b.minimum(j) <= config.fieldFlowBranchLimit()
|
||||
then allowsFieldFlow = true
|
||||
else allowsFieldFlow = false
|
||||
@@ -1136,10 +1174,10 @@ pragma[nomagic]
|
||||
private predicate flowIntoCallNodeCand1(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowIntoCallNodeCand1(call, arg, p, config) and
|
||||
flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and
|
||||
exists(int b, int j |
|
||||
b = branch(arg, config) and
|
||||
j = join(p, config) and
|
||||
b = branch(arg, pragma[only_bind_into](config)) and
|
||||
j = join(p, pragma[only_bind_into](config)) and
|
||||
if b.minimum(j) <= config.fieldFlowBranchLimit()
|
||||
then allowsFieldFlow = true
|
||||
else allowsFieldFlow = false
|
||||
@@ -1156,7 +1194,9 @@ private signature module StageSig {
|
||||
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
|
||||
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
|
||||
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
|
||||
|
||||
predicate storeStepCand(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
|
||||
@@ -1222,7 +1262,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
);
|
||||
|
||||
predicate flowOutOfCall(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
);
|
||||
|
||||
predicate flowIntoCall(
|
||||
@@ -1247,14 +1288,14 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowThroughOutOfCall(
|
||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
|
||||
NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
|
||||
pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call)
|
||||
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call) and
|
||||
c = ret.getEnclosingCallable()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1262,29 +1303,32 @@ private module MkStage<StageSig PrevStage> {
|
||||
* configuration `config`.
|
||||
*
|
||||
* The call context `cc` records whether the node is reached through an
|
||||
* argument in a call, and if so, `argAp` records the access path of that
|
||||
* argument.
|
||||
* argument in a call, and if so, `summaryCtx` and `argAp` record the
|
||||
* corresponding parameter position and access path of that argument, respectively.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate fwdFlow(
|
||||
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow0(node, state, cc, argAp, ap, config) and
|
||||
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
|
||||
filter(node, state, ap, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlow0(
|
||||
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
sourceNode(node, state, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
argAp = apNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
ap = getApNil(node)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
|
||||
fwdFlow(mid, state0, cc, argAp, ap0, config) and
|
||||
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
|
||||
localCc = getLocalCc(mid, cc)
|
||||
|
|
||||
localStep(mid, state0, node, state, true, _, config, localCc) and
|
||||
@@ -1295,65 +1339,82 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
jumpStep(mid, node, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone()
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, ApNil nil |
|
||||
fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and
|
||||
additionalJumpStep(mid, node, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and
|
||||
additionalJumpStateStep(mid, state0, node, state, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(TypedContent tc, Ap ap0 |
|
||||
fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
|
||||
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
|
||||
ap = apCons(tc, ap0)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Ap ap0, Content c |
|
||||
fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
|
||||
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
|
||||
fwdFlowConsCand(ap0, c, ap, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(ApApprox apa |
|
||||
fwdFlowIn(_, node, state, _, cc, _, ap, config) and
|
||||
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
|
||||
apa = getApprox(ap) and
|
||||
if PrevStage::parameterMayFlowThrough(node, _, apa, config)
|
||||
then argAp = apSome(ap)
|
||||
else argAp = apNone()
|
||||
if PrevStage::parameterMayFlowThrough(node, apa, config)
|
||||
then (
|
||||
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
|
||||
argAp = apSome(ap)
|
||||
) else (
|
||||
summaryCtx = TParameterPositionNone() and argAp = apNone()
|
||||
)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
|
||||
exists(
|
||||
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
|
||||
DataFlowCallable inner
|
||||
|
|
||||
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
cc = getCallContextReturn(inner, call, innercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
or
|
||||
exists(DataFlowCall call, Ap argAp0 |
|
||||
fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, cc, argAp, argAp0, config)
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
|
||||
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowStore(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
|
||||
Configuration config
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
exists(DataFlowType contentType |
|
||||
fwdFlow(node1, state, cc, argAp, ap1, config) and
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
|
||||
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
|
||||
typecheckStore(ap1, contentType)
|
||||
)
|
||||
@@ -1366,7 +1427,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
|
||||
exists(TypedContent tc |
|
||||
fwdFlowStore(_, tail, tc, _, _, _, _, config) and
|
||||
fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and
|
||||
tc.getContent() = c and
|
||||
cons = apCons(tc, tail)
|
||||
)
|
||||
@@ -1374,21 +1435,21 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead(
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
|
||||
Configuration config
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
fwdFlow(node1, state, cc, argAp, ap, config) and
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
getHeadContent(ap) = c
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIn(
|
||||
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp,
|
||||
Ap ap, Configuration config
|
||||
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ArgNodeEx arg, boolean allowsFieldFlow |
|
||||
fwdFlow(arg, state, outercc, argAp, ap, config) and
|
||||
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
|
||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
@@ -1396,29 +1457,19 @@ private module MkStage<StageSig PrevStage> {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowOutNotFromArg(
|
||||
NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
|
||||
private predicate fwdFlowOutFromArg(
|
||||
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(
|
||||
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
|
||||
DataFlowCallable inner
|
||||
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
||||
|
|
||||
fwdFlow(ret, state, innercc, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
ccOut = getCallContextReturn(inner, call, innercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowOutFromArg(
|
||||
DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
|
||||
fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
|
||||
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
|
||||
config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1428,11 +1479,13 @@ private module MkStage<StageSig PrevStage> {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(
|
||||
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
ParameterPosition pos, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
|
||||
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
|
||||
exists(ParamNodeEx param |
|
||||
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
|
||||
pos = param.getPosition()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1440,27 +1493,40 @@ private module MkStage<StageSig PrevStage> {
|
||||
private predicate storeStepFwd(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
|
||||
) {
|
||||
fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
|
||||
fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and
|
||||
ap2 = apCons(tc, ap1) and
|
||||
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config)
|
||||
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config)
|
||||
}
|
||||
|
||||
private predicate readStepFwd(
|
||||
NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
|
||||
) {
|
||||
fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and
|
||||
fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and
|
||||
fwdFlowConsCand(ap1, c, ap2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
|
||||
exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
|
||||
fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
|
||||
private predicate returnFlowsThrough0(
|
||||
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
|
||||
ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(boolean allowsFieldFlow |
|
||||
fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
|
||||
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
|
||||
pragma[only_bind_into](config))
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnFlowsThrough(
|
||||
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c, ParameterPosition ppos |
|
||||
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
|
||||
p.isParameterOf(c, ppos) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1468,118 +1534,130 @@ private module MkStage<StageSig PrevStage> {
|
||||
private predicate flowThroughIntoCall(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
|
||||
PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
|
||||
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnNodeMayFlowThrough(
|
||||
RetNodeEx ret, FlowState state, Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
|
||||
exists(Ap argAp |
|
||||
flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
|
||||
returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` with access path `ap` is part of a path from a source to a
|
||||
* sink in the configuration `config`.
|
||||
*
|
||||
* The Boolean `toReturn` records whether the node must be returned from the
|
||||
* enclosing callable in order to reach a sink, and if so, `returnAp` records
|
||||
* the access path of the returned value.
|
||||
* The parameter `returnCtx` records whether (and how) the node must be returned
|
||||
* from the enclosing callable in order to reach a sink, and if so, `returnAp`
|
||||
* records the access path of the returned value.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate revFlow(
|
||||
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
revFlow0(node, state, toReturn, returnAp, ap, config) and
|
||||
fwdFlow(node, state, _, _, ap, config)
|
||||
revFlow0(node, state, returnCtx, returnAp, ap, config) and
|
||||
fwdFlow(node, state, _, _, _, ap, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlow0(
|
||||
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
fwdFlow(node, state, _, _, ap, config) and
|
||||
fwdFlow(node, state, _, _, _, ap, config) and
|
||||
sinkNode(node, state, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
(
|
||||
if hasSinkCallCtx(config)
|
||||
then returnCtx = TReturnCtxNoFlowThrough()
|
||||
else returnCtx = TReturnCtxNone()
|
||||
) and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0 |
|
||||
localStep(node, state, mid, state0, true, _, config, _) and
|
||||
revFlow(mid, state0, toReturn, returnAp, ap, config)
|
||||
revFlow(mid, state0, returnCtx, returnAp, ap, config)
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and
|
||||
revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
|
||||
revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
jumpStep(node, mid, config) and
|
||||
revFlow(mid, state, _, _, ap, config) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone()
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, ApNil nil |
|
||||
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
additionalJumpStep(node, mid, config) and
|
||||
revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
additionalJumpStateStep(node, state, mid, state0, config) and
|
||||
revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil,
|
||||
pragma[only_bind_into](config)) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Ap ap0, Content c |
|
||||
revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
|
||||
revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and
|
||||
revFlowConsCand(ap0, c, ap, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(NodeEx mid, Ap ap0 |
|
||||
revFlow(mid, state, toReturn, returnAp, ap0, config) and
|
||||
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
|
||||
readStepFwd(node, ap, _, mid, ap0, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
revFlowInNotToReturn(node, state, returnAp, ap, config) and
|
||||
toReturn = false
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
|
||||
flowIntoCall(_, node, p, allowsFieldFlow, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
returnCtx = TReturnCtxNone()
|
||||
)
|
||||
or
|
||||
exists(DataFlowCall call, Ap returnAp0 |
|
||||
revFlowInToReturn(call, node, state, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 |
|
||||
revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
revFlowOut(_, node, state, _, _, ap, config) and
|
||||
toReturn = true and
|
||||
if returnNodeMayFlowThrough(node, state, ap, config)
|
||||
then returnAp = apSome(ap)
|
||||
else returnAp = apNone()
|
||||
exists(ReturnKindExt kind |
|
||||
revFlowOut(_, node, kind, state, _, _, ap, config) and
|
||||
if returnFlowsThrough(node, kind, state, _, _, _, ap, config)
|
||||
then (
|
||||
returnCtx = TReturnCtxMaybeFlowThrough(kind) and
|
||||
returnAp = apSome(ap)
|
||||
) else (
|
||||
returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowStore(
|
||||
Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid,
|
||||
boolean toReturn, ApOption returnAp, Configuration config
|
||||
ReturnCtx returnCtx, ApOption returnAp, Configuration config
|
||||
) {
|
||||
revFlow(mid, state, toReturn, returnAp, ap0, config) and
|
||||
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
|
||||
storeStepFwd(node, ap, tc, mid, ap0, config) and
|
||||
tc.getContent() = c
|
||||
}
|
||||
@@ -1599,35 +1677,27 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowOut(
|
||||
DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx,
|
||||
ApOption returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(NodeEx out, boolean allowsFieldFlow |
|
||||
revFlow(out, state, toReturn, returnAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowInNotToReturn(
|
||||
ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, false, returnAp, ap, config) and
|
||||
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
|
||||
revFlow(out, state, returnCtx, returnAp, ap, config) and
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowInToReturn(
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, true, apSome(returnAp), ap, config) and
|
||||
revFlow(pragma[only_bind_into](p), state,
|
||||
TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
|
||||
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1638,11 +1708,12 @@ private module MkStage<StageSig PrevStage> {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate revFlowIsReturned(
|
||||
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(RetNodeEx ret, FlowState state, CcCall ccc |
|
||||
revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and
|
||||
fwdFlow(ret, state, ccc, apSome(_), ap, config) and
|
||||
revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and
|
||||
returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and
|
||||
matchesCall(ccc, call)
|
||||
)
|
||||
}
|
||||
@@ -1713,40 +1784,39 @@ private module MkStage<StageSig PrevStage> {
|
||||
validAp(ap, config)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate parameterFlow(
|
||||
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
|
||||
pragma[nomagic]
|
||||
private predicate parameterFlowsThroughRev(
|
||||
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
|
||||
) {
|
||||
revFlow(p, _, true, apSome(ap0), ap, config) and
|
||||
c = p.getEnclosingCallable()
|
||||
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
}
|
||||
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||
exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
|
||||
parameterFlow(p, ap, ap0, c, config) and
|
||||
c = ret.getEnclosingCallable() and
|
||||
revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_),
|
||||
pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and
|
||||
fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
pragma[nomagic]
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
|
||||
exists(RetNodeEx ret, ReturnKindExt kind |
|
||||
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
|
||||
parameterFlowsThroughRev(p, ap, kind, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
|
||||
exists(ParamNodeEx p, Ap ap |
|
||||
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
|
||||
parameterFlowsThroughRev(p, ap, kind, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
|
||||
exists(
|
||||
Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
|
||||
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
|
||||
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
||||
|
|
||||
revFlow(arg, state, toReturn, returnAp, ap, config) and
|
||||
revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
|
||||
revFlow(arg, state, returnCtx, returnAp, ap, config) and
|
||||
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1754,14 +1824,13 @@ private module MkStage<StageSig PrevStage> {
|
||||
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
|
||||
) {
|
||||
fwd = true and
|
||||
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and
|
||||
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and
|
||||
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
|
||||
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
|
||||
states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and
|
||||
states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and
|
||||
tuples =
|
||||
count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
|
||||
fwdFlow(n, state, cc, argAp, ap, config)
|
||||
)
|
||||
count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config))
|
||||
or
|
||||
fwd = false and
|
||||
nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
|
||||
@@ -1769,8 +1838,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
|
||||
states = count(FlowState state | revFlow(_, state, _, _, _, config)) and
|
||||
tuples =
|
||||
count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap |
|
||||
revFlow(n, state, b, retAp, ap, config)
|
||||
count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap |
|
||||
revFlow(n, state, returnCtx, retAp, ap, config)
|
||||
)
|
||||
}
|
||||
/* End: Stage logic. */
|
||||
@@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
|
||||
exists(lcc)
|
||||
}
|
||||
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
|
||||
|
||||
predicate flowIntoCall = flowIntoCallNodeCand1/5;
|
||||
|
||||
@@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand2(
|
||||
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
|
||||
flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and
|
||||
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
|
||||
Stage2::revFlowAlias(node1, pragma[only_bind_into](config))
|
||||
}
|
||||
@@ -2021,8 +2091,8 @@ private module LocalFlowBigStep {
|
||||
exists(NodeEx next | Stage2::revFlow(next, state, config) |
|
||||
jumpStep(node, next, config) or
|
||||
additionalJumpStep(node, next, config) or
|
||||
flowIntoCallNodeCand1(_, node, next, config) or
|
||||
flowOutOfCallNodeCand1(_, node, next, config) or
|
||||
flowIntoCallNodeCand2(_, node, next, _, config) or
|
||||
flowOutOfCallNodeCand2(_, node, _, next, _, config) or
|
||||
Stage2::storeStepCand(node, _, _, next, _, config) or
|
||||
Stage2::readStepCand(node, _, next, config)
|
||||
)
|
||||
@@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
|
||||
}
|
||||
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
|
||||
|
||||
predicate flowIntoCall = flowIntoCallNodeCand2/5;
|
||||
|
||||
@@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx(
|
||||
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
|
||||
) {
|
||||
exists(AccessPathFront apf |
|
||||
Stage3::revFlow(node, state, true, _, apf, config) and
|
||||
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
|
||||
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
|
||||
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
|
||||
config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
|
||||
pragma[nomagic]
|
||||
predicate flowOutOfCall(
|
||||
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
exists(FlowState state |
|
||||
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
|
||||
flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
|
||||
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
|
||||
pragma[only_bind_into](config))
|
||||
@@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeMayUseSummary0(
|
||||
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
|
||||
NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa,
|
||||
Configuration config
|
||||
) {
|
||||
exists(AccessPathApprox apa0 |
|
||||
Stage4::parameterMayFlowThrough(_, c, _, _) and
|
||||
Stage4::revFlow(n, state, true, _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
|
||||
n.getEnclosingCallable() = c
|
||||
c = n.getEnclosingCallable() and
|
||||
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
|
||||
TAccessPathApproxSome(apa), apa0, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2522,9 +2595,10 @@ pragma[nomagic]
|
||||
private predicate nodeMayUseSummary(
|
||||
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c |
|
||||
Stage4::parameterMayFlowThrough(_, c, apa, config) and
|
||||
nodeMayUseSummary0(n, c, state, apa, config)
|
||||
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
|
||||
Stage4::parameterMayFlowThrough(p, apa, config) and
|
||||
nodeMayUseSummary0(n, c, pos, state, apa, config) and
|
||||
p.isParameterOf(c, pos)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2532,7 +2606,7 @@ private newtype TSummaryCtx =
|
||||
TSummaryCtxNone() or
|
||||
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
|
||||
exists(Configuration config |
|
||||
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
|
||||
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
|
||||
Stage4::revFlow(p, state, _, config)
|
||||
)
|
||||
}
|
||||
@@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough(
|
||||
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
|
||||
AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos |
|
||||
exists(PathNodeMid mid, RetNodeEx ret |
|
||||
pathNode(mid, ret, state, cc, sc, ap, config, _) and
|
||||
kind = ret.getKind() and
|
||||
apa = ap.getApprox() and
|
||||
pos = sc.getParameterPos() and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
sc.getParamNode().allowParameterReturnInSelf()
|
||||
)
|
||||
parameterFlowThroughAllowed(sc.getParamNode(), kind)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ abstract class Configuration extends string {
|
||||
/**
|
||||
* Holds if `sink` is a relevant data flow sink accepting `state`.
|
||||
*/
|
||||
predicate isSink(Node source, FlowState state) { none() }
|
||||
predicate isSink(Node sink, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited. This completely removes
|
||||
@@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
|
||||
}
|
||||
|
||||
ParameterPosition getPosition() { this.isParameterOf(_, result) }
|
||||
|
||||
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
|
||||
}
|
||||
|
||||
private class RetNodeEx extends NodeEx {
|
||||
@@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if flow from `p` to a return node of kind `kind` is allowed.
|
||||
*
|
||||
* We don't expect a parameter to return stored in itself, unless
|
||||
* explicitly allowed
|
||||
*/
|
||||
bindingset[p, kind]
|
||||
private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) {
|
||||
exists(ParameterPosition pos | p.isParameterOf(_, pos) |
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
allowParameterReturnInSelfCached(p.asNode())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if flow from a parameter at position `pos` inside `c` to a return node of
|
||||
* kind `kind` is allowed.
|
||||
*
|
||||
* We don't expect a parameter to return stored in itself, unless
|
||||
* explicitly allowed
|
||||
*/
|
||||
bindingset[c, pos, kind]
|
||||
private predicate parameterFlowThroughAllowed(
|
||||
DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
p.isParameterOf(c, pos) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
private module Stage1 implements StageSig {
|
||||
class Ap = Unit;
|
||||
|
||||
@@ -981,21 +1011,22 @@ private module Stage1 implements StageSig {
|
||||
* candidate for the origin of a summary.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||
exists(ReturnKindExt kind |
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
|
||||
exists(DataFlowCallable c, ReturnKindExt kind |
|
||||
throughFlowNodeCand(p, config) and
|
||||
returnFlowCallableNodeCand(c, kind, config) and
|
||||
p.getEnclosingCallable() = c and
|
||||
exists(ap) and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
|
||||
throughFlowNodeCand(ret, config) and
|
||||
kind = ret.getKind()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
|
||||
exists(ArgNodeEx arg, boolean toReturn |
|
||||
@@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1(
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand1(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config
|
||||
) {
|
||||
viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
|
||||
Stage1::revFlow(ret, config) and
|
||||
not outBarrier(ret, config) and
|
||||
not inBarrier(out, config)
|
||||
exists(ReturnPosition pos |
|
||||
viableReturnPosOutNodeCand1(call, pos, out, config) and
|
||||
pos = ret.getReturnPosition() and
|
||||
kind = pos.getKind() and
|
||||
Stage1::revFlow(ret, config) and
|
||||
not outBarrier(ret, config) and
|
||||
not inBarrier(out, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1(
|
||||
* edge in the graph of paths between sources and sinks that ignores call
|
||||
* contexts.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private int branch(NodeEx n1, Configuration conf) {
|
||||
result =
|
||||
strictcount(NodeEx n |
|
||||
flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
|
||||
flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) {
|
||||
* edge in the graph of paths between sources and sinks that ignores call
|
||||
* contexts.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private int join(NodeEx n2, Configuration conf) {
|
||||
result =
|
||||
strictcount(NodeEx n |
|
||||
flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
|
||||
flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand1(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
flowOutOfCallNodeCand1(call, ret, out, config) and
|
||||
flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and
|
||||
exists(int b, int j |
|
||||
b = branch(ret, config) and
|
||||
j = join(out, config) and
|
||||
b = branch(ret, pragma[only_bind_into](config)) and
|
||||
j = join(out, pragma[only_bind_into](config)) and
|
||||
if b.minimum(j) <= config.fieldFlowBranchLimit()
|
||||
then allowsFieldFlow = true
|
||||
else allowsFieldFlow = false
|
||||
@@ -1136,10 +1174,10 @@ pragma[nomagic]
|
||||
private predicate flowIntoCallNodeCand1(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowIntoCallNodeCand1(call, arg, p, config) and
|
||||
flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and
|
||||
exists(int b, int j |
|
||||
b = branch(arg, config) and
|
||||
j = join(p, config) and
|
||||
b = branch(arg, pragma[only_bind_into](config)) and
|
||||
j = join(p, pragma[only_bind_into](config)) and
|
||||
if b.minimum(j) <= config.fieldFlowBranchLimit()
|
||||
then allowsFieldFlow = true
|
||||
else allowsFieldFlow = false
|
||||
@@ -1156,7 +1194,9 @@ private signature module StageSig {
|
||||
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
|
||||
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
|
||||
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
|
||||
|
||||
predicate storeStepCand(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
|
||||
@@ -1222,7 +1262,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
);
|
||||
|
||||
predicate flowOutOfCall(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
);
|
||||
|
||||
predicate flowIntoCall(
|
||||
@@ -1247,14 +1288,14 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowThroughOutOfCall(
|
||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
|
||||
NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
|
||||
pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call)
|
||||
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call) and
|
||||
c = ret.getEnclosingCallable()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1262,29 +1303,32 @@ private module MkStage<StageSig PrevStage> {
|
||||
* configuration `config`.
|
||||
*
|
||||
* The call context `cc` records whether the node is reached through an
|
||||
* argument in a call, and if so, `argAp` records the access path of that
|
||||
* argument.
|
||||
* argument in a call, and if so, `summaryCtx` and `argAp` record the
|
||||
* corresponding parameter position and access path of that argument, respectively.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate fwdFlow(
|
||||
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow0(node, state, cc, argAp, ap, config) and
|
||||
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
|
||||
filter(node, state, ap, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlow0(
|
||||
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
sourceNode(node, state, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
argAp = apNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
ap = getApNil(node)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
|
||||
fwdFlow(mid, state0, cc, argAp, ap0, config) and
|
||||
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
|
||||
localCc = getLocalCc(mid, cc)
|
||||
|
|
||||
localStep(mid, state0, node, state, true, _, config, localCc) and
|
||||
@@ -1295,65 +1339,82 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
jumpStep(mid, node, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone()
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, ApNil nil |
|
||||
fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and
|
||||
additionalJumpStep(mid, node, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and
|
||||
additionalJumpStateStep(mid, state0, node, state, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(TypedContent tc, Ap ap0 |
|
||||
fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
|
||||
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
|
||||
ap = apCons(tc, ap0)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Ap ap0, Content c |
|
||||
fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
|
||||
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
|
||||
fwdFlowConsCand(ap0, c, ap, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(ApApprox apa |
|
||||
fwdFlowIn(_, node, state, _, cc, _, ap, config) and
|
||||
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
|
||||
apa = getApprox(ap) and
|
||||
if PrevStage::parameterMayFlowThrough(node, _, apa, config)
|
||||
then argAp = apSome(ap)
|
||||
else argAp = apNone()
|
||||
if PrevStage::parameterMayFlowThrough(node, apa, config)
|
||||
then (
|
||||
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
|
||||
argAp = apSome(ap)
|
||||
) else (
|
||||
summaryCtx = TParameterPositionNone() and argAp = apNone()
|
||||
)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
|
||||
exists(
|
||||
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
|
||||
DataFlowCallable inner
|
||||
|
|
||||
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
cc = getCallContextReturn(inner, call, innercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
or
|
||||
exists(DataFlowCall call, Ap argAp0 |
|
||||
fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, cc, argAp, argAp0, config)
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
|
||||
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowStore(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
|
||||
Configuration config
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
exists(DataFlowType contentType |
|
||||
fwdFlow(node1, state, cc, argAp, ap1, config) and
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
|
||||
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
|
||||
typecheckStore(ap1, contentType)
|
||||
)
|
||||
@@ -1366,7 +1427,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
|
||||
exists(TypedContent tc |
|
||||
fwdFlowStore(_, tail, tc, _, _, _, _, config) and
|
||||
fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and
|
||||
tc.getContent() = c and
|
||||
cons = apCons(tc, tail)
|
||||
)
|
||||
@@ -1374,21 +1435,21 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead(
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
|
||||
Configuration config
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
fwdFlow(node1, state, cc, argAp, ap, config) and
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
getHeadContent(ap) = c
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIn(
|
||||
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp,
|
||||
Ap ap, Configuration config
|
||||
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ArgNodeEx arg, boolean allowsFieldFlow |
|
||||
fwdFlow(arg, state, outercc, argAp, ap, config) and
|
||||
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
|
||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
@@ -1396,29 +1457,19 @@ private module MkStage<StageSig PrevStage> {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowOutNotFromArg(
|
||||
NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
|
||||
private predicate fwdFlowOutFromArg(
|
||||
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(
|
||||
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
|
||||
DataFlowCallable inner
|
||||
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
||||
|
|
||||
fwdFlow(ret, state, innercc, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
ccOut = getCallContextReturn(inner, call, innercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowOutFromArg(
|
||||
DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
|
||||
fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
|
||||
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
|
||||
config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1428,11 +1479,13 @@ private module MkStage<StageSig PrevStage> {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(
|
||||
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
ParameterPosition pos, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
|
||||
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
|
||||
exists(ParamNodeEx param |
|
||||
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
|
||||
pos = param.getPosition()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1440,27 +1493,40 @@ private module MkStage<StageSig PrevStage> {
|
||||
private predicate storeStepFwd(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
|
||||
) {
|
||||
fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
|
||||
fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and
|
||||
ap2 = apCons(tc, ap1) and
|
||||
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config)
|
||||
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config)
|
||||
}
|
||||
|
||||
private predicate readStepFwd(
|
||||
NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
|
||||
) {
|
||||
fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and
|
||||
fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and
|
||||
fwdFlowConsCand(ap1, c, ap2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
|
||||
exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
|
||||
fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
|
||||
private predicate returnFlowsThrough0(
|
||||
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
|
||||
ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(boolean allowsFieldFlow |
|
||||
fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
|
||||
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
|
||||
pragma[only_bind_into](config))
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnFlowsThrough(
|
||||
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c, ParameterPosition ppos |
|
||||
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
|
||||
p.isParameterOf(c, ppos) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1468,118 +1534,130 @@ private module MkStage<StageSig PrevStage> {
|
||||
private predicate flowThroughIntoCall(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
|
||||
PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
|
||||
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnNodeMayFlowThrough(
|
||||
RetNodeEx ret, FlowState state, Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
|
||||
exists(Ap argAp |
|
||||
flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
|
||||
returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` with access path `ap` is part of a path from a source to a
|
||||
* sink in the configuration `config`.
|
||||
*
|
||||
* The Boolean `toReturn` records whether the node must be returned from the
|
||||
* enclosing callable in order to reach a sink, and if so, `returnAp` records
|
||||
* the access path of the returned value.
|
||||
* The parameter `returnCtx` records whether (and how) the node must be returned
|
||||
* from the enclosing callable in order to reach a sink, and if so, `returnAp`
|
||||
* records the access path of the returned value.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate revFlow(
|
||||
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
revFlow0(node, state, toReturn, returnAp, ap, config) and
|
||||
fwdFlow(node, state, _, _, ap, config)
|
||||
revFlow0(node, state, returnCtx, returnAp, ap, config) and
|
||||
fwdFlow(node, state, _, _, _, ap, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlow0(
|
||||
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
fwdFlow(node, state, _, _, ap, config) and
|
||||
fwdFlow(node, state, _, _, _, ap, config) and
|
||||
sinkNode(node, state, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
(
|
||||
if hasSinkCallCtx(config)
|
||||
then returnCtx = TReturnCtxNoFlowThrough()
|
||||
else returnCtx = TReturnCtxNone()
|
||||
) and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0 |
|
||||
localStep(node, state, mid, state0, true, _, config, _) and
|
||||
revFlow(mid, state0, toReturn, returnAp, ap, config)
|
||||
revFlow(mid, state0, returnCtx, returnAp, ap, config)
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and
|
||||
revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
|
||||
revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
jumpStep(node, mid, config) and
|
||||
revFlow(mid, state, _, _, ap, config) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone()
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, ApNil nil |
|
||||
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
additionalJumpStep(node, mid, config) and
|
||||
revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
additionalJumpStateStep(node, state, mid, state0, config) and
|
||||
revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil,
|
||||
pragma[only_bind_into](config)) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Ap ap0, Content c |
|
||||
revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
|
||||
revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and
|
||||
revFlowConsCand(ap0, c, ap, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(NodeEx mid, Ap ap0 |
|
||||
revFlow(mid, state, toReturn, returnAp, ap0, config) and
|
||||
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
|
||||
readStepFwd(node, ap, _, mid, ap0, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
revFlowInNotToReturn(node, state, returnAp, ap, config) and
|
||||
toReturn = false
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
|
||||
flowIntoCall(_, node, p, allowsFieldFlow, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
returnCtx = TReturnCtxNone()
|
||||
)
|
||||
or
|
||||
exists(DataFlowCall call, Ap returnAp0 |
|
||||
revFlowInToReturn(call, node, state, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 |
|
||||
revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
revFlowOut(_, node, state, _, _, ap, config) and
|
||||
toReturn = true and
|
||||
if returnNodeMayFlowThrough(node, state, ap, config)
|
||||
then returnAp = apSome(ap)
|
||||
else returnAp = apNone()
|
||||
exists(ReturnKindExt kind |
|
||||
revFlowOut(_, node, kind, state, _, _, ap, config) and
|
||||
if returnFlowsThrough(node, kind, state, _, _, _, ap, config)
|
||||
then (
|
||||
returnCtx = TReturnCtxMaybeFlowThrough(kind) and
|
||||
returnAp = apSome(ap)
|
||||
) else (
|
||||
returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowStore(
|
||||
Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid,
|
||||
boolean toReturn, ApOption returnAp, Configuration config
|
||||
ReturnCtx returnCtx, ApOption returnAp, Configuration config
|
||||
) {
|
||||
revFlow(mid, state, toReturn, returnAp, ap0, config) and
|
||||
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
|
||||
storeStepFwd(node, ap, tc, mid, ap0, config) and
|
||||
tc.getContent() = c
|
||||
}
|
||||
@@ -1599,35 +1677,27 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowOut(
|
||||
DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx,
|
||||
ApOption returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(NodeEx out, boolean allowsFieldFlow |
|
||||
revFlow(out, state, toReturn, returnAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowInNotToReturn(
|
||||
ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, false, returnAp, ap, config) and
|
||||
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
|
||||
revFlow(out, state, returnCtx, returnAp, ap, config) and
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowInToReturn(
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, true, apSome(returnAp), ap, config) and
|
||||
revFlow(pragma[only_bind_into](p), state,
|
||||
TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
|
||||
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1638,11 +1708,12 @@ private module MkStage<StageSig PrevStage> {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate revFlowIsReturned(
|
||||
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(RetNodeEx ret, FlowState state, CcCall ccc |
|
||||
revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and
|
||||
fwdFlow(ret, state, ccc, apSome(_), ap, config) and
|
||||
revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and
|
||||
returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and
|
||||
matchesCall(ccc, call)
|
||||
)
|
||||
}
|
||||
@@ -1713,40 +1784,39 @@ private module MkStage<StageSig PrevStage> {
|
||||
validAp(ap, config)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate parameterFlow(
|
||||
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
|
||||
pragma[nomagic]
|
||||
private predicate parameterFlowsThroughRev(
|
||||
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
|
||||
) {
|
||||
revFlow(p, _, true, apSome(ap0), ap, config) and
|
||||
c = p.getEnclosingCallable()
|
||||
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
}
|
||||
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||
exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
|
||||
parameterFlow(p, ap, ap0, c, config) and
|
||||
c = ret.getEnclosingCallable() and
|
||||
revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_),
|
||||
pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and
|
||||
fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
pragma[nomagic]
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
|
||||
exists(RetNodeEx ret, ReturnKindExt kind |
|
||||
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
|
||||
parameterFlowsThroughRev(p, ap, kind, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
|
||||
exists(ParamNodeEx p, Ap ap |
|
||||
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
|
||||
parameterFlowsThroughRev(p, ap, kind, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
|
||||
exists(
|
||||
Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
|
||||
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
|
||||
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
||||
|
|
||||
revFlow(arg, state, toReturn, returnAp, ap, config) and
|
||||
revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
|
||||
revFlow(arg, state, returnCtx, returnAp, ap, config) and
|
||||
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1754,14 +1824,13 @@ private module MkStage<StageSig PrevStage> {
|
||||
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
|
||||
) {
|
||||
fwd = true and
|
||||
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and
|
||||
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and
|
||||
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
|
||||
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
|
||||
states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and
|
||||
states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and
|
||||
tuples =
|
||||
count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
|
||||
fwdFlow(n, state, cc, argAp, ap, config)
|
||||
)
|
||||
count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config))
|
||||
or
|
||||
fwd = false and
|
||||
nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
|
||||
@@ -1769,8 +1838,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
|
||||
states = count(FlowState state | revFlow(_, state, _, _, _, config)) and
|
||||
tuples =
|
||||
count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap |
|
||||
revFlow(n, state, b, retAp, ap, config)
|
||||
count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap |
|
||||
revFlow(n, state, returnCtx, retAp, ap, config)
|
||||
)
|
||||
}
|
||||
/* End: Stage logic. */
|
||||
@@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
|
||||
exists(lcc)
|
||||
}
|
||||
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
|
||||
|
||||
predicate flowIntoCall = flowIntoCallNodeCand1/5;
|
||||
|
||||
@@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand2(
|
||||
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
|
||||
flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and
|
||||
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
|
||||
Stage2::revFlowAlias(node1, pragma[only_bind_into](config))
|
||||
}
|
||||
@@ -2021,8 +2091,8 @@ private module LocalFlowBigStep {
|
||||
exists(NodeEx next | Stage2::revFlow(next, state, config) |
|
||||
jumpStep(node, next, config) or
|
||||
additionalJumpStep(node, next, config) or
|
||||
flowIntoCallNodeCand1(_, node, next, config) or
|
||||
flowOutOfCallNodeCand1(_, node, next, config) or
|
||||
flowIntoCallNodeCand2(_, node, next, _, config) or
|
||||
flowOutOfCallNodeCand2(_, node, _, next, _, config) or
|
||||
Stage2::storeStepCand(node, _, _, next, _, config) or
|
||||
Stage2::readStepCand(node, _, next, config)
|
||||
)
|
||||
@@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
|
||||
}
|
||||
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
|
||||
|
||||
predicate flowIntoCall = flowIntoCallNodeCand2/5;
|
||||
|
||||
@@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx(
|
||||
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
|
||||
) {
|
||||
exists(AccessPathFront apf |
|
||||
Stage3::revFlow(node, state, true, _, apf, config) and
|
||||
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
|
||||
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
|
||||
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
|
||||
config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
|
||||
pragma[nomagic]
|
||||
predicate flowOutOfCall(
|
||||
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
exists(FlowState state |
|
||||
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
|
||||
flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
|
||||
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
|
||||
pragma[only_bind_into](config))
|
||||
@@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeMayUseSummary0(
|
||||
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
|
||||
NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa,
|
||||
Configuration config
|
||||
) {
|
||||
exists(AccessPathApprox apa0 |
|
||||
Stage4::parameterMayFlowThrough(_, c, _, _) and
|
||||
Stage4::revFlow(n, state, true, _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
|
||||
n.getEnclosingCallable() = c
|
||||
c = n.getEnclosingCallable() and
|
||||
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
|
||||
TAccessPathApproxSome(apa), apa0, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2522,9 +2595,10 @@ pragma[nomagic]
|
||||
private predicate nodeMayUseSummary(
|
||||
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c |
|
||||
Stage4::parameterMayFlowThrough(_, c, apa, config) and
|
||||
nodeMayUseSummary0(n, c, state, apa, config)
|
||||
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
|
||||
Stage4::parameterMayFlowThrough(p, apa, config) and
|
||||
nodeMayUseSummary0(n, c, pos, state, apa, config) and
|
||||
p.isParameterOf(c, pos)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2532,7 +2606,7 @@ private newtype TSummaryCtx =
|
||||
TSummaryCtxNone() or
|
||||
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
|
||||
exists(Configuration config |
|
||||
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
|
||||
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
|
||||
Stage4::revFlow(p, state, _, config)
|
||||
)
|
||||
}
|
||||
@@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough(
|
||||
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
|
||||
AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos |
|
||||
exists(PathNodeMid mid, RetNodeEx ret |
|
||||
pathNode(mid, ret, state, cc, sc, ap, config, _) and
|
||||
kind = ret.getKind() and
|
||||
apa = ap.getApprox() and
|
||||
pos = sc.getParameterPos() and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
sc.getParamNode().allowParameterReturnInSelf()
|
||||
)
|
||||
parameterFlowThroughAllowed(sc.getParamNode(), kind)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ abstract class Configuration extends string {
|
||||
/**
|
||||
* Holds if `sink` is a relevant data flow sink accepting `state`.
|
||||
*/
|
||||
predicate isSink(Node source, FlowState state) { none() }
|
||||
predicate isSink(Node sink, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited. This completely removes
|
||||
@@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
|
||||
}
|
||||
|
||||
ParameterPosition getPosition() { this.isParameterOf(_, result) }
|
||||
|
||||
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
|
||||
}
|
||||
|
||||
private class RetNodeEx extends NodeEx {
|
||||
@@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if flow from `p` to a return node of kind `kind` is allowed.
|
||||
*
|
||||
* We don't expect a parameter to return stored in itself, unless
|
||||
* explicitly allowed
|
||||
*/
|
||||
bindingset[p, kind]
|
||||
private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) {
|
||||
exists(ParameterPosition pos | p.isParameterOf(_, pos) |
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
allowParameterReturnInSelfCached(p.asNode())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if flow from a parameter at position `pos` inside `c` to a return node of
|
||||
* kind `kind` is allowed.
|
||||
*
|
||||
* We don't expect a parameter to return stored in itself, unless
|
||||
* explicitly allowed
|
||||
*/
|
||||
bindingset[c, pos, kind]
|
||||
private predicate parameterFlowThroughAllowed(
|
||||
DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
p.isParameterOf(c, pos) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
private module Stage1 implements StageSig {
|
||||
class Ap = Unit;
|
||||
|
||||
@@ -981,21 +1011,22 @@ private module Stage1 implements StageSig {
|
||||
* candidate for the origin of a summary.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||
exists(ReturnKindExt kind |
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
|
||||
exists(DataFlowCallable c, ReturnKindExt kind |
|
||||
throughFlowNodeCand(p, config) and
|
||||
returnFlowCallableNodeCand(c, kind, config) and
|
||||
p.getEnclosingCallable() = c and
|
||||
exists(ap) and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
|
||||
throughFlowNodeCand(ret, config) and
|
||||
kind = ret.getKind()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
|
||||
exists(ArgNodeEx arg, boolean toReturn |
|
||||
@@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1(
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand1(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config
|
||||
) {
|
||||
viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
|
||||
Stage1::revFlow(ret, config) and
|
||||
not outBarrier(ret, config) and
|
||||
not inBarrier(out, config)
|
||||
exists(ReturnPosition pos |
|
||||
viableReturnPosOutNodeCand1(call, pos, out, config) and
|
||||
pos = ret.getReturnPosition() and
|
||||
kind = pos.getKind() and
|
||||
Stage1::revFlow(ret, config) and
|
||||
not outBarrier(ret, config) and
|
||||
not inBarrier(out, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1(
|
||||
* edge in the graph of paths between sources and sinks that ignores call
|
||||
* contexts.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private int branch(NodeEx n1, Configuration conf) {
|
||||
result =
|
||||
strictcount(NodeEx n |
|
||||
flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
|
||||
flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) {
|
||||
* edge in the graph of paths between sources and sinks that ignores call
|
||||
* contexts.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private int join(NodeEx n2, Configuration conf) {
|
||||
result =
|
||||
strictcount(NodeEx n |
|
||||
flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
|
||||
flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand1(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
flowOutOfCallNodeCand1(call, ret, out, config) and
|
||||
flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and
|
||||
exists(int b, int j |
|
||||
b = branch(ret, config) and
|
||||
j = join(out, config) and
|
||||
b = branch(ret, pragma[only_bind_into](config)) and
|
||||
j = join(out, pragma[only_bind_into](config)) and
|
||||
if b.minimum(j) <= config.fieldFlowBranchLimit()
|
||||
then allowsFieldFlow = true
|
||||
else allowsFieldFlow = false
|
||||
@@ -1136,10 +1174,10 @@ pragma[nomagic]
|
||||
private predicate flowIntoCallNodeCand1(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowIntoCallNodeCand1(call, arg, p, config) and
|
||||
flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and
|
||||
exists(int b, int j |
|
||||
b = branch(arg, config) and
|
||||
j = join(p, config) and
|
||||
b = branch(arg, pragma[only_bind_into](config)) and
|
||||
j = join(p, pragma[only_bind_into](config)) and
|
||||
if b.minimum(j) <= config.fieldFlowBranchLimit()
|
||||
then allowsFieldFlow = true
|
||||
else allowsFieldFlow = false
|
||||
@@ -1156,7 +1194,9 @@ private signature module StageSig {
|
||||
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
|
||||
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
|
||||
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
|
||||
|
||||
predicate storeStepCand(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
|
||||
@@ -1222,7 +1262,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
);
|
||||
|
||||
predicate flowOutOfCall(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
);
|
||||
|
||||
predicate flowIntoCall(
|
||||
@@ -1247,14 +1288,14 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowThroughOutOfCall(
|
||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
|
||||
NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
|
||||
pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call)
|
||||
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call) and
|
||||
c = ret.getEnclosingCallable()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1262,29 +1303,32 @@ private module MkStage<StageSig PrevStage> {
|
||||
* configuration `config`.
|
||||
*
|
||||
* The call context `cc` records whether the node is reached through an
|
||||
* argument in a call, and if so, `argAp` records the access path of that
|
||||
* argument.
|
||||
* argument in a call, and if so, `summaryCtx` and `argAp` record the
|
||||
* corresponding parameter position and access path of that argument, respectively.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate fwdFlow(
|
||||
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow0(node, state, cc, argAp, ap, config) and
|
||||
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
|
||||
filter(node, state, ap, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlow0(
|
||||
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
sourceNode(node, state, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
argAp = apNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
ap = getApNil(node)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
|
||||
fwdFlow(mid, state0, cc, argAp, ap0, config) and
|
||||
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
|
||||
localCc = getLocalCc(mid, cc)
|
||||
|
|
||||
localStep(mid, state0, node, state, true, _, config, localCc) and
|
||||
@@ -1295,65 +1339,82 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
jumpStep(mid, node, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone()
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, ApNil nil |
|
||||
fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and
|
||||
additionalJumpStep(mid, node, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and
|
||||
additionalJumpStateStep(mid, state0, node, state, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(TypedContent tc, Ap ap0 |
|
||||
fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
|
||||
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
|
||||
ap = apCons(tc, ap0)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Ap ap0, Content c |
|
||||
fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
|
||||
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
|
||||
fwdFlowConsCand(ap0, c, ap, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(ApApprox apa |
|
||||
fwdFlowIn(_, node, state, _, cc, _, ap, config) and
|
||||
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
|
||||
apa = getApprox(ap) and
|
||||
if PrevStage::parameterMayFlowThrough(node, _, apa, config)
|
||||
then argAp = apSome(ap)
|
||||
else argAp = apNone()
|
||||
if PrevStage::parameterMayFlowThrough(node, apa, config)
|
||||
then (
|
||||
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
|
||||
argAp = apSome(ap)
|
||||
) else (
|
||||
summaryCtx = TParameterPositionNone() and argAp = apNone()
|
||||
)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
|
||||
exists(
|
||||
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
|
||||
DataFlowCallable inner
|
||||
|
|
||||
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
cc = getCallContextReturn(inner, call, innercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
or
|
||||
exists(DataFlowCall call, Ap argAp0 |
|
||||
fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, cc, argAp, argAp0, config)
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
|
||||
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowStore(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
|
||||
Configuration config
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
exists(DataFlowType contentType |
|
||||
fwdFlow(node1, state, cc, argAp, ap1, config) and
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
|
||||
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
|
||||
typecheckStore(ap1, contentType)
|
||||
)
|
||||
@@ -1366,7 +1427,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
|
||||
exists(TypedContent tc |
|
||||
fwdFlowStore(_, tail, tc, _, _, _, _, config) and
|
||||
fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and
|
||||
tc.getContent() = c and
|
||||
cons = apCons(tc, tail)
|
||||
)
|
||||
@@ -1374,21 +1435,21 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead(
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
|
||||
Configuration config
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
fwdFlow(node1, state, cc, argAp, ap, config) and
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
getHeadContent(ap) = c
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIn(
|
||||
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp,
|
||||
Ap ap, Configuration config
|
||||
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ArgNodeEx arg, boolean allowsFieldFlow |
|
||||
fwdFlow(arg, state, outercc, argAp, ap, config) and
|
||||
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
|
||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
@@ -1396,29 +1457,19 @@ private module MkStage<StageSig PrevStage> {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowOutNotFromArg(
|
||||
NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
|
||||
private predicate fwdFlowOutFromArg(
|
||||
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(
|
||||
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
|
||||
DataFlowCallable inner
|
||||
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
||||
|
|
||||
fwdFlow(ret, state, innercc, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
ccOut = getCallContextReturn(inner, call, innercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowOutFromArg(
|
||||
DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
|
||||
fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
|
||||
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
|
||||
config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1428,11 +1479,13 @@ private module MkStage<StageSig PrevStage> {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(
|
||||
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
ParameterPosition pos, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
|
||||
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
|
||||
exists(ParamNodeEx param |
|
||||
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
|
||||
pos = param.getPosition()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1440,27 +1493,40 @@ private module MkStage<StageSig PrevStage> {
|
||||
private predicate storeStepFwd(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
|
||||
) {
|
||||
fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
|
||||
fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and
|
||||
ap2 = apCons(tc, ap1) and
|
||||
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config)
|
||||
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config)
|
||||
}
|
||||
|
||||
private predicate readStepFwd(
|
||||
NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
|
||||
) {
|
||||
fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and
|
||||
fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and
|
||||
fwdFlowConsCand(ap1, c, ap2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
|
||||
exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
|
||||
fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
|
||||
private predicate returnFlowsThrough0(
|
||||
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
|
||||
ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(boolean allowsFieldFlow |
|
||||
fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
|
||||
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
|
||||
pragma[only_bind_into](config))
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnFlowsThrough(
|
||||
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c, ParameterPosition ppos |
|
||||
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
|
||||
p.isParameterOf(c, ppos) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1468,118 +1534,130 @@ private module MkStage<StageSig PrevStage> {
|
||||
private predicate flowThroughIntoCall(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
|
||||
PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
|
||||
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnNodeMayFlowThrough(
|
||||
RetNodeEx ret, FlowState state, Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
|
||||
exists(Ap argAp |
|
||||
flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
|
||||
returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` with access path `ap` is part of a path from a source to a
|
||||
* sink in the configuration `config`.
|
||||
*
|
||||
* The Boolean `toReturn` records whether the node must be returned from the
|
||||
* enclosing callable in order to reach a sink, and if so, `returnAp` records
|
||||
* the access path of the returned value.
|
||||
* The parameter `returnCtx` records whether (and how) the node must be returned
|
||||
* from the enclosing callable in order to reach a sink, and if so, `returnAp`
|
||||
* records the access path of the returned value.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate revFlow(
|
||||
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
revFlow0(node, state, toReturn, returnAp, ap, config) and
|
||||
fwdFlow(node, state, _, _, ap, config)
|
||||
revFlow0(node, state, returnCtx, returnAp, ap, config) and
|
||||
fwdFlow(node, state, _, _, _, ap, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlow0(
|
||||
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
fwdFlow(node, state, _, _, ap, config) and
|
||||
fwdFlow(node, state, _, _, _, ap, config) and
|
||||
sinkNode(node, state, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
(
|
||||
if hasSinkCallCtx(config)
|
||||
then returnCtx = TReturnCtxNoFlowThrough()
|
||||
else returnCtx = TReturnCtxNone()
|
||||
) and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0 |
|
||||
localStep(node, state, mid, state0, true, _, config, _) and
|
||||
revFlow(mid, state0, toReturn, returnAp, ap, config)
|
||||
revFlow(mid, state0, returnCtx, returnAp, ap, config)
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and
|
||||
revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
|
||||
revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
jumpStep(node, mid, config) and
|
||||
revFlow(mid, state, _, _, ap, config) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone()
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, ApNil nil |
|
||||
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
additionalJumpStep(node, mid, config) and
|
||||
revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
additionalJumpStateStep(node, state, mid, state0, config) and
|
||||
revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil,
|
||||
pragma[only_bind_into](config)) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Ap ap0, Content c |
|
||||
revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
|
||||
revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and
|
||||
revFlowConsCand(ap0, c, ap, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(NodeEx mid, Ap ap0 |
|
||||
revFlow(mid, state, toReturn, returnAp, ap0, config) and
|
||||
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
|
||||
readStepFwd(node, ap, _, mid, ap0, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
revFlowInNotToReturn(node, state, returnAp, ap, config) and
|
||||
toReturn = false
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
|
||||
flowIntoCall(_, node, p, allowsFieldFlow, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
returnCtx = TReturnCtxNone()
|
||||
)
|
||||
or
|
||||
exists(DataFlowCall call, Ap returnAp0 |
|
||||
revFlowInToReturn(call, node, state, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 |
|
||||
revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
revFlowOut(_, node, state, _, _, ap, config) and
|
||||
toReturn = true and
|
||||
if returnNodeMayFlowThrough(node, state, ap, config)
|
||||
then returnAp = apSome(ap)
|
||||
else returnAp = apNone()
|
||||
exists(ReturnKindExt kind |
|
||||
revFlowOut(_, node, kind, state, _, _, ap, config) and
|
||||
if returnFlowsThrough(node, kind, state, _, _, _, ap, config)
|
||||
then (
|
||||
returnCtx = TReturnCtxMaybeFlowThrough(kind) and
|
||||
returnAp = apSome(ap)
|
||||
) else (
|
||||
returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowStore(
|
||||
Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid,
|
||||
boolean toReturn, ApOption returnAp, Configuration config
|
||||
ReturnCtx returnCtx, ApOption returnAp, Configuration config
|
||||
) {
|
||||
revFlow(mid, state, toReturn, returnAp, ap0, config) and
|
||||
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
|
||||
storeStepFwd(node, ap, tc, mid, ap0, config) and
|
||||
tc.getContent() = c
|
||||
}
|
||||
@@ -1599,35 +1677,27 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowOut(
|
||||
DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx,
|
||||
ApOption returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(NodeEx out, boolean allowsFieldFlow |
|
||||
revFlow(out, state, toReturn, returnAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowInNotToReturn(
|
||||
ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, false, returnAp, ap, config) and
|
||||
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
|
||||
revFlow(out, state, returnCtx, returnAp, ap, config) and
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowInToReturn(
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, true, apSome(returnAp), ap, config) and
|
||||
revFlow(pragma[only_bind_into](p), state,
|
||||
TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
|
||||
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1638,11 +1708,12 @@ private module MkStage<StageSig PrevStage> {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate revFlowIsReturned(
|
||||
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(RetNodeEx ret, FlowState state, CcCall ccc |
|
||||
revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and
|
||||
fwdFlow(ret, state, ccc, apSome(_), ap, config) and
|
||||
revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and
|
||||
returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and
|
||||
matchesCall(ccc, call)
|
||||
)
|
||||
}
|
||||
@@ -1713,40 +1784,39 @@ private module MkStage<StageSig PrevStage> {
|
||||
validAp(ap, config)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate parameterFlow(
|
||||
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
|
||||
pragma[nomagic]
|
||||
private predicate parameterFlowsThroughRev(
|
||||
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
|
||||
) {
|
||||
revFlow(p, _, true, apSome(ap0), ap, config) and
|
||||
c = p.getEnclosingCallable()
|
||||
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
}
|
||||
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||
exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
|
||||
parameterFlow(p, ap, ap0, c, config) and
|
||||
c = ret.getEnclosingCallable() and
|
||||
revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_),
|
||||
pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and
|
||||
fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
pragma[nomagic]
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
|
||||
exists(RetNodeEx ret, ReturnKindExt kind |
|
||||
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
|
||||
parameterFlowsThroughRev(p, ap, kind, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
|
||||
exists(ParamNodeEx p, Ap ap |
|
||||
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
|
||||
parameterFlowsThroughRev(p, ap, kind, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
|
||||
exists(
|
||||
Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
|
||||
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
|
||||
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
||||
|
|
||||
revFlow(arg, state, toReturn, returnAp, ap, config) and
|
||||
revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
|
||||
revFlow(arg, state, returnCtx, returnAp, ap, config) and
|
||||
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1754,14 +1824,13 @@ private module MkStage<StageSig PrevStage> {
|
||||
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
|
||||
) {
|
||||
fwd = true and
|
||||
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and
|
||||
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and
|
||||
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
|
||||
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
|
||||
states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and
|
||||
states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and
|
||||
tuples =
|
||||
count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
|
||||
fwdFlow(n, state, cc, argAp, ap, config)
|
||||
)
|
||||
count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config))
|
||||
or
|
||||
fwd = false and
|
||||
nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
|
||||
@@ -1769,8 +1838,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
|
||||
states = count(FlowState state | revFlow(_, state, _, _, _, config)) and
|
||||
tuples =
|
||||
count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap |
|
||||
revFlow(n, state, b, retAp, ap, config)
|
||||
count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap |
|
||||
revFlow(n, state, returnCtx, retAp, ap, config)
|
||||
)
|
||||
}
|
||||
/* End: Stage logic. */
|
||||
@@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
|
||||
exists(lcc)
|
||||
}
|
||||
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
|
||||
|
||||
predicate flowIntoCall = flowIntoCallNodeCand1/5;
|
||||
|
||||
@@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand2(
|
||||
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
|
||||
flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and
|
||||
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
|
||||
Stage2::revFlowAlias(node1, pragma[only_bind_into](config))
|
||||
}
|
||||
@@ -2021,8 +2091,8 @@ private module LocalFlowBigStep {
|
||||
exists(NodeEx next | Stage2::revFlow(next, state, config) |
|
||||
jumpStep(node, next, config) or
|
||||
additionalJumpStep(node, next, config) or
|
||||
flowIntoCallNodeCand1(_, node, next, config) or
|
||||
flowOutOfCallNodeCand1(_, node, next, config) or
|
||||
flowIntoCallNodeCand2(_, node, next, _, config) or
|
||||
flowOutOfCallNodeCand2(_, node, _, next, _, config) or
|
||||
Stage2::storeStepCand(node, _, _, next, _, config) or
|
||||
Stage2::readStepCand(node, _, next, config)
|
||||
)
|
||||
@@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
|
||||
}
|
||||
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
|
||||
|
||||
predicate flowIntoCall = flowIntoCallNodeCand2/5;
|
||||
|
||||
@@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx(
|
||||
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
|
||||
) {
|
||||
exists(AccessPathFront apf |
|
||||
Stage3::revFlow(node, state, true, _, apf, config) and
|
||||
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
|
||||
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
|
||||
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
|
||||
config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
|
||||
pragma[nomagic]
|
||||
predicate flowOutOfCall(
|
||||
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
exists(FlowState state |
|
||||
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
|
||||
flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
|
||||
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
|
||||
pragma[only_bind_into](config))
|
||||
@@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeMayUseSummary0(
|
||||
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
|
||||
NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa,
|
||||
Configuration config
|
||||
) {
|
||||
exists(AccessPathApprox apa0 |
|
||||
Stage4::parameterMayFlowThrough(_, c, _, _) and
|
||||
Stage4::revFlow(n, state, true, _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
|
||||
n.getEnclosingCallable() = c
|
||||
c = n.getEnclosingCallable() and
|
||||
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
|
||||
TAccessPathApproxSome(apa), apa0, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2522,9 +2595,10 @@ pragma[nomagic]
|
||||
private predicate nodeMayUseSummary(
|
||||
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c |
|
||||
Stage4::parameterMayFlowThrough(_, c, apa, config) and
|
||||
nodeMayUseSummary0(n, c, state, apa, config)
|
||||
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
|
||||
Stage4::parameterMayFlowThrough(p, apa, config) and
|
||||
nodeMayUseSummary0(n, c, pos, state, apa, config) and
|
||||
p.isParameterOf(c, pos)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2532,7 +2606,7 @@ private newtype TSummaryCtx =
|
||||
TSummaryCtxNone() or
|
||||
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
|
||||
exists(Configuration config |
|
||||
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
|
||||
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
|
||||
Stage4::revFlow(p, state, _, config)
|
||||
)
|
||||
}
|
||||
@@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough(
|
||||
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
|
||||
AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos |
|
||||
exists(PathNodeMid mid, RetNodeEx ret |
|
||||
pathNode(mid, ret, state, cc, sc, ap, config, _) and
|
||||
kind = ret.getKind() and
|
||||
apa = ap.getApprox() and
|
||||
pos = sc.getParameterPos() and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
sc.getParamNode().allowParameterReturnInSelf()
|
||||
)
|
||||
parameterFlowThroughAllowed(sc.getParamNode(), kind)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -915,6 +915,17 @@ private module Cached {
|
||||
TDataFlowCallNone() or
|
||||
TDataFlowCallSome(DataFlowCall call)
|
||||
|
||||
cached
|
||||
newtype TParameterPositionOption =
|
||||
TParameterPositionNone() or
|
||||
TParameterPositionSome(ParameterPosition pos)
|
||||
|
||||
cached
|
||||
newtype TReturnCtx =
|
||||
TReturnCtxNone() or
|
||||
TReturnCtxNoFlowThrough() or
|
||||
TReturnCtxMaybeFlowThrough(ReturnKindExt kind)
|
||||
|
||||
cached
|
||||
newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) }
|
||||
|
||||
@@ -1304,6 +1315,44 @@ class DataFlowCallOption extends TDataFlowCallOption {
|
||||
}
|
||||
}
|
||||
|
||||
/** An optional `ParameterPosition`. */
|
||||
class ParameterPositionOption extends TParameterPositionOption {
|
||||
string toString() {
|
||||
this = TParameterPositionNone() and
|
||||
result = "(none)"
|
||||
or
|
||||
exists(ParameterPosition pos |
|
||||
this = TParameterPositionSome(pos) and
|
||||
result = pos.toString()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A return context used to calculate flow summaries in reverse flow.
|
||||
*
|
||||
* The possible values are:
|
||||
*
|
||||
* - `TReturnCtxNone()`: no return flow.
|
||||
* - `TReturnCtxNoFlowThrough()`: return flow, but flow through is not possible.
|
||||
* - `TReturnCtxMaybeFlowThrough(ReturnKindExt kind)`: return flow, of kind `kind`, and
|
||||
* flow through may be possible.
|
||||
*/
|
||||
class ReturnCtx extends TReturnCtx {
|
||||
string toString() {
|
||||
this = TReturnCtxNone() and
|
||||
result = "(none)"
|
||||
or
|
||||
this = TReturnCtxNoFlowThrough() and
|
||||
result = "(no flow through)"
|
||||
or
|
||||
exists(ReturnKindExt kind |
|
||||
this = TReturnCtxMaybeFlowThrough(kind) and
|
||||
result = kind.toString()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** A `Content` tagged with the type of a containing object. */
|
||||
class TypedContent extends MkTypedContent {
|
||||
private Content c;
|
||||
|
||||
@@ -244,4 +244,20 @@ module Consistency {
|
||||
not callable = viableCallable(call) and
|
||||
not any(ConsistencyConfiguration c).viableImplInCallContextTooLargeExclude(call, ctx, callable)
|
||||
}
|
||||
|
||||
query predicate uniqueParameterNodeAtPosition(
|
||||
DataFlowCallable c, ParameterPosition pos, Node p, string msg
|
||||
) {
|
||||
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
|
||||
) {
|
||||
isParameterNode(p, c, pos) and
|
||||
not exists(unique(ParameterPosition pos0 | isParameterNode(p, c, pos0))) and
|
||||
msg = "Parameter node with multiple positions."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ abstract class Configuration extends string {
|
||||
/**
|
||||
* Holds if `sink` is a relevant data flow sink accepting `state`.
|
||||
*/
|
||||
predicate isSink(Node source, FlowState state) { none() }
|
||||
predicate isSink(Node sink, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited. This completely removes
|
||||
@@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
|
||||
}
|
||||
|
||||
ParameterPosition getPosition() { this.isParameterOf(_, result) }
|
||||
|
||||
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
|
||||
}
|
||||
|
||||
private class RetNodeEx extends NodeEx {
|
||||
@@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if flow from `p` to a return node of kind `kind` is allowed.
|
||||
*
|
||||
* We don't expect a parameter to return stored in itself, unless
|
||||
* explicitly allowed
|
||||
*/
|
||||
bindingset[p, kind]
|
||||
private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) {
|
||||
exists(ParameterPosition pos | p.isParameterOf(_, pos) |
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
allowParameterReturnInSelfCached(p.asNode())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if flow from a parameter at position `pos` inside `c` to a return node of
|
||||
* kind `kind` is allowed.
|
||||
*
|
||||
* We don't expect a parameter to return stored in itself, unless
|
||||
* explicitly allowed
|
||||
*/
|
||||
bindingset[c, pos, kind]
|
||||
private predicate parameterFlowThroughAllowed(
|
||||
DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
p.isParameterOf(c, pos) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
private module Stage1 implements StageSig {
|
||||
class Ap = Unit;
|
||||
|
||||
@@ -981,21 +1011,22 @@ private module Stage1 implements StageSig {
|
||||
* candidate for the origin of a summary.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||
exists(ReturnKindExt kind |
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
|
||||
exists(DataFlowCallable c, ReturnKindExt kind |
|
||||
throughFlowNodeCand(p, config) and
|
||||
returnFlowCallableNodeCand(c, kind, config) and
|
||||
p.getEnclosingCallable() = c and
|
||||
exists(ap) and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
|
||||
throughFlowNodeCand(ret, config) and
|
||||
kind = ret.getKind()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
|
||||
exists(ArgNodeEx arg, boolean toReturn |
|
||||
@@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1(
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand1(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config
|
||||
) {
|
||||
viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
|
||||
Stage1::revFlow(ret, config) and
|
||||
not outBarrier(ret, config) and
|
||||
not inBarrier(out, config)
|
||||
exists(ReturnPosition pos |
|
||||
viableReturnPosOutNodeCand1(call, pos, out, config) and
|
||||
pos = ret.getReturnPosition() and
|
||||
kind = pos.getKind() and
|
||||
Stage1::revFlow(ret, config) and
|
||||
not outBarrier(ret, config) and
|
||||
not inBarrier(out, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1(
|
||||
* edge in the graph of paths between sources and sinks that ignores call
|
||||
* contexts.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private int branch(NodeEx n1, Configuration conf) {
|
||||
result =
|
||||
strictcount(NodeEx n |
|
||||
flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
|
||||
flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) {
|
||||
* edge in the graph of paths between sources and sinks that ignores call
|
||||
* contexts.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private int join(NodeEx n2, Configuration conf) {
|
||||
result =
|
||||
strictcount(NodeEx n |
|
||||
flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
|
||||
flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand1(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
flowOutOfCallNodeCand1(call, ret, out, config) and
|
||||
flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and
|
||||
exists(int b, int j |
|
||||
b = branch(ret, config) and
|
||||
j = join(out, config) and
|
||||
b = branch(ret, pragma[only_bind_into](config)) and
|
||||
j = join(out, pragma[only_bind_into](config)) and
|
||||
if b.minimum(j) <= config.fieldFlowBranchLimit()
|
||||
then allowsFieldFlow = true
|
||||
else allowsFieldFlow = false
|
||||
@@ -1136,10 +1174,10 @@ pragma[nomagic]
|
||||
private predicate flowIntoCallNodeCand1(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowIntoCallNodeCand1(call, arg, p, config) and
|
||||
flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and
|
||||
exists(int b, int j |
|
||||
b = branch(arg, config) and
|
||||
j = join(p, config) and
|
||||
b = branch(arg, pragma[only_bind_into](config)) and
|
||||
j = join(p, pragma[only_bind_into](config)) and
|
||||
if b.minimum(j) <= config.fieldFlowBranchLimit()
|
||||
then allowsFieldFlow = true
|
||||
else allowsFieldFlow = false
|
||||
@@ -1156,7 +1194,9 @@ private signature module StageSig {
|
||||
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
|
||||
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
|
||||
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
|
||||
|
||||
predicate storeStepCand(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
|
||||
@@ -1222,7 +1262,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
);
|
||||
|
||||
predicate flowOutOfCall(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
);
|
||||
|
||||
predicate flowIntoCall(
|
||||
@@ -1247,14 +1288,14 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowThroughOutOfCall(
|
||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
|
||||
NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
|
||||
pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call)
|
||||
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call) and
|
||||
c = ret.getEnclosingCallable()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1262,29 +1303,32 @@ private module MkStage<StageSig PrevStage> {
|
||||
* configuration `config`.
|
||||
*
|
||||
* The call context `cc` records whether the node is reached through an
|
||||
* argument in a call, and if so, `argAp` records the access path of that
|
||||
* argument.
|
||||
* argument in a call, and if so, `summaryCtx` and `argAp` record the
|
||||
* corresponding parameter position and access path of that argument, respectively.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate fwdFlow(
|
||||
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow0(node, state, cc, argAp, ap, config) and
|
||||
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
|
||||
filter(node, state, ap, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlow0(
|
||||
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
sourceNode(node, state, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
argAp = apNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
ap = getApNil(node)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
|
||||
fwdFlow(mid, state0, cc, argAp, ap0, config) and
|
||||
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
|
||||
localCc = getLocalCc(mid, cc)
|
||||
|
|
||||
localStep(mid, state0, node, state, true, _, config, localCc) and
|
||||
@@ -1295,65 +1339,82 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
jumpStep(mid, node, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone()
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, ApNil nil |
|
||||
fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and
|
||||
additionalJumpStep(mid, node, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and
|
||||
additionalJumpStateStep(mid, state0, node, state, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(TypedContent tc, Ap ap0 |
|
||||
fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
|
||||
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
|
||||
ap = apCons(tc, ap0)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Ap ap0, Content c |
|
||||
fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
|
||||
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
|
||||
fwdFlowConsCand(ap0, c, ap, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(ApApprox apa |
|
||||
fwdFlowIn(_, node, state, _, cc, _, ap, config) and
|
||||
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
|
||||
apa = getApprox(ap) and
|
||||
if PrevStage::parameterMayFlowThrough(node, _, apa, config)
|
||||
then argAp = apSome(ap)
|
||||
else argAp = apNone()
|
||||
if PrevStage::parameterMayFlowThrough(node, apa, config)
|
||||
then (
|
||||
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
|
||||
argAp = apSome(ap)
|
||||
) else (
|
||||
summaryCtx = TParameterPositionNone() and argAp = apNone()
|
||||
)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
|
||||
exists(
|
||||
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
|
||||
DataFlowCallable inner
|
||||
|
|
||||
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
cc = getCallContextReturn(inner, call, innercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
or
|
||||
exists(DataFlowCall call, Ap argAp0 |
|
||||
fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, cc, argAp, argAp0, config)
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
|
||||
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowStore(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
|
||||
Configuration config
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
exists(DataFlowType contentType |
|
||||
fwdFlow(node1, state, cc, argAp, ap1, config) and
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
|
||||
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
|
||||
typecheckStore(ap1, contentType)
|
||||
)
|
||||
@@ -1366,7 +1427,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
|
||||
exists(TypedContent tc |
|
||||
fwdFlowStore(_, tail, tc, _, _, _, _, config) and
|
||||
fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and
|
||||
tc.getContent() = c and
|
||||
cons = apCons(tc, tail)
|
||||
)
|
||||
@@ -1374,21 +1435,21 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead(
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
|
||||
Configuration config
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
fwdFlow(node1, state, cc, argAp, ap, config) and
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
getHeadContent(ap) = c
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIn(
|
||||
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp,
|
||||
Ap ap, Configuration config
|
||||
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ArgNodeEx arg, boolean allowsFieldFlow |
|
||||
fwdFlow(arg, state, outercc, argAp, ap, config) and
|
||||
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
|
||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
@@ -1396,29 +1457,19 @@ private module MkStage<StageSig PrevStage> {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowOutNotFromArg(
|
||||
NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
|
||||
private predicate fwdFlowOutFromArg(
|
||||
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(
|
||||
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
|
||||
DataFlowCallable inner
|
||||
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
||||
|
|
||||
fwdFlow(ret, state, innercc, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
ccOut = getCallContextReturn(inner, call, innercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowOutFromArg(
|
||||
DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
|
||||
fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
|
||||
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
|
||||
config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1428,11 +1479,13 @@ private module MkStage<StageSig PrevStage> {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(
|
||||
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
ParameterPosition pos, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
|
||||
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
|
||||
exists(ParamNodeEx param |
|
||||
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
|
||||
pos = param.getPosition()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1440,27 +1493,40 @@ private module MkStage<StageSig PrevStage> {
|
||||
private predicate storeStepFwd(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
|
||||
) {
|
||||
fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
|
||||
fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and
|
||||
ap2 = apCons(tc, ap1) and
|
||||
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config)
|
||||
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config)
|
||||
}
|
||||
|
||||
private predicate readStepFwd(
|
||||
NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
|
||||
) {
|
||||
fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and
|
||||
fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and
|
||||
fwdFlowConsCand(ap1, c, ap2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
|
||||
exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
|
||||
fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
|
||||
private predicate returnFlowsThrough0(
|
||||
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
|
||||
ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(boolean allowsFieldFlow |
|
||||
fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
|
||||
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
|
||||
pragma[only_bind_into](config))
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnFlowsThrough(
|
||||
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c, ParameterPosition ppos |
|
||||
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
|
||||
p.isParameterOf(c, ppos) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1468,118 +1534,130 @@ private module MkStage<StageSig PrevStage> {
|
||||
private predicate flowThroughIntoCall(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
|
||||
PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
|
||||
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnNodeMayFlowThrough(
|
||||
RetNodeEx ret, FlowState state, Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
|
||||
exists(Ap argAp |
|
||||
flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
|
||||
returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` with access path `ap` is part of a path from a source to a
|
||||
* sink in the configuration `config`.
|
||||
*
|
||||
* The Boolean `toReturn` records whether the node must be returned from the
|
||||
* enclosing callable in order to reach a sink, and if so, `returnAp` records
|
||||
* the access path of the returned value.
|
||||
* The parameter `returnCtx` records whether (and how) the node must be returned
|
||||
* from the enclosing callable in order to reach a sink, and if so, `returnAp`
|
||||
* records the access path of the returned value.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate revFlow(
|
||||
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
revFlow0(node, state, toReturn, returnAp, ap, config) and
|
||||
fwdFlow(node, state, _, _, ap, config)
|
||||
revFlow0(node, state, returnCtx, returnAp, ap, config) and
|
||||
fwdFlow(node, state, _, _, _, ap, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlow0(
|
||||
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
fwdFlow(node, state, _, _, ap, config) and
|
||||
fwdFlow(node, state, _, _, _, ap, config) and
|
||||
sinkNode(node, state, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
(
|
||||
if hasSinkCallCtx(config)
|
||||
then returnCtx = TReturnCtxNoFlowThrough()
|
||||
else returnCtx = TReturnCtxNone()
|
||||
) and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0 |
|
||||
localStep(node, state, mid, state0, true, _, config, _) and
|
||||
revFlow(mid, state0, toReturn, returnAp, ap, config)
|
||||
revFlow(mid, state0, returnCtx, returnAp, ap, config)
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and
|
||||
revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
|
||||
revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
jumpStep(node, mid, config) and
|
||||
revFlow(mid, state, _, _, ap, config) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone()
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, ApNil nil |
|
||||
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
additionalJumpStep(node, mid, config) and
|
||||
revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
additionalJumpStateStep(node, state, mid, state0, config) and
|
||||
revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil,
|
||||
pragma[only_bind_into](config)) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Ap ap0, Content c |
|
||||
revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
|
||||
revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and
|
||||
revFlowConsCand(ap0, c, ap, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(NodeEx mid, Ap ap0 |
|
||||
revFlow(mid, state, toReturn, returnAp, ap0, config) and
|
||||
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
|
||||
readStepFwd(node, ap, _, mid, ap0, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
revFlowInNotToReturn(node, state, returnAp, ap, config) and
|
||||
toReturn = false
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
|
||||
flowIntoCall(_, node, p, allowsFieldFlow, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
returnCtx = TReturnCtxNone()
|
||||
)
|
||||
or
|
||||
exists(DataFlowCall call, Ap returnAp0 |
|
||||
revFlowInToReturn(call, node, state, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 |
|
||||
revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
revFlowOut(_, node, state, _, _, ap, config) and
|
||||
toReturn = true and
|
||||
if returnNodeMayFlowThrough(node, state, ap, config)
|
||||
then returnAp = apSome(ap)
|
||||
else returnAp = apNone()
|
||||
exists(ReturnKindExt kind |
|
||||
revFlowOut(_, node, kind, state, _, _, ap, config) and
|
||||
if returnFlowsThrough(node, kind, state, _, _, _, ap, config)
|
||||
then (
|
||||
returnCtx = TReturnCtxMaybeFlowThrough(kind) and
|
||||
returnAp = apSome(ap)
|
||||
) else (
|
||||
returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowStore(
|
||||
Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid,
|
||||
boolean toReturn, ApOption returnAp, Configuration config
|
||||
ReturnCtx returnCtx, ApOption returnAp, Configuration config
|
||||
) {
|
||||
revFlow(mid, state, toReturn, returnAp, ap0, config) and
|
||||
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
|
||||
storeStepFwd(node, ap, tc, mid, ap0, config) and
|
||||
tc.getContent() = c
|
||||
}
|
||||
@@ -1599,35 +1677,27 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowOut(
|
||||
DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx,
|
||||
ApOption returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(NodeEx out, boolean allowsFieldFlow |
|
||||
revFlow(out, state, toReturn, returnAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowInNotToReturn(
|
||||
ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, false, returnAp, ap, config) and
|
||||
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
|
||||
revFlow(out, state, returnCtx, returnAp, ap, config) and
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowInToReturn(
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, true, apSome(returnAp), ap, config) and
|
||||
revFlow(pragma[only_bind_into](p), state,
|
||||
TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
|
||||
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1638,11 +1708,12 @@ private module MkStage<StageSig PrevStage> {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate revFlowIsReturned(
|
||||
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(RetNodeEx ret, FlowState state, CcCall ccc |
|
||||
revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and
|
||||
fwdFlow(ret, state, ccc, apSome(_), ap, config) and
|
||||
revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and
|
||||
returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and
|
||||
matchesCall(ccc, call)
|
||||
)
|
||||
}
|
||||
@@ -1713,40 +1784,39 @@ private module MkStage<StageSig PrevStage> {
|
||||
validAp(ap, config)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate parameterFlow(
|
||||
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
|
||||
pragma[nomagic]
|
||||
private predicate parameterFlowsThroughRev(
|
||||
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
|
||||
) {
|
||||
revFlow(p, _, true, apSome(ap0), ap, config) and
|
||||
c = p.getEnclosingCallable()
|
||||
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
}
|
||||
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||
exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
|
||||
parameterFlow(p, ap, ap0, c, config) and
|
||||
c = ret.getEnclosingCallable() and
|
||||
revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_),
|
||||
pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and
|
||||
fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
pragma[nomagic]
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
|
||||
exists(RetNodeEx ret, ReturnKindExt kind |
|
||||
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
|
||||
parameterFlowsThroughRev(p, ap, kind, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
|
||||
exists(ParamNodeEx p, Ap ap |
|
||||
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
|
||||
parameterFlowsThroughRev(p, ap, kind, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
|
||||
exists(
|
||||
Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
|
||||
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
|
||||
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
||||
|
|
||||
revFlow(arg, state, toReturn, returnAp, ap, config) and
|
||||
revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
|
||||
revFlow(arg, state, returnCtx, returnAp, ap, config) and
|
||||
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1754,14 +1824,13 @@ private module MkStage<StageSig PrevStage> {
|
||||
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
|
||||
) {
|
||||
fwd = true and
|
||||
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and
|
||||
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and
|
||||
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
|
||||
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
|
||||
states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and
|
||||
states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and
|
||||
tuples =
|
||||
count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
|
||||
fwdFlow(n, state, cc, argAp, ap, config)
|
||||
)
|
||||
count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config))
|
||||
or
|
||||
fwd = false and
|
||||
nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
|
||||
@@ -1769,8 +1838,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
|
||||
states = count(FlowState state | revFlow(_, state, _, _, _, config)) and
|
||||
tuples =
|
||||
count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap |
|
||||
revFlow(n, state, b, retAp, ap, config)
|
||||
count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap |
|
||||
revFlow(n, state, returnCtx, retAp, ap, config)
|
||||
)
|
||||
}
|
||||
/* End: Stage logic. */
|
||||
@@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
|
||||
exists(lcc)
|
||||
}
|
||||
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
|
||||
|
||||
predicate flowIntoCall = flowIntoCallNodeCand1/5;
|
||||
|
||||
@@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand2(
|
||||
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
|
||||
flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and
|
||||
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
|
||||
Stage2::revFlowAlias(node1, pragma[only_bind_into](config))
|
||||
}
|
||||
@@ -2021,8 +2091,8 @@ private module LocalFlowBigStep {
|
||||
exists(NodeEx next | Stage2::revFlow(next, state, config) |
|
||||
jumpStep(node, next, config) or
|
||||
additionalJumpStep(node, next, config) or
|
||||
flowIntoCallNodeCand1(_, node, next, config) or
|
||||
flowOutOfCallNodeCand1(_, node, next, config) or
|
||||
flowIntoCallNodeCand2(_, node, next, _, config) or
|
||||
flowOutOfCallNodeCand2(_, node, _, next, _, config) or
|
||||
Stage2::storeStepCand(node, _, _, next, _, config) or
|
||||
Stage2::readStepCand(node, _, next, config)
|
||||
)
|
||||
@@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
|
||||
}
|
||||
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
|
||||
|
||||
predicate flowIntoCall = flowIntoCallNodeCand2/5;
|
||||
|
||||
@@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx(
|
||||
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
|
||||
) {
|
||||
exists(AccessPathFront apf |
|
||||
Stage3::revFlow(node, state, true, _, apf, config) and
|
||||
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
|
||||
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
|
||||
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
|
||||
config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
|
||||
pragma[nomagic]
|
||||
predicate flowOutOfCall(
|
||||
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
exists(FlowState state |
|
||||
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
|
||||
flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
|
||||
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
|
||||
pragma[only_bind_into](config))
|
||||
@@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeMayUseSummary0(
|
||||
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
|
||||
NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa,
|
||||
Configuration config
|
||||
) {
|
||||
exists(AccessPathApprox apa0 |
|
||||
Stage4::parameterMayFlowThrough(_, c, _, _) and
|
||||
Stage4::revFlow(n, state, true, _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
|
||||
n.getEnclosingCallable() = c
|
||||
c = n.getEnclosingCallable() and
|
||||
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
|
||||
TAccessPathApproxSome(apa), apa0, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2522,9 +2595,10 @@ pragma[nomagic]
|
||||
private predicate nodeMayUseSummary(
|
||||
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c |
|
||||
Stage4::parameterMayFlowThrough(_, c, apa, config) and
|
||||
nodeMayUseSummary0(n, c, state, apa, config)
|
||||
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
|
||||
Stage4::parameterMayFlowThrough(p, apa, config) and
|
||||
nodeMayUseSummary0(n, c, pos, state, apa, config) and
|
||||
p.isParameterOf(c, pos)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2532,7 +2606,7 @@ private newtype TSummaryCtx =
|
||||
TSummaryCtxNone() or
|
||||
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
|
||||
exists(Configuration config |
|
||||
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
|
||||
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
|
||||
Stage4::revFlow(p, state, _, config)
|
||||
)
|
||||
}
|
||||
@@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough(
|
||||
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
|
||||
AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos |
|
||||
exists(PathNodeMid mid, RetNodeEx ret |
|
||||
pathNode(mid, ret, state, cc, sc, ap, config, _) and
|
||||
kind = ret.getKind() and
|
||||
apa = ap.getApprox() and
|
||||
pos = sc.getParameterPos() and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
sc.getParamNode().allowParameterReturnInSelf()
|
||||
)
|
||||
parameterFlowThroughAllowed(sc.getParamNode(), kind)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,642 +1,21 @@
|
||||
/**
|
||||
* DEPRECATED: Use `semmle.code.cpp.ir.dataflow.TaintTracking` as a replacement.
|
||||
*
|
||||
* An IR taint tracking library that uses an IR DataFlow configuration to track
|
||||
* taint from user inputs as defined by `semmle.code.cpp.security.Security`.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.security.Security
|
||||
private import semmle.code.cpp.ir.dataflow.DataFlow
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
|
||||
private import semmle.code.cpp.ir.dataflow.DataFlow3
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import semmle.code.cpp.ir.dataflow.ResolveCall
|
||||
private import semmle.code.cpp.controlflow.IRGuards
|
||||
private import semmle.code.cpp.models.interfaces.Taint
|
||||
private import semmle.code.cpp.models.interfaces.DataFlow
|
||||
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.DefaultTaintTrackingImpl as DefaultTaintTrackingImpl
|
||||
|
||||
/**
|
||||
* A predictable instruction is one where an external user can predict
|
||||
* the value. For example, a literal in the source code is considered
|
||||
* predictable.
|
||||
*/
|
||||
private predicate predictableInstruction(Instruction instr) {
|
||||
instr instanceof ConstantInstruction
|
||||
or
|
||||
instr instanceof StringConstantInstruction
|
||||
or
|
||||
// This could be a conversion on a string literal
|
||||
predictableInstruction(instr.(UnaryInstruction).getUnary())
|
||||
}
|
||||
deprecated predicate predictableOnlyFlow = DefaultTaintTrackingImpl::predictableOnlyFlow/1;
|
||||
|
||||
/**
|
||||
* Functions that we should only allow taint to flow through (to the return
|
||||
* value) if all but the source argument are 'predictable'. This is done to
|
||||
* emulate the old security library's implementation rather than due to any
|
||||
* strong belief that this is the right approach.
|
||||
*
|
||||
* Note that the list itself is not very principled; it consists of all the
|
||||
* functions listed in the old security library's [default] `isPureFunction`
|
||||
* that have more than one argument, but are not in the old taint tracking
|
||||
* library's `returnArgument` predicate.
|
||||
*/
|
||||
predicate predictableOnlyFlow(string name) {
|
||||
name =
|
||||
[
|
||||
"strcasestr", "strchnul", "strchr", "strchrnul", "strcmp", "strcspn", "strncmp", "strndup",
|
||||
"strnlen", "strrchr", "strspn", "strstr", "strtod", "strtof", "strtol", "strtoll", "strtoq",
|
||||
"strtoul"
|
||||
]
|
||||
}
|
||||
deprecated predicate tainted = DefaultTaintTrackingImpl::tainted/2;
|
||||
|
||||
private DataFlow::Node getNodeForSource(Expr source) {
|
||||
isUserInput(source, _) and
|
||||
result = getNodeForExpr(source)
|
||||
}
|
||||
deprecated predicate taintedIncludingGlobalVars =
|
||||
DefaultTaintTrackingImpl::taintedIncludingGlobalVars/3;
|
||||
|
||||
private DataFlow::Node getNodeForExpr(Expr node) {
|
||||
result = DataFlow::exprNode(node)
|
||||
or
|
||||
// Some of the sources in `isUserInput` are intended to match the value of
|
||||
// an expression, while others (those modeled below) are intended to match
|
||||
// the taint that propagates out of an argument, like the `char *` argument
|
||||
// to `gets`. It's impossible here to tell which is which, but the "access
|
||||
// to argv" source is definitely not intended to match an output argument,
|
||||
// and it causes false positives if we let it.
|
||||
//
|
||||
// This case goes together with the similar (but not identical) rule in
|
||||
// `nodeIsBarrierIn`.
|
||||
result = DataFlow::definitionByReferenceNodeFromArgument(node) and
|
||||
not argv(node.(VariableAccess).getTarget())
|
||||
}
|
||||
deprecated predicate globalVarFromId = DefaultTaintTrackingImpl::globalVarFromId/1;
|
||||
|
||||
private class DefaultTaintTrackingCfg extends TaintTracking::Configuration {
|
||||
DefaultTaintTrackingCfg() { this = "DefaultTaintTrackingCfg" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source = getNodeForSource(_) }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { exists(adjustedSink(sink)) }
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { nodeIsBarrier(node) }
|
||||
|
||||
override predicate isSanitizerIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
|
||||
}
|
||||
|
||||
private class ToGlobalVarTaintTrackingCfg extends TaintTracking::Configuration {
|
||||
ToGlobalVarTaintTrackingCfg() { this = "GlobalVarTaintTrackingCfg" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source = getNodeForSource(_) }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
sink.asVariable() instanceof GlobalOrNamespaceVariable
|
||||
}
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) {
|
||||
writesVariable(n1.asInstruction(), n2.asVariable().(GlobalOrNamespaceVariable))
|
||||
or
|
||||
readsVariable(n2.asInstruction(), n1.asVariable().(GlobalOrNamespaceVariable))
|
||||
}
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { nodeIsBarrier(node) }
|
||||
|
||||
override predicate isSanitizerIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
|
||||
}
|
||||
|
||||
private class FromGlobalVarTaintTrackingCfg extends TaintTracking2::Configuration {
|
||||
FromGlobalVarTaintTrackingCfg() { this = "FromGlobalVarTaintTrackingCfg" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
// This set of sources should be reasonably small, which is good for
|
||||
// performance since the set of sinks is very large.
|
||||
exists(ToGlobalVarTaintTrackingCfg otherCfg | otherCfg.hasFlowTo(source))
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { exists(adjustedSink(sink)) }
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) {
|
||||
// Additional step for flow out of variables. There is no flow _into_
|
||||
// variables in this configuration, so this step only serves to take flow
|
||||
// out of a variable that's a source.
|
||||
readsVariable(n2.asInstruction(), n1.asVariable())
|
||||
}
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { nodeIsBarrier(node) }
|
||||
|
||||
override predicate isSanitizerIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
|
||||
}
|
||||
|
||||
private predicate readsVariable(LoadInstruction load, Variable var) {
|
||||
load.getSourceAddress().(VariableAddressInstruction).getAstVariable() = var
|
||||
}
|
||||
|
||||
private predicate writesVariable(StoreInstruction store, Variable var) {
|
||||
store.getDestinationAddress().(VariableAddressInstruction).getAstVariable() = var
|
||||
}
|
||||
|
||||
/**
|
||||
* A variable that has any kind of upper-bound check anywhere in the program. This is
|
||||
* biased towards being inclusive because there are a lot of valid ways of doing an
|
||||
* upper bounds checks if we don't consider where it occurs, for example:
|
||||
* ```
|
||||
* if (x < 10) { sink(x); }
|
||||
*
|
||||
* if (10 > y) { sink(y); }
|
||||
*
|
||||
* if (z > 10) { z = 10; }
|
||||
* sink(z);
|
||||
* ```
|
||||
*/
|
||||
// TODO: This coarse overapproximation, ported from the old taint tracking
|
||||
// library, could be replaced with an actual semantic check that a particular
|
||||
// variable _access_ is guarded by an upper-bound check. We probably don't want
|
||||
// to do this right away since it could expose a lot of FPs that were
|
||||
// previously suppressed by this predicate by coincidence.
|
||||
private predicate hasUpperBoundsCheck(Variable var) {
|
||||
exists(RelationalOperation oper, VariableAccess access |
|
||||
oper.getAnOperand() = access and
|
||||
access.getTarget() = var and
|
||||
// Comparing to 0 is not an upper bound check
|
||||
not oper.getAnOperand().getValue() = "0"
|
||||
)
|
||||
}
|
||||
|
||||
private predicate nodeIsBarrierEqualityCandidate(
|
||||
DataFlow::Node node, Operand access, Variable checkedVar
|
||||
) {
|
||||
readsVariable(node.asInstruction(), checkedVar) and
|
||||
any(IRGuardCondition guard).ensuresEq(access, _, _, node.asInstruction().getBlock(), true)
|
||||
}
|
||||
|
||||
cached
|
||||
private module Cached {
|
||||
cached
|
||||
predicate nodeIsBarrier(DataFlow::Node node) {
|
||||
exists(Variable checkedVar |
|
||||
readsVariable(node.asInstruction(), checkedVar) and
|
||||
hasUpperBoundsCheck(checkedVar)
|
||||
)
|
||||
or
|
||||
exists(Variable checkedVar, Operand access |
|
||||
/*
|
||||
* This node is guarded by a condition that forces the accessed variable
|
||||
* to equal something else. For example:
|
||||
* ```
|
||||
* x = taintsource()
|
||||
* if (x == 10) {
|
||||
* taintsink(x); // not considered tainted
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
|
||||
nodeIsBarrierEqualityCandidate(node, access, checkedVar) and
|
||||
readsVariable(access.getDef(), checkedVar)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate nodeIsBarrierIn(DataFlow::Node node) {
|
||||
// don't use dataflow into taint sources, as this leads to duplicate results.
|
||||
exists(Expr source | isUserInput(source, _) |
|
||||
node = DataFlow::exprNode(source)
|
||||
or
|
||||
// This case goes together with the similar (but not identical) rule in
|
||||
// `getNodeForSource`.
|
||||
node = DataFlow::definitionByReferenceNodeFromArgument(source)
|
||||
)
|
||||
or
|
||||
// don't use dataflow into binary instructions if both operands are unpredictable
|
||||
exists(BinaryInstruction iTo |
|
||||
iTo = node.asInstruction() and
|
||||
not predictableInstruction(iTo.getLeft()) and
|
||||
not predictableInstruction(iTo.getRight()) and
|
||||
// propagate taint from either the pointer or the offset, regardless of predictability
|
||||
not iTo instanceof PointerArithmeticInstruction
|
||||
)
|
||||
or
|
||||
// don't use dataflow through calls to pure functions if two or more operands
|
||||
// are unpredictable
|
||||
exists(Instruction iFrom1, Instruction iFrom2, CallInstruction iTo |
|
||||
iTo = node.asInstruction() and
|
||||
isPureFunction(iTo.getStaticCallTarget().getName()) and
|
||||
iFrom1 = iTo.getAnArgument() and
|
||||
iFrom2 = iTo.getAnArgument() and
|
||||
not predictableInstruction(iFrom1) and
|
||||
not predictableInstruction(iFrom2) and
|
||||
iFrom1 != iFrom2
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
Element adjustedSink(DataFlow::Node sink) {
|
||||
// TODO: is it more appropriate to use asConvertedExpr here and avoid
|
||||
// `getConversion*`? Or will that cause us to miss some cases where there's
|
||||
// flow to a conversion (like a `ReferenceDereferenceExpr`) and we want to
|
||||
// pretend there was flow to the converted `Expr` for the sake of
|
||||
// compatibility.
|
||||
sink.asExpr().getConversion*() = result
|
||||
or
|
||||
// For compatibility, send flow from arguments to parameters, even for
|
||||
// functions with no body.
|
||||
exists(FunctionCall call, int i |
|
||||
sink.asExpr() = call.getArgument(pragma[only_bind_into](i)) and
|
||||
result = resolveCall(call).getParameter(pragma[only_bind_into](i))
|
||||
)
|
||||
or
|
||||
// For compatibility, send flow into a `Variable` if there is flow to any
|
||||
// Load or Store of that variable.
|
||||
exists(CopyInstruction copy |
|
||||
copy.getSourceValue() = sink.asInstruction() and
|
||||
(
|
||||
readsVariable(copy, result) or
|
||||
writesVariable(copy, result)
|
||||
) and
|
||||
not hasUpperBoundsCheck(result)
|
||||
)
|
||||
or
|
||||
// For compatibility, send flow into a `NotExpr` even if it's part of a
|
||||
// short-circuiting condition and thus might get skipped.
|
||||
result.(NotExpr).getOperand() = sink.asExpr()
|
||||
or
|
||||
// Taint postfix and prefix crement operations when their operand is tainted.
|
||||
result.(CrementOperation).getAnOperand() = sink.asExpr()
|
||||
or
|
||||
// Taint `e1 += e2`, `e &= e2` and friends when `e1` or `e2` is tainted.
|
||||
result.(AssignOperation).getAnOperand() = sink.asExpr()
|
||||
or
|
||||
result =
|
||||
sink.asOperand()
|
||||
.(SideEffectOperand)
|
||||
.getUse()
|
||||
.(ReadSideEffectInstruction)
|
||||
.getArgumentDef()
|
||||
.getUnconvertedResultExpression()
|
||||
}
|
||||
|
||||
/**
|
||||
* Step to return value of a modeled function when an input taints the
|
||||
* dereference of the return value.
|
||||
*/
|
||||
cached
|
||||
predicate additionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) {
|
||||
exists(CallInstruction call, Function func, FunctionInput modelIn, FunctionOutput modelOut |
|
||||
n1.asOperand() = callInput(call, modelIn) and
|
||||
(
|
||||
func.(TaintFunction).hasTaintFlow(modelIn, modelOut)
|
||||
or
|
||||
func.(DataFlowFunction).hasDataFlow(modelIn, modelOut)
|
||||
) and
|
||||
call.getStaticCallTarget() = func and
|
||||
modelOut.isReturnValueDeref() and
|
||||
call = n2.asInstruction()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private import Cached
|
||||
|
||||
/**
|
||||
* Holds if `tainted` may contain taint from `source`.
|
||||
*
|
||||
* A tainted expression is either directly user input, or is
|
||||
* computed from user input in a way that users can probably
|
||||
* control the exact output of the computation.
|
||||
*
|
||||
* This doesn't include data flow through global variables.
|
||||
* If you need that you must call `taintedIncludingGlobalVars`.
|
||||
*/
|
||||
cached
|
||||
predicate tainted(Expr source, Element tainted) {
|
||||
exists(DefaultTaintTrackingCfg cfg, DataFlow::Node sink |
|
||||
cfg.hasFlow(getNodeForSource(source), sink) and
|
||||
tainted = adjustedSink(sink)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `tainted` may contain taint from `source`, where the taint passed
|
||||
* through a global variable named `globalVar`.
|
||||
*
|
||||
* A tainted expression is either directly user input, or is
|
||||
* computed from user input in a way that users can probably
|
||||
* control the exact output of the computation.
|
||||
*
|
||||
* This version gives the same results as tainted but also includes
|
||||
* data flow through global variables.
|
||||
*
|
||||
* The parameter `globalVar` is the qualified name of the last global variable
|
||||
* used to move the value from source to tainted. If the taint did not pass
|
||||
* through a global variable, then `globalVar = ""`.
|
||||
*/
|
||||
cached
|
||||
predicate taintedIncludingGlobalVars(Expr source, Element tainted, string globalVar) {
|
||||
tainted(source, tainted) and
|
||||
globalVar = ""
|
||||
or
|
||||
exists(
|
||||
ToGlobalVarTaintTrackingCfg toCfg, FromGlobalVarTaintTrackingCfg fromCfg,
|
||||
DataFlow::VariableNode variableNode, GlobalOrNamespaceVariable global, DataFlow::Node sink
|
||||
|
|
||||
global = variableNode.getVariable() and
|
||||
toCfg.hasFlow(getNodeForSource(source), variableNode) and
|
||||
fromCfg.hasFlow(variableNode, sink) and
|
||||
tainted = adjustedSink(sink) and
|
||||
global = globalVarFromId(globalVar)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the global variable whose qualified name is `id`. Use this predicate
|
||||
* together with `taintedIncludingGlobalVars`. Example:
|
||||
*
|
||||
* ```
|
||||
* exists(string varName |
|
||||
* taintedIncludingGlobalVars(source, tainted, varName) and
|
||||
* var = globalVarFromId(varName)
|
||||
* )
|
||||
* ```
|
||||
*/
|
||||
GlobalOrNamespaceVariable globalVarFromId(string id) { id = result.getQualifiedName() }
|
||||
|
||||
/**
|
||||
* Provides definitions for augmenting source/sink pairs with data-flow paths
|
||||
* between them. From a `@kind path-problem` query, import this module in the
|
||||
* global scope, extend `TaintTrackingConfiguration`, and use `taintedWithPath`
|
||||
* in place of `tainted`.
|
||||
*
|
||||
* Importing this module will also import the query predicates that contain the
|
||||
* taint paths.
|
||||
*/
|
||||
module TaintedWithPath {
|
||||
private newtype TSingleton = MkSingleton()
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration that matches sources and sinks in the same
|
||||
* way as the `tainted` predicate.
|
||||
*
|
||||
* Override `isSink` and `taintThroughGlobals` as needed, but do not provide
|
||||
* a characteristic predicate.
|
||||
*/
|
||||
class TaintTrackingConfiguration extends TSingleton {
|
||||
/** Override this to specify which elements are sources in this configuration. */
|
||||
predicate isSource(Expr source) { exists(getNodeForSource(source)) }
|
||||
|
||||
/** Override this to specify which elements are sinks in this configuration. */
|
||||
abstract predicate isSink(Element e);
|
||||
|
||||
/** Override this to specify which expressions are barriers in this configuration. */
|
||||
predicate isBarrier(Expr e) { nodeIsBarrier(getNodeForExpr(e)) }
|
||||
|
||||
/**
|
||||
* Override this predicate to `any()` to allow taint to flow through global
|
||||
* variables.
|
||||
*/
|
||||
predicate taintThroughGlobals() { none() }
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = "TaintTrackingConfiguration" }
|
||||
}
|
||||
|
||||
private class AdjustedConfiguration extends TaintTracking3::Configuration {
|
||||
AdjustedConfiguration() { this = "AdjustedConfiguration" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
exists(TaintTrackingConfiguration cfg, Expr e |
|
||||
cfg.isSource(e) and source = getNodeForExpr(e)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
exists(TaintTrackingConfiguration cfg | cfg.isSink(adjustedSink(sink)))
|
||||
}
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) {
|
||||
// Steps into and out of global variables
|
||||
exists(TaintTrackingConfiguration cfg | cfg.taintThroughGlobals() |
|
||||
writesVariable(n1.asInstruction(), n2.asVariable().(GlobalOrNamespaceVariable))
|
||||
or
|
||||
readsVariable(n2.asInstruction(), n1.asVariable().(GlobalOrNamespaceVariable))
|
||||
)
|
||||
or
|
||||
additionalTaintStep(n1, n2)
|
||||
}
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) {
|
||||
exists(TaintTrackingConfiguration cfg, Expr e | cfg.isBarrier(e) and node = getNodeForExpr(e))
|
||||
}
|
||||
|
||||
override predicate isSanitizerIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
|
||||
}
|
||||
|
||||
/*
|
||||
* A sink `Element` may map to multiple `DataFlowX::PathNode`s via (the
|
||||
* inverse of) `adjustedSink`. For example, an `Expr` maps to all its
|
||||
* conversions, and a `Variable` maps to all loads and stores from it. Because
|
||||
* the path node is part of the tuple that constitutes the alert, this leads
|
||||
* to duplicate alerts.
|
||||
*
|
||||
* To avoid showing duplicates, we edit the graph to replace the final node
|
||||
* coming from the data-flow library with a node that matches exactly the
|
||||
* `Element` sink that's requested.
|
||||
*
|
||||
* The same is done for sources.
|
||||
*/
|
||||
|
||||
private newtype TPathNode =
|
||||
TWrapPathNode(DataFlow3::PathNode n) or
|
||||
// There's a single newtype constructor for both sources and sinks since
|
||||
// that makes it easiest to deal with the case where source = sink.
|
||||
TEndpointPathNode(Element e) {
|
||||
exists(AdjustedConfiguration cfg, DataFlow3::Node sourceNode, DataFlow3::Node sinkNode |
|
||||
cfg.hasFlow(sourceNode, sinkNode)
|
||||
|
|
||||
sourceNode = getNodeForExpr(e) and
|
||||
exists(TaintTrackingConfiguration ttCfg | ttCfg.isSource(e))
|
||||
or
|
||||
e = adjustedSink(sinkNode) and
|
||||
exists(TaintTrackingConfiguration ttCfg | ttCfg.isSink(e))
|
||||
)
|
||||
}
|
||||
|
||||
/** An opaque type used for the nodes of a data-flow path. */
|
||||
class PathNode extends TPathNode {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { none() }
|
||||
|
||||
/**
|
||||
* 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
|
||||
) {
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*/
|
||||
module Private {
|
||||
/** Gets a predecessor `PathNode` of `pathNode`, if any. */
|
||||
PathNode getAPredecessor(PathNode pathNode) { edges(result, pathNode) }
|
||||
|
||||
/** Gets the element that `pathNode` wraps, if any. */
|
||||
Element getElementFromPathNode(PathNode pathNode) {
|
||||
exists(DataFlow::Node node | node = pathNode.(WrapPathNode).inner().getNode() |
|
||||
result = node.asInstruction().getAst()
|
||||
or
|
||||
result = node.asOperand().getDef().getAst()
|
||||
)
|
||||
or
|
||||
result = pathNode.(EndpointPathNode).inner()
|
||||
}
|
||||
}
|
||||
|
||||
private class WrapPathNode extends PathNode, TWrapPathNode {
|
||||
DataFlow3::PathNode inner() { this = TWrapPathNode(result) }
|
||||
|
||||
override string toString() { result = this.inner().toString() }
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
this.inner().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
}
|
||||
|
||||
private class EndpointPathNode extends PathNode, TEndpointPathNode {
|
||||
Expr inner() { this = TEndpointPathNode(result) }
|
||||
|
||||
override string toString() { result = this.inner().toString() }
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
this.inner()
|
||||
.getLocation()
|
||||
.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
}
|
||||
|
||||
/** A PathNode whose `Element` is a source. It may also be a sink. */
|
||||
private class InitialPathNode extends EndpointPathNode {
|
||||
InitialPathNode() { exists(TaintTrackingConfiguration cfg | cfg.isSource(this.inner())) }
|
||||
}
|
||||
|
||||
/** A PathNode whose `Element` is a sink. It may also be a source. */
|
||||
private class FinalPathNode extends EndpointPathNode {
|
||||
FinalPathNode() { exists(TaintTrackingConfiguration cfg | cfg.isSink(this.inner())) }
|
||||
}
|
||||
|
||||
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
|
||||
query predicate edges(PathNode a, PathNode b) {
|
||||
DataFlow3::PathGraph::edges(a.(WrapPathNode).inner(), b.(WrapPathNode).inner())
|
||||
or
|
||||
// To avoid showing trivial-looking steps, we _replace_ the last node instead
|
||||
// of adding an edge out of it.
|
||||
exists(WrapPathNode sinkNode |
|
||||
DataFlow3::PathGraph::edges(a.(WrapPathNode).inner(), sinkNode.inner()) and
|
||||
b.(FinalPathNode).inner() = adjustedSink(sinkNode.inner().getNode())
|
||||
)
|
||||
or
|
||||
// Same for the first node
|
||||
exists(WrapPathNode sourceNode |
|
||||
DataFlow3::PathGraph::edges(sourceNode.inner(), b.(WrapPathNode).inner()) and
|
||||
sourceNode.inner().getNode() = getNodeForExpr(a.(InitialPathNode).inner())
|
||||
)
|
||||
or
|
||||
// Finally, handle the case where the path goes directly from a source to a
|
||||
// sink, meaning that they both need to be translated.
|
||||
exists(WrapPathNode sinkNode, WrapPathNode sourceNode |
|
||||
DataFlow3::PathGraph::edges(sourceNode.inner(), sinkNode.inner()) and
|
||||
sourceNode.inner().getNode() = getNodeForExpr(a.(InitialPathNode).inner()) and
|
||||
b.(FinalPathNode).inner() = adjustedSink(sinkNode.inner().getNode())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there is flow from `arg` to `out` across a call that can by summarized by the flow
|
||||
* from `par` to `ret` within it, in the graph of data flow path explanations.
|
||||
*/
|
||||
query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) {
|
||||
DataFlow3::PathGraph::subpaths(arg.(WrapPathNode).inner(), par.(WrapPathNode).inner(),
|
||||
ret.(WrapPathNode).inner(), out.(WrapPathNode).inner())
|
||||
or
|
||||
// To avoid showing trivial-looking steps, we _replace_ the last node instead
|
||||
// of adding an edge out of it.
|
||||
exists(WrapPathNode sinkNode |
|
||||
DataFlow3::PathGraph::subpaths(arg.(WrapPathNode).inner(), par.(WrapPathNode).inner(),
|
||||
ret.(WrapPathNode).inner(), sinkNode.inner()) and
|
||||
out.(FinalPathNode).inner() = adjustedSink(sinkNode.inner().getNode())
|
||||
)
|
||||
or
|
||||
// Same for the first node
|
||||
exists(WrapPathNode sourceNode |
|
||||
DataFlow3::PathGraph::subpaths(sourceNode.inner(), par.(WrapPathNode).inner(),
|
||||
ret.(WrapPathNode).inner(), out.(WrapPathNode).inner()) and
|
||||
sourceNode.inner().getNode() = getNodeForExpr(arg.(InitialPathNode).inner())
|
||||
)
|
||||
or
|
||||
// Finally, handle the case where the path goes directly from a source to a
|
||||
// sink, meaning that they both need to be translated.
|
||||
exists(WrapPathNode sinkNode, WrapPathNode sourceNode |
|
||||
DataFlow3::PathGraph::subpaths(sourceNode.inner(), par.(WrapPathNode).inner(),
|
||||
ret.(WrapPathNode).inner(), sinkNode.inner()) and
|
||||
sourceNode.inner().getNode() = getNodeForExpr(arg.(InitialPathNode).inner()) and
|
||||
out.(FinalPathNode).inner() = adjustedSink(sinkNode.inner().getNode())
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `n` is a node in the graph of data flow path explanations. */
|
||||
query predicate nodes(PathNode n, string key, string val) {
|
||||
key = "semmle.label" and val = n.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `tainted` may contain taint from `source`, where `sourceNode` and
|
||||
* `sinkNode` are the corresponding `PathNode`s that can be used in a query
|
||||
* to provide path explanations. Extend `TaintTrackingConfiguration` to use
|
||||
* this predicate.
|
||||
*
|
||||
* A tainted expression is either directly user input, or is computed from
|
||||
* user input in a way that users can probably control the exact output of
|
||||
* the computation.
|
||||
*/
|
||||
predicate taintedWithPath(Expr source, Element tainted, PathNode sourceNode, PathNode sinkNode) {
|
||||
exists(AdjustedConfiguration cfg, DataFlow3::Node flowSource, DataFlow3::Node flowSink |
|
||||
source = sourceNode.(InitialPathNode).inner() and
|
||||
flowSource = getNodeForExpr(source) and
|
||||
cfg.hasFlow(flowSource, flowSink) and
|
||||
tainted = adjustedSink(flowSink) and
|
||||
tainted = sinkNode.(FinalPathNode).inner()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate isGlobalVariablePathNode(WrapPathNode n) {
|
||||
n.inner().getNode().asVariable() instanceof GlobalOrNamespaceVariable
|
||||
}
|
||||
|
||||
private predicate edgesWithoutGlobals(PathNode a, PathNode b) {
|
||||
edges(a, b) and
|
||||
not isGlobalVariablePathNode(a) and
|
||||
not isGlobalVariablePathNode(b)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `tainted` can be reached from a taint source without passing
|
||||
* through a global variable.
|
||||
*/
|
||||
predicate taintedWithoutGlobals(Element tainted) {
|
||||
exists(AdjustedConfiguration cfg, PathNode sourceNode, FinalPathNode sinkNode |
|
||||
cfg.isSource(sourceNode.(WrapPathNode).inner().getNode()) and
|
||||
edgesWithoutGlobals+(sourceNode, sinkNode) and
|
||||
tainted = sinkNode.inner()
|
||||
)
|
||||
}
|
||||
}
|
||||
deprecated module TaintedWithPath = DefaultTaintTrackingImpl::TaintedWithPath;
|
||||
|
||||
@@ -70,7 +70,7 @@ abstract class Configuration extends string {
|
||||
/**
|
||||
* Holds if `sink` is a relevant data flow sink accepting `state`.
|
||||
*/
|
||||
predicate isSink(Node source, FlowState state) { none() }
|
||||
predicate isSink(Node sink, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited. This completely removes
|
||||
@@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
|
||||
}
|
||||
|
||||
ParameterPosition getPosition() { this.isParameterOf(_, result) }
|
||||
|
||||
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
|
||||
}
|
||||
|
||||
private class RetNodeEx extends NodeEx {
|
||||
@@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if flow from `p` to a return node of kind `kind` is allowed.
|
||||
*
|
||||
* We don't expect a parameter to return stored in itself, unless
|
||||
* explicitly allowed
|
||||
*/
|
||||
bindingset[p, kind]
|
||||
private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) {
|
||||
exists(ParameterPosition pos | p.isParameterOf(_, pos) |
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
allowParameterReturnInSelfCached(p.asNode())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if flow from a parameter at position `pos` inside `c` to a return node of
|
||||
* kind `kind` is allowed.
|
||||
*
|
||||
* We don't expect a parameter to return stored in itself, unless
|
||||
* explicitly allowed
|
||||
*/
|
||||
bindingset[c, pos, kind]
|
||||
private predicate parameterFlowThroughAllowed(
|
||||
DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
p.isParameterOf(c, pos) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
private module Stage1 implements StageSig {
|
||||
class Ap = Unit;
|
||||
|
||||
@@ -981,21 +1011,22 @@ private module Stage1 implements StageSig {
|
||||
* candidate for the origin of a summary.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||
exists(ReturnKindExt kind |
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
|
||||
exists(DataFlowCallable c, ReturnKindExt kind |
|
||||
throughFlowNodeCand(p, config) and
|
||||
returnFlowCallableNodeCand(c, kind, config) and
|
||||
p.getEnclosingCallable() = c and
|
||||
exists(ap) and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
|
||||
throughFlowNodeCand(ret, config) and
|
||||
kind = ret.getKind()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
|
||||
exists(ArgNodeEx arg, boolean toReturn |
|
||||
@@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1(
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand1(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config
|
||||
) {
|
||||
viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
|
||||
Stage1::revFlow(ret, config) and
|
||||
not outBarrier(ret, config) and
|
||||
not inBarrier(out, config)
|
||||
exists(ReturnPosition pos |
|
||||
viableReturnPosOutNodeCand1(call, pos, out, config) and
|
||||
pos = ret.getReturnPosition() and
|
||||
kind = pos.getKind() and
|
||||
Stage1::revFlow(ret, config) and
|
||||
not outBarrier(ret, config) and
|
||||
not inBarrier(out, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1(
|
||||
* edge in the graph of paths between sources and sinks that ignores call
|
||||
* contexts.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private int branch(NodeEx n1, Configuration conf) {
|
||||
result =
|
||||
strictcount(NodeEx n |
|
||||
flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
|
||||
flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) {
|
||||
* edge in the graph of paths between sources and sinks that ignores call
|
||||
* contexts.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private int join(NodeEx n2, Configuration conf) {
|
||||
result =
|
||||
strictcount(NodeEx n |
|
||||
flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
|
||||
flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand1(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
flowOutOfCallNodeCand1(call, ret, out, config) and
|
||||
flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and
|
||||
exists(int b, int j |
|
||||
b = branch(ret, config) and
|
||||
j = join(out, config) and
|
||||
b = branch(ret, pragma[only_bind_into](config)) and
|
||||
j = join(out, pragma[only_bind_into](config)) and
|
||||
if b.minimum(j) <= config.fieldFlowBranchLimit()
|
||||
then allowsFieldFlow = true
|
||||
else allowsFieldFlow = false
|
||||
@@ -1136,10 +1174,10 @@ pragma[nomagic]
|
||||
private predicate flowIntoCallNodeCand1(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowIntoCallNodeCand1(call, arg, p, config) and
|
||||
flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and
|
||||
exists(int b, int j |
|
||||
b = branch(arg, config) and
|
||||
j = join(p, config) and
|
||||
b = branch(arg, pragma[only_bind_into](config)) and
|
||||
j = join(p, pragma[only_bind_into](config)) and
|
||||
if b.minimum(j) <= config.fieldFlowBranchLimit()
|
||||
then allowsFieldFlow = true
|
||||
else allowsFieldFlow = false
|
||||
@@ -1156,7 +1194,9 @@ private signature module StageSig {
|
||||
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
|
||||
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
|
||||
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
|
||||
|
||||
predicate storeStepCand(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
|
||||
@@ -1222,7 +1262,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
);
|
||||
|
||||
predicate flowOutOfCall(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
);
|
||||
|
||||
predicate flowIntoCall(
|
||||
@@ -1247,14 +1288,14 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowThroughOutOfCall(
|
||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
|
||||
NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
|
||||
pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call)
|
||||
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call) and
|
||||
c = ret.getEnclosingCallable()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1262,29 +1303,32 @@ private module MkStage<StageSig PrevStage> {
|
||||
* configuration `config`.
|
||||
*
|
||||
* The call context `cc` records whether the node is reached through an
|
||||
* argument in a call, and if so, `argAp` records the access path of that
|
||||
* argument.
|
||||
* argument in a call, and if so, `summaryCtx` and `argAp` record the
|
||||
* corresponding parameter position and access path of that argument, respectively.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate fwdFlow(
|
||||
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow0(node, state, cc, argAp, ap, config) and
|
||||
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
|
||||
filter(node, state, ap, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlow0(
|
||||
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
sourceNode(node, state, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
argAp = apNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
ap = getApNil(node)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
|
||||
fwdFlow(mid, state0, cc, argAp, ap0, config) and
|
||||
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
|
||||
localCc = getLocalCc(mid, cc)
|
||||
|
|
||||
localStep(mid, state0, node, state, true, _, config, localCc) and
|
||||
@@ -1295,65 +1339,82 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
jumpStep(mid, node, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone()
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, ApNil nil |
|
||||
fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and
|
||||
additionalJumpStep(mid, node, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and
|
||||
additionalJumpStateStep(mid, state0, node, state, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(TypedContent tc, Ap ap0 |
|
||||
fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
|
||||
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
|
||||
ap = apCons(tc, ap0)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Ap ap0, Content c |
|
||||
fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
|
||||
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
|
||||
fwdFlowConsCand(ap0, c, ap, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(ApApprox apa |
|
||||
fwdFlowIn(_, node, state, _, cc, _, ap, config) and
|
||||
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
|
||||
apa = getApprox(ap) and
|
||||
if PrevStage::parameterMayFlowThrough(node, _, apa, config)
|
||||
then argAp = apSome(ap)
|
||||
else argAp = apNone()
|
||||
if PrevStage::parameterMayFlowThrough(node, apa, config)
|
||||
then (
|
||||
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
|
||||
argAp = apSome(ap)
|
||||
) else (
|
||||
summaryCtx = TParameterPositionNone() and argAp = apNone()
|
||||
)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
|
||||
exists(
|
||||
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
|
||||
DataFlowCallable inner
|
||||
|
|
||||
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
cc = getCallContextReturn(inner, call, innercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
or
|
||||
exists(DataFlowCall call, Ap argAp0 |
|
||||
fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, cc, argAp, argAp0, config)
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
|
||||
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowStore(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
|
||||
Configuration config
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
exists(DataFlowType contentType |
|
||||
fwdFlow(node1, state, cc, argAp, ap1, config) and
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
|
||||
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
|
||||
typecheckStore(ap1, contentType)
|
||||
)
|
||||
@@ -1366,7 +1427,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
|
||||
exists(TypedContent tc |
|
||||
fwdFlowStore(_, tail, tc, _, _, _, _, config) and
|
||||
fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and
|
||||
tc.getContent() = c and
|
||||
cons = apCons(tc, tail)
|
||||
)
|
||||
@@ -1374,21 +1435,21 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead(
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
|
||||
Configuration config
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
fwdFlow(node1, state, cc, argAp, ap, config) and
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
getHeadContent(ap) = c
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIn(
|
||||
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp,
|
||||
Ap ap, Configuration config
|
||||
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ArgNodeEx arg, boolean allowsFieldFlow |
|
||||
fwdFlow(arg, state, outercc, argAp, ap, config) and
|
||||
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
|
||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
@@ -1396,29 +1457,19 @@ private module MkStage<StageSig PrevStage> {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowOutNotFromArg(
|
||||
NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
|
||||
private predicate fwdFlowOutFromArg(
|
||||
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(
|
||||
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
|
||||
DataFlowCallable inner
|
||||
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
||||
|
|
||||
fwdFlow(ret, state, innercc, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
ccOut = getCallContextReturn(inner, call, innercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowOutFromArg(
|
||||
DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
|
||||
fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
|
||||
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
|
||||
config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1428,11 +1479,13 @@ private module MkStage<StageSig PrevStage> {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(
|
||||
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
ParameterPosition pos, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
|
||||
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
|
||||
exists(ParamNodeEx param |
|
||||
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
|
||||
pos = param.getPosition()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1440,27 +1493,40 @@ private module MkStage<StageSig PrevStage> {
|
||||
private predicate storeStepFwd(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
|
||||
) {
|
||||
fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
|
||||
fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and
|
||||
ap2 = apCons(tc, ap1) and
|
||||
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config)
|
||||
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config)
|
||||
}
|
||||
|
||||
private predicate readStepFwd(
|
||||
NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
|
||||
) {
|
||||
fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and
|
||||
fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and
|
||||
fwdFlowConsCand(ap1, c, ap2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
|
||||
exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
|
||||
fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
|
||||
private predicate returnFlowsThrough0(
|
||||
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
|
||||
ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(boolean allowsFieldFlow |
|
||||
fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
|
||||
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
|
||||
pragma[only_bind_into](config))
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnFlowsThrough(
|
||||
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c, ParameterPosition ppos |
|
||||
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
|
||||
p.isParameterOf(c, ppos) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1468,118 +1534,130 @@ private module MkStage<StageSig PrevStage> {
|
||||
private predicate flowThroughIntoCall(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
|
||||
PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
|
||||
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnNodeMayFlowThrough(
|
||||
RetNodeEx ret, FlowState state, Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
|
||||
exists(Ap argAp |
|
||||
flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
|
||||
returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` with access path `ap` is part of a path from a source to a
|
||||
* sink in the configuration `config`.
|
||||
*
|
||||
* The Boolean `toReturn` records whether the node must be returned from the
|
||||
* enclosing callable in order to reach a sink, and if so, `returnAp` records
|
||||
* the access path of the returned value.
|
||||
* The parameter `returnCtx` records whether (and how) the node must be returned
|
||||
* from the enclosing callable in order to reach a sink, and if so, `returnAp`
|
||||
* records the access path of the returned value.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate revFlow(
|
||||
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
revFlow0(node, state, toReturn, returnAp, ap, config) and
|
||||
fwdFlow(node, state, _, _, ap, config)
|
||||
revFlow0(node, state, returnCtx, returnAp, ap, config) and
|
||||
fwdFlow(node, state, _, _, _, ap, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlow0(
|
||||
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
fwdFlow(node, state, _, _, ap, config) and
|
||||
fwdFlow(node, state, _, _, _, ap, config) and
|
||||
sinkNode(node, state, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
(
|
||||
if hasSinkCallCtx(config)
|
||||
then returnCtx = TReturnCtxNoFlowThrough()
|
||||
else returnCtx = TReturnCtxNone()
|
||||
) and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0 |
|
||||
localStep(node, state, mid, state0, true, _, config, _) and
|
||||
revFlow(mid, state0, toReturn, returnAp, ap, config)
|
||||
revFlow(mid, state0, returnCtx, returnAp, ap, config)
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and
|
||||
revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
|
||||
revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
jumpStep(node, mid, config) and
|
||||
revFlow(mid, state, _, _, ap, config) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone()
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, ApNil nil |
|
||||
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
additionalJumpStep(node, mid, config) and
|
||||
revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
additionalJumpStateStep(node, state, mid, state0, config) and
|
||||
revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil,
|
||||
pragma[only_bind_into](config)) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Ap ap0, Content c |
|
||||
revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
|
||||
revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and
|
||||
revFlowConsCand(ap0, c, ap, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(NodeEx mid, Ap ap0 |
|
||||
revFlow(mid, state, toReturn, returnAp, ap0, config) and
|
||||
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
|
||||
readStepFwd(node, ap, _, mid, ap0, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
revFlowInNotToReturn(node, state, returnAp, ap, config) and
|
||||
toReturn = false
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
|
||||
flowIntoCall(_, node, p, allowsFieldFlow, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
returnCtx = TReturnCtxNone()
|
||||
)
|
||||
or
|
||||
exists(DataFlowCall call, Ap returnAp0 |
|
||||
revFlowInToReturn(call, node, state, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 |
|
||||
revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
revFlowOut(_, node, state, _, _, ap, config) and
|
||||
toReturn = true and
|
||||
if returnNodeMayFlowThrough(node, state, ap, config)
|
||||
then returnAp = apSome(ap)
|
||||
else returnAp = apNone()
|
||||
exists(ReturnKindExt kind |
|
||||
revFlowOut(_, node, kind, state, _, _, ap, config) and
|
||||
if returnFlowsThrough(node, kind, state, _, _, _, ap, config)
|
||||
then (
|
||||
returnCtx = TReturnCtxMaybeFlowThrough(kind) and
|
||||
returnAp = apSome(ap)
|
||||
) else (
|
||||
returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowStore(
|
||||
Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid,
|
||||
boolean toReturn, ApOption returnAp, Configuration config
|
||||
ReturnCtx returnCtx, ApOption returnAp, Configuration config
|
||||
) {
|
||||
revFlow(mid, state, toReturn, returnAp, ap0, config) and
|
||||
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
|
||||
storeStepFwd(node, ap, tc, mid, ap0, config) and
|
||||
tc.getContent() = c
|
||||
}
|
||||
@@ -1599,35 +1677,27 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowOut(
|
||||
DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx,
|
||||
ApOption returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(NodeEx out, boolean allowsFieldFlow |
|
||||
revFlow(out, state, toReturn, returnAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowInNotToReturn(
|
||||
ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, false, returnAp, ap, config) and
|
||||
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
|
||||
revFlow(out, state, returnCtx, returnAp, ap, config) and
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowInToReturn(
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, true, apSome(returnAp), ap, config) and
|
||||
revFlow(pragma[only_bind_into](p), state,
|
||||
TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
|
||||
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1638,11 +1708,12 @@ private module MkStage<StageSig PrevStage> {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate revFlowIsReturned(
|
||||
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(RetNodeEx ret, FlowState state, CcCall ccc |
|
||||
revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and
|
||||
fwdFlow(ret, state, ccc, apSome(_), ap, config) and
|
||||
revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and
|
||||
returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and
|
||||
matchesCall(ccc, call)
|
||||
)
|
||||
}
|
||||
@@ -1713,40 +1784,39 @@ private module MkStage<StageSig PrevStage> {
|
||||
validAp(ap, config)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate parameterFlow(
|
||||
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
|
||||
pragma[nomagic]
|
||||
private predicate parameterFlowsThroughRev(
|
||||
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
|
||||
) {
|
||||
revFlow(p, _, true, apSome(ap0), ap, config) and
|
||||
c = p.getEnclosingCallable()
|
||||
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
}
|
||||
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||
exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
|
||||
parameterFlow(p, ap, ap0, c, config) and
|
||||
c = ret.getEnclosingCallable() and
|
||||
revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_),
|
||||
pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and
|
||||
fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
pragma[nomagic]
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
|
||||
exists(RetNodeEx ret, ReturnKindExt kind |
|
||||
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
|
||||
parameterFlowsThroughRev(p, ap, kind, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
|
||||
exists(ParamNodeEx p, Ap ap |
|
||||
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
|
||||
parameterFlowsThroughRev(p, ap, kind, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
|
||||
exists(
|
||||
Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
|
||||
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
|
||||
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
||||
|
|
||||
revFlow(arg, state, toReturn, returnAp, ap, config) and
|
||||
revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
|
||||
revFlow(arg, state, returnCtx, returnAp, ap, config) and
|
||||
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1754,14 +1824,13 @@ private module MkStage<StageSig PrevStage> {
|
||||
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
|
||||
) {
|
||||
fwd = true and
|
||||
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and
|
||||
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and
|
||||
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
|
||||
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
|
||||
states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and
|
||||
states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and
|
||||
tuples =
|
||||
count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
|
||||
fwdFlow(n, state, cc, argAp, ap, config)
|
||||
)
|
||||
count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config))
|
||||
or
|
||||
fwd = false and
|
||||
nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
|
||||
@@ -1769,8 +1838,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
|
||||
states = count(FlowState state | revFlow(_, state, _, _, _, config)) and
|
||||
tuples =
|
||||
count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap |
|
||||
revFlow(n, state, b, retAp, ap, config)
|
||||
count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap |
|
||||
revFlow(n, state, returnCtx, retAp, ap, config)
|
||||
)
|
||||
}
|
||||
/* End: Stage logic. */
|
||||
@@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
|
||||
exists(lcc)
|
||||
}
|
||||
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
|
||||
|
||||
predicate flowIntoCall = flowIntoCallNodeCand1/5;
|
||||
|
||||
@@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand2(
|
||||
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
|
||||
flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and
|
||||
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
|
||||
Stage2::revFlowAlias(node1, pragma[only_bind_into](config))
|
||||
}
|
||||
@@ -2021,8 +2091,8 @@ private module LocalFlowBigStep {
|
||||
exists(NodeEx next | Stage2::revFlow(next, state, config) |
|
||||
jumpStep(node, next, config) or
|
||||
additionalJumpStep(node, next, config) or
|
||||
flowIntoCallNodeCand1(_, node, next, config) or
|
||||
flowOutOfCallNodeCand1(_, node, next, config) or
|
||||
flowIntoCallNodeCand2(_, node, next, _, config) or
|
||||
flowOutOfCallNodeCand2(_, node, _, next, _, config) or
|
||||
Stage2::storeStepCand(node, _, _, next, _, config) or
|
||||
Stage2::readStepCand(node, _, next, config)
|
||||
)
|
||||
@@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
|
||||
}
|
||||
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
|
||||
|
||||
predicate flowIntoCall = flowIntoCallNodeCand2/5;
|
||||
|
||||
@@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx(
|
||||
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
|
||||
) {
|
||||
exists(AccessPathFront apf |
|
||||
Stage3::revFlow(node, state, true, _, apf, config) and
|
||||
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
|
||||
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
|
||||
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
|
||||
config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
|
||||
pragma[nomagic]
|
||||
predicate flowOutOfCall(
|
||||
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
exists(FlowState state |
|
||||
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
|
||||
flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
|
||||
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
|
||||
pragma[only_bind_into](config))
|
||||
@@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeMayUseSummary0(
|
||||
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
|
||||
NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa,
|
||||
Configuration config
|
||||
) {
|
||||
exists(AccessPathApprox apa0 |
|
||||
Stage4::parameterMayFlowThrough(_, c, _, _) and
|
||||
Stage4::revFlow(n, state, true, _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
|
||||
n.getEnclosingCallable() = c
|
||||
c = n.getEnclosingCallable() and
|
||||
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
|
||||
TAccessPathApproxSome(apa), apa0, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2522,9 +2595,10 @@ pragma[nomagic]
|
||||
private predicate nodeMayUseSummary(
|
||||
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c |
|
||||
Stage4::parameterMayFlowThrough(_, c, apa, config) and
|
||||
nodeMayUseSummary0(n, c, state, apa, config)
|
||||
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
|
||||
Stage4::parameterMayFlowThrough(p, apa, config) and
|
||||
nodeMayUseSummary0(n, c, pos, state, apa, config) and
|
||||
p.isParameterOf(c, pos)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2532,7 +2606,7 @@ private newtype TSummaryCtx =
|
||||
TSummaryCtxNone() or
|
||||
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
|
||||
exists(Configuration config |
|
||||
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
|
||||
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
|
||||
Stage4::revFlow(p, state, _, config)
|
||||
)
|
||||
}
|
||||
@@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough(
|
||||
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
|
||||
AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos |
|
||||
exists(PathNodeMid mid, RetNodeEx ret |
|
||||
pathNode(mid, ret, state, cc, sc, ap, config, _) and
|
||||
kind = ret.getKind() and
|
||||
apa = ap.getApprox() and
|
||||
pos = sc.getParameterPos() and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
sc.getParamNode().allowParameterReturnInSelf()
|
||||
)
|
||||
parameterFlowThroughAllowed(sc.getParamNode(), kind)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ abstract class Configuration extends string {
|
||||
/**
|
||||
* Holds if `sink` is a relevant data flow sink accepting `state`.
|
||||
*/
|
||||
predicate isSink(Node source, FlowState state) { none() }
|
||||
predicate isSink(Node sink, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited. This completely removes
|
||||
@@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
|
||||
}
|
||||
|
||||
ParameterPosition getPosition() { this.isParameterOf(_, result) }
|
||||
|
||||
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
|
||||
}
|
||||
|
||||
private class RetNodeEx extends NodeEx {
|
||||
@@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if flow from `p` to a return node of kind `kind` is allowed.
|
||||
*
|
||||
* We don't expect a parameter to return stored in itself, unless
|
||||
* explicitly allowed
|
||||
*/
|
||||
bindingset[p, kind]
|
||||
private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) {
|
||||
exists(ParameterPosition pos | p.isParameterOf(_, pos) |
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
allowParameterReturnInSelfCached(p.asNode())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if flow from a parameter at position `pos` inside `c` to a return node of
|
||||
* kind `kind` is allowed.
|
||||
*
|
||||
* We don't expect a parameter to return stored in itself, unless
|
||||
* explicitly allowed
|
||||
*/
|
||||
bindingset[c, pos, kind]
|
||||
private predicate parameterFlowThroughAllowed(
|
||||
DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
p.isParameterOf(c, pos) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
private module Stage1 implements StageSig {
|
||||
class Ap = Unit;
|
||||
|
||||
@@ -981,21 +1011,22 @@ private module Stage1 implements StageSig {
|
||||
* candidate for the origin of a summary.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||
exists(ReturnKindExt kind |
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
|
||||
exists(DataFlowCallable c, ReturnKindExt kind |
|
||||
throughFlowNodeCand(p, config) and
|
||||
returnFlowCallableNodeCand(c, kind, config) and
|
||||
p.getEnclosingCallable() = c and
|
||||
exists(ap) and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
|
||||
throughFlowNodeCand(ret, config) and
|
||||
kind = ret.getKind()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
|
||||
exists(ArgNodeEx arg, boolean toReturn |
|
||||
@@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1(
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand1(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config
|
||||
) {
|
||||
viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
|
||||
Stage1::revFlow(ret, config) and
|
||||
not outBarrier(ret, config) and
|
||||
not inBarrier(out, config)
|
||||
exists(ReturnPosition pos |
|
||||
viableReturnPosOutNodeCand1(call, pos, out, config) and
|
||||
pos = ret.getReturnPosition() and
|
||||
kind = pos.getKind() and
|
||||
Stage1::revFlow(ret, config) and
|
||||
not outBarrier(ret, config) and
|
||||
not inBarrier(out, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1(
|
||||
* edge in the graph of paths between sources and sinks that ignores call
|
||||
* contexts.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private int branch(NodeEx n1, Configuration conf) {
|
||||
result =
|
||||
strictcount(NodeEx n |
|
||||
flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
|
||||
flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) {
|
||||
* edge in the graph of paths between sources and sinks that ignores call
|
||||
* contexts.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private int join(NodeEx n2, Configuration conf) {
|
||||
result =
|
||||
strictcount(NodeEx n |
|
||||
flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
|
||||
flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand1(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
flowOutOfCallNodeCand1(call, ret, out, config) and
|
||||
flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and
|
||||
exists(int b, int j |
|
||||
b = branch(ret, config) and
|
||||
j = join(out, config) and
|
||||
b = branch(ret, pragma[only_bind_into](config)) and
|
||||
j = join(out, pragma[only_bind_into](config)) and
|
||||
if b.minimum(j) <= config.fieldFlowBranchLimit()
|
||||
then allowsFieldFlow = true
|
||||
else allowsFieldFlow = false
|
||||
@@ -1136,10 +1174,10 @@ pragma[nomagic]
|
||||
private predicate flowIntoCallNodeCand1(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowIntoCallNodeCand1(call, arg, p, config) and
|
||||
flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and
|
||||
exists(int b, int j |
|
||||
b = branch(arg, config) and
|
||||
j = join(p, config) and
|
||||
b = branch(arg, pragma[only_bind_into](config)) and
|
||||
j = join(p, pragma[only_bind_into](config)) and
|
||||
if b.minimum(j) <= config.fieldFlowBranchLimit()
|
||||
then allowsFieldFlow = true
|
||||
else allowsFieldFlow = false
|
||||
@@ -1156,7 +1194,9 @@ private signature module StageSig {
|
||||
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
|
||||
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
|
||||
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
|
||||
|
||||
predicate storeStepCand(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
|
||||
@@ -1222,7 +1262,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
);
|
||||
|
||||
predicate flowOutOfCall(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
);
|
||||
|
||||
predicate flowIntoCall(
|
||||
@@ -1247,14 +1288,14 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowThroughOutOfCall(
|
||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
|
||||
NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
|
||||
pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call)
|
||||
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call) and
|
||||
c = ret.getEnclosingCallable()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1262,29 +1303,32 @@ private module MkStage<StageSig PrevStage> {
|
||||
* configuration `config`.
|
||||
*
|
||||
* The call context `cc` records whether the node is reached through an
|
||||
* argument in a call, and if so, `argAp` records the access path of that
|
||||
* argument.
|
||||
* argument in a call, and if so, `summaryCtx` and `argAp` record the
|
||||
* corresponding parameter position and access path of that argument, respectively.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate fwdFlow(
|
||||
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow0(node, state, cc, argAp, ap, config) and
|
||||
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
|
||||
filter(node, state, ap, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlow0(
|
||||
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
sourceNode(node, state, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
argAp = apNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
ap = getApNil(node)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
|
||||
fwdFlow(mid, state0, cc, argAp, ap0, config) and
|
||||
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
|
||||
localCc = getLocalCc(mid, cc)
|
||||
|
|
||||
localStep(mid, state0, node, state, true, _, config, localCc) and
|
||||
@@ -1295,65 +1339,82 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
jumpStep(mid, node, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone()
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, ApNil nil |
|
||||
fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and
|
||||
additionalJumpStep(mid, node, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and
|
||||
additionalJumpStateStep(mid, state0, node, state, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(TypedContent tc, Ap ap0 |
|
||||
fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
|
||||
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
|
||||
ap = apCons(tc, ap0)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Ap ap0, Content c |
|
||||
fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
|
||||
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
|
||||
fwdFlowConsCand(ap0, c, ap, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(ApApprox apa |
|
||||
fwdFlowIn(_, node, state, _, cc, _, ap, config) and
|
||||
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
|
||||
apa = getApprox(ap) and
|
||||
if PrevStage::parameterMayFlowThrough(node, _, apa, config)
|
||||
then argAp = apSome(ap)
|
||||
else argAp = apNone()
|
||||
if PrevStage::parameterMayFlowThrough(node, apa, config)
|
||||
then (
|
||||
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
|
||||
argAp = apSome(ap)
|
||||
) else (
|
||||
summaryCtx = TParameterPositionNone() and argAp = apNone()
|
||||
)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
|
||||
exists(
|
||||
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
|
||||
DataFlowCallable inner
|
||||
|
|
||||
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
cc = getCallContextReturn(inner, call, innercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
or
|
||||
exists(DataFlowCall call, Ap argAp0 |
|
||||
fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, cc, argAp, argAp0, config)
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
|
||||
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowStore(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
|
||||
Configuration config
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
exists(DataFlowType contentType |
|
||||
fwdFlow(node1, state, cc, argAp, ap1, config) and
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
|
||||
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
|
||||
typecheckStore(ap1, contentType)
|
||||
)
|
||||
@@ -1366,7 +1427,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
|
||||
exists(TypedContent tc |
|
||||
fwdFlowStore(_, tail, tc, _, _, _, _, config) and
|
||||
fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and
|
||||
tc.getContent() = c and
|
||||
cons = apCons(tc, tail)
|
||||
)
|
||||
@@ -1374,21 +1435,21 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead(
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
|
||||
Configuration config
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
fwdFlow(node1, state, cc, argAp, ap, config) and
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
getHeadContent(ap) = c
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIn(
|
||||
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp,
|
||||
Ap ap, Configuration config
|
||||
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ArgNodeEx arg, boolean allowsFieldFlow |
|
||||
fwdFlow(arg, state, outercc, argAp, ap, config) and
|
||||
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
|
||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
@@ -1396,29 +1457,19 @@ private module MkStage<StageSig PrevStage> {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowOutNotFromArg(
|
||||
NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
|
||||
private predicate fwdFlowOutFromArg(
|
||||
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(
|
||||
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
|
||||
DataFlowCallable inner
|
||||
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
||||
|
|
||||
fwdFlow(ret, state, innercc, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
ccOut = getCallContextReturn(inner, call, innercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowOutFromArg(
|
||||
DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
|
||||
fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
|
||||
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
|
||||
config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1428,11 +1479,13 @@ private module MkStage<StageSig PrevStage> {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(
|
||||
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
ParameterPosition pos, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
|
||||
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
|
||||
exists(ParamNodeEx param |
|
||||
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
|
||||
pos = param.getPosition()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1440,27 +1493,40 @@ private module MkStage<StageSig PrevStage> {
|
||||
private predicate storeStepFwd(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
|
||||
) {
|
||||
fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
|
||||
fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and
|
||||
ap2 = apCons(tc, ap1) and
|
||||
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config)
|
||||
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config)
|
||||
}
|
||||
|
||||
private predicate readStepFwd(
|
||||
NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
|
||||
) {
|
||||
fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and
|
||||
fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and
|
||||
fwdFlowConsCand(ap1, c, ap2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
|
||||
exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
|
||||
fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
|
||||
private predicate returnFlowsThrough0(
|
||||
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
|
||||
ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(boolean allowsFieldFlow |
|
||||
fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
|
||||
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
|
||||
pragma[only_bind_into](config))
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnFlowsThrough(
|
||||
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c, ParameterPosition ppos |
|
||||
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
|
||||
p.isParameterOf(c, ppos) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1468,118 +1534,130 @@ private module MkStage<StageSig PrevStage> {
|
||||
private predicate flowThroughIntoCall(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
|
||||
PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
|
||||
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnNodeMayFlowThrough(
|
||||
RetNodeEx ret, FlowState state, Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
|
||||
exists(Ap argAp |
|
||||
flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
|
||||
returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` with access path `ap` is part of a path from a source to a
|
||||
* sink in the configuration `config`.
|
||||
*
|
||||
* The Boolean `toReturn` records whether the node must be returned from the
|
||||
* enclosing callable in order to reach a sink, and if so, `returnAp` records
|
||||
* the access path of the returned value.
|
||||
* The parameter `returnCtx` records whether (and how) the node must be returned
|
||||
* from the enclosing callable in order to reach a sink, and if so, `returnAp`
|
||||
* records the access path of the returned value.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate revFlow(
|
||||
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
revFlow0(node, state, toReturn, returnAp, ap, config) and
|
||||
fwdFlow(node, state, _, _, ap, config)
|
||||
revFlow0(node, state, returnCtx, returnAp, ap, config) and
|
||||
fwdFlow(node, state, _, _, _, ap, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlow0(
|
||||
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
fwdFlow(node, state, _, _, ap, config) and
|
||||
fwdFlow(node, state, _, _, _, ap, config) and
|
||||
sinkNode(node, state, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
(
|
||||
if hasSinkCallCtx(config)
|
||||
then returnCtx = TReturnCtxNoFlowThrough()
|
||||
else returnCtx = TReturnCtxNone()
|
||||
) and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0 |
|
||||
localStep(node, state, mid, state0, true, _, config, _) and
|
||||
revFlow(mid, state0, toReturn, returnAp, ap, config)
|
||||
revFlow(mid, state0, returnCtx, returnAp, ap, config)
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and
|
||||
revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
|
||||
revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
jumpStep(node, mid, config) and
|
||||
revFlow(mid, state, _, _, ap, config) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone()
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, ApNil nil |
|
||||
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
additionalJumpStep(node, mid, config) and
|
||||
revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
additionalJumpStateStep(node, state, mid, state0, config) and
|
||||
revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil,
|
||||
pragma[only_bind_into](config)) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Ap ap0, Content c |
|
||||
revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
|
||||
revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and
|
||||
revFlowConsCand(ap0, c, ap, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(NodeEx mid, Ap ap0 |
|
||||
revFlow(mid, state, toReturn, returnAp, ap0, config) and
|
||||
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
|
||||
readStepFwd(node, ap, _, mid, ap0, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
revFlowInNotToReturn(node, state, returnAp, ap, config) and
|
||||
toReturn = false
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
|
||||
flowIntoCall(_, node, p, allowsFieldFlow, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
returnCtx = TReturnCtxNone()
|
||||
)
|
||||
or
|
||||
exists(DataFlowCall call, Ap returnAp0 |
|
||||
revFlowInToReturn(call, node, state, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 |
|
||||
revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
revFlowOut(_, node, state, _, _, ap, config) and
|
||||
toReturn = true and
|
||||
if returnNodeMayFlowThrough(node, state, ap, config)
|
||||
then returnAp = apSome(ap)
|
||||
else returnAp = apNone()
|
||||
exists(ReturnKindExt kind |
|
||||
revFlowOut(_, node, kind, state, _, _, ap, config) and
|
||||
if returnFlowsThrough(node, kind, state, _, _, _, ap, config)
|
||||
then (
|
||||
returnCtx = TReturnCtxMaybeFlowThrough(kind) and
|
||||
returnAp = apSome(ap)
|
||||
) else (
|
||||
returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowStore(
|
||||
Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid,
|
||||
boolean toReturn, ApOption returnAp, Configuration config
|
||||
ReturnCtx returnCtx, ApOption returnAp, Configuration config
|
||||
) {
|
||||
revFlow(mid, state, toReturn, returnAp, ap0, config) and
|
||||
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
|
||||
storeStepFwd(node, ap, tc, mid, ap0, config) and
|
||||
tc.getContent() = c
|
||||
}
|
||||
@@ -1599,35 +1677,27 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowOut(
|
||||
DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx,
|
||||
ApOption returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(NodeEx out, boolean allowsFieldFlow |
|
||||
revFlow(out, state, toReturn, returnAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowInNotToReturn(
|
||||
ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, false, returnAp, ap, config) and
|
||||
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
|
||||
revFlow(out, state, returnCtx, returnAp, ap, config) and
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowInToReturn(
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, true, apSome(returnAp), ap, config) and
|
||||
revFlow(pragma[only_bind_into](p), state,
|
||||
TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
|
||||
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1638,11 +1708,12 @@ private module MkStage<StageSig PrevStage> {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate revFlowIsReturned(
|
||||
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(RetNodeEx ret, FlowState state, CcCall ccc |
|
||||
revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and
|
||||
fwdFlow(ret, state, ccc, apSome(_), ap, config) and
|
||||
revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and
|
||||
returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and
|
||||
matchesCall(ccc, call)
|
||||
)
|
||||
}
|
||||
@@ -1713,40 +1784,39 @@ private module MkStage<StageSig PrevStage> {
|
||||
validAp(ap, config)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate parameterFlow(
|
||||
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
|
||||
pragma[nomagic]
|
||||
private predicate parameterFlowsThroughRev(
|
||||
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
|
||||
) {
|
||||
revFlow(p, _, true, apSome(ap0), ap, config) and
|
||||
c = p.getEnclosingCallable()
|
||||
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
}
|
||||
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||
exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
|
||||
parameterFlow(p, ap, ap0, c, config) and
|
||||
c = ret.getEnclosingCallable() and
|
||||
revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_),
|
||||
pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and
|
||||
fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
pragma[nomagic]
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
|
||||
exists(RetNodeEx ret, ReturnKindExt kind |
|
||||
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
|
||||
parameterFlowsThroughRev(p, ap, kind, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
|
||||
exists(ParamNodeEx p, Ap ap |
|
||||
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
|
||||
parameterFlowsThroughRev(p, ap, kind, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
|
||||
exists(
|
||||
Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
|
||||
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
|
||||
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
||||
|
|
||||
revFlow(arg, state, toReturn, returnAp, ap, config) and
|
||||
revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
|
||||
revFlow(arg, state, returnCtx, returnAp, ap, config) and
|
||||
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1754,14 +1824,13 @@ private module MkStage<StageSig PrevStage> {
|
||||
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
|
||||
) {
|
||||
fwd = true and
|
||||
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and
|
||||
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and
|
||||
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
|
||||
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
|
||||
states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and
|
||||
states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and
|
||||
tuples =
|
||||
count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
|
||||
fwdFlow(n, state, cc, argAp, ap, config)
|
||||
)
|
||||
count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config))
|
||||
or
|
||||
fwd = false and
|
||||
nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
|
||||
@@ -1769,8 +1838,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
|
||||
states = count(FlowState state | revFlow(_, state, _, _, _, config)) and
|
||||
tuples =
|
||||
count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap |
|
||||
revFlow(n, state, b, retAp, ap, config)
|
||||
count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap |
|
||||
revFlow(n, state, returnCtx, retAp, ap, config)
|
||||
)
|
||||
}
|
||||
/* End: Stage logic. */
|
||||
@@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
|
||||
exists(lcc)
|
||||
}
|
||||
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
|
||||
|
||||
predicate flowIntoCall = flowIntoCallNodeCand1/5;
|
||||
|
||||
@@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand2(
|
||||
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
|
||||
flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and
|
||||
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
|
||||
Stage2::revFlowAlias(node1, pragma[only_bind_into](config))
|
||||
}
|
||||
@@ -2021,8 +2091,8 @@ private module LocalFlowBigStep {
|
||||
exists(NodeEx next | Stage2::revFlow(next, state, config) |
|
||||
jumpStep(node, next, config) or
|
||||
additionalJumpStep(node, next, config) or
|
||||
flowIntoCallNodeCand1(_, node, next, config) or
|
||||
flowOutOfCallNodeCand1(_, node, next, config) or
|
||||
flowIntoCallNodeCand2(_, node, next, _, config) or
|
||||
flowOutOfCallNodeCand2(_, node, _, next, _, config) or
|
||||
Stage2::storeStepCand(node, _, _, next, _, config) or
|
||||
Stage2::readStepCand(node, _, next, config)
|
||||
)
|
||||
@@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
|
||||
}
|
||||
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
|
||||
|
||||
predicate flowIntoCall = flowIntoCallNodeCand2/5;
|
||||
|
||||
@@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx(
|
||||
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
|
||||
) {
|
||||
exists(AccessPathFront apf |
|
||||
Stage3::revFlow(node, state, true, _, apf, config) and
|
||||
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
|
||||
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
|
||||
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
|
||||
config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
|
||||
pragma[nomagic]
|
||||
predicate flowOutOfCall(
|
||||
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
exists(FlowState state |
|
||||
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
|
||||
flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
|
||||
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
|
||||
pragma[only_bind_into](config))
|
||||
@@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeMayUseSummary0(
|
||||
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
|
||||
NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa,
|
||||
Configuration config
|
||||
) {
|
||||
exists(AccessPathApprox apa0 |
|
||||
Stage4::parameterMayFlowThrough(_, c, _, _) and
|
||||
Stage4::revFlow(n, state, true, _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
|
||||
n.getEnclosingCallable() = c
|
||||
c = n.getEnclosingCallable() and
|
||||
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
|
||||
TAccessPathApproxSome(apa), apa0, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2522,9 +2595,10 @@ pragma[nomagic]
|
||||
private predicate nodeMayUseSummary(
|
||||
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c |
|
||||
Stage4::parameterMayFlowThrough(_, c, apa, config) and
|
||||
nodeMayUseSummary0(n, c, state, apa, config)
|
||||
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
|
||||
Stage4::parameterMayFlowThrough(p, apa, config) and
|
||||
nodeMayUseSummary0(n, c, pos, state, apa, config) and
|
||||
p.isParameterOf(c, pos)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2532,7 +2606,7 @@ private newtype TSummaryCtx =
|
||||
TSummaryCtxNone() or
|
||||
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
|
||||
exists(Configuration config |
|
||||
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
|
||||
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
|
||||
Stage4::revFlow(p, state, _, config)
|
||||
)
|
||||
}
|
||||
@@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough(
|
||||
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
|
||||
AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos |
|
||||
exists(PathNodeMid mid, RetNodeEx ret |
|
||||
pathNode(mid, ret, state, cc, sc, ap, config, _) and
|
||||
kind = ret.getKind() and
|
||||
apa = ap.getApprox() and
|
||||
pos = sc.getParameterPos() and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
sc.getParamNode().allowParameterReturnInSelf()
|
||||
)
|
||||
parameterFlowThroughAllowed(sc.getParamNode(), kind)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ abstract class Configuration extends string {
|
||||
/**
|
||||
* Holds if `sink` is a relevant data flow sink accepting `state`.
|
||||
*/
|
||||
predicate isSink(Node source, FlowState state) { none() }
|
||||
predicate isSink(Node sink, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited. This completely removes
|
||||
@@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
|
||||
}
|
||||
|
||||
ParameterPosition getPosition() { this.isParameterOf(_, result) }
|
||||
|
||||
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
|
||||
}
|
||||
|
||||
private class RetNodeEx extends NodeEx {
|
||||
@@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if flow from `p` to a return node of kind `kind` is allowed.
|
||||
*
|
||||
* We don't expect a parameter to return stored in itself, unless
|
||||
* explicitly allowed
|
||||
*/
|
||||
bindingset[p, kind]
|
||||
private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) {
|
||||
exists(ParameterPosition pos | p.isParameterOf(_, pos) |
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
allowParameterReturnInSelfCached(p.asNode())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if flow from a parameter at position `pos` inside `c` to a return node of
|
||||
* kind `kind` is allowed.
|
||||
*
|
||||
* We don't expect a parameter to return stored in itself, unless
|
||||
* explicitly allowed
|
||||
*/
|
||||
bindingset[c, pos, kind]
|
||||
private predicate parameterFlowThroughAllowed(
|
||||
DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
p.isParameterOf(c, pos) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
private module Stage1 implements StageSig {
|
||||
class Ap = Unit;
|
||||
|
||||
@@ -981,21 +1011,22 @@ private module Stage1 implements StageSig {
|
||||
* candidate for the origin of a summary.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||
exists(ReturnKindExt kind |
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
|
||||
exists(DataFlowCallable c, ReturnKindExt kind |
|
||||
throughFlowNodeCand(p, config) and
|
||||
returnFlowCallableNodeCand(c, kind, config) and
|
||||
p.getEnclosingCallable() = c and
|
||||
exists(ap) and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
|
||||
throughFlowNodeCand(ret, config) and
|
||||
kind = ret.getKind()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
|
||||
exists(ArgNodeEx arg, boolean toReturn |
|
||||
@@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1(
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand1(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config
|
||||
) {
|
||||
viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
|
||||
Stage1::revFlow(ret, config) and
|
||||
not outBarrier(ret, config) and
|
||||
not inBarrier(out, config)
|
||||
exists(ReturnPosition pos |
|
||||
viableReturnPosOutNodeCand1(call, pos, out, config) and
|
||||
pos = ret.getReturnPosition() and
|
||||
kind = pos.getKind() and
|
||||
Stage1::revFlow(ret, config) and
|
||||
not outBarrier(ret, config) and
|
||||
not inBarrier(out, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1(
|
||||
* edge in the graph of paths between sources and sinks that ignores call
|
||||
* contexts.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private int branch(NodeEx n1, Configuration conf) {
|
||||
result =
|
||||
strictcount(NodeEx n |
|
||||
flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
|
||||
flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) {
|
||||
* edge in the graph of paths between sources and sinks that ignores call
|
||||
* contexts.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private int join(NodeEx n2, Configuration conf) {
|
||||
result =
|
||||
strictcount(NodeEx n |
|
||||
flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
|
||||
flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand1(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
flowOutOfCallNodeCand1(call, ret, out, config) and
|
||||
flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and
|
||||
exists(int b, int j |
|
||||
b = branch(ret, config) and
|
||||
j = join(out, config) and
|
||||
b = branch(ret, pragma[only_bind_into](config)) and
|
||||
j = join(out, pragma[only_bind_into](config)) and
|
||||
if b.minimum(j) <= config.fieldFlowBranchLimit()
|
||||
then allowsFieldFlow = true
|
||||
else allowsFieldFlow = false
|
||||
@@ -1136,10 +1174,10 @@ pragma[nomagic]
|
||||
private predicate flowIntoCallNodeCand1(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowIntoCallNodeCand1(call, arg, p, config) and
|
||||
flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and
|
||||
exists(int b, int j |
|
||||
b = branch(arg, config) and
|
||||
j = join(p, config) and
|
||||
b = branch(arg, pragma[only_bind_into](config)) and
|
||||
j = join(p, pragma[only_bind_into](config)) and
|
||||
if b.minimum(j) <= config.fieldFlowBranchLimit()
|
||||
then allowsFieldFlow = true
|
||||
else allowsFieldFlow = false
|
||||
@@ -1156,7 +1194,9 @@ private signature module StageSig {
|
||||
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
|
||||
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
|
||||
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
|
||||
|
||||
predicate storeStepCand(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
|
||||
@@ -1222,7 +1262,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
);
|
||||
|
||||
predicate flowOutOfCall(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
);
|
||||
|
||||
predicate flowIntoCall(
|
||||
@@ -1247,14 +1288,14 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowThroughOutOfCall(
|
||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
|
||||
NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
|
||||
pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call)
|
||||
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call) and
|
||||
c = ret.getEnclosingCallable()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1262,29 +1303,32 @@ private module MkStage<StageSig PrevStage> {
|
||||
* configuration `config`.
|
||||
*
|
||||
* The call context `cc` records whether the node is reached through an
|
||||
* argument in a call, and if so, `argAp` records the access path of that
|
||||
* argument.
|
||||
* argument in a call, and if so, `summaryCtx` and `argAp` record the
|
||||
* corresponding parameter position and access path of that argument, respectively.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate fwdFlow(
|
||||
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow0(node, state, cc, argAp, ap, config) and
|
||||
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
|
||||
filter(node, state, ap, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlow0(
|
||||
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
sourceNode(node, state, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
argAp = apNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
ap = getApNil(node)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
|
||||
fwdFlow(mid, state0, cc, argAp, ap0, config) and
|
||||
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
|
||||
localCc = getLocalCc(mid, cc)
|
||||
|
|
||||
localStep(mid, state0, node, state, true, _, config, localCc) and
|
||||
@@ -1295,65 +1339,82 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
jumpStep(mid, node, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone()
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, ApNil nil |
|
||||
fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and
|
||||
additionalJumpStep(mid, node, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and
|
||||
additionalJumpStateStep(mid, state0, node, state, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(TypedContent tc, Ap ap0 |
|
||||
fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
|
||||
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
|
||||
ap = apCons(tc, ap0)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Ap ap0, Content c |
|
||||
fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
|
||||
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
|
||||
fwdFlowConsCand(ap0, c, ap, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(ApApprox apa |
|
||||
fwdFlowIn(_, node, state, _, cc, _, ap, config) and
|
||||
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
|
||||
apa = getApprox(ap) and
|
||||
if PrevStage::parameterMayFlowThrough(node, _, apa, config)
|
||||
then argAp = apSome(ap)
|
||||
else argAp = apNone()
|
||||
if PrevStage::parameterMayFlowThrough(node, apa, config)
|
||||
then (
|
||||
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
|
||||
argAp = apSome(ap)
|
||||
) else (
|
||||
summaryCtx = TParameterPositionNone() and argAp = apNone()
|
||||
)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
|
||||
exists(
|
||||
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
|
||||
DataFlowCallable inner
|
||||
|
|
||||
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
cc = getCallContextReturn(inner, call, innercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
or
|
||||
exists(DataFlowCall call, Ap argAp0 |
|
||||
fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, cc, argAp, argAp0, config)
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
|
||||
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowStore(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
|
||||
Configuration config
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
exists(DataFlowType contentType |
|
||||
fwdFlow(node1, state, cc, argAp, ap1, config) and
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
|
||||
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
|
||||
typecheckStore(ap1, contentType)
|
||||
)
|
||||
@@ -1366,7 +1427,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
|
||||
exists(TypedContent tc |
|
||||
fwdFlowStore(_, tail, tc, _, _, _, _, config) and
|
||||
fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and
|
||||
tc.getContent() = c and
|
||||
cons = apCons(tc, tail)
|
||||
)
|
||||
@@ -1374,21 +1435,21 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead(
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
|
||||
Configuration config
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
fwdFlow(node1, state, cc, argAp, ap, config) and
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
getHeadContent(ap) = c
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIn(
|
||||
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp,
|
||||
Ap ap, Configuration config
|
||||
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ArgNodeEx arg, boolean allowsFieldFlow |
|
||||
fwdFlow(arg, state, outercc, argAp, ap, config) and
|
||||
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
|
||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
@@ -1396,29 +1457,19 @@ private module MkStage<StageSig PrevStage> {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowOutNotFromArg(
|
||||
NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
|
||||
private predicate fwdFlowOutFromArg(
|
||||
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(
|
||||
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
|
||||
DataFlowCallable inner
|
||||
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
||||
|
|
||||
fwdFlow(ret, state, innercc, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
ccOut = getCallContextReturn(inner, call, innercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowOutFromArg(
|
||||
DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
|
||||
fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
|
||||
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
|
||||
config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1428,11 +1479,13 @@ private module MkStage<StageSig PrevStage> {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(
|
||||
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
ParameterPosition pos, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
|
||||
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
|
||||
exists(ParamNodeEx param |
|
||||
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
|
||||
pos = param.getPosition()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1440,27 +1493,40 @@ private module MkStage<StageSig PrevStage> {
|
||||
private predicate storeStepFwd(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
|
||||
) {
|
||||
fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
|
||||
fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and
|
||||
ap2 = apCons(tc, ap1) and
|
||||
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config)
|
||||
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config)
|
||||
}
|
||||
|
||||
private predicate readStepFwd(
|
||||
NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
|
||||
) {
|
||||
fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and
|
||||
fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and
|
||||
fwdFlowConsCand(ap1, c, ap2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
|
||||
exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
|
||||
fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
|
||||
private predicate returnFlowsThrough0(
|
||||
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
|
||||
ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(boolean allowsFieldFlow |
|
||||
fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
|
||||
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
|
||||
pragma[only_bind_into](config))
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnFlowsThrough(
|
||||
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c, ParameterPosition ppos |
|
||||
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
|
||||
p.isParameterOf(c, ppos) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1468,118 +1534,130 @@ private module MkStage<StageSig PrevStage> {
|
||||
private predicate flowThroughIntoCall(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
|
||||
PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
|
||||
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnNodeMayFlowThrough(
|
||||
RetNodeEx ret, FlowState state, Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
|
||||
exists(Ap argAp |
|
||||
flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
|
||||
returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` with access path `ap` is part of a path from a source to a
|
||||
* sink in the configuration `config`.
|
||||
*
|
||||
* The Boolean `toReturn` records whether the node must be returned from the
|
||||
* enclosing callable in order to reach a sink, and if so, `returnAp` records
|
||||
* the access path of the returned value.
|
||||
* The parameter `returnCtx` records whether (and how) the node must be returned
|
||||
* from the enclosing callable in order to reach a sink, and if so, `returnAp`
|
||||
* records the access path of the returned value.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate revFlow(
|
||||
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
revFlow0(node, state, toReturn, returnAp, ap, config) and
|
||||
fwdFlow(node, state, _, _, ap, config)
|
||||
revFlow0(node, state, returnCtx, returnAp, ap, config) and
|
||||
fwdFlow(node, state, _, _, _, ap, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlow0(
|
||||
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
fwdFlow(node, state, _, _, ap, config) and
|
||||
fwdFlow(node, state, _, _, _, ap, config) and
|
||||
sinkNode(node, state, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
(
|
||||
if hasSinkCallCtx(config)
|
||||
then returnCtx = TReturnCtxNoFlowThrough()
|
||||
else returnCtx = TReturnCtxNone()
|
||||
) and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0 |
|
||||
localStep(node, state, mid, state0, true, _, config, _) and
|
||||
revFlow(mid, state0, toReturn, returnAp, ap, config)
|
||||
revFlow(mid, state0, returnCtx, returnAp, ap, config)
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and
|
||||
revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
|
||||
revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
jumpStep(node, mid, config) and
|
||||
revFlow(mid, state, _, _, ap, config) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone()
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, ApNil nil |
|
||||
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
additionalJumpStep(node, mid, config) and
|
||||
revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
additionalJumpStateStep(node, state, mid, state0, config) and
|
||||
revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil,
|
||||
pragma[only_bind_into](config)) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Ap ap0, Content c |
|
||||
revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
|
||||
revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and
|
||||
revFlowConsCand(ap0, c, ap, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(NodeEx mid, Ap ap0 |
|
||||
revFlow(mid, state, toReturn, returnAp, ap0, config) and
|
||||
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
|
||||
readStepFwd(node, ap, _, mid, ap0, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
revFlowInNotToReturn(node, state, returnAp, ap, config) and
|
||||
toReturn = false
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
|
||||
flowIntoCall(_, node, p, allowsFieldFlow, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
returnCtx = TReturnCtxNone()
|
||||
)
|
||||
or
|
||||
exists(DataFlowCall call, Ap returnAp0 |
|
||||
revFlowInToReturn(call, node, state, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 |
|
||||
revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
revFlowOut(_, node, state, _, _, ap, config) and
|
||||
toReturn = true and
|
||||
if returnNodeMayFlowThrough(node, state, ap, config)
|
||||
then returnAp = apSome(ap)
|
||||
else returnAp = apNone()
|
||||
exists(ReturnKindExt kind |
|
||||
revFlowOut(_, node, kind, state, _, _, ap, config) and
|
||||
if returnFlowsThrough(node, kind, state, _, _, _, ap, config)
|
||||
then (
|
||||
returnCtx = TReturnCtxMaybeFlowThrough(kind) and
|
||||
returnAp = apSome(ap)
|
||||
) else (
|
||||
returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowStore(
|
||||
Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid,
|
||||
boolean toReturn, ApOption returnAp, Configuration config
|
||||
ReturnCtx returnCtx, ApOption returnAp, Configuration config
|
||||
) {
|
||||
revFlow(mid, state, toReturn, returnAp, ap0, config) and
|
||||
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
|
||||
storeStepFwd(node, ap, tc, mid, ap0, config) and
|
||||
tc.getContent() = c
|
||||
}
|
||||
@@ -1599,35 +1677,27 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowOut(
|
||||
DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx,
|
||||
ApOption returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(NodeEx out, boolean allowsFieldFlow |
|
||||
revFlow(out, state, toReturn, returnAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowInNotToReturn(
|
||||
ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, false, returnAp, ap, config) and
|
||||
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
|
||||
revFlow(out, state, returnCtx, returnAp, ap, config) and
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowInToReturn(
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, true, apSome(returnAp), ap, config) and
|
||||
revFlow(pragma[only_bind_into](p), state,
|
||||
TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
|
||||
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1638,11 +1708,12 @@ private module MkStage<StageSig PrevStage> {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate revFlowIsReturned(
|
||||
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(RetNodeEx ret, FlowState state, CcCall ccc |
|
||||
revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and
|
||||
fwdFlow(ret, state, ccc, apSome(_), ap, config) and
|
||||
revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and
|
||||
returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and
|
||||
matchesCall(ccc, call)
|
||||
)
|
||||
}
|
||||
@@ -1713,40 +1784,39 @@ private module MkStage<StageSig PrevStage> {
|
||||
validAp(ap, config)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate parameterFlow(
|
||||
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
|
||||
pragma[nomagic]
|
||||
private predicate parameterFlowsThroughRev(
|
||||
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
|
||||
) {
|
||||
revFlow(p, _, true, apSome(ap0), ap, config) and
|
||||
c = p.getEnclosingCallable()
|
||||
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
}
|
||||
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||
exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
|
||||
parameterFlow(p, ap, ap0, c, config) and
|
||||
c = ret.getEnclosingCallable() and
|
||||
revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_),
|
||||
pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and
|
||||
fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
pragma[nomagic]
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
|
||||
exists(RetNodeEx ret, ReturnKindExt kind |
|
||||
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
|
||||
parameterFlowsThroughRev(p, ap, kind, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
|
||||
exists(ParamNodeEx p, Ap ap |
|
||||
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
|
||||
parameterFlowsThroughRev(p, ap, kind, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
|
||||
exists(
|
||||
Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
|
||||
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
|
||||
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
||||
|
|
||||
revFlow(arg, state, toReturn, returnAp, ap, config) and
|
||||
revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
|
||||
revFlow(arg, state, returnCtx, returnAp, ap, config) and
|
||||
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1754,14 +1824,13 @@ private module MkStage<StageSig PrevStage> {
|
||||
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
|
||||
) {
|
||||
fwd = true and
|
||||
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and
|
||||
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and
|
||||
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
|
||||
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
|
||||
states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and
|
||||
states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and
|
||||
tuples =
|
||||
count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
|
||||
fwdFlow(n, state, cc, argAp, ap, config)
|
||||
)
|
||||
count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config))
|
||||
or
|
||||
fwd = false and
|
||||
nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
|
||||
@@ -1769,8 +1838,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
|
||||
states = count(FlowState state | revFlow(_, state, _, _, _, config)) and
|
||||
tuples =
|
||||
count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap |
|
||||
revFlow(n, state, b, retAp, ap, config)
|
||||
count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap |
|
||||
revFlow(n, state, returnCtx, retAp, ap, config)
|
||||
)
|
||||
}
|
||||
/* End: Stage logic. */
|
||||
@@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
|
||||
exists(lcc)
|
||||
}
|
||||
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
|
||||
|
||||
predicate flowIntoCall = flowIntoCallNodeCand1/5;
|
||||
|
||||
@@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand2(
|
||||
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
|
||||
flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and
|
||||
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
|
||||
Stage2::revFlowAlias(node1, pragma[only_bind_into](config))
|
||||
}
|
||||
@@ -2021,8 +2091,8 @@ private module LocalFlowBigStep {
|
||||
exists(NodeEx next | Stage2::revFlow(next, state, config) |
|
||||
jumpStep(node, next, config) or
|
||||
additionalJumpStep(node, next, config) or
|
||||
flowIntoCallNodeCand1(_, node, next, config) or
|
||||
flowOutOfCallNodeCand1(_, node, next, config) or
|
||||
flowIntoCallNodeCand2(_, node, next, _, config) or
|
||||
flowOutOfCallNodeCand2(_, node, _, next, _, config) or
|
||||
Stage2::storeStepCand(node, _, _, next, _, config) or
|
||||
Stage2::readStepCand(node, _, next, config)
|
||||
)
|
||||
@@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
|
||||
}
|
||||
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
|
||||
|
||||
predicate flowIntoCall = flowIntoCallNodeCand2/5;
|
||||
|
||||
@@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx(
|
||||
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
|
||||
) {
|
||||
exists(AccessPathFront apf |
|
||||
Stage3::revFlow(node, state, true, _, apf, config) and
|
||||
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
|
||||
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
|
||||
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
|
||||
config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
|
||||
pragma[nomagic]
|
||||
predicate flowOutOfCall(
|
||||
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
exists(FlowState state |
|
||||
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
|
||||
flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
|
||||
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
|
||||
pragma[only_bind_into](config))
|
||||
@@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeMayUseSummary0(
|
||||
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
|
||||
NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa,
|
||||
Configuration config
|
||||
) {
|
||||
exists(AccessPathApprox apa0 |
|
||||
Stage4::parameterMayFlowThrough(_, c, _, _) and
|
||||
Stage4::revFlow(n, state, true, _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
|
||||
n.getEnclosingCallable() = c
|
||||
c = n.getEnclosingCallable() and
|
||||
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
|
||||
TAccessPathApproxSome(apa), apa0, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2522,9 +2595,10 @@ pragma[nomagic]
|
||||
private predicate nodeMayUseSummary(
|
||||
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c |
|
||||
Stage4::parameterMayFlowThrough(_, c, apa, config) and
|
||||
nodeMayUseSummary0(n, c, state, apa, config)
|
||||
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
|
||||
Stage4::parameterMayFlowThrough(p, apa, config) and
|
||||
nodeMayUseSummary0(n, c, pos, state, apa, config) and
|
||||
p.isParameterOf(c, pos)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2532,7 +2606,7 @@ private newtype TSummaryCtx =
|
||||
TSummaryCtxNone() or
|
||||
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
|
||||
exists(Configuration config |
|
||||
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
|
||||
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
|
||||
Stage4::revFlow(p, state, _, config)
|
||||
)
|
||||
}
|
||||
@@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough(
|
||||
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
|
||||
AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos |
|
||||
exists(PathNodeMid mid, RetNodeEx ret |
|
||||
pathNode(mid, ret, state, cc, sc, ap, config, _) and
|
||||
kind = ret.getKind() and
|
||||
apa = ap.getApprox() and
|
||||
pos = sc.getParameterPos() and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
sc.getParamNode().allowParameterReturnInSelf()
|
||||
)
|
||||
parameterFlowThroughAllowed(sc.getParamNode(), kind)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ abstract class Configuration extends string {
|
||||
/**
|
||||
* Holds if `sink` is a relevant data flow sink accepting `state`.
|
||||
*/
|
||||
predicate isSink(Node source, FlowState state) { none() }
|
||||
predicate isSink(Node sink, FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited. This completely removes
|
||||
@@ -319,8 +319,6 @@ private class ParamNodeEx extends NodeEx {
|
||||
}
|
||||
|
||||
ParameterPosition getPosition() { this.isParameterOf(_, result) }
|
||||
|
||||
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
|
||||
}
|
||||
|
||||
private class RetNodeEx extends NodeEx {
|
||||
@@ -608,6 +606,38 @@ private predicate hasSinkCallCtx(Configuration config) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if flow from `p` to a return node of kind `kind` is allowed.
|
||||
*
|
||||
* We don't expect a parameter to return stored in itself, unless
|
||||
* explicitly allowed
|
||||
*/
|
||||
bindingset[p, kind]
|
||||
private predicate parameterFlowThroughAllowed(ParamNodeEx p, ReturnKindExt kind) {
|
||||
exists(ParameterPosition pos | p.isParameterOf(_, pos) |
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
allowParameterReturnInSelfCached(p.asNode())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if flow from a parameter at position `pos` inside `c` to a return node of
|
||||
* kind `kind` is allowed.
|
||||
*
|
||||
* We don't expect a parameter to return stored in itself, unless
|
||||
* explicitly allowed
|
||||
*/
|
||||
bindingset[c, pos, kind]
|
||||
private predicate parameterFlowThroughAllowed(
|
||||
DataFlowCallable c, ParameterPosition pos, ReturnKindExt kind
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
p.isParameterOf(c, pos) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
private module Stage1 implements StageSig {
|
||||
class Ap = Unit;
|
||||
|
||||
@@ -981,21 +1011,22 @@ private module Stage1 implements StageSig {
|
||||
* candidate for the origin of a summary.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||
exists(ReturnKindExt kind |
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
|
||||
exists(DataFlowCallable c, ReturnKindExt kind |
|
||||
throughFlowNodeCand(p, config) and
|
||||
returnFlowCallableNodeCand(c, kind, config) and
|
||||
p.getEnclosingCallable() = c and
|
||||
exists(ap) and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
|
||||
throughFlowNodeCand(ret, config) and
|
||||
kind = ret.getKind()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
|
||||
exists(ArgNodeEx arg, boolean toReturn |
|
||||
@@ -1052,12 +1083,16 @@ private predicate viableReturnPosOutNodeCand1(
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand1(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, Configuration config
|
||||
) {
|
||||
viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and
|
||||
Stage1::revFlow(ret, config) and
|
||||
not outBarrier(ret, config) and
|
||||
not inBarrier(out, config)
|
||||
exists(ReturnPosition pos |
|
||||
viableReturnPosOutNodeCand1(call, pos, out, config) and
|
||||
pos = ret.getReturnPosition() and
|
||||
kind = pos.getKind() and
|
||||
Stage1::revFlow(ret, config) and
|
||||
not outBarrier(ret, config) and
|
||||
not inBarrier(out, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1087,10 +1122,11 @@ private predicate flowIntoCallNodeCand1(
|
||||
* edge in the graph of paths between sources and sinks that ignores call
|
||||
* contexts.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private int branch(NodeEx n1, Configuration conf) {
|
||||
result =
|
||||
strictcount(NodeEx n |
|
||||
flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
|
||||
flowOutOfCallNodeCand1(_, n1, _, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1099,10 +1135,11 @@ private int branch(NodeEx n1, Configuration conf) {
|
||||
* edge in the graph of paths between sources and sinks that ignores call
|
||||
* contexts.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private int join(NodeEx n2, Configuration conf) {
|
||||
result =
|
||||
strictcount(NodeEx n |
|
||||
flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
|
||||
flowOutOfCallNodeCand1(_, n, _, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1115,12 +1152,13 @@ private int join(NodeEx n2, Configuration conf) {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand1(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
flowOutOfCallNodeCand1(call, ret, out, config) and
|
||||
flowOutOfCallNodeCand1(call, ret, kind, out, pragma[only_bind_into](config)) and
|
||||
exists(int b, int j |
|
||||
b = branch(ret, config) and
|
||||
j = join(out, config) and
|
||||
b = branch(ret, pragma[only_bind_into](config)) and
|
||||
j = join(out, pragma[only_bind_into](config)) and
|
||||
if b.minimum(j) <= config.fieldFlowBranchLimit()
|
||||
then allowsFieldFlow = true
|
||||
else allowsFieldFlow = false
|
||||
@@ -1136,10 +1174,10 @@ pragma[nomagic]
|
||||
private predicate flowIntoCallNodeCand1(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowIntoCallNodeCand1(call, arg, p, config) and
|
||||
flowIntoCallNodeCand1(call, arg, p, pragma[only_bind_into](config)) and
|
||||
exists(int b, int j |
|
||||
b = branch(arg, config) and
|
||||
j = join(p, config) and
|
||||
b = branch(arg, pragma[only_bind_into](config)) and
|
||||
j = join(p, pragma[only_bind_into](config)) and
|
||||
if b.minimum(j) <= config.fieldFlowBranchLimit()
|
||||
then allowsFieldFlow = true
|
||||
else allowsFieldFlow = false
|
||||
@@ -1156,7 +1194,9 @@ private signature module StageSig {
|
||||
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
|
||||
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config);
|
||||
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config);
|
||||
|
||||
predicate storeStepCand(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
|
||||
@@ -1222,7 +1262,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
);
|
||||
|
||||
predicate flowOutOfCall(
|
||||
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
);
|
||||
|
||||
predicate flowIntoCall(
|
||||
@@ -1247,14 +1288,14 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowThroughOutOfCall(
|
||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
DataFlowCall call, DataFlowCallable c, CcCall ccc, RetNodeEx ret, ReturnKindExt kind,
|
||||
NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
|
||||
pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call)
|
||||
PrevStage::returnMayFlowThrough(ret, kind, pragma[only_bind_into](config)) and
|
||||
matchesCall(ccc, call) and
|
||||
c = ret.getEnclosingCallable()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1262,29 +1303,32 @@ private module MkStage<StageSig PrevStage> {
|
||||
* configuration `config`.
|
||||
*
|
||||
* The call context `cc` records whether the node is reached through an
|
||||
* argument in a call, and if so, `argAp` records the access path of that
|
||||
* argument.
|
||||
* argument in a call, and if so, `summaryCtx` and `argAp` record the
|
||||
* corresponding parameter position and access path of that argument, respectively.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate fwdFlow(
|
||||
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow0(node, state, cc, argAp, ap, config) and
|
||||
fwdFlow0(node, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
|
||||
filter(node, state, ap, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlow0(
|
||||
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
sourceNode(node, state, config) and
|
||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
||||
argAp = apNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
ap = getApNil(node)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
|
||||
fwdFlow(mid, state0, cc, argAp, ap0, config) and
|
||||
fwdFlow(mid, state0, cc, summaryCtx, argAp, ap0, config) and
|
||||
localCc = getLocalCc(mid, cc)
|
||||
|
|
||||
localStep(mid, state0, node, state, true, _, config, localCc) and
|
||||
@@ -1295,65 +1339,82 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
jumpStep(mid, node, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone()
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, ApNil nil |
|
||||
fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, state, _, _, _, nil, pragma[only_bind_into](config)) and
|
||||
additionalJumpStep(mid, node, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and
|
||||
fwdFlow(mid, state0, _, _, _, nil, pragma[only_bind_into](config)) and
|
||||
additionalJumpStateStep(mid, state0, node, state, config) and
|
||||
cc = ccNone() and
|
||||
summaryCtx = TParameterPositionNone() and
|
||||
argAp = apNone() and
|
||||
ap = getApNil(node)
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(TypedContent tc, Ap ap0 |
|
||||
fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
|
||||
fwdFlowStore(_, ap0, tc, node, state, cc, summaryCtx, argAp, config) and
|
||||
ap = apCons(tc, ap0)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(Ap ap0, Content c |
|
||||
fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
|
||||
fwdFlowRead(ap0, c, _, node, state, cc, summaryCtx, argAp, config) and
|
||||
fwdFlowConsCand(ap0, c, ap, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(ApApprox apa |
|
||||
fwdFlowIn(_, node, state, _, cc, _, ap, config) and
|
||||
fwdFlowIn(_, node, state, _, cc, _, _, ap, config) and
|
||||
apa = getApprox(ap) and
|
||||
if PrevStage::parameterMayFlowThrough(node, _, apa, config)
|
||||
then argAp = apSome(ap)
|
||||
else argAp = apNone()
|
||||
if PrevStage::parameterMayFlowThrough(node, apa, config)
|
||||
then (
|
||||
summaryCtx = TParameterPositionSome(node.(ParamNodeEx).getPosition()) and
|
||||
argAp = apSome(ap)
|
||||
) else (
|
||||
summaryCtx = TParameterPositionNone() and argAp = apNone()
|
||||
)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
|
||||
exists(
|
||||
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
|
||||
DataFlowCallable inner
|
||||
|
|
||||
fwdFlow(ret, state, innercc, summaryCtx, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, _, node, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
cc = getCallContextReturn(inner, call, innercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
or
|
||||
exists(DataFlowCall call, Ap argAp0 |
|
||||
fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, cc, argAp, argAp0, config)
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ParameterPosition summaryCtx0, Ap argAp0 |
|
||||
fwdFlowOutFromArg(call, node, state, summaryCtx0, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(call, cc, summaryCtx, argAp, summaryCtx0, argAp0, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowStore(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
|
||||
Configuration config
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
exists(DataFlowType contentType |
|
||||
fwdFlow(node1, state, cc, argAp, ap1, config) and
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap1, config) and
|
||||
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
|
||||
typecheckStore(ap1, contentType)
|
||||
)
|
||||
@@ -1366,7 +1427,7 @@ private module MkStage<StageSig PrevStage> {
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
|
||||
exists(TypedContent tc |
|
||||
fwdFlowStore(_, tail, tc, _, _, _, _, config) and
|
||||
fwdFlowStore(_, tail, tc, _, _, _, _, _, config) and
|
||||
tc.getContent() = c and
|
||||
cons = apCons(tc, tail)
|
||||
)
|
||||
@@ -1374,21 +1435,21 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead(
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
|
||||
Configuration config
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
fwdFlow(node1, state, cc, argAp, ap, config) and
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
getHeadContent(ap) = c
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIn(
|
||||
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp,
|
||||
Ap ap, Configuration config
|
||||
DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc,
|
||||
ParameterPositionOption summaryCtx, ApOption argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ArgNodeEx arg, boolean allowsFieldFlow |
|
||||
fwdFlow(arg, state, outercc, argAp, ap, config) and
|
||||
fwdFlow(arg, state, outercc, summaryCtx, argAp, ap, config) and
|
||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
@@ -1396,29 +1457,19 @@ private module MkStage<StageSig PrevStage> {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowOutNotFromArg(
|
||||
NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
|
||||
private predicate fwdFlowOutFromArg(
|
||||
DataFlowCall call, NodeEx out, FlowState state, ParameterPosition summaryCtx, Ap argAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(
|
||||
DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
|
||||
DataFlowCallable inner
|
||||
DataFlowCallable c, RetNodeEx ret, ReturnKindExt kind, boolean allowsFieldFlow, CcCall ccc
|
||||
|
|
||||
fwdFlow(ret, state, innercc, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
ccOut = getCallContextReturn(inner, call, innercc) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowOutFromArg(
|
||||
DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
|
||||
fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
fwdFlow(pragma[only_bind_into](ret), state, pragma[only_bind_into](ccc),
|
||||
TParameterPositionSome(pragma[only_bind_into](summaryCtx)), apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(call, pragma[only_bind_into](c), ccc, ret, kind, out, allowsFieldFlow,
|
||||
config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(c, pragma[only_bind_into](summaryCtx), kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1428,11 +1479,13 @@ private module MkStage<StageSig PrevStage> {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(
|
||||
DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
|
||||
DataFlowCall call, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
ParameterPosition pos, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p |
|
||||
fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
|
||||
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
|
||||
exists(ParamNodeEx param |
|
||||
fwdFlowIn(call, param, _, cc, _, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::parameterMayFlowThrough(param, unbindApa(getApprox(ap)), config) and
|
||||
pos = param.getPosition()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1440,27 +1493,40 @@ private module MkStage<StageSig PrevStage> {
|
||||
private predicate storeStepFwd(
|
||||
NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
|
||||
) {
|
||||
fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
|
||||
fwdFlowStore(node1, ap1, tc, node2, _, _, _, _, config) and
|
||||
ap2 = apCons(tc, ap1) and
|
||||
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config)
|
||||
fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, _, config)
|
||||
}
|
||||
|
||||
private predicate readStepFwd(
|
||||
NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config
|
||||
) {
|
||||
fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and
|
||||
fwdFlowRead(ap1, c, n1, n2, _, _, _, _, config) and
|
||||
fwdFlowConsCand(ap1, c, ap2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
|
||||
exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
|
||||
fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
|
||||
private predicate returnFlowsThrough0(
|
||||
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, DataFlowCallable c,
|
||||
ParameterPosition ppos, Ap argAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(boolean allowsFieldFlow |
|
||||
fwdFlow(ret, state, ccc, TParameterPositionSome(ppos), apSome(argAp), ap, config) and
|
||||
flowThroughOutOfCall(_, c, _, pragma[only_bind_into](ret), kind, _, allowsFieldFlow,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
|
||||
fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
|
||||
pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
|
||||
pragma[only_bind_into](config))
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnFlowsThrough(
|
||||
RetNodeEx ret, ReturnKindExt kind, FlowState state, CcCall ccc, ParamNodeEx p, Ap argAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c, ParameterPosition ppos |
|
||||
returnFlowsThrough0(ret, kind, state, ccc, c, ppos, argAp, ap, config) and
|
||||
p.isParameterOf(c, ppos) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1468,118 +1534,130 @@ private module MkStage<StageSig PrevStage> {
|
||||
private predicate flowThroughIntoCall(
|
||||
DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
|
||||
) {
|
||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
|
||||
PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
|
||||
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate returnNodeMayFlowThrough(
|
||||
RetNodeEx ret, FlowState state, Ap ap, Configuration config
|
||||
) {
|
||||
fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config)
|
||||
exists(Ap argAp |
|
||||
flowIntoCall(call, pragma[only_bind_into](arg), pragma[only_bind_into](p), allowsFieldFlow,
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlow(arg, _, _, _, _, pragma[only_bind_into](argAp), pragma[only_bind_into](config)) and
|
||||
returnFlowsThrough(_, _, _, _, p, pragma[only_bind_into](argAp), _,
|
||||
pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` with access path `ap` is part of a path from a source to a
|
||||
* sink in the configuration `config`.
|
||||
*
|
||||
* The Boolean `toReturn` records whether the node must be returned from the
|
||||
* enclosing callable in order to reach a sink, and if so, `returnAp` records
|
||||
* the access path of the returned value.
|
||||
* The parameter `returnCtx` records whether (and how) the node must be returned
|
||||
* from the enclosing callable in order to reach a sink, and if so, `returnAp`
|
||||
* records the access path of the returned value.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
additional predicate revFlow(
|
||||
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
revFlow0(node, state, toReturn, returnAp, ap, config) and
|
||||
fwdFlow(node, state, _, _, ap, config)
|
||||
revFlow0(node, state, returnCtx, returnAp, ap, config) and
|
||||
fwdFlow(node, state, _, _, _, ap, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlow0(
|
||||
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
NodeEx node, FlowState state, ReturnCtx returnCtx, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
fwdFlow(node, state, _, _, ap, config) and
|
||||
fwdFlow(node, state, _, _, _, ap, config) and
|
||||
sinkNode(node, state, config) and
|
||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
||||
(
|
||||
if hasSinkCallCtx(config)
|
||||
then returnCtx = TReturnCtxNoFlowThrough()
|
||||
else returnCtx = TReturnCtxNone()
|
||||
) and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0 |
|
||||
localStep(node, state, mid, state0, true, _, config, _) and
|
||||
revFlow(mid, state0, toReturn, returnAp, ap, config)
|
||||
revFlow(mid, state0, returnCtx, returnAp, ap, config)
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, pragma[only_bind_into](state), _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and
|
||||
revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and
|
||||
revFlow(mid, state0, returnCtx, returnAp, nil, pragma[only_bind_into](config)) and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid |
|
||||
jumpStep(node, mid, config) and
|
||||
revFlow(mid, state, _, _, ap, config) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone()
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, ApNil nil |
|
||||
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
additionalJumpStep(node, mid, config) and
|
||||
revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
exists(NodeEx mid, FlowState state0, ApNil nil |
|
||||
fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
fwdFlow(node, _, _, _, _, ap, pragma[only_bind_into](config)) and
|
||||
additionalJumpStateStep(node, state, mid, state0, config) and
|
||||
revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil,
|
||||
pragma[only_bind_into](config)) and
|
||||
toReturn = false and
|
||||
returnCtx = TReturnCtxNone() and
|
||||
returnAp = apNone() and
|
||||
ap instanceof ApNil
|
||||
)
|
||||
or
|
||||
// store
|
||||
exists(Ap ap0, Content c |
|
||||
revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
|
||||
revFlowStore(ap0, c, ap, node, state, _, _, returnCtx, returnAp, config) and
|
||||
revFlowConsCand(ap0, c, ap, config)
|
||||
)
|
||||
or
|
||||
// read
|
||||
exists(NodeEx mid, Ap ap0 |
|
||||
revFlow(mid, state, toReturn, returnAp, ap0, config) and
|
||||
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
|
||||
readStepFwd(node, ap, _, mid, ap0, config)
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
revFlowInNotToReturn(node, state, returnAp, ap, config) and
|
||||
toReturn = false
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, TReturnCtxNone(), returnAp, ap, config) and
|
||||
flowIntoCall(_, node, p, allowsFieldFlow, config) and
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
returnCtx = TReturnCtxNone()
|
||||
)
|
||||
or
|
||||
exists(DataFlowCall call, Ap returnAp0 |
|
||||
revFlowInToReturn(call, node, state, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ReturnKindExt returnKind0, Ap returnAp0 |
|
||||
revFlowInToReturn(call, node, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
)
|
||||
or
|
||||
// flow out of a callable
|
||||
revFlowOut(_, node, state, _, _, ap, config) and
|
||||
toReturn = true and
|
||||
if returnNodeMayFlowThrough(node, state, ap, config)
|
||||
then returnAp = apSome(ap)
|
||||
else returnAp = apNone()
|
||||
exists(ReturnKindExt kind |
|
||||
revFlowOut(_, node, kind, state, _, _, ap, config) and
|
||||
if returnFlowsThrough(node, kind, state, _, _, _, ap, config)
|
||||
then (
|
||||
returnCtx = TReturnCtxMaybeFlowThrough(kind) and
|
||||
returnAp = apSome(ap)
|
||||
) else (
|
||||
returnCtx = TReturnCtxNoFlowThrough() and returnAp = apNone()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowStore(
|
||||
Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid,
|
||||
boolean toReturn, ApOption returnAp, Configuration config
|
||||
ReturnCtx returnCtx, ApOption returnAp, Configuration config
|
||||
) {
|
||||
revFlow(mid, state, toReturn, returnAp, ap0, config) and
|
||||
revFlow(mid, state, returnCtx, returnAp, ap0, config) and
|
||||
storeStepFwd(node, ap, tc, mid, ap0, config) and
|
||||
tc.getContent() = c
|
||||
}
|
||||
@@ -1599,35 +1677,27 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowOut(
|
||||
DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap,
|
||||
Configuration config
|
||||
DataFlowCall call, RetNodeEx ret, ReturnKindExt kind, FlowState state, ReturnCtx returnCtx,
|
||||
ApOption returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(NodeEx out, boolean allowsFieldFlow |
|
||||
revFlow(out, state, toReturn, returnAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowInNotToReturn(
|
||||
ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, false, returnAp, ap, config) and
|
||||
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
|
||||
revFlow(out, state, returnCtx, returnAp, ap, config) and
|
||||
flowOutOfCall(call, ret, kind, out, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowInToReturn(
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnKindExt kind, Ap returnAp, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||
revFlow(p, state, true, apSome(returnAp), ap, config) and
|
||||
revFlow(pragma[only_bind_into](p), state,
|
||||
TReturnCtxMaybeFlowThrough(pragma[only_bind_into](kind)), apSome(returnAp), ap, config) and
|
||||
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
||||
(if allowsFieldFlow = false then ap instanceof ApNil else any()) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1638,11 +1708,12 @@ private module MkStage<StageSig PrevStage> {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate revFlowIsReturned(
|
||||
DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
|
||||
DataFlowCall call, ReturnCtx returnCtx, ApOption returnAp, ReturnKindExt kind, Ap ap,
|
||||
Configuration config
|
||||
) {
|
||||
exists(RetNodeEx ret, FlowState state, CcCall ccc |
|
||||
revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and
|
||||
fwdFlow(ret, state, ccc, apSome(_), ap, config) and
|
||||
revFlowOut(call, ret, kind, state, returnCtx, returnAp, ap, config) and
|
||||
returnFlowsThrough(ret, kind, state, ccc, _, _, ap, config) and
|
||||
matchesCall(ccc, call)
|
||||
)
|
||||
}
|
||||
@@ -1713,40 +1784,39 @@ private module MkStage<StageSig PrevStage> {
|
||||
validAp(ap, config)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate parameterFlow(
|
||||
ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
|
||||
pragma[nomagic]
|
||||
private predicate parameterFlowsThroughRev(
|
||||
ParamNodeEx p, Ap ap, ReturnKindExt kind, Configuration config
|
||||
) {
|
||||
revFlow(p, _, true, apSome(ap0), ap, config) and
|
||||
c = p.getEnclosingCallable()
|
||||
revFlow(p, _, TReturnCtxMaybeFlowThrough(kind), apSome(_), ap, config) and
|
||||
parameterFlowThroughAllowed(p, kind)
|
||||
}
|
||||
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||
exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
|
||||
parameterFlow(p, ap, ap0, c, config) and
|
||||
c = ret.getEnclosingCallable() and
|
||||
revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_),
|
||||
pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and
|
||||
fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
p.allowParameterReturnInSelf()
|
||||
)
|
||||
pragma[nomagic]
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
|
||||
exists(RetNodeEx ret, ReturnKindExt kind |
|
||||
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
|
||||
parameterFlowsThroughRev(p, ap, kind, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate returnMayFlowThrough(RetNodeEx ret, ReturnKindExt kind, Configuration config) {
|
||||
exists(ParamNodeEx p, Ap ap |
|
||||
returnFlowsThrough(ret, kind, _, _, p, ap, _, config) and
|
||||
parameterFlowsThroughRev(p, ap, kind, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) {
|
||||
exists(
|
||||
Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
|
||||
ReturnKindExt returnKind0, Ap returnAp0, ArgNodeEx arg, FlowState state,
|
||||
ReturnCtx returnCtx, ApOption returnAp, Ap ap
|
||||
|
|
||||
revFlow(arg, state, toReturn, returnAp, ap, config) and
|
||||
revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
|
||||
revFlow(arg, state, returnCtx, returnAp, ap, config) and
|
||||
revFlowInToReturn(call, arg, state, returnKind0, returnAp0, ap, config) and
|
||||
revFlowIsReturned(call, returnCtx, returnAp, returnKind0, returnAp0, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1754,14 +1824,13 @@ private module MkStage<StageSig PrevStage> {
|
||||
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
|
||||
) {
|
||||
fwd = true and
|
||||
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and
|
||||
nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, _, config)) and
|
||||
fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and
|
||||
conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and
|
||||
states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and
|
||||
states = count(FlowState state | fwdFlow(_, state, _, _, _, _, config)) and
|
||||
tuples =
|
||||
count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
|
||||
fwdFlow(n, state, cc, argAp, ap, config)
|
||||
)
|
||||
count(NodeEx n, FlowState state, Cc cc, ParameterPositionOption summaryCtx, ApOption argAp,
|
||||
Ap ap | fwdFlow(n, state, cc, summaryCtx, argAp, ap, config))
|
||||
or
|
||||
fwd = false and
|
||||
nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
|
||||
@@ -1769,8 +1838,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and
|
||||
states = count(FlowState state | revFlow(_, state, _, _, _, config)) and
|
||||
tuples =
|
||||
count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap |
|
||||
revFlow(n, state, b, retAp, ap, config)
|
||||
count(NodeEx n, FlowState state, ReturnCtx returnCtx, ApOption retAp, Ap ap |
|
||||
revFlow(n, state, returnCtx, retAp, ap, config)
|
||||
)
|
||||
}
|
||||
/* End: Stage logic. */
|
||||
@@ -1915,7 +1984,7 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
|
||||
exists(lcc)
|
||||
}
|
||||
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand1/6;
|
||||
|
||||
predicate flowIntoCall = flowIntoCallNodeCand1/5;
|
||||
|
||||
@@ -1951,9 +2020,10 @@ private module Stage2 implements StageSig {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate flowOutOfCallNodeCand2(
|
||||
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and
|
||||
flowOutOfCallNodeCand1(call, node1, kind, node2, allowsFieldFlow, config) and
|
||||
Stage2::revFlow(node2, pragma[only_bind_into](config)) and
|
||||
Stage2::revFlowAlias(node1, pragma[only_bind_into](config))
|
||||
}
|
||||
@@ -2021,8 +2091,8 @@ private module LocalFlowBigStep {
|
||||
exists(NodeEx next | Stage2::revFlow(next, state, config) |
|
||||
jumpStep(node, next, config) or
|
||||
additionalJumpStep(node, next, config) or
|
||||
flowIntoCallNodeCand1(_, node, next, config) or
|
||||
flowOutOfCallNodeCand1(_, node, next, config) or
|
||||
flowIntoCallNodeCand2(_, node, next, _, config) or
|
||||
flowOutOfCallNodeCand2(_, node, _, next, _, config) or
|
||||
Stage2::storeStepCand(node, _, _, next, _, config) or
|
||||
Stage2::readStepCand(node, _, next, config)
|
||||
)
|
||||
@@ -2163,7 +2233,7 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
|
||||
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
|
||||
}
|
||||
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
|
||||
predicate flowOutOfCall = flowOutOfCallNodeCand2/6;
|
||||
|
||||
predicate flowIntoCall = flowIntoCallNodeCand2/5;
|
||||
|
||||
@@ -2233,8 +2303,9 @@ private predicate flowCandSummaryCtx(
|
||||
NodeEx node, FlowState state, AccessPathFront argApf, Configuration config
|
||||
) {
|
||||
exists(AccessPathFront apf |
|
||||
Stage3::revFlow(node, state, true, _, apf, config) and
|
||||
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config)
|
||||
Stage3::revFlow(node, state, TReturnCtxMaybeFlowThrough(_), _, apf, config) and
|
||||
Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), _, TAccessPathFrontSome(argApf), apf,
|
||||
config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2468,10 +2539,11 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
|
||||
pragma[nomagic]
|
||||
predicate flowOutOfCall(
|
||||
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
|
||||
DataFlowCall call, RetNodeEx node1, ReturnKindExt kind, NodeEx node2, boolean allowsFieldFlow,
|
||||
Configuration config
|
||||
) {
|
||||
exists(FlowState state |
|
||||
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
|
||||
flowOutOfCallNodeCand2(call, node1, kind, node2, allowsFieldFlow, config) and
|
||||
PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
|
||||
PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
|
||||
pragma[only_bind_into](config))
|
||||
@@ -2508,13 +2580,14 @@ private Configuration unbindConf(Configuration conf) {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate nodeMayUseSummary0(
|
||||
NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config
|
||||
NodeEx n, DataFlowCallable c, ParameterPosition pos, FlowState state, AccessPathApprox apa,
|
||||
Configuration config
|
||||
) {
|
||||
exists(AccessPathApprox apa0 |
|
||||
Stage4::parameterMayFlowThrough(_, c, _, _) and
|
||||
Stage4::revFlow(n, state, true, _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and
|
||||
n.getEnclosingCallable() = c
|
||||
c = n.getEnclosingCallable() and
|
||||
Stage4::revFlow(n, state, TReturnCtxMaybeFlowThrough(_), _, apa0, config) and
|
||||
Stage4::fwdFlow(n, state, any(CallContextCall ccc), TParameterPositionSome(pos),
|
||||
TAccessPathApproxSome(apa), apa0, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2522,9 +2595,10 @@ pragma[nomagic]
|
||||
private predicate nodeMayUseSummary(
|
||||
NodeEx n, FlowState state, AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(DataFlowCallable c |
|
||||
Stage4::parameterMayFlowThrough(_, c, apa, config) and
|
||||
nodeMayUseSummary0(n, c, state, apa, config)
|
||||
exists(DataFlowCallable c, ParameterPosition pos, ParamNodeEx p |
|
||||
Stage4::parameterMayFlowThrough(p, apa, config) and
|
||||
nodeMayUseSummary0(n, c, pos, state, apa, config) and
|
||||
p.isParameterOf(c, pos)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2532,7 +2606,7 @@ private newtype TSummaryCtx =
|
||||
TSummaryCtxNone() or
|
||||
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
|
||||
exists(Configuration config |
|
||||
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
|
||||
Stage4::parameterMayFlowThrough(p, ap.getApprox(), config) and
|
||||
Stage4::revFlow(p, state, _, config)
|
||||
)
|
||||
}
|
||||
@@ -3453,17 +3527,11 @@ private predicate paramFlowsThrough(
|
||||
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
|
||||
AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos |
|
||||
exists(PathNodeMid mid, RetNodeEx ret |
|
||||
pathNode(mid, ret, state, cc, sc, ap, config, _) and
|
||||
kind = ret.getKind() and
|
||||
apa = ap.getApprox() and
|
||||
pos = sc.getParameterPos() and
|
||||
// we don't expect a parameter to return stored in itself, unless explicitly allowed
|
||||
(
|
||||
not kind.(ParamUpdateReturnKind).getPosition() = pos
|
||||
or
|
||||
sc.getParamNode().allowParameterReturnInSelf()
|
||||
)
|
||||
parameterFlowThroughAllowed(sc.getParamNode(), kind)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -915,6 +915,17 @@ private module Cached {
|
||||
TDataFlowCallNone() or
|
||||
TDataFlowCallSome(DataFlowCall call)
|
||||
|
||||
cached
|
||||
newtype TParameterPositionOption =
|
||||
TParameterPositionNone() or
|
||||
TParameterPositionSome(ParameterPosition pos)
|
||||
|
||||
cached
|
||||
newtype TReturnCtx =
|
||||
TReturnCtxNone() or
|
||||
TReturnCtxNoFlowThrough() or
|
||||
TReturnCtxMaybeFlowThrough(ReturnKindExt kind)
|
||||
|
||||
cached
|
||||
newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) }
|
||||
|
||||
@@ -1304,6 +1315,44 @@ class DataFlowCallOption extends TDataFlowCallOption {
|
||||
}
|
||||
}
|
||||
|
||||
/** An optional `ParameterPosition`. */
|
||||
class ParameterPositionOption extends TParameterPositionOption {
|
||||
string toString() {
|
||||
this = TParameterPositionNone() and
|
||||
result = "(none)"
|
||||
or
|
||||
exists(ParameterPosition pos |
|
||||
this = TParameterPositionSome(pos) and
|
||||
result = pos.toString()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A return context used to calculate flow summaries in reverse flow.
|
||||
*
|
||||
* The possible values are:
|
||||
*
|
||||
* - `TReturnCtxNone()`: no return flow.
|
||||
* - `TReturnCtxNoFlowThrough()`: return flow, but flow through is not possible.
|
||||
* - `TReturnCtxMaybeFlowThrough(ReturnKindExt kind)`: return flow, of kind `kind`, and
|
||||
* flow through may be possible.
|
||||
*/
|
||||
class ReturnCtx extends TReturnCtx {
|
||||
string toString() {
|
||||
this = TReturnCtxNone() and
|
||||
result = "(none)"
|
||||
or
|
||||
this = TReturnCtxNoFlowThrough() and
|
||||
result = "(no flow through)"
|
||||
or
|
||||
exists(ReturnKindExt kind |
|
||||
this = TReturnCtxMaybeFlowThrough(kind) and
|
||||
result = kind.toString()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** A `Content` tagged with the type of a containing object. */
|
||||
class TypedContent extends MkTypedContent {
|
||||
private Content c;
|
||||
|
||||
@@ -244,4 +244,20 @@ module Consistency {
|
||||
not callable = viableCallable(call) and
|
||||
not any(ConsistencyConfiguration c).viableImplInCallContextTooLargeExclude(call, ctx, callable)
|
||||
}
|
||||
|
||||
query predicate uniqueParameterNodeAtPosition(
|
||||
DataFlowCallable c, ParameterPosition pos, Node p, string msg
|
||||
) {
|
||||
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
|
||||
) {
|
||||
isParameterNode(p, c, pos) and
|
||||
not exists(unique(ParameterPosition pos0 | isParameterNode(p, c, pos0))) and
|
||||
msg = "Parameter node with multiple positions."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,644 @@
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* An IR taint tracking library that uses an IR DataFlow configuration to track
|
||||
* taint from user inputs as defined by `semmle.code.cpp.security.Security`.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.security.Security
|
||||
private import semmle.code.cpp.ir.dataflow.DataFlow
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
|
||||
private import semmle.code.cpp.ir.dataflow.DataFlow3
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import semmle.code.cpp.ir.dataflow.ResolveCall
|
||||
private import semmle.code.cpp.controlflow.IRGuards
|
||||
private import semmle.code.cpp.models.interfaces.Taint
|
||||
private import semmle.code.cpp.models.interfaces.DataFlow
|
||||
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
|
||||
|
||||
/**
|
||||
* A predictable instruction is one where an external user can predict
|
||||
* the value. For example, a literal in the source code is considered
|
||||
* predictable.
|
||||
*/
|
||||
private predicate predictableInstruction(Instruction instr) {
|
||||
instr instanceof ConstantInstruction
|
||||
or
|
||||
instr instanceof StringConstantInstruction
|
||||
or
|
||||
// This could be a conversion on a string literal
|
||||
predictableInstruction(instr.(UnaryInstruction).getUnary())
|
||||
}
|
||||
|
||||
/**
|
||||
* Functions that we should only allow taint to flow through (to the return
|
||||
* value) if all but the source argument are 'predictable'. This is done to
|
||||
* emulate the old security library's implementation rather than due to any
|
||||
* strong belief that this is the right approach.
|
||||
*
|
||||
* Note that the list itself is not very principled; it consists of all the
|
||||
* functions listed in the old security library's [default] `isPureFunction`
|
||||
* that have more than one argument, but are not in the old taint tracking
|
||||
* library's `returnArgument` predicate.
|
||||
*/
|
||||
predicate predictableOnlyFlow(string name) {
|
||||
name =
|
||||
[
|
||||
"strcasestr", "strchnul", "strchr", "strchrnul", "strcmp", "strcspn", "strncmp", "strndup",
|
||||
"strnlen", "strrchr", "strspn", "strstr", "strtod", "strtof", "strtol", "strtoll", "strtoq",
|
||||
"strtoul"
|
||||
]
|
||||
}
|
||||
|
||||
private DataFlow::Node getNodeForSource(Expr source) {
|
||||
isUserInput(source, _) and
|
||||
result = getNodeForExpr(source)
|
||||
}
|
||||
|
||||
private DataFlow::Node getNodeForExpr(Expr node) {
|
||||
result = DataFlow::exprNode(node)
|
||||
or
|
||||
// Some of the sources in `isUserInput` are intended to match the value of
|
||||
// an expression, while others (those modeled below) are intended to match
|
||||
// the taint that propagates out of an argument, like the `char *` argument
|
||||
// to `gets`. It's impossible here to tell which is which, but the "access
|
||||
// to argv" source is definitely not intended to match an output argument,
|
||||
// and it causes false positives if we let it.
|
||||
//
|
||||
// This case goes together with the similar (but not identical) rule in
|
||||
// `nodeIsBarrierIn`.
|
||||
result = DataFlow::definitionByReferenceNodeFromArgument(node) and
|
||||
not argv(node.(VariableAccess).getTarget())
|
||||
}
|
||||
|
||||
private class DefaultTaintTrackingCfg extends TaintTracking::Configuration {
|
||||
DefaultTaintTrackingCfg() { this = "DefaultTaintTrackingCfg" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source = getNodeForSource(_) }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { exists(adjustedSink(sink)) }
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { nodeIsBarrier(node) }
|
||||
|
||||
override predicate isSanitizerIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
|
||||
}
|
||||
|
||||
private class ToGlobalVarTaintTrackingCfg extends TaintTracking::Configuration {
|
||||
ToGlobalVarTaintTrackingCfg() { this = "GlobalVarTaintTrackingCfg" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source = getNodeForSource(_) }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
sink.asVariable() instanceof GlobalOrNamespaceVariable
|
||||
}
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) {
|
||||
writesVariable(n1.asInstruction(), n2.asVariable().(GlobalOrNamespaceVariable))
|
||||
or
|
||||
readsVariable(n2.asInstruction(), n1.asVariable().(GlobalOrNamespaceVariable))
|
||||
}
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { nodeIsBarrier(node) }
|
||||
|
||||
override predicate isSanitizerIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
|
||||
}
|
||||
|
||||
private class FromGlobalVarTaintTrackingCfg extends TaintTracking2::Configuration {
|
||||
FromGlobalVarTaintTrackingCfg() { this = "FromGlobalVarTaintTrackingCfg" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
// This set of sources should be reasonably small, which is good for
|
||||
// performance since the set of sinks is very large.
|
||||
exists(ToGlobalVarTaintTrackingCfg otherCfg | otherCfg.hasFlowTo(source))
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { exists(adjustedSink(sink)) }
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) {
|
||||
// Additional step for flow out of variables. There is no flow _into_
|
||||
// variables in this configuration, so this step only serves to take flow
|
||||
// out of a variable that's a source.
|
||||
readsVariable(n2.asInstruction(), n1.asVariable())
|
||||
}
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { nodeIsBarrier(node) }
|
||||
|
||||
override predicate isSanitizerIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
|
||||
}
|
||||
|
||||
private predicate readsVariable(LoadInstruction load, Variable var) {
|
||||
load.getSourceAddress().(VariableAddressInstruction).getAstVariable() = var
|
||||
}
|
||||
|
||||
private predicate writesVariable(StoreInstruction store, Variable var) {
|
||||
store.getDestinationAddress().(VariableAddressInstruction).getAstVariable() = var
|
||||
}
|
||||
|
||||
/**
|
||||
* A variable that has any kind of upper-bound check anywhere in the program. This is
|
||||
* biased towards being inclusive because there are a lot of valid ways of doing an
|
||||
* upper bounds checks if we don't consider where it occurs, for example:
|
||||
* ```
|
||||
* if (x < 10) { sink(x); }
|
||||
*
|
||||
* if (10 > y) { sink(y); }
|
||||
*
|
||||
* if (z > 10) { z = 10; }
|
||||
* sink(z);
|
||||
* ```
|
||||
*/
|
||||
// TODO: This coarse overapproximation, ported from the old taint tracking
|
||||
// library, could be replaced with an actual semantic check that a particular
|
||||
// variable _access_ is guarded by an upper-bound check. We probably don't want
|
||||
// to do this right away since it could expose a lot of FPs that were
|
||||
// previously suppressed by this predicate by coincidence.
|
||||
private predicate hasUpperBoundsCheck(Variable var) {
|
||||
exists(RelationalOperation oper, VariableAccess access |
|
||||
oper.getAnOperand() = access and
|
||||
access.getTarget() = var and
|
||||
// Comparing to 0 is not an upper bound check
|
||||
not oper.getAnOperand().getValue() = "0"
|
||||
)
|
||||
}
|
||||
|
||||
private predicate nodeIsBarrierEqualityCandidate(
|
||||
DataFlow::Node node, Operand access, Variable checkedVar
|
||||
) {
|
||||
readsVariable(node.asInstruction(), checkedVar) and
|
||||
any(IRGuardCondition guard).ensuresEq(access, _, _, node.asInstruction().getBlock(), true)
|
||||
}
|
||||
|
||||
cached
|
||||
private module Cached {
|
||||
cached
|
||||
predicate nodeIsBarrier(DataFlow::Node node) {
|
||||
exists(Variable checkedVar |
|
||||
readsVariable(node.asInstruction(), checkedVar) and
|
||||
hasUpperBoundsCheck(checkedVar)
|
||||
)
|
||||
or
|
||||
exists(Variable checkedVar, Operand access |
|
||||
/*
|
||||
* This node is guarded by a condition that forces the accessed variable
|
||||
* to equal something else. For example:
|
||||
* ```
|
||||
* x = taintsource()
|
||||
* if (x == 10) {
|
||||
* taintsink(x); // not considered tainted
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
|
||||
nodeIsBarrierEqualityCandidate(node, access, checkedVar) and
|
||||
readsVariable(access.getDef(), checkedVar)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate nodeIsBarrierIn(DataFlow::Node node) {
|
||||
// don't use dataflow into taint sources, as this leads to duplicate results.
|
||||
exists(Expr source | isUserInput(source, _) |
|
||||
node = DataFlow::exprNode(source)
|
||||
or
|
||||
// This case goes together with the similar (but not identical) rule in
|
||||
// `getNodeForSource`.
|
||||
node = DataFlow::definitionByReferenceNodeFromArgument(source)
|
||||
)
|
||||
or
|
||||
// don't use dataflow into binary instructions if both operands are unpredictable
|
||||
exists(BinaryInstruction iTo |
|
||||
iTo = node.asInstruction() and
|
||||
not predictableInstruction(iTo.getLeft()) and
|
||||
not predictableInstruction(iTo.getRight()) and
|
||||
// propagate taint from either the pointer or the offset, regardless of predictability
|
||||
not iTo instanceof PointerArithmeticInstruction
|
||||
)
|
||||
or
|
||||
// don't use dataflow through calls to pure functions if two or more operands
|
||||
// are unpredictable
|
||||
exists(Instruction iFrom1, Instruction iFrom2, CallInstruction iTo |
|
||||
iTo = node.asInstruction() and
|
||||
isPureFunction(iTo.getStaticCallTarget().getName()) and
|
||||
iFrom1 = iTo.getAnArgument() and
|
||||
iFrom2 = iTo.getAnArgument() and
|
||||
not predictableInstruction(iFrom1) and
|
||||
not predictableInstruction(iFrom2) and
|
||||
iFrom1 != iFrom2
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
Element adjustedSink(DataFlow::Node sink) {
|
||||
// TODO: is it more appropriate to use asConvertedExpr here and avoid
|
||||
// `getConversion*`? Or will that cause us to miss some cases where there's
|
||||
// flow to a conversion (like a `ReferenceDereferenceExpr`) and we want to
|
||||
// pretend there was flow to the converted `Expr` for the sake of
|
||||
// compatibility.
|
||||
sink.asExpr().getConversion*() = result
|
||||
or
|
||||
// For compatibility, send flow from arguments to parameters, even for
|
||||
// functions with no body.
|
||||
exists(FunctionCall call, int i |
|
||||
sink.asExpr() = call.getArgument(pragma[only_bind_into](i)) and
|
||||
result = resolveCall(call).getParameter(pragma[only_bind_into](i))
|
||||
)
|
||||
or
|
||||
// For compatibility, send flow into a `Variable` if there is flow to any
|
||||
// Load or Store of that variable.
|
||||
exists(CopyInstruction copy |
|
||||
copy.getSourceValue() = sink.asInstruction() and
|
||||
(
|
||||
readsVariable(copy, result) or
|
||||
writesVariable(copy, result)
|
||||
) and
|
||||
not hasUpperBoundsCheck(result)
|
||||
)
|
||||
or
|
||||
// For compatibility, send flow into a `NotExpr` even if it's part of a
|
||||
// short-circuiting condition and thus might get skipped.
|
||||
result.(NotExpr).getOperand() = sink.asExpr()
|
||||
or
|
||||
// Taint postfix and prefix crement operations when their operand is tainted.
|
||||
result.(CrementOperation).getAnOperand() = sink.asExpr()
|
||||
or
|
||||
// Taint `e1 += e2`, `e &= e2` and friends when `e1` or `e2` is tainted.
|
||||
result.(AssignOperation).getAnOperand() = sink.asExpr()
|
||||
or
|
||||
result =
|
||||
sink.asOperand()
|
||||
.(SideEffectOperand)
|
||||
.getUse()
|
||||
.(ReadSideEffectInstruction)
|
||||
.getArgumentDef()
|
||||
.getUnconvertedResultExpression()
|
||||
}
|
||||
|
||||
/**
|
||||
* Step to return value of a modeled function when an input taints the
|
||||
* dereference of the return value.
|
||||
*/
|
||||
cached
|
||||
predicate additionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) {
|
||||
exists(CallInstruction call, Function func, FunctionInput modelIn, FunctionOutput modelOut |
|
||||
n1.asOperand() = callInput(call, modelIn) and
|
||||
(
|
||||
func.(TaintFunction).hasTaintFlow(modelIn, modelOut)
|
||||
or
|
||||
func.(DataFlowFunction).hasDataFlow(modelIn, modelOut)
|
||||
) and
|
||||
call.getStaticCallTarget() = func and
|
||||
modelOut.isReturnValueDeref() and
|
||||
call = n2.asInstruction()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private import Cached
|
||||
|
||||
/**
|
||||
* Holds if `tainted` may contain taint from `source`.
|
||||
*
|
||||
* A tainted expression is either directly user input, or is
|
||||
* computed from user input in a way that users can probably
|
||||
* control the exact output of the computation.
|
||||
*
|
||||
* This doesn't include data flow through global variables.
|
||||
* If you need that you must call `taintedIncludingGlobalVars`.
|
||||
*/
|
||||
cached
|
||||
predicate tainted(Expr source, Element tainted) {
|
||||
exists(DefaultTaintTrackingCfg cfg, DataFlow::Node sink |
|
||||
cfg.hasFlow(getNodeForSource(source), sink) and
|
||||
tainted = adjustedSink(sink)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `tainted` may contain taint from `source`, where the taint passed
|
||||
* through a global variable named `globalVar`.
|
||||
*
|
||||
* A tainted expression is either directly user input, or is
|
||||
* computed from user input in a way that users can probably
|
||||
* control the exact output of the computation.
|
||||
*
|
||||
* This version gives the same results as tainted but also includes
|
||||
* data flow through global variables.
|
||||
*
|
||||
* The parameter `globalVar` is the qualified name of the last global variable
|
||||
* used to move the value from source to tainted. If the taint did not pass
|
||||
* through a global variable, then `globalVar = ""`.
|
||||
*/
|
||||
cached
|
||||
predicate taintedIncludingGlobalVars(Expr source, Element tainted, string globalVar) {
|
||||
tainted(source, tainted) and
|
||||
globalVar = ""
|
||||
or
|
||||
exists(
|
||||
ToGlobalVarTaintTrackingCfg toCfg, FromGlobalVarTaintTrackingCfg fromCfg,
|
||||
DataFlow::VariableNode variableNode, GlobalOrNamespaceVariable global, DataFlow::Node sink
|
||||
|
|
||||
global = variableNode.getVariable() and
|
||||
toCfg.hasFlow(getNodeForSource(source), variableNode) and
|
||||
fromCfg.hasFlow(variableNode, sink) and
|
||||
tainted = adjustedSink(sink) and
|
||||
global = globalVarFromId(globalVar)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the global variable whose qualified name is `id`. Use this predicate
|
||||
* together with `taintedIncludingGlobalVars`. Example:
|
||||
*
|
||||
* ```
|
||||
* exists(string varName |
|
||||
* taintedIncludingGlobalVars(source, tainted, varName) and
|
||||
* var = globalVarFromId(varName)
|
||||
* )
|
||||
* ```
|
||||
*/
|
||||
GlobalOrNamespaceVariable globalVarFromId(string id) { id = result.getQualifiedName() }
|
||||
|
||||
/**
|
||||
* Provides definitions for augmenting source/sink pairs with data-flow paths
|
||||
* between them. From a `@kind path-problem` query, import this module in the
|
||||
* global scope, extend `TaintTrackingConfiguration`, and use `taintedWithPath`
|
||||
* in place of `tainted`.
|
||||
*
|
||||
* Importing this module will also import the query predicates that contain the
|
||||
* taint paths.
|
||||
*/
|
||||
module TaintedWithPath {
|
||||
private newtype TSingleton = MkSingleton()
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration that matches sources and sinks in the same
|
||||
* way as the `tainted` predicate.
|
||||
*
|
||||
* Override `isSink` and `taintThroughGlobals` as needed, but do not provide
|
||||
* a characteristic predicate.
|
||||
*/
|
||||
class TaintTrackingConfiguration extends TSingleton {
|
||||
/** Override this to specify which elements are sources in this configuration. */
|
||||
predicate isSource(Expr source) { exists(getNodeForSource(source)) }
|
||||
|
||||
/** Override this to specify which elements are sinks in this configuration. */
|
||||
abstract predicate isSink(Element e);
|
||||
|
||||
/** Override this to specify which expressions are barriers in this configuration. */
|
||||
predicate isBarrier(Expr e) { nodeIsBarrier(getNodeForExpr(e)) }
|
||||
|
||||
/**
|
||||
* Override this predicate to `any()` to allow taint to flow through global
|
||||
* variables.
|
||||
*/
|
||||
predicate taintThroughGlobals() { none() }
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = "TaintTrackingConfiguration" }
|
||||
}
|
||||
|
||||
private class AdjustedConfiguration extends TaintTracking3::Configuration {
|
||||
AdjustedConfiguration() { this = "AdjustedConfiguration" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
exists(TaintTrackingConfiguration cfg, Expr e |
|
||||
cfg.isSource(e) and source = getNodeForExpr(e)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
exists(TaintTrackingConfiguration cfg | cfg.isSink(adjustedSink(sink)))
|
||||
}
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) {
|
||||
// Steps into and out of global variables
|
||||
exists(TaintTrackingConfiguration cfg | cfg.taintThroughGlobals() |
|
||||
writesVariable(n1.asInstruction(), n2.asVariable().(GlobalOrNamespaceVariable))
|
||||
or
|
||||
readsVariable(n2.asInstruction(), n1.asVariable().(GlobalOrNamespaceVariable))
|
||||
)
|
||||
or
|
||||
additionalTaintStep(n1, n2)
|
||||
}
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) {
|
||||
exists(TaintTrackingConfiguration cfg, Expr e | cfg.isBarrier(e) and node = getNodeForExpr(e))
|
||||
}
|
||||
|
||||
override predicate isSanitizerIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
|
||||
}
|
||||
|
||||
/*
|
||||
* A sink `Element` may map to multiple `DataFlowX::PathNode`s via (the
|
||||
* inverse of) `adjustedSink`. For example, an `Expr` maps to all its
|
||||
* conversions, and a `Variable` maps to all loads and stores from it. Because
|
||||
* the path node is part of the tuple that constitutes the alert, this leads
|
||||
* to duplicate alerts.
|
||||
*
|
||||
* To avoid showing duplicates, we edit the graph to replace the final node
|
||||
* coming from the data-flow library with a node that matches exactly the
|
||||
* `Element` sink that's requested.
|
||||
*
|
||||
* The same is done for sources.
|
||||
*/
|
||||
|
||||
private newtype TPathNode =
|
||||
TWrapPathNode(DataFlow3::PathNode n) or
|
||||
// There's a single newtype constructor for both sources and sinks since
|
||||
// that makes it easiest to deal with the case where source = sink.
|
||||
TEndpointPathNode(Element e) {
|
||||
exists(AdjustedConfiguration cfg, DataFlow3::Node sourceNode, DataFlow3::Node sinkNode |
|
||||
cfg.hasFlow(sourceNode, sinkNode)
|
||||
|
|
||||
sourceNode = getNodeForExpr(e) and
|
||||
exists(TaintTrackingConfiguration ttCfg | ttCfg.isSource(e))
|
||||
or
|
||||
e = adjustedSink(sinkNode) and
|
||||
exists(TaintTrackingConfiguration ttCfg | ttCfg.isSink(e))
|
||||
)
|
||||
}
|
||||
|
||||
/** An opaque type used for the nodes of a data-flow path. */
|
||||
class PathNode extends TPathNode {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { none() }
|
||||
|
||||
/**
|
||||
* 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
|
||||
) {
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*/
|
||||
module Private {
|
||||
/** Gets a predecessor `PathNode` of `pathNode`, if any. */
|
||||
PathNode getAPredecessor(PathNode pathNode) { edges(result, pathNode) }
|
||||
|
||||
/** Gets the element that `pathNode` wraps, if any. */
|
||||
Element getElementFromPathNode(PathNode pathNode) {
|
||||
exists(DataFlow::Node node | node = pathNode.(WrapPathNode).inner().getNode() |
|
||||
result = node.asInstruction().getAst()
|
||||
or
|
||||
result = node.asOperand().getDef().getAst()
|
||||
)
|
||||
or
|
||||
result = pathNode.(EndpointPathNode).inner()
|
||||
}
|
||||
}
|
||||
|
||||
private class WrapPathNode extends PathNode, TWrapPathNode {
|
||||
DataFlow3::PathNode inner() { this = TWrapPathNode(result) }
|
||||
|
||||
override string toString() { result = this.inner().toString() }
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
this.inner().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
}
|
||||
|
||||
private class EndpointPathNode extends PathNode, TEndpointPathNode {
|
||||
Expr inner() { this = TEndpointPathNode(result) }
|
||||
|
||||
override string toString() { result = this.inner().toString() }
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
this.inner()
|
||||
.getLocation()
|
||||
.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
}
|
||||
|
||||
/** A PathNode whose `Element` is a source. It may also be a sink. */
|
||||
private class InitialPathNode extends EndpointPathNode {
|
||||
InitialPathNode() { exists(TaintTrackingConfiguration cfg | cfg.isSource(this.inner())) }
|
||||
}
|
||||
|
||||
/** A PathNode whose `Element` is a sink. It may also be a source. */
|
||||
private class FinalPathNode extends EndpointPathNode {
|
||||
FinalPathNode() { exists(TaintTrackingConfiguration cfg | cfg.isSink(this.inner())) }
|
||||
}
|
||||
|
||||
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
|
||||
query predicate edges(PathNode a, PathNode b) {
|
||||
DataFlow3::PathGraph::edges(a.(WrapPathNode).inner(), b.(WrapPathNode).inner())
|
||||
or
|
||||
// To avoid showing trivial-looking steps, we _replace_ the last node instead
|
||||
// of adding an edge out of it.
|
||||
exists(WrapPathNode sinkNode |
|
||||
DataFlow3::PathGraph::edges(a.(WrapPathNode).inner(), sinkNode.inner()) and
|
||||
b.(FinalPathNode).inner() = adjustedSink(sinkNode.inner().getNode())
|
||||
)
|
||||
or
|
||||
// Same for the first node
|
||||
exists(WrapPathNode sourceNode |
|
||||
DataFlow3::PathGraph::edges(sourceNode.inner(), b.(WrapPathNode).inner()) and
|
||||
sourceNode.inner().getNode() = getNodeForExpr(a.(InitialPathNode).inner())
|
||||
)
|
||||
or
|
||||
// Finally, handle the case where the path goes directly from a source to a
|
||||
// sink, meaning that they both need to be translated.
|
||||
exists(WrapPathNode sinkNode, WrapPathNode sourceNode |
|
||||
DataFlow3::PathGraph::edges(sourceNode.inner(), sinkNode.inner()) and
|
||||
sourceNode.inner().getNode() = getNodeForExpr(a.(InitialPathNode).inner()) and
|
||||
b.(FinalPathNode).inner() = adjustedSink(sinkNode.inner().getNode())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there is flow from `arg` to `out` across a call that can by summarized by the flow
|
||||
* from `par` to `ret` within it, in the graph of data flow path explanations.
|
||||
*/
|
||||
query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) {
|
||||
DataFlow3::PathGraph::subpaths(arg.(WrapPathNode).inner(), par.(WrapPathNode).inner(),
|
||||
ret.(WrapPathNode).inner(), out.(WrapPathNode).inner())
|
||||
or
|
||||
// To avoid showing trivial-looking steps, we _replace_ the last node instead
|
||||
// of adding an edge out of it.
|
||||
exists(WrapPathNode sinkNode |
|
||||
DataFlow3::PathGraph::subpaths(arg.(WrapPathNode).inner(), par.(WrapPathNode).inner(),
|
||||
ret.(WrapPathNode).inner(), sinkNode.inner()) and
|
||||
out.(FinalPathNode).inner() = adjustedSink(sinkNode.inner().getNode())
|
||||
)
|
||||
or
|
||||
// Same for the first node
|
||||
exists(WrapPathNode sourceNode |
|
||||
DataFlow3::PathGraph::subpaths(sourceNode.inner(), par.(WrapPathNode).inner(),
|
||||
ret.(WrapPathNode).inner(), out.(WrapPathNode).inner()) and
|
||||
sourceNode.inner().getNode() = getNodeForExpr(arg.(InitialPathNode).inner())
|
||||
)
|
||||
or
|
||||
// Finally, handle the case where the path goes directly from a source to a
|
||||
// sink, meaning that they both need to be translated.
|
||||
exists(WrapPathNode sinkNode, WrapPathNode sourceNode |
|
||||
DataFlow3::PathGraph::subpaths(sourceNode.inner(), par.(WrapPathNode).inner(),
|
||||
ret.(WrapPathNode).inner(), sinkNode.inner()) and
|
||||
sourceNode.inner().getNode() = getNodeForExpr(arg.(InitialPathNode).inner()) and
|
||||
out.(FinalPathNode).inner() = adjustedSink(sinkNode.inner().getNode())
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `n` is a node in the graph of data flow path explanations. */
|
||||
query predicate nodes(PathNode n, string key, string val) {
|
||||
key = "semmle.label" and val = n.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `tainted` may contain taint from `source`, where `sourceNode` and
|
||||
* `sinkNode` are the corresponding `PathNode`s that can be used in a query
|
||||
* to provide path explanations. Extend `TaintTrackingConfiguration` to use
|
||||
* this predicate.
|
||||
*
|
||||
* A tainted expression is either directly user input, or is computed from
|
||||
* user input in a way that users can probably control the exact output of
|
||||
* the computation.
|
||||
*/
|
||||
predicate taintedWithPath(Expr source, Element tainted, PathNode sourceNode, PathNode sinkNode) {
|
||||
exists(AdjustedConfiguration cfg, DataFlow3::Node flowSource, DataFlow3::Node flowSink |
|
||||
source = sourceNode.(InitialPathNode).inner() and
|
||||
flowSource = getNodeForExpr(source) and
|
||||
cfg.hasFlow(flowSource, flowSink) and
|
||||
tainted = adjustedSink(flowSink) and
|
||||
tainted = sinkNode.(FinalPathNode).inner()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate isGlobalVariablePathNode(WrapPathNode n) {
|
||||
n.inner().getNode().asVariable() instanceof GlobalOrNamespaceVariable
|
||||
}
|
||||
|
||||
private predicate edgesWithoutGlobals(PathNode a, PathNode b) {
|
||||
edges(a, b) and
|
||||
not isGlobalVariablePathNode(a) and
|
||||
not isGlobalVariablePathNode(b)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `tainted` can be reached from a taint source without passing
|
||||
* through a global variable.
|
||||
*/
|
||||
predicate taintedWithoutGlobals(Element tainted) {
|
||||
exists(AdjustedConfiguration cfg, PathNode sourceNode, FinalPathNode sinkNode |
|
||||
cfg.isSource(sourceNode.(WrapPathNode).inner().getNode()) and
|
||||
edgesWithoutGlobals+(sourceNode, sinkNode) and
|
||||
tainted = sinkNode.inner()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,19 @@
|
||||
/**
|
||||
* Provides an implementation class modeling the POSIX function `getenv`.
|
||||
* Provides an implementation class modeling the POSIX function `getenv` and
|
||||
* various similar functions.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.models.interfaces.FlowSource
|
||||
|
||||
/**
|
||||
* The POSIX function `getenv`.
|
||||
* The POSIX function `getenv`, the GNU function `secure_getenv`, and the
|
||||
* Windows function `_wgetenv`.
|
||||
*/
|
||||
class Getenv extends LocalFlowSourceFunction {
|
||||
Getenv() { this.hasGlobalOrStdOrBslName("getenv") }
|
||||
Getenv() {
|
||||
this.hasGlobalOrStdOrBslName("getenv") or this.hasGlobalName(["secure_getenv", "_wgetenv"])
|
||||
}
|
||||
|
||||
override predicate hasLocalFlowSource(FunctionOutput output, string description) {
|
||||
(
|
||||
|
||||
@@ -31,7 +31,17 @@ private class IteratorTraits extends Class {
|
||||
* `std::iterator_traits` instantiation for it.
|
||||
*/
|
||||
private class IteratorByTraits extends Iterator {
|
||||
IteratorByTraits() { exists(IteratorTraits it | it.getIteratorType() = this) }
|
||||
IteratorTraits trait;
|
||||
|
||||
IteratorByTraits() { trait.getIteratorType() = this }
|
||||
|
||||
override Type getValueType() {
|
||||
exists(TypedefType t |
|
||||
trait.getAMember() = t and
|
||||
t.getName() = "value_type" and
|
||||
result = t.getUnderlyingType()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -42,20 +52,27 @@ private class IteratorByTraits extends Iterator {
|
||||
*/
|
||||
private class IteratorByPointer extends Iterator instanceof PointerType {
|
||||
IteratorByPointer() { not this instanceof IteratorByTraits }
|
||||
|
||||
override Type getValueType() { result = super.getBaseType() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A type which has the typedefs expected for an iterator.
|
||||
*/
|
||||
private class IteratorByTypedefs extends Iterator, Class {
|
||||
TypedefType valueType;
|
||||
|
||||
IteratorByTypedefs() {
|
||||
this.getAMember().(TypedefType).hasName("difference_type") and
|
||||
this.getAMember().(TypedefType).hasName("value_type") and
|
||||
valueType = this.getAMember() and
|
||||
valueType.hasName("value_type") and
|
||||
this.getAMember().(TypedefType).hasName("pointer") and
|
||||
this.getAMember().(TypedefType).hasName("reference") and
|
||||
this.getAMember().(TypedefType).hasName("iterator_category") and
|
||||
not this.hasQualifiedName(["std", "bsl"], "iterator_traits")
|
||||
}
|
||||
|
||||
override Type getValueType() { result = valueType.getUnderlyingType() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -63,6 +80,8 @@ private class IteratorByTypedefs extends Iterator, Class {
|
||||
*/
|
||||
private class StdIterator extends Iterator, Class {
|
||||
StdIterator() { this.hasQualifiedName(["std", "bsl"], "iterator") }
|
||||
|
||||
override Type getValueType() { result = this.getTemplateArgument(1).(Type).getUnderlyingType() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -166,12 +185,15 @@ private class IteratorSubOperator extends Operator, TaintFunction {
|
||||
/**
|
||||
* A non-member `operator+=` or `operator-=` function for an iterator type.
|
||||
*/
|
||||
private class IteratorAssignArithmeticOperator extends Operator, DataFlowFunction, TaintFunction {
|
||||
class IteratorAssignArithmeticOperator extends Operator {
|
||||
IteratorAssignArithmeticOperator() {
|
||||
this.hasName(["operator+=", "operator-="]) and
|
||||
exists(getIteratorArgumentInput(this, 0))
|
||||
}
|
||||
}
|
||||
|
||||
private class IteratorAssignArithmeticOperatorModel extends IteratorAssignArithmeticOperator,
|
||||
DataFlowFunction, TaintFunction {
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isParameter(0) and
|
||||
output.isReturnValue()
|
||||
@@ -210,11 +232,14 @@ class IteratorPointerDereferenceMemberOperator extends MemberFunction, TaintFunc
|
||||
/**
|
||||
* An `operator++` or `operator--` member function for an iterator type.
|
||||
*/
|
||||
private class IteratorCrementMemberOperator extends MemberFunction, DataFlowFunction, TaintFunction {
|
||||
class IteratorCrementMemberOperator extends MemberFunction {
|
||||
IteratorCrementMemberOperator() {
|
||||
this.getClassAndName(["operator++", "operator--"]) instanceof Iterator
|
||||
}
|
||||
}
|
||||
|
||||
private class IteratorCrementMemberOperatorModel extends IteratorCrementMemberOperator,
|
||||
DataFlowFunction, TaintFunction {
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isQualifierAddress() and
|
||||
output.isReturnValue()
|
||||
|
||||
@@ -5,38 +5,53 @@
|
||||
import semmle.code.cpp.models.interfaces.Taint
|
||||
import semmle.code.cpp.models.interfaces.Iterator
|
||||
|
||||
/**
|
||||
* A sequence container template class (for example, `std::vector`) from the
|
||||
* standard library.
|
||||
*/
|
||||
abstract class StdSequenceContainer extends Class {
|
||||
Type getElementType() { result = this.getTemplateArgument(0) }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `std::array` template class.
|
||||
*/
|
||||
private class Array extends Class {
|
||||
private class Array extends StdSequenceContainer {
|
||||
Array() { this.hasQualifiedName(["std", "bsl"], "array") }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `std::string` template class.
|
||||
*/
|
||||
private class String extends StdSequenceContainer {
|
||||
String() { this.hasQualifiedName(["std", "bsl"], "basic_string") }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `std::deque` template class.
|
||||
*/
|
||||
private class Deque extends Class {
|
||||
private class Deque extends StdSequenceContainer {
|
||||
Deque() { this.hasQualifiedName(["std", "bsl"], "deque") }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `std::forward_list` template class.
|
||||
*/
|
||||
private class ForwardList extends Class {
|
||||
private class ForwardList extends StdSequenceContainer {
|
||||
ForwardList() { this.hasQualifiedName(["std", "bsl"], "forward_list") }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `std::list` template class.
|
||||
*/
|
||||
private class List extends Class {
|
||||
private class List extends StdSequenceContainer {
|
||||
List() { this.hasQualifiedName(["std", "bsl"], "list") }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `std::vector` template class.
|
||||
*/
|
||||
private class Vector extends Class {
|
||||
private class Vector extends StdSequenceContainer {
|
||||
Vector() { this.hasQualifiedName(["std", "bsl"], "vector") }
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,15 @@ private class StdBasicString extends ClassTemplateInstantiation {
|
||||
|
||||
/**
|
||||
* The `std::basic_string::iterator` declaration.
|
||||
*
|
||||
* Intuitively, this class shouldn't be necessary as it's already captured
|
||||
* by the `StdIterator` class. However, this class ensures that the typedef inside the
|
||||
* body of the `std::string` class is also seen as an iterator.
|
||||
*
|
||||
* Eventually, we should be consistent about which of the following should be recognized as iterators:
|
||||
* 1. The typedef type.
|
||||
* 2. The template class of the resolved type.
|
||||
* 3. Any instantiation of the resolved type.
|
||||
*/
|
||||
private class StdBasicStringIterator extends Iterator, Type {
|
||||
StdBasicStringIterator() {
|
||||
|
||||
@@ -29,5 +29,17 @@ abstract class GetIteratorFunction extends Function {
|
||||
|
||||
/**
|
||||
* A type which can be used as an iterator.
|
||||
*
|
||||
* Note: Do _not_ `extend` when inheriting from this class in queries. Always use `instanceof`:
|
||||
* ```
|
||||
* class MyIterator instanceof Iterator { ... }
|
||||
* ```
|
||||
*/
|
||||
abstract class Iterator extends Type { }
|
||||
abstract class Iterator extends Type {
|
||||
/**
|
||||
* Gets the value type of this iterator, if any.
|
||||
*
|
||||
* For example, the value type of a `std::vector<int>::iterator` is `int`.
|
||||
*/
|
||||
Type getValueType() { none() }
|
||||
}
|
||||
|
||||
@@ -121,7 +121,9 @@ private predicate moveToDependingOnSide(Expr src, Expr dest) {
|
||||
* (this is done to avoid false positives). Because of this we need to track if the tainted element came from an argument
|
||||
* or not, and for that we use destFromArg
|
||||
*/
|
||||
private predicate betweenFunctionsValueMoveTo(Element src, Element dest, boolean destFromArg) {
|
||||
deprecated private predicate betweenFunctionsValueMoveTo(
|
||||
Element src, Element dest, boolean destFromArg
|
||||
) {
|
||||
not unreachable(src) and
|
||||
not unreachable(dest) and
|
||||
(
|
||||
@@ -162,13 +164,13 @@ private predicate betweenFunctionsValueMoveTo(Element src, Element dest, boolean
|
||||
// predicate folding for proper join-order
|
||||
// bad magic: pushes down predicate that ruins join-order
|
||||
pragma[nomagic]
|
||||
private predicate resolveCallWithParam(Call call, Function called, int i, Parameter p) {
|
||||
deprecated private predicate resolveCallWithParam(Call call, Function called, int i, Parameter p) {
|
||||
called = resolveCall(call) and
|
||||
p = called.getParameter(i)
|
||||
}
|
||||
|
||||
/** A variable for which flow through is allowed. */
|
||||
library class FlowVariable extends Variable {
|
||||
deprecated library class FlowVariable extends Variable {
|
||||
FlowVariable() {
|
||||
(
|
||||
this instanceof LocalScopeVariable or
|
||||
@@ -179,11 +181,11 @@ library class FlowVariable extends Variable {
|
||||
}
|
||||
|
||||
/** A local scope variable for which flow through is allowed. */
|
||||
library class FlowLocalScopeVariable extends Variable {
|
||||
deprecated library class FlowLocalScopeVariable extends Variable {
|
||||
FlowLocalScopeVariable() { this instanceof LocalScopeVariable }
|
||||
}
|
||||
|
||||
private predicate insideFunctionValueMoveTo(Element src, Element dest) {
|
||||
deprecated private predicate insideFunctionValueMoveTo(Element src, Element dest) {
|
||||
not unreachable(src) and
|
||||
not unreachable(dest) and
|
||||
(
|
||||
@@ -324,7 +326,7 @@ private predicate unionAccess(Variable v, Field f, FieldAccess a) {
|
||||
a.getQualifier() = v.getAnAccess()
|
||||
}
|
||||
|
||||
GlobalOrNamespaceVariable globalVarFromId(string id) {
|
||||
deprecated GlobalOrNamespaceVariable globalVarFromId(string id) {
|
||||
if result instanceof NamespaceVariable
|
||||
then id = result.getNamespace() + "::" + result.getName()
|
||||
else id = result.getName()
|
||||
@@ -353,7 +355,7 @@ private predicate hasUpperBoundsCheck(Variable var) {
|
||||
}
|
||||
|
||||
cached
|
||||
private predicate taintedWithArgsAndGlobalVars(
|
||||
deprecated private predicate taintedWithArgsAndGlobalVars(
|
||||
Element src, Element dest, boolean destFromArg, string globalVar
|
||||
) {
|
||||
isUserInput(src, _) and
|
||||
@@ -395,7 +397,7 @@ private predicate taintedWithArgsAndGlobalVars(
|
||||
* This doesn't include data flow through global variables.
|
||||
* If you need that you must call taintedIncludingGlobalVars.
|
||||
*/
|
||||
predicate tainted(Expr source, Element tainted) {
|
||||
deprecated predicate tainted(Expr source, Element tainted) {
|
||||
taintedWithArgsAndGlobalVars(source, tainted, _, "")
|
||||
}
|
||||
|
||||
@@ -410,7 +412,7 @@ predicate tainted(Expr source, Element tainted) {
|
||||
* The parameter `globalVar` is the name of the last global variable used to move the
|
||||
* value from source to tainted.
|
||||
*/
|
||||
predicate taintedIncludingGlobalVars(Expr source, Element tainted, string globalVar) {
|
||||
deprecated predicate taintedIncludingGlobalVars(Expr source, Element tainted, string globalVar) {
|
||||
taintedWithArgsAndGlobalVars(source, tainted, _, globalVar)
|
||||
}
|
||||
|
||||
@@ -541,14 +543,14 @@ private predicate returnArgument(Function f, int sourceArg) {
|
||||
* targets a virtual method, simple data flow analysis is performed
|
||||
* in order to identify target(s).
|
||||
*/
|
||||
Function resolveCall(Call call) {
|
||||
deprecated Function resolveCall(Call call) {
|
||||
result = call.getTarget()
|
||||
or
|
||||
result = call.(DataSensitiveCallExpr).resolve()
|
||||
}
|
||||
|
||||
/** A data sensitive call expression. */
|
||||
abstract library class DataSensitiveCallExpr extends Expr {
|
||||
abstract deprecated library class DataSensitiveCallExpr extends Expr {
|
||||
DataSensitiveCallExpr() { not unreachable(this) }
|
||||
|
||||
abstract Expr getSrc();
|
||||
@@ -579,7 +581,7 @@ abstract library class DataSensitiveCallExpr extends Expr {
|
||||
}
|
||||
|
||||
/** Call through a function pointer. */
|
||||
library class DataSensitiveExprCall extends DataSensitiveCallExpr, ExprCall {
|
||||
deprecated library class DataSensitiveExprCall extends DataSensitiveCallExpr, ExprCall {
|
||||
override Expr getSrc() { result = getExpr() }
|
||||
|
||||
override Function resolve() {
|
||||
@@ -588,7 +590,8 @@ library class DataSensitiveExprCall extends DataSensitiveCallExpr, ExprCall {
|
||||
}
|
||||
|
||||
/** Call to a virtual function. */
|
||||
library class DataSensitiveOverriddenFunctionCall extends DataSensitiveCallExpr, FunctionCall {
|
||||
deprecated library class DataSensitiveOverriddenFunctionCall extends DataSensitiveCallExpr,
|
||||
FunctionCall {
|
||||
DataSensitiveOverriddenFunctionCall() {
|
||||
exists(getTarget().(VirtualFunction).getAnOverridingFunction())
|
||||
}
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## 0.4.5
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.4.4
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
* Note: this query is not assigned a precision yet because we don't want it on
|
||||
* LGTM until its performance is well understood.
|
||||
* Note: this query is not assigned a precision yet because we don't want it
|
||||
* to be included in query suites until its performance is well understood.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
@@ -52,7 +52,7 @@ class Library extends LibraryT {
|
||||
// The versions reported for C/C++ dependencies are just the versions that
|
||||
// happen to be installed on the system where the build takes place.
|
||||
// Reporting those versions is likely to cause misunderstandings, both for
|
||||
// people reading them and for the vulnerability checker of lgtm.
|
||||
// people reading them and for vulnerability checkers.
|
||||
result = "unknown"
|
||||
}
|
||||
|
||||
|
||||
@@ -17,8 +17,9 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.security.FunctionWithWrappers
|
||||
import semmle.code.cpp.security.Security
|
||||
import semmle.code.cpp.security.TaintTracking
|
||||
import TaintedWithPath
|
||||
import semmle.code.cpp.ir.IR
|
||||
import semmle.code.cpp.ir.dataflow.TaintTracking
|
||||
import DataFlow::PathGraph
|
||||
|
||||
/**
|
||||
* A function for opening a file.
|
||||
@@ -46,18 +47,91 @@ class FileFunction extends FunctionWithWrappers {
|
||||
override predicate interestingArg(int arg) { arg = 0 }
|
||||
}
|
||||
|
||||
class TaintedPathConfiguration extends TaintTrackingConfiguration {
|
||||
override predicate isSink(Element tainted) {
|
||||
exists(FileFunction fileFunction | fileFunction.outermostWrapperFunctionCall(tainted, _))
|
||||
Expr asSourceExpr(DataFlow::Node node) {
|
||||
result = node.asConvertedExpr()
|
||||
or
|
||||
result = node.asDefiningArgument()
|
||||
}
|
||||
|
||||
Expr asSinkExpr(DataFlow::Node node) {
|
||||
result =
|
||||
node.asOperand()
|
||||
.(SideEffectOperand)
|
||||
.getUse()
|
||||
.(ReadSideEffectInstruction)
|
||||
.getArgumentDef()
|
||||
.getUnconvertedResultExpression()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds for a variable that has any kind of upper-bound check anywhere in the program.
|
||||
* This is biased towards being inclusive and being a coarse overapproximation because
|
||||
* there are a lot of valid ways of doing an upper bounds checks if we don't consider
|
||||
* where it occurs, for example:
|
||||
* ```cpp
|
||||
* if (x < 10) { sink(x); }
|
||||
*
|
||||
* if (10 > y) { sink(y); }
|
||||
*
|
||||
* if (z > 10) { z = 10; }
|
||||
* sink(z);
|
||||
* ```
|
||||
*/
|
||||
predicate hasUpperBoundsCheck(Variable var) {
|
||||
exists(RelationalOperation oper, VariableAccess access |
|
||||
oper.getAnOperand() = access and
|
||||
access.getTarget() = var and
|
||||
// Comparing to 0 is not an upper bound check
|
||||
not oper.getAnOperand().getValue() = "0"
|
||||
)
|
||||
}
|
||||
|
||||
class TaintedPathConfiguration extends TaintTracking::Configuration {
|
||||
TaintedPathConfiguration() { this = "TaintedPathConfiguration" }
|
||||
|
||||
override predicate isSource(DataFlow::Node node) { isUserInput(asSourceExpr(node), _) }
|
||||
|
||||
override predicate isSink(DataFlow::Node node) {
|
||||
exists(FileFunction fileFunction |
|
||||
fileFunction.outermostWrapperFunctionCall(asSinkExpr(node), _)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSanitizerIn(DataFlow::Node node) { this.isSource(node) }
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) {
|
||||
node.asExpr().(Call).getTarget().getUnspecifiedType() instanceof ArithmeticType
|
||||
or
|
||||
exists(LoadInstruction load, Variable checkedVar |
|
||||
load = node.asInstruction() and
|
||||
checkedVar = load.getSourceAddress().(VariableAddressInstruction).getAstVariable() and
|
||||
hasUpperBoundsCheck(checkedVar)
|
||||
)
|
||||
}
|
||||
|
||||
predicate hasFilteredFlowPath(DataFlow::PathNode source, DataFlow::PathNode sink) {
|
||||
this.hasFlowPath(source, sink) and
|
||||
// The use of `isUserInput` in `isSink` in combination with `asSourceExpr` causes
|
||||
// duplicate results. Filter these duplicates. The proper solution is to switch to
|
||||
// using `LocalFlowSource` and `RemoteFlowSource`, but this currently only supports
|
||||
// a subset of the cases supported by `isUserInput`.
|
||||
not exists(DataFlow::PathNode source2 |
|
||||
this.hasFlowPath(source2, sink) and
|
||||
asSourceExpr(source.getNode()) = asSourceExpr(source2.getNode())
|
||||
|
|
||||
not exists(source.getNode().asConvertedExpr()) and exists(source2.getNode().asConvertedExpr())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
from
|
||||
FileFunction fileFunction, Expr taintedArg, Expr taintSource, PathNode sourceNode,
|
||||
PathNode sinkNode, string taintCause, string callChain
|
||||
FileFunction fileFunction, Expr taintedArg, Expr taintSource, TaintedPathConfiguration cfg,
|
||||
DataFlow::PathNode sourceNode, DataFlow::PathNode sinkNode, string taintCause, string callChain
|
||||
where
|
||||
taintedArg = asSinkExpr(sinkNode.getNode()) and
|
||||
fileFunction.outermostWrapperFunctionCall(taintedArg, callChain) and
|
||||
taintedWithPath(taintSource, taintedArg, sourceNode, sinkNode) and
|
||||
cfg.hasFilteredFlowPath(sourceNode, sinkNode) and
|
||||
taintSource = asSourceExpr(sourceNode.getNode()) and
|
||||
isUserInput(taintSource, taintCause)
|
||||
select taintedArg, sourceNode, sinkNode,
|
||||
"This argument to a file access function is derived from $@ and then passed to " + callChain + ".",
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.commons.Environment
|
||||
import semmle.code.cpp.security.TaintTracking
|
||||
import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl
|
||||
import TaintedWithPath
|
||||
|
||||
/** A call that prints its arguments to `stdout`. */
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.security.Security
|
||||
import semmle.code.cpp.security.FunctionWithWrappers
|
||||
import semmle.code.cpp.security.TaintTracking
|
||||
import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl
|
||||
import TaintedWithPath
|
||||
|
||||
class SqlLikeFunction extends FunctionWithWrappers {
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.security.Security
|
||||
import semmle.code.cpp.security.TaintTracking
|
||||
import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl
|
||||
import TaintedWithPath
|
||||
|
||||
predicate isProcessOperationExplanation(Expr arg, string processOperation) {
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
import semmle.code.cpp.security.BufferWrite
|
||||
import semmle.code.cpp.security.Security
|
||||
import semmle.code.cpp.security.TaintTracking
|
||||
import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl
|
||||
import TaintedWithPath
|
||||
|
||||
/*
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.security.Security
|
||||
import semmle.code.cpp.security.FunctionWithWrappers
|
||||
import semmle.code.cpp.security.TaintTracking
|
||||
import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl
|
||||
import TaintedWithPath
|
||||
|
||||
class Configuration extends TaintTrackingConfiguration {
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.security.FunctionWithWrappers
|
||||
import semmle.code.cpp.security.Security
|
||||
import semmle.code.cpp.security.TaintTracking
|
||||
import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl
|
||||
import TaintedWithPath
|
||||
|
||||
class Configuration extends TaintTrackingConfiguration {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.commons.NullTermination
|
||||
import semmle.code.cpp.security.TaintTracking
|
||||
import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl
|
||||
|
||||
/** A user-controlled expression that may not be null terminated. */
|
||||
class TaintSource extends VariableAccess {
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.security.Overflow
|
||||
import semmle.code.cpp.security.Security
|
||||
import semmle.code.cpp.security.TaintTracking
|
||||
import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl
|
||||
import TaintedWithPath
|
||||
import Bounded
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.security.Overflow
|
||||
import semmle.code.cpp.security.Security
|
||||
import semmle.code.cpp.security.TaintTracking
|
||||
import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl
|
||||
|
||||
predicate isMaxValue(Expr mie) {
|
||||
exists(MacroInvocation mi |
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
|
||||
import semmle.code.cpp.security.TaintTracking
|
||||
import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl
|
||||
|
||||
/** Holds if `expr` might overflow. */
|
||||
predicate outOfBoundsExpr(Expr expr, string kind) {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
* external/cwe/cwe-290
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.security.TaintTracking
|
||||
import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl
|
||||
import TaintedWithPath
|
||||
|
||||
predicate hardCodedAddressOrIP(StringLiteral txt) {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
* external/cwe/cwe-807
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.security.TaintTracking
|
||||
import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl
|
||||
import TaintedWithPath
|
||||
|
||||
predicate sensitiveCondition(Expr condition, Expr raise) {
|
||||
|
||||
3
cpp/ql/src/change-notes/released/0.4.5.md
Normal file
3
cpp/ql/src/change-notes/released/0.4.5.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.4.5
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.4.4
|
||||
lastReleaseVersion: 0.4.5
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/cpp-queries
|
||||
version: 0.4.5-dev
|
||||
version: 0.4.6-dev
|
||||
groups:
|
||||
- cpp
|
||||
- queries
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
WARNING: Module TaintedWithPath has been deprecated and may be removed in future (tainted.ql:9,8-47)
|
||||
WARNING: Predicate tainted has been deprecated and may be removed in future (tainted.ql:20,49-74)
|
||||
|
||||
@@ -1,18 +1,6 @@
|
||||
#include "../shared.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int main() {
|
||||
|
||||
|
||||
|
||||
sink(_strdup(getenv("VAR"))); // $ ir MISSING: ast
|
||||
sink(strdup(getenv("VAR"))); // $ ast,ir
|
||||
sink(unmodeled_function(getenv("VAR"))); // clean by assumption
|
||||
@@ -59,9 +47,6 @@ void test_outparams() {
|
||||
sink(p2); // $ ir MISSING: ast
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
struct XY {
|
||||
int x;
|
||||
int y;
|
||||
@@ -230,24 +215,17 @@ void test_recv() {
|
||||
|
||||
// --- send and related functions ---
|
||||
|
||||
int send(int, const void*, int, int);
|
||||
|
||||
void test_send(char* buffer, int length) {
|
||||
send(0, buffer, length, 0); // $ remote
|
||||
}
|
||||
|
||||
struct iovec {
|
||||
void *iov_base;
|
||||
unsigned iov_len;
|
||||
};
|
||||
|
||||
int readv(int, const struct iovec*, int);
|
||||
int writev(int, const struct iovec*, int);
|
||||
|
||||
void sink(const iovec* iovs);
|
||||
void sink(iovec);
|
||||
|
||||
int test_readv_and_writev(iovec* iovs) {
|
||||
void test_readv_and_writev(iovec* iovs) {
|
||||
readv(0, iovs, 16);
|
||||
sink(iovs); // $ast,ir
|
||||
sink(iovs[0]); // $ast,ir
|
||||
@@ -256,6 +234,4 @@ int test_readv_and_writev(iovec* iovs) {
|
||||
char* p = (char*)iovs[1].iov_base;
|
||||
sink(p); // $ MISSING: ast,ir
|
||||
sink(*p); // $ MISSING: ast,ir
|
||||
|
||||
writev(0, iovs, 16); // $ remote
|
||||
}
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
/** This tests that we are able to detect remote flow sinks. */
|
||||
|
||||
import cpp
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
import semmle.code.cpp.security.FlowSources
|
||||
|
||||
class RemoteFlowSinkTest extends InlineExpectationsTest {
|
||||
RemoteFlowSinkTest() { this = "RemoteFlowSinkTest" }
|
||||
|
||||
override string getARelevantTag() { result = "remote" }
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
tag = "remote" and
|
||||
value = "" and
|
||||
exists(RemoteFlowSink node |
|
||||
location = node.getLocation() and
|
||||
element = node.toString()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
WARNING: Module TaintedWithPath has been deprecated and may be removed in future (tainted.ql:10,8-47)
|
||||
WARNING: Predicate tainted has been deprecated and may be removed in future (tainted.ql:21,3-28)
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
WARNING: Predicate taintedIncludingGlobalVars has been deprecated and may be removed in future (global.ql:8,3-47)
|
||||
WARNING: Predicate taintedIncludingGlobalVars has been deprecated and may be removed in future (global.ql:12,3-53)
|
||||
|
||||
@@ -93,3 +93,5 @@ postWithInFlow
|
||||
| test.cpp:499:4:499:4 | p [inner post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| test.cpp:505:35:505:35 | x [inner post update] | PostUpdateNode should not be the target of local flow. |
|
||||
viableImplInCallContextTooLarge
|
||||
uniqueParameterNodeAtPosition
|
||||
uniqueParameterNodePosition
|
||||
|
||||
@@ -637,3 +637,5 @@ postWithInFlow
|
||||
| true_upon_entry.cpp:101:18:101:18 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| true_upon_entry.cpp:102:5:102:5 | x [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
viableImplInCallContextTooLarge
|
||||
uniqueParameterNodeAtPosition
|
||||
uniqueParameterNodePosition
|
||||
|
||||
@@ -158,3 +158,5 @@ postWithInFlow
|
||||
| struct_init.c:24:11:24:12 | ab [inner post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| struct_init.c:36:17:36:24 | nestedAB [inner post update] | PostUpdateNode should not be the target of local flow. |
|
||||
viableImplInCallContextTooLarge
|
||||
uniqueParameterNodeAtPosition
|
||||
uniqueParameterNodePosition
|
||||
|
||||
@@ -1326,3 +1326,5 @@ postWithInFlow
|
||||
| struct_init.c:46:16:46:24 | pointerAB [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| struct_init.c:46:16:46:24 | pointerAB [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
viableImplInCallContextTooLarge
|
||||
uniqueParameterNodeAtPosition
|
||||
uniqueParameterNodePosition
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
WARNING: Predicate taintedIncludingGlobalVars has been deprecated and may be removed in future (tainted.ql:5,3-29)
|
||||
| test.cpp:23:23:23:28 | call to getenv | test.cpp:8:24:8:25 | s1 | |
|
||||
| test.cpp:23:23:23:28 | call to getenv | test.cpp:23:14:23:19 | envStr | |
|
||||
| test.cpp:23:23:23:28 | call to getenv | test.cpp:23:23:23:28 | call to getenv | |
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
WARNING: Module TaintedWithPath has been deprecated and may be removed in future (tainted_diff.ql:5,35-54)
|
||||
WARNING: Module TaintedWithPath has been deprecated and may be removed in future (tainted_diff.ql:12,7-26)
|
||||
WARNING: Module TaintedWithPath has been deprecated and may be removed in future (tainted_diff.ql:16,3-22)
|
||||
WARNING: Predicate taintedIncludingGlobalVars has been deprecated and may be removed in future (tainted_diff.ql:11,3-34)
|
||||
WARNING: Predicate taintedIncludingGlobalVars has been deprecated and may be removed in future (tainted_diff.ql:17,7-38)
|
||||
| test.cpp:23:23:23:28 | call to getenv | test.cpp:8:24:8:25 | s1 | AST only |
|
||||
| test.cpp:23:23:23:28 | call to getenv | test.cpp:23:14:23:19 | envStr | AST only |
|
||||
| test.cpp:38:23:38:28 | call to getenv | test.cpp:8:24:8:25 | s1 | AST only |
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
WARNING: Module TaintedWithPath has been deprecated and may be removed in future (tainted_ir.ql:3,35-50)
|
||||
WARNING: Module TaintedWithPath has been deprecated and may be removed in future (tainted_ir.ql:9,3-18)
|
||||
| test.cpp:23:23:23:28 | call to getenv | test.cpp:23:23:23:28 | call to getenv |
|
||||
| test.cpp:23:23:23:28 | call to getenv | test.cpp:23:23:23:40 | (const char *)... |
|
||||
| test.cpp:23:23:23:28 | call to getenv | test.cpp:25:6:25:29 | ! ... |
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
/** This tests that we are able to detect local flow sources. */
|
||||
|
||||
import cpp
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
import semmle.code.cpp.security.FlowSources
|
||||
|
||||
class LocalFlowSourceTest extends InlineExpectationsTest {
|
||||
LocalFlowSourceTest() { this = "LocalFlowSourceTest" }
|
||||
|
||||
override string getARelevantTag() { result = "local_source" }
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
tag = "local_source" and
|
||||
value = "" and
|
||||
exists(LocalFlowSource node |
|
||||
location = node.getLocation() and
|
||||
element = node.toString()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/** This tests that we are able to detect remote flow sources and sinks. */
|
||||
|
||||
import cpp
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
import semmle.code.cpp.security.FlowSources
|
||||
|
||||
class RemoteFlowSourceTest extends InlineExpectationsTest {
|
||||
RemoteFlowSourceTest() { this = "RemoteFlowSourceTest" }
|
||||
|
||||
override string getARelevantTag() { result = "remote_source" }
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
tag = "remote_source" and
|
||||
value = "" and
|
||||
exists(RemoteFlowSource node |
|
||||
location = node.getLocation() and
|
||||
element = node.toString()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class RemoteFlowSinkTest extends InlineExpectationsTest {
|
||||
RemoteFlowSinkTest() { this = "RemoteFlowSinkTest" }
|
||||
|
||||
override string getARelevantTag() { result = "remote_sink" }
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
tag = "remote_sink" and
|
||||
value = "" and
|
||||
exists(RemoteFlowSink node |
|
||||
location = node.getLocation() and
|
||||
element = node.toString()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
char *getenv(const char *name);
|
||||
char *secure_getenv(const char *name);
|
||||
wchar_t *_wgetenv(const wchar_t *name);
|
||||
|
||||
void test_getenv() {
|
||||
void *var1 = getenv("VAR"); // $ local_source
|
||||
void *var2 = secure_getenv("VAR"); // $ local_source
|
||||
void *var3 = _wgetenv(L"VAR"); // $ local_source
|
||||
}
|
||||
|
||||
int send(int, const void*, int, int);
|
||||
|
||||
void test_send(char* buffer, int length) {
|
||||
send(0, buffer, length, 0); // $ remote_sink
|
||||
}
|
||||
|
||||
struct iovec {
|
||||
void *iov_base;
|
||||
unsigned iov_len;
|
||||
};
|
||||
|
||||
int readv(int, const struct iovec*, int);
|
||||
int writev(int, const struct iovec*, int);
|
||||
|
||||
void test_readv_and_writev(iovec* iovs) {
|
||||
readv(0, iovs, 16); // $ remote_source
|
||||
writev(0, iovs, 16); // $ remote_sink
|
||||
}
|
||||
@@ -127,3 +127,5 @@ postWithInFlow
|
||||
| static_init_templates.cpp:21:2:21:4 | val [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| try_catch.cpp:7:8:7:8 | call to exception | PostUpdateNode should not be the target of local flow. |
|
||||
viableImplInCallContextTooLarge
|
||||
uniqueParameterNodeAtPosition
|
||||
uniqueParameterNodePosition
|
||||
|
||||
@@ -2713,3 +2713,7 @@ postWithInFlow
|
||||
| whilestmt.c:40:7:40:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| whilestmt.c:42:7:42:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
viableImplInCallContextTooLarge
|
||||
uniqueParameterNodeAtPosition
|
||||
| ir.cpp:724:6:724:13 | TryCatch | 0 | ir.cpp:735:22:735:22 | *s | Parameters with overlapping positions. |
|
||||
| ir.cpp:724:6:724:13 | TryCatch | 0 | ir.cpp:738:24:738:24 | *e | Parameters with overlapping positions. |
|
||||
uniqueParameterNodePosition
|
||||
|
||||
@@ -1,19 +1,8 @@
|
||||
edges
|
||||
| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:55:27:55:38 | ... + ... | CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | (const char *)... |
|
||||
| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:55:27:55:38 | ... + ... | CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | data |
|
||||
| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:55:27:55:38 | ... + ... | CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | data |
|
||||
| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:55:27:55:38 | ... + ... | CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | data indirection |
|
||||
| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:55:27:55:38 | fgets output argument | CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | (const char *)... |
|
||||
| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:55:27:55:38 | fgets output argument | CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | data |
|
||||
| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:55:27:55:38 | fgets output argument | CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | data |
|
||||
| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:55:27:55:38 | fgets output argument | CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | data indirection |
|
||||
subpaths
|
||||
nodes
|
||||
| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:55:27:55:38 | ... + ... | semmle.label | ... + ... |
|
||||
| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:55:27:55:38 | fgets output argument | semmle.label | fgets output argument |
|
||||
| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | (const char *)... | semmle.label | (const char *)... |
|
||||
| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | data | semmle.label | data |
|
||||
| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | data | semmle.label | data |
|
||||
| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | data indirection | semmle.label | data indirection |
|
||||
subpaths
|
||||
#select
|
||||
| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | data | CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:55:27:55:38 | ... + ... | CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | data | This argument to a file access function is derived from $@ and then passed to fopen(filename). | CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:55:27:55:38 | ... + ... | user input (fgets) |
|
||||
| CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | data | CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:55:27:55:38 | fgets output argument | CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:77:23:77:26 | data indirection | This argument to a file access function is derived from $@ and then passed to fopen(filename). | CWE23_Relative_Path_Traversal__char_console_fopen_11.cpp:55:27:55:38 | ... + ... | user input (fgets) |
|
||||
|
||||
@@ -1,19 +1,24 @@
|
||||
edges
|
||||
| test.c:9:23:9:26 | argv | test.c:17:11:17:18 | (const char *)... |
|
||||
| test.c:9:23:9:26 | argv | test.c:17:11:17:18 | (const char *)... |
|
||||
| test.c:9:23:9:26 | argv | test.c:17:11:17:18 | fileName |
|
||||
| test.c:9:23:9:26 | argv | test.c:17:11:17:18 | fileName |
|
||||
| test.c:9:23:9:26 | argv | test.c:17:11:17:18 | fileName |
|
||||
| test.c:9:23:9:26 | argv | test.c:17:11:17:18 | fileName |
|
||||
| test.c:9:23:9:26 | argv | test.c:17:11:17:18 | fileName indirection |
|
||||
| test.c:9:23:9:26 | argv | test.c:17:11:17:18 | fileName indirection |
|
||||
subpaths
|
||||
| test.c:31:22:31:25 | argv | test.c:32:11:32:18 | fileName indirection |
|
||||
| test.c:37:17:37:24 | fileName | test.c:38:11:38:18 | fileName indirection |
|
||||
| test.c:37:17:37:24 | scanf output argument | test.c:38:11:38:18 | fileName indirection |
|
||||
| test.c:43:17:43:24 | fileName | test.c:44:11:44:18 | fileName indirection |
|
||||
| test.c:43:17:43:24 | scanf output argument | test.c:44:11:44:18 | fileName indirection |
|
||||
nodes
|
||||
| test.c:9:23:9:26 | argv | semmle.label | argv |
|
||||
| test.c:9:23:9:26 | argv | semmle.label | argv |
|
||||
| test.c:17:11:17:18 | (const char *)... | semmle.label | (const char *)... |
|
||||
| test.c:17:11:17:18 | fileName | semmle.label | fileName |
|
||||
| test.c:17:11:17:18 | fileName | semmle.label | fileName |
|
||||
| test.c:17:11:17:18 | fileName indirection | semmle.label | fileName indirection |
|
||||
| test.c:31:22:31:25 | argv | semmle.label | argv |
|
||||
| test.c:32:11:32:18 | fileName indirection | semmle.label | fileName indirection |
|
||||
| test.c:37:17:37:24 | fileName | semmle.label | fileName |
|
||||
| test.c:37:17:37:24 | scanf output argument | semmle.label | scanf output argument |
|
||||
| test.c:38:11:38:18 | fileName indirection | semmle.label | fileName indirection |
|
||||
| test.c:43:17:43:24 | fileName | semmle.label | fileName |
|
||||
| test.c:43:17:43:24 | scanf output argument | semmle.label | scanf output argument |
|
||||
| test.c:44:11:44:18 | fileName indirection | semmle.label | fileName indirection |
|
||||
subpaths
|
||||
#select
|
||||
| test.c:17:11:17:18 | fileName | test.c:9:23:9:26 | argv | test.c:17:11:17:18 | fileName | This argument to a file access function is derived from $@ and then passed to fopen(filename). | test.c:9:23:9:26 | argv | user input (argv) |
|
||||
| test.c:17:11:17:18 | fileName | test.c:9:23:9:26 | argv | test.c:17:11:17:18 | fileName indirection | This argument to a file access function is derived from $@ and then passed to fopen(filename). | test.c:9:23:9:26 | argv | user input (argv) |
|
||||
| test.c:32:11:32:18 | fileName | test.c:31:22:31:25 | argv | test.c:32:11:32:18 | fileName indirection | This argument to a file access function is derived from $@ and then passed to fopen(filename). | test.c:31:22:31:25 | argv | user input (argv) |
|
||||
| test.c:38:11:38:18 | fileName | test.c:37:17:37:24 | fileName | test.c:38:11:38:18 | fileName indirection | This argument to a file access function is derived from $@ and then passed to fopen(filename). | test.c:37:17:37:24 | fileName | user input (scanf) |
|
||||
| test.c:44:11:44:18 | fileName | test.c:43:17:43:24 | fileName | test.c:44:11:44:18 | fileName indirection | This argument to a file access function is derived from $@ and then passed to fopen(filename). | test.c:43:17:43:24 | fileName | user input (scanf) |
|
||||
|
||||
@@ -11,3 +11,7 @@ FILE *fopen(const char *filename, const char *mode);
|
||||
int sprintf(char *s, const char *format, ...);
|
||||
size_t strlen(const char *s);
|
||||
char *strncat(char *s1, const char *s2, size_t n);
|
||||
int scanf(const char *format, ...);
|
||||
void *malloc(size_t size);
|
||||
double strtod(const char *ptr, char **endptr);
|
||||
char *getenv(const char *name);
|
||||
|
||||
@@ -26,5 +26,38 @@ int main(int argc, char** argv) {
|
||||
strncat(fileName+len, fixed, FILENAME_MAX-len-1);
|
||||
fopen(fileName, "wb+");
|
||||
}
|
||||
|
||||
{
|
||||
char *fileName = argv[1];
|
||||
fopen(fileName, "wb+"); // BAD
|
||||
}
|
||||
|
||||
{
|
||||
char fileName[20];
|
||||
scanf("%s", fileName);
|
||||
fopen(fileName, "wb+"); // BAD
|
||||
}
|
||||
|
||||
{
|
||||
char *fileName = (char*)malloc(20 * sizeof(char));
|
||||
scanf("%s", fileName);
|
||||
fopen(fileName, "wb+"); // BAD
|
||||
}
|
||||
|
||||
{
|
||||
char *aNumber = getenv("A_NUMBER");
|
||||
double number = strtod(aNumber, 0);
|
||||
char fileName[20];
|
||||
sprintf(fileName, "/foo/%f", number);
|
||||
fopen(fileName, "wb+"); // GOOD
|
||||
}
|
||||
|
||||
{
|
||||
void read(const char *fileName);
|
||||
read(argv[1]); // BAD [NOT DETECTED]
|
||||
}
|
||||
}
|
||||
|
||||
void read(char *fileName) {
|
||||
fopen(fileName, "wb+");
|
||||
}
|
||||
|
||||
@@ -332,4 +332,13 @@ void ptr_diff_case() {
|
||||
char* admin_begin_pos = strstr(user, "ADMIN");
|
||||
int offset = admin_begin_pos ? user - admin_begin_pos : 0;
|
||||
malloc(offset); // GOOD
|
||||
}
|
||||
}
|
||||
|
||||
void equality_barrier() {
|
||||
int size1 = atoi(getenv("USER"));
|
||||
int size2 = atoi(getenv("USER"));
|
||||
|
||||
if (size1 == size2) {
|
||||
int* a = (int*)malloc(size1 * sizeof(int)); // GOOD
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,5 +95,12 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
}
|
||||
|
||||
// GOOD: check the user input first
|
||||
int maxConnections3 = atoi(argv[1]);
|
||||
int maxConnections4 = atoi(argv[1]);
|
||||
if (maxConnections3 == maxConnections4) {
|
||||
startServer(maxConnections3 * 1000);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## 1.3.5
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 1.3.4
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
## 1.3.5
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 1.3.4
|
||||
lastReleaseVersion: 1.3.5
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user