mirror of
https://github.com/github/codeql.git
synced 2026-05-27 01:21:23 +02:00
Compare commits
603 Commits
redsun82/r
...
js/shared-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7e4fbe2f14 | ||
|
|
942ba189f7 | ||
|
|
f8dc7eb25b | ||
|
|
4a6030c592 | ||
|
|
cd6ebb103e | ||
|
|
dc2f39c399 | ||
|
|
de5e6ddeed | ||
|
|
c204527c08 | ||
|
|
33e8bd5032 | ||
|
|
3acd4814de | ||
|
|
b392391138 | ||
|
|
c4a7abda0a | ||
|
|
d6b8d42936 | ||
|
|
290a1043b1 | ||
|
|
023f48ff1c | ||
|
|
508c7e6e85 | ||
|
|
be939dca29 | ||
|
|
00688ebd79 | ||
|
|
09fd27af80 | ||
|
|
927d359cfa | ||
|
|
b5b8af3aa2 | ||
|
|
a6ec51a951 | ||
|
|
71959f5faa | ||
|
|
3a63dbcd5d | ||
|
|
4ffe70dd3b | ||
|
|
87b9e6001d | ||
|
|
ef2215dd53 | ||
|
|
049fab4c72 | ||
|
|
66b2b5df8d | ||
|
|
ccd3681f83 | ||
|
|
025a67384f | ||
|
|
0b2b341283 | ||
|
|
b7d1da8741 | ||
|
|
dfb34832fd | ||
|
|
2cc6ffbd28 | ||
|
|
c1e21974c6 | ||
|
|
5ed0222b1a | ||
|
|
f351558547 | ||
|
|
9b6f39c1fe | ||
|
|
eea7804b62 | ||
|
|
d8c301a96b | ||
|
|
1ef5b595ae | ||
|
|
bd9f656be2 | ||
|
|
a91c1dc715 | ||
|
|
729efff6a4 | ||
|
|
23e6a825aa | ||
|
|
029e2604a3 | ||
|
|
c13e173681 | ||
|
|
93972fcb2e | ||
|
|
8e7eedc172 | ||
|
|
3928efe05f | ||
|
|
8efd870192 | ||
|
|
d8c05b5388 | ||
|
|
ee87d4c948 | ||
|
|
e34fbc8bd1 | ||
|
|
8340841d54 | ||
|
|
950ae44d03 | ||
|
|
132dbd7517 | ||
|
|
f9d739c173 | ||
|
|
fa5cc90167 | ||
|
|
a3ef0b94b9 | ||
|
|
d6246707e4 | ||
|
|
402d4e11c4 | ||
|
|
9a80c403a0 | ||
|
|
360398481b | ||
|
|
e5ae7e0231 | ||
|
|
947b785d47 | ||
|
|
0b2914ff13 | ||
|
|
db00dad033 | ||
|
|
cf6d166d29 | ||
|
|
079294e55f | ||
|
|
ac6da6c2b1 | ||
|
|
d993c888b1 | ||
|
|
69b361ae70 | ||
|
|
73af3f3536 | ||
|
|
ebe596f227 | ||
|
|
d83ddfabaa | ||
|
|
a398599bfb | ||
|
|
c951a29e2a | ||
|
|
ddd05b5d1b | ||
|
|
9f2b436d35 | ||
|
|
4975e7b739 | ||
|
|
913357b70d | ||
|
|
5a90b25c45 | ||
|
|
5327847744 | ||
|
|
3bdfdd0573 | ||
|
|
404dd33498 | ||
|
|
6f73aa552d | ||
|
|
20dfbdc5cc | ||
|
|
da3fcda4fc | ||
|
|
4c4a8d7619 | ||
|
|
f2968f4e14 | ||
|
|
cad4f39aee | ||
|
|
0edb30638a | ||
|
|
defbbb2a24 | ||
|
|
aab3428bc7 | ||
|
|
df0375103c | ||
|
|
aabcc108dd | ||
|
|
2d16b5276d | ||
|
|
129388c78a | ||
|
|
4ee60138b7 | ||
|
|
820f81fc10 | ||
|
|
a9e89ed8e3 | ||
|
|
bcc1669f4c | ||
|
|
4e25036cdc | ||
|
|
d381ab1260 | ||
|
|
d6964ff755 | ||
|
|
2112ecc44d | ||
|
|
dc3d7a0159 | ||
|
|
42a7208704 | ||
|
|
d9a43dbd85 | ||
|
|
8907252814 | ||
|
|
3573f0b065 | ||
|
|
355f7cdd54 | ||
|
|
c38e3a23eb | ||
|
|
8e8de5cf23 | ||
|
|
daddff0dc6 | ||
|
|
15d999a9dc | ||
|
|
5f42a715f6 | ||
|
|
14ca1c134b | ||
|
|
12289d4c39 | ||
|
|
114d4a141a | ||
|
|
3cf14d8506 | ||
|
|
cca980298f | ||
|
|
a8fdd759f9 | ||
|
|
68e2f27180 | ||
|
|
8e37a5cd55 | ||
|
|
0a967325e7 | ||
|
|
40ea5f582c | ||
|
|
a53d294d91 | ||
|
|
97b78e752b | ||
|
|
f8abc5afee | ||
|
|
889100a243 | ||
|
|
afdbf2c3c6 | ||
|
|
736388809d | ||
|
|
f9c0ba3826 | ||
|
|
815581dc11 | ||
|
|
5aa1242117 | ||
|
|
0eb543e0a9 | ||
|
|
8efdc2df7b | ||
|
|
cba7b98f7a | ||
|
|
77f8e8ef4e | ||
|
|
38c9023dd9 | ||
|
|
0cd01cb96f | ||
|
|
0802107d9a | ||
|
|
66eb458134 | ||
|
|
6e7c5a3707 | ||
|
|
be617cee4a | ||
|
|
703cad9e95 | ||
|
|
2a2a4d2b67 | ||
|
|
d1694013ff | ||
|
|
8fe39bdd38 | ||
|
|
71a6a47713 | ||
|
|
f6d0835c64 | ||
|
|
ef833de60e | ||
|
|
e2b2d1c9ab | ||
|
|
712c69ebc8 | ||
|
|
f8ff504f5c | ||
|
|
b8d652c5b2 | ||
|
|
3f0d0e3a05 | ||
|
|
b3461989b1 | ||
|
|
0d79c7141c | ||
|
|
62c17d3f4e | ||
|
|
f620191da4 | ||
|
|
2ae7386775 | ||
|
|
2ef652da2c | ||
|
|
21494fbdff | ||
|
|
a574ff1669 | ||
|
|
08d25c122d | ||
|
|
75ab4856b8 | ||
|
|
e6680dec8f | ||
|
|
0ce1fe767d | ||
|
|
04a3a6707f | ||
|
|
834d35bc42 | ||
|
|
871bc3b84a | ||
|
|
f5a6485ef2 | ||
|
|
72e522631d | ||
|
|
7e162f5451 | ||
|
|
4f839070a0 | ||
|
|
8887ca1722 | ||
|
|
1832e93766 | ||
|
|
4d7401a074 | ||
|
|
3548544970 | ||
|
|
a568d8c086 | ||
|
|
f758b67d30 | ||
|
|
249104b8ae | ||
|
|
13ee597848 | ||
|
|
988fa9c0ef | ||
|
|
0b1e859e70 | ||
|
|
c2abb0fbd0 | ||
|
|
82682d9a62 | ||
|
|
bc7753de29 | ||
|
|
0cd2e3f9eb | ||
|
|
071189a9e9 | ||
|
|
e1aff15f29 | ||
|
|
27e61a1f3d | ||
|
|
89463d73f5 | ||
|
|
935e1c065a | ||
|
|
89849fae87 | ||
|
|
5e27257405 | ||
|
|
054558d7b5 | ||
|
|
8bca66493f | ||
|
|
404b0f24f2 | ||
|
|
422c089a39 | ||
|
|
628f60d2e3 | ||
|
|
2db89c1b02 | ||
|
|
2722c45737 | ||
|
|
103a6ea8a6 | ||
|
|
02c5e49de8 | ||
|
|
1f6335f9ba | ||
|
|
3319870d00 | ||
|
|
32f020ee6f | ||
|
|
cab8a40d00 | ||
|
|
9c6b6981e2 | ||
|
|
2f0c80a98b | ||
|
|
440cbb7f0a | ||
|
|
6349903110 | ||
|
|
e34064e3b5 | ||
|
|
df12f255ac | ||
|
|
66d6bda716 | ||
|
|
805fd0b46e | ||
|
|
8818fcc207 | ||
|
|
c94a01e6b6 | ||
|
|
bf62582f53 | ||
|
|
82d61e4194 | ||
|
|
c2e9dca1de | ||
|
|
f073f3b791 | ||
|
|
65da9b41b5 | ||
|
|
b4bd8e701c | ||
|
|
930a7b6e28 | ||
|
|
7a77432024 | ||
|
|
1ac7591faf | ||
|
|
9dad2d62d7 | ||
|
|
ce00bd2cc9 | ||
|
|
4e62a512c5 | ||
|
|
84820adf3c | ||
|
|
948d21ca07 | ||
|
|
dcdb2e5133 | ||
|
|
b7dd455aff | ||
|
|
d52bc971b8 | ||
|
|
d1c9e47d23 | ||
|
|
01669908f2 | ||
|
|
80a5a5909e | ||
|
|
d2daec4c66 | ||
|
|
023dcce400 | ||
|
|
37676f41aa | ||
|
|
7f2eae0966 | ||
|
|
7acc5689cf | ||
|
|
5ed362f7d6 | ||
|
|
33b7ba41ca | ||
|
|
80ee372ddf | ||
|
|
637baabe37 | ||
|
|
2fb108419c | ||
|
|
1e9e57e46e | ||
|
|
52ba91a7f8 | ||
|
|
1243188825 | ||
|
|
18b39460f5 | ||
|
|
d3e70c1e97 | ||
|
|
1b85feb1fa | ||
|
|
d557c7689c | ||
|
|
1efef2ca3c | ||
|
|
ad52b71922 | ||
|
|
c0997c28cb | ||
|
|
4473e6d977 | ||
|
|
cb874945bf | ||
|
|
bd94fe1574 | ||
|
|
e05e077b33 | ||
|
|
16b08b74eb | ||
|
|
958602e43e | ||
|
|
e784813c3b | ||
|
|
67fdd864c9 | ||
|
|
81af9a1658 | ||
|
|
12370e9210 | ||
|
|
0ebe8bdd91 | ||
|
|
d31499d727 | ||
|
|
8dc0505f84 | ||
|
|
c3c003b275 | ||
|
|
9fc99d6f9d | ||
|
|
d626e79ed3 | ||
|
|
992c144559 | ||
|
|
beaacf96b3 | ||
|
|
3fca27bee2 | ||
|
|
ed0af958a9 | ||
|
|
3b663bd2f6 | ||
|
|
211b42d0ce | ||
|
|
9e600424cc | ||
|
|
78e961cef3 | ||
|
|
81e74d8bb5 | ||
|
|
7363b578b1 | ||
|
|
a258489551 | ||
|
|
443987b484 | ||
|
|
12e316b99d | ||
|
|
e2e91ac7d9 | ||
|
|
72daa980a0 | ||
|
|
5d2ce172eb | ||
|
|
6cbe04dcb7 | ||
|
|
341bacfe55 | ||
|
|
1cd00a118c | ||
|
|
5e4c0906d8 | ||
|
|
7ba6995854 | ||
|
|
1df69ec1d2 | ||
|
|
0e4e0f4fdd | ||
|
|
74ab346348 | ||
|
|
2712bf821a | ||
|
|
bc04131c72 | ||
|
|
e1bed42481 | ||
|
|
cf90c83604 | ||
|
|
3b09bc548e | ||
|
|
3ea1134cc1 | ||
|
|
3fcf4ef7a1 | ||
|
|
e4f7560bcd | ||
|
|
15fc450a9e | ||
|
|
da696817a3 | ||
|
|
133b016c7c | ||
|
|
e87e543850 | ||
|
|
7790f68fe2 | ||
|
|
3b34cd72f2 | ||
|
|
24983a5836 | ||
|
|
87454a4f11 | ||
|
|
0ddb1c87f5 | ||
|
|
e0ca1b0482 | ||
|
|
3d4287b7cc | ||
|
|
013d226ae3 | ||
|
|
55d4e7e742 | ||
|
|
094112c905 | ||
|
|
fb9732a33f | ||
|
|
1da68aac73 | ||
|
|
a9a8351cce | ||
|
|
379c7ef20a | ||
|
|
92bb4b3da8 | ||
|
|
4568967a76 | ||
|
|
65a36b0b3b | ||
|
|
f65879eef1 | ||
|
|
cb5dbb919d | ||
|
|
a2d53c261b | ||
|
|
837a8be1b8 | ||
|
|
2e2181be2c | ||
|
|
3e196f83f1 | ||
|
|
aa8bd332bf | ||
|
|
371f7ef551 | ||
|
|
df42e7c527 | ||
|
|
4e7bd9ddd8 | ||
|
|
4389b5c999 | ||
|
|
34e6864fa3 | ||
|
|
ac1dd1850e | ||
|
|
5084d0260f | ||
|
|
895cb872ad | ||
|
|
079a622cf9 | ||
|
|
6a083136d7 | ||
|
|
acdc896c04 | ||
|
|
53a2a66dd0 | ||
|
|
5c7e623c47 | ||
|
|
c04f0beb8a | ||
|
|
60c3d077b2 | ||
|
|
bbb1c8c374 | ||
|
|
ed33a6e91b | ||
|
|
fa7ad03068 | ||
|
|
623dbda77d | ||
|
|
a72f79576a | ||
|
|
6c7d745a2b | ||
|
|
5d77c336fc | ||
|
|
4cdaccd22e | ||
|
|
2adaf0f935 | ||
|
|
47c519fc0a | ||
|
|
4b8ae2a4f3 | ||
|
|
7cfe3dae85 | ||
|
|
379952febc | ||
|
|
a2dd47aeb2 | ||
|
|
423fd04545 | ||
|
|
c54f5858b1 | ||
|
|
c66000d2fa | ||
|
|
2d814428d6 | ||
|
|
0a143a5f52 | ||
|
|
1a532dac29 | ||
|
|
c8bbad6c4d | ||
|
|
df64388d79 | ||
|
|
5c222f7b05 | ||
|
|
1d267efb6b | ||
|
|
e5924c1f84 | ||
|
|
14fc790617 | ||
|
|
1c730bc66e | ||
|
|
c3806a2210 | ||
|
|
90f0e07e49 | ||
|
|
ee10702e73 | ||
|
|
df0488a470 | ||
|
|
c52a4b0621 | ||
|
|
e53c0cdce7 | ||
|
|
2473274681 | ||
|
|
af7b4e3063 | ||
|
|
53efb5837b | ||
|
|
88edc06517 | ||
|
|
fc7c2c5b17 | ||
|
|
e67e89dd70 | ||
|
|
3bebd709b3 | ||
|
|
6c0c67dce4 | ||
|
|
b0ea81276b | ||
|
|
5811a3c5a6 | ||
|
|
8c4e5e8876 | ||
|
|
6b35a766a6 | ||
|
|
dd7aff555d | ||
|
|
f0d7c3a7f0 | ||
|
|
6e32f27652 | ||
|
|
6c8fb61f60 | ||
|
|
64a9598b89 | ||
|
|
505c532af7 | ||
|
|
102ca77acf | ||
|
|
ecf418b8f6 | ||
|
|
bd3fccd1a8 | ||
|
|
20df5adbaa | ||
|
|
f43a189f06 | ||
|
|
536c115c1c | ||
|
|
23d28fc098 | ||
|
|
5a2260b481 | ||
|
|
19f14622f3 | ||
|
|
2de9af2236 | ||
|
|
c408ab9e6a | ||
|
|
711a08b0d4 | ||
|
|
eff5f3b7d6 | ||
|
|
ddf6eb3a04 | ||
|
|
8ecdb5cefe | ||
|
|
82abd867a0 | ||
|
|
e5bc8db2f0 | ||
|
|
bb1f729a3f | ||
|
|
97567f412e | ||
|
|
5e7d1d5c2c | ||
|
|
fa8933eb41 | ||
|
|
ea4bc9cdbb | ||
|
|
406b080ce3 | ||
|
|
0a2050bc42 | ||
|
|
11983faccf | ||
|
|
b31f20a64e | ||
|
|
e0aae53ac7 | ||
|
|
fce2be0af3 | ||
|
|
e640154048 | ||
|
|
14e75be510 | ||
|
|
e66f27cfe3 | ||
|
|
4043bc13ab | ||
|
|
858c79e395 | ||
|
|
13a8e0fbf0 | ||
|
|
2c1aa08f79 | ||
|
|
478dd25f3e | ||
|
|
433489478d | ||
|
|
e2f3565227 | ||
|
|
b3fad7a8dc | ||
|
|
5aafd33cec | ||
|
|
76e0445af0 | ||
|
|
28fc8ba0c1 | ||
|
|
f94aa2ceec | ||
|
|
a02ab2ad88 | ||
|
|
3c7c5377ec | ||
|
|
5775fe6d6e | ||
|
|
9faf300dd0 | ||
|
|
e738b5d125 | ||
|
|
d3f5169e66 | ||
|
|
51dec79401 | ||
|
|
24bab27ffe | ||
|
|
7c5eb89491 | ||
|
|
98c79e7674 | ||
|
|
9b46c4596c | ||
|
|
bab639f23c | ||
|
|
85e8998067 | ||
|
|
2eff07f476 | ||
|
|
b5ad36686e | ||
|
|
75c915b2a3 | ||
|
|
c2f66c0f93 | ||
|
|
b304fb4337 | ||
|
|
32eddd3c07 | ||
|
|
b8a0afbb9f | ||
|
|
6c9f4a10ac | ||
|
|
e5946bf43b | ||
|
|
771519bbc5 | ||
|
|
2364bd84e0 | ||
|
|
98d1bb3826 | ||
|
|
81bd292a16 | ||
|
|
dd8a24c6c0 | ||
|
|
458f0a077c | ||
|
|
0d10aba67d | ||
|
|
50aace3fa3 | ||
|
|
9372f7993d | ||
|
|
995df41532 | ||
|
|
3983530983 | ||
|
|
b9344134d3 | ||
|
|
d2053445a7 | ||
|
|
2eec47b52c | ||
|
|
644f9683b1 | ||
|
|
a2d4a03c0e | ||
|
|
6600fe9d51 | ||
|
|
09892279e6 | ||
|
|
466ffdf8f5 | ||
|
|
09b0ba0c1f | ||
|
|
92812eee78 | ||
|
|
af05789cbf | ||
|
|
c652470e2f | ||
|
|
1a95961bac | ||
|
|
9a15a557b4 | ||
|
|
ff086377cb | ||
|
|
d35959a098 | ||
|
|
43be45207d | ||
|
|
c55300d4b0 | ||
|
|
b8847dbc5d | ||
|
|
c2d170b4fd | ||
|
|
03f8c0fc5e | ||
|
|
83095535f9 | ||
|
|
ba9edb4e54 | ||
|
|
d08e4504ff | ||
|
|
6e3f4bd7d8 | ||
|
|
7f4d42ddcd | ||
|
|
758f42495c | ||
|
|
32022ccbda | ||
|
|
5af608c937 | ||
|
|
25962a9ba6 | ||
|
|
51624c02a2 | ||
|
|
63343b1ba4 | ||
|
|
d446444667 | ||
|
|
06835a800c | ||
|
|
4af7694309 | ||
|
|
b9bd0520e2 | ||
|
|
dcc73a7f90 | ||
|
|
2400af4bc3 | ||
|
|
e1fae3d16d | ||
|
|
fd98b2546d | ||
|
|
cd1a1e25ae | ||
|
|
99f63b1cfa | ||
|
|
8c001916b6 | ||
|
|
e3ab5bdd16 | ||
|
|
9128722627 | ||
|
|
8715c1b324 | ||
|
|
bc88f50a5f | ||
|
|
4bac90252c | ||
|
|
f4d62c3225 | ||
|
|
2935aac559 | ||
|
|
8e95a90d03 | ||
|
|
abd937a49d | ||
|
|
d324e554f3 | ||
|
|
30f1fbc10d | ||
|
|
f14303acea | ||
|
|
2296a273c4 | ||
|
|
85617c292e | ||
|
|
395f52303c | ||
|
|
7a1aead831 | ||
|
|
e9189f965f | ||
|
|
ae680e747b | ||
|
|
40d68cb4dc | ||
|
|
b8a6f81669 | ||
|
|
a5c221fcfc | ||
|
|
adf7d5409d | ||
|
|
f1f45927b1 | ||
|
|
81d2721248 | ||
|
|
46fd727a55 | ||
|
|
92816b1c9a | ||
|
|
b2216627be | ||
|
|
d7b4e0c206 | ||
|
|
cf5450dbd5 | ||
|
|
5f05232e02 | ||
|
|
46b90e51fc | ||
|
|
e091fdefa4 | ||
|
|
2818fa62d6 | ||
|
|
547a8a958a | ||
|
|
65e9706c8e | ||
|
|
fcfab5238e | ||
|
|
17233a6749 | ||
|
|
ccd6d3dcd7 | ||
|
|
449ec72dbe | ||
|
|
aa5a2836f5 | ||
|
|
bc68b6a7f8 | ||
|
|
26f7f94246 | ||
|
|
c924b4a220 | ||
|
|
1ed3235639 | ||
|
|
277292e3b9 | ||
|
|
06fd9c2359 | ||
|
|
46e4cdc623 | ||
|
|
7bcf8b858b | ||
|
|
16df2c31bb | ||
|
|
3ef478669b | ||
|
|
9fef8803ed | ||
|
|
e31ae3a1bf | ||
|
|
0c2e52baba | ||
|
|
da3a0de814 | ||
|
|
f0c2afe39e | ||
|
|
5054c43b18 | ||
|
|
4319b07798 | ||
|
|
a31e251529 | ||
|
|
46fec8ea7e | ||
|
|
3f20d71a9b | ||
|
|
6037ff553c | ||
|
|
27c7d5004a | ||
|
|
1afe06e3a5 | ||
|
|
c24a0e00f5 | ||
|
|
5bccc652c8 | ||
|
|
293899d648 | ||
|
|
32070abb27 | ||
|
|
60101f5e6a | ||
|
|
8dc0800526 | ||
|
|
f316da78d2 | ||
|
|
760873c01c | ||
|
|
3455463e71 | ||
|
|
c839822eb9 | ||
|
|
01952f17bf | ||
|
|
21300eef4c | ||
|
|
b499c6075a | ||
|
|
79e7aae9f6 | ||
|
|
51ef0e5836 | ||
|
|
60b179bda2 |
422
Cargo.lock
generated
422
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
19
MODULE.bazel
19
MODULE.bazel
@@ -37,7 +37,7 @@ bazel_dep(name = "buildifier_prebuilt", version = "6.4.0", dev_dependency = True
|
||||
# the versions there are canonical, the versions here are used for CI in github/codeql, as well as for the vendoring of dependencies.
|
||||
RUST_EDITION = "2021"
|
||||
|
||||
RUST_VERSION = "1.81.0"
|
||||
RUST_VERSION = "1.82.0"
|
||||
|
||||
rust = use_extension("@rules_rust//rust:extensions.bzl", "rust")
|
||||
rust.toolchain(
|
||||
@@ -70,7 +70,22 @@ use_repo(py_deps, "vendor__anyhow-1.0.44", "vendor__cc-1.0.70", "vendor__clap-2.
|
||||
# deps for ruby+rust
|
||||
# keep in sync by running `misc/bazel/3rdparty/update_cargo_deps.sh`
|
||||
tree_sitter_extractors_deps = use_extension("//misc/bazel/3rdparty:tree_sitter_extractors_extension.bzl", "r")
|
||||
use_repo(tree_sitter_extractors_deps, "vendor__anyhow-1.0.93", "vendor__argfile-0.2.1", "vendor__chrono-0.4.38", "vendor__clap-4.5.20", "vendor__dunce-1.0.5", "vendor__encoding-0.2.33", "vendor__figment-0.10.19", "vendor__flate2-1.0.34", "vendor__glob-0.3.1", "vendor__globset-0.4.15", "vendor__itertools-0.10.5", "vendor__itertools-0.13.0", "vendor__lazy_static-1.5.0", "vendor__log-0.4.22", "vendor__num-traits-0.2.19", "vendor__num_cpus-1.16.0", "vendor__proc-macro2-1.0.89", "vendor__quote-1.0.37", "vendor__ra_ap_base_db-0.0.232", "vendor__ra_ap_cfg-0.0.232", "vendor__ra_ap_hir-0.0.232", "vendor__ra_ap_hir_def-0.0.232", "vendor__ra_ap_hir_expand-0.0.232", "vendor__ra_ap_ide_db-0.0.232", "vendor__ra_ap_intern-0.0.232", "vendor__ra_ap_load-cargo-0.0.232", "vendor__ra_ap_parser-0.0.232", "vendor__ra_ap_paths-0.0.232", "vendor__ra_ap_project_model-0.0.232", "vendor__ra_ap_span-0.0.232", "vendor__ra_ap_syntax-0.0.232", "vendor__ra_ap_vfs-0.0.232", "vendor__rand-0.8.5", "vendor__rayon-1.10.0", "vendor__regex-1.11.1", "vendor__serde-1.0.214", "vendor__serde_json-1.0.133", "vendor__serde_with-3.11.0", "vendor__stderrlog-0.6.0", "vendor__syn-2.0.87", "vendor__tracing-0.1.40", "vendor__tracing-subscriber-0.3.18", "vendor__tree-sitter-0.24.4", "vendor__tree-sitter-embedded-template-0.23.2", "vendor__tree-sitter-json-0.24.8", "vendor__tree-sitter-ql-0.23.1", "vendor__tree-sitter-ruby-0.23.1", "vendor__triomphe-0.1.14", "vendor__ungrammar-1.16.1")
|
||||
use_repo(tree_sitter_extractors_deps, "vendor__anyhow-1.0.94", "vendor__argfile-0.2.1", "vendor__chrono-0.4.39", "vendor__clap-4.5.23", "vendor__dunce-1.0.5", "vendor__either-1.13.0", "vendor__encoding-0.2.33", "vendor__figment-0.10.19", "vendor__flate2-1.0.35", "vendor__glob-0.3.1", "vendor__globset-0.4.15", "vendor__itertools-0.12.1", "vendor__itertools-0.13.0", "vendor__lazy_static-1.5.0", "vendor__log-0.4.22", "vendor__num-traits-0.2.19", "vendor__num_cpus-1.16.0", "vendor__proc-macro2-1.0.92", "vendor__quote-1.0.37", "vendor__ra_ap_base_db-0.0.248", "vendor__ra_ap_cfg-0.0.248", "vendor__ra_ap_hir-0.0.248", "vendor__ra_ap_hir_def-0.0.248", "vendor__ra_ap_hir_expand-0.0.248", "vendor__ra_ap_ide_db-0.0.248", "vendor__ra_ap_intern-0.0.248", "vendor__ra_ap_load-cargo-0.0.248", "vendor__ra_ap_parser-0.0.248", "vendor__ra_ap_paths-0.0.248", "vendor__ra_ap_project_model-0.0.248", "vendor__ra_ap_span-0.0.248", "vendor__ra_ap_stdx-0.0.248", "vendor__ra_ap_syntax-0.0.248", "vendor__ra_ap_vfs-0.0.248", "vendor__rand-0.8.5", "vendor__rayon-1.10.0", "vendor__regex-1.11.1", "vendor__serde-1.0.216", "vendor__serde_json-1.0.133", "vendor__serde_with-3.11.0", "vendor__stderrlog-0.6.0", "vendor__syn-2.0.90", "vendor__tracing-0.1.41", "vendor__tracing-subscriber-0.3.19", "vendor__tree-sitter-0.24.5", "vendor__tree-sitter-embedded-template-0.23.2", "vendor__tree-sitter-json-0.24.8", "vendor__tree-sitter-ql-0.23.1", "vendor__tree-sitter-ruby-0.23.1", "vendor__triomphe-0.1.14", "vendor__ungrammar-1.16.1")
|
||||
|
||||
http_archive = use_repo_rule("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
|
||||
|
||||
# rust-analyzer sources needed by the rust ast-generator (see `rust/ast-generator/README.md`)
|
||||
http_archive(
|
||||
name = "rust-analyzer-src",
|
||||
build_file = "//rust/ast-generator:BUILD.rust-analyzer-src.bazel",
|
||||
integrity = "sha256-jl4KJmZku+ilMLnuX2NU+qa1v10IauSiDiz23sZo360=",
|
||||
patch_args = ["-p1"],
|
||||
patches = [
|
||||
"//rust/ast-generator:patches/rust-analyzer.patch",
|
||||
],
|
||||
strip_prefix = "rust-analyzer-2024-12-16",
|
||||
url = "https://github.com/rust-lang/rust-analyzer/archive/refs/tags/2024-12-16.tar.gz",
|
||||
)
|
||||
|
||||
dotnet = use_extension("@rules_dotnet//dotnet:extensions.bzl", "dotnet")
|
||||
dotnet.toolchain(dotnet_version = "9.0.100")
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
### Deprecated APIs
|
||||
|
||||
* The `NonThrowing` class (`semmle.code.cpp.models.interfaces.NonThrowing`) has been deprecated. Please use the `NonCppThrowingFunction` class instead.
|
||||
* The `NonThrowingFunction` class (`semmle.code.cpp.models.interfaces.NonThrowing.NonThrowingFunction`) has been deprecated. Please use the `NonCppThrowingFunction` class instead.
|
||||
|
||||
## 2.1.1
|
||||
|
||||
|
||||
4
cpp/ql/lib/change-notes/2024-12-04-guard-conditions.md
Normal file
4
cpp/ql/lib/change-notes/2024-12-04-guard-conditions.md
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The `Guards` library (`semmle.code.cpp.controlflow.Guards`) has been improved to recognize more guard conditions.
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: feature
|
||||
---
|
||||
* A new class `TemplateParameterBase` was introduced, which represents C++ non-type template parameters, type template parameters, and template template parameters.
|
||||
4
cpp/ql/lib/change-notes/2024-12-17-template-parameter.md
Normal file
4
cpp/ql/lib/change-notes/2024-12-17-template-parameter.md
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: deprecated
|
||||
---
|
||||
* The `TemplateParameter` class, representing C++ type template parameters has been deprecated. Use `TypeTemplateParameter` instead.
|
||||
@@ -6,4 +6,4 @@
|
||||
|
||||
### Deprecated APIs
|
||||
|
||||
* The `NonThrowing` class (`semmle.code.cpp.models.interfaces.NonThrowing`) has been deprecated. Please use the `NonCppThrowingFunction` class instead.
|
||||
* The `NonThrowingFunction` class (`semmle.code.cpp.models.interfaces.NonThrowing.NonThrowingFunction`) has been deprecated. Please use the `NonCppThrowingFunction` class instead.
|
||||
|
||||
@@ -952,7 +952,7 @@ class ClassTemplateSpecialization extends Class {
|
||||
result.getNamespace() = this.getNamespace() and
|
||||
// It is distinguished by the fact that each of its template arguments
|
||||
// is a distinct template parameter.
|
||||
count(TemplateParameter tp | tp = result.getATemplateArgument()) =
|
||||
count(TemplateParameterBase tp | tp = result.getATemplateArgument()) =
|
||||
count(int i | exists(result.getTemplateArgument(i)))
|
||||
}
|
||||
|
||||
@@ -1006,7 +1006,7 @@ private predicate isPartialClassTemplateSpecialization(Class c) {
|
||||
*/
|
||||
|
||||
exists(Type ta | ta = c.getATemplateArgument() and ta.involvesTemplateParameter()) and
|
||||
count(TemplateParameter tp | tp = c.getATemplateArgument()) !=
|
||||
count(TemplateParameterBase tp | tp = c.getATemplateArgument()) !=
|
||||
count(int i | exists(c.getTemplateArgument(i)))
|
||||
}
|
||||
|
||||
@@ -1091,7 +1091,7 @@ class ProxyClass extends UserType {
|
||||
override Location getLocation() { result = this.getTemplateParameter().getDefinitionLocation() }
|
||||
|
||||
/** Gets the template parameter for which this is the proxy class. */
|
||||
TemplateParameter getTemplateParameter() {
|
||||
TypeTemplateParameter getTemplateParameter() {
|
||||
is_proxy_class_for(underlyingElement(this), unresolveElement(result))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,7 +187,7 @@ class Declaration extends Locatable, @declaration {
|
||||
this instanceof Parameter or
|
||||
this instanceof ProxyClass or
|
||||
this instanceof LocalVariable or
|
||||
this instanceof TemplateParameter or
|
||||
this instanceof TypeTemplateParameter or
|
||||
this.(UserType).isLocal()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ private string getScopePrefix(Declaration decl) {
|
||||
result = "(" + type.getEnclosingFunction().(DumpFunction).getIdentityString() + ")::"
|
||||
)
|
||||
or
|
||||
decl instanceof TemplateParameter and result = ""
|
||||
decl instanceof TypeTemplateParameter and result = ""
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1666,6 +1666,28 @@ class RoutineType extends Type, @routinetype {
|
||||
}
|
||||
}
|
||||
|
||||
abstract private class TemplateParameterImpl extends Locatable {
|
||||
override string getAPrimaryQlClass() { result = "TemplateParameterImpl" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ template parameter.
|
||||
*
|
||||
* In the example below, `T`, `TT`, and `I` are template parameters:
|
||||
* ```
|
||||
* template <class T, template<typename> TT, int I>
|
||||
* class C { };
|
||||
* ```
|
||||
*/
|
||||
final class TemplateParameterBase = TemplateParameterImpl;
|
||||
|
||||
/**
|
||||
* A C++ `typename` (or `class`) template parameter.
|
||||
*
|
||||
* DEPRECATED: Use `TypeTemplateParameter` instead.
|
||||
*/
|
||||
deprecated class TemplateParameter = TypeTemplateParameter;
|
||||
|
||||
/**
|
||||
* A C++ `typename` (or `class`) template parameter.
|
||||
*
|
||||
@@ -1675,12 +1697,12 @@ class RoutineType extends Type, @routinetype {
|
||||
* class C { };
|
||||
* ```
|
||||
*/
|
||||
class TemplateParameter extends UserType {
|
||||
TemplateParameter() {
|
||||
class TypeTemplateParameter extends UserType, TemplateParameterImpl {
|
||||
TypeTemplateParameter() {
|
||||
usertypes(underlyingElement(this), _, 7) or usertypes(underlyingElement(this), _, 8)
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() { result = "TemplateParameter" }
|
||||
override string getAPrimaryQlClass() { result = "TypeTemplateParameter" }
|
||||
|
||||
override predicate involvesTemplateParameter() { any() }
|
||||
}
|
||||
@@ -1695,7 +1717,7 @@ class TemplateParameter extends UserType {
|
||||
* void foo(const Container<Elem> &value) { }
|
||||
* ```
|
||||
*/
|
||||
class TemplateTemplateParameter extends TemplateParameter {
|
||||
class TemplateTemplateParameter extends TypeTemplateParameter {
|
||||
TemplateTemplateParameter() { usertypes(underlyingElement(this), _, 8) }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "TemplateTemplateParameter" }
|
||||
@@ -1707,7 +1729,7 @@ class TemplateTemplateParameter extends TemplateParameter {
|
||||
* auto val = some_typed_expr();
|
||||
* ```
|
||||
*/
|
||||
class AutoType extends TemplateParameter {
|
||||
class AutoType extends TypeTemplateParameter {
|
||||
AutoType() { usertypes(underlyingElement(this), "auto", 7) }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "AutoType" }
|
||||
|
||||
@@ -66,7 +66,7 @@ class Symbol extends DependsSource {
|
||||
not this.(TypeDeclarationEntry).getType() instanceof LocalEnum and
|
||||
not this.(TypeDeclarationEntry).getType() instanceof LocalClass and
|
||||
not this.(TypeDeclarationEntry).getType() instanceof LocalTypedefType and
|
||||
not this.(TypeDeclarationEntry).getType() instanceof TemplateParameter
|
||||
not this.(TypeDeclarationEntry).getType() instanceof TypeTemplateParameter
|
||||
or
|
||||
this instanceof NamespaceDeclarationEntry
|
||||
)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -504,7 +504,7 @@ private string getTypeNameWithoutFunctionTemplates(Function f, int n, int remain
|
||||
result = getParameterTypeWithoutTemplateArguments(templateFunction, n)
|
||||
)
|
||||
or
|
||||
exists(string mid, TemplateParameter tp, Function templateFunction |
|
||||
exists(string mid, TypeTemplateParameter tp, Function templateFunction |
|
||||
mid = getTypeNameWithoutFunctionTemplates(f, n, remaining + 1) and
|
||||
templateFunction = getFullyTemplatedFunction(f) and
|
||||
tp = templateFunction.getTemplateArgument(remaining) and
|
||||
@@ -529,7 +529,7 @@ private string getTypeNameWithoutClassTemplates(Function f, int n, int remaining
|
||||
remaining = 0 and
|
||||
result = getTypeNameWithoutFunctionTemplates(f, n, 0)
|
||||
or
|
||||
exists(string mid, TemplateParameter tp, Class template |
|
||||
exists(string mid, TypeTemplateParameter tp, Class template |
|
||||
mid = getTypeNameWithoutClassTemplates(f, n, remaining + 1) and
|
||||
isClassConstructedFrom(f.getDeclaringType(), template) and
|
||||
tp = template.getTemplateArgument(remaining) and
|
||||
|
||||
@@ -130,7 +130,7 @@ class Declaration extends @declaration {
|
||||
this instanceof Parameter or
|
||||
this instanceof ProxyClass or
|
||||
this instanceof LocalVariable or
|
||||
this instanceof TemplateParameter or
|
||||
this instanceof TypeTemplateParameter or
|
||||
this.(UserType).isLocal()
|
||||
)
|
||||
}
|
||||
@@ -226,8 +226,8 @@ class ProxyClass extends UserType {
|
||||
ProxyClass() { usertypes(this, _, 9) }
|
||||
}
|
||||
|
||||
class TemplateParameter extends UserType {
|
||||
TemplateParameter() { usertypes(this, _, 7) or usertypes(this, _, 8) }
|
||||
class TypeTemplateParameter extends UserType {
|
||||
TypeTemplateParameter() { usertypes(this, _, 7) or usertypes(this, _, 8) }
|
||||
}
|
||||
|
||||
class TemplateClass extends UserType {
|
||||
|
||||
@@ -1237,12 +1237,14 @@ module IsUnreachableInCall {
|
||||
int getValue() { result = value }
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
bindingset[right]
|
||||
pragma[inline_late]
|
||||
private predicate ensuresEq(Operand left, Operand right, int k, IRBlock block, boolean areEqual) {
|
||||
any(G::IRGuardCondition guard).ensuresEq(left, right, k, block, areEqual)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
bindingset[right]
|
||||
pragma[inline_late]
|
||||
private predicate ensuresLt(Operand left, Operand right, int k, IRBlock block, boolean areEqual) {
|
||||
any(G::IRGuardCondition guard).ensuresLt(left, right, k, block, areEqual)
|
||||
}
|
||||
|
||||
@@ -2275,7 +2275,7 @@ private predicate guardControlsPhiInput(
|
||||
*/
|
||||
signature predicate guardChecksSig(IRGuardCondition g, Expr e, boolean branch);
|
||||
|
||||
bindingset[g, n]
|
||||
bindingset[g]
|
||||
pragma[inline_late]
|
||||
private predicate controls(IRGuardCondition g, Node n, boolean edge) {
|
||||
g.controls(n.getBasicBlock(), edge)
|
||||
@@ -2288,6 +2288,15 @@ private predicate controls(IRGuardCondition g, Node n, boolean edge) {
|
||||
* in data flow and taint tracking.
|
||||
*/
|
||||
module BarrierGuard<guardChecksSig/3 guardChecks> {
|
||||
bindingset[value, n]
|
||||
pragma[inline_late]
|
||||
private predicate convertedExprHasValueNumber(ValueNumber value, Node n) {
|
||||
exists(Expr e |
|
||||
e = value.getAnInstruction().getConvertedResultExpression() and
|
||||
n.asConvertedExpr() = e
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an expression node that is safely guarded by the given guard check.
|
||||
*
|
||||
@@ -2321,9 +2330,8 @@ module BarrierGuard<guardChecksSig/3 guardChecks> {
|
||||
* NOTE: If an indirect expression is tracked, use `getAnIndirectBarrierNode` instead.
|
||||
*/
|
||||
Node getABarrierNode() {
|
||||
exists(IRGuardCondition g, Expr e, ValueNumber value, boolean edge |
|
||||
e = value.getAnInstruction().getConvertedResultExpression() and
|
||||
result.asConvertedExpr() = e and
|
||||
exists(IRGuardCondition g, ValueNumber value, boolean edge |
|
||||
convertedExprHasValueNumber(value, result) and
|
||||
guardChecks(g,
|
||||
pragma[only_bind_into](value.getAnInstruction().getConvertedResultExpression()), edge) and
|
||||
controls(g, result, edge)
|
||||
@@ -2374,6 +2382,17 @@ module BarrierGuard<guardChecksSig/3 guardChecks> {
|
||||
*/
|
||||
Node getAnIndirectBarrierNode() { result = getAnIndirectBarrierNode(_) }
|
||||
|
||||
bindingset[value, n]
|
||||
pragma[inline_late]
|
||||
private predicate indirectConvertedExprHasValueNumber(
|
||||
int indirectionIndex, ValueNumber value, Node n
|
||||
) {
|
||||
exists(Expr e |
|
||||
e = value.getAnInstruction().getConvertedResultExpression() and
|
||||
n.asIndirectConvertedExpr(indirectionIndex) = e
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an indirect expression node with indirection index `indirectionIndex` that is
|
||||
* safely guarded by the given guard check.
|
||||
@@ -2409,9 +2428,8 @@ module BarrierGuard<guardChecksSig/3 guardChecks> {
|
||||
* NOTE: If a non-indirect expression is tracked, use `getABarrierNode` instead.
|
||||
*/
|
||||
Node getAnIndirectBarrierNode(int indirectionIndex) {
|
||||
exists(IRGuardCondition g, Expr e, ValueNumber value, boolean edge |
|
||||
e = value.getAnInstruction().getConvertedResultExpression() and
|
||||
result.asIndirectConvertedExpr(indirectionIndex) = e and
|
||||
exists(IRGuardCondition g, ValueNumber value, boolean edge |
|
||||
indirectConvertedExprHasValueNumber(indirectionIndex, value, result) and
|
||||
guardChecks(g,
|
||||
pragma[only_bind_into](value.getAnInstruction().getConvertedResultExpression()), edge) and
|
||||
controls(g, result, edge)
|
||||
@@ -2450,12 +2468,20 @@ private EdgeKind getConditionalEdge(boolean branch) {
|
||||
* in data flow and taint tracking.
|
||||
*/
|
||||
module InstructionBarrierGuard<instructionGuardChecksSig/3 instructionGuardChecks> {
|
||||
bindingset[value, n]
|
||||
pragma[inline_late]
|
||||
private predicate operandHasValueNumber(ValueNumber value, Node n) {
|
||||
exists(Operand use |
|
||||
use = value.getAnInstruction().getAUse() and
|
||||
n.asOperand() = use
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets a node that is safely guarded by the given guard check. */
|
||||
Node getABarrierNode() {
|
||||
exists(IRGuardCondition g, ValueNumber value, boolean edge, Operand use |
|
||||
exists(IRGuardCondition g, ValueNumber value, boolean edge |
|
||||
instructionGuardChecks(g, pragma[only_bind_into](value.getAnInstruction()), edge) and
|
||||
use = value.getAnInstruction().getAUse() and
|
||||
result.asOperand() = use and
|
||||
operandHasValueNumber(value, result) and
|
||||
controls(g, result, edge)
|
||||
)
|
||||
or
|
||||
|
||||
@@ -51,6 +51,7 @@ class ValueNumber extends TValueNumber {
|
||||
/**
|
||||
* Gets an `Operand` whose definition is exact and has this value number.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
final Operand getAUse() { this = valueNumber(result.getDef()) }
|
||||
|
||||
final string getKind() {
|
||||
|
||||
@@ -51,6 +51,7 @@ class ValueNumber extends TValueNumber {
|
||||
/**
|
||||
* Gets an `Operand` whose definition is exact and has this value number.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
final Operand getAUse() { this = valueNumber(result.getDef()) }
|
||||
|
||||
final string getKind() {
|
||||
|
||||
@@ -51,6 +51,7 @@ class ValueNumber extends TValueNumber {
|
||||
/**
|
||||
* Gets an `Operand` whose definition is exact and has this value number.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
final Operand getAUse() { this = valueNumber(result.getDef()) }
|
||||
|
||||
final string getKind() {
|
||||
|
||||
@@ -437,6 +437,78 @@ module SignAnalysis<DeltaSig D> {
|
||||
not hasGuard(v, pos, result)
|
||||
}
|
||||
|
||||
private SemExpr posBoundGetElement(int i, SemSsaVariable v, SsaReadPosition pos) {
|
||||
result =
|
||||
rank[i + 1](SemExpr bound, SemBasicBlock b, int blockOrder, int indexOrder |
|
||||
posBound(bound, v, pos) and
|
||||
b = bound.getBasicBlock() and
|
||||
blockOrder = b.getUniqueId() and
|
||||
bound = b.getInstruction(indexOrder)
|
||||
|
|
||||
bound order by blockOrder, indexOrder
|
||||
)
|
||||
}
|
||||
|
||||
private predicate posBoundOkFromIndex(int i, SemSsaVariable v, SsaReadPosition pos) {
|
||||
i = 0 and
|
||||
posBoundOk(posBoundGetElement(i, v, pos), v, pos)
|
||||
or
|
||||
posBoundOkFromIndex(i - 1, v, pos) and
|
||||
posBoundOk(posBoundGetElement(i, v, pos), v, pos)
|
||||
}
|
||||
|
||||
private predicate posBoundGuardedSsaSignOk(SemSsaVariable v, SsaReadPosition pos) {
|
||||
posBoundOkFromIndex(max(int i | exists(posBoundGetElement(i, v, pos))), v, pos)
|
||||
}
|
||||
|
||||
private SemExpr negBoundGetElement(int i, SemSsaVariable v, SsaReadPosition pos) {
|
||||
result =
|
||||
rank[i + 1](SemExpr bound, SemBasicBlock b, int blockOrder, int indexOrder |
|
||||
negBound(bound, v, pos) and
|
||||
b = bound.getBasicBlock() and
|
||||
blockOrder = b.getUniqueId() and
|
||||
bound = b.getInstruction(indexOrder)
|
||||
|
|
||||
bound order by blockOrder, indexOrder
|
||||
)
|
||||
}
|
||||
|
||||
private predicate negBoundOkFromIndex(int i, SemSsaVariable v, SsaReadPosition pos) {
|
||||
i = 0 and
|
||||
negBoundOk(negBoundGetElement(i, v, pos), v, pos)
|
||||
or
|
||||
negBoundOkFromIndex(i - 1, v, pos) and
|
||||
negBoundOk(negBoundGetElement(i, v, pos), v, pos)
|
||||
}
|
||||
|
||||
private predicate negBoundGuardedSsaSignOk(SemSsaVariable v, SsaReadPosition pos) {
|
||||
negBoundOkFromIndex(max(int i | exists(negBoundGetElement(i, v, pos))), v, pos)
|
||||
}
|
||||
|
||||
private SemExpr zeroBoundGetElement(int i, SemSsaVariable v, SsaReadPosition pos) {
|
||||
result =
|
||||
rank[i + 1](SemExpr bound, SemBasicBlock b, int blockOrder, int indexOrder |
|
||||
zeroBound(bound, v, pos) and
|
||||
b = bound.getBasicBlock() and
|
||||
blockOrder = b.getUniqueId() and
|
||||
bound = b.getInstruction(indexOrder)
|
||||
|
|
||||
bound order by blockOrder, indexOrder
|
||||
)
|
||||
}
|
||||
|
||||
private predicate zeroBoundOkFromIndex(int i, SemSsaVariable v, SsaReadPosition pos) {
|
||||
i = 0 and
|
||||
zeroBoundOk(zeroBoundGetElement(i, v, pos), v, pos)
|
||||
or
|
||||
zeroBoundOkFromIndex(i - 1, v, pos) and
|
||||
zeroBoundOk(zeroBoundGetElement(i, v, pos), v, pos)
|
||||
}
|
||||
|
||||
private predicate zeroBoundGuardedSsaSignOk(SemSsaVariable v, SsaReadPosition pos) {
|
||||
zeroBoundOkFromIndex(max(int i | exists(zeroBoundGetElement(i, v, pos))), v, pos)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a possible sign of `v` at read position `pos`, where a guard could have
|
||||
* ruled out the sign but does not.
|
||||
@@ -444,13 +516,19 @@ module SignAnalysis<DeltaSig D> {
|
||||
*/
|
||||
private Sign guardedSsaSignOk(SemSsaVariable v, SsaReadPosition pos) {
|
||||
result = TPos() and
|
||||
forex(SemExpr bound | posBound(bound, v, pos) | posBoundOk(bound, v, pos))
|
||||
// optimised version of
|
||||
// `forex(SemExpr bound | posBound(bound, v, pos) | posBoundOk(bound, v, pos))`
|
||||
posBoundGuardedSsaSignOk(v, pos)
|
||||
or
|
||||
result = TNeg() and
|
||||
forex(SemExpr bound | negBound(bound, v, pos) | negBoundOk(bound, v, pos))
|
||||
// optimised version of
|
||||
// `forex(SemExpr bound | negBound(bound, v, pos) | negBoundOk(bound, v, pos))`
|
||||
negBoundGuardedSsaSignOk(v, pos)
|
||||
or
|
||||
result = TZero() and
|
||||
forex(SemExpr bound | zeroBound(bound, v, pos) | zeroBoundOk(bound, v, pos))
|
||||
// optimised version of
|
||||
// `forex(SemExpr bound | zeroBound(bound, v, pos) | zeroBoundOk(bound, v, pos))`
|
||||
zeroBoundGuardedSsaSignOk(v, pos)
|
||||
}
|
||||
|
||||
/** Gets a possible sign for `v` at `pos`. */
|
||||
|
||||
@@ -26,7 +26,7 @@ import cpp
|
||||
*/
|
||||
class TemplateDependentType extends Type {
|
||||
TemplateDependentType() {
|
||||
this instanceof TemplateParameter
|
||||
this instanceof TypeTemplateParameter
|
||||
or
|
||||
exists(TemplateDependentType t |
|
||||
this.refersToDirectly(t) and
|
||||
|
||||
@@ -49,7 +49,7 @@ predicate nanTest(EqualityOperation cmp) {
|
||||
pointlessSelfComparison(cmp) and
|
||||
exists(Type t | t = cmp.getLeftOperand().getUnspecifiedType() |
|
||||
t instanceof FloatingPointType or
|
||||
t instanceof TemplateParameter
|
||||
t instanceof TypeTemplateParameter
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import cpp
|
||||
|
||||
private predicate mightHaveConstMethods(Type t) {
|
||||
t instanceof Class or
|
||||
t instanceof TemplateParameter
|
||||
t instanceof TypeTemplateParameter
|
||||
}
|
||||
|
||||
predicate hasSuperfluousConstReturn(Function f) {
|
||||
|
||||
@@ -26,7 +26,7 @@ predicate functionsMissingReturnStmt(Function f, ControlFlowNode blame) {
|
||||
exists(Type returnType |
|
||||
returnType = f.getUnspecifiedType() and
|
||||
not returnType instanceof VoidType and
|
||||
not returnType instanceof TemplateParameter
|
||||
not returnType instanceof TypeTemplateParameter
|
||||
) and
|
||||
exists(ReturnStmt s |
|
||||
f.getAPredecessor() = s and
|
||||
|
||||
@@ -915,7 +915,7 @@ VacuousDestructorCall.cpp:
|
||||
# 2| [TemplateFunction,TopLevelFunction] void CallDestructor<T>(T, T*)
|
||||
# 2| <params>:
|
||||
# 2| getParameter(0): [Parameter] x
|
||||
# 2| Type = [TemplateParameter] T
|
||||
# 2| Type = [TypeTemplateParameter] T
|
||||
# 2| getParameter(1): [Parameter] y
|
||||
# 2| Type = [PointerType] T *
|
||||
# 2| getEntryPoint(): [BlockStmt] { ... }
|
||||
@@ -927,7 +927,7 @@ VacuousDestructorCall.cpp:
|
||||
# 3| Type = [UnknownType] unknown
|
||||
# 3| ValueCategory = prvalue
|
||||
# 3| getChild(-1): [VariableAccess] x
|
||||
# 3| Type = [TemplateParameter] T
|
||||
# 3| Type = [TypeTemplateParameter] T
|
||||
# 3| ValueCategory = lvalue
|
||||
# 4| getStmt(1): [ExprStmt] ExprStmt
|
||||
# 4| getExpr(): [ExprCall] call to expression
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
| test.cpp:49:12:49:12 | Load: x | test.cpp:46:22:46:22 | ValueNumberBound | -1 | true | CompareLT: ... < ... | test.cpp:48:9:48:13 | test.cpp:48:9:48:13 |
|
||||
| test.cpp:49:12:49:12 | Load: x | test.cpp:46:29:46:29 | ValueNumberBound | -2 | true | CompareLT: ... < ... | test.cpp:48:9:48:13 | test.cpp:48:9:48:13 |
|
||||
| test.cpp:54:12:54:12 | Load: x | test.cpp:46:22:46:22 | ValueNumberBound | -1 | true | CompareLT: ... < ... | test.cpp:52:7:52:11 | test.cpp:52:7:52:11 |
|
||||
| test.cpp:54:12:54:12 | Load: x | test.cpp:46:29:46:29 | ValueNumberBound | -2 | true | CompareLT: ... < ... | test.cpp:52:7:52:11 | test.cpp:52:7:52:11 |
|
||||
| test.cpp:62:10:62:13 | Load: iter | test.cpp:60:17:60:17 | ValueNumberBound | 0 | false | NoReason | file://:0:0:0:0 | file://:0:0:0:0 |
|
||||
| test.cpp:62:10:62:13 | Load: iter | test.cpp:60:17:60:17 | ValueNumberBound | 3 | true | CompareLT: ... < ... | test.cpp:61:32:61:51 | test.cpp:61:32:61:51 |
|
||||
| test.cpp:62:10:62:13 | Load: iter | test.cpp:61:39:61:51 | ValueNumberBound | -1 | true | CompareLT: ... < ... | test.cpp:61:32:61:51 | test.cpp:61:32:61:51 |
|
||||
|
||||
@@ -51,7 +51,7 @@ int test5(int x, int y, int z) {
|
||||
}
|
||||
if (x < y) {
|
||||
if (y < z) {
|
||||
sink(x); // x < z is not inferred here
|
||||
sink(x); // x < z is inferred here
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +42,8 @@ astGuards
|
||||
astGuardsCompare
|
||||
| 7 | 0 < x+0 when ... > ... is true |
|
||||
| 7 | 0 >= x+0 when ... > ... is false |
|
||||
| 7 | ... > ... != 0 when ... > ... is true |
|
||||
| 7 | ... > ... == 0 when ... > ... is false |
|
||||
| 7 | x < 0+1 when ... > ... is false |
|
||||
| 7 | x >= 0+1 when ... > ... is true |
|
||||
| 17 | 0 < x+1 when ... < ... is false |
|
||||
@@ -50,6 +52,12 @@ astGuardsCompare
|
||||
| 17 | 1 < y+0 when ... && ... is true |
|
||||
| 17 | 1 < y+0 when ... > ... is true |
|
||||
| 17 | 1 >= y+0 when ... > ... is false |
|
||||
| 17 | ... < ... != 0 when ... && ... is true |
|
||||
| 17 | ... < ... != 0 when ... < ... is true |
|
||||
| 17 | ... < ... == 0 when ... < ... is false |
|
||||
| 17 | ... > ... != 0 when ... && ... is true |
|
||||
| 17 | ... > ... != 0 when ... > ... is true |
|
||||
| 17 | ... > ... == 0 when ... > ... is false |
|
||||
| 17 | x < 0+0 when ... && ... is true |
|
||||
| 17 | x < 0+0 when ... < ... is true |
|
||||
| 17 | x >= 0+0 when ... < ... is false |
|
||||
@@ -60,30 +68,42 @@ astGuardsCompare
|
||||
| 18 | call to get == 0 when call to get is false |
|
||||
| 26 | 0 < x+0 when ... > ... is true |
|
||||
| 26 | 0 >= x+0 when ... > ... is false |
|
||||
| 26 | ... > ... != 0 when ... > ... is true |
|
||||
| 26 | ... > ... == 0 when ... > ... is false |
|
||||
| 26 | x < 0+1 when ... > ... is false |
|
||||
| 26 | x >= 0+1 when ... > ... is true |
|
||||
| 31 | - ... != x+0 when ... == ... is false |
|
||||
| 31 | - ... == x+0 when ... == ... is true |
|
||||
| 31 | ... == ... != 0 when ... == ... is true |
|
||||
| 31 | ... == ... == 0 when ... == ... is false |
|
||||
| 31 | x != -1 when ... == ... is false |
|
||||
| 31 | x != - ...+0 when ... == ... is false |
|
||||
| 31 | x == -1 when ... == ... is true |
|
||||
| 31 | x == - ...+0 when ... == ... is true |
|
||||
| 34 | 10 < j+1 when ... < ... is false |
|
||||
| 34 | 10 >= j+1 when ... < ... is true |
|
||||
| 34 | ... < ... != 0 when ... < ... is true |
|
||||
| 34 | ... < ... == 0 when ... < ... is false |
|
||||
| 34 | j < 10+0 when ... < ... is true |
|
||||
| 34 | j >= 10+0 when ... < ... is false |
|
||||
| 42 | 10 < j+1 when ... < ... is false |
|
||||
| 42 | 10 >= j+1 when ... < ... is true |
|
||||
| 42 | ... < ... != 0 when ... < ... is true |
|
||||
| 42 | ... < ... == 0 when ... < ... is false |
|
||||
| 42 | call to getABool != 0 when call to getABool is true |
|
||||
| 42 | call to getABool == 0 when call to getABool is false |
|
||||
| 42 | j < 10+0 when ... < ... is true |
|
||||
| 42 | j >= 10+0 when ... < ... is false |
|
||||
| 44 | 0 < z+0 when ... > ... is true |
|
||||
| 44 | 0 >= z+0 when ... > ... is false |
|
||||
| 44 | ... > ... != 0 when ... > ... is true |
|
||||
| 44 | ... > ... == 0 when ... > ... is false |
|
||||
| 44 | z < 0+1 when ... > ... is false |
|
||||
| 44 | z >= 0+1 when ... > ... is true |
|
||||
| 45 | 0 < y+0 when ... > ... is true |
|
||||
| 45 | 0 >= y+0 when ... > ... is false |
|
||||
| 45 | ... > ... != 0 when ... > ... is true |
|
||||
| 45 | ... > ... == 0 when ... > ... is false |
|
||||
| 45 | y < 0+1 when ... > ... is false |
|
||||
| 45 | y >= 0+1 when ... > ... is true |
|
||||
| 58 | 0 != x+0 when ... == ... is false |
|
||||
@@ -92,6 +112,12 @@ astGuardsCompare
|
||||
| 58 | 0 < y+1 when ... \|\| ... is false |
|
||||
| 58 | 0 == x+0 when ... == ... is true |
|
||||
| 58 | 0 >= y+1 when ... < ... is true |
|
||||
| 58 | ... < ... != 0 when ... < ... is true |
|
||||
| 58 | ... < ... == 0 when ... < ... is false |
|
||||
| 58 | ... < ... == 0 when ... \|\| ... is false |
|
||||
| 58 | ... == ... != 0 when ... == ... is true |
|
||||
| 58 | ... == ... == 0 when ... == ... is false |
|
||||
| 58 | ... == ... == 0 when ... \|\| ... is false |
|
||||
| 58 | x != 0 when ... == ... is false |
|
||||
| 58 | x != 0 when ... \|\| ... is false |
|
||||
| 58 | x != 0+0 when ... == ... is false |
|
||||
@@ -103,6 +129,8 @@ astGuardsCompare
|
||||
| 58 | y >= 0+0 when ... \|\| ... is false |
|
||||
| 75 | 0 != x+0 when ... == ... is false |
|
||||
| 75 | 0 == x+0 when ... == ... is true |
|
||||
| 75 | ... == ... != 0 when ... == ... is true |
|
||||
| 75 | ... == ... == 0 when ... == ... is false |
|
||||
| 75 | x != 0 when ... == ... is false |
|
||||
| 75 | x != 0+0 when ... == ... is false |
|
||||
| 75 | x == 0 when ... == ... is true |
|
||||
@@ -113,6 +141,12 @@ astGuardsCompare
|
||||
| 85 | 0 == x+0 when ... && ... is true |
|
||||
| 85 | 0 == x+0 when ... == ... is true |
|
||||
| 85 | 0 == y+0 when ... != ... is false |
|
||||
| 85 | ... != ... != 0 when ... != ... is true |
|
||||
| 85 | ... != ... != 0 when ... && ... is true |
|
||||
| 85 | ... != ... == 0 when ... != ... is false |
|
||||
| 85 | ... == ... != 0 when ... && ... is true |
|
||||
| 85 | ... == ... != 0 when ... == ... is true |
|
||||
| 85 | ... == ... == 0 when ... == ... is false |
|
||||
| 85 | x != 0 when ... == ... is false |
|
||||
| 85 | x != 0+0 when ... == ... is false |
|
||||
| 85 | x == 0 when ... && ... is true |
|
||||
@@ -127,12 +161,16 @@ astGuardsCompare
|
||||
| 85 | y == 0+0 when ... != ... is false |
|
||||
| 94 | 0 != x+0 when ... != ... is true |
|
||||
| 94 | 0 == x+0 when ... != ... is false |
|
||||
| 94 | ... != ... != 0 when ... != ... is true |
|
||||
| 94 | ... != ... == 0 when ... != ... is false |
|
||||
| 94 | x != 0 when ... != ... is true |
|
||||
| 94 | x != 0+0 when ... != ... is true |
|
||||
| 94 | x == 0 when ... != ... is false |
|
||||
| 94 | x == 0+0 when ... != ... is false |
|
||||
| 102 | 10 < j+1 when ... < ... is false |
|
||||
| 102 | 10 >= j+1 when ... < ... is true |
|
||||
| 102 | ... < ... != 0 when ... < ... is true |
|
||||
| 102 | ... < ... == 0 when ... < ... is false |
|
||||
| 102 | j < 10+0 when ... < ... is true |
|
||||
| 102 | j >= 10+0 when ... < ... is false |
|
||||
| 109 | 0 != x+0 when ... == ... is false |
|
||||
@@ -141,6 +179,12 @@ astGuardsCompare
|
||||
| 109 | 0 < y+1 when ... \|\| ... is false |
|
||||
| 109 | 0 == x+0 when ... == ... is true |
|
||||
| 109 | 0 >= y+1 when ... < ... is true |
|
||||
| 109 | ... < ... != 0 when ... < ... is true |
|
||||
| 109 | ... < ... == 0 when ... < ... is false |
|
||||
| 109 | ... < ... == 0 when ... \|\| ... is false |
|
||||
| 109 | ... == ... != 0 when ... == ... is true |
|
||||
| 109 | ... == ... == 0 when ... == ... is false |
|
||||
| 109 | ... == ... == 0 when ... \|\| ... is false |
|
||||
| 109 | x != 0 when ... == ... is false |
|
||||
| 109 | x != 0 when ... \|\| ... is false |
|
||||
| 109 | x != 0+0 when ... == ... is false |
|
||||
@@ -173,6 +217,8 @@ astGuardsCompare
|
||||
| 152 | y == 0 when y is false |
|
||||
| 156 | ... + ... != x+0 when ... == ... is false |
|
||||
| 156 | ... + ... == x+0 when ... == ... is true |
|
||||
| 156 | ... == ... != 0 when ... == ... is true |
|
||||
| 156 | ... == ... == 0 when ... == ... is false |
|
||||
| 156 | x != ... + ...+0 when ... == ... is false |
|
||||
| 156 | x != y+42 when ... == ... is false |
|
||||
| 156 | x == ... + ...+0 when ... == ... is true |
|
||||
@@ -181,6 +227,8 @@ astGuardsCompare
|
||||
| 156 | y == x+-42 when ... == ... is true |
|
||||
| 159 | ... - ... != x+0 when ... == ... is false |
|
||||
| 159 | ... - ... == x+0 when ... == ... is true |
|
||||
| 159 | ... == ... != 0 when ... == ... is true |
|
||||
| 159 | ... == ... == 0 when ... == ... is false |
|
||||
| 159 | x != ... - ...+0 when ... == ... is false |
|
||||
| 159 | x != y+-42 when ... == ... is false |
|
||||
| 159 | x == ... - ...+0 when ... == ... is true |
|
||||
@@ -189,6 +237,8 @@ astGuardsCompare
|
||||
| 159 | y == x+42 when ... == ... is true |
|
||||
| 162 | ... + ... < x+1 when ... < ... is false |
|
||||
| 162 | ... + ... >= x+1 when ... < ... is true |
|
||||
| 162 | ... < ... != 0 when ... < ... is true |
|
||||
| 162 | ... < ... == 0 when ... < ... is false |
|
||||
| 162 | x < ... + ...+0 when ... < ... is true |
|
||||
| 162 | x < y+42 when ... < ... is true |
|
||||
| 162 | x >= ... + ...+0 when ... < ... is false |
|
||||
@@ -197,6 +247,8 @@ astGuardsCompare
|
||||
| 162 | y >= x+-41 when ... < ... is true |
|
||||
| 165 | ... - ... < x+1 when ... < ... is false |
|
||||
| 165 | ... - ... >= x+1 when ... < ... is true |
|
||||
| 165 | ... < ... != 0 when ... < ... is true |
|
||||
| 165 | ... < ... == 0 when ... < ... is false |
|
||||
| 165 | x < ... - ...+0 when ... < ... is true |
|
||||
| 165 | x < y+-42 when ... < ... is true |
|
||||
| 165 | x >= ... - ...+0 when ... < ... is false |
|
||||
@@ -205,6 +257,8 @@ astGuardsCompare
|
||||
| 165 | y >= x+43 when ... < ... is true |
|
||||
| 175 | 0 != call to foo+0 when ... == ... is false |
|
||||
| 175 | 0 == call to foo+0 when ... == ... is true |
|
||||
| 175 | ... == ... != 0 when ... == ... is true |
|
||||
| 175 | ... == ... == 0 when ... == ... is false |
|
||||
| 175 | call to foo != 0 when ... == ... is false |
|
||||
| 175 | call to foo != 0+0 when ... == ... is false |
|
||||
| 175 | call to foo == 0 when ... == ... is true |
|
||||
@@ -414,10 +468,20 @@ astGuardsEnsure
|
||||
| test.c:75:9:75:14 | ... == ... | test.c:75:9:75:9 | x | == | test.c:75:14:75:14 | 0 | 0 | 75 | 77 |
|
||||
| test.c:75:9:75:14 | ... == ... | test.c:75:14:75:14 | 0 | != | test.c:75:9:75:9 | x | 0 | 78 | 79 |
|
||||
| test.c:75:9:75:14 | ... == ... | test.c:75:14:75:14 | 0 | == | test.c:75:9:75:9 | x | 0 | 75 | 77 |
|
||||
| test.c:75:9:75:14 | ... == ... | test.c:85:8:85:8 | x | != | test.c:85:13:85:13 | 0 | 0 | 78 | 79 |
|
||||
| test.c:75:9:75:14 | ... == ... | test.c:85:8:85:8 | x | == | test.c:85:13:85:13 | 0 | 0 | 75 | 77 |
|
||||
| test.c:75:9:75:14 | ... == ... | test.c:85:13:85:13 | 0 | != | test.c:85:8:85:8 | x | 0 | 78 | 79 |
|
||||
| test.c:75:9:75:14 | ... == ... | test.c:85:13:85:13 | 0 | == | test.c:85:8:85:8 | x | 0 | 75 | 77 |
|
||||
| test.c:85:8:85:13 | ... == ... | test.c:75:9:75:9 | x | == | test.c:75:14:75:14 | 0 | 0 | 85 | 85 |
|
||||
| test.c:85:8:85:13 | ... == ... | test.c:75:9:75:9 | x | == | test.c:75:14:75:14 | 0 | 0 | 86 | 86 |
|
||||
| test.c:85:8:85:13 | ... == ... | test.c:75:14:75:14 | 0 | == | test.c:75:9:75:9 | x | 0 | 85 | 85 |
|
||||
| test.c:85:8:85:13 | ... == ... | test.c:75:14:75:14 | 0 | == | test.c:75:9:75:9 | x | 0 | 86 | 86 |
|
||||
| test.c:85:8:85:13 | ... == ... | test.c:85:8:85:8 | x | == | test.c:85:13:85:13 | 0 | 0 | 85 | 85 |
|
||||
| test.c:85:8:85:13 | ... == ... | test.c:85:8:85:8 | x | == | test.c:85:13:85:13 | 0 | 0 | 86 | 86 |
|
||||
| test.c:85:8:85:13 | ... == ... | test.c:85:13:85:13 | 0 | == | test.c:85:8:85:8 | x | 0 | 85 | 85 |
|
||||
| test.c:85:8:85:13 | ... == ... | test.c:85:13:85:13 | 0 | == | test.c:85:8:85:8 | x | 0 | 86 | 86 |
|
||||
| test.c:85:8:85:23 | ... && ... | test.c:75:9:75:9 | x | == | test.c:75:14:75:14 | 0 | 0 | 86 | 86 |
|
||||
| test.c:85:8:85:23 | ... && ... | test.c:75:14:75:14 | 0 | == | test.c:75:9:75:9 | x | 0 | 86 | 86 |
|
||||
| test.c:85:8:85:23 | ... && ... | test.c:85:8:85:8 | x | == | test.c:85:13:85:13 | 0 | 0 | 86 | 86 |
|
||||
| test.c:85:8:85:23 | ... && ... | test.c:85:13:85:13 | 0 | == | test.c:85:8:85:8 | x | 0 | 86 | 86 |
|
||||
| test.c:85:8:85:23 | ... && ... | test.c:85:18:85:18 | y | != | test.c:85:23:85:23 | 0 | 0 | 86 | 86 |
|
||||
@@ -491,16 +555,81 @@ astGuardsEnsure
|
||||
| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:12:31:13 | - ... | == | test.cpp:31:7:31:7 | x | 0 | 30 | 30 |
|
||||
| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:12:31:13 | - ... | == | test.cpp:31:7:31:7 | x | 0 | 31 | 32 |
|
||||
astGuardsEnsure_const
|
||||
| test.c:7:9:7:13 | ... > ... | test.c:7:9:7:13 | ... > ... | != | 0 | 7 | 9 |
|
||||
| test.c:7:9:7:13 | ... > ... | test.c:7:9:7:13 | ... > ... | == | 0 | 10 | 11 |
|
||||
| test.c:17:8:17:12 | ... < ... | test.c:17:8:17:12 | ... < ... | != | 0 | 17 | 17 |
|
||||
| test.c:17:8:17:12 | ... < ... | test.c:17:8:17:12 | ... < ... | != | 0 | 18 | 18 |
|
||||
| test.c:17:8:17:21 | ... && ... | test.c:17:8:17:12 | ... < ... | != | 0 | 18 | 18 |
|
||||
| test.c:17:8:17:21 | ... && ... | test.c:17:17:17:21 | ... > ... | != | 0 | 18 | 18 |
|
||||
| test.c:17:17:17:21 | ... > ... | test.c:17:17:17:21 | ... > ... | != | 0 | 18 | 18 |
|
||||
| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:15 | ... > ... | != | 0 | 26 | 28 |
|
||||
| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:15 | ... > ... | == | 0 | 2 | 2 |
|
||||
| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:15 | ... > ... | == | 0 | 31 | 34 |
|
||||
| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:15 | ... > ... | == | 0 | 34 | 34 |
|
||||
| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:15 | ... > ... | == | 0 | 39 | 42 |
|
||||
| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:15 | ... > ... | == | 0 | 42 | 42 |
|
||||
| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:15 | ... > ... | == | 0 | 42 | 44 |
|
||||
| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:15 | ... > ... | == | 0 | 45 | 45 |
|
||||
| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:15 | ... > ... | == | 0 | 45 | 47 |
|
||||
| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:15 | ... > ... | == | 0 | 51 | 53 |
|
||||
| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:15 | ... > ... | == | 0 | 56 | 58 |
|
||||
| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:15 | ... > ... | == | 0 | 58 | 58 |
|
||||
| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:15 | ... > ... | == | 0 | 58 | 66 |
|
||||
| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:15 | ... > ... | == | 0 | 62 | 62 |
|
||||
| test.c:34:16:34:21 | ... < ... | test.c:34:16:34:21 | ... < ... | != | 0 | 34 | 34 |
|
||||
| test.c:34:16:34:21 | ... < ... | test.c:34:16:34:21 | ... < ... | == | 0 | 2 | 2 |
|
||||
| test.c:34:16:34:21 | ... < ... | test.c:34:16:34:21 | ... < ... | == | 0 | 39 | 42 |
|
||||
| test.c:34:16:34:21 | ... < ... | test.c:34:16:34:21 | ... < ... | == | 0 | 42 | 42 |
|
||||
| test.c:34:16:34:21 | ... < ... | test.c:34:16:34:21 | ... < ... | == | 0 | 42 | 44 |
|
||||
| test.c:34:16:34:21 | ... < ... | test.c:34:16:34:21 | ... < ... | == | 0 | 45 | 45 |
|
||||
| test.c:34:16:34:21 | ... < ... | test.c:34:16:34:21 | ... < ... | == | 0 | 45 | 47 |
|
||||
| test.c:34:16:34:21 | ... < ... | test.c:34:16:34:21 | ... < ... | == | 0 | 51 | 53 |
|
||||
| test.c:34:16:34:21 | ... < ... | test.c:34:16:34:21 | ... < ... | == | 0 | 56 | 58 |
|
||||
| test.c:34:16:34:21 | ... < ... | test.c:34:16:34:21 | ... < ... | == | 0 | 58 | 58 |
|
||||
| test.c:34:16:34:21 | ... < ... | test.c:34:16:34:21 | ... < ... | == | 0 | 58 | 66 |
|
||||
| test.c:34:16:34:21 | ... < ... | test.c:34:16:34:21 | ... < ... | == | 0 | 62 | 62 |
|
||||
| test.c:42:16:42:21 | ... < ... | test.c:42:16:42:21 | ... < ... | != | 0 | 42 | 42 |
|
||||
| test.c:42:16:42:21 | ... < ... | test.c:42:16:42:21 | ... < ... | != | 0 | 42 | 44 |
|
||||
| test.c:42:16:42:21 | ... < ... | test.c:42:16:42:21 | ... < ... | != | 0 | 45 | 45 |
|
||||
| test.c:42:16:42:21 | ... < ... | test.c:42:16:42:21 | ... < ... | != | 0 | 45 | 47 |
|
||||
| test.c:42:16:42:21 | ... < ... | test.c:42:16:42:21 | ... < ... | != | 0 | 51 | 53 |
|
||||
| test.c:44:12:44:16 | ... > ... | test.c:44:12:44:16 | ... > ... | != | 0 | 45 | 45 |
|
||||
| test.c:44:12:44:16 | ... > ... | test.c:44:12:44:16 | ... > ... | != | 0 | 45 | 47 |
|
||||
| test.c:44:12:44:16 | ... > ... | test.c:44:12:44:16 | ... > ... | == | 0 | 42 | 42 |
|
||||
| test.c:44:12:44:16 | ... > ... | test.c:44:12:44:16 | ... > ... | == | 0 | 51 | 53 |
|
||||
| test.c:45:16:45:20 | ... > ... | test.c:45:16:45:20 | ... > ... | != | 0 | 45 | 47 |
|
||||
| test.c:58:9:58:14 | ... == ... | test.c:58:9:58:9 | x | != | 0 | 58 | 58 |
|
||||
| test.c:58:9:58:14 | ... == ... | test.c:58:9:58:9 | x | != | 0 | 62 | 62 |
|
||||
| test.c:58:9:58:14 | ... == ... | test.c:58:9:58:14 | ... == ... | == | 0 | 58 | 58 |
|
||||
| test.c:58:9:58:14 | ... == ... | test.c:58:9:58:14 | ... == ... | == | 0 | 62 | 62 |
|
||||
| test.c:58:9:58:23 | ... \|\| ... | test.c:58:9:58:9 | x | != | 0 | 62 | 62 |
|
||||
| test.c:58:9:58:23 | ... \|\| ... | test.c:58:9:58:14 | ... == ... | == | 0 | 62 | 62 |
|
||||
| test.c:58:9:58:23 | ... \|\| ... | test.c:58:19:58:23 | ... < ... | == | 0 | 62 | 62 |
|
||||
| test.c:58:19:58:23 | ... < ... | test.c:58:19:58:23 | ... < ... | == | 0 | 62 | 62 |
|
||||
| test.c:75:9:75:14 | ... == ... | test.c:75:9:75:9 | x | != | 0 | 78 | 79 |
|
||||
| test.c:75:9:75:14 | ... == ... | test.c:75:9:75:9 | x | == | 0 | 75 | 77 |
|
||||
| test.c:75:9:75:14 | ... == ... | test.c:75:9:75:14 | ... == ... | != | 0 | 75 | 77 |
|
||||
| test.c:75:9:75:14 | ... == ... | test.c:75:9:75:14 | ... == ... | == | 0 | 78 | 79 |
|
||||
| test.c:75:9:75:14 | ... == ... | test.c:85:8:85:8 | x | != | 0 | 78 | 79 |
|
||||
| test.c:75:9:75:14 | ... == ... | test.c:85:8:85:8 | x | == | 0 | 75 | 77 |
|
||||
| test.c:75:9:75:14 | ... == ... | test.c:85:8:85:13 | ... == ... | != | 0 | 75 | 77 |
|
||||
| test.c:75:9:75:14 | ... == ... | test.c:85:8:85:13 | ... == ... | == | 0 | 78 | 79 |
|
||||
| test.c:85:8:85:13 | ... == ... | test.c:75:9:75:9 | x | == | 0 | 85 | 85 |
|
||||
| test.c:85:8:85:13 | ... == ... | test.c:75:9:75:9 | x | == | 0 | 86 | 86 |
|
||||
| test.c:85:8:85:13 | ... == ... | test.c:75:9:75:14 | ... == ... | != | 0 | 85 | 85 |
|
||||
| test.c:85:8:85:13 | ... == ... | test.c:75:9:75:14 | ... == ... | != | 0 | 86 | 86 |
|
||||
| test.c:85:8:85:13 | ... == ... | test.c:85:8:85:8 | x | == | 0 | 85 | 85 |
|
||||
| test.c:85:8:85:13 | ... == ... | test.c:85:8:85:8 | x | == | 0 | 86 | 86 |
|
||||
| test.c:85:8:85:13 | ... == ... | test.c:85:8:85:13 | ... == ... | != | 0 | 85 | 85 |
|
||||
| test.c:85:8:85:13 | ... == ... | test.c:85:8:85:13 | ... == ... | != | 0 | 86 | 86 |
|
||||
| test.c:85:8:85:23 | ... && ... | test.c:75:9:75:9 | x | == | 0 | 86 | 86 |
|
||||
| test.c:85:8:85:23 | ... && ... | test.c:75:9:75:14 | ... == ... | != | 0 | 86 | 86 |
|
||||
| test.c:85:8:85:23 | ... && ... | test.c:85:8:85:8 | x | == | 0 | 86 | 86 |
|
||||
| test.c:85:8:85:23 | ... && ... | test.c:85:8:85:13 | ... == ... | != | 0 | 86 | 86 |
|
||||
| test.c:85:8:85:23 | ... && ... | test.c:85:18:85:18 | y | != | 0 | 86 | 86 |
|
||||
| test.c:85:8:85:23 | ... && ... | test.c:85:18:85:23 | ... != ... | != | 0 | 86 | 86 |
|
||||
| test.c:85:18:85:23 | ... != ... | test.c:85:18:85:18 | y | != | 0 | 86 | 86 |
|
||||
| test.c:85:18:85:23 | ... != ... | test.c:85:18:85:23 | ... != ... | != | 0 | 86 | 86 |
|
||||
| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:11 | x | != | 0 | 94 | 96 |
|
||||
| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:11 | x | == | 0 | 70 | 70 |
|
||||
| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:11 | x | == | 0 | 99 | 102 |
|
||||
@@ -509,16 +638,41 @@ astGuardsEnsure_const
|
||||
| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:11 | x | == | 0 | 109 | 109 |
|
||||
| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:11 | x | == | 0 | 109 | 117 |
|
||||
| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:11 | x | == | 0 | 113 | 113 |
|
||||
| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:16 | ... != ... | != | 0 | 94 | 96 |
|
||||
| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:16 | ... != ... | == | 0 | 70 | 70 |
|
||||
| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:16 | ... != ... | == | 0 | 99 | 102 |
|
||||
| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:16 | ... != ... | == | 0 | 102 | 102 |
|
||||
| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:16 | ... != ... | == | 0 | 107 | 109 |
|
||||
| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:16 | ... != ... | == | 0 | 109 | 109 |
|
||||
| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:16 | ... != ... | == | 0 | 109 | 117 |
|
||||
| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:16 | ... != ... | == | 0 | 113 | 113 |
|
||||
| test.c:102:16:102:21 | ... < ... | test.c:102:16:102:21 | ... < ... | != | 0 | 102 | 102 |
|
||||
| test.c:102:16:102:21 | ... < ... | test.c:102:16:102:21 | ... < ... | == | 0 | 70 | 70 |
|
||||
| test.c:102:16:102:21 | ... < ... | test.c:102:16:102:21 | ... < ... | == | 0 | 107 | 109 |
|
||||
| test.c:102:16:102:21 | ... < ... | test.c:102:16:102:21 | ... < ... | == | 0 | 109 | 109 |
|
||||
| test.c:102:16:102:21 | ... < ... | test.c:102:16:102:21 | ... < ... | == | 0 | 109 | 117 |
|
||||
| test.c:102:16:102:21 | ... < ... | test.c:102:16:102:21 | ... < ... | == | 0 | 113 | 113 |
|
||||
| test.c:109:9:109:14 | ... == ... | test.c:109:9:109:9 | x | != | 0 | 109 | 109 |
|
||||
| test.c:109:9:109:14 | ... == ... | test.c:109:9:109:9 | x | != | 0 | 113 | 113 |
|
||||
| test.c:109:9:109:14 | ... == ... | test.c:109:9:109:14 | ... == ... | == | 0 | 109 | 109 |
|
||||
| test.c:109:9:109:14 | ... == ... | test.c:109:9:109:14 | ... == ... | == | 0 | 113 | 113 |
|
||||
| test.c:109:9:109:23 | ... \|\| ... | test.c:109:9:109:9 | x | != | 0 | 113 | 113 |
|
||||
| test.c:109:9:109:23 | ... \|\| ... | test.c:109:9:109:14 | ... == ... | == | 0 | 113 | 113 |
|
||||
| test.c:109:9:109:23 | ... \|\| ... | test.c:109:19:109:23 | ... < ... | == | 0 | 113 | 113 |
|
||||
| test.c:109:19:109:23 | ... < ... | test.c:109:19:109:23 | ... < ... | == | 0 | 113 | 113 |
|
||||
| test.c:126:7:126:7 | 1 | test.c:126:7:126:7 | 1 | != | 0 | 126 | 126 |
|
||||
| test.c:126:7:126:7 | 1 | test.c:126:7:126:7 | 1 | != | 0 | 126 | 128 |
|
||||
| test.c:126:7:126:7 | 1 | test.c:126:7:126:7 | 1 | != | 0 | 131 | 131 |
|
||||
| test.c:126:7:126:7 | 1 | test.c:126:7:126:7 | 1 | != | 0 | 131 | 132 |
|
||||
| test.c:126:7:126:7 | 1 | test.c:126:7:126:7 | 1 | != | 0 | 134 | 123 |
|
||||
| test.c:126:7:126:7 | 1 | test.c:127:9:127:9 | 1 | != | 0 | 126 | 126 |
|
||||
| test.c:126:7:126:7 | 1 | test.c:127:9:127:9 | 1 | != | 0 | 126 | 128 |
|
||||
| test.c:126:7:126:7 | 1 | test.c:127:9:127:9 | 1 | != | 0 | 131 | 131 |
|
||||
| test.c:126:7:126:7 | 1 | test.c:127:9:127:9 | 1 | != | 0 | 131 | 132 |
|
||||
| test.c:126:7:126:7 | 1 | test.c:127:9:127:9 | 1 | != | 0 | 134 | 123 |
|
||||
| test.c:126:7:126:28 | ... && ... | test.c:126:7:126:7 | 1 | != | 0 | 126 | 128 |
|
||||
| test.c:126:7:126:28 | ... && ... | test.c:126:12:126:26 | call to test3_condition | != | 0 | 126 | 128 |
|
||||
| test.c:126:7:126:28 | ... && ... | test.c:127:9:127:9 | 1 | != | 0 | 126 | 128 |
|
||||
| test.c:126:12:126:26 | call to test3_condition | test.c:126:12:126:26 | call to test3_condition | != | 0 | 126 | 128 |
|
||||
| test.c:131:7:131:7 | b | test.c:131:7:131:7 | b | != | 0 | 131 | 132 |
|
||||
| test.c:137:7:137:7 | 0 | test.c:137:7:137:7 | 0 | == | 0 | 142 | 136 |
|
||||
@@ -529,8 +683,14 @@ astGuardsEnsure_const
|
||||
| test.c:152:10:152:15 | ... && ... | test.c:152:10:152:10 | x | != | 0 | 151 | 152 |
|
||||
| test.c:152:10:152:15 | ... && ... | test.c:152:15:152:15 | y | != | 0 | 151 | 152 |
|
||||
| test.c:152:15:152:15 | y | test.c:152:15:152:15 | y | != | 0 | 151 | 152 |
|
||||
| test.c:156:9:156:19 | ... == ... | test.c:156:9:156:19 | ... == ... | != | 0 | 156 | 157 |
|
||||
| test.c:159:9:159:19 | ... == ... | test.c:159:9:159:19 | ... == ... | != | 0 | 159 | 160 |
|
||||
| test.c:162:9:162:18 | ... < ... | test.c:162:9:162:18 | ... < ... | != | 0 | 162 | 163 |
|
||||
| test.c:165:9:165:18 | ... < ... | test.c:165:9:165:18 | ... < ... | != | 0 | 165 | 166 |
|
||||
| test.c:175:13:175:32 | ... == ... | test.c:175:13:175:15 | call to foo | != | 0 | 175 | 175 |
|
||||
| test.c:175:13:175:32 | ... == ... | test.c:175:13:175:15 | call to foo | == | 0 | 175 | 175 |
|
||||
| test.c:175:13:175:32 | ... == ... | test.c:175:13:175:32 | ... == ... | != | 0 | 175 | 175 |
|
||||
| test.c:175:13:175:32 | ... == ... | test.c:175:13:175:32 | ... == ... | == | 0 | 175 | 175 |
|
||||
| test.c:181:9:181:9 | x | test.c:181:9:181:9 | x | != | 0 | 181 | 182 |
|
||||
| test.c:181:9:181:9 | x | test.c:181:9:181:9 | x | != | 0 | 186 | 180 |
|
||||
| test.c:181:9:181:9 | x | test.c:181:9:181:9 | x | == | 0 | 183 | 184 |
|
||||
@@ -539,6 +699,10 @@ astGuardsEnsure_const
|
||||
| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:7:31:7 | x | != | -1 | 34 | 34 |
|
||||
| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:7:31:7 | x | == | -1 | 30 | 30 |
|
||||
| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:7:31:7 | x | == | -1 | 31 | 32 |
|
||||
| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:7:31:13 | ... == ... | != | 0 | 30 | 30 |
|
||||
| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:7:31:13 | ... == ... | != | 0 | 31 | 32 |
|
||||
| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:7:31:13 | ... == ... | == | 0 | 30 | 30 |
|
||||
| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:7:31:13 | ... == ... | == | 0 | 34 | 34 |
|
||||
| test.cpp:42:13:42:20 | call to getABool | test.cpp:42:13:42:20 | call to getABool | != | 0 | 43 | 45 |
|
||||
| test.cpp:42:13:42:20 | call to getABool | test.cpp:42:13:42:20 | call to getABool | == | 0 | 53 | 53 |
|
||||
irGuards
|
||||
@@ -579,6 +743,8 @@ irGuards
|
||||
irGuardsCompare
|
||||
| 7 | 0 < x+0 when CompareGT: ... > ... is true |
|
||||
| 7 | 0 >= x+0 when CompareGT: ... > ... is false |
|
||||
| 7 | ... > ... != 0 when CompareGT: ... > ... is true |
|
||||
| 7 | ... > ... == 0 when CompareGT: ... > ... is false |
|
||||
| 7 | x < 0+1 when CompareGT: ... > ... is false |
|
||||
| 7 | x < 1 when CompareGT: ... > ... is false |
|
||||
| 7 | x >= 0+1 when CompareGT: ... > ... is true |
|
||||
@@ -587,6 +753,10 @@ irGuardsCompare
|
||||
| 17 | 0 >= x+1 when CompareLT: ... < ... is true |
|
||||
| 17 | 1 < y+0 when CompareGT: ... > ... is true |
|
||||
| 17 | 1 >= y+0 when CompareGT: ... > ... is false |
|
||||
| 17 | ... < ... != 0 when CompareLT: ... < ... is true |
|
||||
| 17 | ... < ... == 0 when CompareLT: ... < ... is false |
|
||||
| 17 | ... > ... != 0 when CompareGT: ... > ... is true |
|
||||
| 17 | ... > ... == 0 when CompareGT: ... > ... is false |
|
||||
| 17 | x < 0 when CompareLT: ... < ... is true |
|
||||
| 17 | x < 0+0 when CompareLT: ... < ... is true |
|
||||
| 17 | x >= 0 when CompareLT: ... < ... is false |
|
||||
@@ -599,24 +769,32 @@ irGuardsCompare
|
||||
| 18 | call to get == 0 when CompareNE: (bool)... is false |
|
||||
| 26 | 0 < x+0 when CompareGT: ... > ... is true |
|
||||
| 26 | 0 >= x+0 when CompareGT: ... > ... is false |
|
||||
| 26 | ... > ... != 0 when CompareGT: ... > ... is true |
|
||||
| 26 | ... > ... == 0 when CompareGT: ... > ... is false |
|
||||
| 26 | x < 0+1 when CompareGT: ... > ... is false |
|
||||
| 26 | x < 1 when CompareGT: ... > ... is false |
|
||||
| 26 | x >= 0+1 when CompareGT: ... > ... is true |
|
||||
| 26 | x >= 1 when CompareGT: ... > ... is true |
|
||||
| 31 | - ... != x+0 when CompareEQ: ... == ... is false |
|
||||
| 31 | - ... == x+0 when CompareEQ: ... == ... is true |
|
||||
| 31 | ... == ... != 0 when CompareEQ: ... == ... is true |
|
||||
| 31 | ... == ... == 0 when CompareEQ: ... == ... is false |
|
||||
| 31 | x != -1 when CompareEQ: ... == ... is false |
|
||||
| 31 | x != - ...+0 when CompareEQ: ... == ... is false |
|
||||
| 31 | x == -1 when CompareEQ: ... == ... is true |
|
||||
| 31 | x == - ...+0 when CompareEQ: ... == ... is true |
|
||||
| 34 | 10 < j+1 when CompareLT: ... < ... is false |
|
||||
| 34 | 10 >= j+1 when CompareLT: ... < ... is true |
|
||||
| 34 | ... < ... != 0 when CompareLT: ... < ... is true |
|
||||
| 34 | ... < ... == 0 when CompareLT: ... < ... is false |
|
||||
| 34 | j < 10 when CompareLT: ... < ... is true |
|
||||
| 34 | j < 10+0 when CompareLT: ... < ... is true |
|
||||
| 34 | j >= 10 when CompareLT: ... < ... is false |
|
||||
| 34 | j >= 10+0 when CompareLT: ... < ... is false |
|
||||
| 42 | 10 < j+1 when CompareLT: ... < ... is false |
|
||||
| 42 | 10 >= j+1 when CompareLT: ... < ... is true |
|
||||
| 42 | ... < ... != 0 when CompareLT: ... < ... is true |
|
||||
| 42 | ... < ... == 0 when CompareLT: ... < ... is false |
|
||||
| 42 | call to getABool != 0 when Call: call to getABool is true |
|
||||
| 42 | call to getABool == 0 when Call: call to getABool is false |
|
||||
| 42 | j < 10 when CompareLT: ... < ... is true |
|
||||
@@ -625,12 +803,16 @@ irGuardsCompare
|
||||
| 42 | j >= 10+0 when CompareLT: ... < ... is false |
|
||||
| 44 | 0 < z+0 when CompareGT: ... > ... is true |
|
||||
| 44 | 0 >= z+0 when CompareGT: ... > ... is false |
|
||||
| 44 | ... > ... != 0 when CompareGT: ... > ... is true |
|
||||
| 44 | ... > ... == 0 when CompareGT: ... > ... is false |
|
||||
| 44 | z < 0+1 when CompareGT: ... > ... is false |
|
||||
| 44 | z < 1 when CompareGT: ... > ... is false |
|
||||
| 44 | z >= 0+1 when CompareGT: ... > ... is true |
|
||||
| 44 | z >= 1 when CompareGT: ... > ... is true |
|
||||
| 45 | 0 < y+0 when CompareGT: ... > ... is true |
|
||||
| 45 | 0 >= y+0 when CompareGT: ... > ... is false |
|
||||
| 45 | ... > ... != 0 when CompareGT: ... > ... is true |
|
||||
| 45 | ... > ... == 0 when CompareGT: ... > ... is false |
|
||||
| 45 | y < 0+1 when CompareGT: ... > ... is false |
|
||||
| 45 | y < 1 when CompareGT: ... > ... is false |
|
||||
| 45 | y >= 0+1 when CompareGT: ... > ... is true |
|
||||
@@ -639,6 +821,10 @@ irGuardsCompare
|
||||
| 58 | 0 < y+1 when CompareLT: ... < ... is false |
|
||||
| 58 | 0 == x+0 when CompareEQ: ... == ... is true |
|
||||
| 58 | 0 >= y+1 when CompareLT: ... < ... is true |
|
||||
| 58 | ... < ... != 0 when CompareLT: ... < ... is true |
|
||||
| 58 | ... < ... == 0 when CompareLT: ... < ... is false |
|
||||
| 58 | ... == ... != 0 when CompareEQ: ... == ... is true |
|
||||
| 58 | ... == ... == 0 when CompareEQ: ... == ... is false |
|
||||
| 58 | x != 0 when CompareEQ: ... == ... is false |
|
||||
| 58 | x != 0+0 when CompareEQ: ... == ... is false |
|
||||
| 58 | x == 0 when CompareEQ: ... == ... is true |
|
||||
@@ -649,6 +835,8 @@ irGuardsCompare
|
||||
| 58 | y >= 0+0 when CompareLT: ... < ... is false |
|
||||
| 75 | 0 != x+0 when CompareEQ: ... == ... is false |
|
||||
| 75 | 0 == x+0 when CompareEQ: ... == ... is true |
|
||||
| 75 | ... == ... != 0 when CompareEQ: ... == ... is true |
|
||||
| 75 | ... == ... == 0 when CompareEQ: ... == ... is false |
|
||||
| 75 | x != 0 when CompareEQ: ... == ... is false |
|
||||
| 75 | x != 0+0 when CompareEQ: ... == ... is false |
|
||||
| 75 | x == 0 when CompareEQ: ... == ... is true |
|
||||
@@ -657,6 +845,10 @@ irGuardsCompare
|
||||
| 85 | 0 != y+0 when CompareNE: ... != ... is true |
|
||||
| 85 | 0 == x+0 when CompareEQ: ... == ... is true |
|
||||
| 85 | 0 == y+0 when CompareNE: ... != ... is false |
|
||||
| 85 | ... != ... != 0 when CompareNE: ... != ... is true |
|
||||
| 85 | ... != ... == 0 when CompareNE: ... != ... is false |
|
||||
| 85 | ... == ... != 0 when CompareEQ: ... == ... is true |
|
||||
| 85 | ... == ... == 0 when CompareEQ: ... == ... is false |
|
||||
| 85 | x != 0 when CompareEQ: ... == ... is false |
|
||||
| 85 | x != 0+0 when CompareEQ: ... == ... is false |
|
||||
| 85 | x == 0 when CompareEQ: ... == ... is true |
|
||||
@@ -667,12 +859,16 @@ irGuardsCompare
|
||||
| 85 | y == 0+0 when CompareNE: ... != ... is false |
|
||||
| 94 | 0 != x+0 when CompareNE: ... != ... is true |
|
||||
| 94 | 0 == x+0 when CompareNE: ... != ... is false |
|
||||
| 94 | ... != ... != 0 when CompareNE: ... != ... is true |
|
||||
| 94 | ... != ... == 0 when CompareNE: ... != ... is false |
|
||||
| 94 | x != 0 when CompareNE: ... != ... is true |
|
||||
| 94 | x != 0+0 when CompareNE: ... != ... is true |
|
||||
| 94 | x == 0 when CompareNE: ... != ... is false |
|
||||
| 94 | x == 0+0 when CompareNE: ... != ... is false |
|
||||
| 102 | 10 < j+1 when CompareLT: ... < ... is false |
|
||||
| 102 | 10 >= j+1 when CompareLT: ... < ... is true |
|
||||
| 102 | ... < ... != 0 when CompareLT: ... < ... is true |
|
||||
| 102 | ... < ... == 0 when CompareLT: ... < ... is false |
|
||||
| 102 | j < 10 when CompareLT: ... < ... is true |
|
||||
| 102 | j < 10+0 when CompareLT: ... < ... is true |
|
||||
| 102 | j >= 10 when CompareLT: ... < ... is false |
|
||||
@@ -681,6 +877,10 @@ irGuardsCompare
|
||||
| 109 | 0 < y+1 when CompareLT: ... < ... is false |
|
||||
| 109 | 0 == x+0 when CompareEQ: ... == ... is true |
|
||||
| 109 | 0 >= y+1 when CompareLT: ... < ... is true |
|
||||
| 109 | ... < ... != 0 when CompareLT: ... < ... is true |
|
||||
| 109 | ... < ... == 0 when CompareLT: ... < ... is false |
|
||||
| 109 | ... == ... != 0 when CompareEQ: ... == ... is true |
|
||||
| 109 | ... == ... == 0 when CompareEQ: ... == ... is false |
|
||||
| 109 | x != 0 when CompareEQ: ... == ... is false |
|
||||
| 109 | x != 0+0 when CompareEQ: ... == ... is false |
|
||||
| 109 | x == 0 when CompareEQ: ... == ... is true |
|
||||
@@ -708,6 +908,8 @@ irGuardsCompare
|
||||
| 152 | y == 0 when Load: y is false |
|
||||
| 156 | ... + ... != x+0 when CompareEQ: ... == ... is false |
|
||||
| 156 | ... + ... == x+0 when CompareEQ: ... == ... is true |
|
||||
| 156 | ... == ... != 0 when CompareEQ: ... == ... is true |
|
||||
| 156 | ... == ... == 0 when CompareEQ: ... == ... is false |
|
||||
| 156 | x != ... + ...+0 when CompareEQ: ... == ... is false |
|
||||
| 156 | x != y+42 when CompareEQ: ... == ... is false |
|
||||
| 156 | x == ... + ...+0 when CompareEQ: ... == ... is true |
|
||||
@@ -716,6 +918,8 @@ irGuardsCompare
|
||||
| 156 | y == x+-42 when CompareEQ: ... == ... is true |
|
||||
| 159 | ... - ... != x+0 when CompareEQ: ... == ... is false |
|
||||
| 159 | ... - ... == x+0 when CompareEQ: ... == ... is true |
|
||||
| 159 | ... == ... != 0 when CompareEQ: ... == ... is true |
|
||||
| 159 | ... == ... == 0 when CompareEQ: ... == ... is false |
|
||||
| 159 | x != ... - ...+0 when CompareEQ: ... == ... is false |
|
||||
| 159 | x != y+-42 when CompareEQ: ... == ... is false |
|
||||
| 159 | x == ... - ...+0 when CompareEQ: ... == ... is true |
|
||||
@@ -724,6 +928,8 @@ irGuardsCompare
|
||||
| 159 | y == x+42 when CompareEQ: ... == ... is true |
|
||||
| 162 | ... + ... < x+1 when CompareLT: ... < ... is false |
|
||||
| 162 | ... + ... >= x+1 when CompareLT: ... < ... is true |
|
||||
| 162 | ... < ... != 0 when CompareLT: ... < ... is true |
|
||||
| 162 | ... < ... == 0 when CompareLT: ... < ... is false |
|
||||
| 162 | x < ... + ...+0 when CompareLT: ... < ... is true |
|
||||
| 162 | x < y+42 when CompareLT: ... < ... is true |
|
||||
| 162 | x >= ... + ...+0 when CompareLT: ... < ... is false |
|
||||
@@ -732,6 +938,8 @@ irGuardsCompare
|
||||
| 162 | y >= x+-41 when CompareLT: ... < ... is true |
|
||||
| 165 | ... - ... < x+1 when CompareLT: ... < ... is false |
|
||||
| 165 | ... - ... >= x+1 when CompareLT: ... < ... is true |
|
||||
| 165 | ... < ... != 0 when CompareLT: ... < ... is true |
|
||||
| 165 | ... < ... == 0 when CompareLT: ... < ... is false |
|
||||
| 165 | x < ... - ...+0 when CompareLT: ... < ... is true |
|
||||
| 165 | x < y+-42 when CompareLT: ... < ... is true |
|
||||
| 165 | x >= ... - ...+0 when CompareLT: ... < ... is false |
|
||||
@@ -740,6 +948,8 @@ irGuardsCompare
|
||||
| 165 | y >= x+43 when CompareLT: ... < ... is true |
|
||||
| 175 | 0 != call to foo+0 when CompareEQ: ... == ... is false |
|
||||
| 175 | 0 == call to foo+0 when CompareEQ: ... == ... is true |
|
||||
| 175 | ... == ... != 0 when CompareEQ: ... == ... is true |
|
||||
| 175 | ... == ... == 0 when CompareEQ: ... == ... is false |
|
||||
| 175 | call to foo != 0 when CompareEQ: ... == ... is false |
|
||||
| 175 | call to foo != 0+0 when CompareEQ: ... == ... is false |
|
||||
| 175 | call to foo == 0 when CompareEQ: ... == ... is true |
|
||||
@@ -930,6 +1140,14 @@ irGuardsEnsure
|
||||
| test.c:75:9:75:14 | CompareEQ: ... == ... | test.c:75:9:75:9 | Load: x | == | test.c:75:14:75:14 | Constant: 0 | 0 | 76 | 76 |
|
||||
| test.c:75:9:75:14 | CompareEQ: ... == ... | test.c:75:14:75:14 | Constant: 0 | != | test.c:75:9:75:9 | Load: x | 0 | 79 | 79 |
|
||||
| test.c:75:9:75:14 | CompareEQ: ... == ... | test.c:75:14:75:14 | Constant: 0 | == | test.c:75:9:75:9 | Load: x | 0 | 76 | 76 |
|
||||
| test.c:75:9:75:14 | CompareEQ: ... == ... | test.c:85:8:85:8 | Load: x | != | test.c:85:13:85:13 | Constant: 0 | 0 | 79 | 79 |
|
||||
| test.c:75:9:75:14 | CompareEQ: ... == ... | test.c:85:8:85:8 | Load: x | == | test.c:85:13:85:13 | Constant: 0 | 0 | 76 | 76 |
|
||||
| test.c:75:9:75:14 | CompareEQ: ... == ... | test.c:85:13:85:13 | Constant: 0 | != | test.c:85:8:85:8 | Load: x | 0 | 79 | 79 |
|
||||
| test.c:75:9:75:14 | CompareEQ: ... == ... | test.c:85:13:85:13 | Constant: 0 | == | test.c:85:8:85:8 | Load: x | 0 | 76 | 76 |
|
||||
| test.c:85:8:85:13 | CompareEQ: ... == ... | test.c:75:9:75:9 | Load: x | == | test.c:75:14:75:14 | Constant: 0 | 0 | 85 | 85 |
|
||||
| test.c:85:8:85:13 | CompareEQ: ... == ... | test.c:75:9:75:9 | Load: x | == | test.c:75:14:75:14 | Constant: 0 | 0 | 86 | 86 |
|
||||
| test.c:85:8:85:13 | CompareEQ: ... == ... | test.c:75:14:75:14 | Constant: 0 | == | test.c:75:9:75:9 | Load: x | 0 | 85 | 85 |
|
||||
| test.c:85:8:85:13 | CompareEQ: ... == ... | test.c:75:14:75:14 | Constant: 0 | == | test.c:75:9:75:9 | Load: x | 0 | 86 | 86 |
|
||||
| test.c:85:8:85:13 | CompareEQ: ... == ... | test.c:85:8:85:8 | Load: x | == | test.c:85:13:85:13 | Constant: 0 | 0 | 85 | 85 |
|
||||
| test.c:85:8:85:13 | CompareEQ: ... == ... | test.c:85:8:85:8 | Load: x | == | test.c:85:13:85:13 | Constant: 0 | 0 | 86 | 86 |
|
||||
| test.c:85:8:85:13 | CompareEQ: ... == ... | test.c:85:13:85:13 | Constant: 0 | == | test.c:85:8:85:8 | Load: x | 0 | 85 | 85 |
|
||||
@@ -1003,9 +1221,14 @@ irGuardsEnsure
|
||||
irGuardsEnsure_const
|
||||
| test.c:7:9:7:13 | CompareGT: ... > ... | test.c:7:9:7:9 | Load: x | < | 1 | 11 | 11 |
|
||||
| test.c:7:9:7:13 | CompareGT: ... > ... | test.c:7:9:7:9 | Load: x | >= | 1 | 8 | 8 |
|
||||
| test.c:7:9:7:13 | CompareGT: ... > ... | test.c:7:9:7:13 | CompareGT: ... > ... | != | 0 | 8 | 8 |
|
||||
| test.c:7:9:7:13 | CompareGT: ... > ... | test.c:7:9:7:13 | CompareGT: ... > ... | == | 0 | 11 | 11 |
|
||||
| test.c:17:8:17:12 | CompareLT: ... < ... | test.c:17:8:17:8 | Load: x | < | 0 | 17 | 17 |
|
||||
| test.c:17:8:17:12 | CompareLT: ... < ... | test.c:17:8:17:8 | Load: x | < | 0 | 18 | 18 |
|
||||
| test.c:17:8:17:12 | CompareLT: ... < ... | test.c:17:8:17:12 | CompareLT: ... < ... | != | 0 | 17 | 17 |
|
||||
| test.c:17:8:17:12 | CompareLT: ... < ... | test.c:17:8:17:12 | CompareLT: ... < ... | != | 0 | 18 | 18 |
|
||||
| test.c:17:17:17:21 | CompareGT: ... > ... | test.c:17:17:17:17 | Load: y | >= | 2 | 18 | 18 |
|
||||
| test.c:17:17:17:21 | CompareGT: ... > ... | test.c:17:17:17:21 | CompareGT: ... > ... | != | 0 | 18 | 18 |
|
||||
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:11 | Load: x | < | 1 | 2 | 2 |
|
||||
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:11 | Load: x | < | 1 | 31 | 31 |
|
||||
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:11 | Load: x | < | 1 | 34 | 34 |
|
||||
@@ -1021,6 +1244,21 @@ irGuardsEnsure_const
|
||||
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:11 | Load: x | < | 1 | 59 | 59 |
|
||||
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:11 | Load: x | < | 1 | 62 | 62 |
|
||||
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:11 | Load: x | >= | 1 | 27 | 27 |
|
||||
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:15 | CompareGT: ... > ... | != | 0 | 27 | 27 |
|
||||
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:15 | CompareGT: ... > ... | == | 0 | 2 | 2 |
|
||||
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:15 | CompareGT: ... > ... | == | 0 | 31 | 31 |
|
||||
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:15 | CompareGT: ... > ... | == | 0 | 34 | 34 |
|
||||
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:15 | CompareGT: ... > ... | == | 0 | 35 | 35 |
|
||||
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:15 | CompareGT: ... > ... | == | 0 | 39 | 39 |
|
||||
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:15 | CompareGT: ... > ... | == | 0 | 42 | 42 |
|
||||
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:15 | CompareGT: ... > ... | == | 0 | 43 | 43 |
|
||||
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:15 | CompareGT: ... > ... | == | 0 | 45 | 45 |
|
||||
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:15 | CompareGT: ... > ... | == | 0 | 46 | 46 |
|
||||
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:15 | CompareGT: ... > ... | == | 0 | 52 | 52 |
|
||||
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:15 | CompareGT: ... > ... | == | 0 | 56 | 56 |
|
||||
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:15 | CompareGT: ... > ... | == | 0 | 58 | 58 |
|
||||
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:15 | CompareGT: ... > ... | == | 0 | 59 | 59 |
|
||||
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:15 | CompareGT: ... > ... | == | 0 | 62 | 62 |
|
||||
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:16:34:16 | Load: j | < | 10 | 35 | 35 |
|
||||
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:16:34:16 | Load: j | >= | 10 | 2 | 2 |
|
||||
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:16:34:16 | Load: j | >= | 10 | 39 | 39 |
|
||||
@@ -1033,22 +1271,58 @@ irGuardsEnsure_const
|
||||
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:16:34:16 | Load: j | >= | 10 | 58 | 58 |
|
||||
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:16:34:16 | Load: j | >= | 10 | 59 | 59 |
|
||||
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:16:34:16 | Load: j | >= | 10 | 62 | 62 |
|
||||
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:16:34:21 | CompareLT: ... < ... | != | 0 | 35 | 35 |
|
||||
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:16:34:21 | CompareLT: ... < ... | == | 0 | 2 | 2 |
|
||||
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:16:34:21 | CompareLT: ... < ... | == | 0 | 39 | 39 |
|
||||
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:16:34:21 | CompareLT: ... < ... | == | 0 | 42 | 42 |
|
||||
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:16:34:21 | CompareLT: ... < ... | == | 0 | 43 | 43 |
|
||||
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:16:34:21 | CompareLT: ... < ... | == | 0 | 45 | 45 |
|
||||
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:16:34:21 | CompareLT: ... < ... | == | 0 | 46 | 46 |
|
||||
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:16:34:21 | CompareLT: ... < ... | == | 0 | 52 | 52 |
|
||||
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:16:34:21 | CompareLT: ... < ... | == | 0 | 56 | 56 |
|
||||
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:16:34:21 | CompareLT: ... < ... | == | 0 | 58 | 58 |
|
||||
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:16:34:21 | CompareLT: ... < ... | == | 0 | 59 | 59 |
|
||||
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:16:34:21 | CompareLT: ... < ... | == | 0 | 62 | 62 |
|
||||
| test.c:42:16:42:21 | CompareLT: ... < ... | test.c:42:16:42:16 | Load: j | < | 10 | 43 | 43 |
|
||||
| test.c:42:16:42:21 | CompareLT: ... < ... | test.c:42:16:42:16 | Load: j | < | 10 | 45 | 45 |
|
||||
| test.c:42:16:42:21 | CompareLT: ... < ... | test.c:42:16:42:16 | Load: j | < | 10 | 46 | 46 |
|
||||
| test.c:42:16:42:21 | CompareLT: ... < ... | test.c:42:16:42:16 | Load: j | < | 10 | 52 | 52 |
|
||||
| test.c:42:16:42:21 | CompareLT: ... < ... | test.c:42:16:42:21 | CompareLT: ... < ... | != | 0 | 43 | 43 |
|
||||
| test.c:42:16:42:21 | CompareLT: ... < ... | test.c:42:16:42:21 | CompareLT: ... < ... | != | 0 | 45 | 45 |
|
||||
| test.c:42:16:42:21 | CompareLT: ... < ... | test.c:42:16:42:21 | CompareLT: ... < ... | != | 0 | 46 | 46 |
|
||||
| test.c:42:16:42:21 | CompareLT: ... < ... | test.c:42:16:42:21 | CompareLT: ... < ... | != | 0 | 52 | 52 |
|
||||
| test.c:44:12:44:16 | CompareGT: ... > ... | test.c:44:12:44:12 | Load: z | < | 1 | 52 | 52 |
|
||||
| test.c:44:12:44:16 | CompareGT: ... > ... | test.c:44:12:44:12 | Load: z | >= | 1 | 45 | 45 |
|
||||
| test.c:44:12:44:16 | CompareGT: ... > ... | test.c:44:12:44:12 | Load: z | >= | 1 | 46 | 46 |
|
||||
| test.c:44:12:44:16 | CompareGT: ... > ... | test.c:44:12:44:16 | CompareGT: ... > ... | != | 0 | 45 | 45 |
|
||||
| test.c:44:12:44:16 | CompareGT: ... > ... | test.c:44:12:44:16 | CompareGT: ... > ... | != | 0 | 46 | 46 |
|
||||
| test.c:44:12:44:16 | CompareGT: ... > ... | test.c:44:12:44:16 | CompareGT: ... > ... | == | 0 | 52 | 52 |
|
||||
| test.c:45:16:45:20 | CompareGT: ... > ... | test.c:45:16:45:16 | Load: y | >= | 1 | 46 | 46 |
|
||||
| test.c:45:16:45:20 | CompareGT: ... > ... | test.c:45:16:45:20 | CompareGT: ... > ... | != | 0 | 46 | 46 |
|
||||
| test.c:58:9:58:14 | CompareEQ: ... == ... | test.c:58:9:58:9 | Load: x | != | 0 | 58 | 58 |
|
||||
| test.c:58:9:58:14 | CompareEQ: ... == ... | test.c:58:9:58:9 | Load: x | != | 0 | 62 | 62 |
|
||||
| test.c:58:9:58:14 | CompareEQ: ... == ... | test.c:58:9:58:14 | CompareEQ: ... == ... | == | 0 | 58 | 58 |
|
||||
| test.c:58:9:58:14 | CompareEQ: ... == ... | test.c:58:9:58:14 | CompareEQ: ... == ... | == | 0 | 62 | 62 |
|
||||
| test.c:58:19:58:23 | CompareLT: ... < ... | test.c:58:19:58:19 | Load: y | >= | 0 | 62 | 62 |
|
||||
| test.c:58:19:58:23 | CompareLT: ... < ... | test.c:58:19:58:23 | CompareLT: ... < ... | == | 0 | 62 | 62 |
|
||||
| test.c:75:9:75:14 | CompareEQ: ... == ... | test.c:75:9:75:9 | Load: x | != | 0 | 79 | 79 |
|
||||
| test.c:75:9:75:14 | CompareEQ: ... == ... | test.c:75:9:75:9 | Load: x | == | 0 | 76 | 76 |
|
||||
| test.c:75:9:75:14 | CompareEQ: ... == ... | test.c:75:9:75:14 | CompareEQ: ... == ... | != | 0 | 76 | 76 |
|
||||
| test.c:75:9:75:14 | CompareEQ: ... == ... | test.c:75:9:75:14 | CompareEQ: ... == ... | == | 0 | 79 | 79 |
|
||||
| test.c:75:9:75:14 | CompareEQ: ... == ... | test.c:85:8:85:8 | Load: x | != | 0 | 79 | 79 |
|
||||
| test.c:75:9:75:14 | CompareEQ: ... == ... | test.c:85:8:85:8 | Load: x | == | 0 | 76 | 76 |
|
||||
| test.c:75:9:75:14 | CompareEQ: ... == ... | test.c:85:8:85:13 | CompareEQ: ... == ... | != | 0 | 76 | 76 |
|
||||
| test.c:75:9:75:14 | CompareEQ: ... == ... | test.c:85:8:85:13 | CompareEQ: ... == ... | == | 0 | 79 | 79 |
|
||||
| test.c:85:8:85:13 | CompareEQ: ... == ... | test.c:75:9:75:9 | Load: x | == | 0 | 85 | 85 |
|
||||
| test.c:85:8:85:13 | CompareEQ: ... == ... | test.c:75:9:75:9 | Load: x | == | 0 | 86 | 86 |
|
||||
| test.c:85:8:85:13 | CompareEQ: ... == ... | test.c:75:9:75:14 | CompareEQ: ... == ... | != | 0 | 85 | 85 |
|
||||
| test.c:85:8:85:13 | CompareEQ: ... == ... | test.c:75:9:75:14 | CompareEQ: ... == ... | != | 0 | 86 | 86 |
|
||||
| test.c:85:8:85:13 | CompareEQ: ... == ... | test.c:85:8:85:8 | Load: x | == | 0 | 85 | 85 |
|
||||
| test.c:85:8:85:13 | CompareEQ: ... == ... | test.c:85:8:85:8 | Load: x | == | 0 | 86 | 86 |
|
||||
| test.c:85:8:85:13 | CompareEQ: ... == ... | test.c:85:8:85:13 | CompareEQ: ... == ... | != | 0 | 85 | 85 |
|
||||
| test.c:85:8:85:13 | CompareEQ: ... == ... | test.c:85:8:85:13 | CompareEQ: ... == ... | != | 0 | 86 | 86 |
|
||||
| test.c:85:18:85:23 | CompareNE: ... != ... | test.c:85:18:85:18 | Load: y | != | 0 | 86 | 86 |
|
||||
| test.c:85:18:85:23 | CompareNE: ... != ... | test.c:85:18:85:23 | CompareNE: ... != ... | != | 0 | 86 | 86 |
|
||||
| test.c:94:11:94:16 | CompareNE: ... != ... | test.c:94:11:94:11 | Load: x | != | 0 | 95 | 95 |
|
||||
| test.c:94:11:94:16 | CompareNE: ... != ... | test.c:94:11:94:11 | Load: x | == | 0 | 70 | 70 |
|
||||
| test.c:94:11:94:16 | CompareNE: ... != ... | test.c:94:11:94:11 | Load: x | == | 0 | 99 | 99 |
|
||||
@@ -1058,34 +1332,78 @@ irGuardsEnsure_const
|
||||
| test.c:94:11:94:16 | CompareNE: ... != ... | test.c:94:11:94:11 | Load: x | == | 0 | 109 | 109 |
|
||||
| test.c:94:11:94:16 | CompareNE: ... != ... | test.c:94:11:94:11 | Load: x | == | 0 | 110 | 110 |
|
||||
| test.c:94:11:94:16 | CompareNE: ... != ... | test.c:94:11:94:11 | Load: x | == | 0 | 113 | 113 |
|
||||
| test.c:94:11:94:16 | CompareNE: ... != ... | test.c:94:11:94:16 | CompareNE: ... != ... | != | 0 | 95 | 95 |
|
||||
| test.c:94:11:94:16 | CompareNE: ... != ... | test.c:94:11:94:16 | CompareNE: ... != ... | == | 0 | 70 | 70 |
|
||||
| test.c:94:11:94:16 | CompareNE: ... != ... | test.c:94:11:94:16 | CompareNE: ... != ... | == | 0 | 99 | 99 |
|
||||
| test.c:94:11:94:16 | CompareNE: ... != ... | test.c:94:11:94:16 | CompareNE: ... != ... | == | 0 | 102 | 102 |
|
||||
| test.c:94:11:94:16 | CompareNE: ... != ... | test.c:94:11:94:16 | CompareNE: ... != ... | == | 0 | 103 | 103 |
|
||||
| test.c:94:11:94:16 | CompareNE: ... != ... | test.c:94:11:94:16 | CompareNE: ... != ... | == | 0 | 107 | 107 |
|
||||
| test.c:94:11:94:16 | CompareNE: ... != ... | test.c:94:11:94:16 | CompareNE: ... != ... | == | 0 | 109 | 109 |
|
||||
| test.c:94:11:94:16 | CompareNE: ... != ... | test.c:94:11:94:16 | CompareNE: ... != ... | == | 0 | 110 | 110 |
|
||||
| test.c:94:11:94:16 | CompareNE: ... != ... | test.c:94:11:94:16 | CompareNE: ... != ... | == | 0 | 113 | 113 |
|
||||
| test.c:102:16:102:21 | CompareLT: ... < ... | test.c:102:16:102:16 | Load: j | < | 10 | 103 | 103 |
|
||||
| test.c:102:16:102:21 | CompareLT: ... < ... | test.c:102:16:102:16 | Load: j | >= | 10 | 70 | 70 |
|
||||
| test.c:102:16:102:21 | CompareLT: ... < ... | test.c:102:16:102:16 | Load: j | >= | 10 | 107 | 107 |
|
||||
| test.c:102:16:102:21 | CompareLT: ... < ... | test.c:102:16:102:16 | Load: j | >= | 10 | 109 | 109 |
|
||||
| test.c:102:16:102:21 | CompareLT: ... < ... | test.c:102:16:102:16 | Load: j | >= | 10 | 110 | 110 |
|
||||
| test.c:102:16:102:21 | CompareLT: ... < ... | test.c:102:16:102:16 | Load: j | >= | 10 | 113 | 113 |
|
||||
| test.c:102:16:102:21 | CompareLT: ... < ... | test.c:102:16:102:21 | CompareLT: ... < ... | != | 0 | 103 | 103 |
|
||||
| test.c:102:16:102:21 | CompareLT: ... < ... | test.c:102:16:102:21 | CompareLT: ... < ... | == | 0 | 70 | 70 |
|
||||
| test.c:102:16:102:21 | CompareLT: ... < ... | test.c:102:16:102:21 | CompareLT: ... < ... | == | 0 | 107 | 107 |
|
||||
| test.c:102:16:102:21 | CompareLT: ... < ... | test.c:102:16:102:21 | CompareLT: ... < ... | == | 0 | 109 | 109 |
|
||||
| test.c:102:16:102:21 | CompareLT: ... < ... | test.c:102:16:102:21 | CompareLT: ... < ... | == | 0 | 110 | 110 |
|
||||
| test.c:102:16:102:21 | CompareLT: ... < ... | test.c:102:16:102:21 | CompareLT: ... < ... | == | 0 | 113 | 113 |
|
||||
| test.c:109:9:109:14 | CompareEQ: ... == ... | test.c:109:9:109:9 | Load: x | != | 0 | 109 | 109 |
|
||||
| test.c:109:9:109:14 | CompareEQ: ... == ... | test.c:109:9:109:9 | Load: x | != | 0 | 113 | 113 |
|
||||
| test.c:109:9:109:14 | CompareEQ: ... == ... | test.c:109:9:109:14 | CompareEQ: ... == ... | == | 0 | 109 | 109 |
|
||||
| test.c:109:9:109:14 | CompareEQ: ... == ... | test.c:109:9:109:14 | CompareEQ: ... == ... | == | 0 | 113 | 113 |
|
||||
| test.c:109:19:109:23 | CompareLT: ... < ... | test.c:109:19:109:19 | Load: y | >= | 0 | 113 | 113 |
|
||||
| test.c:109:19:109:23 | CompareLT: ... < ... | test.c:109:19:109:23 | CompareLT: ... < ... | == | 0 | 113 | 113 |
|
||||
| test.c:126:7:126:7 | Constant: 1 | test.c:126:7:126:7 | Constant: 1 | != | 0 | 126 | 126 |
|
||||
| test.c:126:7:126:7 | Constant: 1 | test.c:126:7:126:7 | Constant: 1 | != | 0 | 127 | 127 |
|
||||
| test.c:126:7:126:7 | Constant: 1 | test.c:126:7:126:7 | Constant: 1 | != | 0 | 131 | 131 |
|
||||
| test.c:126:7:126:7 | Constant: 1 | test.c:126:7:126:7 | Constant: 1 | != | 0 | 132 | 132 |
|
||||
| test.c:126:7:126:7 | Constant: 1 | test.c:126:7:126:7 | Constant: 1 | != | 0 | 134 | 134 |
|
||||
| test.c:126:7:126:7 | Constant: 1 | test.c:127:5:127:9 | Store: ... = ... | != | 0 | 126 | 126 |
|
||||
| test.c:126:7:126:7 | Constant: 1 | test.c:127:5:127:9 | Store: ... = ... | != | 0 | 127 | 127 |
|
||||
| test.c:126:7:126:7 | Constant: 1 | test.c:127:5:127:9 | Store: ... = ... | != | 0 | 131 | 131 |
|
||||
| test.c:126:7:126:7 | Constant: 1 | test.c:127:5:127:9 | Store: ... = ... | != | 0 | 132 | 132 |
|
||||
| test.c:126:7:126:7 | Constant: 1 | test.c:127:5:127:9 | Store: ... = ... | != | 0 | 134 | 134 |
|
||||
| test.c:126:7:126:7 | Constant: 1 | test.c:127:9:127:9 | Constant: 1 | != | 0 | 126 | 126 |
|
||||
| test.c:126:7:126:7 | Constant: 1 | test.c:127:9:127:9 | Constant: 1 | != | 0 | 127 | 127 |
|
||||
| test.c:126:7:126:7 | Constant: 1 | test.c:127:9:127:9 | Constant: 1 | != | 0 | 131 | 131 |
|
||||
| test.c:126:7:126:7 | Constant: 1 | test.c:127:9:127:9 | Constant: 1 | != | 0 | 132 | 132 |
|
||||
| test.c:126:7:126:7 | Constant: 1 | test.c:127:9:127:9 | Constant: 1 | != | 0 | 134 | 134 |
|
||||
| test.c:126:12:126:26 | Call: call to test3_condition | test.c:126:12:126:26 | Call: call to test3_condition | != | 0 | 127 | 127 |
|
||||
| test.c:131:7:131:7 | Load: b | test.c:131:7:131:7 | Load: b | != | 0 | 132 | 132 |
|
||||
| test.c:131:7:131:7 | Load: b | test.c:131:7:131:7 | Phi: b | != | 0 | 132 | 132 |
|
||||
| test.c:137:7:137:7 | Constant: 0 | test.c:137:7:137:7 | Constant: 0 | == | 0 | 142 | 142 |
|
||||
| test.c:146:7:146:8 | LogicalNot: ! ... | test.c:146:7:146:8 | LogicalNot: ! ... | != | 0 | 147 | 147 |
|
||||
| test.c:146:8:146:8 | Load: x | test.c:145:16:145:16 | InitializeParameter: x | == | 0 | 147 | 147 |
|
||||
| test.c:146:8:146:8 | Load: x | test.c:146:8:146:8 | Load: x | == | 0 | 147 | 147 |
|
||||
| test.c:152:10:152:10 | Load: x | test.c:151:16:151:16 | InitializeParameter: x | != | 0 | 152 | 152 |
|
||||
| test.c:152:10:152:10 | Load: x | test.c:152:10:152:10 | Load: x | != | 0 | 152 | 152 |
|
||||
| test.c:152:15:152:15 | Load: y | test.c:151:23:151:23 | InitializeParameter: y | != | 0 | 152 | 152 |
|
||||
| test.c:152:15:152:15 | Load: y | test.c:152:15:152:15 | Load: y | != | 0 | 152 | 152 |
|
||||
| test.c:156:9:156:19 | CompareEQ: ... == ... | test.c:156:9:156:19 | CompareEQ: ... == ... | != | 0 | 156 | 157 |
|
||||
| test.c:159:9:159:19 | CompareEQ: ... == ... | test.c:159:9:159:19 | CompareEQ: ... == ... | != | 0 | 159 | 160 |
|
||||
| test.c:162:9:162:18 | CompareLT: ... < ... | test.c:162:9:162:18 | CompareLT: ... < ... | != | 0 | 162 | 163 |
|
||||
| test.c:165:9:165:18 | CompareLT: ... < ... | test.c:165:9:165:18 | CompareLT: ... < ... | != | 0 | 165 | 166 |
|
||||
| test.c:175:13:175:32 | CompareEQ: ... == ... | test.c:175:13:175:15 | Call: call to foo | != | 0 | 175 | 175 |
|
||||
| test.c:175:13:175:32 | CompareEQ: ... == ... | test.c:175:13:175:15 | Call: call to foo | == | 0 | 175 | 175 |
|
||||
| test.c:175:13:175:32 | CompareEQ: ... == ... | test.c:175:13:175:32 | CompareEQ: ... == ... | != | 0 | 175 | 175 |
|
||||
| test.c:175:13:175:32 | CompareEQ: ... == ... | test.c:175:13:175:32 | CompareEQ: ... == ... | == | 0 | 175 | 175 |
|
||||
| test.c:181:9:181:9 | Load: x | test.c:180:20:180:20 | InitializeParameter: x | != | 0 | 182 | 182 |
|
||||
| test.c:181:9:181:9 | Load: x | test.c:180:20:180:20 | InitializeParameter: x | == | 0 | 184 | 184 |
|
||||
| test.c:181:9:181:9 | Load: x | test.c:181:9:181:9 | Load: x | != | 0 | 182 | 182 |
|
||||
| test.c:181:9:181:9 | Load: x | test.c:181:9:181:9 | Load: x | == | 0 | 184 | 184 |
|
||||
| test.cpp:18:8:18:12 | CompareNE: (bool)... | test.cpp:18:8:18:10 | Call: call to get | != | 0 | 19 | 19 |
|
||||
| test.cpp:18:8:18:12 | CompareNE: (bool)... | test.cpp:18:8:18:12 | CompareNE: (bool)... | != | 0 | 19 | 19 |
|
||||
| test.cpp:31:7:31:13 | CompareEQ: ... == ... | test.cpp:31:7:31:7 | Load: x | != | -1 | 34 | 34 |
|
||||
| test.cpp:31:7:31:13 | CompareEQ: ... == ... | test.cpp:31:7:31:7 | Load: x | == | -1 | 30 | 30 |
|
||||
| test.cpp:31:7:31:13 | CompareEQ: ... == ... | test.cpp:31:7:31:7 | Load: x | == | -1 | 32 | 32 |
|
||||
| test.cpp:31:7:31:13 | CompareEQ: ... == ... | test.cpp:31:7:31:13 | CompareEQ: ... == ... | != | 0 | 30 | 30 |
|
||||
| test.cpp:31:7:31:13 | CompareEQ: ... == ... | test.cpp:31:7:31:13 | CompareEQ: ... == ... | != | 0 | 32 | 32 |
|
||||
| test.cpp:31:7:31:13 | CompareEQ: ... == ... | test.cpp:31:7:31:13 | CompareEQ: ... == ... | == | 0 | 34 | 34 |
|
||||
| test.cpp:42:13:42:20 | Call: call to getABool | test.cpp:42:13:42:20 | Call: call to getABool | != | 0 | 44 | 44 |
|
||||
| test.cpp:42:13:42:20 | Call: call to getABool | test.cpp:42:13:42:20 | Call: call to getABool | == | 0 | 53 | 53 |
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
| 7 | 0 < x+0 when ... > ... is true |
|
||||
| 7 | 0 >= x+0 when ... > ... is false |
|
||||
| 7 | ... > ... != 0 when ... > ... is true |
|
||||
| 7 | ... > ... == 0 when ... > ... is false |
|
||||
| 7 | x < 0+1 when ... > ... is false |
|
||||
| 7 | x < 1 when ... > ... is false |
|
||||
| 7 | x >= 0+1 when ... > ... is true |
|
||||
@@ -10,6 +12,12 @@
|
||||
| 17 | 1 < y+0 when ... && ... is true |
|
||||
| 17 | 1 < y+0 when ... > ... is true |
|
||||
| 17 | 1 >= y+0 when ... > ... is false |
|
||||
| 17 | ... < ... != 0 when ... && ... is true |
|
||||
| 17 | ... < ... != 0 when ... < ... is true |
|
||||
| 17 | ... < ... == 0 when ... < ... is false |
|
||||
| 17 | ... > ... != 0 when ... && ... is true |
|
||||
| 17 | ... > ... != 0 when ... > ... is true |
|
||||
| 17 | ... > ... == 0 when ... > ... is false |
|
||||
| 17 | x < 0 when ... && ... is true |
|
||||
| 17 | x < 0 when ... < ... is true |
|
||||
| 17 | x < 0+0 when ... && ... is true |
|
||||
@@ -26,24 +34,32 @@
|
||||
| 18 | call to get == 0 when call to get is false |
|
||||
| 26 | 0 < x+0 when ... > ... is true |
|
||||
| 26 | 0 >= x+0 when ... > ... is false |
|
||||
| 26 | ... > ... != 0 when ... > ... is true |
|
||||
| 26 | ... > ... == 0 when ... > ... is false |
|
||||
| 26 | x < 0+1 when ... > ... is false |
|
||||
| 26 | x < 1 when ... > ... is false |
|
||||
| 26 | x >= 0+1 when ... > ... is true |
|
||||
| 26 | x >= 1 when ... > ... is true |
|
||||
| 31 | - ... != x+0 when ... == ... is false |
|
||||
| 31 | - ... == x+0 when ... == ... is true |
|
||||
| 31 | ... == ... != 0 when ... == ... is true |
|
||||
| 31 | ... == ... == 0 when ... == ... is false |
|
||||
| 31 | x != -1 when ... == ... is false |
|
||||
| 31 | x != - ...+0 when ... == ... is false |
|
||||
| 31 | x == -1 when ... == ... is true |
|
||||
| 31 | x == - ...+0 when ... == ... is true |
|
||||
| 34 | 10 < j+1 when ... < ... is false |
|
||||
| 34 | 10 >= j+1 when ... < ... is true |
|
||||
| 34 | ... < ... != 0 when ... < ... is true |
|
||||
| 34 | ... < ... == 0 when ... < ... is false |
|
||||
| 34 | j < 10 when ... < ... is true |
|
||||
| 34 | j < 10+0 when ... < ... is true |
|
||||
| 34 | j >= 10 when ... < ... is false |
|
||||
| 34 | j >= 10+0 when ... < ... is false |
|
||||
| 42 | 10 < j+1 when ... < ... is false |
|
||||
| 42 | 10 >= j+1 when ... < ... is true |
|
||||
| 42 | ... < ... != 0 when ... < ... is true |
|
||||
| 42 | ... < ... == 0 when ... < ... is false |
|
||||
| 42 | call to getABool != 0 when call to getABool is true |
|
||||
| 42 | call to getABool == 0 when call to getABool is false |
|
||||
| 42 | j < 10 when ... < ... is true |
|
||||
@@ -52,12 +68,16 @@
|
||||
| 42 | j >= 10+0 when ... < ... is false |
|
||||
| 44 | 0 < z+0 when ... > ... is true |
|
||||
| 44 | 0 >= z+0 when ... > ... is false |
|
||||
| 44 | ... > ... != 0 when ... > ... is true |
|
||||
| 44 | ... > ... == 0 when ... > ... is false |
|
||||
| 44 | z < 0+1 when ... > ... is false |
|
||||
| 44 | z < 1 when ... > ... is false |
|
||||
| 44 | z >= 0+1 when ... > ... is true |
|
||||
| 44 | z >= 1 when ... > ... is true |
|
||||
| 45 | 0 < y+0 when ... > ... is true |
|
||||
| 45 | 0 >= y+0 when ... > ... is false |
|
||||
| 45 | ... > ... != 0 when ... > ... is true |
|
||||
| 45 | ... > ... == 0 when ... > ... is false |
|
||||
| 45 | y < 0+1 when ... > ... is false |
|
||||
| 45 | y < 1 when ... > ... is false |
|
||||
| 45 | y >= 0+1 when ... > ... is true |
|
||||
@@ -68,6 +88,12 @@
|
||||
| 58 | 0 < y+1 when ... \|\| ... is false |
|
||||
| 58 | 0 == x+0 when ... == ... is true |
|
||||
| 58 | 0 >= y+1 when ... < ... is true |
|
||||
| 58 | ... < ... != 0 when ... < ... is true |
|
||||
| 58 | ... < ... == 0 when ... < ... is false |
|
||||
| 58 | ... < ... == 0 when ... \|\| ... is false |
|
||||
| 58 | ... == ... != 0 when ... == ... is true |
|
||||
| 58 | ... == ... == 0 when ... == ... is false |
|
||||
| 58 | ... == ... == 0 when ... \|\| ... is false |
|
||||
| 58 | x != 0 when ... == ... is false |
|
||||
| 58 | x != 0 when ... \|\| ... is false |
|
||||
| 58 | x != 0+0 when ... == ... is false |
|
||||
@@ -89,6 +115,8 @@
|
||||
| 74 | i >= 11 when i is Case[11..20] |
|
||||
| 75 | 0 != x+0 when ... == ... is false |
|
||||
| 75 | 0 == x+0 when ... == ... is true |
|
||||
| 75 | ... == ... != 0 when ... == ... is true |
|
||||
| 75 | ... == ... == 0 when ... == ... is false |
|
||||
| 75 | x != 0 when ... == ... is false |
|
||||
| 75 | x != 0+0 when ... == ... is false |
|
||||
| 75 | x == 0 when ... == ... is true |
|
||||
@@ -99,6 +127,12 @@
|
||||
| 85 | 0 == x+0 when ... && ... is true |
|
||||
| 85 | 0 == x+0 when ... == ... is true |
|
||||
| 85 | 0 == y+0 when ... != ... is false |
|
||||
| 85 | ... != ... != 0 when ... != ... is true |
|
||||
| 85 | ... != ... != 0 when ... && ... is true |
|
||||
| 85 | ... != ... == 0 when ... != ... is false |
|
||||
| 85 | ... == ... != 0 when ... && ... is true |
|
||||
| 85 | ... == ... != 0 when ... == ... is true |
|
||||
| 85 | ... == ... == 0 when ... == ... is false |
|
||||
| 85 | x != 0 when ... == ... is false |
|
||||
| 85 | x != 0+0 when ... == ... is false |
|
||||
| 85 | x == 0 when ... && ... is true |
|
||||
@@ -115,18 +149,26 @@
|
||||
| 93 | c == 0 when c is false |
|
||||
| 94 | 0 != x+0 when ... != ... is true |
|
||||
| 94 | 0 == x+0 when ... != ... is false |
|
||||
| 94 | ... != ... != 0 when ... != ... is true |
|
||||
| 94 | ... != ... == 0 when ... != ... is false |
|
||||
| 94 | x != 0 when ... != ... is true |
|
||||
| 94 | x != 0+0 when ... != ... is true |
|
||||
| 94 | x == 0 when ... != ... is false |
|
||||
| 94 | x == 0+0 when ... != ... is false |
|
||||
| 99 | f != 0 when f is true |
|
||||
| 99 | f == 0 when f is false |
|
||||
| 102 | 10 < j+1 when ... < ... is false |
|
||||
| 102 | 10 >= j+1 when ... < ... is true |
|
||||
| 102 | ... < ... != 0 when ... < ... is true |
|
||||
| 102 | ... < ... == 0 when ... < ... is false |
|
||||
| 102 | j < 10 when ... < ... is true |
|
||||
| 102 | j < 10+0 when ... < ... is true |
|
||||
| 102 | j >= 10 when ... < ... is false |
|
||||
| 102 | j >= 10+0 when ... < ... is false |
|
||||
| 105 | 0.0 != f+0 when ... != ... is true |
|
||||
| 105 | 0.0 == f+0 when ... != ... is false |
|
||||
| 105 | ... != ... != 0 when ... != ... is true |
|
||||
| 105 | ... != ... == 0 when ... != ... is false |
|
||||
| 105 | f != 0.0+0 when ... != ... is true |
|
||||
| 105 | f == 0.0+0 when ... != ... is false |
|
||||
| 109 | 0 != x+0 when ... == ... is false |
|
||||
@@ -135,6 +177,12 @@
|
||||
| 109 | 0 < y+1 when ... \|\| ... is false |
|
||||
| 109 | 0 == x+0 when ... == ... is true |
|
||||
| 109 | 0 >= y+1 when ... < ... is true |
|
||||
| 109 | ... < ... != 0 when ... < ... is true |
|
||||
| 109 | ... < ... == 0 when ... < ... is false |
|
||||
| 109 | ... < ... == 0 when ... \|\| ... is false |
|
||||
| 109 | ... == ... != 0 when ... == ... is true |
|
||||
| 109 | ... == ... == 0 when ... == ... is false |
|
||||
| 109 | ... == ... == 0 when ... \|\| ... is false |
|
||||
| 109 | x != 0 when ... == ... is false |
|
||||
| 109 | x != 0 when ... \|\| ... is false |
|
||||
| 109 | x != 0+0 when ... == ... is false |
|
||||
@@ -149,6 +197,8 @@
|
||||
| 109 | y >= 0+0 when ... \|\| ... is false |
|
||||
| 111 | 0.0 != i+0 when ... != ... is true |
|
||||
| 111 | 0.0 == i+0 when ... != ... is false |
|
||||
| 111 | ... != ... != 0 when ... != ... is true |
|
||||
| 111 | ... != ... == 0 when ... != ... is false |
|
||||
| 111 | i != 0.0+0 when ... != ... is true |
|
||||
| 111 | i == 0.0+0 when ... != ... is false |
|
||||
| 122 | b != 0 when b is true |
|
||||
@@ -166,6 +216,8 @@
|
||||
| 126 | call to test3_condition == 0 when call to test3_condition is false |
|
||||
| 131 | ... + ... != a+0 when call to __builtin_expect is false |
|
||||
| 131 | ... + ... == a+0 when call to __builtin_expect is true |
|
||||
| 131 | ... == ... != 0 when call to __builtin_expect is true |
|
||||
| 131 | ... == ... == 0 when call to __builtin_expect is false |
|
||||
| 131 | a != ... + ...+0 when call to __builtin_expect is false |
|
||||
| 131 | a != b+42 when call to __builtin_expect is false |
|
||||
| 131 | a == ... + ...+0 when call to __builtin_expect is true |
|
||||
@@ -176,6 +228,8 @@
|
||||
| 131 | b == a+-42 when call to __builtin_expect is true |
|
||||
| 131 | call to __builtin_expect != 0 when call to __builtin_expect is true |
|
||||
| 131 | call to __builtin_expect == 0 when call to __builtin_expect is false |
|
||||
| 135 | ... != ... != 0 when call to __builtin_expect is true |
|
||||
| 135 | ... != ... == 0 when call to __builtin_expect is false |
|
||||
| 135 | ... + ... != a+0 when call to __builtin_expect is true |
|
||||
| 135 | ... + ... == a+0 when call to __builtin_expect is false |
|
||||
| 135 | a != ... + ...+0 when call to __builtin_expect is true |
|
||||
@@ -190,6 +244,8 @@
|
||||
| 137 | 0 == 0 when 0 is false |
|
||||
| 141 | 42 != a+0 when call to __builtin_expect is false |
|
||||
| 141 | 42 == a+0 when call to __builtin_expect is true |
|
||||
| 141 | ... == ... != 0 when call to __builtin_expect is true |
|
||||
| 141 | ... == ... == 0 when call to __builtin_expect is false |
|
||||
| 141 | a != 42 when call to __builtin_expect is false |
|
||||
| 141 | a != 42+0 when call to __builtin_expect is false |
|
||||
| 141 | a == 42 when call to __builtin_expect is true |
|
||||
@@ -198,6 +254,8 @@
|
||||
| 141 | call to __builtin_expect == 0 when call to __builtin_expect is false |
|
||||
| 145 | 42 != a+0 when call to __builtin_expect is true |
|
||||
| 145 | 42 == a+0 when call to __builtin_expect is false |
|
||||
| 145 | ... != ... != 0 when call to __builtin_expect is true |
|
||||
| 145 | ... != ... == 0 when call to __builtin_expect is false |
|
||||
| 145 | a != 42 when call to __builtin_expect is true |
|
||||
| 145 | a != 42+0 when call to __builtin_expect is true |
|
||||
| 145 | a == 42 when call to __builtin_expect is false |
|
||||
|
||||
@@ -99,10 +99,20 @@ binary
|
||||
| test.c:75:9:75:14 | ... == ... | test.c:75:9:75:9 | x | == | test.c:75:14:75:14 | 0 | 0 | 75 | 77 |
|
||||
| test.c:75:9:75:14 | ... == ... | test.c:75:14:75:14 | 0 | != | test.c:75:9:75:9 | x | 0 | 78 | 79 |
|
||||
| test.c:75:9:75:14 | ... == ... | test.c:75:14:75:14 | 0 | == | test.c:75:9:75:9 | x | 0 | 75 | 77 |
|
||||
| test.c:75:9:75:14 | ... == ... | test.c:85:8:85:8 | x | != | test.c:85:13:85:13 | 0 | 0 | 78 | 79 |
|
||||
| test.c:75:9:75:14 | ... == ... | test.c:85:8:85:8 | x | == | test.c:85:13:85:13 | 0 | 0 | 75 | 77 |
|
||||
| test.c:75:9:75:14 | ... == ... | test.c:85:13:85:13 | 0 | != | test.c:85:8:85:8 | x | 0 | 78 | 79 |
|
||||
| test.c:75:9:75:14 | ... == ... | test.c:85:13:85:13 | 0 | == | test.c:85:8:85:8 | x | 0 | 75 | 77 |
|
||||
| test.c:85:8:85:13 | ... == ... | test.c:75:9:75:9 | x | == | test.c:75:14:75:14 | 0 | 0 | 85 | 85 |
|
||||
| test.c:85:8:85:13 | ... == ... | test.c:75:9:75:9 | x | == | test.c:75:14:75:14 | 0 | 0 | 86 | 86 |
|
||||
| test.c:85:8:85:13 | ... == ... | test.c:75:14:75:14 | 0 | == | test.c:75:9:75:9 | x | 0 | 85 | 85 |
|
||||
| test.c:85:8:85:13 | ... == ... | test.c:75:14:75:14 | 0 | == | test.c:75:9:75:9 | x | 0 | 86 | 86 |
|
||||
| test.c:85:8:85:13 | ... == ... | test.c:85:8:85:8 | x | == | test.c:85:13:85:13 | 0 | 0 | 85 | 85 |
|
||||
| test.c:85:8:85:13 | ... == ... | test.c:85:8:85:8 | x | == | test.c:85:13:85:13 | 0 | 0 | 86 | 86 |
|
||||
| test.c:85:8:85:13 | ... == ... | test.c:85:13:85:13 | 0 | == | test.c:85:8:85:8 | x | 0 | 85 | 85 |
|
||||
| test.c:85:8:85:13 | ... == ... | test.c:85:13:85:13 | 0 | == | test.c:85:8:85:8 | x | 0 | 86 | 86 |
|
||||
| test.c:85:8:85:23 | ... && ... | test.c:75:9:75:9 | x | == | test.c:75:14:75:14 | 0 | 0 | 86 | 86 |
|
||||
| test.c:85:8:85:23 | ... && ... | test.c:75:14:75:14 | 0 | == | test.c:75:9:75:9 | x | 0 | 86 | 86 |
|
||||
| test.c:85:8:85:23 | ... && ... | test.c:85:8:85:8 | x | == | test.c:85:13:85:13 | 0 | 0 | 86 | 86 |
|
||||
| test.c:85:8:85:23 | ... && ... | test.c:85:13:85:13 | 0 | == | test.c:85:8:85:8 | x | 0 | 86 | 86 |
|
||||
| test.c:85:8:85:23 | ... && ... | test.c:85:18:85:18 | y | != | test.c:85:23:85:23 | 0 | 0 | 86 | 86 |
|
||||
@@ -174,11 +184,18 @@ binary
|
||||
unary
|
||||
| test.c:7:9:7:13 | ... > ... | test.c:7:9:7:9 | x | < | 1 | 10 | 11 |
|
||||
| test.c:7:9:7:13 | ... > ... | test.c:7:9:7:9 | x | >= | 1 | 7 | 9 |
|
||||
| test.c:7:9:7:13 | ... > ... | test.c:7:9:7:13 | ... > ... | != | 0 | 7 | 9 |
|
||||
| test.c:7:9:7:13 | ... > ... | test.c:7:9:7:13 | ... > ... | == | 0 | 10 | 11 |
|
||||
| test.c:17:8:17:12 | ... < ... | test.c:17:8:17:8 | x | < | 0 | 17 | 17 |
|
||||
| test.c:17:8:17:12 | ... < ... | test.c:17:8:17:8 | x | < | 0 | 18 | 18 |
|
||||
| test.c:17:8:17:12 | ... < ... | test.c:17:8:17:12 | ... < ... | != | 0 | 17 | 17 |
|
||||
| test.c:17:8:17:12 | ... < ... | test.c:17:8:17:12 | ... < ... | != | 0 | 18 | 18 |
|
||||
| test.c:17:8:17:21 | ... && ... | test.c:17:8:17:8 | x | < | 0 | 18 | 18 |
|
||||
| test.c:17:8:17:21 | ... && ... | test.c:17:8:17:12 | ... < ... | != | 0 | 18 | 18 |
|
||||
| test.c:17:8:17:21 | ... && ... | test.c:17:17:17:17 | y | >= | 2 | 18 | 18 |
|
||||
| test.c:17:8:17:21 | ... && ... | test.c:17:17:17:21 | ... > ... | != | 0 | 18 | 18 |
|
||||
| test.c:17:17:17:21 | ... > ... | test.c:17:17:17:17 | y | >= | 2 | 18 | 18 |
|
||||
| test.c:17:17:17:21 | ... > ... | test.c:17:17:17:21 | ... > ... | != | 0 | 18 | 18 |
|
||||
| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:11 | x | < | 1 | 2 | 2 |
|
||||
| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:11 | x | < | 1 | 31 | 34 |
|
||||
| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:11 | x | < | 1 | 34 | 34 |
|
||||
@@ -193,6 +210,20 @@ unary
|
||||
| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:11 | x | < | 1 | 58 | 66 |
|
||||
| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:11 | x | < | 1 | 62 | 62 |
|
||||
| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:11 | x | >= | 1 | 26 | 28 |
|
||||
| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:15 | ... > ... | != | 0 | 26 | 28 |
|
||||
| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:15 | ... > ... | == | 0 | 2 | 2 |
|
||||
| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:15 | ... > ... | == | 0 | 31 | 34 |
|
||||
| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:15 | ... > ... | == | 0 | 34 | 34 |
|
||||
| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:15 | ... > ... | == | 0 | 39 | 42 |
|
||||
| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:15 | ... > ... | == | 0 | 42 | 42 |
|
||||
| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:15 | ... > ... | == | 0 | 42 | 44 |
|
||||
| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:15 | ... > ... | == | 0 | 45 | 45 |
|
||||
| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:15 | ... > ... | == | 0 | 45 | 47 |
|
||||
| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:15 | ... > ... | == | 0 | 51 | 53 |
|
||||
| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:15 | ... > ... | == | 0 | 56 | 58 |
|
||||
| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:15 | ... > ... | == | 0 | 58 | 58 |
|
||||
| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:15 | ... > ... | == | 0 | 58 | 66 |
|
||||
| test.c:26:11:26:15 | ... > ... | test.c:26:11:26:15 | ... > ... | == | 0 | 62 | 62 |
|
||||
| test.c:34:16:34:21 | ... < ... | test.c:34:16:34:16 | j | < | 10 | 34 | 34 |
|
||||
| test.c:34:16:34:21 | ... < ... | test.c:34:16:34:16 | j | >= | 10 | 2 | 2 |
|
||||
| test.c:34:16:34:21 | ... < ... | test.c:34:16:34:16 | j | >= | 10 | 39 | 42 |
|
||||
@@ -205,28 +236,72 @@ unary
|
||||
| test.c:34:16:34:21 | ... < ... | test.c:34:16:34:16 | j | >= | 10 | 58 | 58 |
|
||||
| test.c:34:16:34:21 | ... < ... | test.c:34:16:34:16 | j | >= | 10 | 58 | 66 |
|
||||
| test.c:34:16:34:21 | ... < ... | test.c:34:16:34:16 | j | >= | 10 | 62 | 62 |
|
||||
| test.c:34:16:34:21 | ... < ... | test.c:34:16:34:21 | ... < ... | != | 0 | 34 | 34 |
|
||||
| test.c:34:16:34:21 | ... < ... | test.c:34:16:34:21 | ... < ... | == | 0 | 2 | 2 |
|
||||
| test.c:34:16:34:21 | ... < ... | test.c:34:16:34:21 | ... < ... | == | 0 | 39 | 42 |
|
||||
| test.c:34:16:34:21 | ... < ... | test.c:34:16:34:21 | ... < ... | == | 0 | 42 | 42 |
|
||||
| test.c:34:16:34:21 | ... < ... | test.c:34:16:34:21 | ... < ... | == | 0 | 42 | 44 |
|
||||
| test.c:34:16:34:21 | ... < ... | test.c:34:16:34:21 | ... < ... | == | 0 | 45 | 45 |
|
||||
| test.c:34:16:34:21 | ... < ... | test.c:34:16:34:21 | ... < ... | == | 0 | 45 | 47 |
|
||||
| test.c:34:16:34:21 | ... < ... | test.c:34:16:34:21 | ... < ... | == | 0 | 51 | 53 |
|
||||
| test.c:34:16:34:21 | ... < ... | test.c:34:16:34:21 | ... < ... | == | 0 | 56 | 58 |
|
||||
| test.c:34:16:34:21 | ... < ... | test.c:34:16:34:21 | ... < ... | == | 0 | 58 | 58 |
|
||||
| test.c:34:16:34:21 | ... < ... | test.c:34:16:34:21 | ... < ... | == | 0 | 58 | 66 |
|
||||
| test.c:34:16:34:21 | ... < ... | test.c:34:16:34:21 | ... < ... | == | 0 | 62 | 62 |
|
||||
| test.c:42:16:42:21 | ... < ... | test.c:42:16:42:16 | j | < | 10 | 42 | 42 |
|
||||
| test.c:42:16:42:21 | ... < ... | test.c:42:16:42:16 | j | < | 10 | 42 | 44 |
|
||||
| test.c:42:16:42:21 | ... < ... | test.c:42:16:42:16 | j | < | 10 | 45 | 45 |
|
||||
| test.c:42:16:42:21 | ... < ... | test.c:42:16:42:16 | j | < | 10 | 45 | 47 |
|
||||
| test.c:42:16:42:21 | ... < ... | test.c:42:16:42:16 | j | < | 10 | 51 | 53 |
|
||||
| test.c:42:16:42:21 | ... < ... | test.c:42:16:42:21 | ... < ... | != | 0 | 42 | 42 |
|
||||
| test.c:42:16:42:21 | ... < ... | test.c:42:16:42:21 | ... < ... | != | 0 | 42 | 44 |
|
||||
| test.c:42:16:42:21 | ... < ... | test.c:42:16:42:21 | ... < ... | != | 0 | 45 | 45 |
|
||||
| test.c:42:16:42:21 | ... < ... | test.c:42:16:42:21 | ... < ... | != | 0 | 45 | 47 |
|
||||
| test.c:42:16:42:21 | ... < ... | test.c:42:16:42:21 | ... < ... | != | 0 | 51 | 53 |
|
||||
| test.c:44:12:44:16 | ... > ... | test.c:44:12:44:12 | z | < | 1 | 42 | 42 |
|
||||
| test.c:44:12:44:16 | ... > ... | test.c:44:12:44:12 | z | < | 1 | 51 | 53 |
|
||||
| test.c:44:12:44:16 | ... > ... | test.c:44:12:44:12 | z | >= | 1 | 45 | 45 |
|
||||
| test.c:44:12:44:16 | ... > ... | test.c:44:12:44:12 | z | >= | 1 | 45 | 47 |
|
||||
| test.c:44:12:44:16 | ... > ... | test.c:44:12:44:16 | ... > ... | != | 0 | 45 | 45 |
|
||||
| test.c:44:12:44:16 | ... > ... | test.c:44:12:44:16 | ... > ... | != | 0 | 45 | 47 |
|
||||
| test.c:44:12:44:16 | ... > ... | test.c:44:12:44:16 | ... > ... | == | 0 | 42 | 42 |
|
||||
| test.c:44:12:44:16 | ... > ... | test.c:44:12:44:16 | ... > ... | == | 0 | 51 | 53 |
|
||||
| test.c:45:16:45:20 | ... > ... | test.c:45:16:45:16 | y | >= | 1 | 45 | 47 |
|
||||
| test.c:45:16:45:20 | ... > ... | test.c:45:16:45:20 | ... > ... | != | 0 | 45 | 47 |
|
||||
| test.c:58:9:58:14 | ... == ... | test.c:58:9:58:9 | x | != | 0 | 58 | 58 |
|
||||
| test.c:58:9:58:14 | ... == ... | test.c:58:9:58:9 | x | != | 0 | 62 | 62 |
|
||||
| test.c:58:9:58:14 | ... == ... | test.c:58:9:58:14 | ... == ... | == | 0 | 58 | 58 |
|
||||
| test.c:58:9:58:14 | ... == ... | test.c:58:9:58:14 | ... == ... | == | 0 | 62 | 62 |
|
||||
| test.c:58:9:58:23 | ... \|\| ... | test.c:58:9:58:9 | x | != | 0 | 62 | 62 |
|
||||
| test.c:58:9:58:23 | ... \|\| ... | test.c:58:9:58:14 | ... == ... | == | 0 | 62 | 62 |
|
||||
| test.c:58:9:58:23 | ... \|\| ... | test.c:58:19:58:19 | y | >= | 0 | 62 | 62 |
|
||||
| test.c:58:9:58:23 | ... \|\| ... | test.c:58:19:58:23 | ... < ... | == | 0 | 62 | 62 |
|
||||
| test.c:58:19:58:23 | ... < ... | test.c:58:19:58:19 | y | >= | 0 | 62 | 62 |
|
||||
| test.c:58:19:58:23 | ... < ... | test.c:58:19:58:23 | ... < ... | == | 0 | 62 | 62 |
|
||||
| test.c:75:9:75:14 | ... == ... | test.c:75:9:75:9 | x | != | 0 | 78 | 79 |
|
||||
| test.c:75:9:75:14 | ... == ... | test.c:75:9:75:9 | x | == | 0 | 75 | 77 |
|
||||
| test.c:75:9:75:14 | ... == ... | test.c:75:9:75:14 | ... == ... | != | 0 | 75 | 77 |
|
||||
| test.c:75:9:75:14 | ... == ... | test.c:75:9:75:14 | ... == ... | == | 0 | 78 | 79 |
|
||||
| test.c:75:9:75:14 | ... == ... | test.c:85:8:85:8 | x | != | 0 | 78 | 79 |
|
||||
| test.c:75:9:75:14 | ... == ... | test.c:85:8:85:8 | x | == | 0 | 75 | 77 |
|
||||
| test.c:75:9:75:14 | ... == ... | test.c:85:8:85:13 | ... == ... | != | 0 | 75 | 77 |
|
||||
| test.c:75:9:75:14 | ... == ... | test.c:85:8:85:13 | ... == ... | == | 0 | 78 | 79 |
|
||||
| test.c:85:8:85:13 | ... == ... | test.c:75:9:75:9 | x | == | 0 | 85 | 85 |
|
||||
| test.c:85:8:85:13 | ... == ... | test.c:75:9:75:9 | x | == | 0 | 86 | 86 |
|
||||
| test.c:85:8:85:13 | ... == ... | test.c:75:9:75:14 | ... == ... | != | 0 | 85 | 85 |
|
||||
| test.c:85:8:85:13 | ... == ... | test.c:75:9:75:14 | ... == ... | != | 0 | 86 | 86 |
|
||||
| test.c:85:8:85:13 | ... == ... | test.c:85:8:85:8 | x | == | 0 | 85 | 85 |
|
||||
| test.c:85:8:85:13 | ... == ... | test.c:85:8:85:8 | x | == | 0 | 86 | 86 |
|
||||
| test.c:85:8:85:13 | ... == ... | test.c:85:8:85:13 | ... == ... | != | 0 | 85 | 85 |
|
||||
| test.c:85:8:85:13 | ... == ... | test.c:85:8:85:13 | ... == ... | != | 0 | 86 | 86 |
|
||||
| test.c:85:8:85:23 | ... && ... | test.c:75:9:75:9 | x | == | 0 | 86 | 86 |
|
||||
| test.c:85:8:85:23 | ... && ... | test.c:75:9:75:14 | ... == ... | != | 0 | 86 | 86 |
|
||||
| test.c:85:8:85:23 | ... && ... | test.c:85:8:85:8 | x | == | 0 | 86 | 86 |
|
||||
| test.c:85:8:85:23 | ... && ... | test.c:85:8:85:13 | ... == ... | != | 0 | 86 | 86 |
|
||||
| test.c:85:8:85:23 | ... && ... | test.c:85:18:85:18 | y | != | 0 | 86 | 86 |
|
||||
| test.c:85:8:85:23 | ... && ... | test.c:85:18:85:23 | ... != ... | != | 0 | 86 | 86 |
|
||||
| test.c:85:18:85:23 | ... != ... | test.c:85:18:85:18 | y | != | 0 | 86 | 86 |
|
||||
| test.c:85:18:85:23 | ... != ... | test.c:85:18:85:23 | ... != ... | != | 0 | 86 | 86 |
|
||||
| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:11 | x | != | 0 | 94 | 96 |
|
||||
| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:11 | x | == | 0 | 70 | 70 |
|
||||
| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:11 | x | == | 0 | 99 | 102 |
|
||||
@@ -235,24 +310,49 @@ unary
|
||||
| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:11 | x | == | 0 | 109 | 109 |
|
||||
| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:11 | x | == | 0 | 109 | 117 |
|
||||
| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:11 | x | == | 0 | 113 | 113 |
|
||||
| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:16 | ... != ... | != | 0 | 94 | 96 |
|
||||
| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:16 | ... != ... | == | 0 | 70 | 70 |
|
||||
| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:16 | ... != ... | == | 0 | 99 | 102 |
|
||||
| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:16 | ... != ... | == | 0 | 102 | 102 |
|
||||
| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:16 | ... != ... | == | 0 | 107 | 109 |
|
||||
| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:16 | ... != ... | == | 0 | 109 | 109 |
|
||||
| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:16 | ... != ... | == | 0 | 109 | 117 |
|
||||
| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:16 | ... != ... | == | 0 | 113 | 113 |
|
||||
| test.c:102:16:102:21 | ... < ... | test.c:102:16:102:16 | j | < | 10 | 102 | 102 |
|
||||
| test.c:102:16:102:21 | ... < ... | test.c:102:16:102:16 | j | >= | 10 | 70 | 70 |
|
||||
| test.c:102:16:102:21 | ... < ... | test.c:102:16:102:16 | j | >= | 10 | 107 | 109 |
|
||||
| test.c:102:16:102:21 | ... < ... | test.c:102:16:102:16 | j | >= | 10 | 109 | 109 |
|
||||
| test.c:102:16:102:21 | ... < ... | test.c:102:16:102:16 | j | >= | 10 | 109 | 117 |
|
||||
| test.c:102:16:102:21 | ... < ... | test.c:102:16:102:16 | j | >= | 10 | 113 | 113 |
|
||||
| test.c:102:16:102:21 | ... < ... | test.c:102:16:102:21 | ... < ... | != | 0 | 102 | 102 |
|
||||
| test.c:102:16:102:21 | ... < ... | test.c:102:16:102:21 | ... < ... | == | 0 | 70 | 70 |
|
||||
| test.c:102:16:102:21 | ... < ... | test.c:102:16:102:21 | ... < ... | == | 0 | 107 | 109 |
|
||||
| test.c:102:16:102:21 | ... < ... | test.c:102:16:102:21 | ... < ... | == | 0 | 109 | 109 |
|
||||
| test.c:102:16:102:21 | ... < ... | test.c:102:16:102:21 | ... < ... | == | 0 | 109 | 117 |
|
||||
| test.c:102:16:102:21 | ... < ... | test.c:102:16:102:21 | ... < ... | == | 0 | 113 | 113 |
|
||||
| test.c:109:9:109:14 | ... == ... | test.c:109:9:109:9 | x | != | 0 | 109 | 109 |
|
||||
| test.c:109:9:109:14 | ... == ... | test.c:109:9:109:9 | x | != | 0 | 113 | 113 |
|
||||
| test.c:109:9:109:14 | ... == ... | test.c:109:9:109:14 | ... == ... | == | 0 | 109 | 109 |
|
||||
| test.c:109:9:109:14 | ... == ... | test.c:109:9:109:14 | ... == ... | == | 0 | 113 | 113 |
|
||||
| test.c:109:9:109:23 | ... \|\| ... | test.c:109:9:109:9 | x | != | 0 | 113 | 113 |
|
||||
| test.c:109:9:109:23 | ... \|\| ... | test.c:109:9:109:14 | ... == ... | == | 0 | 113 | 113 |
|
||||
| test.c:109:9:109:23 | ... \|\| ... | test.c:109:19:109:19 | y | >= | 0 | 113 | 113 |
|
||||
| test.c:109:9:109:23 | ... \|\| ... | test.c:109:19:109:23 | ... < ... | == | 0 | 113 | 113 |
|
||||
| test.c:109:19:109:23 | ... < ... | test.c:109:19:109:19 | y | >= | 0 | 113 | 113 |
|
||||
| test.c:109:19:109:23 | ... < ... | test.c:109:19:109:23 | ... < ... | == | 0 | 113 | 113 |
|
||||
| test.c:126:7:126:7 | 1 | test.c:126:7:126:7 | 1 | != | 0 | 126 | 126 |
|
||||
| test.c:126:7:126:7 | 1 | test.c:126:7:126:7 | 1 | != | 0 | 126 | 128 |
|
||||
| test.c:126:7:126:7 | 1 | test.c:126:7:126:7 | 1 | != | 0 | 131 | 131 |
|
||||
| test.c:126:7:126:7 | 1 | test.c:126:7:126:7 | 1 | != | 0 | 131 | 132 |
|
||||
| test.c:126:7:126:7 | 1 | test.c:126:7:126:7 | 1 | != | 0 | 134 | 123 |
|
||||
| test.c:126:7:126:7 | 1 | test.c:127:9:127:9 | 1 | != | 0 | 126 | 126 |
|
||||
| test.c:126:7:126:7 | 1 | test.c:127:9:127:9 | 1 | != | 0 | 126 | 128 |
|
||||
| test.c:126:7:126:7 | 1 | test.c:127:9:127:9 | 1 | != | 0 | 131 | 131 |
|
||||
| test.c:126:7:126:7 | 1 | test.c:127:9:127:9 | 1 | != | 0 | 131 | 132 |
|
||||
| test.c:126:7:126:7 | 1 | test.c:127:9:127:9 | 1 | != | 0 | 134 | 123 |
|
||||
| test.c:126:7:126:28 | ... && ... | test.c:126:7:126:7 | 1 | != | 0 | 126 | 128 |
|
||||
| test.c:126:7:126:28 | ... && ... | test.c:126:12:126:26 | call to test3_condition | != | 0 | 126 | 128 |
|
||||
| test.c:126:7:126:28 | ... && ... | test.c:127:9:127:9 | 1 | != | 0 | 126 | 128 |
|
||||
| test.c:126:12:126:26 | call to test3_condition | test.c:126:12:126:26 | call to test3_condition | != | 0 | 126 | 128 |
|
||||
| test.c:131:7:131:7 | b | test.c:131:7:131:7 | b | != | 0 | 131 | 132 |
|
||||
| test.c:137:7:137:7 | 0 | test.c:137:7:137:7 | 0 | == | 0 | 142 | 136 |
|
||||
@@ -269,6 +369,10 @@ unary
|
||||
| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:7:31:7 | x | != | -1 | 34 | 34 |
|
||||
| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:7:31:7 | x | == | -1 | 30 | 30 |
|
||||
| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:7:31:7 | x | == | -1 | 31 | 32 |
|
||||
| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:7:31:13 | ... == ... | != | 0 | 30 | 30 |
|
||||
| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:7:31:13 | ... == ... | != | 0 | 31 | 32 |
|
||||
| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:7:31:13 | ... == ... | == | 0 | 30 | 30 |
|
||||
| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:7:31:13 | ... == ... | == | 0 | 34 | 34 |
|
||||
| test.cpp:42:13:42:20 | call to getABool | test.cpp:42:13:42:20 | call to getABool | != | 0 | 43 | 45 |
|
||||
| test.cpp:42:13:42:20 | call to getABool | test.cpp:42:13:42:20 | call to getABool | == | 0 | 53 | 53 |
|
||||
| test.cpp:61:10:61:10 | i | test.cpp:61:10:61:10 | i | == | 0 | 62 | 64 |
|
||||
@@ -278,13 +382,20 @@ unary
|
||||
| test.cpp:74:10:74:10 | i | test.cpp:74:10:74:10 | i | >= | 0 | 75 | 77 |
|
||||
| test.cpp:74:10:74:10 | i | test.cpp:74:10:74:10 | i | >= | 11 | 78 | 79 |
|
||||
| test.cpp:93:6:93:6 | c | test.cpp:93:6:93:6 | c | != | 0 | 93 | 94 |
|
||||
| test.cpp:99:6:99:6 | f | test.cpp:99:6:99:6 | f | != | 0 | 99 | 100 |
|
||||
| test.cpp:105:6:105:14 | ... != ... | test.cpp:105:6:105:14 | ... != ... | != | 0 | 105 | 106 |
|
||||
| test.cpp:111:6:111:14 | ... != ... | test.cpp:111:6:111:14 | ... != ... | != | 0 | 111 | 112 |
|
||||
| test.cpp:122:9:122:9 | b | test.cpp:122:9:122:9 | b | != | 0 | 123 | 125 |
|
||||
| test.cpp:122:9:122:9 | b | test.cpp:122:9:122:9 | b | != | 0 | 125 | 125 |
|
||||
| test.cpp:125:13:125:20 | ! ... | test.cpp:125:13:125:20 | ! ... | != | 0 | 125 | 125 |
|
||||
| test.cpp:125:14:125:17 | call to safe | test.cpp:125:14:125:17 | call to safe | == | 0 | 125 | 125 |
|
||||
| test.cpp:131:6:131:21 | call to __builtin_expect | test.cpp:131:6:131:21 | call to __builtin_expect | != | 0 | 131 | 132 |
|
||||
| test.cpp:131:6:131:21 | call to __builtin_expect | test.cpp:131:23:131:33 | ... == ... | != | 0 | 131 | 132 |
|
||||
| test.cpp:135:6:135:21 | call to __builtin_expect | test.cpp:135:6:135:21 | call to __builtin_expect | != | 0 | 135 | 136 |
|
||||
| test.cpp:135:6:135:21 | call to __builtin_expect | test.cpp:135:23:135:33 | ... != ... | != | 0 | 135 | 136 |
|
||||
| test.cpp:141:6:141:21 | call to __builtin_expect | test.cpp:141:6:141:21 | call to __builtin_expect | != | 0 | 141 | 142 |
|
||||
| test.cpp:141:6:141:21 | call to __builtin_expect | test.cpp:141:23:141:23 | a | == | 42 | 141 | 142 |
|
||||
| test.cpp:141:6:141:21 | call to __builtin_expect | test.cpp:141:23:141:29 | ... == ... | != | 0 | 141 | 142 |
|
||||
| test.cpp:145:6:145:21 | call to __builtin_expect | test.cpp:145:6:145:21 | call to __builtin_expect | != | 0 | 145 | 146 |
|
||||
| test.cpp:145:6:145:21 | call to __builtin_expect | test.cpp:145:23:145:23 | a | != | 42 | 145 | 146 |
|
||||
| test.cpp:145:6:145:21 | call to __builtin_expect | test.cpp:145:23:145:29 | ... != ... | != | 0 | 145 | 146 |
|
||||
|
||||
@@ -56,7 +56,7 @@ union MyUnion0 {
|
||||
// Typedef
|
||||
typedef MyClass0 *MyClassPtr;
|
||||
|
||||
// TemplateClass, TemplateParameter (UserType)
|
||||
// TemplateClass, TypeTemplateParameter (UserType)
|
||||
|
||||
template <typename T>
|
||||
class myTemplateClass
|
||||
@@ -113,7 +113,7 @@ public:
|
||||
// Typedef
|
||||
typedef MyClass1 *MyClassPtr;
|
||||
|
||||
// TemplateClass, TemplateParameter (UserType)
|
||||
// TemplateClass, TypeTemplateParameter (UserType)
|
||||
|
||||
template <typename T>
|
||||
class myTemplateClass
|
||||
|
||||
@@ -9016,9 +9016,9 @@ ir.cpp:
|
||||
# 704| [TemplateFunction,TopLevelFunction] T min<T>(T, T)
|
||||
# 704| <params>:
|
||||
# 704| getParameter(0): [Parameter] x
|
||||
# 704| Type = [TemplateParameter] T
|
||||
# 704| Type = [TypeTemplateParameter] T
|
||||
# 704| getParameter(1): [Parameter] y
|
||||
# 704| Type = [TemplateParameter] T
|
||||
# 704| Type = [TypeTemplateParameter] T
|
||||
# 704| getEntryPoint(): [BlockStmt] { ... }
|
||||
# 705| getStmt(0): [ReturnStmt] return ...
|
||||
# 705| getExpr(): [ConditionalExpr] ... ? ... : ...
|
||||
@@ -9028,16 +9028,16 @@ ir.cpp:
|
||||
# 705| Type = [UnknownType] unknown
|
||||
# 705| ValueCategory = prvalue
|
||||
# 705| getLesserOperand(): [VariableAccess] x
|
||||
# 705| Type = [TemplateParameter] T
|
||||
# 705| Type = [TypeTemplateParameter] T
|
||||
# 705| ValueCategory = lvalue
|
||||
# 705| getGreaterOperand(): [VariableAccess] y
|
||||
# 705| Type = [TemplateParameter] T
|
||||
# 705| Type = [TypeTemplateParameter] T
|
||||
# 705| ValueCategory = lvalue
|
||||
# 705| getThen(): [VariableAccess] x
|
||||
# 705| Type = [TemplateParameter] T
|
||||
# 705| Type = [TypeTemplateParameter] T
|
||||
# 705| ValueCategory = lvalue
|
||||
# 705| getElse(): [VariableAccess] y
|
||||
# 705| Type = [TemplateParameter] T
|
||||
# 705| Type = [TypeTemplateParameter] T
|
||||
# 705| ValueCategory = lvalue
|
||||
# 705| getCondition().getFullyConverted(): [CStyleCast] (bool)...
|
||||
# 705| Conversion = [BoolConversion] conversion to bool
|
||||
@@ -9095,24 +9095,24 @@ ir.cpp:
|
||||
# 715| [MemberFunction,TemplateFunction] T Outer<T>::Func<U, V>(U, V)
|
||||
# 715| <params>:
|
||||
# 715| getParameter(0): [Parameter] x
|
||||
# 715| Type = [TemplateParameter] U
|
||||
# 715| Type = [TypeTemplateParameter] U
|
||||
# 715| getParameter(1): [Parameter] y
|
||||
# 715| Type = [TemplateParameter] V
|
||||
# 715| Type = [TypeTemplateParameter] V
|
||||
# 715| getEntryPoint(): [BlockStmt] { ... }
|
||||
# 716| getStmt(0): [ReturnStmt] return ...
|
||||
# 716| getExpr(): [Literal] 0
|
||||
# 716| Type = [TemplateParameter] T
|
||||
# 716| Type = [TypeTemplateParameter] T
|
||||
# 716| Value = [Literal] 0
|
||||
# 716| ValueCategory = prvalue
|
||||
# 716| getExpr().getFullyConverted(): [TemporaryObjectExpr] temporary object
|
||||
# 716| Type = [TemplateParameter] T
|
||||
# 716| Type = [TypeTemplateParameter] T
|
||||
# 716| ValueCategory = prvalue(load)
|
||||
# 715| [MemberFunction,TemplateFunction] long Outer<long>::Func<U, V>(U, V)
|
||||
# 715| <params>:
|
||||
# 715| getParameter(0): [Parameter] x
|
||||
# 715| Type = [TemplateParameter] U
|
||||
# 715| Type = [TypeTemplateParameter] U
|
||||
# 715| getParameter(1): [Parameter] y
|
||||
# 715| Type = [TemplateParameter] V
|
||||
# 715| Type = [TypeTemplateParameter] V
|
||||
# 715| [FunctionTemplateInstantiation,MemberFunction] long Outer<long>::Func<void*, char>(void*, char)
|
||||
# 715| <params>:
|
||||
# 715| getParameter(0): [Parameter] x
|
||||
@@ -12559,7 +12559,7 @@ ir.cpp:
|
||||
# 1109| [Constructor] void std::vector<T>::vector(T)
|
||||
# 1109| <params>:
|
||||
# 1109| getParameter(0): [Parameter] (unnamed parameter 0)
|
||||
# 1109| Type = [TemplateParameter] T
|
||||
# 1109| Type = [TypeTemplateParameter] T
|
||||
# 1109| [Constructor] void std::vector<char>::vector(char)
|
||||
# 1109| <params>:
|
||||
# 1109| getParameter(0): [Parameter] (unnamed parameter 0)
|
||||
@@ -12601,15 +12601,15 @@ ir.cpp:
|
||||
# 1120| [Operator,TemplateFunction,TopLevelFunction] bool std::operator==<T>(iterator, iterator)
|
||||
# 1120| <params>:
|
||||
# 1120| getParameter(0): [Parameter] left
|
||||
# 1120| Type = [TemplateParameter] iterator
|
||||
# 1120| Type = [TypeTemplateParameter] iterator
|
||||
# 1120| getParameter(1): [Parameter] right
|
||||
# 1120| Type = [TemplateParameter] iterator
|
||||
# 1120| Type = [TypeTemplateParameter] iterator
|
||||
# 1122| [Operator,TemplateFunction,TopLevelFunction] bool std::operator!=<T>(iterator, iterator)
|
||||
# 1122| <params>:
|
||||
# 1122| getParameter(0): [Parameter] left
|
||||
# 1122| Type = [TemplateParameter] iterator
|
||||
# 1122| Type = [TypeTemplateParameter] iterator
|
||||
# 1122| getParameter(1): [Parameter] right
|
||||
# 1122| Type = [TemplateParameter] iterator
|
||||
# 1122| Type = [TypeTemplateParameter] iterator
|
||||
# 1126| [TopLevelFunction] void RangeBasedFor(std::vector<int> const&)
|
||||
# 1126| <params>:
|
||||
# 1126| getParameter(0): [Parameter] v
|
||||
@@ -14129,11 +14129,11 @@ ir.cpp:
|
||||
# 1375| getEntryPoint(): [BlockStmt] { ... }
|
||||
# 1376| getStmt(0): [ReturnStmt] return ...
|
||||
# 1376| getExpr(): [Literal] 0
|
||||
# 1376| Type = [TemplateParameter] T
|
||||
# 1376| Type = [TypeTemplateParameter] T
|
||||
# 1376| Value = [Literal] 0
|
||||
# 1376| ValueCategory = prvalue
|
||||
# 1376| getExpr().getFullyConverted(): [TemporaryObjectExpr] temporary object
|
||||
# 1376| Type = [TemplateParameter] T
|
||||
# 1376| Type = [TypeTemplateParameter] T
|
||||
# 1376| ValueCategory = prvalue(load)
|
||||
# 1375| [FunctionTemplateInstantiation,TopLevelFunction] copy_constructor defaultConstruct<copy_constructor>()
|
||||
# 1375| <params>:
|
||||
@@ -14223,7 +14223,7 @@ ir.cpp:
|
||||
# 1409| [TemplateFunction,TopLevelFunction] void acceptValue<T>(T)
|
||||
# 1409| <params>:
|
||||
# 1409| getParameter(0): [Parameter] v
|
||||
# 1409| Type = [TemplateParameter] T
|
||||
# 1409| Type = [TypeTemplateParameter] T
|
||||
# 1409| [FunctionTemplateInstantiation,TopLevelFunction] void acceptValue<copy_constructor>(copy_constructor)
|
||||
# 1409| <params>:
|
||||
# 1409| getParameter(0): [Parameter] v
|
||||
@@ -20584,7 +20584,7 @@ ir.cpp:
|
||||
# 2256| Type = [LValueReferenceType] T &
|
||||
# 2256| ValueCategory = prvalue(load)
|
||||
# 2256| getExpr().getFullyConverted(): [ReferenceDereferenceExpr] (reference dereference)
|
||||
# 2256| Type = [TemplateParameter] T
|
||||
# 2256| Type = [TypeTemplateParameter] T
|
||||
# 2256| ValueCategory = lvalue
|
||||
# 2256| [FunctionTemplateInstantiation,TopLevelFunction] int& vacuous_destructor_call::get<int>(int&)
|
||||
# 2256| <params>:
|
||||
@@ -20648,7 +20648,7 @@ ir.cpp:
|
||||
# 2260| Type = [LValueReferenceType] T &
|
||||
# 2260| ValueCategory = prvalue(load)
|
||||
# 2260| getArgument(0).getFullyConverted(): [ReferenceDereferenceExpr] (reference dereference)
|
||||
# 2260| Type = [TemplateParameter] T
|
||||
# 2260| Type = [TypeTemplateParameter] T
|
||||
# 2260| ValueCategory = lvalue
|
||||
# 2261| getStmt(1): [ReturnStmt] return ...
|
||||
# 2259| [FunctionTemplateInstantiation,TopLevelFunction] void vacuous_destructor_call::call_destructor<int>(int&)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
| proxy_class.cpp:1:8:1:11 | Base | Struct |
|
||||
| proxy_class.cpp:3:20:3:20 | T | ProxyClass |
|
||||
| proxy_class.cpp:3:20:3:20 | T | TemplateParameter |
|
||||
| proxy_class.cpp:3:20:3:20 | T | TypeTemplateParameter |
|
||||
| proxy_class.cpp:4:8:4:14 | Derived<Base> | Struct |
|
||||
|
||||
@@ -3,7 +3,7 @@ import cpp
|
||||
string clazz(Declaration d) {
|
||||
d instanceof ProxyClass and result = "ProxyClass"
|
||||
or
|
||||
d instanceof TemplateParameter and result = "TemplateParameter"
|
||||
d instanceof TypeTemplateParameter and result = "TypeTemplateParameter"
|
||||
or
|
||||
d instanceof Struct and result = "Struct"
|
||||
}
|
||||
|
||||
@@ -52,6 +52,9 @@ edges
|
||||
| test.cpp:541:39:541:40 | sscanf output argument | test.cpp:549:8:549:8 | e | provenance | |
|
||||
| test.cpp:541:43:541:44 | sscanf output argument | test.cpp:545:8:545:8 | f | provenance | |
|
||||
| test.cpp:541:43:541:44 | sscanf output argument | test.cpp:550:8:550:8 | f | provenance | |
|
||||
| test.cpp:559:30:559:31 | scanf output argument | test.cpp:561:9:561:9 | i | provenance | |
|
||||
| test.cpp:567:35:567:36 | scanf output argument | test.cpp:569:9:569:9 | i | provenance | |
|
||||
| test.cpp:575:30:575:31 | scanf output argument | test.cpp:577:9:577:9 | i | provenance | |
|
||||
nodes
|
||||
| test.cpp:34:15:34:16 | scanf output argument | semmle.label | scanf output argument |
|
||||
| test.cpp:35:7:35:7 | i | semmle.label | i |
|
||||
@@ -154,6 +157,12 @@ nodes
|
||||
| test.cpp:548:8:548:8 | d | semmle.label | d |
|
||||
| test.cpp:549:8:549:8 | e | semmle.label | e |
|
||||
| test.cpp:550:8:550:8 | f | semmle.label | f |
|
||||
| test.cpp:559:30:559:31 | scanf output argument | semmle.label | scanf output argument |
|
||||
| test.cpp:561:9:561:9 | i | semmle.label | i |
|
||||
| test.cpp:567:35:567:36 | scanf output argument | semmle.label | scanf output argument |
|
||||
| test.cpp:569:9:569:9 | i | semmle.label | i |
|
||||
| test.cpp:575:30:575:31 | scanf output argument | semmle.label | scanf output argument |
|
||||
| test.cpp:577:9:577:9 | i | semmle.label | i |
|
||||
subpaths
|
||||
#select
|
||||
| test.cpp:35:7:35:7 | i | test.cpp:34:15:34:16 | scanf output argument | test.cpp:35:7:35:7 | i | This variable is read, but may not have been written. It should be guarded by a check that the $@ returns at least 1. | test.cpp:34:3:34:7 | call to scanf | call to scanf |
|
||||
@@ -177,3 +186,5 @@ subpaths
|
||||
| test.cpp:484:9:484:9 | i | test.cpp:480:25:480:26 | scanf output argument | test.cpp:484:9:484:9 | i | This variable is read, but may not have been written. It should be guarded by a check that the $@ returns at least 1. | test.cpp:480:13:480:17 | call to scanf | call to scanf |
|
||||
| test.cpp:495:8:495:8 | i | test.cpp:491:25:491:26 | scanf output argument | test.cpp:495:8:495:8 | i | This variable is read, but may not have been written. It should be guarded by a check that the $@ returns at least 1. | test.cpp:491:13:491:17 | call to scanf | call to scanf |
|
||||
| test.cpp:545:8:545:8 | f | test.cpp:541:43:541:44 | sscanf output argument | test.cpp:545:8:545:8 | f | This variable is read, but may not have been written. It should be guarded by a check that the $@ returns at least 3. | test.cpp:541:10:541:15 | call to sscanf | call to sscanf |
|
||||
| test.cpp:569:9:569:9 | i | test.cpp:567:35:567:36 | scanf output argument | test.cpp:569:9:569:9 | i | This variable is read, but may not have been written. It should be guarded by a check that the $@ returns at least 1. | test.cpp:567:23:567:27 | call to scanf | call to scanf |
|
||||
| test.cpp:577:9:577:9 | i | test.cpp:575:30:575:31 | scanf output argument | test.cpp:577:9:577:9 | i | This variable is read, but may not have been written. It should be guarded by a check that the $@ returns at least 1. | test.cpp:575:18:575:22 | call to scanf | call to scanf |
|
||||
|
||||
@@ -553,3 +553,27 @@ void switch_cases(const char *data) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void test_scanf_compared_right_away() {
|
||||
int i;
|
||||
bool success = scanf("%d", &i) == 1;
|
||||
if(success) {
|
||||
use(i); // GOOD
|
||||
}
|
||||
}
|
||||
|
||||
void test_scanf_compared_in_conjunct_right(bool b) {
|
||||
int i;
|
||||
bool success = b && scanf("%d", &i) == 1;
|
||||
if(success) {
|
||||
use(i); // GOOD [FALSE POSITIVE]
|
||||
}
|
||||
}
|
||||
|
||||
void test_scanf_compared_in_conjunct_left(bool b) {
|
||||
int i;
|
||||
bool success = scanf("%d", &i) == 1 && b;
|
||||
if(success) {
|
||||
use(i); // GOOD [FALSE POSITIVE]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ JsonToItemsTaskFactory,,,11,,,,,,,,,,,,,,,,,,,1,10
|
||||
Microsoft.Android.Build,,1,14,,,,,,,,,,,,,1,,,,,,12,2
|
||||
Microsoft.Apple.Build,,,7,,,,,,,,,,,,,,,,,,,7,
|
||||
Microsoft.ApplicationBlocks.Data,28,,,,,,,,,,,,28,,,,,,,,,,
|
||||
Microsoft.AspNetCore.Components,,2,1,,,,,,,,,,,,,,,,2,,,1,
|
||||
Microsoft.AspNetCore.Components,2,2,2,,,,,,,2,,,,,,,,,2,,,1,1
|
||||
Microsoft.AspNetCore.Mvc,,,2,,,,,,,,,,,,,,,,,,,,2
|
||||
Microsoft.AspNetCore.WebUtilities,,,2,,,,,,,,,,,,,,,,,,,2,
|
||||
Microsoft.CSharp,,,2,,,,,,,,,,,,,,,,,,,2,
|
||||
|
||||
|
@@ -9,6 +9,6 @@ C# framework & library support
|
||||
Framework / library,Package,Flow sources,Taint & value steps,Sinks (total),`CWE-079` :sub:`Cross-site scripting`
|
||||
`ServiceStack <https://servicestack.net/>`_,"``ServiceStack.*``, ``ServiceStack``",,7,194,
|
||||
System,"``System.*``, ``System``",47,10819,54,5
|
||||
Others,"``Amazon.Lambda.APIGatewayEvents``, ``Amazon.Lambda.Core``, ``Dapper``, ``ILCompiler``, ``ILLink.RoslynAnalyzer``, ``ILLink.Shared``, ``ILLink.Tasks``, ``Internal.IL``, ``Internal.Pgo``, ``Internal.TypeSystem``, ``JsonToItemsTaskFactory``, ``Microsoft.Android.Build``, ``Microsoft.Apple.Build``, ``Microsoft.ApplicationBlocks.Data``, ``Microsoft.AspNetCore.Components``, ``Microsoft.AspNetCore.Mvc``, ``Microsoft.AspNetCore.WebUtilities``, ``Microsoft.CSharp``, ``Microsoft.Diagnostics.Tools.Pgo``, ``Microsoft.DotNet.Build.Tasks``, ``Microsoft.DotNet.PlatformAbstractions``, ``Microsoft.EntityFrameworkCore``, ``Microsoft.Extensions.Caching.Distributed``, ``Microsoft.Extensions.Caching.Memory``, ``Microsoft.Extensions.Configuration``, ``Microsoft.Extensions.DependencyInjection``, ``Microsoft.Extensions.DependencyModel``, ``Microsoft.Extensions.Diagnostics.Metrics``, ``Microsoft.Extensions.FileProviders``, ``Microsoft.Extensions.FileSystemGlobbing``, ``Microsoft.Extensions.Hosting``, ``Microsoft.Extensions.Http``, ``Microsoft.Extensions.Logging``, ``Microsoft.Extensions.Options``, ``Microsoft.Extensions.Primitives``, ``Microsoft.Interop``, ``Microsoft.JSInterop``, ``Microsoft.NET.Build.Tasks``, ``Microsoft.NET.Sdk.WebAssembly``, ``Microsoft.NET.WebAssembly.Webcil``, ``Microsoft.VisualBasic``, ``Microsoft.WebAssembly.Build.Tasks``, ``Microsoft.Win32``, ``Mono.Linker``, ``MySql.Data.MySqlClient``, ``Newtonsoft.Json``, ``SourceGenerators``, ``Windows.Security.Cryptography.Core``",59,2073,150,2
|
||||
Totals,,106,12899,398,7
|
||||
Others,"``Amazon.Lambda.APIGatewayEvents``, ``Amazon.Lambda.Core``, ``Dapper``, ``ILCompiler``, ``ILLink.RoslynAnalyzer``, ``ILLink.Shared``, ``ILLink.Tasks``, ``Internal.IL``, ``Internal.Pgo``, ``Internal.TypeSystem``, ``JsonToItemsTaskFactory``, ``Microsoft.Android.Build``, ``Microsoft.Apple.Build``, ``Microsoft.ApplicationBlocks.Data``, ``Microsoft.AspNetCore.Components``, ``Microsoft.AspNetCore.Mvc``, ``Microsoft.AspNetCore.WebUtilities``, ``Microsoft.CSharp``, ``Microsoft.Diagnostics.Tools.Pgo``, ``Microsoft.DotNet.Build.Tasks``, ``Microsoft.DotNet.PlatformAbstractions``, ``Microsoft.EntityFrameworkCore``, ``Microsoft.Extensions.Caching.Distributed``, ``Microsoft.Extensions.Caching.Memory``, ``Microsoft.Extensions.Configuration``, ``Microsoft.Extensions.DependencyInjection``, ``Microsoft.Extensions.DependencyModel``, ``Microsoft.Extensions.Diagnostics.Metrics``, ``Microsoft.Extensions.FileProviders``, ``Microsoft.Extensions.FileSystemGlobbing``, ``Microsoft.Extensions.Hosting``, ``Microsoft.Extensions.Http``, ``Microsoft.Extensions.Logging``, ``Microsoft.Extensions.Options``, ``Microsoft.Extensions.Primitives``, ``Microsoft.Interop``, ``Microsoft.JSInterop``, ``Microsoft.NET.Build.Tasks``, ``Microsoft.NET.Sdk.WebAssembly``, ``Microsoft.NET.WebAssembly.Webcil``, ``Microsoft.VisualBasic``, ``Microsoft.WebAssembly.Build.Tasks``, ``Microsoft.Win32``, ``Mono.Linker``, ``MySql.Data.MySqlClient``, ``Newtonsoft.Json``, ``SourceGenerators``, ``Windows.Security.Cryptography.Core``",59,2074,152,4
|
||||
Totals,,106,12900,400,9
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Added the constructor and explicit cast operator of `Microsoft.AspNetCore.Components.MarkupString` as an `html-injection` sink. This will help catch cross-site scripting resulting from using `MarkupString`.
|
||||
@@ -0,0 +1,6 @@
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: codeql/csharp-all
|
||||
extensible: summaryModel
|
||||
data:
|
||||
- ["Microsoft.AspNetCore.Components.CompilerServices", "RuntimeHelpers", False, "TypeCheck<T>", "(T)", "", "Argument[0]", "ReturnValue", "value", "manual"]
|
||||
@@ -10,3 +10,9 @@ extensions:
|
||||
extensible: summaryModel
|
||||
data:
|
||||
- ["Microsoft.AspNetCore.Components", "NagivationManager", True, "ToAbsoluteUri", "(System.String)", "", "Argument[0]", "ReturnValue", "taint", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/csharp-all
|
||||
extensible: sinkModel
|
||||
data:
|
||||
- ["Microsoft.AspNetCore.Components", "MarkupString", False, "MarkupString", "(System.String)", "", "Argument[0]", "html-injection", "manual"]
|
||||
- ["Microsoft.AspNetCore.Components", "MarkupString", False, "op_Explicit", "(System.String)", "", "Argument[0]", "html-injection", "manual"]
|
||||
|
||||
163
csharp/ql/lib/semmle/code/csharp/telemetry/ExternalApi.qll
Normal file
163
csharp/ql/lib/semmle/code/csharp/telemetry/ExternalApi.qll
Normal file
@@ -0,0 +1,163 @@
|
||||
/** Provides classes and predicates related to handling APIs from external libraries. */
|
||||
|
||||
private import csharp
|
||||
private import semmle.code.csharp.dispatch.Dispatch
|
||||
private import semmle.code.csharp.dataflow.FlowSummary
|
||||
private import semmle.code.csharp.dataflow.internal.DataFlowPrivate
|
||||
private import semmle.code.csharp.dataflow.internal.DataFlowDispatch as DataFlowDispatch
|
||||
private import semmle.code.csharp.dataflow.internal.ExternalFlow
|
||||
private import semmle.code.csharp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
private import semmle.code.csharp.dataflow.internal.TaintTrackingPrivate
|
||||
private import semmle.code.csharp.security.dataflow.flowsources.ApiSources as ApiSources
|
||||
private import semmle.code.csharp.security.dataflow.flowsinks.ApiSinks as ApiSinks
|
||||
private import TestLibrary
|
||||
|
||||
/** Holds if the given callable is not worth supporting. */
|
||||
private predicate isUninteresting(Callable c) {
|
||||
c.getDeclaringType() instanceof TestLibrary
|
||||
or
|
||||
c.(Constructor).isParameterless()
|
||||
or
|
||||
// The data flow library uses read/store steps for properties, so we don't need to model them,
|
||||
// if both a getter and a setter exist.
|
||||
c.(Accessor).getDeclaration().(Property).isReadWrite()
|
||||
}
|
||||
|
||||
/**
|
||||
* An external API from either the C# Standard Library or a 3rd party library.
|
||||
*/
|
||||
class ExternalApi extends Callable {
|
||||
ExternalApi() {
|
||||
this.isUnboundDeclaration() and
|
||||
this.fromLibrary() and
|
||||
this.(Modifiable).isEffectivelyPublic() and
|
||||
not isUninteresting(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the unbound type, name and parameter types of this API.
|
||||
*/
|
||||
bindingset[this]
|
||||
private string getSignature() {
|
||||
result =
|
||||
nestedName(this.getDeclaringType().getUnboundDeclaration()) + "." + this.getName() + "(" +
|
||||
parameterQualifiedTypeNamesToString(this) + ")"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the namespace of this API.
|
||||
*/
|
||||
bindingset[this]
|
||||
string getNamespace() { this.getDeclaringType().hasFullyQualifiedName(result, _) }
|
||||
|
||||
/**
|
||||
* Gets the namespace and signature of this API.
|
||||
*/
|
||||
bindingset[this]
|
||||
string getApiName() { result = this.getNamespace() + "#" + this.getSignature() }
|
||||
|
||||
/** Gets a node that is an input to a call to this API. */
|
||||
private ArgumentNode getAnInput() {
|
||||
result
|
||||
.getCall()
|
||||
.(DataFlowDispatch::NonDelegateDataFlowCall)
|
||||
.getATarget(_)
|
||||
.getUnboundDeclaration() = this
|
||||
}
|
||||
|
||||
/** Gets a node that is an output from a call to this API. */
|
||||
private DataFlow::Node getAnOutput() {
|
||||
exists(Call c, DataFlowDispatch::NonDelegateDataFlowCall dc |
|
||||
dc.getDispatchCall().getCall() = c and
|
||||
c.getTarget().getUnboundDeclaration() = this
|
||||
|
|
||||
result = DataFlowDispatch::getAnOutNode(dc, _)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if this API has a supported summary. */
|
||||
pragma[nomagic]
|
||||
predicate hasSummary() {
|
||||
this instanceof SummarizedCallable
|
||||
or
|
||||
defaultAdditionalTaintStep(this.getAnInput(), _, _)
|
||||
}
|
||||
|
||||
/** Holds if this API is a known source. */
|
||||
pragma[nomagic]
|
||||
predicate isSource() { this.getAnOutput() instanceof ApiSources::SourceNode }
|
||||
|
||||
/** Holds if this API is a known sink. */
|
||||
pragma[nomagic]
|
||||
predicate isSink() { this.getAnInput() instanceof ApiSinks::SinkNode }
|
||||
|
||||
/** Holds if this API is a known neutral. */
|
||||
pragma[nomagic]
|
||||
predicate isNeutral() { this instanceof FlowSummaryImpl::Public::NeutralCallable }
|
||||
|
||||
/**
|
||||
* Holds if this API is supported by existing CodeQL libraries, that is, it is either a
|
||||
* recognized source, sink or neutral or it has a flow summary.
|
||||
*/
|
||||
predicate isSupported() {
|
||||
this.hasSummary() or this.isSource() or this.isSink() or this.isNeutral()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the nested name of the type `t`.
|
||||
*
|
||||
* If the type is not a nested type, the result is the same as \`getName()\`.
|
||||
* Otherwise the name of the nested type is prefixed with a \`+\` and appended to
|
||||
* the name of the enclosing type, which might be a nested type as well.
|
||||
*/
|
||||
private string nestedName(Type t) {
|
||||
not exists(t.getDeclaringType().getUnboundDeclaration()) and
|
||||
result = t.getName()
|
||||
or
|
||||
nestedName(t.getDeclaringType().getUnboundDeclaration()) + "+" + t.getName() = result
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the limit for the number of results produced by a telemetry query.
|
||||
*/
|
||||
int resultLimit() { result = 100 }
|
||||
|
||||
/**
|
||||
* Holds if it is relevant to count usages of `api`.
|
||||
*/
|
||||
signature predicate relevantApi(ExternalApi api);
|
||||
|
||||
/**
|
||||
* Given a predicate to count relevant API usages, this module provides a predicate
|
||||
* for restricting the number or returned results based on a certain limit.
|
||||
*/
|
||||
module Results<relevantApi/1 getRelevantUsages> {
|
||||
private int getUsages(string apiName) {
|
||||
result =
|
||||
strictcount(Call c, ExternalApi api |
|
||||
c.getTarget().getUnboundDeclaration() = api and
|
||||
apiName = api.getApiName() and
|
||||
getRelevantUsages(api) and
|
||||
c.fromSource()
|
||||
)
|
||||
}
|
||||
|
||||
private int getOrder(string apiName) {
|
||||
apiName =
|
||||
rank[result](string name, int usages |
|
||||
usages = getUsages(name)
|
||||
|
|
||||
name order by usages desc, name
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there exists an API with `apiName` that is being used `usages` times
|
||||
* and if it is in the top results (guarded by resultLimit).
|
||||
*/
|
||||
predicate restrict(string apiName, int usages) {
|
||||
usages = getUsages(apiName) and
|
||||
getOrder(apiName) <= resultLimit()
|
||||
}
|
||||
}
|
||||
18
csharp/ql/lib/semmle/code/csharp/telemetry/TestLibrary.qll
Normal file
18
csharp/ql/lib/semmle/code/csharp/telemetry/TestLibrary.qll
Normal file
@@ -0,0 +1,18 @@
|
||||
/** Provides classes and predicates related to handling test libraries. */
|
||||
|
||||
private import csharp
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate isTestNamespace(Namespace ns) {
|
||||
ns.getFullName()
|
||||
.matches([
|
||||
"NUnit.Framework%", "Xunit%", "Microsoft.VisualStudio.TestTools.UnitTesting%", "Moq%"
|
||||
])
|
||||
}
|
||||
|
||||
/**
|
||||
* A test library.
|
||||
*/
|
||||
class TestLibrary extends RefType {
|
||||
TestLibrary() { isTestNamespace(this.getNamespace()) }
|
||||
}
|
||||
@@ -1,163 +1,4 @@
|
||||
/** Provides classes and predicates related to handling APIs from external libraries. */
|
||||
// Use `semmle.code.csharp.telemetry.ExternalApi` instead.
|
||||
deprecated module;
|
||||
|
||||
private import csharp
|
||||
private import semmle.code.csharp.dispatch.Dispatch
|
||||
private import semmle.code.csharp.dataflow.FlowSummary
|
||||
private import semmle.code.csharp.dataflow.internal.DataFlowPrivate
|
||||
private import semmle.code.csharp.dataflow.internal.DataFlowDispatch as DataFlowDispatch
|
||||
private import semmle.code.csharp.dataflow.internal.ExternalFlow
|
||||
private import semmle.code.csharp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
private import semmle.code.csharp.dataflow.internal.TaintTrackingPrivate
|
||||
private import semmle.code.csharp.security.dataflow.flowsources.ApiSources as ApiSources
|
||||
private import semmle.code.csharp.security.dataflow.flowsinks.ApiSinks as ApiSinks
|
||||
private import TestLibrary
|
||||
|
||||
/** Holds if the given callable is not worth supporting. */
|
||||
private predicate isUninteresting(Callable c) {
|
||||
c.getDeclaringType() instanceof TestLibrary
|
||||
or
|
||||
c.(Constructor).isParameterless()
|
||||
or
|
||||
// The data flow library uses read/store steps for properties, so we don't need to model them,
|
||||
// if both a getter and a setter exist.
|
||||
c.(Accessor).getDeclaration().(Property).isReadWrite()
|
||||
}
|
||||
|
||||
/**
|
||||
* An external API from either the C# Standard Library or a 3rd party library.
|
||||
*/
|
||||
class ExternalApi extends Callable {
|
||||
ExternalApi() {
|
||||
this.isUnboundDeclaration() and
|
||||
this.fromLibrary() and
|
||||
this.(Modifiable).isEffectivelyPublic() and
|
||||
not isUninteresting(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the unbound type, name and parameter types of this API.
|
||||
*/
|
||||
bindingset[this]
|
||||
private string getSignature() {
|
||||
result =
|
||||
nestedName(this.getDeclaringType().getUnboundDeclaration()) + "." + this.getName() + "(" +
|
||||
parameterQualifiedTypeNamesToString(this) + ")"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the namespace of this API.
|
||||
*/
|
||||
bindingset[this]
|
||||
string getNamespace() { this.getDeclaringType().hasFullyQualifiedName(result, _) }
|
||||
|
||||
/**
|
||||
* Gets the namespace and signature of this API.
|
||||
*/
|
||||
bindingset[this]
|
||||
string getApiName() { result = this.getNamespace() + "#" + this.getSignature() }
|
||||
|
||||
/** Gets a node that is an input to a call to this API. */
|
||||
private ArgumentNode getAnInput() {
|
||||
result
|
||||
.getCall()
|
||||
.(DataFlowDispatch::NonDelegateDataFlowCall)
|
||||
.getATarget(_)
|
||||
.getUnboundDeclaration() = this
|
||||
}
|
||||
|
||||
/** Gets a node that is an output from a call to this API. */
|
||||
private DataFlow::Node getAnOutput() {
|
||||
exists(Call c, DataFlowDispatch::NonDelegateDataFlowCall dc |
|
||||
dc.getDispatchCall().getCall() = c and
|
||||
c.getTarget().getUnboundDeclaration() = this
|
||||
|
|
||||
result = DataFlowDispatch::getAnOutNode(dc, _)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if this API has a supported summary. */
|
||||
pragma[nomagic]
|
||||
predicate hasSummary() {
|
||||
this instanceof SummarizedCallable
|
||||
or
|
||||
defaultAdditionalTaintStep(this.getAnInput(), _, _)
|
||||
}
|
||||
|
||||
/** Holds if this API is a known source. */
|
||||
pragma[nomagic]
|
||||
predicate isSource() { this.getAnOutput() instanceof ApiSources::SourceNode }
|
||||
|
||||
/** Holds if this API is a known sink. */
|
||||
pragma[nomagic]
|
||||
predicate isSink() { this.getAnInput() instanceof ApiSinks::SinkNode }
|
||||
|
||||
/** Holds if this API is a known neutral. */
|
||||
pragma[nomagic]
|
||||
predicate isNeutral() { this instanceof FlowSummaryImpl::Public::NeutralCallable }
|
||||
|
||||
/**
|
||||
* Holds if this API is supported by existing CodeQL libraries, that is, it is either a
|
||||
* recognized source, sink or neutral or it has a flow summary.
|
||||
*/
|
||||
predicate isSupported() {
|
||||
this.hasSummary() or this.isSource() or this.isSink() or this.isNeutral()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the nested name of the type `t`.
|
||||
*
|
||||
* If the type is not a nested type, the result is the same as \`getName()\`.
|
||||
* Otherwise the name of the nested type is prefixed with a \`+\` and appended to
|
||||
* the name of the enclosing type, which might be a nested type as well.
|
||||
*/
|
||||
private string nestedName(Type t) {
|
||||
not exists(t.getDeclaringType().getUnboundDeclaration()) and
|
||||
result = t.getName()
|
||||
or
|
||||
nestedName(t.getDeclaringType().getUnboundDeclaration()) + "+" + t.getName() = result
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the limit for the number of results produced by a telemetry query.
|
||||
*/
|
||||
int resultLimit() { result = 100 }
|
||||
|
||||
/**
|
||||
* Holds if it is relevant to count usages of `api`.
|
||||
*/
|
||||
signature predicate relevantApi(ExternalApi api);
|
||||
|
||||
/**
|
||||
* Given a predicate to count relevant API usages, this module provides a predicate
|
||||
* for restricting the number or returned results based on a certain limit.
|
||||
*/
|
||||
module Results<relevantApi/1 getRelevantUsages> {
|
||||
private int getUsages(string apiName) {
|
||||
result =
|
||||
strictcount(Call c, ExternalApi api |
|
||||
c.getTarget().getUnboundDeclaration() = api and
|
||||
apiName = api.getApiName() and
|
||||
getRelevantUsages(api) and
|
||||
c.fromSource()
|
||||
)
|
||||
}
|
||||
|
||||
private int getOrder(string apiName) {
|
||||
apiName =
|
||||
rank[result](string name, int usages |
|
||||
usages = getUsages(name)
|
||||
|
|
||||
name order by usages desc, name
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there exists an API with `apiName` that is being used `usages` times
|
||||
* and if it is in the top results (guarded by resultLimit).
|
||||
*/
|
||||
predicate restrict(string apiName, int usages) {
|
||||
usages = getUsages(apiName) and
|
||||
getOrder(apiName) <= resultLimit()
|
||||
}
|
||||
}
|
||||
import semmle.code.csharp.telemetry.ExternalApi
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
private import csharp
|
||||
private import semmle.code.csharp.dispatch.Dispatch
|
||||
private import ExternalApi
|
||||
private import semmle.code.csharp.telemetry.ExternalApi
|
||||
|
||||
private predicate getRelevantUsages(string namespace, int usages) {
|
||||
usages =
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
private import csharp
|
||||
private import semmle.code.csharp.dispatch.Dispatch
|
||||
private import ExternalApi
|
||||
private import semmle.code.csharp.telemetry.ExternalApi
|
||||
|
||||
private predicate relevant(ExternalApi api) { api.isSupported() }
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
private import csharp
|
||||
private import semmle.code.csharp.dispatch.Dispatch
|
||||
private import ExternalApi
|
||||
private import semmle.code.csharp.telemetry.ExternalApi
|
||||
|
||||
private predicate relevant(ExternalApi api) { api.isSink() }
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
private import csharp
|
||||
private import semmle.code.csharp.dispatch.Dispatch
|
||||
private import ExternalApi
|
||||
private import semmle.code.csharp.telemetry.ExternalApi
|
||||
|
||||
private predicate relevant(ExternalApi api) { api.isSource() }
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
private import csharp
|
||||
private import semmle.code.csharp.dispatch.Dispatch
|
||||
private import ExternalApi
|
||||
private import semmle.code.csharp.telemetry.ExternalApi
|
||||
|
||||
private predicate relevant(ExternalApi api) { api.hasSummary() }
|
||||
|
||||
|
||||
@@ -1,16 +1,4 @@
|
||||
private import csharp
|
||||
// Use `semmle.code.csharp.telemetry.TestLibrary` instead.
|
||||
deprecated module;
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate isTestNamespace(Namespace ns) {
|
||||
ns.getFullName()
|
||||
.matches([
|
||||
"NUnit.Framework%", "Xunit%", "Microsoft.VisualStudio.TestTools.UnitTesting%", "Moq%"
|
||||
])
|
||||
}
|
||||
|
||||
/**
|
||||
* A test library.
|
||||
*/
|
||||
class TestLibrary extends RefType {
|
||||
TestLibrary() { isTestNamespace(this.getNamespace()) }
|
||||
}
|
||||
import semmle.code.csharp.telemetry.TestLibrary
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
private import csharp
|
||||
private import ExternalApi
|
||||
private import semmle.code.csharp.telemetry.ExternalApi
|
||||
|
||||
private predicate relevant(ExternalApi api) { not api.isSupported() }
|
||||
|
||||
|
||||
4
csharp/ql/src/change-notes/2024-12-17-move-libraries.md
Normal file
4
csharp/ql/src/change-notes/2024-12-17-move-libraries.md
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The `ExternalApi` and `TestLibrary` modules have been moved to the library pack.
|
||||
@@ -9,7 +9,7 @@
|
||||
*/
|
||||
|
||||
private import csharp
|
||||
private import Telemetry.ExternalApi
|
||||
private import semmle.code.csharp.telemetry.ExternalApi
|
||||
|
||||
from Call c, ExternalApi api
|
||||
where
|
||||
|
||||
@@ -5,7 +5,7 @@ private import semmle.code.csharp.dataflow.FlowSummary
|
||||
private import semmle.code.csharp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
private import semmle.code.csharp.dataflow.internal.ExternalFlow
|
||||
private import semmle.code.csharp.frameworks.Test
|
||||
private import Telemetry.TestLibrary
|
||||
private import semmle.code.csharp.telemetry.TestLibrary
|
||||
|
||||
/** Holds if the given callable is not worth supporting. */
|
||||
private predicate isUninteresting(Callable c) {
|
||||
|
||||
@@ -250,6 +250,8 @@ sink
|
||||
| Dapper;SqlMapper;QuerySingleOrDefaultAsync;(System.Data.IDbConnection,System.String,System.Object,System.Data.IDbTransaction,System.Nullable<System.Int32>,System.Nullable<System.Data.CommandType>);Argument[1];sql-injection;manual |
|
||||
| Dapper;SqlMapper;QuerySingleOrDefaultAsync;(System.Data.IDbConnection,System.Type,System.String,System.Object,System.Data.IDbTransaction,System.Nullable<System.Int32>,System.Nullable<System.Data.CommandType>);Argument[2];sql-injection;manual |
|
||||
| Dapper;SqlMapper;QuerySingleOrDefaultAsync<T>;(System.Data.IDbConnection,System.String,System.Object,System.Data.IDbTransaction,System.Nullable<System.Int32>,System.Nullable<System.Data.CommandType>);Argument[1];sql-injection;manual |
|
||||
| Microsoft.AspNetCore.Components;MarkupString;MarkupString;(System.String);Argument[0];html-injection;manual |
|
||||
| Microsoft.AspNetCore.Components;MarkupString;op_Explicit;(System.String);Argument[0];html-injection;manual |
|
||||
| Microsoft.EntityFrameworkCore;RelationalDatabaseFacadeExtensions;ExecuteSqlRaw;(Microsoft.EntityFrameworkCore.Infrastructure.DatabaseFacade,System.String,System.Collections.Generic.IEnumerable<System.Object>);Argument[1];sql-injection;manual |
|
||||
| Microsoft.EntityFrameworkCore;RelationalDatabaseFacadeExtensions;ExecuteSqlRaw;(Microsoft.EntityFrameworkCore.Infrastructure.DatabaseFacade,System.String,System.Object[]);Argument[1];sql-injection;manual |
|
||||
| Microsoft.EntityFrameworkCore;RelationalDatabaseFacadeExtensions;ExecuteSqlRawAsync;(Microsoft.EntityFrameworkCore.Infrastructure.DatabaseFacade,System.String,System.Collections.Generic.IEnumerable<System.Object>,System.Threading.CancellationToken);Argument[1];sql-injection;manual |
|
||||
@@ -951,6 +953,7 @@ summary
|
||||
| Microsoft.AspNetCore.Components.CompilerServices;RuntimeHelpers;InvokeAsynchronousDelegate;(System.Action);Argument[0];Argument[0].Parameter[delegate-self];value;hq-generated |
|
||||
| Microsoft.AspNetCore.Components.CompilerServices;RuntimeHelpers;InvokeAsynchronousDelegate;(System.Func<System.Threading.Tasks.Task>);Argument[0];Argument[0].Parameter[delegate-self];value;hq-generated |
|
||||
| Microsoft.AspNetCore.Components.CompilerServices;RuntimeHelpers;InvokeSynchronousDelegate;(System.Action);Argument[0];Argument[0].Parameter[delegate-self];value;hq-generated |
|
||||
| Microsoft.AspNetCore.Components.CompilerServices;RuntimeHelpers;TypeCheck<T>;(T);Argument[0];ReturnValue;value;manual |
|
||||
| Microsoft.AspNetCore.Components.Forms.Mapping;FormValueMappingContext;set_MapErrorToContainer;(System.Action<System.String,System.Object>);Argument[0];Argument[0].Parameter[delegate-self];value;hq-generated |
|
||||
| Microsoft.AspNetCore.Components.Forms.Mapping;FormValueMappingContext;set_OnError;(System.Action<System.String,System.FormattableString,System.String>);Argument[0];Argument[0].Parameter[delegate-self];value;hq-generated |
|
||||
| Microsoft.AspNetCore.Components.Forms;EditContext;GetValidationMessages;(System.Linq.Expressions.Expression<System.Func<System.Object>>);Argument[0];Argument[0].Parameter[delegate-self];value;hq-generated |
|
||||
|
||||
@@ -164,6 +164,7 @@
|
||||
| Microsoft.AspNetCore.Components.CompilerServices;RuntimeHelpers;InvokeAsynchronousDelegate;(System.Action);Argument[0];Argument[0].Parameter[delegate-self];value;hq-generated |
|
||||
| Microsoft.AspNetCore.Components.CompilerServices;RuntimeHelpers;InvokeAsynchronousDelegate;(System.Func<System.Threading.Tasks.Task>);Argument[0];Argument[0].Parameter[delegate-self];value;hq-generated |
|
||||
| Microsoft.AspNetCore.Components.CompilerServices;RuntimeHelpers;InvokeSynchronousDelegate;(System.Action);Argument[0];Argument[0].Parameter[delegate-self];value;hq-generated |
|
||||
| Microsoft.AspNetCore.Components.CompilerServices;RuntimeHelpers;TypeCheck<T>;(T);Argument[0];ReturnValue;value;manual |
|
||||
| Microsoft.AspNetCore.Components.Forms.Mapping;FormValueMappingContext;set_MapErrorToContainer;(System.Action<System.String,System.Object>);Argument[0];Argument[0].Parameter[delegate-self];value;hq-generated |
|
||||
| Microsoft.AspNetCore.Components.Forms.Mapping;FormValueMappingContext;set_OnError;(System.Action<System.String,System.FormattableString,System.String>);Argument[0];Argument[0].Parameter[delegate-self];value;hq-generated |
|
||||
| Microsoft.AspNetCore.Components.Forms;EditContext;GetValidationMessages;(System.Linq.Expressions.Expression<System.Func<System.Object>>);Argument[0];Argument[0].Parameter[delegate-self];value;hq-generated |
|
||||
|
||||
@@ -204,58 +204,45 @@ data flow solver that can check whether there is (global) data flow from a sourc
|
||||
Optionally, configurations may specify extra data flow edges to be added to the data flow graph, and may also specify `barriers`. Barriers are data flow nodes or edges through
|
||||
which data should not be tracked for the purposes of this analysis.
|
||||
|
||||
To define a configuration, extend the class ``DataFlow::Configuration`` as follows:
|
||||
To define a configuration, add a module that implements the signature ``DataFlow::ConfigSig`` and pass it to ``DataFlow::Global`` as follows:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
class MyDataFlowConfiguration extends DataFlow::Configuration {
|
||||
MyDataFlowConfiguration() { this = "MyDataFlowConfiguration" }
|
||||
module MyAnalysisConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { /* ... */ }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { /* ... */ }
|
||||
predicate isSink(DataFlow::Node sink) { /* ... */ }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { /* ... */ }
|
||||
|
||||
// optional overrides:
|
||||
override predicate isBarrier(DataFlow::Node nd) { /* ... */ }
|
||||
override predicate isBarrierEdge(DataFlow::Node pred, DataFlow::Node succ) { /* ... */ }
|
||||
override predicate isAdditionalFlowStep(DataFlow::Node pred, DataFlow::Node succ) { /* ... */ }
|
||||
// optional predicates:
|
||||
predicate isBarrier(DataFlow::Node nd) { /* ... */ }
|
||||
predicate isAdditionalFlowStep(DataFlow::Node pred, DataFlow::Node succ) { /* ... */ }
|
||||
}
|
||||
|
||||
The characteristic predicate ``MyDataFlowConfiguration()`` defines the name of the configuration, so ``"MyDataFlowConfiguration"`` should be replaced by a suitable
|
||||
name describing your particular analysis configuration.
|
||||
module MyAnalysisFlow = DataFlow::Global<MyAnalysisConfig>
|
||||
|
||||
The data flow analysis is performed using the predicate ``hasFlow(source, sink)``:
|
||||
The data flow analysis is performed using the predicate ``MyAnalysisFlow::flow(source, sink)``:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
from MyDataFlowConfiguration dataflow, DataFlow::Node source, DataFlow::Node sink
|
||||
where dataflow.hasFlow(source, sink)
|
||||
from DataFlow::Node source, DataFlow::Node sink
|
||||
where MyAnalysisFlow::flow(source, sink)
|
||||
select source, "Data flow from $@ to $@.", source, source.toString(), sink, sink.toString()
|
||||
|
||||
Using global taint tracking
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Global taint tracking extends global data flow with additional non-value-preserving steps, such as flow through string-manipulating operations. To use it, simply extend
|
||||
``TaintTracking::Configuration`` instead of ``DataFlow::Configuration``:
|
||||
Global taint tracking extends global data flow with additional non-value-preserving steps, such as flow through string-manipulating operations. To use it, simply
|
||||
use ``TaintTracking::Global<...>`` instead of ``DataFlow::Global<...>``:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
class MyTaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
MyTaintTrackingConfiguration() { this = "MyTaintTrackingConfiguration" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { /* ... */ }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { /* ... */ }
|
||||
module MyAnalysisConfig implements DataFlow::ConfigSig {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
Analogous to ``isAdditionalFlowStep``, there is a predicate ``isAdditionalTaintStep`` that you can override to specify custom flow steps to consider in the analysis.
|
||||
Instead of the ``isBarrier`` and ``isBarrierEdge`` predicates, the taint tracking configuration includes ``isSanitizer`` and ``isSanitizerEdge`` predicates that specify
|
||||
data flow nodes or edges that act as taint sanitizers and hence stop flow from a source to a sink.
|
||||
module MyAnalysisFlow = TaintTracking::Global<MyAnalysisConfig>
|
||||
|
||||
Similar to global data flow, the characteristic predicate ``MyTaintTrackingConfiguration()`` defines the unique name of the configuration, so ``"MyTaintTrackingConfiguration"``
|
||||
should be replaced by an appropriate descriptive name.
|
||||
|
||||
The taint tracking analysis is again performed using the predicate ``hasFlow(source, sink)``.
|
||||
The taint tracking analysis is again performed using the predicate ``MyAnalysisFlow::flow(source, sink)``.
|
||||
|
||||
Examples
|
||||
~~~~~~~~
|
||||
@@ -267,20 +254,20 @@ time using global taint tracking.
|
||||
|
||||
import javascript
|
||||
|
||||
class CommandLineFileNameConfiguration extends TaintTracking::Configuration {
|
||||
CommandLineFileNameConfiguration() { this = "CommandLineFileNameConfiguration" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
module CommandLineFileNameConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
DataFlow::globalVarRef("process").getAPropertyRead("argv").getAPropertyRead() = source
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
DataFlow::moduleMember("fs", "readFile").getACall().getArgument(0) = sink
|
||||
}
|
||||
}
|
||||
|
||||
from CommandLineFileNameConfiguration cfg, DataFlow::Node source, DataFlow::Node sink
|
||||
where cfg.hasFlow(source, sink)
|
||||
module CommandLineFileNameFlow = TaintTracking::Global<CommandLineFileNameConfig>;
|
||||
|
||||
from DataFlow::Node source, DataFlow::Node sink
|
||||
where CommandLineFileNameFlow::flow(source, sink)
|
||||
select source, sink
|
||||
|
||||
This query will now find flows that involve inter-procedural steps, like in the following example (where the individual steps have been marked with comments
|
||||
@@ -325,15 +312,15 @@ with an error if it does not. We could then use that function in ``readFileHelpe
|
||||
}
|
||||
|
||||
For the purposes of our above analysis, ``checkPath`` is a `sanitizer`: its output is always untainted, even if its input is tainted. To model this
|
||||
we can add an override of ``isSanitizer`` to our taint-tracking configuration like this:
|
||||
we can add an ``isBarrier`` predicate to our taint-tracking configuration like this:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
class CommandLineFileNameConfiguration extends TaintTracking::Configuration {
|
||||
module CommandLineFileNameConfig implements DataFlow::ConfigSig {
|
||||
|
||||
// ...
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node nd) {
|
||||
predicate isBarrier(DataFlow::Node nd) {
|
||||
nd.(DataFlow::CallNode).getCalleeName() = "checkPath"
|
||||
}
|
||||
}
|
||||
@@ -359,36 +346,36 @@ Note that ``checkPath`` is now no longer a sanitizer in the sense described abov
|
||||
through ``checkPath`` any more. The flow is, however, `guarded` by ``checkPath`` in the sense that the expression ``checkPath(p)`` has to evaluate
|
||||
to ``true`` (or, more precisely, to a truthy value) in order for the flow to happen.
|
||||
|
||||
Such sanitizer guards can be supported by defining a new subclass of ``TaintTracking::SanitizerGuardNode`` and overriding the predicate
|
||||
``isSanitizerGuard`` in the taint-tracking configuration class to add all instances of this class as sanitizer guards to the configuration.
|
||||
Such sanitizer guards can be supported by defining a class with a ``blocksExpr`` predicate and using the `DataFlow::MakeBarrierGuard`` module
|
||||
to implement the ``isBarrier`` predicate.
|
||||
|
||||
For our above example, we would begin by defining a subclass of ``SanitizerGuardNode`` that identifies guards of the form ``checkPath(...)``:
|
||||
For our above example, we would begin by defining a subclass of ``DataFlow::CallNode`` that identifies guards of the form ``checkPath(...)``:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
class CheckPathSanitizerGuard extends TaintTracking::SanitizerGuardNode, DataFlow::CallNode {
|
||||
class CheckPathSanitizerGuard extends DataFlow::CallNode {
|
||||
CheckPathSanitizerGuard() { this.getCalleeName() = "checkPath" }
|
||||
|
||||
override predicate sanitizes(boolean outcome, Expr e) {
|
||||
predicate blocksExpr(boolean outcome, Expr e) {
|
||||
outcome = true and
|
||||
e = getArgument(0).asExpr()
|
||||
e = this.getArgument(0).asExpr()
|
||||
}
|
||||
}
|
||||
|
||||
The characteristic predicate of this class checks that the sanitizer guard is a call to a function named ``checkPath``. The overriding definition
|
||||
of ``sanitizes`` says such a call sanitizes its first argument (that is, ``getArgument(0)``) if it evaluates to ``true`` (or rather, a truthy
|
||||
The characteristic predicate of this class checks that the sanitizer guard is a call to a function named ``checkPath``. The definition
|
||||
of ``blocksExpr`` says such a call sanitizes its first argument (that is, ``getArgument(0)``) if it evaluates to ``true`` (or rather, a truthy
|
||||
value).
|
||||
|
||||
Now we can override ``isSanitizerGuard`` to add these sanitizer guards to our configuration:
|
||||
Now we can implement ``isBarrier`` to add this sanitizer guard to our configuration:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
class CommandLineFileNameConfiguration extends TaintTracking::Configuration {
|
||||
module CommandLineFileNameConfig implements DataFlow::ConfigSig {
|
||||
|
||||
// ...
|
||||
|
||||
override predicate isSanitizerGuard(TaintTracking::SanitizerGuardNode nd) {
|
||||
nd instanceof CheckPathSanitizerGuard
|
||||
predicate isBarrier(DataFlow::Node node) {
|
||||
node = DataFlow::MakeBarrierGuard<CheckPathSanitizerGuard>::getABarrierNode()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -399,7 +386,7 @@ reach there if ``checkPath(p)`` evaluates to a truthy value. Consequently, there
|
||||
Additional taint steps
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Sometimes the default data flow and taint steps provided by ``DataFlow::Configuration`` and ``TaintTracking::Configuration`` are not sufficient
|
||||
Sometimes the default data flow and taint steps provided by the data flow library are not sufficient
|
||||
and we need to add additional flow or taint steps to our configuration to make it find the expected flow. For example, this can happen because
|
||||
the analyzed program uses a function from an external library whose source code is not available to the analysis, or because it uses a function
|
||||
that is too difficult to analyze.
|
||||
@@ -420,20 +407,20 @@ to resolve any symlinks in the path ``p`` before passing it to ``readFile``:
|
||||
Resolving symlinks does not make an unsafe path any safer, so we would still like our query to flag this, but since the standard library does
|
||||
not have a model of ``resolve-symlinks`` it will no longer return any results.
|
||||
|
||||
We can fix this quite easily by adding an overriding definition of the ``isAdditionalTaintStep`` predicate to our configuration, introducing an
|
||||
We can fix this quite easily by adding a definition of the ``isAdditionalFlowStep`` predicate to our configuration, introducing an
|
||||
additional taint step from the first argument of ``resolveSymlinks`` to its result:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
class CommandLineFileNameConfiguration extends TaintTracking::Configuration {
|
||||
module CommandLineFileNameConfig implements DataFlow::ConfigSig {
|
||||
|
||||
// ...
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
exists(DataFlow::CallNode c |
|
||||
c = DataFlow::moduleImport("resolve-symlinks").getACall() and
|
||||
pred = c.getArgument(0) and
|
||||
succ = c
|
||||
node1 = c.getArgument(0) and
|
||||
node2 = c
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -444,11 +431,11 @@ to wrap it in a new subclass of ``TaintTracking::SharedTaintStep`` like this:
|
||||
.. code-block:: ql
|
||||
|
||||
class StepThroughResolveSymlinks extends TaintTracking::SharedTaintStep {
|
||||
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
override predicate step(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
exists(DataFlow::CallNode c |
|
||||
c = DataFlow::moduleImport("resolve-symlinks").getACall() and
|
||||
pred = c.getArgument(0) and
|
||||
succ = c
|
||||
node1 = c.getArgument(0) and
|
||||
node2 = c
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -494,18 +481,18 @@ Exercise 2
|
||||
|
||||
import javascript
|
||||
|
||||
class HardCodedTagNameConfiguration extends DataFlow::Configuration {
|
||||
HardCodedTagNameConfiguration() { this = "HardCodedTagNameConfiguration" }
|
||||
module HardCodedTagNameConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source.asExpr() instanceof ConstantString }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source.asExpr() instanceof ConstantString }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
sink = DataFlow::globalVarRef("document").getAMethodCall("createElement").getArgument(0)
|
||||
}
|
||||
}
|
||||
|
||||
from HardCodedTagNameConfiguration cfg, DataFlow::Node source, DataFlow::Node sink
|
||||
where cfg.hasFlow(source, sink)
|
||||
module HardCodedTagNameFlow = DataFlow::Global<HardCodedTagNameConfig>;
|
||||
|
||||
from DataFlow::Node source, DataFlow::Node sink
|
||||
where HardCodedTagNameFlow::flow(source, sink)
|
||||
select source, sink
|
||||
|
||||
Exercise 3
|
||||
@@ -540,18 +527,18 @@ Exercise 4
|
||||
}
|
||||
}
|
||||
|
||||
class HardCodedTagNameConfiguration extends DataFlow::Configuration {
|
||||
HardCodedTagNameConfiguration() { this = "HardCodedTagNameConfiguration" }
|
||||
module HardCodedTagNameConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof ArrayEntryCallResult }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof ArrayEntryCallResult }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
sink = DataFlow::globalVarRef("document").getAMethodCall("createElement").getArgument(0)
|
||||
}
|
||||
}
|
||||
|
||||
from HardCodedTagNameConfiguration cfg, DataFlow::Node source, DataFlow::Node sink
|
||||
where cfg.hasFlow(source, sink)
|
||||
module HardCodedTagNameFlow = DataFlow::Global<HardCodedTagNameConfig>;
|
||||
|
||||
from DataFlow::Node source, DataFlow::Node sink
|
||||
where HardCodedTagNameFlow::flow(source, sink)
|
||||
select source, sink
|
||||
|
||||
Further reading
|
||||
|
||||
@@ -700,19 +700,16 @@ The data flow graph-based analyses described so far are all intraprocedural: the
|
||||
|
||||
We distinguish here between data flow proper, and *taint tracking*: the latter not only considers value-preserving flow (such as from variable definitions to uses), but also cases where one value influences ("taints") another without determining it entirely. For example, in the assignment ``s2 = s1.substring(i)``, the value of ``s1`` influences the value of ``s2``, because ``s2`` is assigned a substring of ``s1``. In general, ``s2`` will not be assigned ``s1`` itself, so there is no data flow from ``s1`` to ``s2``, but ``s1`` still taints ``s2``.
|
||||
|
||||
It is a common pattern that we wish to specify data flow or taint analysis in terms of its *sources* (where flow starts), *sinks* (where it should be tracked), and *barriers* or *sanitizers* (where flow is interrupted). Sanitizers they are very common in security analyses: for example, an analysis that tracks the flow of untrusted user input into, say, a SQL query has to keep track of code that validates the input, thereby making it safe to use. Such a validation step is an example of a sanitizer.
|
||||
It is a common pattern that we wish to specify data flow or taint analysis in terms of its *sources* (where flow starts), *sinks* (where it should be tracked), and *barriers* (also called *sanitizers*) where flow is interrupted. Sanitizers they are very common in security analyses: for example, an analysis that tracks the flow of untrusted user input into, say, a SQL query has to keep track of code that validates the input, thereby making it safe to use. Such a validation step is an example of a sanitizer.
|
||||
|
||||
The classes ``DataFlow::Configuration`` and ``TaintTracking::Configuration`` allow specifying a data flow or taint analysis, respectively, by overriding the following predicates:
|
||||
A module implementing the signature `DataFlow::ConfigSig` may specify a data flow or taint analysis by implementing the following predicates:
|
||||
|
||||
- ``isSource(DataFlow::Node nd)`` selects all nodes ``nd`` from where flow tracking starts.
|
||||
- ``isSink(DataFlow::Node nd)`` selects all nodes ``nd`` to which the flow is tracked.
|
||||
- ``isBarrier(DataFlow::Node nd)`` selects all nodes ``nd`` that act as a barrier for data flow; ``isSanitizer`` is the corresponding predicate for taint tracking configurations.
|
||||
- ``isBarrierEdge(DataFlow::Node src, DataFlow::Node trg)`` is a variant of ``isBarrier(nd)`` that allows specifying barrier *edges* in addition to barrier nodes; again, ``isSanitizerEdge`` is the corresponding predicate for taint tracking;
|
||||
- ``isAdditionalFlowStep(DataFlow::Node src, DataFlow::Node trg)`` allows specifying custom additional flow steps for this analysis; ``isAdditionalTaintStep`` is the corresponding predicate for taint tracking configurations.
|
||||
- ``isBarrier(DataFlow::Node nd)`` selects all nodes ``nd`` that act as a barrier/sanitizer for data flow.
|
||||
- ``isAdditionalFlowStep(DataFlow::Node src, DataFlow::Node trg)`` allows specifying custom additional flow steps for this analysis.
|
||||
|
||||
Since for technical reasons both ``Configuration`` classes are subtypes of ``string``, you have to choose a unique name for each flow configuration and equate ``this`` with it in the characteristic predicate (as in the example below).
|
||||
|
||||
The predicate ``Configuration.hasFlow`` performs the actual flow tracking, starting at a source and looking for flow to a sink that does not pass through a barrier node or edge.
|
||||
Such a module can be passed to ``DataFlow::Global<...>``. This will produce a module with a ``flow`` predicate that performs the actual flow tracking, starting at a source and looking for flow to a sink that does not pass through a barrier node.
|
||||
|
||||
For example, suppose that we are developing an analysis to find hard-coded passwords. We might write a simple query that looks for string constants flowing into variables named ``"password"``.
|
||||
|
||||
@@ -720,35 +717,27 @@ For example, suppose that we are developing an analysis to find hard-coded passw
|
||||
|
||||
import javascript
|
||||
|
||||
class PasswordTracker extends DataFlow::Configuration {
|
||||
PasswordTracker() {
|
||||
// unique identifier for this configuration
|
||||
this = "PasswordTracker"
|
||||
}
|
||||
module PasswordConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node nd) { nd.asExpr() instanceof StringLiteral }
|
||||
|
||||
override predicate isSource(DataFlow::Node nd) {
|
||||
nd.asExpr() instanceof StringLiteral
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node nd) {
|
||||
passwordVarAssign(_, nd)
|
||||
}
|
||||
|
||||
predicate passwordVarAssign(Variable v, DataFlow::Node nd) {
|
||||
v.getAnAssignedExpr() = nd.asExpr() and
|
||||
v.getName().toLowerCase() = "password"
|
||||
}
|
||||
predicate isSink(DataFlow::Node nd) { passwordVarAssign(_, nd) }
|
||||
}
|
||||
|
||||
Now we can rephrase our query to use ``Configuration.hasFlow``:
|
||||
predicate passwordVarAssign(Variable v, DataFlow::Node nd) {
|
||||
v.getAnAssignedExpr() = nd.asExpr() and
|
||||
v.getName().toLowerCase() = "password"
|
||||
}
|
||||
|
||||
module PasswordFlow = DataFlow::Global<PasswordConfig>;
|
||||
|
||||
Now we can rephrase our query to use ``PasswordFlow::flow``:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
from PasswordTracker pt, DataFlow::Node source, DataFlow::Node sink, Variable v
|
||||
where pt.hasFlow(source, sink) and pt.passwordVarAssign(v, sink)
|
||||
from DataFlow::Node source, DataFlow::Node sink, Variable v
|
||||
where PasswordFlow::flow(_, sink) and passwordVarAssign(v, sink)
|
||||
select sink, "Password variable " + v + " is assigned a constant string."
|
||||
|
||||
|
||||
Syntax errors
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
|
||||
@@ -16,18 +16,17 @@ Use the following template to create a taint tracking path query:
|
||||
* @kind path-problem
|
||||
*/
|
||||
import javascript
|
||||
import DataFlow
|
||||
import DataFlow::PathGraph
|
||||
|
||||
class MyConfig extends TaintTracking::Configuration {
|
||||
MyConfig() { this = "MyConfig" }
|
||||
override predicate isSource(Node node) { ... }
|
||||
override predicate isSink(Node node) { ... }
|
||||
override predicate isAdditionalTaintStep(Node pred, Node succ) { ... }
|
||||
module MyConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node node) { ... }
|
||||
predicate isSink(DataFlow::Node node) { ... }
|
||||
predicate isAdditionalFlowStep(DataFlow::Node pred, DataFlow::Node succ) { ... }
|
||||
}
|
||||
|
||||
from MyConfig cfg, PathNode source, PathNode sink
|
||||
where cfg.hasFlowPath(source, sink)
|
||||
module MyFlow = TaintTracking::Global<MyConfig>;
|
||||
|
||||
from MyFlow::PathNode source, MyFlow::PathNode sink
|
||||
where MyFlow::flowPath(source, sink)
|
||||
select sink.getNode(), source, sink, "taint from $@.", source.getNode(), "here"
|
||||
|
||||
This query reports flow paths which:
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
.. _using-flow-labels-for-precise-data-flow-analysis:
|
||||
|
||||
Using flow labels for precise data flow analysis
|
||||
Using flow state for precise data flow analysis
|
||||
================================================
|
||||
|
||||
You can associate flow labels with each value tracked by the flow analysis to determine whether the flow contains potential vulnerabilities.
|
||||
You can associate a flow state with each value tracked by the flow analysis to determine whether the flow contains potential vulnerabilities.
|
||||
|
||||
Overview
|
||||
--------
|
||||
@@ -16,9 +16,9 @@ program, and associates a flag with every data value telling us whether it might
|
||||
source node.
|
||||
|
||||
In some cases, you may want to track more detailed information about data values. This can be done
|
||||
by associating flow labels with data values, as shown in this tutorial. We will first discuss the
|
||||
general idea behind flow labels and then show how to use them in practice. Finally, we will give an
|
||||
overview of the API involved and provide some pointers to standard queries that use flow labels.
|
||||
by associating flow states with data values, as shown in this tutorial. We will first discuss the
|
||||
general idea behind flow states and then show how to use them in practice. Finally, we will give an
|
||||
overview of the API involved and provide some pointers to standard queries that use flow states.
|
||||
|
||||
Limitations of basic data-flow analysis
|
||||
---------------------------------------
|
||||
@@ -47,22 +47,21 @@ contain ``..`` components. Untrusted user input has both bits set initially, ind
|
||||
off individual bits, and if a value that has at least one bit set is interpreted as a path, a
|
||||
potential vulnerability is flagged.
|
||||
|
||||
Using flow labels
|
||||
Using flow states
|
||||
-----------------
|
||||
|
||||
You can handle these cases and others like them by associating a set of `flow labels` (sometimes
|
||||
also referred to as `taint kinds`) with each value being tracked by the analysis. Value-preserving
|
||||
You can handle these cases and others like them by associating a set of `flow states` (sometimes
|
||||
also referred to as `flow labels` or `taint kinds`) with each value being tracked by the analysis. Value-preserving
|
||||
data-flow steps (such as flow steps from writes to a variable to its reads) preserve the set of flow
|
||||
labels, but other steps may add or remove flow labels. Sanitizers, in particular, are simply flow
|
||||
steps that remove some or all flow labels. The initial set of flow labels for a value is determined
|
||||
states, but other steps may add or remove flow states. The initial set of flow states for a value is determined
|
||||
by the source node that gives rise to it. Similarly, sink nodes can specify that an incoming value
|
||||
needs to have a certain flow label (or one of a set of flow labels) in order for the flow to be
|
||||
needs to have a certain flow state (or one of a set of flow states) in order for the flow to be
|
||||
flagged as a potential vulnerability.
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
As an example of using flow labels, we will show how to write a query that flags property accesses
|
||||
As an example of using flow state, we will show how to write a query that flags property accesses
|
||||
on JSON values that come from user-controlled input where we have not checked whether the value is
|
||||
``null``, so that the property access may cause a runtime exception.
|
||||
|
||||
@@ -88,8 +87,8 @@ This code, on the other hand, should not be flagged:
|
||||
}
|
||||
}
|
||||
|
||||
We will first try to write a query to find this kind of problem without flow labels, and use the
|
||||
difficulties we encounter as a motivation for bringing flow labels into play, which will make the
|
||||
We will first try to write a query to find this kind of problem without flow state, and use the
|
||||
difficulties we encounter as a motivation for bringing flow state into play, which will make the
|
||||
query much easier to implement.
|
||||
|
||||
To get started, let's write a query that simply flags any flow from ``JSON.parse`` into the base of
|
||||
@@ -99,24 +98,24 @@ a property access:
|
||||
|
||||
import javascript
|
||||
|
||||
class JsonTrackingConfig extends DataFlow::Configuration {
|
||||
JsonTrackingConfig() { this = "JsonTrackingConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node nd) {
|
||||
module JsonTrackingConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node nd) {
|
||||
exists(JsonParserCall jpc |
|
||||
nd = jpc.getOutput()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node nd) {
|
||||
predicate isSink(DataFlow::Node nd) {
|
||||
exists(DataFlow::PropRef pr |
|
||||
nd = pr.getBase()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
from JsonTrackingConfig cfg, DataFlow::Node source, DataFlow::Node sink
|
||||
where cfg.hasFlow(source, sink)
|
||||
module JsonTrackingFlow = DataFlow::Global<JsonTrackingConfig>;
|
||||
|
||||
from DataFlow::Node source, DataFlow::Node sink
|
||||
where JsonTrackingFlow::flow(source, sink)
|
||||
select sink, "Property access on JSON value originating $@.", source, "here"
|
||||
|
||||
Note that we use the ``JsonParserCall`` class from the standard library to model various JSON
|
||||
@@ -127,8 +126,7 @@ introduced any sanitizers yet.
|
||||
|
||||
There are many ways of checking for nullness directly or indirectly. Since this is not the main
|
||||
focus of this tutorial, we will only show how to model one specific case: if some variable ``v`` is
|
||||
known to be truthy, it cannot be ``null``. This kind of condition is easily expressed using a
|
||||
``BarrierGuardNode`` (or its counterpart ``SanitizerGuardNode`` for taint-tracking configurations).
|
||||
known to be truthy, it cannot be ``null``. This kind of condition is expressed using a "barrier guard".
|
||||
A barrier guard node is a data-flow node ``b`` that blocks flow through some other node ``nd``,
|
||||
provided that some condition checked at ``b`` is known to hold, that is, evaluate to a truthy value.
|
||||
|
||||
@@ -139,29 +137,29 @@ is a barrier guard blocking flow through the use of ``data`` on the right-hand s
|
||||
At this point we know that ``data`` has evaluated to a truthy value, so it cannot be ``null``
|
||||
anymore.
|
||||
|
||||
Implementing this additional condition is easy. We implement a subclass of ``DataFlow::BarrierGuardNode``:
|
||||
Implementing this additional condition is easy. We implement a class with a predicate called ``blocksExpr``:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
class TruthinessCheck extends DataFlow::BarrierGuardNode, DataFlow::ValueNode {
|
||||
class TruthinessCheck extends DataFlow::ValueNode {
|
||||
SsaVariable v;
|
||||
|
||||
TruthinessCheck() {
|
||||
astNode = v.getAUse()
|
||||
}
|
||||
|
||||
override predicate blocks(boolean outcome, Expr e) {
|
||||
predicate blocksExpr(boolean outcome, Expr e) {
|
||||
outcome = true and
|
||||
e = astNode
|
||||
}
|
||||
}
|
||||
|
||||
and then use it to override predicate ``isBarrierGuard`` in our configuration class:
|
||||
and then use it to implement the predicate ``isBarrier`` in our configuration module:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
override predicate isBarrierGuard(DataFlow::BarrierGuardNode guard) {
|
||||
guard instanceof TruthinessCheck
|
||||
predicate isBarrier(DataFlow::Node node) {
|
||||
node = DataFlow::MakeBarrierGuard<TruthinessCheck>::getABarrierNode()
|
||||
}
|
||||
|
||||
With this change, we now flag the problematic case and don't flag the unproblematic case above.
|
||||
@@ -182,11 +180,11 @@ checked for null-guardedness:
|
||||
}
|
||||
}
|
||||
|
||||
We could try to remedy the situation by overriding ``isAdditionalFlowStep`` in our configuration class to track values through property reads:
|
||||
We could try to remedy the situation by adding ``isAdditionalFlowStep`` in our configuration module to track values through property reads:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
override predicate isAdditionalFlowStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
predicate isAdditionalFlowStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
succ.(DataFlow::PropRead).getBase() = pred
|
||||
}
|
||||
|
||||
@@ -199,79 +197,86 @@ altogether, it should simply record the fact that ``root`` itself is known to be
|
||||
Any property read from ``root``, on the other hand, may well be null and needs to be checked
|
||||
separately.
|
||||
|
||||
We can achieve this by introducing two different flow labels, ``json`` and ``maybe-null``. The former
|
||||
We can achieve this by introducing two different flow states, ``json`` and ``maybe-null``. The former
|
||||
means that the value we are dealing with comes from a JSON object, the latter that it may be
|
||||
``null``. The result of any call to ``JSON.parse`` has both labels. A property read from a value
|
||||
with label ``json`` also has both labels. Checking truthiness removes the ``maybe-null`` label.
|
||||
Accessing a property on a value that has the ``maybe-null`` label should be flagged.
|
||||
``null``. The result of any call to ``JSON.parse`` has both states. A property read from a value
|
||||
with state ``json`` also results in a value with both states. Checking truthiness removes the ``maybe-null`` state.
|
||||
Accessing a property on a value that has the ``maybe-null`` state should be flagged.
|
||||
|
||||
To implement this, we start by defining two new subclasses of the class ``DataFlow::FlowLabel``:
|
||||
To implement this, we first change the signature of our configuration module to ``DataFlow::StateConfigSig``, and
|
||||
replace ``DataFlow::Global<...>`` with ``DataFlow::GlobalWithState<...>``:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
class JsonLabel extends DataFlow::FlowLabel {
|
||||
JsonLabel() {
|
||||
this = "json"
|
||||
}
|
||||
module JsonTrackingConfig implements DataFlow::StateConfigSig {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
class MaybeNullLabel extends DataFlow::FlowLabel {
|
||||
MaybeNullLabel() {
|
||||
this = "maybe-null"
|
||||
}
|
||||
}
|
||||
module JsonTrackingFlow = DataFlow::GlobalWithState<JsonTrackingConfig>;
|
||||
|
||||
Then we extend our ``isSource`` predicate from above to track flow labels by overriding the two-argument version instead of the one-argument version:
|
||||
We then add a class called ``FlowState`` which has one value for each flow state:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
override predicate isSource(DataFlow::Node nd, DataFlow::FlowLabel lbl) {
|
||||
module JsonTrackingConfig implements DataFlow::StateConfigSig {
|
||||
class FlowState extends string {
|
||||
FlowState() {
|
||||
this = ["json", "maybe-null"]
|
||||
}
|
||||
}
|
||||
|
||||
/* ... */
|
||||
}
|
||||
|
||||
Then we extend our ``isSource`` predicate with an additional parameter to specify the flow state:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
predicate isSource(DataFlow::Node nd, FlowState state) {
|
||||
exists(JsonParserCall jpc |
|
||||
nd = jpc.getOutput() and
|
||||
(lbl instanceof JsonLabel or lbl instanceof MaybeNullLabel)
|
||||
state = ["json", "maybe-null"] // start in either state
|
||||
)
|
||||
}
|
||||
|
||||
Similarly, we make ``isSink`` flow-label aware and require the base of the property read to have the ``maybe-null`` label:
|
||||
Similarly, we update ``isSink`` and require the base of the property read to have the ``maybe-null`` state:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
override predicate isSink(DataFlow::Node nd, DataFlow::FlowLabel lbl) {
|
||||
predicate isSink(DataFlow::Node nd, FlowState state) {
|
||||
exists(DataFlow::PropRef pr |
|
||||
nd = pr.getBase() and
|
||||
lbl instanceof MaybeNullLabel
|
||||
state = "maybe-null"
|
||||
)
|
||||
}
|
||||
|
||||
Our overriding definition of ``isAdditionalFlowStep`` now needs to specify two flow labels, a
|
||||
predecessor label ``predlbl`` and a successor label ``succlbl``. In addition to specifying flow from
|
||||
the predecessor node ``pred`` to the successor node ``succ``, it requires that ``pred`` has label
|
||||
``predlbl``, and adds label ``succlbl`` to ``succ``. In our case, we use this to add both the
|
||||
``json`` label and the ``maybe-null`` label to any property read from a value labeled with ``json``
|
||||
(no matter whether it has the ``maybe-null`` label):
|
||||
Our definition of ``isAdditionalFlowStep`` now needs to specify two flow states, a
|
||||
predecessor state ``predState`` and a successor state ``succState``. In addition to specifying flow from
|
||||
the predecessor node ``pred`` to the successor node ``succ``, it requires that ``pred`` has state
|
||||
``predState``, and adds state ``succState`` to ``succ``. In our case, we use this to add both the
|
||||
``json`` state and the ``maybe-null`` state to any property read from a value in the ``json`` state
|
||||
(no matter whether it has the ``maybe-null`` state):
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
override predicate isAdditionalFlowStep(DataFlow::Node pred, DataFlow::Node succ,
|
||||
DataFlow::FlowLabel predlbl, DataFlow::FlowLabel succlbl) {
|
||||
predicate isAdditionalFlowStep(DataFlow::Node pred, FlowState predState,
|
||||
DataFlow::Node succ, FlowState succState) {
|
||||
succ.(DataFlow::PropRead).getBase() = pred and
|
||||
predlbl instanceof JsonLabel and
|
||||
(succlbl instanceof JsonLabel or succlbl instanceof MaybeNullLabel)
|
||||
predState = "json" and
|
||||
succState = ["json", "maybe-null"]
|
||||
}
|
||||
|
||||
Finally, we turn ``TruthinessCheck`` from a ``BarrierGuardNode`` into a ``LabeledBarrierGuardNode``,
|
||||
specifying that it only removes the ``maybe-null`` label (but not the ``json`` label) from the
|
||||
sanitized value:
|
||||
Finally, we add an additional parameter to the ``isBarrier`` predicate to specify the flow state
|
||||
to block at the ``TruthinessCheck`` barrier.
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
class TruthinessCheck extends DataFlow::LabeledBarrierGuardNode, DataFlow::ValueNode {
|
||||
...
|
||||
module JsonTrackingConfig implements DataFlow::StateConfigSig {
|
||||
/* ... */
|
||||
|
||||
override predicate blocks(boolean outcome, Expr e, DataFlow::FlowLabel lbl) {
|
||||
outcome = true and
|
||||
e = astNode and
|
||||
lbl instanceof MaybeNullLabel
|
||||
predicate isBarrier(DataFlow::Node node, FlowState state) {
|
||||
node = DataFlow::MakeBarrierGuard<TruthinessCheck>::getABarrierNode() and
|
||||
state = "maybe-null"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -283,66 +288,60 @@ step by step in the UI:
|
||||
/** @kind path-problem */
|
||||
|
||||
import javascript
|
||||
import DataFlow::PathGraph
|
||||
|
||||
class JsonLabel extends DataFlow::FlowLabel {
|
||||
JsonLabel() {
|
||||
this = "json"
|
||||
}
|
||||
}
|
||||
|
||||
class MaybeNullLabel extends DataFlow::FlowLabel {
|
||||
MaybeNullLabel() {
|
||||
this = "maybe-null"
|
||||
}
|
||||
}
|
||||
|
||||
class TruthinessCheck extends DataFlow::LabeledBarrierGuardNode, DataFlow::ValueNode {
|
||||
class TruthinessCheck extends DataFlow::ValueNode {
|
||||
SsaVariable v;
|
||||
|
||||
TruthinessCheck() {
|
||||
astNode = v.getAUse()
|
||||
}
|
||||
|
||||
override predicate blocks(boolean outcome, Expr e, DataFlow::FlowLabel lbl) {
|
||||
predicate blocksExpr(boolean outcome, Expr e, JsonTrackingConfig::FlowState state) {
|
||||
outcome = true and
|
||||
e = astNode and
|
||||
lbl instanceof MaybeNullLabel
|
||||
state = "maybe-null"
|
||||
}
|
||||
}
|
||||
|
||||
class JsonTrackingConfig extends DataFlow::Configuration {
|
||||
JsonTrackingConfig() { this = "JsonTrackingConfig" }
|
||||
module JsonTrackingConfig implements DataFlow::StateConfigSig {
|
||||
class FlowState extends string {
|
||||
FlowState() {
|
||||
this = ["json", "maybe-null"]
|
||||
}
|
||||
}
|
||||
|
||||
override predicate isSource(DataFlow::Node nd, DataFlow::FlowLabel lbl) {
|
||||
predicate isSource(DataFlow::Node nd, FlowState state) {
|
||||
exists(JsonParserCall jpc |
|
||||
nd = jpc.getOutput() and
|
||||
(lbl instanceof JsonLabel or lbl instanceof MaybeNullLabel)
|
||||
state = ["json", "maybe-null"] // start in either state
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node nd, DataFlow::FlowLabel lbl) {
|
||||
predicate isSink(DataFlow::Node nd, FlowState state) {
|
||||
exists(DataFlow::PropRef pr |
|
||||
nd = pr.getBase() and
|
||||
lbl instanceof MaybeNullLabel
|
||||
state = "maybe-null"
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isAdditionalFlowStep(DataFlow::Node pred, DataFlow::Node succ,
|
||||
DataFlow::FlowLabel predlbl, DataFlow::FlowLabel succlbl) {
|
||||
predicate isAdditionalFlowStep(DataFlow::Node pred, FlowState predState,
|
||||
DataFlow::Node succ, FlowState succState) {
|
||||
succ.(DataFlow::PropRead).getBase() = pred and
|
||||
predlbl instanceof JsonLabel and
|
||||
(succlbl instanceof JsonLabel or succlbl instanceof MaybeNullLabel)
|
||||
predState = "json" and
|
||||
succState = ["json", "maybe-null"]
|
||||
}
|
||||
|
||||
override predicate isBarrierGuard(DataFlow::BarrierGuardNode guard) {
|
||||
guard instanceof TruthinessCheck
|
||||
predicate isBarrier(DataFlow::Node node, FlowState state) {
|
||||
node = DataFlow::MakeBarrierGuard<TruthinessCheck>::getABarrierNode() and
|
||||
state = "maybe-null"
|
||||
}
|
||||
}
|
||||
|
||||
from JsonTrackingConfig cfg, DataFlow::PathNode source, DataFlow::PathNode sink
|
||||
where cfg.hasFlowPath(source, sink)
|
||||
select sink, source, sink, "Property access on JSON value originating $@.", source, "here"
|
||||
module JsonTrackingFlow = DataFlow::GlobalWithState<JsonTrackingConfig>;
|
||||
|
||||
from DataFlow::Node source, DataFlow::Node sink
|
||||
where JsonTrackingFlow::flow(source, sink)
|
||||
select sink, "Property access on JSON value originating $@.", source, "here"
|
||||
|
||||
We ran this query on the https://github.com/finos/plexus-interop repository. Many of the
|
||||
results were false positives since the query does not currently model many ways in which we can check
|
||||
@@ -354,52 +353,30 @@ this tutorial.
|
||||
API
|
||||
---
|
||||
|
||||
Plain data-flow configurations implicitly use a single flow label "data", which indicates that a
|
||||
data value originated from a source. You can use the predicate ``DataFlow::FlowLabel::data()``,
|
||||
which returns this flow label, as a symbolic name for it.
|
||||
Flow state can be used in modules implementing the ``DataFlow::StateConfigSig`` signature. Compared to a ``DataFlow::ConfigSig`` the main differences are:
|
||||
|
||||
Taint-tracking configurations add a second flow label "taint" (``DataFlow::FlowLabel::taint()``),
|
||||
which is similar to "data", but includes values that have passed through non-value preserving steps
|
||||
such as string operations.
|
||||
- The module must be passed to ``DataFlow::GlobalWithState<...>`` or ``TaintTracking::GlobalWithState<...>``.
|
||||
instead of ``DataFlow::Global<...>`` or ``TaintTracking::Global<...>``.
|
||||
- The module must contain a type named ``FlowState``.
|
||||
- ``isSource`` expects an additional parameter specifying the flow state.
|
||||
- ``isSink`` optionally can take an additional parameter specifying the flow state.
|
||||
If omitted, the sinks are in effect for all flow states.
|
||||
- ``isAdditionalFlowStep`` optionally can take two additional parameters specifying the predecessor and successor flow states.
|
||||
If omitted, the generated steps apply for any flow state and preserve the current flow state.
|
||||
- ``isBarrier`` optionally can take an additional parameter specifying the flow state to block.
|
||||
If omitted, the barriers block all flow states.
|
||||
|
||||
Each of the three member predicates ``isSource``, ``isSink`` and
|
||||
``isAdditionalFlowStep``/``isAdditionalTaintStep`` has one version that uses the default flow
|
||||
labels, and one version that allows specifying custom flow labels through additional arguments.
|
||||
|
||||
For ``isSource``, there is one additional argument specifying which flow label(s) should be
|
||||
associated with values originating from this source. If multiple flow labels are specified, each
|
||||
value is associated with `all` of them.
|
||||
|
||||
For ``isSink``, the additional argument specifies which flow label(s) a value that flows into this
|
||||
source may be associated with. If multiple flow labels are specified, then any value that is
|
||||
associated with `at least one` of them will be considered by the configuration.
|
||||
|
||||
For ``isAdditionalFlowStep`` there are two additional arguments ``predlbl`` and ``succlbl``, which
|
||||
allow flow steps to act as flow label transformers. If a value associated with ``predlbl`` arrives
|
||||
at the start node of the additional step, it is propagated to the end node and associated with
|
||||
``succlbl``. Of course, ``predlbl`` and ``succlbl`` may be the same, indicating that the flow step
|
||||
preserves this label. There can also be multiple values of ``succlbl`` for a single ``predlbl`` or
|
||||
vice versa.
|
||||
|
||||
Note that if you do not restrict ``succlbl`` then it will be allowed to range over all flow labels.
|
||||
This may cause labels that were previously blocked on a path to reappear, which is not usually what
|
||||
you want.
|
||||
|
||||
The flow label-aware version of ``isBarrier`` is called ``isLabeledBarrier``: unlike ``isBarrier``,
|
||||
which prevents any flow past the given node, it only blocks flow of values associated with one of
|
||||
the specified flow labels.
|
||||
|
||||
Standard queries using flow labels
|
||||
Standard queries using flow state
|
||||
----------------------------------
|
||||
|
||||
Some of our standard security queries use flow labels. You can look at their implementation
|
||||
to get a feeling for how to use flow labels in practice.
|
||||
Some of our standard security queries use flow state. You can look at their implementation
|
||||
to get a feeling for how to use flow state in practice.
|
||||
|
||||
In particular, both of the examples mentioned in the section on limitations of basic data flow above
|
||||
are from standard security queries that use flow labels. The `Prototype-polluting merge call
|
||||
<https://codeql.github.com/codeql-query-help/javascript/js-prototype-pollution/>`_ query uses two flow labels to distinguish completely
|
||||
are from standard security queries that use flow state. The `Prototype-polluting merge call
|
||||
<https://codeql.github.com/codeql-query-help/javascript/js-prototype-pollution/>`_ query uses two flow states to distinguish completely
|
||||
tainted objects from partially tainted objects. The `Uncontrolled data used in path expression
|
||||
<https://codeql.github.com/codeql-query-help/javascript/js-path-injection/>`_ query uses four flow labels to track whether a user-controlled
|
||||
<https://codeql.github.com/codeql-query-help/javascript/js-path-injection/>`_ query uses four flow states to track whether a user-controlled
|
||||
string may be an absolute path and whether it may contain ``..`` components.
|
||||
|
||||
Further reading
|
||||
|
||||
@@ -1,142 +1,142 @@
|
||||
package,sink,source,summary,sink:command-injection,sink:credentials-key,sink:jwt,sink:log-injection,sink:nosql-injection,sink:path-injection,sink:regex-use[0],sink:regex-use[1],sink:regex-use[c],sink:request-forgery,sink:request-forgery[TCP Addr + Port],sink:sql-injection,sink:url-redirection,sink:url-redirection[0],sink:url-redirection[receiver],sink:xpath-injection,source:environment,source:file,source:remote,source:stdin,summary:taint,summary:value
|
||||
,,,8,,,,,,,,,,,,,,,,,,,,,3,5
|
||||
archive/tar,,,5,,,,,,,,,,,,,,,,,,,,,5,
|
||||
archive/zip,,,6,,,,,,,,,,,,,,,,,,,,,6,
|
||||
bufio,,,17,,,,,,,,,,,,,,,,,,,,,17,
|
||||
bytes,,,43,,,,,,,,,,,,,,,,,,,,,43,
|
||||
clevergo.tech/clevergo,1,,,,,,,,,,,,,,,,,1,,,,,,,
|
||||
compress/bzip2,,,1,,,,,,,,,,,,,,,,,,,,,1,
|
||||
compress/flate,,,4,,,,,,,,,,,,,,,,,,,,,4,
|
||||
compress/gzip,,,3,,,,,,,,,,,,,,,,,,,,,3,
|
||||
compress/lzw,,,1,,,,,,,,,,,,,,,,,,,,,1,
|
||||
compress/zlib,,,4,,,,,,,,,,,,,,,,,,,,,4,
|
||||
container/heap,,,5,,,,,,,,,,,,,,,,,,,,,5,
|
||||
container/list,,,20,,,,,,,,,,,,,,,,,,,,,20,
|
||||
container/ring,,,5,,,,,,,,,,,,,,,,,,,,,5,
|
||||
context,,,5,,,,,,,,,,,,,,,,,,,,,5,
|
||||
crypto,,,10,,,,,,,,,,,,,,,,,,,,,10,
|
||||
database/sql,30,,11,,,,,,,,,,,,30,,,,,,,,,11,
|
||||
encoding,,,77,,,,,,,,,,,,,,,,,,,,,77,
|
||||
errors,,,3,,,,,,,,,,,,,,,,,,,,,3,
|
||||
expvar,,,6,,,,,,,,,,,,,,,,,,,,,6,
|
||||
fmt,3,,16,,,,3,,,,,,,,,,,,,,,,,16,
|
||||
github.com/ChrisTrenkamp/goxpath,3,,,,,,,,,,,,,,,,,,3,,,,,,
|
||||
github.com/Masterminds/squirrel,32,,,,,,,,,,,,,,32,,,,,,,,,,
|
||||
github.com/Sirupsen/logrus,145,,,,,,145,,,,,,,,,,,,,,,,,,
|
||||
github.com/antchfx/htmlquery,4,,,,,,,,,,,,,,,,,,4,,,,,,
|
||||
github.com/antchfx/jsonquery,4,,,,,,,,,,,,,,,,,,4,,,,,,
|
||||
github.com/antchfx/xmlquery,8,,,,,,,,,,,,,,,,,,8,,,,,,
|
||||
github.com/antchfx/xpath,4,,,,,,,,,,,,,,,,,,4,,,,,,
|
||||
github.com/appleboy/gin-jwt,1,,,,1,,,,,,,,,,,,,,,,,,,,
|
||||
github.com/astaxie/beego,71,21,21,,,,34,,5,,,,,,30,2,,,,,,21,,21,
|
||||
github.com/beego/beego,142,42,42,,,,68,,10,,,,,,60,4,,,,,,42,,42,
|
||||
github.com/caarlos0/env,,5,2,,,,,,,,,,,,,,,,,5,,,,1,1
|
||||
github.com/clevergo/clevergo,1,,,,,,,,,,,,,,,,,1,,,,,,,
|
||||
github.com/codeskyblue/go-sh,4,,,4,,,,,,,,,,,,,,,,,,,,,
|
||||
github.com/couchbase/gocb,8,,18,,,,,8,,,,,,,,,,,,,,,,18,
|
||||
github.com/couchbaselabs/gocb,8,,18,,,,,8,,,,,,,,,,,,,,,,18,
|
||||
github.com/crankycoder/xmlpath,2,,,,,,,,,,,,,,,,,,2,,,,,,
|
||||
github.com/cristalhq/jwt,1,,,,1,,,,,,,,,,,,,,,,,,,,
|
||||
github.com/davecgh/go-spew/spew,9,,,,,,9,,,,,,,,,,,,,,,,,,
|
||||
github.com/dgrijalva/jwt-go,3,,9,,2,1,,,,,,,,,,,,,,,,,,9,
|
||||
github.com/elazarl/goproxy,2,2,2,,,,2,,,,,,,,,,,,,,,2,,2,
|
||||
github.com/emicklei/go-restful,,7,,,,,,,,,,,,,,,,,,,,7,,,
|
||||
github.com/evanphx/json-patch,,,12,,,,,,,,,,,,,,,,,,,,,12,
|
||||
github.com/form3tech-oss/jwt-go,2,,,,2,,,,,,,,,,,,,,,,,,,,
|
||||
github.com/gin-gonic/gin,3,46,2,,,,,,3,,,,,,,,,,,,,46,,2,
|
||||
github.com/go-chi/chi,,3,,,,,,,,,,,,,,,,,,,,3,,,
|
||||
github.com/go-chi/jwtauth,1,,,,1,,,,,,,,,,,,,,,,,,,,
|
||||
github.com/go-gorm/gorm,13,,,,,,,,,,,,,,13,,,,,,,,,,
|
||||
github.com/go-jose/go-jose,3,,4,,2,1,,,,,,,,,,,,,,,,,,4,
|
||||
github.com/go-kit/kit/auth/jwt,1,,,,1,,,,,,,,,,,,,,,,,,,,
|
||||
github.com/go-pg/pg/orm,,,6,,,,,,,,,,,,,,,,,,,,,6,
|
||||
github.com/go-xmlpath/xmlpath,2,,,,,,,,,,,,,,,,,,2,,,,,,
|
||||
github.com/go-xorm/xorm,34,,,,,,,,,,,,,,34,,,,,,,,,,
|
||||
github.com/gobuffalo/envy,,7,,,,,,,,,,,,,,,,,,7,,,,,
|
||||
github.com/gobwas/ws,,2,,,,,,,,,,,,,,,,,,,,2,,,
|
||||
github.com/gofiber/fiber,5,,,,,,,,4,,,,,,,,,1,,,,,,,
|
||||
github.com/gogf/gf-jwt,1,,,,1,,,,,,,,,,,,,,,,,,,,
|
||||
github.com/gogf/gf/database/gdb,51,,,,,,,,,,,,,,51,,,,,,,,,,
|
||||
github.com/going/toolkit/xmlpath,2,,,,,,,,,,,,,,,,,,2,,,,,,
|
||||
github.com/golang-jwt/jwt,3,,11,,2,1,,,,,,,,,,,,,,,,,,11,
|
||||
github.com/golang/glog,90,,,,,,90,,,,,,,,,,,,,,,,,,
|
||||
github.com/golang/protobuf/proto,,,4,,,,,,,,,,,,,,,,,,,,,4,
|
||||
github.com/gorilla/mux,,1,,,,,,,,,,,,,,,,,,,,1,,,
|
||||
github.com/gorilla/websocket,,3,,,,,,,,,,,,,,,,,,,,3,,,
|
||||
github.com/hashicorp/go-envparse,,1,,,,,,,,,,,,,,,,,,1,,,,,
|
||||
github.com/jbowtie/gokogiri/xml,4,,,,,,,,,,,,,,,,,,4,,,,,,
|
||||
github.com/jbowtie/gokogiri/xpath,1,,,,,,,,,,,,,,,,,,1,,,,,,
|
||||
github.com/jinzhu/gorm,13,,,,,,,,,,,,,,13,,,,,,,,,,
|
||||
github.com/jmoiron/sqlx,12,,,,,,,,,,,,,,12,,,,,,,,,,
|
||||
github.com/joho/godotenv,,4,,,,,,,,,,,,,,,,,,4,,,,,
|
||||
github.com/json-iterator/go,,,4,,,,,,,,,,,,,,,,,,,,,4,
|
||||
github.com/kataras/iris/context,6,,,,,,,,6,,,,,,,,,,,,,,,,
|
||||
github.com/kataras/iris/middleware/jwt,2,,,,2,,,,,,,,,,,,,,,,,,,,
|
||||
github.com/kataras/iris/server/web/context,6,,,,,,,,6,,,,,,,,,,,,,,,,
|
||||
github.com/kataras/jwt,5,,,,5,,,,,,,,,,,,,,,,,,,,
|
||||
github.com/kelseyhightower/envconfig,,6,,,,,,,,,,,,,,,,,,6,,,,,
|
||||
github.com/labstack/echo,3,12,2,,,,,,2,,,,,,,1,,,,,,12,,2,
|
||||
github.com/lann/squirrel,32,,,,,,,,,,,,,,32,,,,,,,,,,
|
||||
github.com/lestrrat-go/jwx,2,,,,2,,,,,,,,,,,,,,,,,,,,
|
||||
github.com/lestrrat-go/libxml2/parser,3,,,,,,,,,,,,,,,,,,3,,,,,,
|
||||
github.com/lestrrat/go-jwx/jwk,1,,,,1,,,,,,,,,,,,,,,,,,,,
|
||||
github.com/masterzen/xmlpath,2,,,,,,,,,,,,,,,,,,2,,,,,,
|
||||
github.com/moovweb/gokogiri/xml,4,,,,,,,,,,,,,,,,,,4,,,,,,
|
||||
github.com/moovweb/gokogiri/xpath,1,,,,,,,,,,,,,,,,,,1,,,,,,
|
||||
github.com/ory/fosite/token/jwt,2,,,,2,,,,,,,,,,,,,,,,,,,,
|
||||
github.com/raindog308/gorqlite,24,,,,,,,,,,,,,,24,,,,,,,,,,
|
||||
github.com/revel/revel,2,23,10,,,,,,1,,,,,,,1,,,,,,23,,10,
|
||||
github.com/robfig/revel,2,23,10,,,,,,1,,,,,,,1,,,,,,23,,10,
|
||||
github.com/rqlite/gorqlite,24,,,,,,,,,,,,,,24,,,,,,,,,,
|
||||
github.com/santhosh-tekuri/xpathparser,2,,,,,,,,,,,,,,,,,,2,,,,,,
|
||||
github.com/sendgrid/sendgrid-go/helpers/mail,,,1,,,,,,,,,,,,,,,,,,,,,1,
|
||||
github.com/sirupsen/logrus,145,,,,,,145,,,,,,,,,,,,,,,,,,
|
||||
github.com/spf13/afero,34,,,,,,,,34,,,,,,,,,,,,,,,,
|
||||
github.com/square/go-jose,3,,4,,2,1,,,,,,,,,,,,,,,,,,4,
|
||||
github.com/uptrace/bun,63,,,,,,,,,,,,,,63,,,,,,,,,,
|
||||
github.com/valyala/fasthttp,35,50,5,,,,,,8,,,,17,8,,2,,,,,,50,,5,
|
||||
go.mongodb.org/mongo-driver/mongo,14,,,,,,,14,,,,,,,,,,,,,,,,,
|
||||
go.uber.org/zap,33,,11,,,,33,,,,,,,,,,,,,,,,,11,
|
||||
golang.org/x/crypto/ssh,4,,,4,,,,,,,,,,,,,,,,,,,,,
|
||||
golang.org/x/net/context,,,5,,,,,,,,,,,,,,,,,,,,,5,
|
||||
golang.org/x/net/html,,,16,,,,,,,,,,,,,,,,,,,,,16,
|
||||
golang.org/x/net/websocket,,2,,,,,,,,,,,,,,,,,,,,2,,,
|
||||
google.golang.org/protobuf/internal/encoding/text,,,1,,,,,,,,,,,,,,,,,,,,,1,
|
||||
google.golang.org/protobuf/internal/impl,,,2,,,,,,,,,,,,,,,,,,,,,2,
|
||||
google.golang.org/protobuf/proto,,,8,,,,,,,,,,,,,,,,,,,,,8,
|
||||
google.golang.org/protobuf/reflect/protoreflect,,,1,,,,,,,,,,,,,,,,,,,,,1,
|
||||
gopkg.in/Masterminds/squirrel,32,,,,,,,,,,,,,,32,,,,,,,,,,
|
||||
gopkg.in/couchbase/gocb,8,,18,,,,,8,,,,,,,,,,,,,,,,18,
|
||||
gopkg.in/glog,90,,,,,,90,,,,,,,,,,,,,,,,,,
|
||||
gopkg.in/go-jose/go-jose,3,,4,,2,1,,,,,,,,,,,,,,,,,,4,
|
||||
gopkg.in/go-xmlpath/xmlpath,2,,,,,,,,,,,,,,,,,,2,,,,,,
|
||||
gopkg.in/macaron,1,12,1,,,,,,,,,,,,,,,1,,,,12,,1,
|
||||
gopkg.in/square/go-jose,3,,4,,2,1,,,,,,,,,,,,,,,,,,4,
|
||||
gopkg.in/xmlpath,2,,,,,,,,,,,,,,,,,,2,,,,,,
|
||||
gopkg.in/yaml,,,9,,,,,,,,,,,,,,,,,,,,,9,
|
||||
gorm.io/gorm,13,,,,,,,,,,,,,,13,,,,,,,,,,
|
||||
html,,,8,,,,,,,,,,,,,,,,,,,,,8,
|
||||
io,5,4,34,,,,,,5,,,,,,,,,,,,4,,,34,
|
||||
k8s.io/api/core,,,10,,,,,,,,,,,,,,,,,,,,,10,
|
||||
k8s.io/apimachinery/pkg/runtime,,,47,,,,,,,,,,,,,,,,,,,,,47,
|
||||
k8s.io/klog,90,,,,,,90,,,,,,,,,,,,,,,,,,
|
||||
launchpad.net/xmlpath,2,,,,,,,,,,,,,,,,,,2,,,,,,
|
||||
log,20,,3,,,,20,,,,,,,,,,,,,,,,,3,
|
||||
math/big,,,1,,,,,,,,,,,,,,,,,,,,,1,
|
||||
mime,,,14,,,,,,,,,,,,,,,,,,,,,14,
|
||||
net,2,16,100,,,,,,1,,,,,,,,1,,,,,16,,100,
|
||||
nhooyr.io/websocket,,2,,,,,,,,,,,,,,,,,,,,2,,,
|
||||
os,29,11,6,3,,,,,26,,,,,,,,,,,7,3,,1,6,
|
||||
path,,,18,,,,,,,,,,,,,,,,,,,,,18,
|
||||
reflect,,,37,,,,,,,,,,,,,,,,,,,,,37,
|
||||
regexp,10,,20,,,,,,,3,3,4,,,,,,,,,,,,20,
|
||||
slices,,,17,,,,,,,,,,,,,,,,,,,,,,17
|
||||
sort,,,1,,,,,,,,,,,,,,,,,,,,,1,
|
||||
strconv,,,9,,,,,,,,,,,,,,,,,,,,,9,
|
||||
strings,,,34,,,,,,,,,,,,,,,,,,,,,34,
|
||||
sync,,,34,,,,,,,,,,,,,,,,,,,,,34,
|
||||
syscall,5,2,8,5,,,,,,,,,,,,,,,,2,,,,8,
|
||||
text/scanner,,,3,,,,,,,,,,,,,,,,,,,,,3,
|
||||
text/tabwriter,,,1,,,,,,,,,,,,,,,,,,,,,1,
|
||||
text/template,,,6,,,,,,,,,,,,,,,,,,,,,6,
|
||||
xorm.io/xorm,34,,,,,,,,,,,,,,34,,,,,,,,,,
|
||||
package,sink,source,summary,sink:command-injection,sink:credentials-key,sink:jwt,sink:log-injection,sink:nosql-injection,sink:path-injection,sink:regex-use[0],sink:regex-use[1],sink:regex-use[c],sink:request-forgery,sink:request-forgery[TCP Addr + Port],sink:sql-injection,sink:url-redirection,sink:url-redirection[0],sink:url-redirection[receiver],sink:xpath-injection,source:commandargs,source:environment,source:file,source:remote,source:stdin,summary:taint,summary:value
|
||||
,,,8,,,,,,,,,,,,,,,,,,,,,,3,5
|
||||
archive/tar,,,5,,,,,,,,,,,,,,,,,,,,,,5,
|
||||
archive/zip,,,6,,,,,,,,,,,,,,,,,,,,,,6,
|
||||
bufio,,,17,,,,,,,,,,,,,,,,,,,,,,17,
|
||||
bytes,,,43,,,,,,,,,,,,,,,,,,,,,,43,
|
||||
clevergo.tech/clevergo,1,,,,,,,,,,,,,,,,,1,,,,,,,,
|
||||
compress/bzip2,,,1,,,,,,,,,,,,,,,,,,,,,,1,
|
||||
compress/flate,,,4,,,,,,,,,,,,,,,,,,,,,,4,
|
||||
compress/gzip,,,3,,,,,,,,,,,,,,,,,,,,,,3,
|
||||
compress/lzw,,,1,,,,,,,,,,,,,,,,,,,,,,1,
|
||||
compress/zlib,,,4,,,,,,,,,,,,,,,,,,,,,,4,
|
||||
container/heap,,,5,,,,,,,,,,,,,,,,,,,,,,5,
|
||||
container/list,,,20,,,,,,,,,,,,,,,,,,,,,,20,
|
||||
container/ring,,,5,,,,,,,,,,,,,,,,,,,,,,5,
|
||||
context,,,5,,,,,,,,,,,,,,,,,,,,,,5,
|
||||
crypto,,,10,,,,,,,,,,,,,,,,,,,,,,10,
|
||||
database/sql,30,,11,,,,,,,,,,,,30,,,,,,,,,,11,
|
||||
encoding,,,77,,,,,,,,,,,,,,,,,,,,,,77,
|
||||
errors,,,3,,,,,,,,,,,,,,,,,,,,,,3,
|
||||
expvar,,,6,,,,,,,,,,,,,,,,,,,,,,6,
|
||||
fmt,3,,16,,,,3,,,,,,,,,,,,,,,,,,16,
|
||||
github.com/ChrisTrenkamp/goxpath,3,,,,,,,,,,,,,,,,,,3,,,,,,,
|
||||
github.com/Masterminds/squirrel,32,,,,,,,,,,,,,,32,,,,,,,,,,,
|
||||
github.com/Sirupsen/logrus,145,,,,,,145,,,,,,,,,,,,,,,,,,,
|
||||
github.com/antchfx/htmlquery,4,,,,,,,,,,,,,,,,,,4,,,,,,,
|
||||
github.com/antchfx/jsonquery,4,,,,,,,,,,,,,,,,,,4,,,,,,,
|
||||
github.com/antchfx/xmlquery,8,,,,,,,,,,,,,,,,,,8,,,,,,,
|
||||
github.com/antchfx/xpath,4,,,,,,,,,,,,,,,,,,4,,,,,,,
|
||||
github.com/appleboy/gin-jwt,1,,,,1,,,,,,,,,,,,,,,,,,,,,
|
||||
github.com/astaxie/beego,71,21,21,,,,34,,5,,,,,,30,2,,,,,,,21,,21,
|
||||
github.com/beego/beego,142,42,42,,,,68,,10,,,,,,60,4,,,,,,,42,,42,
|
||||
github.com/caarlos0/env,,5,2,,,,,,,,,,,,,,,,,,5,,,,1,1
|
||||
github.com/clevergo/clevergo,1,,,,,,,,,,,,,,,,,1,,,,,,,,
|
||||
github.com/codeskyblue/go-sh,4,,,4,,,,,,,,,,,,,,,,,,,,,,
|
||||
github.com/couchbase/gocb,8,,18,,,,,8,,,,,,,,,,,,,,,,,18,
|
||||
github.com/couchbaselabs/gocb,8,,18,,,,,8,,,,,,,,,,,,,,,,,18,
|
||||
github.com/crankycoder/xmlpath,2,,,,,,,,,,,,,,,,,,2,,,,,,,
|
||||
github.com/cristalhq/jwt,1,,,,1,,,,,,,,,,,,,,,,,,,,,
|
||||
github.com/davecgh/go-spew/spew,9,,,,,,9,,,,,,,,,,,,,,,,,,,
|
||||
github.com/dgrijalva/jwt-go,3,,9,,2,1,,,,,,,,,,,,,,,,,,,9,
|
||||
github.com/elazarl/goproxy,2,2,2,,,,2,,,,,,,,,,,,,,,,2,,2,
|
||||
github.com/emicklei/go-restful,,7,,,,,,,,,,,,,,,,,,,,,7,,,
|
||||
github.com/evanphx/json-patch,,,12,,,,,,,,,,,,,,,,,,,,,,12,
|
||||
github.com/form3tech-oss/jwt-go,2,,,,2,,,,,,,,,,,,,,,,,,,,,
|
||||
github.com/gin-gonic/gin,3,46,2,,,,,,3,,,,,,,,,,,,,,46,,2,
|
||||
github.com/go-chi/chi,,3,,,,,,,,,,,,,,,,,,,,,3,,,
|
||||
github.com/go-chi/jwtauth,1,,,,1,,,,,,,,,,,,,,,,,,,,,
|
||||
github.com/go-gorm/gorm,13,,,,,,,,,,,,,,13,,,,,,,,,,,
|
||||
github.com/go-jose/go-jose,3,,4,,2,1,,,,,,,,,,,,,,,,,,,4,
|
||||
github.com/go-kit/kit/auth/jwt,1,,,,1,,,,,,,,,,,,,,,,,,,,,
|
||||
github.com/go-pg/pg/orm,,,6,,,,,,,,,,,,,,,,,,,,,,6,
|
||||
github.com/go-xmlpath/xmlpath,2,,,,,,,,,,,,,,,,,,2,,,,,,,
|
||||
github.com/go-xorm/xorm,34,,,,,,,,,,,,,,34,,,,,,,,,,,
|
||||
github.com/gobuffalo/envy,,7,,,,,,,,,,,,,,,,,,,7,,,,,
|
||||
github.com/gobwas/ws,,2,,,,,,,,,,,,,,,,,,,,,2,,,
|
||||
github.com/gofiber/fiber,5,,,,,,,,4,,,,,,,,,1,,,,,,,,
|
||||
github.com/gogf/gf-jwt,1,,,,1,,,,,,,,,,,,,,,,,,,,,
|
||||
github.com/gogf/gf/database/gdb,51,,,,,,,,,,,,,,51,,,,,,,,,,,
|
||||
github.com/going/toolkit/xmlpath,2,,,,,,,,,,,,,,,,,,2,,,,,,,
|
||||
github.com/golang-jwt/jwt,3,,11,,2,1,,,,,,,,,,,,,,,,,,,11,
|
||||
github.com/golang/glog,90,,,,,,90,,,,,,,,,,,,,,,,,,,
|
||||
github.com/golang/protobuf/proto,,,4,,,,,,,,,,,,,,,,,,,,,,4,
|
||||
github.com/gorilla/mux,,1,,,,,,,,,,,,,,,,,,,,,1,,,
|
||||
github.com/gorilla/websocket,,3,,,,,,,,,,,,,,,,,,,,,3,,,
|
||||
github.com/hashicorp/go-envparse,,1,,,,,,,,,,,,,,,,,,,1,,,,,
|
||||
github.com/jbowtie/gokogiri/xml,4,,,,,,,,,,,,,,,,,,4,,,,,,,
|
||||
github.com/jbowtie/gokogiri/xpath,1,,,,,,,,,,,,,,,,,,1,,,,,,,
|
||||
github.com/jinzhu/gorm,13,,,,,,,,,,,,,,13,,,,,,,,,,,
|
||||
github.com/jmoiron/sqlx,12,,,,,,,,,,,,,,12,,,,,,,,,,,
|
||||
github.com/joho/godotenv,,4,,,,,,,,,,,,,,,,,,,4,,,,,
|
||||
github.com/json-iterator/go,,,4,,,,,,,,,,,,,,,,,,,,,,4,
|
||||
github.com/kataras/iris/context,6,,,,,,,,6,,,,,,,,,,,,,,,,,
|
||||
github.com/kataras/iris/middleware/jwt,2,,,,2,,,,,,,,,,,,,,,,,,,,,
|
||||
github.com/kataras/iris/server/web/context,6,,,,,,,,6,,,,,,,,,,,,,,,,,
|
||||
github.com/kataras/jwt,5,,,,5,,,,,,,,,,,,,,,,,,,,,
|
||||
github.com/kelseyhightower/envconfig,,6,,,,,,,,,,,,,,,,,,,6,,,,,
|
||||
github.com/labstack/echo,3,12,2,,,,,,2,,,,,,,1,,,,,,,12,,2,
|
||||
github.com/lann/squirrel,32,,,,,,,,,,,,,,32,,,,,,,,,,,
|
||||
github.com/lestrrat-go/jwx,2,,,,2,,,,,,,,,,,,,,,,,,,,,
|
||||
github.com/lestrrat-go/libxml2/parser,3,,,,,,,,,,,,,,,,,,3,,,,,,,
|
||||
github.com/lestrrat/go-jwx/jwk,1,,,,1,,,,,,,,,,,,,,,,,,,,,
|
||||
github.com/masterzen/xmlpath,2,,,,,,,,,,,,,,,,,,2,,,,,,,
|
||||
github.com/moovweb/gokogiri/xml,4,,,,,,,,,,,,,,,,,,4,,,,,,,
|
||||
github.com/moovweb/gokogiri/xpath,1,,,,,,,,,,,,,,,,,,1,,,,,,,
|
||||
github.com/ory/fosite/token/jwt,2,,,,2,,,,,,,,,,,,,,,,,,,,,
|
||||
github.com/raindog308/gorqlite,24,,,,,,,,,,,,,,24,,,,,,,,,,,
|
||||
github.com/revel/revel,2,23,10,,,,,,1,,,,,,,1,,,,,,,23,,10,
|
||||
github.com/robfig/revel,2,23,10,,,,,,1,,,,,,,1,,,,,,,23,,10,
|
||||
github.com/rqlite/gorqlite,24,,,,,,,,,,,,,,24,,,,,,,,,,,
|
||||
github.com/santhosh-tekuri/xpathparser,2,,,,,,,,,,,,,,,,,,2,,,,,,,
|
||||
github.com/sendgrid/sendgrid-go/helpers/mail,,,1,,,,,,,,,,,,,,,,,,,,,,1,
|
||||
github.com/sirupsen/logrus,145,,,,,,145,,,,,,,,,,,,,,,,,,,
|
||||
github.com/spf13/afero,34,,,,,,,,34,,,,,,,,,,,,,,,,,
|
||||
github.com/square/go-jose,3,,4,,2,1,,,,,,,,,,,,,,,,,,,4,
|
||||
github.com/uptrace/bun,63,,,,,,,,,,,,,,63,,,,,,,,,,,
|
||||
github.com/valyala/fasthttp,35,50,5,,,,,,8,,,,17,8,,2,,,,,,,50,,5,
|
||||
go.mongodb.org/mongo-driver/mongo,14,,,,,,,14,,,,,,,,,,,,,,,,,,
|
||||
go.uber.org/zap,33,,11,,,,33,,,,,,,,,,,,,,,,,,11,
|
||||
golang.org/x/crypto/ssh,4,,,4,,,,,,,,,,,,,,,,,,,,,,
|
||||
golang.org/x/net/context,,,5,,,,,,,,,,,,,,,,,,,,,,5,
|
||||
golang.org/x/net/html,,,16,,,,,,,,,,,,,,,,,,,,,,16,
|
||||
golang.org/x/net/websocket,,2,,,,,,,,,,,,,,,,,,,,,2,,,
|
||||
google.golang.org/protobuf/internal/encoding/text,,,1,,,,,,,,,,,,,,,,,,,,,,1,
|
||||
google.golang.org/protobuf/internal/impl,,,2,,,,,,,,,,,,,,,,,,,,,,2,
|
||||
google.golang.org/protobuf/proto,,,8,,,,,,,,,,,,,,,,,,,,,,8,
|
||||
google.golang.org/protobuf/reflect/protoreflect,,,1,,,,,,,,,,,,,,,,,,,,,,1,
|
||||
gopkg.in/Masterminds/squirrel,32,,,,,,,,,,,,,,32,,,,,,,,,,,
|
||||
gopkg.in/couchbase/gocb,8,,18,,,,,8,,,,,,,,,,,,,,,,,18,
|
||||
gopkg.in/glog,90,,,,,,90,,,,,,,,,,,,,,,,,,,
|
||||
gopkg.in/go-jose/go-jose,3,,4,,2,1,,,,,,,,,,,,,,,,,,,4,
|
||||
gopkg.in/go-xmlpath/xmlpath,2,,,,,,,,,,,,,,,,,,2,,,,,,,
|
||||
gopkg.in/macaron,1,12,1,,,,,,,,,,,,,,,1,,,,,12,,1,
|
||||
gopkg.in/square/go-jose,3,,4,,2,1,,,,,,,,,,,,,,,,,,,4,
|
||||
gopkg.in/xmlpath,2,,,,,,,,,,,,,,,,,,2,,,,,,,
|
||||
gopkg.in/yaml,,,9,,,,,,,,,,,,,,,,,,,,,,9,
|
||||
gorm.io/gorm,13,,,,,,,,,,,,,,13,,,,,,,,,,,
|
||||
html,,,8,,,,,,,,,,,,,,,,,,,,,,8,
|
||||
io,5,4,34,,,,,,5,,,,,,,,,,,,,4,,,34,
|
||||
k8s.io/api/core,,,10,,,,,,,,,,,,,,,,,,,,,,10,
|
||||
k8s.io/apimachinery/pkg/runtime,,,47,,,,,,,,,,,,,,,,,,,,,,47,
|
||||
k8s.io/klog,90,,,,,,90,,,,,,,,,,,,,,,,,,,
|
||||
launchpad.net/xmlpath,2,,,,,,,,,,,,,,,,,,2,,,,,,,
|
||||
log,20,,3,,,,20,,,,,,,,,,,,,,,,,,3,
|
||||
math/big,,,1,,,,,,,,,,,,,,,,,,,,,,1,
|
||||
mime,,,14,,,,,,,,,,,,,,,,,,,,,,14,
|
||||
net,2,16,100,,,,,,1,,,,,,,,1,,,,,,16,,100,
|
||||
nhooyr.io/websocket,,2,,,,,,,,,,,,,,,,,,,,,2,,,
|
||||
os,29,12,6,3,,,,,26,,,,,,,,,,,1,7,3,,1,6,
|
||||
path,,,18,,,,,,,,,,,,,,,,,,,,,,18,
|
||||
reflect,,,37,,,,,,,,,,,,,,,,,,,,,,37,
|
||||
regexp,10,,20,,,,,,,3,3,4,,,,,,,,,,,,,20,
|
||||
slices,,,17,,,,,,,,,,,,,,,,,,,,,,,17
|
||||
sort,,,1,,,,,,,,,,,,,,,,,,,,,,1,
|
||||
strconv,,,9,,,,,,,,,,,,,,,,,,,,,,9,
|
||||
strings,,,34,,,,,,,,,,,,,,,,,,,,,,34,
|
||||
sync,,,34,,,,,,,,,,,,,,,,,,,,,,34,
|
||||
syscall,5,2,8,5,,,,,,,,,,,,,,,,,2,,,,8,
|
||||
text/scanner,,,3,,,,,,,,,,,,,,,,,,,,,,3,
|
||||
text/tabwriter,,,1,,,,,,,,,,,,,,,,,,,,,,1,
|
||||
text/template,,,6,,,,,,,,,,,,,,,,,,,,,,6,
|
||||
xorm.io/xorm,34,,,,,,,,,,,,,,34,,,,,,,,,,,
|
||||
|
||||
|
@@ -26,7 +26,7 @@ Go framework & library support
|
||||
`Macaron <https://gopkg.in/macaron.v1>`_,``gopkg.in/macaron*``,12,1,1
|
||||
`Revel <http://revel.github.io/>`_,"``github.com/revel/revel*``, ``github.com/robfig/revel*``",46,20,4
|
||||
`SendGrid <https://github.com/sendgrid/sendgrid-go>`_,``github.com/sendgrid/sendgrid-go*``,,1,
|
||||
`Standard library <https://pkg.go.dev/std>`_,"````, ``archive/*``, ``bufio``, ``bytes``, ``cmp``, ``compress/*``, ``container/*``, ``context``, ``crypto``, ``crypto/*``, ``database/*``, ``debug/*``, ``embed``, ``encoding``, ``encoding/*``, ``errors``, ``expvar``, ``flag``, ``fmt``, ``go/*``, ``hash``, ``hash/*``, ``html``, ``html/*``, ``image``, ``image/*``, ``index/*``, ``io``, ``io/*``, ``log``, ``log/*``, ``maps``, ``math``, ``math/*``, ``mime``, ``mime/*``, ``net``, ``net/*``, ``os``, ``os/*``, ``path``, ``path/*``, ``plugin``, ``reflect``, ``reflect/*``, ``regexp``, ``regexp/*``, ``slices``, ``sort``, ``strconv``, ``strings``, ``sync``, ``sync/*``, ``syscall``, ``syscall/*``, ``testing``, ``testing/*``, ``text/*``, ``time``, ``time/*``, ``unicode``, ``unicode/*``, ``unsafe``",33,604,104
|
||||
`Standard library <https://pkg.go.dev/std>`_,"````, ``archive/*``, ``bufio``, ``bytes``, ``cmp``, ``compress/*``, ``container/*``, ``context``, ``crypto``, ``crypto/*``, ``database/*``, ``debug/*``, ``embed``, ``encoding``, ``encoding/*``, ``errors``, ``expvar``, ``flag``, ``fmt``, ``go/*``, ``hash``, ``hash/*``, ``html``, ``html/*``, ``image``, ``image/*``, ``index/*``, ``io``, ``io/*``, ``log``, ``log/*``, ``maps``, ``math``, ``math/*``, ``mime``, ``mime/*``, ``net``, ``net/*``, ``os``, ``os/*``, ``path``, ``path/*``, ``plugin``, ``reflect``, ``reflect/*``, ``regexp``, ``regexp/*``, ``slices``, ``sort``, ``strconv``, ``strings``, ``sync``, ``sync/*``, ``syscall``, ``syscall/*``, ``testing``, ``testing/*``, ``text/*``, ``time``, ``time/*``, ``unicode``, ``unicode/*``, ``unsafe``",34,604,104
|
||||
`XPath <https://github.com/antchfx/xpath>`_,``github.com/antchfx/xpath*``,,,4
|
||||
`appleboy/gin-jwt <https://github.com/appleboy/gin-jwt>`_,``github.com/appleboy/gin-jwt*``,,,1
|
||||
`beego <https://beego.me/>`_,"``github.com/astaxie/beego*``, ``github.com/beego/beego*``",63,63,213
|
||||
@@ -61,5 +61,5 @@ Go framework & library support
|
||||
`yaml <https://gopkg.in/yaml.v3>`_,``gopkg.in/yaml*``,,9,
|
||||
`zap <https://go.uber.org/zap>`_,``go.uber.org/zap*``,,11,33
|
||||
Others,"``github.com/Masterminds/squirrel``, ``github.com/caarlos0/env``, ``github.com/go-gorm/gorm``, ``github.com/go-xorm/xorm``, ``github.com/gobuffalo/envy``, ``github.com/gogf/gf/database/gdb``, ``github.com/hashicorp/go-envparse``, ``github.com/jinzhu/gorm``, ``github.com/jmoiron/sqlx``, ``github.com/joho/godotenv``, ``github.com/kelseyhightower/envconfig``, ``github.com/lann/squirrel``, ``github.com/raindog308/gorqlite``, ``github.com/rqlite/gorqlite``, ``github.com/uptrace/bun``, ``go.mongodb.org/mongo-driver/mongo``, ``gopkg.in/Masterminds/squirrel``, ``gorm.io/gorm``, ``xorm.io/xorm``",23,2,391
|
||||
Totals,,307,928,1532
|
||||
Totals,,308,928,1532
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
import java.util.function.Function;
|
||||
|
||||
class A {
|
||||
String source() { return ""; }
|
||||
|
||||
void sink(String s) { }
|
||||
|
||||
String propagateState(String s, String state) {
|
||||
return "";
|
||||
}
|
||||
|
||||
void foo() {
|
||||
Function<String, String> lambda = Math.random() > 0.5
|
||||
? x -> propagateState(x, "A")
|
||||
: x -> propagateState(x, "B");
|
||||
|
||||
String step0 = source();
|
||||
String step1 = lambda.apply(step0);
|
||||
String step2 = lambda.apply(step1);
|
||||
|
||||
sink(step2);
|
||||
}
|
||||
|
||||
void bar() {
|
||||
Function<String, String> lambda =
|
||||
(x -> Math.random() > 0.5 ? propagateState(x, "A") : propagateState(x, "B"));
|
||||
|
||||
String step0 = source();
|
||||
String step1 = lambda.apply(step0);
|
||||
String step2 = lambda.apply(step1);
|
||||
|
||||
sink(step2);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
nodes
|
||||
| A.java:14:9:14:9 | x : String | semmle.label | x : String |
|
||||
| A.java:14:14:14:35 | propagateState(...) : String | semmle.label | propagateState(...) : String |
|
||||
| A.java:14:29:14:29 | x : String | semmle.label | x : String |
|
||||
| A.java:15:9:15:9 | x : String | semmle.label | x : String |
|
||||
| A.java:15:14:15:35 | propagateState(...) : String | semmle.label | propagateState(...) : String |
|
||||
| A.java:15:29:15:29 | x : String | semmle.label | x : String |
|
||||
| A.java:17:20:17:27 | source(...) : String | semmle.label | source(...) : String |
|
||||
| A.java:18:20:18:38 | apply(...) : String | semmle.label | apply(...) : String |
|
||||
| A.java:18:20:18:38 | apply(...) : String | semmle.label | apply(...) : String |
|
||||
| A.java:18:33:18:37 | step0 : String | semmle.label | step0 : String |
|
||||
| A.java:19:20:19:38 | apply(...) : String | semmle.label | apply(...) : String |
|
||||
| A.java:19:33:19:37 | step1 : String | semmle.label | step1 : String |
|
||||
| A.java:19:33:19:37 | step1 : String | semmle.label | step1 : String |
|
||||
| A.java:21:10:21:14 | step2 | semmle.label | step2 |
|
||||
| A.java:26:8:26:8 | x : String | semmle.label | x : String |
|
||||
| A.java:26:8:26:8 | x : String | semmle.label | x : String |
|
||||
| A.java:26:13:26:81 | ...?...:... : String | semmle.label | ...?...:... : String |
|
||||
| A.java:26:13:26:81 | ...?...:... : String | semmle.label | ...?...:... : String |
|
||||
| A.java:26:35:26:56 | propagateState(...) : String | semmle.label | propagateState(...) : String |
|
||||
| A.java:26:50:26:50 | x : String | semmle.label | x : String |
|
||||
| A.java:26:60:26:81 | propagateState(...) : String | semmle.label | propagateState(...) : String |
|
||||
| A.java:26:75:26:75 | x : String | semmle.label | x : String |
|
||||
| A.java:28:20:28:27 | source(...) : String | semmle.label | source(...) : String |
|
||||
| A.java:29:20:29:38 | apply(...) : String | semmle.label | apply(...) : String |
|
||||
| A.java:29:20:29:38 | apply(...) : String | semmle.label | apply(...) : String |
|
||||
| A.java:29:33:29:37 | step0 : String | semmle.label | step0 : String |
|
||||
| A.java:30:20:30:38 | apply(...) : String | semmle.label | apply(...) : String |
|
||||
| A.java:30:33:30:37 | step1 : String | semmle.label | step1 : String |
|
||||
| A.java:30:33:30:37 | step1 : String | semmle.label | step1 : String |
|
||||
| A.java:32:10:32:14 | step2 | semmle.label | step2 |
|
||||
edges
|
||||
| A.java:14:9:14:9 | x : String | A.java:14:29:14:29 | x : String | provenance | |
|
||||
| A.java:14:29:14:29 | x : String | A.java:14:14:14:35 | propagateState(...) : String | provenance | Config |
|
||||
| A.java:15:9:15:9 | x : String | A.java:15:29:15:29 | x : String | provenance | |
|
||||
| A.java:15:29:15:29 | x : String | A.java:15:14:15:35 | propagateState(...) : String | provenance | Config |
|
||||
| A.java:17:20:17:27 | source(...) : String | A.java:18:33:18:37 | step0 : String | provenance | |
|
||||
| A.java:18:20:18:38 | apply(...) : String | A.java:19:33:19:37 | step1 : String | provenance | |
|
||||
| A.java:18:20:18:38 | apply(...) : String | A.java:19:33:19:37 | step1 : String | provenance | |
|
||||
| A.java:18:33:18:37 | step0 : String | A.java:14:9:14:9 | x : String | provenance | |
|
||||
| A.java:18:33:18:37 | step0 : String | A.java:15:9:15:9 | x : String | provenance | |
|
||||
| A.java:18:33:18:37 | step0 : String | A.java:18:20:18:38 | apply(...) : String | provenance | Config |
|
||||
| A.java:18:33:18:37 | step0 : String | A.java:18:20:18:38 | apply(...) : String | provenance | Config |
|
||||
| A.java:19:20:19:38 | apply(...) : String | A.java:21:10:21:14 | step2 | provenance | |
|
||||
| A.java:19:33:19:37 | step1 : String | A.java:14:9:14:9 | x : String | provenance | |
|
||||
| A.java:19:33:19:37 | step1 : String | A.java:15:9:15:9 | x : String | provenance | |
|
||||
| A.java:19:33:19:37 | step1 : String | A.java:19:20:19:38 | apply(...) : String | provenance | Config |
|
||||
| A.java:19:33:19:37 | step1 : String | A.java:19:20:19:38 | apply(...) : String | provenance | Config |
|
||||
| A.java:26:8:26:8 | x : String | A.java:26:50:26:50 | x : String | provenance | |
|
||||
| A.java:26:8:26:8 | x : String | A.java:26:75:26:75 | x : String | provenance | |
|
||||
| A.java:26:35:26:56 | propagateState(...) : String | A.java:26:13:26:81 | ...?...:... : String | provenance | |
|
||||
| A.java:26:50:26:50 | x : String | A.java:26:35:26:56 | propagateState(...) : String | provenance | Config |
|
||||
| A.java:26:60:26:81 | propagateState(...) : String | A.java:26:13:26:81 | ...?...:... : String | provenance | |
|
||||
| A.java:26:75:26:75 | x : String | A.java:26:60:26:81 | propagateState(...) : String | provenance | Config |
|
||||
| A.java:28:20:28:27 | source(...) : String | A.java:29:33:29:37 | step0 : String | provenance | |
|
||||
| A.java:29:20:29:38 | apply(...) : String | A.java:30:33:30:37 | step1 : String | provenance | |
|
||||
| A.java:29:20:29:38 | apply(...) : String | A.java:30:33:30:37 | step1 : String | provenance | |
|
||||
| A.java:29:33:29:37 | step0 : String | A.java:26:8:26:8 | x : String | provenance | |
|
||||
| A.java:29:33:29:37 | step0 : String | A.java:26:8:26:8 | x : String | provenance | |
|
||||
| A.java:29:33:29:37 | step0 : String | A.java:29:20:29:38 | apply(...) : String | provenance | Config |
|
||||
| A.java:29:33:29:37 | step0 : String | A.java:29:20:29:38 | apply(...) : String | provenance | Config |
|
||||
| A.java:30:20:30:38 | apply(...) : String | A.java:32:10:32:14 | step2 | provenance | |
|
||||
| A.java:30:33:30:37 | step1 : String | A.java:26:8:26:8 | x : String | provenance | |
|
||||
| A.java:30:33:30:37 | step1 : String | A.java:26:8:26:8 | x : String | provenance | |
|
||||
| A.java:30:33:30:37 | step1 : String | A.java:30:20:30:38 | apply(...) : String | provenance | Config |
|
||||
| A.java:30:33:30:37 | step1 : String | A.java:30:20:30:38 | apply(...) : String | provenance | Config |
|
||||
subpaths
|
||||
| A.java:18:33:18:37 | step0 : String | A.java:14:9:14:9 | x : String | A.java:14:14:14:35 | propagateState(...) : String | A.java:18:20:18:38 | apply(...) : String |
|
||||
| A.java:18:33:18:37 | step0 : String | A.java:15:9:15:9 | x : String | A.java:15:14:15:35 | propagateState(...) : String | A.java:18:20:18:38 | apply(...) : String |
|
||||
| A.java:19:33:19:37 | step1 : String | A.java:14:9:14:9 | x : String | A.java:14:14:14:35 | propagateState(...) : String | A.java:19:20:19:38 | apply(...) : String |
|
||||
| A.java:19:33:19:37 | step1 : String | A.java:15:9:15:9 | x : String | A.java:15:14:15:35 | propagateState(...) : String | A.java:19:20:19:38 | apply(...) : String |
|
||||
| A.java:29:33:29:37 | step0 : String | A.java:26:8:26:8 | x : String | A.java:26:13:26:81 | ...?...:... : String | A.java:29:20:29:38 | apply(...) : String |
|
||||
| A.java:29:33:29:37 | step0 : String | A.java:26:8:26:8 | x : String | A.java:26:13:26:81 | ...?...:... : String | A.java:29:20:29:38 | apply(...) : String |
|
||||
| A.java:30:33:30:37 | step1 : String | A.java:26:8:26:8 | x : String | A.java:26:13:26:81 | ...?...:... : String | A.java:30:20:30:38 | apply(...) : String |
|
||||
| A.java:30:33:30:37 | step1 : String | A.java:26:8:26:8 | x : String | A.java:26:13:26:81 | ...?...:... : String | A.java:30:20:30:38 | apply(...) : String |
|
||||
spuriousFlow
|
||||
#select
|
||||
| A.java:17:20:17:27 | source(...) : String | A.java:17:20:17:27 | source(...) : String | A.java:21:10:21:14 | step2 | Flow |
|
||||
| A.java:28:20:28:27 | source(...) : String | A.java:28:20:28:27 | source(...) : String | A.java:32:10:32:14 | step2 | Flow |
|
||||
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* @kind path-problem
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.DataFlow
|
||||
import DataFlow
|
||||
|
||||
MethodCall propagateCall(string state) {
|
||||
result.getMethod().getName() = "propagateState" and
|
||||
state = result.getArgument(1).(StringLiteral).getValue()
|
||||
}
|
||||
|
||||
module TestConfig implements StateConfigSig {
|
||||
class FlowState = string;
|
||||
|
||||
predicate isSource(Node n, FlowState state) {
|
||||
n.asExpr().(MethodCall).getMethod().getName() = "source" and state = ["A", "B"]
|
||||
}
|
||||
|
||||
predicate isSink(Node n, FlowState state) {
|
||||
n.asExpr() = any(MethodCall acc | acc.getMethod().getName() = "sink").getAnArgument() and
|
||||
state = ["A", "B"]
|
||||
}
|
||||
|
||||
predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) {
|
||||
exists(MethodCall call |
|
||||
call = propagateCall(state1) and
|
||||
state2 = state1 and
|
||||
node1.asExpr() = call.getArgument(0) and
|
||||
node2.asExpr() = call
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module TestFlow = DataFlow::GlobalWithState<TestConfig>;
|
||||
|
||||
module Graph = DataFlow::DeduplicatePathGraph<TestFlow::PathNode, TestFlow::PathGraph>;
|
||||
|
||||
/**
|
||||
* Holds if `node` is reachable from a call to `propagateState` with the given `state` argument.
|
||||
* `call` indicates if a call step was taken (i.e. into a subpath).
|
||||
*
|
||||
* We use this to check if one `propagateState` can flow out of another, which is not allowed.
|
||||
*/
|
||||
predicate reachableFromPropagate(Graph::PathNode node, string state, boolean call) {
|
||||
node.getNode().asExpr() = propagateCall(state) and call = false
|
||||
or
|
||||
exists(Graph::PathNode prev | reachableFromPropagate(prev, state, call) |
|
||||
Graph::edges(prev, node, _, _) and
|
||||
not Graph::subpaths(prev, node, _, _) // argument-passing edges are handled separately
|
||||
or
|
||||
Graph::subpaths(prev, _, _, node) // arg -> out (should be included in 'edges' but handle the case here for clarity)
|
||||
)
|
||||
or
|
||||
exists(Graph::PathNode prev |
|
||||
reachableFromPropagate(prev, state, _) and
|
||||
Graph::subpaths(prev, node, _, _) and // arg -> parameter
|
||||
call = true
|
||||
)
|
||||
or
|
||||
exists(Graph::PathNode prev |
|
||||
reachableFromPropagate(prev, state, call) and
|
||||
Graph::subpaths(_, _, prev, node) and // return -> out
|
||||
call = false
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is the return value of a `propagateState` call that appears to be reachable
|
||||
* with a different state than the one propagated by the call, indicating spurious flow resulting from
|
||||
* merging path nodes.
|
||||
*/
|
||||
query predicate spuriousFlow(Graph::PathNode node, string state1, string state2) {
|
||||
reachableFromPropagate(node, state1, _) and
|
||||
node.getNode().asExpr() = propagateCall(state2) and
|
||||
state1 != state2
|
||||
}
|
||||
|
||||
import Graph::PathGraph
|
||||
|
||||
from Graph::PathNode source, Graph::PathNode sink
|
||||
where TestFlow::flowPath(source.getAnOriginalPathNode(), sink.getAnOriginalPathNode())
|
||||
select source, source, sink, "Flow"
|
||||
@@ -9,42 +9,42 @@
|
||||
*/
|
||||
|
||||
import javascript
|
||||
import DataFlow
|
||||
import DataFlow::PathGraph
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration that tracks user-controlled values into a 'userId' property sent to a backend service.
|
||||
*/
|
||||
class IdorTaint extends TaintTracking::Configuration {
|
||||
IdorTaint() { this = "IdorTaint" }
|
||||
module IdorTaintConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node node) { node instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSource(Node node) { node instanceof RemoteFlowSource }
|
||||
predicate isSink(DataFlow::Node node) { exists(ClientRequest req | node = req.getADataNode()) }
|
||||
|
||||
override predicate isSink(Node node) { exists(ClientRequest req | node = req.getADataNode()) }
|
||||
|
||||
override predicate isAdditionalTaintStep(Node pred, Node succ) {
|
||||
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
// Step from x -> { userId: x }
|
||||
succ.(SourceNode).getAPropertyWrite("userId").getRhs() = pred
|
||||
node2.(DataFlow::SourceNode).getAPropertyWrite("userId").getRhs() = node1
|
||||
}
|
||||
|
||||
override predicate isSanitizerGuard(TaintTracking::SanitizerGuardNode node) {
|
||||
predicate isBarrier(DataFlow::Node node) {
|
||||
// After a check like `if (userId === session.user.id)`, the userId is considered safe.
|
||||
node instanceof EqualityGuard
|
||||
node = DataFlow::MakeBarrierGuard<EqualityGuard>::getABarrierNode()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A sanitizer for values that have successfully been compared to another value.
|
||||
*/
|
||||
class EqualityGuard extends TaintTracking::SanitizerGuardNode, ValueNode {
|
||||
class EqualityGuard extends DataFlow::ValueNode {
|
||||
override EqualityTest astNode;
|
||||
|
||||
override predicate sanitizes(boolean outcome, Expr e) {
|
||||
predicate blocksExpr(boolean outcome, Expr e) {
|
||||
e = astNode.getAnOperand() and
|
||||
outcome = astNode.getPolarity()
|
||||
}
|
||||
}
|
||||
|
||||
from IdorTaint cfg, PathNode source, PathNode sink
|
||||
where cfg.hasFlowPath(source, sink)
|
||||
module IdorTaintFlow = TaintTracking::Global<IdorTaintConfig>;
|
||||
|
||||
import IdorTaintFlow::PathGraph
|
||||
|
||||
from IdorTaintFlow::PathNode source, IdorTaintFlow::PathNode sink
|
||||
where IdorTaintFlow::flowPath(source, sink)
|
||||
select sink.getNode(), source, sink, "Unauthenticated user ID from $@.", source.getNode(), "here"
|
||||
|
||||
@@ -9,23 +9,25 @@
|
||||
*/
|
||||
|
||||
import javascript
|
||||
import DataFlow
|
||||
import DataFlow::PathGraph
|
||||
|
||||
class DecodingAfterSanitization extends TaintTracking::Configuration {
|
||||
DecodingAfterSanitization() { this = "DecodingAfterSanitization" }
|
||||
module DecodingAfterSanitizationConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node node) {
|
||||
node.(DataFlow::CallNode).getCalleeName() = "escapeHtml"
|
||||
}
|
||||
|
||||
override predicate isSource(Node node) { node.(CallNode).getCalleeName() = "escapeHtml" }
|
||||
|
||||
override predicate isSink(Node node) {
|
||||
exists(CallNode call |
|
||||
predicate isSink(DataFlow::Node node) {
|
||||
exists(DataFlow::CallNode call |
|
||||
call.getCalleeName().matches("decodeURI%") and
|
||||
node = call.getArgument(0)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
from DecodingAfterSanitization cfg, PathNode source, PathNode sink
|
||||
where cfg.hasFlowPath(source, sink)
|
||||
module DecodingAfterSanitizationFlow = TaintTracking::Global<DecodingAfterSanitizationConfig>;
|
||||
|
||||
import DecodingAfterSanitizationFlow::PathGraph
|
||||
|
||||
from DecodingAfterSanitizationFlow::PathNode source, DecodingAfterSanitizationFlow::PathNode sink
|
||||
where DecodingAfterSanitizationFlow::flowPath(source, sink)
|
||||
select sink.getNode(), source, sink, "URI decoding invalidates the HTML sanitization performed $@.",
|
||||
source.getNode(), "here"
|
||||
|
||||
@@ -9,16 +9,14 @@
|
||||
*/
|
||||
|
||||
import javascript
|
||||
import DataFlow
|
||||
import DataFlow::PathGraph
|
||||
|
||||
/**
|
||||
* A call to a function that may introduce HTML meta-characters by
|
||||
* replacing `%3C` or `\u003C` with `<`.
|
||||
*/
|
||||
class DecodingCall extends CallNode {
|
||||
class DecodingCall extends DataFlow::CallNode {
|
||||
string kind;
|
||||
Node input;
|
||||
DataFlow::Node input;
|
||||
|
||||
DecodingCall() {
|
||||
this.getCalleeName().matches("decodeURI%") and
|
||||
@@ -33,20 +31,24 @@ class DecodingCall extends CallNode {
|
||||
string getKind() { result = kind }
|
||||
|
||||
/** Gets the input being decoded. */
|
||||
Node getInput() { result = input }
|
||||
DataFlow::Node getInput() { result = input }
|
||||
}
|
||||
|
||||
class DecodingAfterSanitization extends TaintTracking::Configuration {
|
||||
DecodingAfterSanitization() { this = "DecodingAfterSanitization" }
|
||||
module DecodingAfterSanitizationConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node node) { node instanceof HtmlSanitizerCall }
|
||||
|
||||
override predicate isSource(Node node) { node instanceof HtmlSanitizerCall }
|
||||
|
||||
override predicate isSink(Node node) { node = any(DecodingCall c).getInput() }
|
||||
predicate isSink(DataFlow::Node node) { node = any(DecodingCall c).getInput() }
|
||||
}
|
||||
|
||||
from DecodingAfterSanitization cfg, PathNode source, PathNode sink, DecodingCall decoder
|
||||
module DecodingAfterSanitizationFlow = TaintTracking::Global<DecodingAfterSanitizationConfig>;
|
||||
|
||||
import DecodingAfterSanitizationFlow::PathGraph
|
||||
|
||||
from
|
||||
DecodingAfterSanitizationFlow::PathNode source, DecodingAfterSanitizationFlow::PathNode sink,
|
||||
DecodingCall decoder
|
||||
where
|
||||
cfg.hasFlowPath(source, sink) and
|
||||
DecodingAfterSanitizationFlow::flowPath(source, sink) and
|
||||
decoder.getInput() = sink.getNode()
|
||||
select sink.getNode(), source, sink, decoder.getKind() + " invalidates $@.", source.getNode(),
|
||||
"this HTML sanitization"
|
||||
|
||||
@@ -8,16 +8,17 @@
|
||||
*/
|
||||
|
||||
import javascript
|
||||
import DataFlow
|
||||
|
||||
class EvalTaint extends TaintTracking::Configuration {
|
||||
EvalTaint() { this = "EvalTaint" }
|
||||
module EvalTaintConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node node) { node instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSource(Node node) { node instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSink(Node node) { node = globalVarRef("eval").getACall().getArgument(0) }
|
||||
predicate isSink(DataFlow::Node node) {
|
||||
node = DataFlow::globalVarRef("eval").getACall().getArgument(0)
|
||||
}
|
||||
}
|
||||
|
||||
from EvalTaint cfg, Node source, Node sink
|
||||
where cfg.hasFlow(source, sink)
|
||||
module EvalTaintFlow = TaintTracking::Global<EvalTaintConfig>;
|
||||
|
||||
from DataFlow::Node source, DataFlow::Node sink
|
||||
where EvalTaintFlow::flow(source, sink)
|
||||
select sink, "Eval with user-controlled input from $@.", source, "here"
|
||||
|
||||
@@ -9,18 +9,20 @@
|
||||
*/
|
||||
|
||||
import javascript
|
||||
import DataFlow
|
||||
import DataFlow::PathGraph
|
||||
|
||||
class EvalTaint extends TaintTracking::Configuration {
|
||||
EvalTaint() { this = "EvalTaint" }
|
||||
module EvalTaintConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node node) { node instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSource(Node node) { node instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSink(Node node) { node = globalVarRef("eval").getACall().getArgument(0) }
|
||||
predicate isSink(DataFlow::Node node) {
|
||||
node = DataFlow::globalVarRef("eval").getACall().getArgument(0)
|
||||
}
|
||||
}
|
||||
|
||||
from EvalTaint cfg, PathNode source, PathNode sink
|
||||
where cfg.hasFlowPath(source, sink)
|
||||
module EvalTaintFlow = TaintTracking::Global<EvalTaintConfig>;
|
||||
|
||||
import EvalTaintFlow::PathGraph
|
||||
|
||||
from EvalTaintFlow::PathNode source, EvalTaintFlow::PathNode sink
|
||||
where EvalTaintFlow::flowPath(source, sink)
|
||||
select sink.getNode(), source, sink, "Eval with user-controlled input from $@.", source.getNode(),
|
||||
"here"
|
||||
|
||||
@@ -9,8 +9,6 @@
|
||||
*/
|
||||
|
||||
import javascript
|
||||
import DataFlow
|
||||
import DataFlow::PathGraph
|
||||
|
||||
/**
|
||||
* A dataflow configuration that tracks authentication tokens ("authKey")
|
||||
@@ -26,33 +24,37 @@ import DataFlow::PathGraph
|
||||
* }), '*');
|
||||
* ```
|
||||
*/
|
||||
class AuthKeyTracking extends DataFlow::Configuration {
|
||||
AuthKeyTracking() { this = "AuthKeyTracking" }
|
||||
module AuthKeyTrackingConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node node) {
|
||||
node.(DataFlow::PropRead).getPropertyName() = "authKey"
|
||||
}
|
||||
|
||||
override predicate isSource(Node node) { node.(PropRead).getPropertyName() = "authKey" }
|
||||
|
||||
override predicate isSink(Node node) {
|
||||
exists(MethodCallNode call |
|
||||
predicate isSink(DataFlow::Node node) {
|
||||
exists(DataFlow::MethodCallNode call |
|
||||
call.getMethodName() = "postMessage" and
|
||||
call.getArgument(1).getStringValue() = "*" and // no restriction on target origin
|
||||
call.getArgument(0) = node
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isAdditionalFlowStep(Node pred, Node succ) {
|
||||
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
// Step into objects: x -> { f: x }
|
||||
succ.(SourceNode).getAPropertyWrite().getRhs() = pred
|
||||
node2.(DataFlow::SourceNode).getAPropertyWrite().getRhs() = node1
|
||||
or
|
||||
// Step through JSON serialization: x -> JSON.stringify(x)
|
||||
// Note: TaintTracking::Configuration includes this step by default, but not DataFlow::Configuration
|
||||
exists(CallNode call |
|
||||
call = globalVarRef("JSON").getAMethodCall("stringify") and
|
||||
pred = call.getArgument(0) and
|
||||
succ = call
|
||||
exists(DataFlow::CallNode call |
|
||||
call = DataFlow::globalVarRef("JSON").getAMethodCall("stringify") and
|
||||
node1 = call.getArgument(0) and
|
||||
node2 = call
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
from AuthKeyTracking cfg, PathNode source, PathNode sink
|
||||
where cfg.hasFlowPath(source, sink)
|
||||
module AuthKeyTracking = DataFlow::Global<AuthKeyTrackingConfig>;
|
||||
|
||||
import AuthKeyTracking::PathGraph
|
||||
|
||||
from AuthKeyTracking::PathNode source, AuthKeyTracking::PathNode sink
|
||||
where AuthKeyTracking::flowPath(source, sink)
|
||||
select sink.getNode(), source, sink, "Message leaks the authKey from $@.", source.getNode(), "here"
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
import javascript
|
||||
import semmle.javascript.security.dataflow.StoredXssQuery
|
||||
import DataFlow::PathGraph
|
||||
import StoredXssFlow::PathGraph
|
||||
|
||||
/**
|
||||
* The data returned from a MySQL query, such as the `data` parameter in this example:
|
||||
@@ -31,6 +31,6 @@ class MysqlSource extends Source {
|
||||
}
|
||||
}
|
||||
|
||||
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
|
||||
where cfg.hasFlowPath(source, sink)
|
||||
from StoredXssFlow::PathNode source, StoredXssFlow::PathNode sink
|
||||
where StoredXssFlow::flowPath(source, sink)
|
||||
select sink.getNode(), source, sink, "Stored XSS from $@.", source.getNode(), "database value."
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
import javascript
|
||||
import semmle.javascript.security.dataflow.StoredXssQuery
|
||||
import DataFlow::PathGraph
|
||||
import StoredXssFlow::PathGraph
|
||||
|
||||
/**
|
||||
* Gets an instance of `mysql.createConnection()`, tracked globally.
|
||||
@@ -45,6 +45,6 @@ class MysqlSource extends Source {
|
||||
MysqlSource() { this = mysqlConnection().getAMethodCall("query").getCallback(1).getParameter(1) }
|
||||
}
|
||||
|
||||
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
|
||||
where cfg.hasFlowPath(source, sink)
|
||||
from StoredXssFlow::PathNode source, StoredXssFlow::PathNode sink
|
||||
where StoredXssFlow::flowPath(source, sink)
|
||||
select sink.getNode(), source, sink, "Stored XSS from $@.", source.getNode(), "database value."
|
||||
|
||||
@@ -8,8 +8,6 @@
|
||||
*/
|
||||
|
||||
import javascript
|
||||
import DataFlow
|
||||
import DataFlow::PathGraph
|
||||
|
||||
/**
|
||||
* Gets the name of an unescaped placeholder in a lodash template.
|
||||
@@ -21,13 +19,11 @@ string getAPlaceholderInString(string s) {
|
||||
result = s.regexpCapture(".*<%=\\s*([a-zA-Z0-9_]+)\\s*%>.*", 1)
|
||||
}
|
||||
|
||||
class TemplateInjection extends TaintTracking::Configuration {
|
||||
TemplateInjection() { this = "TemplateInjection" }
|
||||
module TemplateInjectionConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node node) { node instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSource(Node node) { node instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSink(Node node) {
|
||||
exists(CallNode call, string placeholder |
|
||||
predicate isSink(DataFlow::Node node) {
|
||||
exists(DataFlow::CallNode call, string placeholder |
|
||||
call = LodashUnderscore::member("template").getACall() and
|
||||
placeholder = getAPlaceholderInString(call.getArgument(0).getStringValue()) and
|
||||
node = call.getOptionArgument(1, placeholder)
|
||||
@@ -35,7 +31,11 @@ class TemplateInjection extends TaintTracking::Configuration {
|
||||
}
|
||||
}
|
||||
|
||||
from TemplateInjection cfg, PathNode source, PathNode sink
|
||||
where cfg.hasFlowPath(source, sink)
|
||||
module TemplateInjectionFlow = TaintTracking::Global<TemplateInjectionConfig>;
|
||||
|
||||
import TemplateInjectionFlow::PathGraph
|
||||
|
||||
from TemplateInjectionFlow::PathNode source, TemplateInjectionFlow::PathNode sink
|
||||
where TemplateInjectionFlow::flowPath(source, sink)
|
||||
select sink.getNode(), source, sink,
|
||||
"User-controlled value from $@ occurs unescaped in a lodash template.", source.getNode(), "here."
|
||||
|
||||
@@ -9,8 +9,10 @@ dependencies:
|
||||
codeql/dataflow: ${workspace}
|
||||
codeql/mad: ${workspace}
|
||||
codeql/regex: ${workspace}
|
||||
codeql/ssa: ${workspace}
|
||||
codeql/threat-models: ${workspace}
|
||||
codeql/tutorial: ${workspace}
|
||||
codeql/typetracking: ${workspace}
|
||||
codeql/util: ${workspace}
|
||||
codeql/xml: ${workspace}
|
||||
codeql/yaml: ${workspace}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
import javascript
|
||||
private import semmle.javascript.internal.CachedStages
|
||||
private import Expressions.ExprHasNoEffect
|
||||
private import semmle.javascript.dataflow.internal.DataFlowNode
|
||||
|
||||
/**
|
||||
* Companion module to the `AmdModuleDefinition` class.
|
||||
@@ -84,10 +85,15 @@ class AmdModuleDefinition extends CallExpr instanceof AmdModuleDefinition::Range
|
||||
result instanceof DataFlow::ValueNode
|
||||
}
|
||||
|
||||
private DataFlow::Node getFactoryNodeInternal() {
|
||||
// To avoid recursion, this should not depend on `SourceNode`.
|
||||
result = DataFlow::valueNode(this.getLastArgument()) or
|
||||
result = this.getFactoryNodeInternal().getAPredecessor()
|
||||
/**
|
||||
* Gets the factory function of this module definition.
|
||||
*/
|
||||
Function getFactoryFunction() { TValueNode(result) = this.getFactoryNodeInternal() }
|
||||
|
||||
private EarlyStageNode getFactoryNodeInternal() {
|
||||
result = TValueNode(this.getLastArgument())
|
||||
or
|
||||
DataFlow::localFlowStep(result, this.getFactoryNodeInternal())
|
||||
}
|
||||
|
||||
/** Gets the expression defining this module. */
|
||||
@@ -139,7 +145,10 @@ class AmdModuleDefinition extends CallExpr instanceof AmdModuleDefinition::Range
|
||||
* Gets the `i`th parameter of the factory function of this module.
|
||||
*/
|
||||
private Parameter getFactoryParameter(int i) {
|
||||
this.getFactoryNodeInternal().asExpr().(Function).getParameter(i) = result
|
||||
exists(Function fun |
|
||||
this.getFactoryNodeInternal() = TValueNode(fun) and
|
||||
result = fun.getParameter(i)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,7 +9,7 @@ module ArrayTaintTracking {
|
||||
/**
|
||||
* A taint propagating data flow edge caused by the builtin array functions.
|
||||
*/
|
||||
private class ArrayFunctionTaintStep extends TaintTracking::SharedTaintStep {
|
||||
private class ArrayFunctionTaintStep extends TaintTracking::LegacyTaintStep {
|
||||
override predicate arrayStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
arrayFunctionTaintStep(pred, succ, _)
|
||||
}
|
||||
@@ -130,7 +130,7 @@ private module ArrayDataFlow {
|
||||
* A step modeling the creation of an Array using the `Array.from(x)` method.
|
||||
* The step copies the elements of the argument (set, array, or iterator elements) into the resulting array.
|
||||
*/
|
||||
private class ArrayFrom extends PreCallGraphStep {
|
||||
private class ArrayFrom extends LegacyPreCallGraphStep {
|
||||
override predicate loadStoreStep(
|
||||
DataFlow::Node pred, DataFlow::SourceNode succ, string fromProp, string toProp
|
||||
) {
|
||||
@@ -150,7 +150,7 @@ private module ArrayDataFlow {
|
||||
*
|
||||
* Such a step can occur both with the `push` and `unshift` methods, or when creating a new array.
|
||||
*/
|
||||
private class ArrayCopySpread extends PreCallGraphStep {
|
||||
private class ArrayCopySpread extends LegacyPreCallGraphStep {
|
||||
override predicate loadStoreStep(
|
||||
DataFlow::Node pred, DataFlow::SourceNode succ, string fromProp, string toProp
|
||||
) {
|
||||
@@ -171,7 +171,7 @@ private module ArrayDataFlow {
|
||||
/**
|
||||
* A step for storing an element on an array using `arr.push(e)` or `arr.unshift(e)`.
|
||||
*/
|
||||
private class ArrayAppendStep extends PreCallGraphStep {
|
||||
private class ArrayAppendStep extends LegacyPreCallGraphStep {
|
||||
override predicate storeStep(DataFlow::Node element, DataFlow::SourceNode obj, string prop) {
|
||||
prop = arrayElement() and
|
||||
exists(DataFlow::MethodCallNode call |
|
||||
@@ -202,7 +202,7 @@ private module ArrayDataFlow {
|
||||
* A step for reading/writing an element from an array inside a for-loop.
|
||||
* E.g. a read from `foo[i]` to `bar` in `for(var i = 0; i < arr.length; i++) {bar = foo[i]}`.
|
||||
*/
|
||||
private class ArrayIndexingStep extends PreCallGraphStep {
|
||||
private class ArrayIndexingStep extends LegacyPreCallGraphStep {
|
||||
override predicate loadStep(DataFlow::Node obj, DataFlow::Node element, string prop) {
|
||||
exists(ArrayIndexingAccess access |
|
||||
prop = arrayElement() and
|
||||
@@ -224,7 +224,7 @@ private module ArrayDataFlow {
|
||||
* A step for retrieving an element from an array using `.pop()`, `.shift()`, or `.at()`.
|
||||
* E.g. `array.pop()`.
|
||||
*/
|
||||
private class ArrayPopStep extends PreCallGraphStep {
|
||||
private class ArrayPopStep extends LegacyPreCallGraphStep {
|
||||
override predicate loadStep(DataFlow::Node obj, DataFlow::Node element, string prop) {
|
||||
exists(DataFlow::MethodCallNode call |
|
||||
call.getMethodName() = ["pop", "shift", "at"] and
|
||||
@@ -245,7 +245,7 @@ private module ArrayDataFlow {
|
||||
*
|
||||
* And the second parameter in the callback is the array ifself, so there is a `loadStoreStep` from the array to that second parameter.
|
||||
*/
|
||||
private class ArrayIteration extends PreCallGraphStep {
|
||||
private class ArrayIteration extends LegacyPreCallGraphStep {
|
||||
override predicate loadStep(DataFlow::Node obj, DataFlow::Node element, string prop) {
|
||||
exists(DataFlow::MethodCallNode call |
|
||||
call.getMethodName() = ["map", "forEach"] and
|
||||
@@ -277,7 +277,7 @@ private module ArrayDataFlow {
|
||||
/**
|
||||
* A step for creating an array and storing the elements in the array.
|
||||
*/
|
||||
private class ArrayCreationStep extends PreCallGraphStep {
|
||||
private class ArrayCreationStep extends LegacyPreCallGraphStep {
|
||||
override predicate storeStep(DataFlow::Node element, DataFlow::SourceNode obj, string prop) {
|
||||
exists(DataFlow::ArrayCreationNode array, int i |
|
||||
element = array.getElement(i) and
|
||||
@@ -291,7 +291,7 @@ private module ArrayDataFlow {
|
||||
* A step modeling that `splice` can insert elements into an array.
|
||||
* For example in `array.splice(i, del, e1, e2, ...)`: if any item is tainted, then so is `array`
|
||||
*/
|
||||
private class ArraySpliceStep extends PreCallGraphStep {
|
||||
private class ArraySpliceStep extends LegacyPreCallGraphStep {
|
||||
override predicate storeStep(DataFlow::Node element, DataFlow::SourceNode obj, string prop) {
|
||||
exists(DataFlow::MethodCallNode call |
|
||||
call.getMethodName() = ["splice", "toSpliced"] and
|
||||
@@ -319,7 +319,7 @@ private module ArrayDataFlow {
|
||||
* A step for modeling `concat`.
|
||||
* For example in `e = arr1.concat(arr2, arr3)`: if any of the `arr` is tainted, then so is `e`.
|
||||
*/
|
||||
private class ArrayConcatStep extends PreCallGraphStep {
|
||||
private class ArrayConcatStep extends LegacyPreCallGraphStep {
|
||||
override predicate loadStoreStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) {
|
||||
exists(DataFlow::MethodCallNode call |
|
||||
call.getMethodName() = "concat" and
|
||||
@@ -333,7 +333,7 @@ private module ArrayDataFlow {
|
||||
/**
|
||||
* A step for modeling that elements from an array `arr` also appear in the result from calling `slice`/`splice`/`filter`/`toSpliced`.
|
||||
*/
|
||||
private class ArraySliceStep extends PreCallGraphStep {
|
||||
private class ArraySliceStep extends LegacyPreCallGraphStep {
|
||||
override predicate loadStoreStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) {
|
||||
exists(DataFlow::MethodCallNode call |
|
||||
call.getMethodName() = ["slice", "splice", "filter", "toSpliced"] and
|
||||
@@ -347,7 +347,7 @@ private module ArrayDataFlow {
|
||||
/**
|
||||
* A step modeling that elements from an array `arr` are received by calling `find`.
|
||||
*/
|
||||
private class ArrayFindStep extends PreCallGraphStep {
|
||||
private class ArrayFindStep extends LegacyPreCallGraphStep {
|
||||
override predicate loadStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
|
||||
exists(DataFlow::CallNode call |
|
||||
call = arrayFindCall(pred) and
|
||||
@@ -397,7 +397,7 @@ private module ArrayLibraries {
|
||||
/**
|
||||
* A taint step through the `arrify` library, or other libraries that (maybe) convert values into arrays.
|
||||
*/
|
||||
private class ArrayifyStep extends TaintTracking::SharedTaintStep {
|
||||
private class ArrayifyStep extends TaintTracking::LegacyTaintStep {
|
||||
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
exists(API::CallNode call | call = API::moduleImport(["arrify", "array-ify"]).getACall() |
|
||||
pred = call.getArgument(0) and succ = call
|
||||
@@ -417,7 +417,7 @@ private module ArrayLibraries {
|
||||
/**
|
||||
* A taint step for a library that copies the elements of an array into another array.
|
||||
*/
|
||||
private class ArrayCopyTaint extends TaintTracking::SharedTaintStep {
|
||||
private class ArrayCopyTaint extends TaintTracking::LegacyTaintStep {
|
||||
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
exists(DataFlow::CallNode call |
|
||||
call = arrayCopyCall(pred) and
|
||||
@@ -429,7 +429,7 @@ private module ArrayLibraries {
|
||||
/**
|
||||
* A loadStoreStep for a library that copies the elements of an array into another array.
|
||||
*/
|
||||
private class ArrayCopyLoadStore extends PreCallGraphStep {
|
||||
private class ArrayCopyLoadStore extends LegacyPreCallGraphStep {
|
||||
override predicate loadStoreStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) {
|
||||
exists(DataFlow::CallNode call |
|
||||
call = arrayCopyCall(pred) and
|
||||
@@ -442,7 +442,7 @@ private module ArrayLibraries {
|
||||
/**
|
||||
* A taint step through a call to `Array.prototype.flat` or a polyfill implementing array flattening.
|
||||
*/
|
||||
private class ArrayFlatStep extends TaintTracking::SharedTaintStep {
|
||||
private class ArrayFlatStep extends TaintTracking::LegacyTaintStep {
|
||||
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
exists(DataFlow::CallNode call | succ = call |
|
||||
call.(DataFlow::MethodCallNode).getMethodName() = "flat" and
|
||||
|
||||
@@ -3,356 +3,4 @@
|
||||
* liveness information for local variables.
|
||||
*/
|
||||
|
||||
import javascript
|
||||
private import internal.StmtContainers
|
||||
private import semmle.javascript.internal.CachedStages
|
||||
|
||||
/**
|
||||
* Holds if `nd` starts a new basic block.
|
||||
*/
|
||||
private predicate startsBB(ControlFlowNode nd) {
|
||||
not exists(nd.getAPredecessor()) and exists(nd.getASuccessor())
|
||||
or
|
||||
nd.isJoin()
|
||||
or
|
||||
nd.getAPredecessor().isBranch()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the first node of basic block `succ` is a control flow
|
||||
* successor of the last node of basic block `bb`.
|
||||
*/
|
||||
private predicate succBB(BasicBlock bb, BasicBlock succ) { succ = bb.getLastNode().getASuccessor() }
|
||||
|
||||
/**
|
||||
* Holds if the first node of basic block `bb` is a control flow
|
||||
* successor of the last node of basic block `pre`.
|
||||
*/
|
||||
private predicate predBB(BasicBlock bb, BasicBlock pre) { succBB(pre, bb) }
|
||||
|
||||
/** Holds if `bb` is an entry basic block. */
|
||||
private predicate entryBB(BasicBlock bb) { bb.getFirstNode() instanceof ControlFlowEntryNode }
|
||||
|
||||
/** Holds if `bb` is an exit basic block. */
|
||||
private predicate exitBB(BasicBlock bb) { bb.getLastNode() instanceof ControlFlowExitNode }
|
||||
|
||||
cached
|
||||
private module Internal {
|
||||
/**
|
||||
* Holds if `succ` is a control flow successor of `nd` within the same basic block.
|
||||
*/
|
||||
private predicate intraBBSucc(ControlFlowNode nd, ControlFlowNode succ) {
|
||||
succ = nd.getASuccessor() and
|
||||
not succ instanceof BasicBlock
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `nd` is the `i`th node in basic block `bb`.
|
||||
*
|
||||
* In other words, `i` is the shortest distance from a node `bb`
|
||||
* that starts a basic block to `nd` along the `intraBBSucc` relation.
|
||||
*/
|
||||
cached
|
||||
predicate bbIndex(BasicBlock bb, ControlFlowNode nd, int i) =
|
||||
shortestDistances(startsBB/1, intraBBSucc/2)(bb, nd, i)
|
||||
|
||||
cached
|
||||
int bbLength(BasicBlock bb) { result = strictcount(ControlFlowNode nd | bbIndex(bb, nd, _)) }
|
||||
|
||||
cached
|
||||
predicate useAt(BasicBlock bb, int i, Variable v, VarUse u) {
|
||||
Stages::BasicBlocks::ref() and
|
||||
v = u.getVariable() and
|
||||
bbIndex(bb, u, i)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate defAt(BasicBlock bb, int i, Variable v, VarDef d) {
|
||||
exists(VarRef lhs |
|
||||
lhs = d.getTarget().(BindingPattern).getABindingVarRef() and
|
||||
v = lhs.getVariable()
|
||||
|
|
||||
lhs = d.getTarget() and
|
||||
bbIndex(bb, d, i)
|
||||
or
|
||||
exists(PropertyPattern pp |
|
||||
lhs = pp.getValuePattern() and
|
||||
bbIndex(bb, pp, i)
|
||||
)
|
||||
or
|
||||
exists(ObjectPattern op |
|
||||
lhs = op.getRest() and
|
||||
bbIndex(bb, lhs, i)
|
||||
)
|
||||
or
|
||||
exists(ArrayPattern ap |
|
||||
lhs = ap.getAnElement() and
|
||||
bbIndex(bb, lhs, i)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate reachableBB(BasicBlock bb) {
|
||||
entryBB(bb)
|
||||
or
|
||||
exists(BasicBlock predBB | succBB(predBB, bb) | reachableBB(predBB))
|
||||
}
|
||||
}
|
||||
|
||||
private import Internal
|
||||
|
||||
/** Holds if `dom` is an immediate dominator of `bb`. */
|
||||
cached
|
||||
private predicate bbIDominates(BasicBlock dom, BasicBlock bb) =
|
||||
idominance(entryBB/1, succBB/2)(_, dom, bb)
|
||||
|
||||
/** Holds if `dom` is an immediate post-dominator of `bb`. */
|
||||
cached
|
||||
private predicate bbIPostDominates(BasicBlock dom, BasicBlock bb) =
|
||||
idominance(exitBB/1, predBB/2)(_, dom, bb)
|
||||
|
||||
/**
|
||||
* A basic block, that is, a maximal straight-line sequence of control flow nodes
|
||||
* without branches or joins.
|
||||
*
|
||||
* At the database level, a basic block is represented by its first control flow node.
|
||||
*/
|
||||
class BasicBlock extends @cfg_node, NodeInStmtContainer {
|
||||
cached
|
||||
BasicBlock() { Stages::BasicBlocks::ref() and startsBB(this) }
|
||||
|
||||
/** Gets a basic block succeeding this one. */
|
||||
BasicBlock getASuccessor() { succBB(this, result) }
|
||||
|
||||
/** Gets a basic block preceding this one. */
|
||||
BasicBlock getAPredecessor() { result.getASuccessor() = this }
|
||||
|
||||
/** Gets a node in this block. */
|
||||
ControlFlowNode getANode() { result = this.getNode(_) }
|
||||
|
||||
/** Gets the node at the given position in this block. */
|
||||
ControlFlowNode getNode(int pos) { bbIndex(this, result, pos) }
|
||||
|
||||
/** Gets the first node in this block. */
|
||||
ControlFlowNode getFirstNode() { result = this }
|
||||
|
||||
/** Gets the last node in this block. */
|
||||
ControlFlowNode getLastNode() { result = this.getNode(this.length() - 1) }
|
||||
|
||||
/** Gets the length of this block. */
|
||||
int length() { result = bbLength(this) }
|
||||
|
||||
/** Holds if this basic block uses variable `v` in its `i`th node `u`. */
|
||||
predicate useAt(int i, Variable v, VarUse u) { useAt(this, i, v, u) }
|
||||
|
||||
/** Holds if this basic block defines variable `v` in its `i`th node `d`. */
|
||||
predicate defAt(int i, Variable v, VarDef d) { defAt(this, i, v, d) }
|
||||
|
||||
/**
|
||||
* Holds if `v` is live at entry to this basic block and `u` is a use of `v`
|
||||
* witnessing the liveness.
|
||||
*
|
||||
* In other words, `u` is a use of `v` that is reachable from the
|
||||
* entry node of this basic block without going through a redefinition
|
||||
* of `v`. The use `u` may either be in this basic block, or in another
|
||||
* basic block reachable from this one.
|
||||
*/
|
||||
predicate isLiveAtEntry(Variable v, VarUse u) {
|
||||
// restrict `u` to be reachable from this basic block
|
||||
u = this.getASuccessor*().getANode() and
|
||||
(
|
||||
// shortcut: if `v` is never defined, then it must be live
|
||||
this.isDefinedInSameContainer(v)
|
||||
implies
|
||||
// otherwise, do full liveness computation
|
||||
this.isLiveAtEntryImpl(v, u)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `v` is live at entry to this basic block and `u` is a use of `v`
|
||||
* witnessing the liveness, where `v` is defined at least once in the enclosing
|
||||
* function or script.
|
||||
*/
|
||||
private predicate isLiveAtEntryImpl(Variable v, VarUse u) {
|
||||
this.isLocallyLiveAtEntry(v, u)
|
||||
or
|
||||
this.isDefinedInSameContainer(v) and
|
||||
not this.defAt(_, v, _) and
|
||||
this.getASuccessor().isLiveAtEntryImpl(v, u)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `v` is defined at least once in the function or script to which
|
||||
* this basic block belongs.
|
||||
*/
|
||||
private predicate isDefinedInSameContainer(Variable v) {
|
||||
exists(VarDef def | def.getAVariable() = v and def.getContainer() = this.getContainer())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `v` is a variable that is live at entry to this basic block.
|
||||
*
|
||||
* Note that this is equivalent to `bb.isLiveAtEntry(v, _)`, but may
|
||||
* be more efficient on large databases.
|
||||
*/
|
||||
predicate isLiveAtEntry(Variable v) {
|
||||
this.isLocallyLiveAtEntry(v, _)
|
||||
or
|
||||
not this.defAt(_, v, _) and this.getASuccessor().isLiveAtEntry(v)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if local variable `v` is live at entry to this basic block and
|
||||
* `u` is a use of `v` witnessing the liveness.
|
||||
*/
|
||||
predicate localIsLiveAtEntry(LocalVariable v, VarUse u) {
|
||||
this.isLocallyLiveAtEntry(v, u)
|
||||
or
|
||||
not this.defAt(_, v, _) and this.getASuccessor().localIsLiveAtEntry(v, u)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if local variable `v` is live at entry to this basic block.
|
||||
*/
|
||||
predicate localIsLiveAtEntry(LocalVariable v) {
|
||||
this.isLocallyLiveAtEntry(v, _)
|
||||
or
|
||||
not this.defAt(_, v, _) and this.getASuccessor().localIsLiveAtEntry(v)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `d` is a definition of `v` that is reachable from the beginning of
|
||||
* this basic block without going through a redefinition of `v`.
|
||||
*/
|
||||
predicate localMayBeOverwritten(LocalVariable v, VarDef d) {
|
||||
this.isLocallyOverwritten(v, d)
|
||||
or
|
||||
not this.defAt(_, v, _) and this.getASuccessor().localMayBeOverwritten(v, d)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the next index after `i` in this basic block at which `v` is
|
||||
* defined or used, provided that `d` is a definition of `v` at index `i`.
|
||||
* If there are no further uses or definitions of `v` after `i`, the
|
||||
* result is the length of this basic block.
|
||||
*/
|
||||
private int nextDefOrUseAfter(PurelyLocalVariable v, int i, VarDef d) {
|
||||
this.defAt(i, v, d) and
|
||||
result =
|
||||
min(int j |
|
||||
(this.defAt(j, v, _) or this.useAt(j, v, _) or j = this.length()) and
|
||||
j > i
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `d` defines variable `v` at the `i`th node of this basic block, and
|
||||
* the definition is live, that is, the variable may be read after this
|
||||
* definition and before a re-definition.
|
||||
*/
|
||||
predicate localLiveDefAt(PurelyLocalVariable v, int i, VarDef d) {
|
||||
exists(int j | j = this.nextDefOrUseAfter(v, i, d) |
|
||||
this.useAt(j, v, _)
|
||||
or
|
||||
j = this.length() and this.getASuccessor().localIsLiveAtEntry(v)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `u` is a use of `v` in this basic block, and there are
|
||||
* no definitions of `v` before it.
|
||||
*/
|
||||
private predicate isLocallyLiveAtEntry(Variable v, VarUse u) {
|
||||
exists(int n | this.useAt(n, v, u) | not exists(int m | m < n | this.defAt(m, v, _)))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `d` is a definition of `v` in this basic block, and there are
|
||||
* no other definitions of `v` before it.
|
||||
*/
|
||||
private predicate isLocallyOverwritten(Variable v, VarDef d) {
|
||||
exists(int n | this.defAt(n, v, d) | not exists(int m | m < n | this.defAt(m, v, _)))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the basic block that immediately dominates this basic block.
|
||||
*/
|
||||
ReachableBasicBlock getImmediateDominator() { bbIDominates(result, this) }
|
||||
}
|
||||
|
||||
/**
|
||||
* An unreachable basic block, that is, a basic block
|
||||
* whose first node is unreachable.
|
||||
*/
|
||||
class UnreachableBlock extends BasicBlock {
|
||||
UnreachableBlock() { this.getFirstNode().isUnreachable() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An entry basic block, that is, a basic block
|
||||
* whose first node is the entry node of a statement container.
|
||||
*/
|
||||
class EntryBasicBlock extends BasicBlock {
|
||||
EntryBasicBlock() { entryBB(this) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A basic block that is reachable from an entry basic block.
|
||||
*/
|
||||
class ReachableBasicBlock extends BasicBlock {
|
||||
ReachableBasicBlock() { reachableBB(this) }
|
||||
|
||||
/**
|
||||
* Holds if this basic block strictly dominates `bb`.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate strictlyDominates(ReachableBasicBlock bb) { bbIDominates+(this, bb) }
|
||||
|
||||
/**
|
||||
* Holds if this basic block dominates `bb`.
|
||||
*
|
||||
* This predicate is reflexive: each reachable basic block dominates itself.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate dominates(ReachableBasicBlock bb) { bbIDominates*(this, bb) }
|
||||
|
||||
/**
|
||||
* Holds if this basic block strictly post-dominates `bb`.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate strictlyPostDominates(ReachableBasicBlock bb) { bbIPostDominates+(this, bb) }
|
||||
|
||||
/**
|
||||
* Holds if this basic block post-dominates `bb`.
|
||||
*
|
||||
* This predicate is reflexive: each reachable basic block post-dominates itself.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate postDominates(ReachableBasicBlock bb) { bbIPostDominates*(this, bb) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A reachable basic block with more than one predecessor.
|
||||
*/
|
||||
class ReachableJoinBlock extends ReachableBasicBlock {
|
||||
ReachableJoinBlock() { this.getFirstNode().isJoin() }
|
||||
|
||||
/**
|
||||
* Holds if this basic block belongs to the dominance frontier of `b`, that is
|
||||
* `b` dominates a predecessor of this block, but not this block itself.
|
||||
*
|
||||
* Algorithm from Cooper et al., "A Simple, Fast Dominance Algorithm" (Figure 5),
|
||||
* who in turn attribute it to Ferrante et al., "The program dependence graph and
|
||||
* its use in optimization".
|
||||
*/
|
||||
predicate inDominanceFrontierOf(ReachableBasicBlock b) {
|
||||
b = this.getAPredecessor() and not b = this.getImmediateDominator()
|
||||
or
|
||||
exists(ReachableBasicBlock prev | this.inDominanceFrontierOf(prev) |
|
||||
b = prev.getImmediateDominator() and
|
||||
not b = this.getImmediateDominator()
|
||||
)
|
||||
}
|
||||
}
|
||||
import internal.BasicBlockInternal::Public
|
||||
|
||||
@@ -16,7 +16,7 @@ private module CollectionDataFlow {
|
||||
/**
|
||||
* A step for `Set.add()` method, which adds an element to a Set.
|
||||
*/
|
||||
private class SetAdd extends PreCallGraphStep {
|
||||
private class SetAdd extends LegacyPreCallGraphStep {
|
||||
override predicate storeStep(DataFlow::Node element, DataFlow::SourceNode obj, string prop) {
|
||||
exists(DataFlow::MethodCallNode call |
|
||||
call = obj.getAMethodCall("add") and
|
||||
@@ -29,7 +29,7 @@ private module CollectionDataFlow {
|
||||
/**
|
||||
* A step for the `Set` constructor, which copies any elements from the first argument into the resulting set.
|
||||
*/
|
||||
private class SetConstructor extends PreCallGraphStep {
|
||||
private class SetConstructor extends LegacyPreCallGraphStep {
|
||||
override predicate loadStoreStep(
|
||||
DataFlow::Node pred, DataFlow::SourceNode succ, string fromProp, string toProp
|
||||
) {
|
||||
@@ -49,7 +49,7 @@ private module CollectionDataFlow {
|
||||
* For sets and iterators the l-value are the elements of the set/iterator.
|
||||
* For maps the l-value is a tuple containing a key and a value.
|
||||
*/
|
||||
private class ForOfStep extends PreCallGraphStep {
|
||||
private class ForOfStep extends LegacyPreCallGraphStep {
|
||||
override predicate loadStep(DataFlow::Node obj, DataFlow::Node e, string prop) {
|
||||
exists(ForOfStmt forOf |
|
||||
obj = forOf.getIterationDomain().flow() and
|
||||
@@ -73,7 +73,7 @@ private module CollectionDataFlow {
|
||||
/**
|
||||
* A step for a call to `forEach` on a Set or Map.
|
||||
*/
|
||||
private class SetMapForEach extends PreCallGraphStep {
|
||||
private class SetMapForEach extends LegacyPreCallGraphStep {
|
||||
override predicate loadStep(DataFlow::Node obj, DataFlow::Node element, string prop) {
|
||||
exists(DataFlow::MethodCallNode call |
|
||||
call.getMethodName() = "forEach" and
|
||||
@@ -88,7 +88,7 @@ private module CollectionDataFlow {
|
||||
* A call to the `get` method on a Map.
|
||||
* If the key of the call to `get` has a known string value, then only the value corresponding to that key will be retrieved. (The known string value is encoded as part of the pseudo-property)
|
||||
*/
|
||||
private class MapGet extends PreCallGraphStep {
|
||||
private class MapGet extends LegacyPreCallGraphStep {
|
||||
override predicate loadStep(DataFlow::Node obj, DataFlow::Node element, string prop) {
|
||||
exists(DataFlow::MethodCallNode call |
|
||||
call.getMethodName() = "get" and
|
||||
@@ -108,7 +108,7 @@ private module CollectionDataFlow {
|
||||
* Otherwise the value will be stored into a pseudo-property corresponding to values with unknown keys.
|
||||
* The value will additionally be stored into a pseudo-property corresponding to all values.
|
||||
*/
|
||||
class MapSet extends PreCallGraphStep {
|
||||
class MapSet extends LegacyPreCallGraphStep {
|
||||
override predicate storeStep(DataFlow::Node element, DataFlow::SourceNode obj, string prop) {
|
||||
exists(DataFlow::MethodCallNode call |
|
||||
call = obj.getAMethodCall("set") and
|
||||
@@ -121,7 +121,7 @@ private module CollectionDataFlow {
|
||||
/**
|
||||
* A step for a call to `values` on a Map or a Set.
|
||||
*/
|
||||
private class MapAndSetValues extends PreCallGraphStep {
|
||||
private class MapAndSetValues extends LegacyPreCallGraphStep {
|
||||
override predicate loadStoreStep(
|
||||
DataFlow::Node pred, DataFlow::SourceNode succ, string fromProp, string toProp
|
||||
) {
|
||||
@@ -138,7 +138,7 @@ private module CollectionDataFlow {
|
||||
/**
|
||||
* A step for a call to `keys` on a Set.
|
||||
*/
|
||||
private class SetKeys extends PreCallGraphStep {
|
||||
private class SetKeys extends LegacyPreCallGraphStep {
|
||||
override predicate loadStoreStep(
|
||||
DataFlow::Node pred, DataFlow::SourceNode succ, string fromProp, string toProp
|
||||
) {
|
||||
|
||||
@@ -11,7 +11,7 @@ private import semmle.javascript.dataflow.internal.PreCallGraphStep
|
||||
private module GeneratorDataFlow {
|
||||
private import DataFlow::PseudoProperties
|
||||
|
||||
private class ArrayIteration extends PreCallGraphStep {
|
||||
private class ArrayIteration extends LegacyPreCallGraphStep {
|
||||
override predicate storeStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) {
|
||||
exists(DataFlow::FunctionNode f | f.getFunction().isGenerator() |
|
||||
prop = iteratorElement() and
|
||||
|
||||
@@ -4,6 +4,7 @@ import javascript
|
||||
private import NodeModuleResolutionImpl
|
||||
private import semmle.javascript.DynamicPropertyAccess as DynamicPropertyAccess
|
||||
private import semmle.javascript.internal.CachedStages
|
||||
private import semmle.javascript.dataflow.internal.DataFlowNode
|
||||
|
||||
/**
|
||||
* A Node.js module.
|
||||
@@ -240,69 +241,78 @@ private class RequireVariable extends Variable {
|
||||
*/
|
||||
private predicate moduleInFile(Module m, File f) { m.getFile() = f }
|
||||
|
||||
private predicate isModuleModule(DataFlow::Node nd) {
|
||||
exists(ImportDeclaration imp |
|
||||
imp.getImportedPath().getValue() = "module" and
|
||||
nd =
|
||||
[
|
||||
DataFlow::destructuredModuleImportNode(imp),
|
||||
DataFlow::valueNode(imp.getASpecifier().(ImportNamespaceSpecifier))
|
||||
]
|
||||
private predicate isModuleModule(EarlyStageNode nd) {
|
||||
exists(ImportDeclaration imp | imp.getImportedPath().getValue() = "module" |
|
||||
nd = TDestructuredModuleImportNode(imp)
|
||||
or
|
||||
nd = TValueNode(imp.getASpecifier().(ImportNamespaceSpecifier))
|
||||
)
|
||||
or
|
||||
isModuleModule(nd.getAPredecessor())
|
||||
exists(EarlyStageNode other |
|
||||
isModuleModule(other) and
|
||||
DataFlow::localFlowStep(other, nd)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate isCreateRequire(DataFlow::Node nd) {
|
||||
private predicate isCreateRequire(EarlyStageNode nd) {
|
||||
exists(PropAccess prop |
|
||||
isModuleModule(prop.getBase().flow()) and
|
||||
isModuleModule(TValueNode(prop.getBase())) and
|
||||
prop.getPropertyName() = "createRequire" and
|
||||
nd = prop.flow()
|
||||
nd = TValueNode(prop)
|
||||
)
|
||||
or
|
||||
exists(PropertyPattern prop |
|
||||
isModuleModule(prop.getObjectPattern().flow()) and
|
||||
isModuleModule(TValueNode(prop.getObjectPattern())) and
|
||||
prop.getName() = "createRequire" and
|
||||
nd = prop.getValuePattern().flow()
|
||||
nd = TValueNode(prop.getValuePattern())
|
||||
)
|
||||
or
|
||||
exists(ImportDeclaration decl, NamedImportSpecifier spec |
|
||||
decl.getImportedPath().getValue() = "module" and
|
||||
spec = decl.getASpecifier() and
|
||||
spec.getImportedName() = "createRequire" and
|
||||
nd = spec.flow()
|
||||
nd = TValueNode(spec)
|
||||
)
|
||||
or
|
||||
isCreateRequire(nd.getAPredecessor())
|
||||
exists(EarlyStageNode other |
|
||||
isCreateRequire(other) and
|
||||
DataFlow::localFlowStep(other, nd)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `nd` may refer to `require`, either directly or modulo local data flow.
|
||||
*/
|
||||
cached
|
||||
private predicate isRequire(DataFlow::Node nd) {
|
||||
nd.asExpr() = any(RequireVariable req).getAnAccess() and
|
||||
// `mjs` files explicitly disallow `require`
|
||||
not nd.getFile().getExtension() = "mjs"
|
||||
private predicate isRequire(EarlyStageNode nd) {
|
||||
exists(VarAccess access |
|
||||
access = any(RequireVariable v).getAnAccess() and
|
||||
nd = TValueNode(access) and
|
||||
// `mjs` files explicitly disallow `require`
|
||||
not access.getFile().getExtension() = "mjs"
|
||||
)
|
||||
or
|
||||
isRequire(nd.getAPredecessor())
|
||||
exists(EarlyStageNode other |
|
||||
isRequire(other) and
|
||||
DataFlow::localFlowStep(other, nd)
|
||||
)
|
||||
or
|
||||
// `import { createRequire } from 'module';`.
|
||||
// specialized to ES2015 modules to avoid recursion in the `DataFlow::moduleImport()` predicate and to avoid
|
||||
// negative recursion between `Import.getImportedModuleNode()` and `Import.getImportedModule()`, and
|
||||
// to avoid depending on `SourceNode` as this would make `SourceNode::Range` recursive.
|
||||
exists(CallExpr call |
|
||||
isCreateRequire(call.getCallee().flow()) and
|
||||
nd = call.flow()
|
||||
isCreateRequire(TValueNode(call.getCallee())) and
|
||||
nd = TValueNode(call)
|
||||
)
|
||||
or
|
||||
// `$.require('underscore');`.
|
||||
// NPM as supported in [XSJS files](https://www.npmjs.com/package/@sap/async-xsjs#npm-packages-support).
|
||||
exists(MethodCallExpr require |
|
||||
nd.getFile().getExtension() = ["xsjs", "xsjslib"] and
|
||||
require.getFile().getExtension() = ["xsjs", "xsjslib"] and
|
||||
require.getCalleeName() = "require" and
|
||||
require.getReceiver().(GlobalVarAccess).getName() = "$" and
|
||||
nd = require.getCallee().flow()
|
||||
nd = TValueNode(require.getCallee())
|
||||
)
|
||||
}
|
||||
|
||||
@@ -316,7 +326,7 @@ private predicate isRequire(DataFlow::Node nd) {
|
||||
* ```
|
||||
*/
|
||||
class Require extends CallExpr, Import {
|
||||
Require() { isRequire(this.getCallee().flow()) }
|
||||
Require() { isRequire(TValueNode(this.getCallee())) }
|
||||
|
||||
override PathExpr getImportedPath() { result = this.getArgument(0) }
|
||||
|
||||
@@ -410,7 +420,7 @@ private class RequirePath extends PathExprCandidate {
|
||||
this = any(Require req).getArgument(0)
|
||||
or
|
||||
exists(MethodCallExpr reqres |
|
||||
isRequire(reqres.getReceiver().flow()) and
|
||||
isRequire(TValueNode(reqres.getReceiver())) and
|
||||
reqres.getMethodName() = "resolve" and
|
||||
this = reqres.getArgument(0)
|
||||
)
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
*/
|
||||
|
||||
import javascript
|
||||
private import semmle.javascript.dataflow.internal.DataFlowNode
|
||||
|
||||
/**
|
||||
* Internal representation of paths as lists of components.
|
||||
@@ -381,16 +382,16 @@ private class PathExprString extends PathString {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private DataFlow::Node getAPathExprAlias(PathExpr expr) {
|
||||
result.getImmediatePredecessor().asExpr() = expr
|
||||
private EarlyStageNode getAPathExprAlias(PathExpr expr) {
|
||||
DataFlow::Impl::earlyStageImmediateFlowStep(TValueNode(expr), result)
|
||||
or
|
||||
result.getImmediatePredecessor() = getAPathExprAlias(expr)
|
||||
DataFlow::Impl::earlyStageImmediateFlowStep(getAPathExprAlias(expr), result)
|
||||
}
|
||||
|
||||
private class PathExprFromAlias extends PathExpr {
|
||||
private PathExpr other;
|
||||
|
||||
PathExprFromAlias() { this = getAPathExprAlias(other).asExpr() }
|
||||
PathExprFromAlias() { TValueNode(this) = getAPathExprAlias(other) }
|
||||
|
||||
override string getValue() { result = other.getValue() }
|
||||
|
||||
@@ -435,13 +436,15 @@ abstract class PathExprCandidate extends Expr {
|
||||
pragma[nomagic]
|
||||
private Expr getAPart1() { result = this or result = this.getAPart().getAChildExpr() }
|
||||
|
||||
private EarlyStageNode getAnAliasedPart1() {
|
||||
result = TValueNode(this.getAPart1())
|
||||
or
|
||||
DataFlow::Impl::earlyStageImmediateFlowStep(result, this.getAnAliasedPart1())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an expression that is nested inside this expression.
|
||||
*
|
||||
* Equivalent to `getAChildExpr*()`, but useful to enforce a better join order (in spite of
|
||||
* what the optimizer thinks, there are generally far fewer `PathExprCandidate`s than
|
||||
* `ConstantString`s).
|
||||
* Gets an expression that is depended on by an expression nested inside this expression.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
Expr getAPart() { result = this.getAPart1().flow().getImmediatePredecessor*().asExpr() }
|
||||
Expr getAPart() { TValueNode(result) = this.getAnAliasedPart1() }
|
||||
}
|
||||
|
||||
@@ -6,7 +6,9 @@ import javascript
|
||||
private import dataflow.internal.StepSummary
|
||||
|
||||
/**
|
||||
* A definition of a `Promise` object.
|
||||
* A call to the `Promise` constructor, such as `new Promise((resolve, reject) => { ... })`.
|
||||
*
|
||||
* This includes calls to the built-in `Promise` constructor as well as promise implementations from known libraries, such as `bluebird`.
|
||||
*/
|
||||
abstract class PromiseDefinition extends DataFlow::SourceNode {
|
||||
/** Gets the executor function of this promise object. */
|
||||
@@ -196,6 +198,8 @@ module Promises {
|
||||
|
||||
override string getAProperty() { result = [valueProp(), errorProp()] }
|
||||
}
|
||||
|
||||
predicate promiseConstructorRef = getAPromiseObject/0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -267,7 +271,7 @@ private import semmle.javascript.dataflow.internal.PreCallGraphStep
|
||||
* These steps are for `await p`, `new Promise()`, `Promise.resolve()`,
|
||||
* `Promise.then()`, `Promise.catch()`, and `Promise.finally()`.
|
||||
*/
|
||||
private class PromiseStep extends PreCallGraphStep {
|
||||
private class PromiseStep extends LegacyPreCallGraphStep {
|
||||
override predicate loadStep(DataFlow::Node obj, DataFlow::Node element, string prop) {
|
||||
PromiseFlow::loadStep(obj, element, prop)
|
||||
}
|
||||
@@ -459,7 +463,7 @@ module PromiseFlow {
|
||||
}
|
||||
}
|
||||
|
||||
private class PromiseTaintStep extends TaintTracking::SharedTaintStep {
|
||||
private class PromiseTaintStep extends TaintTracking::LegacyTaintStep {
|
||||
override predicate promiseStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
// from `x` to `new Promise((res, rej) => res(x))`
|
||||
pred = succ.(PromiseDefinition).getResolveParameter().getACall().getArgument(0)
|
||||
@@ -530,7 +534,7 @@ private module AsyncReturnSteps {
|
||||
/**
|
||||
* A data-flow step for ordinary and exceptional returns from async functions.
|
||||
*/
|
||||
private class AsyncReturn extends PreCallGraphStep {
|
||||
private class AsyncReturn extends LegacyPreCallGraphStep {
|
||||
override predicate storeStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) {
|
||||
exists(DataFlow::FunctionNode f | f.getFunction().isAsync() |
|
||||
// ordinary return
|
||||
@@ -548,7 +552,7 @@ private module AsyncReturnSteps {
|
||||
/**
|
||||
* A data-flow step for ordinary return from an async function in a taint configuration.
|
||||
*/
|
||||
private class AsyncTaintReturn extends TaintTracking::SharedTaintStep {
|
||||
private class AsyncTaintReturn extends TaintTracking::LegacyTaintStep {
|
||||
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
exists(Function f |
|
||||
f.isAsync() and
|
||||
@@ -665,7 +669,7 @@ private module ClosurePromise {
|
||||
/**
|
||||
* Taint steps through closure promise methods.
|
||||
*/
|
||||
private class ClosurePromiseTaintStep extends TaintTracking::SharedTaintStep {
|
||||
private class ClosurePromiseTaintStep extends TaintTracking::LegacyTaintStep {
|
||||
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
// static methods in goog.Promise
|
||||
exists(DataFlow::CallNode call, string name |
|
||||
@@ -699,7 +703,7 @@ private module DynamicImportSteps {
|
||||
* let Foo = await import('./foo');
|
||||
* ```
|
||||
*/
|
||||
class DynamicImportStep extends PreCallGraphStep {
|
||||
class DynamicImportStep extends LegacyPreCallGraphStep {
|
||||
override predicate storeStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) {
|
||||
exists(DynamicImportExpr imprt |
|
||||
pred = imprt.getImportedModule().getAnExportedValue("default") and
|
||||
|
||||
@@ -69,7 +69,7 @@ private class ArrayIterationCallbackAsPartialInvoke extends DataFlow::PartialInv
|
||||
* A flow step propagating the exception thrown from a callback to a method whose name coincides
|
||||
* a built-in Array iteration method, such as `forEach` or `map`.
|
||||
*/
|
||||
private class IteratorExceptionStep extends DataFlow::SharedFlowStep {
|
||||
private class IteratorExceptionStep extends DataFlow::LegacyFlowStep {
|
||||
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
exists(DataFlow::MethodCallNode call |
|
||||
call.getMethodName() = ["forEach", "each", "map", "filter", "some", "every", "fold", "reduce"] and
|
||||
@@ -160,6 +160,15 @@ class StringReplaceCall extends DataFlow::MethodCallNode {
|
||||
new = ret.getStringValue()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this call takes a regexp containing a wildcard-like term such as `.`.
|
||||
*
|
||||
* Also see `RegExp::isWildcardLike`.
|
||||
*/
|
||||
final predicate hasRegExpContainingWildcard() {
|
||||
RegExp::isWildcardLike(this.getRegExp().getRoot().getAChild*())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,397 @@
|
||||
/**
|
||||
* This contains three step-contribution classes, in order to support graceful deprecation of the old data flow library.
|
||||
*
|
||||
* - `class AdditionalFlowStep`: steps used only by the new dataflow library
|
||||
* - `class LegacyFlowStep`: steps used only by the old data flow library
|
||||
* - `class SharedFlowStep`: steps used by both
|
||||
*
|
||||
* The latter two will be deprecated in the future, but are currently not marked as `deprecated`.
|
||||
* This is because a library model should be able to support both data flow libraries simultaneously, without itself getting
|
||||
* deprecation warnings.
|
||||
*
|
||||
* To simplify correct consumption of these steps there is a correspondingly-named module for each:
|
||||
*
|
||||
* - `module AdditionalFlowStep`: exposes steps from `AdditionalFlowStep` and `SharedFlowStep` subclasses.
|
||||
* - `module LegacyFlowStep`: exposes steps from `LegacyFlowStep` and `SharedFlowStep` subclasses.
|
||||
* - `module SharedFlowStep`: exposes steps from all three classes.
|
||||
*
|
||||
* This design is intended to simplify consumption of steps, and to ensure existing consumers of `SharedFlowStep`
|
||||
* outside this codebase will continue to work with as few surprises as possible.
|
||||
*/
|
||||
|
||||
private import javascript
|
||||
private import semmle.javascript.internal.CachedStages
|
||||
|
||||
/**
|
||||
* A value-preserving data flow edge that should be used in all data flow configurations in
|
||||
* addition to standard data flow edges.
|
||||
*
|
||||
* This class is a singleton, and thus subclasses do not need to specify a characteristic predicate.
|
||||
*
|
||||
* As an alternative to this class, consider using `DataFlow::SummarizedCallable`.
|
||||
*
|
||||
* Note: For performance reasons, all subclasses of this class should be part
|
||||
* of the standard library. Use `isAdditionalFlowStep` for query-specific flow steps.
|
||||
*/
|
||||
class AdditionalFlowStep extends Unit {
|
||||
/**
|
||||
* Holds if `pred` → `succ` should be considered a value-preserving data flow edge.f
|
||||
*/
|
||||
predicate step(DataFlow::Node pred, DataFlow::Node succ) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `pred` → `succ` should be considered a value-preserving data flow edge that
|
||||
* crosses calling contexts.
|
||||
*/
|
||||
predicate jumpStep(DataFlow::Node pred, DataFlow::Node succ) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `pred` should be stored in the given `content` of the object `succ`.
|
||||
*/
|
||||
predicate storeStep(DataFlow::Node pred, DataFlow::ContentSet contents, DataFlow::Node succ) {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the given `content` of the object in `pred` should be read into `succ`.
|
||||
*/
|
||||
predicate readStep(DataFlow::Node pred, DataFlow::ContentSet contents, DataFlow::Node succ) {
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Contains predicates for accessing the steps contributed by `AdditionalFlowStep` and `SharedFlowStep` subclasses.
|
||||
*/
|
||||
cached
|
||||
module AdditionalFlowStep {
|
||||
cached
|
||||
private module Internal {
|
||||
// Forces this to be part of the `FlowSteps` stage.
|
||||
// We use a public predicate in a private module to avoid warnings about this being unused.
|
||||
cached
|
||||
predicate forceStage() { Stages::FlowSteps::ref() }
|
||||
}
|
||||
|
||||
bindingset[a, b]
|
||||
pragma[inline_late]
|
||||
private predicate sameContainer(DataFlow::Node a, DataFlow::Node b) {
|
||||
a.getContainer() = b.getContainer()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `pred` → `succ` should be considered a data flow edge.
|
||||
*/
|
||||
cached
|
||||
predicate step(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
any(AdditionalFlowStep s).step(pred, succ)
|
||||
or
|
||||
any(SharedFlowStep s).step(pred, succ) and
|
||||
sameContainer(pred, succ)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `pred` → `succ` should be considered a value-preserving data flow edge that
|
||||
* crosses calling contexts.
|
||||
*/
|
||||
cached
|
||||
predicate jumpStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
any(AdditionalFlowStep s).jumpStep(pred, succ)
|
||||
or
|
||||
any(SharedFlowStep s).step(pred, succ) and
|
||||
not sameContainer(pred, succ)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `pred` should be stored in the object `succ` under the property `prop`.
|
||||
*/
|
||||
cached
|
||||
predicate storeStep(DataFlow::Node pred, DataFlow::ContentSet contents, DataFlow::Node succ) {
|
||||
any(AdditionalFlowStep s).storeStep(pred, contents, succ)
|
||||
or
|
||||
exists(string prop |
|
||||
any(SharedFlowStep s).storeStep(pred, succ, prop) and
|
||||
contents = DataFlow::ContentSet::fromLegacyProperty(prop)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the property `prop` of the object `pred` should be read into `succ`.
|
||||
*/
|
||||
cached
|
||||
predicate readStep(DataFlow::Node pred, DataFlow::ContentSet contents, DataFlow::Node succ) {
|
||||
any(AdditionalFlowStep s).readStep(pred, contents, succ)
|
||||
or
|
||||
exists(string prop |
|
||||
any(SharedFlowStep s).loadStep(pred, succ, prop) and
|
||||
contents = DataFlow::ContentSet::fromLegacyProperty(prop)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A data flow edge that is only seen by the old, deprecated data flow library.
|
||||
*
|
||||
* This class is typically used when a step has been replaced by a flow summary. Since the old data flow
|
||||
* library does not support flow summaries, such a step should remain as a legacy step, until the old data flow
|
||||
* library can be removed.
|
||||
*
|
||||
* Note: For performance reasons, all subclasses of this class should be part
|
||||
* of the standard library. Override `Configuration::isAdditionalFlowStep`
|
||||
* for analysis-specific flow steps.
|
||||
*/
|
||||
class LegacyFlowStep extends Unit {
|
||||
/**
|
||||
* Holds if `pred` → `succ` should be considered a data flow edge.
|
||||
*/
|
||||
predicate step(DataFlow::Node pred, DataFlow::Node succ) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED. The `FlowLabel` class and steps involving flow labels are no longer used by any queries.
|
||||
*
|
||||
* Holds if `pred` → `succ` should be considered a data flow edge
|
||||
* transforming values with label `predlbl` to have label `succlbl`.
|
||||
*/
|
||||
deprecated predicate step(
|
||||
DataFlow::Node pred, DataFlow::Node succ, DataFlow::FlowLabel predlbl,
|
||||
DataFlow::FlowLabel succlbl
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `pred` should be stored in the object `succ` under the property `prop`.
|
||||
* The object `succ` must be a `DataFlow::SourceNode` for the object wherein the value is stored.
|
||||
*/
|
||||
predicate storeStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) { none() }
|
||||
|
||||
/**
|
||||
* Holds if the property `prop` of the object `pred` should be loaded into `succ`.
|
||||
*/
|
||||
predicate loadStep(DataFlow::Node pred, DataFlow::Node succ, string prop) { none() }
|
||||
|
||||
/**
|
||||
* Holds if the property `prop` should be copied from the object `pred` to the object `succ`.
|
||||
*/
|
||||
predicate loadStoreStep(DataFlow::Node pred, DataFlow::Node succ, string prop) { none() }
|
||||
|
||||
/**
|
||||
* Holds if the property `loadProp` should be copied from the object `pred` to the property `storeProp` of object `succ`.
|
||||
*/
|
||||
predicate loadStoreStep(
|
||||
DataFlow::Node pred, DataFlow::Node succ, string loadProp, string storeProp
|
||||
) {
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Contains predicates for accessing the steps contributed by `LegacyFlowStep` and `SharedFlowStep` subclasses.
|
||||
*/
|
||||
cached
|
||||
module LegacyFlowStep {
|
||||
/**
|
||||
* Holds if `pred` → `succ` should be considered a data flow edge.
|
||||
*/
|
||||
cached
|
||||
predicate step(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
any(LegacyFlowStep s).step(pred, succ)
|
||||
or
|
||||
any(SharedFlowStep s).step(pred, succ)
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED. The `FlowLabel` class and steps involving flow labels are no longer used by any queries.
|
||||
*
|
||||
* Holds if `pred` → `succ` should be considered a data flow edge
|
||||
* transforming values with label `predlbl` to have label `succlbl`.
|
||||
*/
|
||||
cached
|
||||
deprecated predicate step(
|
||||
DataFlow::Node pred, DataFlow::Node succ, DataFlow::FlowLabel predlbl,
|
||||
DataFlow::FlowLabel succlbl
|
||||
) {
|
||||
any(LegacyFlowStep s).step(pred, succ, predlbl, succlbl)
|
||||
or
|
||||
any(SharedFlowStep s).step(pred, succ, predlbl, succlbl)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `pred` should be stored in the object `succ` under the property `prop`.
|
||||
* The object `succ` must be a `DataFlow::SourceNode` for the object wherein the value is stored.
|
||||
*/
|
||||
cached
|
||||
predicate storeStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) {
|
||||
any(LegacyFlowStep s).storeStep(pred, succ, prop)
|
||||
or
|
||||
any(SharedFlowStep s).storeStep(pred, succ, prop)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the property `prop` of the object `pred` should be loaded into `succ`.
|
||||
*/
|
||||
cached
|
||||
predicate loadStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
|
||||
any(LegacyFlowStep s).loadStep(pred, succ, prop)
|
||||
or
|
||||
any(SharedFlowStep s).loadStep(pred, succ, prop)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the property `prop` should be copied from the object `pred` to the object `succ`.
|
||||
*/
|
||||
cached
|
||||
predicate loadStoreStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
|
||||
any(LegacyFlowStep s).loadStoreStep(pred, succ, prop)
|
||||
or
|
||||
any(SharedFlowStep s).loadStoreStep(pred, succ, prop)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the property `loadProp` should be copied from the object `pred` to the property `storeProp` of object `succ`.
|
||||
*/
|
||||
cached
|
||||
predicate loadStoreStep(
|
||||
DataFlow::Node pred, DataFlow::Node succ, string loadProp, string storeProp
|
||||
) {
|
||||
any(LegacyFlowStep s).loadStoreStep(pred, succ, loadProp, storeProp)
|
||||
or
|
||||
any(SharedFlowStep s).loadStoreStep(pred, succ, loadProp, storeProp)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A data flow edge that should be added to all data flow configurations in
|
||||
* addition to standard data flow edges.
|
||||
*
|
||||
* This class is a singleton, and thus subclasses do not need to specify a characteristic predicate.
|
||||
*
|
||||
* Note: For performance reasons, all subclasses of this class should be part
|
||||
* of the standard library. Override `Configuration::isAdditionalFlowStep`
|
||||
* for analysis-specific flow steps.
|
||||
*/
|
||||
class SharedFlowStep extends Unit {
|
||||
/**
|
||||
* Holds if `pred` → `succ` should be considered a data flow edge.
|
||||
*/
|
||||
predicate step(DataFlow::Node pred, DataFlow::Node succ) { none() }
|
||||
|
||||
/**
|
||||
* DEPRECATED. The `FlowLabel` class and steps involving flow labels are no longer used by any queries.
|
||||
*
|
||||
* Holds if `pred` → `succ` should be considered a data flow edge
|
||||
* transforming values with label `predlbl` to have label `succlbl`.
|
||||
*/
|
||||
deprecated predicate step(
|
||||
DataFlow::Node pred, DataFlow::Node succ, DataFlow::FlowLabel predlbl,
|
||||
DataFlow::FlowLabel succlbl
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `pred` should be stored in the object `succ` under the property `prop`.
|
||||
* The object `succ` must be a `DataFlow::SourceNode` for the object wherein the value is stored.
|
||||
*/
|
||||
predicate storeStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) { none() }
|
||||
|
||||
/**
|
||||
* Holds if the property `prop` of the object `pred` should be loaded into `succ`.
|
||||
*/
|
||||
predicate loadStep(DataFlow::Node pred, DataFlow::Node succ, string prop) { none() }
|
||||
|
||||
/**
|
||||
* Holds if the property `prop` should be copied from the object `pred` to the object `succ`.
|
||||
*/
|
||||
predicate loadStoreStep(DataFlow::Node pred, DataFlow::Node succ, string prop) { none() }
|
||||
|
||||
/**
|
||||
* Holds if the property `loadProp` should be copied from the object `pred` to the property `storeProp` of object `succ`.
|
||||
*/
|
||||
predicate loadStoreStep(
|
||||
DataFlow::Node pred, DataFlow::Node succ, string loadProp, string storeProp
|
||||
) {
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Contains predicates for accessing the steps contributed by `SharedFlowStep`, `LegacyFlowStep`, and `AdditionalFlowStep` subclasses.
|
||||
*/
|
||||
module SharedFlowStep {
|
||||
/**
|
||||
* Holds if `pred` → `succ` should be considered a data flow edge.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate step(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
any(SharedFlowStep s).step(pred, succ)
|
||||
or
|
||||
any(AdditionalFlowStep s).step(pred, succ)
|
||||
or
|
||||
any(LegacyFlowStep s).step(pred, succ)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `pred` should be stored in the object `succ` under the property `prop`.
|
||||
* The object `succ` must be a `DataFlow::SourceNode` for the object wherein the value is stored.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate storeStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) {
|
||||
any(SharedFlowStep s).storeStep(pred, succ, prop)
|
||||
or
|
||||
any(AdditionalFlowStep s)
|
||||
.storeStep(pred, DataFlow::ContentSet::property(prop), succ.getALocalUse())
|
||||
or
|
||||
any(LegacyFlowStep s).storeStep(pred, succ, prop)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the property `prop` of the object `pred` should be loaded into `succ`.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate loadStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
|
||||
any(SharedFlowStep s).loadStep(pred, succ, prop)
|
||||
or
|
||||
any(AdditionalFlowStep s).readStep(pred, DataFlow::ContentSet::property(prop), succ)
|
||||
or
|
||||
any(LegacyFlowStep s).loadStep(pred, succ, prop)
|
||||
}
|
||||
|
||||
// The following are aliases for old step predicates that have no corresponding predicate in AdditionalFlowStep
|
||||
/**
|
||||
* DEPRECATED. The `FlowLabel` class and steps involving flow labels are no longer used by any queries.
|
||||
*
|
||||
* Holds if `pred` → `succ` should be considered a data flow edge
|
||||
* transforming values with label `predlbl` to have label `succlbl`.
|
||||
*/
|
||||
deprecated predicate step(
|
||||
DataFlow::Node pred, DataFlow::Node succ, DataFlow::FlowLabel predlbl,
|
||||
DataFlow::FlowLabel succlbl
|
||||
) {
|
||||
any(SharedFlowStep s).step(pred, succ, predlbl, succlbl)
|
||||
or
|
||||
any(LegacyFlowStep s).step(pred, succ, predlbl, succlbl)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the property `prop` should be copied from the object `pred` to the object `succ`.
|
||||
*/
|
||||
cached
|
||||
predicate loadStoreStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
|
||||
any(SharedFlowStep s).loadStoreStep(pred, succ, prop)
|
||||
or
|
||||
any(LegacyFlowStep s).loadStoreStep(pred, succ, prop)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the property `loadProp` should be copied from the object `pred` to the property `storeProp` of object `succ`.
|
||||
*/
|
||||
cached
|
||||
predicate loadStoreStep(
|
||||
DataFlow::Node pred, DataFlow::Node succ, string loadProp, string storeProp
|
||||
) {
|
||||
any(SharedFlowStep s).loadStoreStep(pred, succ, loadProp, storeProp)
|
||||
or
|
||||
any(LegacyFlowStep s).loadStoreStep(pred, succ, loadProp, storeProp)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,427 @@
|
||||
/**
|
||||
* Note: The contents of this file are exposed with the `TaintTracking::` prefix, via an import in `TaintTracking.qll`.
|
||||
*/
|
||||
|
||||
private import javascript
|
||||
private import semmle.javascript.internal.CachedStages
|
||||
|
||||
/**
|
||||
* A taint-propagating data flow edge that should be added to all taint tracking
|
||||
* configurations, but only those that use the new data flow library.
|
||||
*
|
||||
* This class is a singleton, and thus subclasses do not need to specify a characteristic predicate.
|
||||
*
|
||||
* As an alternative to this class, consider using `DataFlow::SummarizedCallable`.
|
||||
*
|
||||
* Note: For performance reasons, all subclasses of this class should be part
|
||||
* of the standard library. Use `isAdditionalFlowStep` for query-specific taint steps.
|
||||
*/
|
||||
class AdditionalTaintStep extends Unit {
|
||||
/**
|
||||
* Holds if `pred` → `succ` should be considered a taint-propagating
|
||||
* data flow edge.
|
||||
*/
|
||||
predicate step(DataFlow::Node pred, DataFlow::Node succ) { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint-propagating data flow edge that should be added to all taint tracking
|
||||
* configurations in addition to standard data flow edges.
|
||||
*
|
||||
* This class is a singleton, and thus subclasses do not need to specify a characteristic predicate.
|
||||
*
|
||||
* Note: For performance reasons, all subclasses of this class should be part
|
||||
* of the standard library. Override `Configuration::isAdditionalTaintStep`
|
||||
* for analysis-specific taint steps.
|
||||
*
|
||||
* This class has multiple kinds of `step` predicates; these all have the same
|
||||
* effect on taint-tracking configurations. However, the categorization of steps
|
||||
* allows some data-flow configurations to opt in to specific kinds of taint steps.
|
||||
*/
|
||||
class SharedTaintStep extends Unit {
|
||||
// Each step relation in this class should have a cached version in the `Cached` module
|
||||
// and be included in the `sharedTaintStep` predicate.
|
||||
/**
|
||||
* Holds if `pred` → `succ` should be considered a taint-propagating
|
||||
* data flow edge.
|
||||
*/
|
||||
predicate step(DataFlow::Node pred, DataFlow::Node succ) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `pred` → `succ` should be considered a taint-propagating
|
||||
* data flow edge through URI manipulation.
|
||||
*
|
||||
* Does not include string operations that aren't specific to URIs, such
|
||||
* as concatenation and substring operations.
|
||||
*/
|
||||
predicate uriStep(DataFlow::Node pred, DataFlow::Node succ) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `pred` → `succ` should be considered a taint-propagating
|
||||
* data flow edge contributed by the heuristics library.
|
||||
*
|
||||
* Such steps are provided by the `semmle.javascript.heuristics` libraries
|
||||
* and will default to be being empty if those libraries are not imported.
|
||||
*/
|
||||
predicate heuristicStep(DataFlow::Node pred, DataFlow::Node succ) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `pred` → `succ` should be considered a taint-propagating
|
||||
* data flow edge through persistent storage.
|
||||
*/
|
||||
predicate persistentStorageStep(DataFlow::Node pred, DataFlow::Node succ) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `pred` → `succ` should be considered a taint-propagating
|
||||
* data flow edge through the heap.
|
||||
*/
|
||||
predicate heapStep(DataFlow::Node pred, DataFlow::Node succ) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `pred` → `succ` should be considered a taint-propagating
|
||||
* data flow edge through arrays.
|
||||
*
|
||||
* These steps considers an array to be tainted if it contains tainted elements.
|
||||
*/
|
||||
predicate arrayStep(DataFlow::Node pred, DataFlow::Node succ) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `pred` → `succ` should be considered a taint-propagating
|
||||
* data flow edge through the `state` or `props` or a React component.
|
||||
*/
|
||||
predicate viewComponentStep(DataFlow::Node pred, DataFlow::Node succ) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `pred` → `succ` should be considered a taint-propagating
|
||||
* data flow edge through string concatenation.
|
||||
*/
|
||||
predicate stringConcatenationStep(DataFlow::Node pred, DataFlow::Node succ) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `pred` → `succ` should be considered a taint-propagating
|
||||
* data flow edge through string manipulation (other than concatenation).
|
||||
*/
|
||||
predicate stringManipulationStep(DataFlow::Node pred, DataFlow::Node succ) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `pred` → `succ` should be considered a taint-propagating
|
||||
* data flow edge through data serialization, such as `JSON.stringify`.
|
||||
*/
|
||||
predicate serializeStep(DataFlow::Node pred, DataFlow::Node succ) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `pred` → `succ` should be considered a taint-propagating
|
||||
* data flow edge through data deserialization, such as `JSON.parse`.
|
||||
*/
|
||||
predicate deserializeStep(DataFlow::Node pred, DataFlow::Node succ) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `pred` → `succ` should be considered a taint-propagating
|
||||
* data flow edge through a promise.
|
||||
*
|
||||
* These steps consider a promise object to tainted if it can resolve to
|
||||
* a tainted value.
|
||||
*/
|
||||
predicate promiseStep(DataFlow::Node pred, DataFlow::Node succ) { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint-propagating data flow edge that should be used with the old data flow library.
|
||||
*
|
||||
* This class is a singleton, and thus subclasses do not need to specify a characteristic predicate.
|
||||
*
|
||||
* Note: For performance reasons, all subclasses of this class should be part
|
||||
* of the standard library. Override `Configuration::isAdditionalTaintStep`
|
||||
* for analysis-specific taint steps.
|
||||
*
|
||||
* This class has multiple kinds of `step` predicates; these all have the same
|
||||
* effect on taint-tracking configurations. However, the categorization of steps
|
||||
* allows some data-flow configurations to opt in to specific kinds of taint steps.
|
||||
*/
|
||||
class LegacyTaintStep extends Unit {
|
||||
// Each step relation in this class should have a cached version in the `Cached` module
|
||||
// and be included in the `sharedTaintStep` predicate.
|
||||
/**
|
||||
* Holds if `pred` → `succ` should be considered a taint-propagating
|
||||
* data flow edge.
|
||||
*/
|
||||
predicate step(DataFlow::Node pred, DataFlow::Node succ) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `pred` → `succ` should be considered a taint-propagating
|
||||
* data flow edge through URI manipulation.
|
||||
*
|
||||
* Does not include string operations that aren't specific to URIs, such
|
||||
* as concatenation and substring operations.
|
||||
*/
|
||||
predicate uriStep(DataFlow::Node pred, DataFlow::Node succ) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `pred` → `succ` should be considered a taint-propagating
|
||||
* data flow edge contributed by the heuristics library.
|
||||
*
|
||||
* Such steps are provided by the `semmle.javascript.heuristics` libraries
|
||||
* and will default to be being empty if those libraries are not imported.
|
||||
*/
|
||||
predicate heuristicStep(DataFlow::Node pred, DataFlow::Node succ) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `pred` → `succ` should be considered a taint-propagating
|
||||
* data flow edge through persistent storage.
|
||||
*/
|
||||
predicate persistentStorageStep(DataFlow::Node pred, DataFlow::Node succ) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `pred` → `succ` should be considered a taint-propagating
|
||||
* data flow edge through the heap.
|
||||
*/
|
||||
predicate heapStep(DataFlow::Node pred, DataFlow::Node succ) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `pred` → `succ` should be considered a taint-propagating
|
||||
* data flow edge through arrays.
|
||||
*
|
||||
* These steps considers an array to be tainted if it contains tainted elements.
|
||||
*/
|
||||
predicate arrayStep(DataFlow::Node pred, DataFlow::Node succ) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `pred` → `succ` should be considered a taint-propagating
|
||||
* data flow edge through the `state` or `props` or a React component.
|
||||
*/
|
||||
predicate viewComponentStep(DataFlow::Node pred, DataFlow::Node succ) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `pred` → `succ` should be considered a taint-propagating
|
||||
* data flow edge through string concatenation.
|
||||
*/
|
||||
predicate stringConcatenationStep(DataFlow::Node pred, DataFlow::Node succ) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `pred` → `succ` should be considered a taint-propagating
|
||||
* data flow edge through string manipulation (other than concatenation).
|
||||
*/
|
||||
predicate stringManipulationStep(DataFlow::Node pred, DataFlow::Node succ) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `pred` → `succ` should be considered a taint-propagating
|
||||
* data flow edge through data serialization, such as `JSON.stringify`.
|
||||
*/
|
||||
predicate serializeStep(DataFlow::Node pred, DataFlow::Node succ) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `pred` → `succ` should be considered a taint-propagating
|
||||
* data flow edge through data deserialization, such as `JSON.parse`.
|
||||
*/
|
||||
predicate deserializeStep(DataFlow::Node pred, DataFlow::Node succ) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `pred` → `succ` should be considered a taint-propagating
|
||||
* data flow edge through a promise.
|
||||
*
|
||||
* These steps consider a promise object to tainted if it can resolve to
|
||||
* a tainted value.
|
||||
*/
|
||||
predicate promiseStep(DataFlow::Node pred, DataFlow::Node succ) { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Module existing only to ensure all taint steps are cached as a single stage,
|
||||
* and without the the `Unit` type column.
|
||||
*/
|
||||
cached
|
||||
private module Cached {
|
||||
cached
|
||||
predicate forceStage() {
|
||||
// TODO: ensure that this stage is only evaluated if using the old data flow library
|
||||
Stages::Taint::ref()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `pred` → `succ` should be considered a taint-propagating
|
||||
* data flow edge, which doesn't fit into a more specific category.
|
||||
*/
|
||||
cached
|
||||
predicate genericStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
any(SharedTaintStep step).step(pred, succ)
|
||||
or
|
||||
any(LegacyTaintStep step).step(pred, succ)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `pred` → `succ` should be considered a taint-propagating
|
||||
* data flow edge, contribued by the heuristics library.
|
||||
*/
|
||||
cached
|
||||
predicate heuristicStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
any(SharedTaintStep step).heuristicStep(pred, succ)
|
||||
or
|
||||
any(LegacyTaintStep step).heuristicStep(pred, succ)
|
||||
}
|
||||
|
||||
/**
|
||||
* Public taint step relations.
|
||||
*/
|
||||
cached
|
||||
module Public {
|
||||
/**
|
||||
* Holds if `pred` → `succ` should be considered a taint-propagating
|
||||
* data flow edge through a URI library function.
|
||||
*/
|
||||
cached
|
||||
predicate uriStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
any(SharedTaintStep step).uriStep(pred, succ)
|
||||
or
|
||||
any(LegacyTaintStep step).uriStep(pred, succ)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `pred -> succ` is a taint propagating data flow edge through persistent storage.
|
||||
*/
|
||||
cached
|
||||
predicate persistentStorageStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
any(SharedTaintStep step).persistentStorageStep(pred, succ)
|
||||
or
|
||||
any(LegacyTaintStep step).persistentStorageStep(pred, succ)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `pred -> succ` is a taint propagating data flow edge through the heap.
|
||||
*/
|
||||
cached
|
||||
predicate heapStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
any(SharedTaintStep step).heapStep(pred, succ)
|
||||
or
|
||||
any(LegacyTaintStep step).heapStep(pred, succ)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `pred -> succ` is a taint propagating data flow edge through an array.
|
||||
*/
|
||||
cached
|
||||
predicate arrayStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
any(SharedTaintStep step).arrayStep(pred, succ)
|
||||
or
|
||||
any(LegacyTaintStep step).arrayStep(pred, succ)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `pred -> succ` is a taint propagating data flow edge through the
|
||||
* properties of a view compenent, such as the `state` or `props` of a React component.
|
||||
*/
|
||||
cached
|
||||
predicate viewComponentStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
any(SharedTaintStep step).viewComponentStep(pred, succ)
|
||||
or
|
||||
any(LegacyTaintStep step).viewComponentStep(pred, succ)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `pred -> succ` is a taint propagating data flow edge through string
|
||||
* concatenation.
|
||||
*/
|
||||
cached
|
||||
predicate stringConcatenationStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
any(SharedTaintStep step).stringConcatenationStep(pred, succ)
|
||||
or
|
||||
any(LegacyTaintStep step).stringConcatenationStep(pred, succ)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `pred -> succ` is a taint propagating data flow edge through string manipulation
|
||||
* (other than concatenation).
|
||||
*/
|
||||
cached
|
||||
predicate stringManipulationStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
any(SharedTaintStep step).stringManipulationStep(pred, succ)
|
||||
or
|
||||
any(LegacyTaintStep step).stringManipulationStep(pred, succ)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `pred` → `succ` should be considered a taint-propagating
|
||||
* data flow edge through data serialization, such as `JSON.stringify`.
|
||||
*/
|
||||
cached
|
||||
predicate serializeStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
any(SharedTaintStep step).serializeStep(pred, succ)
|
||||
or
|
||||
any(LegacyTaintStep step).serializeStep(pred, succ)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `pred` → `succ` should be considered a taint-propagating
|
||||
* data flow edge through data deserialization, such as `JSON.parse`.
|
||||
*/
|
||||
cached
|
||||
predicate deserializeStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
any(SharedTaintStep step).deserializeStep(pred, succ)
|
||||
or
|
||||
any(LegacyTaintStep step).deserializeStep(pred, succ)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `pred` → `succ` should be considered a taint-propagating
|
||||
* data flow edge through a promise.
|
||||
*
|
||||
* These steps consider a promise object to tainted if it can resolve to
|
||||
* a tainted value.
|
||||
*/
|
||||
cached
|
||||
predicate promiseStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
any(SharedTaintStep step).promiseStep(pred, succ)
|
||||
or
|
||||
any(LegacyTaintStep step).promiseStep(pred, succ)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
import Cached::Public
|
||||
|
||||
/**
|
||||
* Holds if `pred -> succ` is an edge used by all taint-tracking configurations in
|
||||
* the old data flow library.
|
||||
*
|
||||
* The new data flow library uses a different set of steps, exposed by `AdditionalTaintStep::step`.
|
||||
*/
|
||||
predicate sharedTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
Cached::genericStep(pred, succ) or
|
||||
Cached::heuristicStep(pred, succ) or
|
||||
uriStep(pred, succ) or
|
||||
persistentStorageStep(pred, succ) or
|
||||
heapStep(pred, succ) or
|
||||
arrayStep(pred, succ) or
|
||||
viewComponentStep(pred, succ) or
|
||||
stringConcatenationStep(pred, succ) or
|
||||
stringManipulationStep(pred, succ) or
|
||||
serializeStep(pred, succ) or
|
||||
deserializeStep(pred, succ) or
|
||||
promiseStep(pred, succ)
|
||||
}
|
||||
|
||||
/**
|
||||
* Contains predicates for accessing the taint steps used by taint-tracking configurations
|
||||
* in the new data flow library.
|
||||
*/
|
||||
module AdditionalTaintStep {
|
||||
/**
|
||||
* Holds if `pred` → `succ` is considered a taint-propagating data flow edge when
|
||||
* using the new data flow library.
|
||||
*/
|
||||
cached
|
||||
predicate step(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
any(AdditionalTaintStep step).step(pred, succ) or
|
||||
any(SharedTaintStep step).step(pred, succ) or
|
||||
any(SharedTaintStep step).heuristicStep(pred, succ) or
|
||||
any(SharedTaintStep step).uriStep(pred, succ) or
|
||||
any(SharedTaintStep step).persistentStorageStep(pred, succ) or
|
||||
any(SharedTaintStep step).heapStep(pred, succ) or
|
||||
any(SharedTaintStep step).arrayStep(pred, succ) or
|
||||
any(SharedTaintStep step).viewComponentStep(pred, succ) or
|
||||
any(SharedTaintStep step).stringConcatenationStep(pred, succ) or
|
||||
any(SharedTaintStep step).stringManipulationStep(pred, succ) or
|
||||
any(SharedTaintStep step).serializeStep(pred, succ) or
|
||||
any(SharedTaintStep step).deserializeStep(pred, succ) or
|
||||
any(SharedTaintStep step).promiseStep(pred, succ)
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -25,6 +25,8 @@ private import internal.DataFlowNode
|
||||
private import internal.AnalyzedParameters
|
||||
private import internal.PreCallGraphStep
|
||||
private import semmle.javascript.internal.CachedStages
|
||||
private import semmle.javascript.dataflow.internal.DataFlowPrivate as Private
|
||||
private import semmle.javascript.dataflow.internal.VariableOrThis
|
||||
|
||||
module DataFlow {
|
||||
/**
|
||||
@@ -182,29 +184,8 @@ module DataFlow {
|
||||
*/
|
||||
cached
|
||||
DataFlow::Node getImmediatePredecessor() {
|
||||
lvalueFlowStep(result, this) and
|
||||
not lvalueDefaultFlowStep(_, this)
|
||||
or
|
||||
immediateFlowStep(result, this)
|
||||
or
|
||||
// Refinement of variable -> original definition of variable
|
||||
exists(SsaRefinementNode refinement |
|
||||
this = TSsaDefNode(refinement) and
|
||||
result = TSsaDefNode(refinement.getAnInput())
|
||||
)
|
||||
or
|
||||
exists(SsaPhiNode phi |
|
||||
this = TSsaDefNode(phi) and
|
||||
result = TSsaDefNode(phi.getRephinedVariable())
|
||||
)
|
||||
or
|
||||
// IIFE call -> return value of IIFE
|
||||
exists(Function fun |
|
||||
localCall(this.asExpr(), fun) and
|
||||
result = unique(Expr ret | ret = fun.getAReturnedExpr()).flow() and
|
||||
not fun.getExit().isJoin() // can only reach exit by the return statement
|
||||
)
|
||||
or
|
||||
FlowSteps::identityFunctionStep(result, this)
|
||||
}
|
||||
|
||||
@@ -271,6 +252,11 @@ module DataFlow {
|
||||
or
|
||||
this.getFallbackTypeAnnotation().getAnUnderlyingType().hasQualifiedName(moduleName, typeName)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the post-update node corresponding to this node, if any.
|
||||
*/
|
||||
final PostUpdateNode getPostUpdateNode() { result.getPreUpdateNode() = this }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -744,9 +730,7 @@ module DataFlow {
|
||||
private class ParameterFieldAsPropWrite extends PropWrite, PropNode {
|
||||
override ParameterField prop;
|
||||
|
||||
override Node getBase() {
|
||||
thisNode(result, prop.getDeclaringClass().getConstructor().getBody())
|
||||
}
|
||||
override Node getBase() { result = TImplicitThisUse(prop, false) }
|
||||
|
||||
override Expr getPropertyNameExpr() {
|
||||
none() // The parameter value is not the name of the field
|
||||
@@ -754,16 +738,11 @@ module DataFlow {
|
||||
|
||||
override string getPropertyName() { result = prop.getName() }
|
||||
|
||||
override Node getRhs() {
|
||||
exists(Parameter param, Node paramNode |
|
||||
param = prop.getParameter() and
|
||||
parameterNode(paramNode, param)
|
||||
|
|
||||
result = paramNode
|
||||
)
|
||||
}
|
||||
override Node getRhs() { result = TValueNode(prop.getParameter()) }
|
||||
|
||||
override ControlFlowNode getWriteNode() { result = prop.getParameter() }
|
||||
|
||||
override StmtContainer getContainer() { parameter_fields(prop, result, _) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -778,9 +757,7 @@ module DataFlow {
|
||||
exists(prop.getInit())
|
||||
}
|
||||
|
||||
override Node getBase() {
|
||||
thisNode(result, prop.getDeclaringClass().getConstructor().getBody())
|
||||
}
|
||||
override Node getBase() { result = TImplicitThisUse(prop, false) }
|
||||
|
||||
override Expr getPropertyNameExpr() { result = prop.getNameExpr() }
|
||||
|
||||
@@ -971,6 +948,12 @@ module DataFlow {
|
||||
|
||||
override BasicBlock getBasicBlock() { result = function.getExit().getBasicBlock() }
|
||||
|
||||
override StmtContainer getContainer() {
|
||||
// Override this to ensure a container exists even for unreachable returns,
|
||||
// since an unreachable exit CFG node will not have a basic block
|
||||
result = function
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the function corresponding to this exceptional return node.
|
||||
*/
|
||||
@@ -993,6 +976,12 @@ module DataFlow {
|
||||
|
||||
override BasicBlock getBasicBlock() { result = function.getExit().getBasicBlock() }
|
||||
|
||||
override StmtContainer getContainer() {
|
||||
// Override this to ensure a container exists even for unreachable returns,
|
||||
// since an unreachable exit CFG node will not have a basic block
|
||||
result = function
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the function corresponding to this return node.
|
||||
*/
|
||||
@@ -1052,6 +1041,41 @@ module DataFlow {
|
||||
override string toString() { result = "global access path" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A node representing the value passed as `this` argument in a `new` call.
|
||||
*/
|
||||
class NewCallThisArgumentNode extends TNewCallThisArgument, DataFlow::Node {
|
||||
private NewExpr expr;
|
||||
|
||||
NewCallThisArgumentNode() { this = TNewCallThisArgument(expr) }
|
||||
|
||||
override string toString() { result = "implicit 'this' argument of " + expr }
|
||||
|
||||
override StmtContainer getContainer() { result = expr.getContainer() }
|
||||
|
||||
override Location getLocation() { result = expr.getLocation() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A node representing an implicit use of `this` or its post-update node.
|
||||
*/
|
||||
private class ImplicitThisUseNode extends TImplicitThisUse, DataFlow::Node {
|
||||
private ImplicitThisUse use;
|
||||
private boolean isPost;
|
||||
|
||||
ImplicitThisUseNode() { this = TImplicitThisUse(use, isPost) }
|
||||
|
||||
override string toString() {
|
||||
if isPost = false
|
||||
then result = "implicit 'this'"
|
||||
else result = "[post-update] implicit 'this'"
|
||||
}
|
||||
|
||||
override StmtContainer getContainer() { result = use.getUseContainer() }
|
||||
|
||||
override Location getLocation() { result = use.getLocation() }
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL. DO NOT USE.
|
||||
*
|
||||
@@ -1076,6 +1100,14 @@ module DataFlow {
|
||||
* instead.
|
||||
*/
|
||||
module Impl {
|
||||
/**
|
||||
* INTERNAL. DO NOT USE.
|
||||
*
|
||||
* An alias for `Node.getImmediatePredecessor` that can be used at an earlier stage
|
||||
* that does not depend on `DataFlow::Node`.
|
||||
*/
|
||||
predicate earlyStageImmediateFlowStep = immediateFlowStep/2;
|
||||
|
||||
/**
|
||||
* A data flow node representing a function invocation, either explicitly or reflectively,
|
||||
* and either with or without `new`.
|
||||
@@ -1342,6 +1374,61 @@ module DataFlow {
|
||||
override Location getLocation() { result = this.getTag().getLocation() }
|
||||
|
||||
override string toString() { result = this.getTag().toString() }
|
||||
|
||||
override StmtContainer getContainer() { result = this.getTag().getInnerTopLevel() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A node representing the hidden parameter of a function by which a function can refer to itself.
|
||||
*/
|
||||
class FunctionSelfReferenceNode extends DataFlow::Node, TFunctionSelfReferenceNode {
|
||||
private Function function;
|
||||
|
||||
FunctionSelfReferenceNode() { this = TFunctionSelfReferenceNode(function) }
|
||||
|
||||
/** Gets the function. */
|
||||
Function getFunction() { result = function }
|
||||
|
||||
override StmtContainer getContainer() { result = function }
|
||||
|
||||
override BasicBlock getBasicBlock() { result = function.getEntryBB() }
|
||||
|
||||
override string toString() { result = "[function self-reference] " + function.toString() }
|
||||
|
||||
override Location getLocation() { result = function.getLocation() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A post-update node whose pre-node corresponds to an expression. See `DataFlow::PostUpdateNode` for more details.
|
||||
*/
|
||||
class ExprPostUpdateNode extends DataFlow::Node, TExprPostUpdateNode, Private::PostUpdateNode {
|
||||
private AST::ValueNode expr;
|
||||
|
||||
ExprPostUpdateNode() { this = TExprPostUpdateNode(expr) }
|
||||
|
||||
/** Gets the expression for which this is the post-update node. */
|
||||
AST::ValueNode getExpr() { result = expr }
|
||||
|
||||
override StmtContainer getContainer() { result = expr.getContainer() }
|
||||
|
||||
override Location getLocation() { result = expr.getLocation() }
|
||||
|
||||
override string toString() { result = "[post update] " + expr.toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A post-update node.
|
||||
*
|
||||
* This is a data-flow node that represents the new state of an object after its contents have been mutated.
|
||||
* Most notably such nodes exist for arguments to a call and for the base of a property reference.
|
||||
*/
|
||||
class PostUpdateNode extends DataFlow::Node {
|
||||
PostUpdateNode() { Private::postUpdatePair(_, this) }
|
||||
|
||||
/**
|
||||
* Gets the corresponding pre-update node, which is usually the argument to a call or the base of a property reference.
|
||||
*/
|
||||
final DataFlow::Node getPreUpdateNode() { Private::postUpdatePair(result, this) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1374,12 +1461,12 @@ module DataFlow {
|
||||
/**
|
||||
* INTERNAL: Use `parameterNode(Parameter)` instead.
|
||||
*/
|
||||
predicate parameterNode(DataFlow::Node nd, Parameter p) { nd = valueNode(p) }
|
||||
predicate parameterNode(EarlyStageNode nd, Parameter p) { nd = TValueNode(p) }
|
||||
|
||||
/**
|
||||
* INTERNAL: Use `thisNode(StmtContainer container)` instead.
|
||||
*/
|
||||
predicate thisNode(DataFlow::Node node, StmtContainer container) { node = TThisNode(container) }
|
||||
predicate thisNode(EarlyStageNode node, StmtContainer container) { node = TThisNode(container) }
|
||||
|
||||
/**
|
||||
* Gets the node representing the receiver of the given function, or `this` in the given top-level.
|
||||
@@ -1441,7 +1528,15 @@ module DataFlow {
|
||||
* _before_ the l-value is assigned to, whereas `DataFlow::lvalueNode()`
|
||||
* represents the value _after_ the assignment.
|
||||
*/
|
||||
Node lvalueNode(BindingPattern lvalue) {
|
||||
Node lvalueNode(BindingPattern lvalue) { result = lvalueNodeInternal(lvalue) }
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use outside standard library.
|
||||
*
|
||||
* Same as `lvalueNode()` except the return type is `EarlyStageNode`, which allows it to be used
|
||||
* before all data flow nodes have been materialised.
|
||||
*/
|
||||
EarlyStageNode lvalueNodeInternal(BindingPattern lvalue) {
|
||||
exists(SsaExplicitDefinition ssa |
|
||||
ssa.defines(lvalue.(LValue).getDefNode(), lvalue.(VarRef).getVariable()) and
|
||||
result = TSsaDefNode(ssa)
|
||||
@@ -1489,31 +1584,31 @@ module DataFlow {
|
||||
* Holds if there is a step from `pred -> succ` due to an assignment
|
||||
* to an expression in l-value position.
|
||||
*/
|
||||
private predicate lvalueFlowStep(Node pred, Node succ) {
|
||||
private predicate lvalueFlowStep(EarlyStageNode pred, EarlyStageNode succ) {
|
||||
exists(VarDef def |
|
||||
pred = valueNode(defSourceNode(def)) and
|
||||
succ = lvalueNode(def.getTarget())
|
||||
pred = TValueNode(defSourceNode(def)) and
|
||||
succ = lvalueNodeInternal(def.getTarget())
|
||||
)
|
||||
or
|
||||
exists(SimpleParameter param |
|
||||
pred = valueNode(param) and // The value node represents the incoming argument
|
||||
succ = lvalueNode(param) // The SSA node represents the parameters's local variable
|
||||
pred = TValueNode(param) and // The value node represents the incoming argument
|
||||
succ = lvalueNodeInternal(param) // The SSA node represents the parameters's local variable
|
||||
)
|
||||
or
|
||||
exists(Expr arg, Parameter param |
|
||||
localArgumentPassing(arg, param) and
|
||||
pred = valueNode(arg) and
|
||||
succ = valueNode(param)
|
||||
pred = TValueNode(arg) and
|
||||
succ = TValueNode(param)
|
||||
)
|
||||
or
|
||||
exists(PropertyPattern pattern |
|
||||
pred = TPropNode(pattern) and
|
||||
succ = lvalueNode(pattern.getValuePattern())
|
||||
succ = lvalueNodeInternal(pattern.getValuePattern())
|
||||
)
|
||||
or
|
||||
exists(Expr element |
|
||||
pred = TElementPatternNode(_, element) and
|
||||
succ = lvalueNode(element)
|
||||
succ = lvalueNodeInternal(element)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1521,37 +1616,37 @@ module DataFlow {
|
||||
* Holds if there is a step from `pred -> succ` from the default
|
||||
* value of a destructuring pattern or parameter.
|
||||
*/
|
||||
private predicate lvalueDefaultFlowStep(Node pred, Node succ) {
|
||||
private predicate lvalueDefaultFlowStep(EarlyStageNode pred, EarlyStageNode succ) {
|
||||
exists(PropertyPattern pattern |
|
||||
pred = TValueNode(pattern.getDefault()) and
|
||||
succ = lvalueNode(pattern.getValuePattern())
|
||||
succ = lvalueNodeInternal(pattern.getValuePattern())
|
||||
)
|
||||
or
|
||||
exists(ArrayPattern array, int i |
|
||||
pred = TValueNode(array.getDefault(i)) and
|
||||
succ = lvalueNode(array.getElement(i))
|
||||
succ = lvalueNodeInternal(array.getElement(i))
|
||||
)
|
||||
or
|
||||
exists(Parameter param |
|
||||
pred = TValueNode(param.getDefault()) and
|
||||
parameterNode(succ, param)
|
||||
succ = TValueNode(param)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Flow steps shared between `getImmediatePredecessor` and `localFlowStep`.
|
||||
* Flow steps shared between `immediateFlowStep` and `localFlowStep`.
|
||||
*
|
||||
* Inlining is forced because the two relations are indexed differently.
|
||||
*/
|
||||
pragma[inline]
|
||||
private predicate immediateFlowStep(Node pred, Node succ) {
|
||||
private predicate immediateFlowStepShared(EarlyStageNode pred, EarlyStageNode succ) {
|
||||
exists(SsaVariable v |
|
||||
pred = TSsaDefNode(v.getDefinition()) and
|
||||
succ = valueNode(v.getAUse())
|
||||
succ = TValueNode(v.getAUse())
|
||||
)
|
||||
or
|
||||
exists(Expr predExpr, Expr succExpr |
|
||||
pred = valueNode(predExpr) and succ = valueNode(succExpr)
|
||||
pred = TValueNode(predExpr) and succ = TValueNode(succExpr)
|
||||
|
|
||||
predExpr = succExpr.(ParExpr).getExpression()
|
||||
or
|
||||
@@ -1581,25 +1676,61 @@ module DataFlow {
|
||||
// flow from 'this' parameter into 'this' expressions
|
||||
exists(ThisExpr thiz |
|
||||
pred = TThisNode(thiz.getBindingContainer()) and
|
||||
succ = valueNode(thiz)
|
||||
succ = TValueNode(thiz)
|
||||
)
|
||||
or
|
||||
// `f.call(...)` and `f.apply(...)` evaluate to the result of the reflective call they perform
|
||||
pred = TReflectiveCallNode(succ.asExpr(), _)
|
||||
exists(MethodCallExpr call |
|
||||
pred = TReflectiveCallNode(call, _) and
|
||||
succ = TValueNode(call)
|
||||
)
|
||||
or
|
||||
// Pass 'this' into implicit uses of 'this'
|
||||
exists(ImplicitThisUse use |
|
||||
pred = TThisNode(use.getBindingContainer()) and
|
||||
succ = TImplicitThisUse(use, false)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate immediateFlowStep(EarlyStageNode pred, EarlyStageNode succ) {
|
||||
lvalueFlowStep(pred, succ) and
|
||||
not lvalueDefaultFlowStep(_, succ)
|
||||
or
|
||||
immediateFlowStepShared(pred, succ)
|
||||
or
|
||||
// Refinement of variable -> original definition of variable
|
||||
exists(SsaRefinementNode refinement |
|
||||
succ = TSsaDefNode(refinement) and
|
||||
pred = TSsaDefNode(refinement.getAnInput())
|
||||
)
|
||||
or
|
||||
exists(SsaPhiNode phi |
|
||||
succ = TSsaDefNode(phi) and
|
||||
pred = TSsaDefNode(phi.getRephinedVariable())
|
||||
)
|
||||
or
|
||||
// IIFE call -> return value of IIFE
|
||||
exists(Function fun, Expr expr |
|
||||
succ = TValueNode(expr) and
|
||||
localCall(expr, fun) and
|
||||
pred = TValueNode(unique(Expr ret | ret = fun.getAReturnedExpr())) and
|
||||
not fun.getExit().isJoin() // can only reach exit by the return statement
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `pred` to `succ` in one local step.
|
||||
*/
|
||||
cached
|
||||
predicate localFlowStep(Node pred, Node succ) {
|
||||
Stages::DataFlowStage::ref() and
|
||||
predicate localFlowStep(EarlyStageNode pred, EarlyStageNode succ) {
|
||||
Stages::EarlyDataFlowStage::ref() and
|
||||
// flow from RHS into LHS
|
||||
lvalueFlowStep(pred, succ)
|
||||
or
|
||||
lvalueDefaultFlowStep(pred, succ)
|
||||
or
|
||||
immediateFlowStep(pred, succ)
|
||||
immediateFlowStepShared(pred, succ)
|
||||
or
|
||||
// From an assignment or implicit initialization of a captured variable to its flow-insensitive node.
|
||||
exists(SsaDefinition predDef |
|
||||
@@ -1623,7 +1754,7 @@ module DataFlow {
|
||||
)
|
||||
or
|
||||
exists(Expr predExpr, Expr succExpr |
|
||||
pred = valueNode(predExpr) and succ = valueNode(succExpr)
|
||||
pred = TValueNode(predExpr) and succ = TValueNode(succExpr)
|
||||
|
|
||||
predExpr = succExpr.(LogicalOrExpr).getAnOperand()
|
||||
or
|
||||
@@ -1641,18 +1772,17 @@ module DataFlow {
|
||||
or
|
||||
// from returned expr to the FunctionReturnNode.
|
||||
exists(Function f | not f.isAsyncOrGenerator() |
|
||||
DataFlow::functionReturnNode(succ, f) and pred = valueNode(f.getAReturnedExpr())
|
||||
succ = TFunctionReturnNode(f) and pred = TValueNode(f.getAReturnedExpr())
|
||||
)
|
||||
or
|
||||
// from a reflective params node to a reference to the arguments object.
|
||||
exists(DataFlow::ReflectiveParametersNode params, Function f | f = params.getFunction() |
|
||||
succ = f.getArgumentsVariable().getAnAccess().flow() and
|
||||
pred = params
|
||||
exists(Function f |
|
||||
pred = TReflectiveParametersNode(f) and
|
||||
succ = TValueNode(f.getArgumentsVariable().getAnAccess())
|
||||
)
|
||||
}
|
||||
|
||||
/** A load step from a reflective parameter node to each parameter. */
|
||||
private class ReflectiveParamsStep extends PreCallGraphStep {
|
||||
private class ReflectiveParamsStep extends LegacyPreCallGraphStep {
|
||||
override predicate loadStep(DataFlow::Node obj, DataFlow::Node element, string prop) {
|
||||
exists(DataFlow::ReflectiveParametersNode params, DataFlow::FunctionNode f, int i |
|
||||
f.getFunction() = params.getFunction() and
|
||||
@@ -1664,7 +1794,7 @@ module DataFlow {
|
||||
}
|
||||
|
||||
/** A taint step from the reflective parameters node to any parameter. */
|
||||
private class ReflectiveParamsTaintStep extends TaintTracking::SharedTaintStep {
|
||||
private class ReflectiveParamsTaintStep extends TaintTracking::LegacyTaintStep {
|
||||
override predicate step(DataFlow::Node obj, DataFlow::Node element) {
|
||||
exists(DataFlow::ReflectiveParametersNode params, DataFlow::FunctionNode f |
|
||||
f.getFunction() = params.getFunction() and
|
||||
@@ -1801,5 +1931,9 @@ module DataFlow {
|
||||
import TypeInference
|
||||
import Configuration
|
||||
import TypeTracking
|
||||
import AdditionalFlowSteps
|
||||
import internal.FunctionWrapperSteps
|
||||
import internal.sharedlib.DataFlow
|
||||
import internal.BarrierGuards
|
||||
import FlowSummary
|
||||
}
|
||||
|
||||
83
javascript/ql/lib/semmle/javascript/dataflow/FlowSummary.qll
Normal file
83
javascript/ql/lib/semmle/javascript/dataflow/FlowSummary.qll
Normal file
@@ -0,0 +1,83 @@
|
||||
/** Provides classes and predicates for defining flow summaries. */
|
||||
|
||||
private import javascript
|
||||
private import semmle.javascript.dataflow.internal.sharedlib.FlowSummaryImpl as Impl
|
||||
private import semmle.javascript.dataflow.internal.FlowSummaryPrivate
|
||||
private import semmle.javascript.dataflow.internal.sharedlib.DataFlowImplCommon as DataFlowImplCommon
|
||||
private import semmle.javascript.dataflow.internal.DataFlowPrivate
|
||||
|
||||
/**
|
||||
* A model for a function that can propagate data flow.
|
||||
*
|
||||
* This class makes it possible to model flow through functions, using the same mechanism as
|
||||
* `summaryModel` as described in the [library customization docs](https://codeql.github.com/docs/codeql-language-guides/customizing-library-models-for-javascript).
|
||||
*
|
||||
* Extend this class to define summary models directly in CodeQL.
|
||||
* Data extensions and `summaryModel` are usually preferred; but there are a few cases where direct use of this class may be needed:
|
||||
*
|
||||
* - The relevant call sites cannot be matched by the access path syntax, and require the full power of CodeQL.
|
||||
* For example, complex overloading patterns might require more local reasoning at the call site.
|
||||
* - The input/output behaviour cannot be described statically in the access path syntax, but the relevant access paths
|
||||
* can be generated dynamically in CodeQL, based on the usages found in the codebase.
|
||||
*
|
||||
* Subclasses should bind `this` to a unique identifier for the function being modelled. There is no special
|
||||
* interpreation of the `this` value, it should just not clash with the `this`-value used by other classes.
|
||||
*
|
||||
* For example, this models flow through calls such as `require("my-library").myFunction()`:
|
||||
* ```codeql
|
||||
* class MyFunction extends SummarizedCallable {
|
||||
* MyFunction() { this = "MyFunction" }
|
||||
*
|
||||
* override predicate propagatesFlow(string input, string output, boolean preservesValues) {
|
||||
* input = "Argument[0]" and
|
||||
* output = "ReturnValue" and
|
||||
* preservesValue = false
|
||||
* }
|
||||
*
|
||||
* override DataFlow::InvokeNode getACall() {
|
||||
* result = API::moduleImport("my-library").getMember("myFunction").getACall()
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
* This would be equivalent to the following model written as a data extension:
|
||||
* ```yaml
|
||||
* extensions:
|
||||
* - addsTo:
|
||||
* pack: codeql/javascript-all
|
||||
* extensible: summaryModel
|
||||
* data:
|
||||
* - ["my-library", "Member[myFunction]", "Argument[0]", "ReturnValue", "taint"]
|
||||
* ```
|
||||
*/
|
||||
abstract class SummarizedCallable extends LibraryCallable, Impl::Public::SummarizedCallable {
|
||||
bindingset[this]
|
||||
SummarizedCallable() { any() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `input` to `output` through this callable.
|
||||
*
|
||||
* `preservesValue` indicates whether this is a value-preserving step or a taint-step.
|
||||
*
|
||||
* See the [library customization docs](https://codeql.github.com/docs/codeql-language-guides/customizing-library-models-for-javascript) for
|
||||
* the syntax of the `input` and `output` parameters.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate propagatesFlow(string input, string output, boolean preservesValue) { none() }
|
||||
|
||||
override predicate propagatesFlow(
|
||||
string input, string output, boolean preservesValue, string model
|
||||
) {
|
||||
this.propagatesFlow(input, output, preservesValue) and model = this
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the synthesized parameter that results from an input specification
|
||||
* that starts with `Argument[s]` for this library callable.
|
||||
*/
|
||||
DataFlow::ParameterNode getParameter(string s) {
|
||||
exists(ParameterPosition pos |
|
||||
DataFlowImplCommon::parameterNode(result, MkLibraryCallable(this), pos) and
|
||||
s = encodeParameterPosition(pos)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1611,7 +1611,12 @@ class RegExpConstructorInvokeNode extends DataFlow::InvokeNode {
|
||||
* Gets the AST of the regular expression created here, provided that the
|
||||
* first argument is a string literal.
|
||||
*/
|
||||
RegExpTerm getRoot() { result = this.getArgument(0).asExpr().(StringLiteral).asRegExp() }
|
||||
RegExpTerm getRoot() {
|
||||
result = this.getArgument(0).asExpr().(StringLiteral).asRegExp()
|
||||
or
|
||||
// In case someone writes `new RegExp(/foo/)` for some reason
|
||||
result = this.getArgument(0).asExpr().(RegExpLiteral).getRoot()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the flags provided in the second argument, or an empty string if no
|
||||
|
||||
@@ -18,11 +18,14 @@ private import semmle.javascript.dataflow.internal.FlowSteps as FlowSteps
|
||||
private import semmle.javascript.Unit
|
||||
private import semmle.javascript.dataflow.InferredTypes
|
||||
private import semmle.javascript.internal.CachedStages
|
||||
private import semmle.javascript.dataflow.internal.TaintTrackingPrivate as TaintTrackingPrivate
|
||||
|
||||
/**
|
||||
* Provides classes for modeling taint propagation.
|
||||
*/
|
||||
module TaintTracking {
|
||||
import AdditionalTaintSteps
|
||||
|
||||
/**
|
||||
* A data flow tracking configuration that considers taint propagation through
|
||||
* objects, arrays, promises and strings in addition to standard data flow.
|
||||
@@ -30,7 +33,7 @@ module TaintTracking {
|
||||
* If a different set of flow edges is desired, extend this class and override
|
||||
* `isAdditionalTaintStep`.
|
||||
*/
|
||||
abstract class Configuration extends DataFlow::Configuration {
|
||||
abstract deprecated class Configuration extends DataFlow::Configuration {
|
||||
bindingset[this]
|
||||
Configuration() { any() }
|
||||
|
||||
@@ -171,20 +174,88 @@ module TaintTracking {
|
||||
}
|
||||
|
||||
/**
|
||||
* A `SanitizerGuardNode` that controls which taint tracking
|
||||
* configurations it is used in.
|
||||
* A barrier guard that applies to all taint-tracking configurations.
|
||||
*
|
||||
* Note: For performance reasons, all subclasses of this class should be part
|
||||
* of the standard library. Override `Configuration::isSanitizerGuard`
|
||||
* for analysis-specific taint sanitizer guards.
|
||||
* of the standard library. To define a query-specific barrier guard, instead override
|
||||
* `isBarrier` and use the `DataFlow::MakeBarrierGuard` module. For example:
|
||||
* ```codeql
|
||||
* module MyConfig implements DataFlow::ConfigSig {
|
||||
* predicate isBarrier(DataFlow::Node node) {
|
||||
* node = DataFlow::MakeBarrierGuard<MyGuard>
|
||||
* }
|
||||
* }
|
||||
* class MyGuard extends DataFlow::Node {
|
||||
* MyGuard() { ... }
|
||||
* predicate blocksExpr(boolean outcome, Expr e) { ... }
|
||||
* }
|
||||
*/
|
||||
abstract class AdditionalBarrierGuard extends DataFlow::Node {
|
||||
/**
|
||||
* Holds if this node blocks expression `e`, provided it evaluates to `outcome`.
|
||||
*/
|
||||
abstract predicate blocksExpr(boolean outcome, Expr e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal barrier guard class that populates both the new `AdditionalBarrierGuard` class
|
||||
* and the legacy `AdditionalSanitizerGuardNode` class.
|
||||
*
|
||||
* It exposes the member predicates of `AdditionalSanitizerGuardNode` for backwards compatibility.
|
||||
*/
|
||||
abstract private class LegacyAdditionalBarrierGuard extends AdditionalBarrierGuard,
|
||||
AdditionalSanitizerGuardNodeDeprecated
|
||||
{
|
||||
deprecated override predicate sanitizes(boolean outcome, Expr e) { this.blocksExpr(outcome, e) }
|
||||
|
||||
deprecated override predicate appliesTo(Configuration cfg) { any() }
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED. This class was part of the old data flow library which is now deprecated.
|
||||
* Use `TaintTracking::AdditionalBarrierGuard` instead.
|
||||
*/
|
||||
deprecated class AdditionalSanitizerGuardNode = AdditionalSanitizerGuardNodeDeprecated;
|
||||
|
||||
cached
|
||||
abstract class AdditionalSanitizerGuardNode extends SanitizerGuardNode {
|
||||
abstract private class AdditionalSanitizerGuardNodeDeprecated extends DataFlow::Node {
|
||||
// For backwards compatibility, this contains a copy of the SanitizerGuard interface,
|
||||
// but is does not inherit from it as that would cause re-evaluation of cached barriers.
|
||||
/**
|
||||
* Holds if this node blocks expression `e`, provided it evaluates to `outcome`.
|
||||
*/
|
||||
cached
|
||||
deprecated predicate blocks(boolean outcome, Expr e) { none() }
|
||||
|
||||
/**
|
||||
* Holds if this node sanitizes expression `e`, provided it evaluates
|
||||
* to `outcome`.
|
||||
*/
|
||||
cached
|
||||
abstract deprecated predicate sanitizes(boolean outcome, Expr e);
|
||||
|
||||
/**
|
||||
* Holds if this node blocks expression `e` from flow of type `label`, provided it evaluates to `outcome`.
|
||||
*/
|
||||
cached
|
||||
deprecated predicate blocks(boolean outcome, Expr e, DataFlow::FlowLabel label) {
|
||||
this.sanitizes(outcome, e) and label.isTaint()
|
||||
or
|
||||
this.sanitizes(outcome, e, label)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this node sanitizes expression `e`, provided it evaluates
|
||||
* to `outcome`.
|
||||
*/
|
||||
cached
|
||||
deprecated predicate sanitizes(boolean outcome, Expr e, DataFlow::FlowLabel label) { none() }
|
||||
|
||||
/**
|
||||
* Holds if this guard applies to the flow in `cfg`.
|
||||
*/
|
||||
cached
|
||||
abstract predicate appliesTo(Configuration cfg);
|
||||
abstract deprecated predicate appliesTo(Configuration cfg);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -199,7 +270,7 @@ module TaintTracking {
|
||||
* implementations of `sanitizes` will _both_ apply to any configuration that includes either of
|
||||
* them.
|
||||
*/
|
||||
abstract class SanitizerGuardNode extends DataFlow::BarrierGuardNode {
|
||||
abstract deprecated class SanitizerGuardNode extends DataFlow::BarrierGuardNode {
|
||||
override predicate blocks(boolean outcome, Expr e) { none() }
|
||||
|
||||
/**
|
||||
@@ -224,255 +295,12 @@ module TaintTracking {
|
||||
/**
|
||||
* A sanitizer guard node that only blocks specific flow labels.
|
||||
*/
|
||||
abstract class LabeledSanitizerGuardNode extends SanitizerGuardNode, DataFlow::BarrierGuardNode {
|
||||
abstract deprecated class LabeledSanitizerGuardNode extends SanitizerGuardNode,
|
||||
DataFlow::BarrierGuardNode
|
||||
{
|
||||
override predicate sanitizes(boolean outcome, Expr e) { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint-propagating data flow edge that should be added to all taint tracking
|
||||
* configurations in addition to standard data flow edges.
|
||||
*
|
||||
* This class is a singleton, and thus subclasses do not need to specify a characteristic predicate.
|
||||
*
|
||||
* Note: For performance reasons, all subclasses of this class should be part
|
||||
* of the standard library. Override `Configuration::isAdditionalTaintStep`
|
||||
* for analysis-specific taint steps.
|
||||
*
|
||||
* This class has multiple kinds of `step` predicates; these all have the same
|
||||
* effect on taint-tracking configurations. However, the categorization of steps
|
||||
* allows some data-flow configurations to opt in to specific kinds of taint steps.
|
||||
*/
|
||||
class SharedTaintStep extends Unit {
|
||||
// Each step relation in this class should have a cached version in the `Cached` module
|
||||
// and be included in the `sharedTaintStep` predicate.
|
||||
/**
|
||||
* Holds if `pred` → `succ` should be considered a taint-propagating
|
||||
* data flow edge.
|
||||
*/
|
||||
predicate step(DataFlow::Node pred, DataFlow::Node succ) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `pred` → `succ` should be considered a taint-propagating
|
||||
* data flow edge through URI manipulation.
|
||||
*
|
||||
* Does not include string operations that aren't specific to URIs, such
|
||||
* as concatenation and substring operations.
|
||||
*/
|
||||
predicate uriStep(DataFlow::Node pred, DataFlow::Node succ) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `pred` → `succ` should be considered a taint-propagating
|
||||
* data flow edge contributed by the heuristics library.
|
||||
*
|
||||
* Such steps are provided by the `semmle.javascript.heuristics` libraries
|
||||
* and will default to be being empty if those libraries are not imported.
|
||||
*/
|
||||
predicate heuristicStep(DataFlow::Node pred, DataFlow::Node succ) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `pred` → `succ` should be considered a taint-propagating
|
||||
* data flow edge through persistent storage.
|
||||
*/
|
||||
predicate persistentStorageStep(DataFlow::Node pred, DataFlow::Node succ) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `pred` → `succ` should be considered a taint-propagating
|
||||
* data flow edge through the heap.
|
||||
*/
|
||||
predicate heapStep(DataFlow::Node pred, DataFlow::Node succ) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `pred` → `succ` should be considered a taint-propagating
|
||||
* data flow edge through arrays.
|
||||
*
|
||||
* These steps considers an array to be tainted if it contains tainted elements.
|
||||
*/
|
||||
predicate arrayStep(DataFlow::Node pred, DataFlow::Node succ) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `pred` → `succ` should be considered a taint-propagating
|
||||
* data flow edge through the `state` or `props` or a React component.
|
||||
*/
|
||||
predicate viewComponentStep(DataFlow::Node pred, DataFlow::Node succ) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `pred` → `succ` should be considered a taint-propagating
|
||||
* data flow edge through string concatenation.
|
||||
*/
|
||||
predicate stringConcatenationStep(DataFlow::Node pred, DataFlow::Node succ) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `pred` → `succ` should be considered a taint-propagating
|
||||
* data flow edge through string manipulation (other than concatenation).
|
||||
*/
|
||||
predicate stringManipulationStep(DataFlow::Node pred, DataFlow::Node succ) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `pred` → `succ` should be considered a taint-propagating
|
||||
* data flow edge through data serialization, such as `JSON.stringify`.
|
||||
*/
|
||||
predicate serializeStep(DataFlow::Node pred, DataFlow::Node succ) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `pred` → `succ` should be considered a taint-propagating
|
||||
* data flow edge through data deserialization, such as `JSON.parse`.
|
||||
*/
|
||||
predicate deserializeStep(DataFlow::Node pred, DataFlow::Node succ) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `pred` → `succ` should be considered a taint-propagating
|
||||
* data flow edge through a promise.
|
||||
*
|
||||
* These steps consider a promise object to tainted if it can resolve to
|
||||
* a tainted value.
|
||||
*/
|
||||
predicate promiseStep(DataFlow::Node pred, DataFlow::Node succ) { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Module existing only to ensure all taint steps are cached as a single stage,
|
||||
* and without the the `Unit` type column.
|
||||
*/
|
||||
cached
|
||||
private module Cached {
|
||||
cached
|
||||
predicate forceStage() { Stages::Taint::ref() }
|
||||
|
||||
/**
|
||||
* Holds if `pred` → `succ` should be considered a taint-propagating
|
||||
* data flow edge, which doesn't fit into a more specific category.
|
||||
*/
|
||||
cached
|
||||
predicate genericStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
any(SharedTaintStep step).step(pred, succ)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `pred` → `succ` should be considered a taint-propagating
|
||||
* data flow edge, contribued by the heuristics library.
|
||||
*/
|
||||
cached
|
||||
predicate heuristicStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
any(SharedTaintStep step).heuristicStep(pred, succ)
|
||||
}
|
||||
|
||||
/**
|
||||
* Public taint step relations.
|
||||
*/
|
||||
cached
|
||||
module Public {
|
||||
/**
|
||||
* Holds if `pred` → `succ` should be considered a taint-propagating
|
||||
* data flow edge through a URI library function.
|
||||
*/
|
||||
cached
|
||||
predicate uriStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
any(SharedTaintStep step).uriStep(pred, succ)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `pred -> succ` is a taint propagating data flow edge through persistent storage.
|
||||
*/
|
||||
cached
|
||||
predicate persistentStorageStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
any(SharedTaintStep step).persistentStorageStep(pred, succ)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `pred -> succ` is a taint propagating data flow edge through the heap.
|
||||
*/
|
||||
cached
|
||||
predicate heapStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
any(SharedTaintStep step).heapStep(pred, succ)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `pred -> succ` is a taint propagating data flow edge through an array.
|
||||
*/
|
||||
cached
|
||||
predicate arrayStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
any(SharedTaintStep step).arrayStep(pred, succ)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `pred -> succ` is a taint propagating data flow edge through the
|
||||
* properties of a view compenent, such as the `state` or `props` of a React component.
|
||||
*/
|
||||
cached
|
||||
predicate viewComponentStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
any(SharedTaintStep step).viewComponentStep(pred, succ)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `pred -> succ` is a taint propagating data flow edge through string
|
||||
* concatenation.
|
||||
*/
|
||||
cached
|
||||
predicate stringConcatenationStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
any(SharedTaintStep step).stringConcatenationStep(pred, succ)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `pred -> succ` is a taint propagating data flow edge through string manipulation
|
||||
* (other than concatenation).
|
||||
*/
|
||||
cached
|
||||
predicate stringManipulationStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
any(SharedTaintStep step).stringManipulationStep(pred, succ)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `pred` → `succ` should be considered a taint-propagating
|
||||
* data flow edge through data serialization, such as `JSON.stringify`.
|
||||
*/
|
||||
cached
|
||||
predicate serializeStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
any(SharedTaintStep step).serializeStep(pred, succ)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `pred` → `succ` should be considered a taint-propagating
|
||||
* data flow edge through data deserialization, such as `JSON.parse`.
|
||||
*/
|
||||
cached
|
||||
predicate deserializeStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
any(SharedTaintStep step).deserializeStep(pred, succ)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `pred` → `succ` should be considered a taint-propagating
|
||||
* data flow edge through a promise.
|
||||
*
|
||||
* These steps consider a promise object to tainted if it can resolve to
|
||||
* a tainted value.
|
||||
*/
|
||||
cached
|
||||
predicate promiseStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
any(SharedTaintStep step).promiseStep(pred, succ)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
import Cached::Public
|
||||
|
||||
/**
|
||||
* Holds if `pred -> succ` is an edge used by all taint-tracking configurations.
|
||||
*/
|
||||
predicate sharedTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
Cached::genericStep(pred, succ) or
|
||||
Cached::heuristicStep(pred, succ) or
|
||||
uriStep(pred, succ) or
|
||||
persistentStorageStep(pred, succ) or
|
||||
heapStep(pred, succ) or
|
||||
arrayStep(pred, succ) or
|
||||
viewComponentStep(pred, succ) or
|
||||
stringConcatenationStep(pred, succ) or
|
||||
stringManipulationStep(pred, succ) or
|
||||
serializeStep(pred, succ) or
|
||||
deserializeStep(pred, succ) or
|
||||
promiseStep(pred, succ)
|
||||
}
|
||||
|
||||
/** Gets a data flow node referring to the client side URL. */
|
||||
private DataFlow::SourceNode clientSideUrlRef(DataFlow::TypeTracker t) {
|
||||
t.start() and
|
||||
@@ -497,11 +325,19 @@ module TaintTracking {
|
||||
exists(StringSplitCall c |
|
||||
c.getBaseString().getALocalSource() =
|
||||
[DOM::locationRef(), DOM::locationRef().getAPropertyRead("href")] and
|
||||
c.getSeparator() = "?" and
|
||||
c.getSeparator() = ["?", "#"] and
|
||||
read = c.getAPropertyRead("0")
|
||||
)
|
||||
}
|
||||
|
||||
private class HeapLegacyTaintStep extends LegacyTaintStep {
|
||||
override predicate heapStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
// arrays with tainted elements are tainted (in old data flow)
|
||||
succ.(DataFlow::ArrayCreationNode).getAnElement() = pred and
|
||||
not any(PromiseAllCreation call).getArrayNode() = succ
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint propagating data flow edge through object or array elements and
|
||||
* promises.
|
||||
@@ -516,10 +352,6 @@ module TaintTracking {
|
||||
// spreading a tainted value into an array literal gives a tainted array
|
||||
succ.(DataFlow::ArrayCreationNode).getASpreadArgument() = pred
|
||||
or
|
||||
// arrays with tainted elements and objects with tainted property names are tainted
|
||||
succ.(DataFlow::ArrayCreationNode).getAnElement() = pred and
|
||||
not any(PromiseAllCreation call).getArrayNode() = succ
|
||||
or
|
||||
// reading from a tainted object yields a tainted result
|
||||
succ.(DataFlow::PropRead).getBase() = pred and
|
||||
not (
|
||||
@@ -594,6 +426,16 @@ module TaintTracking {
|
||||
}
|
||||
}
|
||||
|
||||
private class LegacySplitTaintStep extends LegacyTaintStep {
|
||||
override predicate stringManipulationStep(DataFlow::Node pred, DataFlow::Node target) {
|
||||
exists(DataFlow::MethodCallNode call |
|
||||
call.getMethodName() = "split" and
|
||||
pred = call.getReceiver() and
|
||||
target = call
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint propagating data flow edge arising from string manipulation
|
||||
* functions defined in the standard library.
|
||||
@@ -610,9 +452,9 @@ module TaintTracking {
|
||||
[
|
||||
"anchor", "big", "blink", "bold", "concat", "fixed", "fontcolor", "fontsize",
|
||||
"italics", "link", "padEnd", "padStart", "repeat", "replace", "replaceAll", "slice",
|
||||
"small", "split", "strike", "sub", "substr", "substring", "sup",
|
||||
"toLocaleLowerCase", "toLocaleUpperCase", "toLowerCase", "toUpperCase", "trim",
|
||||
"trimLeft", "trimRight", "toWellFormed"
|
||||
"small", "strike", "sub", "substr", "substring", "sup", "toLocaleLowerCase",
|
||||
"toLocaleUpperCase", "toLowerCase", "toUpperCase", "trim", "trimLeft", "trimRight",
|
||||
"toWellFormed"
|
||||
]
|
||||
or
|
||||
// sorted, interesting, properties of Object.prototype
|
||||
@@ -652,26 +494,29 @@ module TaintTracking {
|
||||
]).getACall() and
|
||||
pred = c.getArgument(0)
|
||||
)
|
||||
or
|
||||
// In and out of .replace callbacks
|
||||
exists(StringReplaceCall call |
|
||||
// Into the callback if the regexp does not sanitize matches
|
||||
hasWildcardReplaceRegExp(call) and
|
||||
pred = call.getReceiver() and
|
||||
succ = call.getReplacementCallback().getParameter(0)
|
||||
or
|
||||
// Out of the callback
|
||||
pred = call.getReplacementCallback().getReturnNode() and
|
||||
succ = call
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Holds if the given call takes a regexp containing a wildcard. */
|
||||
pragma[noinline]
|
||||
private predicate hasWildcardReplaceRegExp(StringReplaceCall call) {
|
||||
RegExp::isWildcardLike(call.getRegExp().getRoot().getAChild*())
|
||||
/**
|
||||
* A taint propagating edge for the string `replace` function.
|
||||
*
|
||||
* This is a legacy step as it crosses a function boundary, and would thus be converted to a jump step.
|
||||
*/
|
||||
private class ReplaceCallbackSteps extends LegacyTaintStep {
|
||||
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
// In and out of .replace callbacks
|
||||
exists(StringReplaceCall call |
|
||||
// Into the callback if the regexp does not sanitize matches
|
||||
call.hasRegExpContainingWildcard() and
|
||||
pred = call.getReceiver() and
|
||||
succ = call.getReplacementCallback().getParameter(0)
|
||||
or
|
||||
// Out of the callback
|
||||
pred = call.getReplacementCallback().getReturnNode() and
|
||||
succ = call
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -969,7 +814,7 @@ module TaintTracking {
|
||||
* A conditional checking a tainted string against a regular expression, which is
|
||||
* considered to be a sanitizer for all configurations.
|
||||
*/
|
||||
class SanitizingRegExpTest extends AdditionalSanitizerGuardNode, DataFlow::ValueNode {
|
||||
class SanitizingRegExpTest extends LegacyAdditionalBarrierGuard, DataFlow::ValueNode {
|
||||
Expr expr;
|
||||
boolean sanitizedOutcome;
|
||||
|
||||
@@ -1002,12 +847,10 @@ module TaintTracking {
|
||||
|
||||
private boolean getSanitizedOutcome() { result = sanitizedOutcome }
|
||||
|
||||
override predicate sanitizes(boolean outcome, Expr e) {
|
||||
override predicate blocksExpr(boolean outcome, Expr e) {
|
||||
outcome = sanitizedOutcome and
|
||||
e = expr
|
||||
}
|
||||
|
||||
override predicate appliesTo(Configuration cfg) { any() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1017,14 +860,14 @@ module TaintTracking {
|
||||
*
|
||||
* Note that the `includes` method is covered by `MembershipTestSanitizer`.
|
||||
*/
|
||||
class WhitelistContainmentCallSanitizer extends AdditionalSanitizerGuardNode,
|
||||
class WhitelistContainmentCallSanitizer extends LegacyAdditionalBarrierGuard,
|
||||
DataFlow::MethodCallNode
|
||||
{
|
||||
WhitelistContainmentCallSanitizer() {
|
||||
this.getMethodName() = ["contains", "has", "hasOwnProperty", "hasOwn"]
|
||||
}
|
||||
|
||||
override predicate sanitizes(boolean outcome, Expr e) {
|
||||
override predicate blocksExpr(boolean outcome, Expr e) {
|
||||
exists(int propertyIndex |
|
||||
if this.getMethodName() = "hasOwn" then propertyIndex = 1 else propertyIndex = 0
|
||||
|
|
||||
@@ -1032,8 +875,6 @@ module TaintTracking {
|
||||
e = this.getArgument(propertyIndex).asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate appliesTo(Configuration cfg) { any() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1043,33 +884,40 @@ module TaintTracking {
|
||||
*
|
||||
* This sanitizer is not enabled by default.
|
||||
*/
|
||||
class AdHocWhitelistCheckSanitizer extends SanitizerGuardNode, DataFlow::CallNode {
|
||||
class AdHocWhitelistCheckSanitizer extends DataFlow::CallNode {
|
||||
AdHocWhitelistCheckSanitizer() {
|
||||
this.getCalleeName()
|
||||
.regexpMatch("(?i).*((?<!un)safe|whitelist|(?<!in)valid|allow|(?<!un)auth(?!or\\b)).*") and
|
||||
this.getNumArgument() = 1
|
||||
}
|
||||
|
||||
override predicate sanitizes(boolean outcome, Expr e) {
|
||||
/** Holds if this node blocks flow through `e`, provided it evaluates to `outcome`. */
|
||||
predicate blocksExpr(boolean outcome, Expr e) {
|
||||
outcome = true and
|
||||
e = this.getArgument(0).asExpr()
|
||||
}
|
||||
}
|
||||
|
||||
deprecated private class AdHocWhitelistCheckSanitizerAsSanitizerGuardNode extends SanitizerGuardNode instanceof AdHocWhitelistCheckSanitizer
|
||||
{
|
||||
override predicate sanitizes(boolean outcome, Expr e) { super.blocksExpr(outcome, e) }
|
||||
}
|
||||
|
||||
/** Barrier nodes derived from the `AdHocWhitelistCheckSanitizer` class. */
|
||||
module AdHocWhitelistCheckSanitizer = DataFlow::MakeBarrierGuard<AdHocWhitelistCheckSanitizer>;
|
||||
|
||||
/** A check of the form `if(x in o)`, which sanitizes `x` in its "then" branch. */
|
||||
class InSanitizer extends AdditionalSanitizerGuardNode, DataFlow::ValueNode {
|
||||
class InSanitizer extends LegacyAdditionalBarrierGuard, DataFlow::ValueNode {
|
||||
override InExpr astNode;
|
||||
|
||||
override predicate sanitizes(boolean outcome, Expr e) {
|
||||
override predicate blocksExpr(boolean outcome, Expr e) {
|
||||
outcome = true and
|
||||
e = astNode.getLeftOperand()
|
||||
}
|
||||
|
||||
override predicate appliesTo(Configuration cfg) { any() }
|
||||
}
|
||||
|
||||
/** A check of the form `if(o[x] != undefined)`, which sanitizes `x` in its "then" branch. */
|
||||
class UndefinedCheckSanitizer extends AdditionalSanitizerGuardNode, DataFlow::ValueNode {
|
||||
class UndefinedCheckSanitizer extends LegacyAdditionalBarrierGuard, DataFlow::ValueNode {
|
||||
Expr x;
|
||||
override EqualityTest astNode;
|
||||
|
||||
@@ -1085,27 +933,23 @@ module TaintTracking {
|
||||
)
|
||||
}
|
||||
|
||||
override predicate sanitizes(boolean outcome, Expr e) {
|
||||
override predicate blocksExpr(boolean outcome, Expr e) {
|
||||
outcome = astNode.getPolarity().booleanNot() and
|
||||
e = x
|
||||
}
|
||||
|
||||
override predicate appliesTo(Configuration cfg) { any() }
|
||||
}
|
||||
|
||||
/** A check of the form `type x === "undefined"`, which sanitized `x` in its "then" branch. */
|
||||
class TypeOfUndefinedSanitizer extends AdditionalSanitizerGuardNode, DataFlow::ValueNode {
|
||||
class TypeOfUndefinedSanitizer extends LegacyAdditionalBarrierGuard, DataFlow::ValueNode {
|
||||
Expr x;
|
||||
override EqualityTest astNode;
|
||||
|
||||
TypeOfUndefinedSanitizer() { isTypeofGuard(astNode, x, "undefined") }
|
||||
|
||||
override predicate sanitizes(boolean outcome, Expr e) {
|
||||
override predicate blocksExpr(boolean outcome, Expr e) {
|
||||
outcome = astNode.getPolarity() and
|
||||
e = x
|
||||
}
|
||||
|
||||
override predicate appliesTo(Configuration cfg) { any() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1166,7 +1010,7 @@ module TaintTracking {
|
||||
/**
|
||||
* A test of form `x.length === "0"`, preventing `x` from being tainted.
|
||||
*/
|
||||
class IsEmptyGuard extends AdditionalSanitizerGuardNode, DataFlow::ValueNode {
|
||||
class IsEmptyGuard extends LegacyAdditionalBarrierGuard, DataFlow::ValueNode {
|
||||
override EqualityTest astNode;
|
||||
boolean polarity;
|
||||
Expr operand;
|
||||
@@ -1180,24 +1024,20 @@ module TaintTracking {
|
||||
)
|
||||
}
|
||||
|
||||
override predicate sanitizes(boolean outcome, Expr e) { polarity = outcome and e = operand }
|
||||
|
||||
override predicate appliesTo(Configuration cfg) { any() }
|
||||
override predicate blocksExpr(boolean outcome, Expr e) { polarity = outcome and e = operand }
|
||||
}
|
||||
|
||||
/**
|
||||
* A check of the form `whitelist.includes(x)` or equivalent, which sanitizes `x` in its "then" branch.
|
||||
*/
|
||||
class MembershipTestSanitizer extends AdditionalSanitizerGuardNode {
|
||||
class MembershipTestSanitizer extends LegacyAdditionalBarrierGuard {
|
||||
MembershipCandidate candidate;
|
||||
|
||||
MembershipTestSanitizer() { this = candidate.getTest() }
|
||||
|
||||
override predicate sanitizes(boolean outcome, Expr e) {
|
||||
override predicate blocksExpr(boolean outcome, Expr e) {
|
||||
candidate = e.flow() and candidate.getTestPolarity() = outcome
|
||||
}
|
||||
|
||||
override predicate appliesTo(Configuration cfg) { any() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1205,7 +1045,7 @@ module TaintTracking {
|
||||
*
|
||||
* The more typical case of `x.indexOf(y) >= 0` is covered by `MembershipTestSanitizer`.
|
||||
*/
|
||||
class PositiveIndexOfSanitizer extends AdditionalSanitizerGuardNode, DataFlow::ValueNode {
|
||||
class PositiveIndexOfSanitizer extends LegacyAdditionalBarrierGuard, DataFlow::ValueNode {
|
||||
MethodCallExpr indexOf;
|
||||
override RelationalComparison astNode;
|
||||
|
||||
@@ -1218,19 +1058,17 @@ module TaintTracking {
|
||||
)
|
||||
}
|
||||
|
||||
override predicate sanitizes(boolean outcome, Expr e) {
|
||||
override predicate blocksExpr(boolean outcome, Expr e) {
|
||||
outcome = true and
|
||||
e = indexOf.getArgument(0)
|
||||
}
|
||||
|
||||
override predicate appliesTo(Configuration cfg) { any() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An equality test on `e.origin` or `e.source` where `e` is a `postMessage` event object,
|
||||
* considered as a sanitizer for `e`.
|
||||
*/
|
||||
private class PostMessageEventSanitizer extends AdditionalSanitizerGuardNode {
|
||||
private class PostMessageEventSanitizer extends LegacyAdditionalBarrierGuard {
|
||||
VarAccess event;
|
||||
boolean polarity;
|
||||
|
||||
@@ -1247,11 +1085,29 @@ module TaintTracking {
|
||||
)
|
||||
}
|
||||
|
||||
override predicate sanitizes(boolean outcome, Expr e) {
|
||||
override predicate blocksExpr(boolean outcome, Expr e) {
|
||||
outcome = polarity and
|
||||
e = event
|
||||
}
|
||||
}
|
||||
|
||||
override predicate appliesTo(Configuration cfg) { any() }
|
||||
import internal.sharedlib.TaintTracking
|
||||
|
||||
/**
|
||||
* Holds if there is a taint step from `node1` to `node2`.
|
||||
*
|
||||
* This includes steps between synthesized nodes generated by flow summaries.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate defaultTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
TaintTrackingPrivate::defaultAdditionalTaintStep(node1, node2)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is seen as a barrier for taint-tracking.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate defaultSanitizer(DataFlow::Node node) {
|
||||
TaintTrackingPrivate::defaultTaintSanitizer(node)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ class AccessPath extends TAccessPath {
|
||||
* Gets an expression in `bb` represented by this access path.
|
||||
*/
|
||||
cached
|
||||
Expr getAnInstanceIn(BasicBlock bb) {
|
||||
Expr getAnInstanceIn(ReachableBasicBlock bb) {
|
||||
Stages::DataFlowStage::ref() and
|
||||
exists(SsaVariable var |
|
||||
this = MkSsaRoot(var) and
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
private import javascript
|
||||
private import semmle.javascript.dataflow.internal.DataFlowNode
|
||||
private import semmle.javascript.dataflow.internal.DataFlowPrivate
|
||||
|
||||
/**
|
||||
* Gets a data-flow node synthesized using `AdditionalFlowInternal#needsSynthesizedNode`.
|
||||
*/
|
||||
DataFlow::Node getSynthesizedNode(AstNode node, string tag) {
|
||||
result = TGenericSynthesizedNode(node, tag, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* An extension to `AdditionalFlowStep` with additional internal-only predicates.
|
||||
*/
|
||||
class AdditionalFlowInternal extends DataFlow::AdditionalFlowStep {
|
||||
/**
|
||||
* Holds if a data-flow node should be synthesized for the pair `(node, tag)`.
|
||||
*
|
||||
* The node can be obtained using `getSynthesizedNode(node, tag)`.
|
||||
*
|
||||
* `container` will be seen as the node's enclosing container.
|
||||
*/
|
||||
predicate needsSynthesizedNode(AstNode node, string tag, DataFlowCallable container) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `node` should only permit flow of values stored in `contents`.
|
||||
*/
|
||||
predicate expectsContent(DataFlow::Node node, DataFlow::ContentSet contents) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `node` should not permit flow of values stored in `contents`.
|
||||
*/
|
||||
predicate clearsContent(DataFlow::Node node, DataFlow::ContentSet contents) { none() }
|
||||
}
|
||||
@@ -0,0 +1,474 @@
|
||||
/**
|
||||
* A copy of the barrier guard logic from `Configuration.qll` in the JS data flow library.
|
||||
*
|
||||
* This version considers all barrier guards to be relevant.
|
||||
*/
|
||||
|
||||
private import javascript
|
||||
private import semmle.javascript.dataflow.internal.AccessPaths
|
||||
private import semmle.javascript.dataflow.internal.DataFlowPrivate as DataFlowPrivate
|
||||
private import semmle.javascript.dataflow.internal.sharedlib.Ssa as Ssa2
|
||||
|
||||
private signature class BarrierGuardSig extends DataFlow::Node {
|
||||
/**
|
||||
* Holds if this node acts as a barrier for data flow, blocking further flow from `e` if `this` evaluates to `outcome`.
|
||||
*/
|
||||
predicate blocksExpr(boolean outcome, Expr e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a barrier guard class to a set of nodes to include in an implementation of `isBarrier(node)`.
|
||||
*/
|
||||
module MakeBarrierGuard<BarrierGuardSig BaseGuard> {
|
||||
final private class FinalBaseGuard = BaseGuard;
|
||||
|
||||
private class Adapter extends FinalBaseGuard {
|
||||
predicate blocksExpr(boolean outcome, Expr e, Unit state) {
|
||||
super.blocksExpr(outcome, e) and exists(state)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a node that is blocked by a barrier guard.
|
||||
*/
|
||||
DataFlow::Node getABarrierNode() {
|
||||
result = MakeStateBarrierGuard<Unit, Adapter>::getABarrierNode(_)
|
||||
}
|
||||
}
|
||||
|
||||
deprecated private module DeprecationWrapper {
|
||||
signature class LabeledBarrierGuardSig extends DataFlow::Node {
|
||||
/**
|
||||
* Holds if this node acts as a barrier for `label`, blocking further flow from `e` if `this` evaluates to `outcome`.
|
||||
*/
|
||||
predicate blocksExpr(boolean outcome, Expr e, DataFlow::FlowLabel label);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a barrier guard class to a set of nodes to include in an implementation of `isBarrier(node, label)`.
|
||||
*/
|
||||
deprecated module MakeLabeledBarrierGuard<DeprecationWrapper::LabeledBarrierGuardSig BaseGuard> {
|
||||
final private class FinalBaseGuard = BaseGuard;
|
||||
|
||||
private class Adapter extends FinalBaseGuard {
|
||||
predicate blocksExpr(boolean outcome, Expr e, DataFlow::FlowLabel label) {
|
||||
super.blocksExpr(outcome, e, label)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a node and flow label that is blocked by a barrier guard.
|
||||
*/
|
||||
DataFlow::Node getABarrierNode(DataFlow::FlowLabel label) {
|
||||
result = MakeStateBarrierGuard<DataFlow::FlowLabel, Adapter>::getABarrierNode(label)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Contains deprecated signatures.
|
||||
*
|
||||
* This module is a workaround for the fact that deprecated signatures can't refer to deprecated classes
|
||||
* without getting a deprecation warning
|
||||
*/
|
||||
deprecated private module DeprecatedSigs {
|
||||
signature predicate isBarrierGuardSig(DataFlow::BarrierGuardNode node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a labeled barrier guard class to a set of nodes to include in an implementation of `isBarrier(node)` and `isBarrier(node, label)`
|
||||
* in a `DataFlow::StateConfigSig` implementation.
|
||||
*/
|
||||
deprecated module MakeLegacyBarrierGuardLabeled<DeprecatedSigs::isBarrierGuardSig/1 isBarrierGuard> {
|
||||
final private class FinalNode = DataFlow::Node;
|
||||
|
||||
private class Adapter extends FinalNode instanceof DataFlow::BarrierGuardNode {
|
||||
Adapter() { isBarrierGuard(this) }
|
||||
|
||||
predicate blocksExpr(boolean outcome, Expr e, string label) {
|
||||
super.blocks(outcome, e, label)
|
||||
or
|
||||
super.blocks(outcome, e) and label = ""
|
||||
}
|
||||
}
|
||||
|
||||
private module Guards = MakeStateBarrierGuard<string, Adapter>;
|
||||
|
||||
/**
|
||||
* Gets a node that is blocked by a barrier guard.
|
||||
*/
|
||||
DataFlow::Node getABarrierNode() { result = Guards::getABarrierNode("") }
|
||||
|
||||
/**
|
||||
* Gets a node and flow label that is blocked by a barrier guard.
|
||||
*/
|
||||
DataFlow::Node getABarrierNode(DataFlow::FlowLabel label) {
|
||||
result = Guards::getABarrierNode(label)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a barrier guard class to a set of nodes to include in an implementation of `isBarrier(node)` in a `DataFlow::ConfigSig` implementation.
|
||||
*/
|
||||
deprecated module MakeLegacyBarrierGuard<DeprecatedSigs::isBarrierGuardSig/1 isBarrierGuard> {
|
||||
final private class FinalNode = DataFlow::Node;
|
||||
|
||||
private class Adapter extends FinalNode instanceof DataFlow::BarrierGuardNode {
|
||||
Adapter() { isBarrierGuard(this) }
|
||||
|
||||
predicate blocksExpr(boolean outcome, Expr e, string label) {
|
||||
super.blocks(outcome, e, label)
|
||||
or
|
||||
super.blocks(outcome, e) and label = ""
|
||||
}
|
||||
}
|
||||
|
||||
private module Guards = MakeStateBarrierGuard<string, Adapter>;
|
||||
|
||||
/**
|
||||
* Gets a node that is blocked by a barrier guard.
|
||||
*/
|
||||
DataFlow::Node getABarrierNode() { result = Guards::getABarrierNode(["", "data", "taint"]) }
|
||||
}
|
||||
|
||||
bindingset[this]
|
||||
private signature class FlowStateSig;
|
||||
|
||||
private module WithFlowState<FlowStateSig FlowState> {
|
||||
signature class BarrierGuardSig extends DataFlow::Node {
|
||||
/**
|
||||
* Holds if this node acts as a barrier for `state`, blocking further flow from `e` if `this` evaluates to `outcome`.
|
||||
*/
|
||||
predicate blocksExpr(boolean outcome, Expr e, FlowState state);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Projects the dominator tree onto a tree that only considers dominance between `ConditionGuardNode`s.
|
||||
*
|
||||
* This exists to speeds up the dominance check for barrier guards acting on an access path, avoiding the following two
|
||||
* bad join orders:
|
||||
*
|
||||
* - Enumerate all basic blocks dominated by a barrier guard, and then find uses of the access path in those blocks.
|
||||
* - Enumerate all uses of an access path and then select those that are in a dominated block.
|
||||
*
|
||||
* Both joins have pathological cases in different benchmarks.
|
||||
*
|
||||
* We use a join order that is essentially the first one above, except we only enumerate condition guards, not all the blocks.
|
||||
*/
|
||||
cached
|
||||
private module ConditionGuardDominators {
|
||||
/** Gets the condition guard that most-immediately dominates `bb`. */
|
||||
private ConditionGuardNode getDominatingCondition(ReachableBasicBlock bb) {
|
||||
result.getBasicBlock() = bb
|
||||
or
|
||||
not bb = any(ConditionGuardNode guard).getBasicBlock() and
|
||||
result = getDominatingCondition(bb.getImmediateDominator())
|
||||
}
|
||||
|
||||
private predicate immediateDom(ConditionGuardNode dominator, ConditionGuardNode dominated) {
|
||||
dominator = getDominatingCondition(dominated.getBasicBlock().getImmediateDominator())
|
||||
or
|
||||
dominator = dominated // make the fastTC below reflexive
|
||||
}
|
||||
|
||||
/** Gets a condition guard dominated by `node` */
|
||||
cached
|
||||
ConditionGuardNode getADominatedConditionGuard(ConditionGuardNode node) =
|
||||
fastTC(immediateDom/2)(node, result)
|
||||
|
||||
/** Gets a use of `ap` and binds `guard` to its immediately-dominating condition guard (if any). */
|
||||
cached
|
||||
Expr getAnAccessPathUseUnderCondition(AccessPath ap, ConditionGuardNode guard) {
|
||||
exists(ReachableBasicBlock bb |
|
||||
result = ap.getAnInstanceIn(bb) and
|
||||
guard = getDominatingCondition(bb)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a barrier guard class to a set of nodes to include in an implementation of `isBarrier(node, state)`.
|
||||
*/
|
||||
module MakeStateBarrierGuard<
|
||||
FlowStateSig FlowState, WithFlowState<FlowState>::BarrierGuardSig BaseGuard>
|
||||
{
|
||||
final private class FinalNode = DataFlow::Node;
|
||||
|
||||
abstract private class BarrierGuard extends FinalNode {
|
||||
abstract predicate blocksExpr(boolean outcome, Expr test, FlowState state);
|
||||
}
|
||||
|
||||
private class ExplicitBarrierGuard extends BarrierGuard instanceof BaseGuard {
|
||||
override predicate blocksExpr(boolean outcome, Expr test, FlowState state) {
|
||||
BaseGuard.super.blocksExpr(outcome, test, state)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a node and flow state that is blocked by a barrier guard.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
DataFlow::Node getABarrierNode(FlowState state) { barrierGuardBlocksNode(result, state) }
|
||||
|
||||
//
|
||||
// ================================================================================================
|
||||
// NOTE
|
||||
// The rest of this file is a copy of the barrier-guard logic in Configuration.qll except:
|
||||
// - FlowLabel is replaced by FlowState
|
||||
// - BarrierGuardNode and AdditionalBarrierGuardNode are replaced by the BarrierGuard class defined above
|
||||
// - `barrierGuardBlocksEdge` is missing as dataflow2 does not support barrier edges
|
||||
// - `barrierGuardIsRelevant` does not check pruning results as we can't access that from here
|
||||
// - `barrierGuardBlocksNode` has been rewritten to perform better without pruning.
|
||||
// ================================================================================================
|
||||
//
|
||||
/**
|
||||
* Holds if data flow node `guard` acts as a barrier for data flow.
|
||||
*
|
||||
* `state` is bound to the blocked state, or the empty FlowState if all labels should be blocked.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate barrierGuardBlocksExpr(
|
||||
BarrierGuard guard, boolean outcome, Expr test, FlowState state
|
||||
) {
|
||||
guard.blocksExpr(outcome, test, state)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `guard` may block the flow of a value reachable through exploratory flow.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate barrierGuardIsRelevant(BarrierGuard guard) {
|
||||
exists(Expr e |
|
||||
barrierGuardBlocksExpr(guard, _, e, _)
|
||||
// All guards are considered relevant (this is the difference from the main JS lib)
|
||||
// isRelevantForward(e.flow(), _)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data flow node `guard` acts as a barrier for data flow due to aliasing through
|
||||
* an access path.
|
||||
*
|
||||
* `state` is bound to the blocked state, or the empty FlowState if all labels should be blocked.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate barrierGuardBlocksAccessPath(
|
||||
BarrierGuard guard, boolean outcome, AccessPath ap, FlowState state
|
||||
) {
|
||||
barrierGuardIsRelevant(guard) and
|
||||
barrierGuardBlocksExpr(guard, outcome, ap.getAnInstance(), state)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there exists an input variable of `ref` that blocks the state `state`.
|
||||
*
|
||||
* This predicate is outlined to give the optimizer a hint about the join ordering.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate barrierGuardBlocksSsaRefinement(
|
||||
BarrierGuard guard, boolean outcome, SsaRefinementNode ref, FlowState state
|
||||
) {
|
||||
barrierGuardIsRelevant(guard) and
|
||||
guard.getEnclosingExpr() = ref.getGuard().getTest() and
|
||||
forex(SsaVariable input | input = ref.getAnInput() |
|
||||
barrierGuardBlocksExpr(guard, outcome, input.getAUse(), state)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the result of `guard` is used in the branching condition `cond`.
|
||||
*
|
||||
* `outcome` is bound to the outcome of `cond` for join-ordering purposes.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate barrierGuardUsedInCondition(
|
||||
BarrierGuard guard, ConditionGuardNode cond, boolean outcome
|
||||
) {
|
||||
barrierGuardIsRelevant(guard) and
|
||||
outcome = cond.getOutcome() and
|
||||
(
|
||||
cond.getTest() = guard.getEnclosingExpr()
|
||||
or
|
||||
cond.getTest().flow().getImmediatePredecessor+() = guard
|
||||
)
|
||||
}
|
||||
|
||||
private predicate ssa2GuardChecks(
|
||||
Ssa2::SsaDataflowInput::Guard guard, Ssa2::SsaDataflowInput::Expr test, boolean branch,
|
||||
FlowState state
|
||||
) {
|
||||
exists(BarrierGuard g |
|
||||
g.asExpr() = guard and
|
||||
g.blocksExpr(branch, test, state)
|
||||
)
|
||||
}
|
||||
|
||||
private module Ssa2Barrier = Ssa2::BarrierGuardWithState<FlowState, ssa2GuardChecks/4>;
|
||||
|
||||
private predicate ssa2BlocksNode(DataFlow::Node node, FlowState state) {
|
||||
node = DataFlowPrivate::getNodeFromSsa2(Ssa2Barrier::getABarrierNode(state))
|
||||
}
|
||||
|
||||
/** Holds if a barrier guard blocks uses of `ap` in basic blocks dominated by `cond`. */
|
||||
pragma[nomagic]
|
||||
private predicate barrierGuardBlocksAccessPathIn(
|
||||
AccessPath ap, ConditionGuardNode cond, FlowState state
|
||||
) {
|
||||
exists(BarrierGuard guard, boolean outcome |
|
||||
barrierGuardBlocksAccessPath(guard, outcome, ap, state) and
|
||||
barrierGuardUsedInCondition(guard, cond, outcome)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `expr` is an access path reference that is blocked by a barrier guard.
|
||||
*/
|
||||
pragma[noopt]
|
||||
private predicate barrierGuardBlocksAccessPathUse(Expr use, FlowState state) {
|
||||
exists(AccessPath p, ConditionGuardNode cond, ConditionGuardNode useDominator |
|
||||
barrierGuardBlocksAccessPathIn(p, cond, state) and
|
||||
useDominator = ConditionGuardDominators::getADominatedConditionGuard(cond) and
|
||||
use = ConditionGuardDominators::getAnAccessPathUseUnderCondition(p, useDominator)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data flow node `nd` acts as a barrier for data flow, possibly due to aliasing
|
||||
* through an access path.
|
||||
*
|
||||
* `state` is bound to the blocked state.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate barrierGuardBlocksNode(DataFlow::Node nd, FlowState state) {
|
||||
exists(BarrierGuard guard, SsaRefinementNode ref, boolean outcome |
|
||||
nd = DataFlow::ssaDefinitionNode(ref) and
|
||||
outcome = ref.getGuard().(ConditionGuardNode).getOutcome() and
|
||||
barrierGuardBlocksSsaRefinement(guard, outcome, ref, state)
|
||||
)
|
||||
or
|
||||
exists(Expr use |
|
||||
barrierGuardBlocksAccessPathUse(use, state) and
|
||||
nd = DataFlow::valueNode(use)
|
||||
)
|
||||
or
|
||||
ssa2BlocksNode(nd, state)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a logical `and` expression, or parenthesized expression, that contains `guard`.
|
||||
*/
|
||||
private Expr getALogicalAndParent(BarrierGuard guard) {
|
||||
barrierGuardIsRelevant(guard) and result = guard.asExpr()
|
||||
or
|
||||
result.(LogAndExpr).getAnOperand() = getALogicalAndParent(guard)
|
||||
or
|
||||
result.getUnderlyingValue() = getALogicalAndParent(guard)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a logical `or` expression, or parenthesized expression, that contains `guard`.
|
||||
*/
|
||||
private Expr getALogicalOrParent(BarrierGuard guard) {
|
||||
barrierGuardIsRelevant(guard) and result = guard.asExpr()
|
||||
or
|
||||
result.(LogOrExpr).getAnOperand() = getALogicalOrParent(guard)
|
||||
or
|
||||
result.getUnderlyingValue() = getALogicalOrParent(guard)
|
||||
}
|
||||
|
||||
final private class FinalFunction = Function;
|
||||
|
||||
/**
|
||||
* A function that returns the result of a barrier guard.
|
||||
*/
|
||||
private class BarrierGuardFunction extends FinalFunction {
|
||||
DataFlow::ParameterNode sanitizedParameter;
|
||||
BarrierGuard guard;
|
||||
boolean guardOutcome;
|
||||
FlowState state;
|
||||
int paramIndex;
|
||||
|
||||
BarrierGuardFunction() {
|
||||
barrierGuardIsRelevant(guard) and
|
||||
exists(Expr e |
|
||||
exists(Expr returnExpr |
|
||||
returnExpr = guard.asExpr()
|
||||
or
|
||||
// ad hoc support for conjunctions:
|
||||
getALogicalAndParent(guard) = returnExpr and guardOutcome = true
|
||||
or
|
||||
// ad hoc support for disjunctions:
|
||||
getALogicalOrParent(guard) = returnExpr and guardOutcome = false
|
||||
|
|
||||
exists(SsaExplicitDefinition ssa |
|
||||
ssa.getDef().getSource() = returnExpr and
|
||||
ssa.getVariable().getAUse() = this.getAReturnedExpr()
|
||||
)
|
||||
or
|
||||
returnExpr = this.getAReturnedExpr()
|
||||
) and
|
||||
sanitizedParameter.flowsToExpr(e) and
|
||||
barrierGuardBlocksExpr(guard, guardOutcome, e, state)
|
||||
) and
|
||||
sanitizedParameter.getParameter() = this.getParameter(paramIndex)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this function sanitizes argument `e` of call `call`, provided the call evaluates to `outcome`.
|
||||
*/
|
||||
predicate isBarrierCall(DataFlow::CallNode call, Expr e, boolean outcome, FlowState st) {
|
||||
exists(DataFlow::Node arg |
|
||||
DataFlow::argumentPassingStep(pragma[only_bind_into](call), pragma[only_bind_into](arg),
|
||||
pragma[only_bind_into](this), pragma[only_bind_into](sanitizedParameter)) and
|
||||
arg.asExpr() = e and
|
||||
arg = call.getArgument(paramIndex) and
|
||||
outcome = guardOutcome and
|
||||
state = st
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call that sanitizes an argument.
|
||||
*/
|
||||
private class AdditionalBarrierGuardCall extends BarrierGuard instanceof DataFlow::CallNode {
|
||||
BarrierGuardFunction f;
|
||||
|
||||
AdditionalBarrierGuardCall() { f.isBarrierCall(this, _, _, _) }
|
||||
|
||||
override predicate blocksExpr(boolean outcome, Expr e, FlowState state) {
|
||||
f.isBarrierCall(this, e, outcome, state)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A sanitizer where an inner sanitizer is compared against a boolean.
|
||||
* E.g. (assuming `sanitizes(e)` is an existing sanitizer):
|
||||
* ```javascript
|
||||
* if (sanitizes(e) === true) {
|
||||
* // e is sanitized
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
private class CallAgainstEqualityCheck extends BarrierGuard {
|
||||
BarrierGuard prev;
|
||||
boolean polarity;
|
||||
|
||||
CallAgainstEqualityCheck() {
|
||||
prev instanceof DataFlow::CallNode and
|
||||
exists(EqualityTest test, BooleanLiteral bool |
|
||||
this.asExpr() = test and
|
||||
test.hasOperands(prev.asExpr(), bool) and
|
||||
polarity = test.getPolarity().booleanXor(bool.getBoolValue())
|
||||
)
|
||||
}
|
||||
|
||||
override predicate blocksExpr(boolean outcome, Expr e, FlowState state) {
|
||||
exists(boolean prevOutcome |
|
||||
barrierGuardBlocksExpr(prev, prevOutcome, e, state) and
|
||||
outcome = prevOutcome.booleanXor(polarity)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,513 @@
|
||||
private import javascript
|
||||
private import semmle.javascript.frameworks.data.internal.ApiGraphModels as ApiGraphModels
|
||||
private import semmle.javascript.dataflow.internal.FlowSummaryPrivate as FlowSummaryPrivate
|
||||
private import semmle.javascript.dataflow.internal.VariableOrThis
|
||||
private import codeql.dataflow.internal.AccessPathSyntax as AccessPathSyntax
|
||||
|
||||
module Private {
|
||||
import Public
|
||||
|
||||
/**
|
||||
* Gets the largest array index should be propagated precisely through flow summaries.
|
||||
*
|
||||
* Note that all known array indices have a corresponding singleton content, but some will
|
||||
* be collapsed in flow summaries that operate on array elements.
|
||||
*/
|
||||
int getMaxPreciseArrayIndex() { result = 9 }
|
||||
|
||||
/** Gets the largest array index should be propagated precisely through flow summaries. */
|
||||
int getAPreciseArrayIndex() { result = [0 .. getMaxPreciseArrayIndex()] }
|
||||
|
||||
/**
|
||||
* Holds if a MaD access path token of form `name[arg]` exists.
|
||||
*/
|
||||
predicate isAccessPathTokenPresent(string name, string arg) {
|
||||
arg = any(FlowSummaryPrivate::AccessPathToken tok).getAnArgument(name)
|
||||
or
|
||||
arg = any(ApiGraphModels::AccessPathToken tok).getAnArgument(name)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if values associated with `key` should be tracked as a individual contents of a `Map` object.
|
||||
*/
|
||||
private predicate isKnownMapKey(string key) {
|
||||
exists(MethodCallExpr call |
|
||||
call.getMethodName() = "get" and
|
||||
call.getNumArgument() = 1 and
|
||||
call.getArgument(0).getStringValue() = key
|
||||
)
|
||||
or
|
||||
isAccessPathTokenPresent("MapValue", key)
|
||||
}
|
||||
|
||||
/**
|
||||
* A known property name.
|
||||
*/
|
||||
class PropertyName extends string {
|
||||
// Note: unlike the similarly-named class in StepSummary.qll, this class must not depend on DataFlow::Node
|
||||
PropertyName() {
|
||||
this = any(PropAccess access).getPropertyName()
|
||||
or
|
||||
this = any(Property p).getName()
|
||||
or
|
||||
this = any(PropertyPattern p).getName()
|
||||
or
|
||||
this = any(GlobalVariable v).getName()
|
||||
or
|
||||
this = getAPreciseArrayIndex().toString()
|
||||
or
|
||||
isAccessPathTokenPresent("Member", this)
|
||||
}
|
||||
|
||||
/** Gets the array index corresponding to this property name. */
|
||||
pragma[nomagic]
|
||||
int asArrayIndex() { result = this.toInt() and result >= 0 and this = result.toString() }
|
||||
}
|
||||
|
||||
cached
|
||||
newtype TContent =
|
||||
MkPropertyContent(PropertyName name) or
|
||||
MkArrayElementUnknown() or // note: array elements with known index are just properties
|
||||
MkMapKey() or
|
||||
MkMapValueWithUnknownKey() or
|
||||
MkMapValueWithKnownKey(string key) { isKnownMapKey(key) } or
|
||||
MkSetElement() or
|
||||
MkIteratorElement() or
|
||||
MkIteratorError() or
|
||||
MkPromiseValue() or
|
||||
MkPromiseError() or
|
||||
MkCapturedContent(LocalVariableOrThis v) { v.isCaptured() }
|
||||
|
||||
cached
|
||||
newtype TContentSet =
|
||||
MkSingletonContent(Content content) or
|
||||
MkArrayElementKnown(int index) { index = any(PropertyName name).asArrayIndex() } or
|
||||
MkArrayElementLowerBound(int index) { index = [0 .. getMaxPreciseArrayIndex() + 1] } or
|
||||
MkMapValueKnown(string key) { isKnownMapKey(key) } or
|
||||
MkMapValueAll() or
|
||||
MkPromiseFilter() or
|
||||
MkIteratorFilter() or
|
||||
MkAnyProperty() or
|
||||
MkAnyCapturedContent() or
|
||||
// The following content sets are used exclusively as an intermediate value in flow summaries.
|
||||
// These are encoded as a ContentSummaryComponent, although the flow graphs we generate are different
|
||||
// than an ordinary content component. These special content sets should never appear in a step.
|
||||
MkAwaited() or
|
||||
MkAnyPropertyDeep() or
|
||||
MkArrayElementDeep() or
|
||||
MkOptionalStep(string name) { isAccessPathTokenPresent("OptionalStep", name) } or
|
||||
MkOptionalBarrier(string name) { isAccessPathTokenPresent("OptionalBarrier", name) }
|
||||
|
||||
/**
|
||||
* Holds if `cs` is used to encode a special operation as a content component, but should not
|
||||
* be treated as an ordinary content component.
|
||||
*/
|
||||
predicate isSpecialContentSet(ContentSet cs) {
|
||||
cs = MkAwaited() or
|
||||
cs = MkAnyPropertyDeep() or
|
||||
cs = MkArrayElementDeep() or
|
||||
cs instanceof MkOptionalStep or
|
||||
cs instanceof MkOptionalBarrier
|
||||
}
|
||||
}
|
||||
|
||||
module Public {
|
||||
private import Private
|
||||
|
||||
/**
|
||||
* A storage location on an object, such as a property name.
|
||||
*/
|
||||
class Content extends TContent {
|
||||
/** Gets a string representation of this content. */
|
||||
cached
|
||||
string toString() {
|
||||
// Note that these strings are visible to the end-user, in the access path of a PathNode.
|
||||
result = this.asPropertyName()
|
||||
or
|
||||
this.isUnknownArrayElement() and
|
||||
result = "ArrayElement"
|
||||
or
|
||||
this = MkMapKey() and
|
||||
result = "MapKey"
|
||||
or
|
||||
this = MkMapValueWithUnknownKey() and
|
||||
result = "MapValue"
|
||||
or
|
||||
exists(string key |
|
||||
this = MkMapValueWithKnownKey(key) and
|
||||
result = "MapValue[" + key + "]"
|
||||
)
|
||||
or
|
||||
this = MkSetElement() and
|
||||
result = "SetElement"
|
||||
or
|
||||
this = MkIteratorElement() and
|
||||
result = "IteratorElement"
|
||||
or
|
||||
this = MkIteratorError() and
|
||||
result = "IteratorError"
|
||||
or
|
||||
this = MkPromiseValue() and
|
||||
result = "PromiseValue"
|
||||
or
|
||||
this = MkPromiseError() and
|
||||
result = "PromiseError"
|
||||
or
|
||||
result = this.asCapturedVariable().getName()
|
||||
}
|
||||
|
||||
/** Gets the property name represented by this content, if any. */
|
||||
string asPropertyName() { this = MkPropertyContent(result) }
|
||||
|
||||
/** Gets the array index represented by this content, if any. */
|
||||
pragma[nomagic]
|
||||
int asArrayIndex() { result = this.asPropertyName().(PropertyName).asArrayIndex() }
|
||||
|
||||
/** Gets the captured variable represented by this content, if any. */
|
||||
LocalVariableOrThis asCapturedVariable() { this = MkCapturedContent(result) }
|
||||
|
||||
/** Holds if this represents values stored at an unknown array index. */
|
||||
predicate isUnknownArrayElement() { this = MkArrayElementUnknown() }
|
||||
|
||||
/** Holds if this represents values stored in a `Map` at an unknown key. */
|
||||
predicate isMapValueWithUnknownKey() { this = MkMapValueWithUnknownKey() }
|
||||
|
||||
/** Holds if this represents values stored in a `Map` as the given string key. */
|
||||
predicate isMapValueWithKnownKey(string key) { this = MkMapValueWithKnownKey(key) }
|
||||
}
|
||||
|
||||
/**
|
||||
* An entity that represents the set of `Content`s being accessed at a read or store operation.
|
||||
*/
|
||||
class ContentSet extends TContentSet {
|
||||
/** Gets a content that may be stored into when storing into this set. */
|
||||
pragma[inline]
|
||||
Content getAStoreContent() {
|
||||
result = this.asSingleton()
|
||||
or
|
||||
// For array element access with known lower bound, just store into the unknown array element
|
||||
this = ContentSet::arrayElementLowerBound(_) and
|
||||
result.isUnknownArrayElement()
|
||||
or
|
||||
exists(int n |
|
||||
this = ContentSet::arrayElementKnown(n) and
|
||||
result.asArrayIndex() = n
|
||||
)
|
||||
or
|
||||
exists(string key |
|
||||
this = ContentSet::mapValueWithKnownKey(key) and
|
||||
result.isMapValueWithKnownKey(key)
|
||||
)
|
||||
or
|
||||
this = ContentSet::mapValueAll() and
|
||||
result.isMapValueWithUnknownKey()
|
||||
}
|
||||
|
||||
/** Gets a content that may be read from when reading from this set. */
|
||||
pragma[nomagic]
|
||||
Content getAReadContent() {
|
||||
result = this.asSingleton()
|
||||
or
|
||||
this = ContentSet::promiseFilter() and
|
||||
(
|
||||
result = MkPromiseValue()
|
||||
or
|
||||
result = MkPromiseError()
|
||||
)
|
||||
or
|
||||
this = ContentSet::iteratorFilter() and
|
||||
(
|
||||
result = MkIteratorElement()
|
||||
or
|
||||
result = MkIteratorError()
|
||||
)
|
||||
or
|
||||
exists(int bound | this = ContentSet::arrayElementLowerBound(bound) |
|
||||
result.isUnknownArrayElement()
|
||||
or
|
||||
result.asArrayIndex() >= bound
|
||||
)
|
||||
or
|
||||
exists(int n | this = ContentSet::arrayElementKnown(n) |
|
||||
result.isUnknownArrayElement()
|
||||
or
|
||||
result.asArrayIndex() = n
|
||||
)
|
||||
or
|
||||
exists(string key | this = ContentSet::mapValueWithKnownKey(key) |
|
||||
result.isMapValueWithUnknownKey()
|
||||
or
|
||||
result.isMapValueWithKnownKey(key)
|
||||
)
|
||||
or
|
||||
this = ContentSet::mapValueAll() and
|
||||
(
|
||||
result.isMapValueWithUnknownKey()
|
||||
or
|
||||
result.isMapValueWithKnownKey(_)
|
||||
)
|
||||
or
|
||||
this = ContentSet::anyProperty() and
|
||||
(
|
||||
result instanceof MkPropertyContent
|
||||
or
|
||||
result instanceof MkArrayElementUnknown
|
||||
)
|
||||
or
|
||||
this = ContentSet::anyCapturedContent() and
|
||||
result instanceof Private::MkCapturedContent
|
||||
}
|
||||
|
||||
/** Gets the singleton content to be accessed. */
|
||||
Content asSingleton() { this = MkSingletonContent(result) }
|
||||
|
||||
/** Gets the property name to be accessed, provided that this is a singleton content set. */
|
||||
PropertyName asPropertyName() { result = this.asSingleton().asPropertyName() }
|
||||
|
||||
/**
|
||||
* Gets a string representation of this content set.
|
||||
*/
|
||||
string toString() {
|
||||
result = this.asSingleton().toString()
|
||||
or
|
||||
this = ContentSet::promiseFilter() and result = "PromiseFilter"
|
||||
or
|
||||
this = ContentSet::iteratorFilter() and result = "IteratorFilter"
|
||||
or
|
||||
exists(int bound |
|
||||
this = ContentSet::arrayElementLowerBound(bound) and
|
||||
result = "ArrayElement[" + bound + "..]"
|
||||
)
|
||||
or
|
||||
exists(int n | this = ContentSet::arrayElementKnown(n) and result = "ArrayElement[" + n + "]")
|
||||
or
|
||||
this = ContentSet::mapValueAll() and
|
||||
result = "MapValue"
|
||||
or
|
||||
this = ContentSet::anyProperty() and
|
||||
result = "AnyMember"
|
||||
or
|
||||
this = MkAwaited() and result = "Awaited (with coercion)"
|
||||
or
|
||||
this = MkAnyPropertyDeep() and result = "AnyMemberDeep"
|
||||
or
|
||||
this = MkArrayElementDeep() and result = "ArrayElementDeep"
|
||||
or
|
||||
this = MkAnyCapturedContent() and
|
||||
result = "AnyCapturedContent"
|
||||
or
|
||||
exists(string name |
|
||||
this = MkOptionalStep(name) and
|
||||
result = "OptionalStep[" + name + "]"
|
||||
)
|
||||
or
|
||||
exists(string name |
|
||||
this = MkOptionalBarrier(name) and
|
||||
result = "OptionalBarrier[" + name + "]"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Companion module to the `ContentSet` class, providing access to various content sets.
|
||||
*/
|
||||
module ContentSet {
|
||||
/**
|
||||
* A content set containing only the given content.
|
||||
*/
|
||||
pragma[inline]
|
||||
ContentSet singleton(Content content) { result.asSingleton() = content }
|
||||
|
||||
/**
|
||||
* A content set corresponding to the given property name.
|
||||
*/
|
||||
pragma[inline]
|
||||
ContentSet property(PropertyName name) { result.asSingleton().asPropertyName() = name }
|
||||
|
||||
/**
|
||||
* A content set that should only be used in `withContent` and `withoutContent` steps, which
|
||||
* matches the two promise-related contents, `Awaited[value]` and `Awaited[error]`.
|
||||
*/
|
||||
ContentSet promiseFilter() { result = MkPromiseFilter() }
|
||||
|
||||
/**
|
||||
* A content set that should only be used in `withContent` and `withoutContent` steps, which
|
||||
* matches the two iterator-related contents, `IteratorElement` and `IteratorError`.
|
||||
*/
|
||||
ContentSet iteratorFilter() { result = MkIteratorFilter() }
|
||||
|
||||
/**
|
||||
* A content set describing the result of a resolved promise.
|
||||
*/
|
||||
ContentSet promiseValue() { result = singleton(MkPromiseValue()) }
|
||||
|
||||
/**
|
||||
* A content set describing the error stored in a rejected promise.
|
||||
*/
|
||||
ContentSet promiseError() { result = singleton(MkPromiseError()) }
|
||||
|
||||
/**
|
||||
* A content set describing all array elements, regardless of their index in the array.
|
||||
*/
|
||||
ContentSet arrayElement() { result = MkArrayElementLowerBound(0) }
|
||||
|
||||
/**
|
||||
* A content set describing array elements at index `bound` or greater.
|
||||
*
|
||||
* For `bound=0` this gets the same content set as `ContentSet::arrayElement()`, that is,
|
||||
* the content set describing all array elements.
|
||||
*
|
||||
* For large values of `bound` this has no result - see `ContentSet::arrayElementLowerBoundFromInt`.
|
||||
*/
|
||||
ContentSet arrayElementLowerBound(int bound) { result = MkArrayElementLowerBound(bound) }
|
||||
|
||||
/**
|
||||
* A content set describing an access to array index `n`.
|
||||
*
|
||||
* This content set reads from element `n` and the unknown element, and stores to index `n`.
|
||||
*
|
||||
* For large values of `n` this has no result - see `ContentSet::arrayElementFromInt`.
|
||||
*/
|
||||
ContentSet arrayElementKnown(int n) { result = MkArrayElementKnown(n) }
|
||||
|
||||
/**
|
||||
* The singleton content set describing array elements stored at an unknown index.
|
||||
*/
|
||||
ContentSet arrayElementUnknown() { result = singleton(MkArrayElementUnknown()) }
|
||||
|
||||
/**
|
||||
* Gets a content set describing array elements at index `bound` or greater.
|
||||
*
|
||||
* If `bound` is too large, it is truncated to the greatest lower bound we can represent.
|
||||
*/
|
||||
bindingset[bound]
|
||||
ContentSet arrayElementLowerBoundFromInt(int bound) {
|
||||
result = arrayElementLowerBound(bound.minimum(getMaxPreciseArrayIndex() + 1))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the content set describing an access to array index `n`.
|
||||
*
|
||||
* If `n` is too large, it is truncated to the greatest lower bound we can represent.
|
||||
*/
|
||||
bindingset[n]
|
||||
ContentSet arrayElementFromInt(int n) {
|
||||
result = arrayElementKnown(n)
|
||||
or
|
||||
not exists(arrayElementKnown(n)) and
|
||||
result = arrayElementLowerBoundFromInt(n)
|
||||
}
|
||||
|
||||
/** Gets the content set describing the keys of a `Map` object. */
|
||||
ContentSet mapKey() { result = singleton(MkMapKey()) }
|
||||
|
||||
/** Gets the content set describing the values of a `Map` object stored with an unknown key. */
|
||||
ContentSet mapValueWithUnknownKey() { result = singleton(MkMapValueWithUnknownKey()) }
|
||||
|
||||
/**
|
||||
* Gets the content set describing the value of a `Map` object stored with the given known `key`.
|
||||
*
|
||||
* This has no result if `key` is not one of the keys we track precisely. See also `mapValueFromKey`.
|
||||
*/
|
||||
ContentSet mapValueWithKnownKeyStrict(string key) { result = MkMapValueKnown(key) }
|
||||
|
||||
/**
|
||||
* Gets the content set describing an access to a map value with the given `key`.
|
||||
*
|
||||
* This content set also reads from a value stored with an unknown key. Use `mapValueWithKnownKeyStrict` to strictly
|
||||
* refer to known keys.
|
||||
*
|
||||
* This has no result if `key` is not one of the keys we track precisely. See also `mapValueFromKey`.
|
||||
*/
|
||||
ContentSet mapValueWithKnownKey(string key) { result = singleton(MkMapValueWithKnownKey(key)) }
|
||||
|
||||
/** Gets the content set describing all values in a map (with known or unknown key). */
|
||||
ContentSet mapValueAll() { result = MkMapValueAll() }
|
||||
|
||||
/**
|
||||
* Gets the content set describing the value in a `Map` object stored at the given `key`.
|
||||
*
|
||||
* If `key` is not one of the keys we track precisely, this is mapped to the unknown key instead.
|
||||
*/
|
||||
bindingset[key]
|
||||
ContentSet mapValueFromKey(string key) {
|
||||
result = mapValueWithKnownKey(key)
|
||||
or
|
||||
not exists(mapValueWithKnownKey(key)) and
|
||||
result = mapValueWithUnknownKey()
|
||||
}
|
||||
|
||||
/** Gets the content set describing the elements of a `Set` object. */
|
||||
ContentSet setElement() { result = singleton(MkSetElement()) }
|
||||
|
||||
/** Gets the content set describing the elements of an iterator object. */
|
||||
ContentSet iteratorElement() { result = singleton(MkIteratorElement()) }
|
||||
|
||||
/** Gets the content set describing the exception to be thrown when attempting to iterate over the given value. */
|
||||
ContentSet iteratorError() { result = singleton(MkIteratorError()) }
|
||||
|
||||
/**
|
||||
* Gets a content set that reads from all ordinary properties.
|
||||
*
|
||||
* This includes array elements, but not the contents of `Map`, `Set`, `Promise`, or iterator objects.
|
||||
*
|
||||
* This content set has no effect if used in a store step.
|
||||
*/
|
||||
ContentSet anyProperty() { result = MkAnyProperty() }
|
||||
|
||||
/**
|
||||
* Gets a content set corresponding to the pseudo-property `propertyName`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private ContentSet fromLegacyPseudoProperty(string propertyName) {
|
||||
propertyName = Promises::valueProp() and
|
||||
result = promiseValue()
|
||||
or
|
||||
propertyName = Promises::errorProp() and
|
||||
result = promiseError()
|
||||
or
|
||||
propertyName = DataFlow::PseudoProperties::arrayElement() and
|
||||
result = arrayElement()
|
||||
or
|
||||
propertyName = DataFlow::PseudoProperties::iteratorElement() and
|
||||
result = iteratorElement()
|
||||
or
|
||||
propertyName = DataFlow::PseudoProperties::setElement() and
|
||||
result = setElement()
|
||||
or
|
||||
propertyName = DataFlow::PseudoProperties::mapValueAll() and
|
||||
result = mapValueAll()
|
||||
or
|
||||
propertyName = DataFlow::PseudoProperties::mapValueUnknownKey() and
|
||||
result = mapValueWithUnknownKey()
|
||||
or
|
||||
exists(string key |
|
||||
propertyName = DataFlow::PseudoProperties::mapValueKey(key) and
|
||||
result = mapValueWithKnownKey(key)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the content set corresponding to the given property name, where legacy pseudo-properties
|
||||
* are mapped to their corresponding content sets (which are no longer seen as property names).
|
||||
*/
|
||||
bindingset[propertyName]
|
||||
ContentSet fromLegacyProperty(string propertyName) {
|
||||
result = fromLegacyPseudoProperty(propertyName)
|
||||
or
|
||||
not exists(fromLegacyPseudoProperty(propertyName)) and
|
||||
(
|
||||
// In case a map-value key was contributed via a SharedFlowStep, but we don't have a ContentSet for it,
|
||||
// convert it to the unknown key.
|
||||
if DataFlow::PseudoProperties::isMapValueKey(propertyName)
|
||||
then result = mapValueWithUnknownKey()
|
||||
else result = property(propertyName)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a content set that reads from all captured variables stored on a function.
|
||||
*/
|
||||
ContentSet anyCapturedContent() { result = Private::MkAnyCapturedContent() }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
private import javascript
|
||||
private import codeql.dataflow.internal.DataFlowImplConsistency
|
||||
private import sharedlib.DataFlowArg
|
||||
private import semmle.javascript.dataflow.internal.DataFlowPrivate
|
||||
private import semmle.javascript.dataflow.internal.DataFlowNode
|
||||
|
||||
private module ConsistencyConfig implements InputSig<Location, JSDataFlow> {
|
||||
private predicate isAmbientNode(DataFlow::Node node) {
|
||||
exists(AstNode n | n.isAmbient() |
|
||||
node = TValueNode(n) or
|
||||
node = TThisNode(n) or
|
||||
node = TReflectiveParametersNode(n) or
|
||||
node = TPropNode(n) or
|
||||
node = TFunctionSelfReferenceNode(n) or
|
||||
node = TExceptionalFunctionReturnNode(n) or
|
||||
node = TExprPostUpdateNode(n) or
|
||||
node = TExceptionalInvocationReturnNode(n) or
|
||||
node = TDestructuredModuleImportNode(n)
|
||||
)
|
||||
}
|
||||
|
||||
predicate missingLocationExclude(DataFlow::Node n) {
|
||||
n instanceof FlowSummaryNode
|
||||
or
|
||||
n instanceof FlowSummaryIntermediateAwaitStoreNode
|
||||
or
|
||||
n instanceof FlowSummaryDynamicParameterArrayNode
|
||||
or
|
||||
n instanceof FlowSummaryDefaultExceptionalReturn
|
||||
or
|
||||
n instanceof GenericSynthesizedNode
|
||||
or
|
||||
n = DataFlow::globalAccessPathRootPseudoNode()
|
||||
}
|
||||
|
||||
predicate uniqueNodeLocationExclude(DataFlow::Node n) { missingLocationExclude(n) }
|
||||
|
||||
predicate uniqueEnclosingCallableExclude(DataFlow::Node n) { isAmbientNode(n) }
|
||||
|
||||
predicate uniqueCallEnclosingCallableExclude(DataFlowCall call) {
|
||||
isAmbientNode(call.asOrdinaryCall()) or
|
||||
isAmbientNode(call.asAccessorCall())
|
||||
}
|
||||
|
||||
predicate argHasPostUpdateExclude(ArgumentNode node) {
|
||||
// Side-effects directly on these can't propagate back to the caller, and for longer access paths it's too imprecise
|
||||
node instanceof TStaticArgumentArrayNode or
|
||||
node instanceof TDynamicArgumentArrayNode
|
||||
}
|
||||
|
||||
predicate reverseReadExclude(DataFlow::Node node) {
|
||||
node instanceof FlowSummaryDynamicParameterArrayNode
|
||||
}
|
||||
}
|
||||
|
||||
module Consistency = MakeConsistency<Location, JSDataFlow, JSTaintFlow, ConsistencyConfig>;
|
||||
@@ -5,38 +5,157 @@
|
||||
*/
|
||||
|
||||
private import javascript
|
||||
private import codeql.util.Boolean
|
||||
private import semmle.javascript.dataflow.internal.AdditionalFlowInternal
|
||||
private import semmle.javascript.dataflow.internal.Contents::Private
|
||||
private import semmle.javascript.dataflow.internal.sharedlib.DataFlowImplCommon as DataFlowImplCommon
|
||||
private import semmle.javascript.dataflow.internal.sharedlib.Ssa as Ssa2
|
||||
private import semmle.javascript.dataflow.internal.DataFlowPrivate as DataFlowPrivate
|
||||
private import semmle.javascript.dataflow.internal.sharedlib.FlowSummaryImpl as FlowSummaryImpl
|
||||
private import semmle.javascript.dataflow.internal.FlowSummaryPrivate as FlowSummaryPrivate
|
||||
private import semmle.javascript.dataflow.internal.VariableCapture as VariableCapture
|
||||
private import semmle.javascript.dataflow.internal.VariableOrThis
|
||||
|
||||
cached
|
||||
private module Cached {
|
||||
private Content dynamicArgumentsContent() {
|
||||
result.asArrayIndex() = [0 .. 10]
|
||||
or
|
||||
result.isUnknownArrayElement()
|
||||
}
|
||||
|
||||
/**
|
||||
* The raw data type underlying `DataFlow::Node`.
|
||||
*/
|
||||
cached
|
||||
newtype TNode =
|
||||
TValueNode(AST::ValueNode nd) or
|
||||
/** An SSA node from the legacy SSA library */
|
||||
TSsaDefNode(SsaDefinition d) or
|
||||
/** Use of a variable or 'this', with flow from a post-update node (from an earlier use) */
|
||||
TSsaUseNode(ControlFlowNode use) { use = any(Ssa2::SsaConfig::SourceVariable v).getAUse() } or
|
||||
/** Phi-read node (new SSA library). Ordinary phi nodes are represented by TSsaDefNode. */
|
||||
TSsaPhiReadNode(Ssa2::PhiReadNode phi) or
|
||||
/** Input to a phi node (new SSA library) */
|
||||
TSsaInputNode(Ssa2::SsaInputNode input) or
|
||||
TCapturedVariableNode(LocalVariable v) { v.isCaptured() } or
|
||||
TPropNode(@property p) or
|
||||
TRestPatternNode(DestructuringPattern dp, Expr rest) { rest = dp.getRest() } or
|
||||
TElementPatternNode(ArrayPattern ap, Expr p) { p = ap.getElement(_) } or
|
||||
TElementNode(ArrayExpr arr, Expr e) { e = arr.getAnElement() } or
|
||||
TReflectiveCallNode(MethodCallExpr ce, string kind) {
|
||||
ce.getMethodName() = kind and
|
||||
(kind = "call" or kind = "apply")
|
||||
} or
|
||||
TThisNode(StmtContainer f) { f.(Function).getThisBinder() = f or f instanceof TopLevel } or
|
||||
TFunctionSelfReferenceNode(Function f) or
|
||||
TStaticArgumentArrayNode(InvokeExpr node) or
|
||||
TDynamicArgumentArrayNode(InvokeExpr node) { node.isSpreadArgument(_) } or
|
||||
TStaticParameterArrayNode(Function f) {
|
||||
f.getAParameter().isRestParameter() or f.usesArgumentsObject()
|
||||
} or
|
||||
TDynamicParameterArrayNode(Function f) or
|
||||
/** Data about to be stored in the rest parameter object. Needed for shifting array indices. */
|
||||
TRestParameterStoreNode(Function f, Content storeContent) {
|
||||
f.getRestParameter().getIndex() > 0 and
|
||||
storeContent = dynamicArgumentsContent()
|
||||
} or
|
||||
/** Data about to be stored in the dynamic argument array of an invocation. Needed for shifting array indices. */
|
||||
TDynamicArgumentStoreNode(InvokeExpr invoke, Content storeContent) {
|
||||
invoke.isSpreadArgument(_) and
|
||||
storeContent = dynamicArgumentsContent()
|
||||
} or
|
||||
TApplyCallTaintNode(MethodCallExpr node) {
|
||||
node.getMethodName() = "apply" and exists(node.getArgument(1))
|
||||
} or
|
||||
TDestructuredModuleImportNode(ImportDeclaration decl) {
|
||||
exists(decl.getASpecifier().getImportedName())
|
||||
} or
|
||||
THtmlAttributeNode(HTML::Attribute attr) or
|
||||
TXmlAttributeNode(XmlAttribute attr) or
|
||||
TFunctionReturnNode(Function f) or
|
||||
TExceptionalFunctionReturnNode(Function f) or
|
||||
TExceptionalInvocationReturnNode(InvokeExpr e) or
|
||||
TGlobalAccessPathRoot() or
|
||||
TTemplatePlaceholderTag(Templating::TemplatePlaceholderTag tag) or
|
||||
TReflectiveParametersNode(Function f) { f.usesArgumentsObject() } or
|
||||
TExprPostUpdateNode(AST::ValueNode e) {
|
||||
e = any(InvokeExpr invoke).getAnArgument() or
|
||||
e = any(PropAccess access).getBase() or
|
||||
e = any(DestructuringPattern pattern) or
|
||||
e = any(InvokeExpr invoke).getCallee() or
|
||||
// We have read steps out of the await operand, so it technically needs a post-update
|
||||
e = any(AwaitExpr a).getOperand() or
|
||||
e = any(Function f) or // functions are passed as their own self-reference argument
|
||||
// The RHS of an assignment can be an argument to a setter-call, so it needs a post-update node
|
||||
e = any(Assignment asn | asn.getTarget() instanceof PropAccess).getRhs()
|
||||
} or
|
||||
TNewCallThisArgument(NewExpr e) or
|
||||
TImplicitThisUse(ImplicitThisUse use, Boolean isPost) or
|
||||
TFlowSummaryNode(FlowSummaryImpl::Private::SummaryNode sn) or
|
||||
TFlowSummaryDynamicParameterArrayNode(FlowSummaryImpl::Public::SummarizedCallable callable) or
|
||||
TFlowSummaryIntermediateAwaitStoreNode(FlowSummaryImpl::Private::SummaryNode sn) {
|
||||
// NOTE: This dependency goes through the 'Steps' module whose instantiation depends on the call graph,
|
||||
// but the specific predicate we're referering to does not use that information.
|
||||
// So it doesn't cause negative recursion but it might look a bit surprising.
|
||||
FlowSummaryPrivate::Steps::summaryStoreStep(sn, MkAwaited(), _)
|
||||
} or
|
||||
TFlowSummaryDefaultExceptionalReturn(FlowSummaryImpl::Public::SummarizedCallable callable) {
|
||||
not DataFlowPrivate::mentionsExceptionalReturn(callable)
|
||||
} or
|
||||
TSynthCaptureNode(VariableCapture::VariableCaptureOutput::SynthesizedCaptureNode node) or
|
||||
TGenericSynthesizedNode(AstNode node, string tag, DataFlowPrivate::DataFlowCallable container) {
|
||||
any(AdditionalFlowInternal flow).needsSynthesizedNode(node, tag, container)
|
||||
} or
|
||||
TForbiddenRecursionGuard() {
|
||||
none() and
|
||||
// We want to prune irrelevant models before materialising data flow nodes, so types contributed
|
||||
// directly from CodeQL must expose their pruning info without depending on data flow nodes.
|
||||
(any(ModelInput::TypeModel tm).isTypeUsed("") implies any())
|
||||
}
|
||||
|
||||
cached
|
||||
private module Backref {
|
||||
cached
|
||||
predicate backref() {
|
||||
DataFlowImplCommon::forceCachingInSameStage() or
|
||||
exists(any(DataFlow::Node node).toString()) or
|
||||
exists(any(DataFlow::Node node).getContainer()) or
|
||||
any(DataFlow::Node node).hasLocationInfo(_, _, _, _, _) or
|
||||
exists(any(Content c).toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
import Cached
|
||||
|
||||
private class TEarlyStageNode =
|
||||
TValueNode or TSsaDefNode or TCapturedVariableNode or TPropNode or TRestPatternNode or
|
||||
TElementPatternNode or TElementNode or TReflectiveCallNode or TThisNode or
|
||||
TFunctionSelfReferenceNode or TDestructuredModuleImportNode or THtmlAttributeNode or
|
||||
TFunctionReturnNode or TExceptionalFunctionReturnNode or TExceptionalInvocationReturnNode or
|
||||
TGlobalAccessPathRoot or TTemplatePlaceholderTag or TReflectiveParametersNode or
|
||||
TExprPostUpdateNode or TNewCallThisArgument or TStaticArgumentArrayNode or
|
||||
TDynamicArgumentArrayNode or TStaticParameterArrayNode or TDynamicParameterArrayNode or
|
||||
TImplicitThisUse;
|
||||
|
||||
/**
|
||||
* The raw data type underlying `DataFlow::Node`.
|
||||
* A data-flow node that is not a flow summary node.
|
||||
*
|
||||
* This node exists to avoid an unwanted dependency on flow summaries in some parts of the codebase
|
||||
* that should not depend on them.
|
||||
*
|
||||
* In particular, this dependency chain must not result in negative recursion:
|
||||
* - Flow summaries can only be created after pruning irrelevant flow summaries
|
||||
* - To prune irrelevant flow summaries, we must know which packages are imported
|
||||
* - To know which packages are imported, module systems must be evaluated
|
||||
* - The AMD and NodeJS module systems rely on data flow to find calls to `require` and similar.
|
||||
* These module systems must therefore use `EarlyStageNode` instead of `DataFlow::Node`.
|
||||
*/
|
||||
cached
|
||||
newtype TNode =
|
||||
TValueNode(AST::ValueNode nd) or
|
||||
TSsaDefNode(SsaDefinition d) or
|
||||
TCapturedVariableNode(LocalVariable v) { v.isCaptured() } or
|
||||
TPropNode(@property p) or
|
||||
TRestPatternNode(DestructuringPattern dp, Expr rest) { rest = dp.getRest() } or
|
||||
TElementPatternNode(ArrayPattern ap, Expr p) { p = ap.getElement(_) } or
|
||||
TElementNode(ArrayExpr arr, Expr e) { e = arr.getAnElement() } or
|
||||
TReflectiveCallNode(MethodCallExpr ce, string kind) {
|
||||
ce.getMethodName() = kind and
|
||||
(kind = "call" or kind = "apply")
|
||||
} or
|
||||
TThisNode(StmtContainer f) { f.(Function).getThisBinder() = f or f instanceof TopLevel } or
|
||||
TDestructuredModuleImportNode(ImportDeclaration decl) {
|
||||
exists(decl.getASpecifier().getImportedName())
|
||||
} or
|
||||
THtmlAttributeNode(HTML::Attribute attr) or
|
||||
TXmlAttributeNode(XmlAttribute attr) or
|
||||
TFunctionReturnNode(Function f) or
|
||||
TExceptionalFunctionReturnNode(Function f) or
|
||||
TExceptionalInvocationReturnNode(InvokeExpr e) or
|
||||
TGlobalAccessPathRoot() or
|
||||
TTemplatePlaceholderTag(Templating::TemplatePlaceholderTag tag) or
|
||||
TReflectiveParametersNode(Function f) or
|
||||
TForbiddenRecursionGuard() {
|
||||
none() and
|
||||
// We want to prune irrelevant models before materialising data flow nodes, so types contributed
|
||||
// directly from CodeQL must expose their pruning info without depending on data flow nodes.
|
||||
(any(ModelInput::TypeModel tm).isTypeUsed("") implies any())
|
||||
}
|
||||
class EarlyStageNode extends TEarlyStageNode {
|
||||
/** Gets a string representation of this data flow node. */
|
||||
string toString() { result = this.(DataFlow::Node).toString() }
|
||||
|
||||
/** Gets the location of this data flow node. */
|
||||
Location getLocation() { result = this.(DataFlow::Node).getLocation() }
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -30,20 +30,36 @@ predicate returnExpr(Function f, DataFlow::Node source, DataFlow::Node sink) {
|
||||
not f = any(SetterMethodDeclaration decl).getBody()
|
||||
}
|
||||
|
||||
/**
|
||||
* A step from a post-update node to the local sources of the corresponding pre-update node.
|
||||
*
|
||||
* This ensures that `getPostUpdateNode()` can be used in place of `getALocalSource()` when generating
|
||||
* store steps, and the resulting step will work in both data flow analyses.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate legacyPostUpdateStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
exists(DataFlow::Node node |
|
||||
pred = node.getPostUpdateNode() and
|
||||
succ = node.getALocalSource()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow in one step from `pred` to `succ`, taking
|
||||
* additional steps from the configuration into account.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate localFlowStep(
|
||||
deprecated predicate localFlowStep(
|
||||
DataFlow::Node pred, DataFlow::Node succ, DataFlow::Configuration configuration,
|
||||
FlowLabel predlbl, FlowLabel succlbl
|
||||
) {
|
||||
pred = succ.getAPredecessor() and predlbl = succlbl
|
||||
or
|
||||
DataFlow::SharedFlowStep::step(pred, succ) and predlbl = succlbl
|
||||
legacyPostUpdateStep(pred, succ) and predlbl = succlbl
|
||||
or
|
||||
DataFlow::SharedFlowStep::step(pred, succ, predlbl, succlbl)
|
||||
DataFlow::LegacyFlowStep::step(pred, succ) and predlbl = succlbl
|
||||
or
|
||||
DataFlow::LegacyFlowStep::step(pred, succ, predlbl, succlbl)
|
||||
or
|
||||
exists(boolean vp | configuration.isAdditionalFlowStep(pred, succ, vp) |
|
||||
vp = true and
|
||||
@@ -529,9 +545,9 @@ class Boolean extends boolean {
|
||||
/**
|
||||
* A summary of an inter-procedural data flow path.
|
||||
*/
|
||||
newtype TPathSummary =
|
||||
deprecated newtype TPathSummary =
|
||||
/** A summary of an inter-procedural data flow path. */
|
||||
MkPathSummary(Boolean hasReturn, Boolean hasCall, FlowLabel start, FlowLabel end)
|
||||
deprecated MkPathSummary(Boolean hasReturn, Boolean hasCall, FlowLabel start, FlowLabel end)
|
||||
|
||||
/**
|
||||
* A summary of an inter-procedural data flow path.
|
||||
@@ -544,7 +560,7 @@ newtype TPathSummary =
|
||||
* We only want to build properly matched call/return sequences, so if a path has both
|
||||
* call steps and return steps, all return steps must precede all call steps.
|
||||
*/
|
||||
class PathSummary extends TPathSummary {
|
||||
deprecated class PathSummary extends TPathSummary {
|
||||
Boolean hasReturn;
|
||||
Boolean hasCall;
|
||||
FlowLabel start;
|
||||
@@ -618,7 +634,7 @@ class PathSummary extends TPathSummary {
|
||||
}
|
||||
}
|
||||
|
||||
module PathSummary {
|
||||
deprecated module PathSummary {
|
||||
/**
|
||||
* Gets a summary describing a path without any calls or returns.
|
||||
*/
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user