mirror of
https://github.com/github/codeql.git
synced 2026-05-16 04:09:27 +02:00
Compare commits
663 Commits
codeql-cli
...
henrymerce
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6ae028ebb3 | ||
|
|
7fef1a8817 | ||
|
|
78cb53449d | ||
|
|
6c8dfb253d | ||
|
|
d7505e41db | ||
|
|
6e5d56cbcb | ||
|
|
474ddc9bc8 | ||
|
|
c945ece80d | ||
|
|
55b0dbd7b8 | ||
|
|
a66743192e | ||
|
|
a416a089b4 | ||
|
|
87ba9d55b6 | ||
|
|
649bd03db6 | ||
|
|
9470a99092 | ||
|
|
83f0fad014 | ||
|
|
f555c0642e | ||
|
|
f0b0845f9f | ||
|
|
9ffc38f5b1 | ||
|
|
a6fd7a3203 | ||
|
|
993abd4499 | ||
|
|
f0d5a91d59 | ||
|
|
d035937083 | ||
|
|
a505eb6922 | ||
|
|
3c7d9c3c4b | ||
|
|
2160edc789 | ||
|
|
1f928c2910 | ||
|
|
8ae01789b1 | ||
|
|
500097ca76 | ||
|
|
4b9532c6f7 | ||
|
|
f6e1ea5b2a | ||
|
|
2524f23a46 | ||
|
|
aa7e9f0b56 | ||
|
|
69c7c83bc2 | ||
|
|
cf0e464ab9 | ||
|
|
6a97d02247 | ||
|
|
161e5679a7 | ||
|
|
305bfaba2d | ||
|
|
d9d82fc56a | ||
|
|
7c54512859 | ||
|
|
707f532e10 | ||
|
|
fd596ebbbb | ||
|
|
c1c9f963b9 | ||
|
|
07ffa9f1ae | ||
|
|
e54c925b70 | ||
|
|
b94658fd52 | ||
|
|
23d9e2646a | ||
|
|
dcb185b659 | ||
|
|
32be53bf72 | ||
|
|
3fe715abb6 | ||
|
|
35e620a19c | ||
|
|
ce27831b76 | ||
|
|
4af7bc8090 | ||
|
|
d3d56fb0af | ||
|
|
9b39163411 | ||
|
|
43045c1f03 | ||
|
|
a43167faf7 | ||
|
|
2131f35801 | ||
|
|
b55921a391 | ||
|
|
d01d7eea82 | ||
|
|
a666a692f9 | ||
|
|
634041d2d7 | ||
|
|
bc448fe067 | ||
|
|
d41ea6c799 | ||
|
|
121ffbbfa8 | ||
|
|
ecfad6b5c7 | ||
|
|
4627799c93 | ||
|
|
e5ec1e105c | ||
|
|
ac0f2d37db | ||
|
|
55615586ee | ||
|
|
26288ad391 | ||
|
|
ca992f2d3c | ||
|
|
47ab9ba81b | ||
|
|
1fd244923b | ||
|
|
543f5916c4 | ||
|
|
88d1539d43 | ||
|
|
7068a265a6 | ||
|
|
d3244fe298 | ||
|
|
dbe656fe6a | ||
|
|
fd09883bfe | ||
|
|
899e52a68a | ||
|
|
a1d227dbbb | ||
|
|
967765342e | ||
|
|
1ab4af275d | ||
|
|
72547b89e6 | ||
|
|
a5dec5b4aa | ||
|
|
2b7cc15757 | ||
|
|
e450b61464 | ||
|
|
60b23dc505 | ||
|
|
6ef8e51bcf | ||
|
|
f7832adfb8 | ||
|
|
567516471c | ||
|
|
bd64dda4c3 | ||
|
|
3900698b41 | ||
|
|
a896e1522d | ||
|
|
41fbce0ad0 | ||
|
|
a5d18f9b68 | ||
|
|
fe0a494bab | ||
|
|
4bb8b6c992 | ||
|
|
15c611e22f | ||
|
|
3740aba4a8 | ||
|
|
9b405144ff | ||
|
|
94bf3467b7 | ||
|
|
8b9c6712d1 | ||
|
|
40186db768 | ||
|
|
48d24b2264 | ||
|
|
046a37b834 | ||
|
|
c3ef6841d0 | ||
|
|
3be229f097 | ||
|
|
5974af661e | ||
|
|
ba98b08001 | ||
|
|
ebfb1faf77 | ||
|
|
a7ca065411 | ||
|
|
b5633625b3 | ||
|
|
cccca879d9 | ||
|
|
56515c5708 | ||
|
|
cf860f1dac | ||
|
|
05f290f734 | ||
|
|
c5d6792c1e | ||
|
|
6ce160c51c | ||
|
|
724c3e00e0 | ||
|
|
3fafb47b16 | ||
|
|
8cf8b704c5 | ||
|
|
40d02e7e32 | ||
|
|
0df7e9fa4e | ||
|
|
e3bdebf7a0 | ||
|
|
2ace10fcdf | ||
|
|
a45c415c5b | ||
|
|
691a316460 | ||
|
|
064568c36d | ||
|
|
653c900d62 | ||
|
|
c51e951d1e | ||
|
|
209fe8d7e5 | ||
|
|
e4c3544a3f | ||
|
|
3151aeff48 | ||
|
|
047cd2b706 | ||
|
|
5e3b6fa341 | ||
|
|
ff58d5a7c0 | ||
|
|
9e75a4be34 | ||
|
|
98d73bf474 | ||
|
|
07a20752bc | ||
|
|
50be54385a | ||
|
|
aa9ab41e30 | ||
|
|
708d3870ee | ||
|
|
64f0dfb174 | ||
|
|
4e904dd87d | ||
|
|
b54f74a68a | ||
|
|
5db1984315 | ||
|
|
eed2aee17d | ||
|
|
9f50f67e6d | ||
|
|
b19fd7bb72 | ||
|
|
0db54e08b8 | ||
|
|
74fd2c1c38 | ||
|
|
4fdbda3543 | ||
|
|
6e71c68f33 | ||
|
|
1ffa15ea96 | ||
|
|
8abc37fba3 | ||
|
|
ca435763b0 | ||
|
|
cd7b013a0c | ||
|
|
749dfe4358 | ||
|
|
b92af8bcec | ||
|
|
fa469587c1 | ||
|
|
c0511ca9f9 | ||
|
|
700a2dbb93 | ||
|
|
be9908df87 | ||
|
|
384d0212b1 | ||
|
|
71e1218ad5 | ||
|
|
9db19613d6 | ||
|
|
fc01e5607f | ||
|
|
b8194bd1f8 | ||
|
|
2de230ea75 | ||
|
|
249e431e87 | ||
|
|
7d62e33feb | ||
|
|
27b41c2016 | ||
|
|
c747914ef2 | ||
|
|
b8b42eaea3 | ||
|
|
61125b4bf2 | ||
|
|
3179546b8c | ||
|
|
4ef9a6cf2a | ||
|
|
4b6a59a126 | ||
|
|
9678534f25 | ||
|
|
1b6cb340d3 | ||
|
|
aae69c6537 | ||
|
|
aa35fcafeb | ||
|
|
27fd46b855 | ||
|
|
6730396ad6 | ||
|
|
6c3f44bba8 | ||
|
|
2b946aee5a | ||
|
|
b7df18b97e | ||
|
|
064d89735b | ||
|
|
03922aa1f5 | ||
|
|
f6fa1276a6 | ||
|
|
05a138694d | ||
|
|
7f1affa122 | ||
|
|
182d435dc6 | ||
|
|
7e9913a8a7 | ||
|
|
a9c51e7300 | ||
|
|
c9537f2639 | ||
|
|
817a142abc | ||
|
|
cb195a0dc4 | ||
|
|
be5b7bb4c4 | ||
|
|
92a5a2a06a | ||
|
|
d5f1c19152 | ||
|
|
c9ec983cd8 | ||
|
|
39591687ba | ||
|
|
76c9b6466e | ||
|
|
91152d3a65 | ||
|
|
191962f64c | ||
|
|
bf5851f1c2 | ||
|
|
bdbf5a4fae | ||
|
|
1a507ff497 | ||
|
|
50f2557dd2 | ||
|
|
3f1e81533c | ||
|
|
c8eeb5f73e | ||
|
|
339c4c6ce0 | ||
|
|
7d9ebaf9d8 | ||
|
|
768be9ec2c | ||
|
|
23eb4d2009 | ||
|
|
75aa1e8a3b | ||
|
|
02d60a26eb | ||
|
|
1c56c30eba | ||
|
|
59d87e2570 | ||
|
|
f94a7fc2f0 | ||
|
|
5a420f2bae | ||
|
|
8ed28157e1 | ||
|
|
f65ec97ac2 | ||
|
|
8880b38b1f | ||
|
|
b6007cf324 | ||
|
|
173012578e | ||
|
|
54725ccbb9 | ||
|
|
61d69f2cc8 | ||
|
|
2b4296feb1 | ||
|
|
cf565970e3 | ||
|
|
1068edeb28 | ||
|
|
2c70106d2d | ||
|
|
bbdd7c9b57 | ||
|
|
7affbfc6cb | ||
|
|
6255662114 | ||
|
|
e6f81bcf0b | ||
|
|
6ee5cdf2b2 | ||
|
|
69ce24d4b8 | ||
|
|
65ea01e145 | ||
|
|
ab7d257569 | ||
|
|
2ac7b4bab4 | ||
|
|
058f3af4b2 | ||
|
|
cbaee937d0 | ||
|
|
cfc950f803 | ||
|
|
6a93099b64 | ||
|
|
0e0b18c214 | ||
|
|
0addce5be4 | ||
|
|
ae2c122159 | ||
|
|
24f76f9a17 | ||
|
|
2120868939 | ||
|
|
2c5da85e3b | ||
|
|
5646af56dd | ||
|
|
0d42e546a0 | ||
|
|
44bb41e84b | ||
|
|
c90dc62cc4 | ||
|
|
aec18e7123 | ||
|
|
5aff5c3254 | ||
|
|
066f83630d | ||
|
|
3880b48736 | ||
|
|
ca5e3b4489 | ||
|
|
307bef0ec3 | ||
|
|
52e2a69db9 | ||
|
|
32b5c7fe06 | ||
|
|
034d0a7b10 | ||
|
|
552e11de19 | ||
|
|
669e207600 | ||
|
|
a4d7bfbb2b | ||
|
|
d1cc2cc999 | ||
|
|
5157236999 | ||
|
|
bdfdcbd673 | ||
|
|
10518744cf | ||
|
|
c61dec1dff | ||
|
|
16d058f498 | ||
|
|
5d163b4c15 | ||
|
|
25de82c78c | ||
|
|
bec0064396 | ||
|
|
6d952bda27 | ||
|
|
8737c1442b | ||
|
|
bb423828de | ||
|
|
f2241e04e5 | ||
|
|
988c1bc044 | ||
|
|
19b7d46099 | ||
|
|
4ba9d10b9a | ||
|
|
1637b72092 | ||
|
|
e24e5b13f5 | ||
|
|
3604557e62 | ||
|
|
4685fc0a32 | ||
|
|
68eba11dbf | ||
|
|
aebf7bdff4 | ||
|
|
5a89fa3f67 | ||
|
|
885d26805f | ||
|
|
9a85b761a1 | ||
|
|
39977e9a43 | ||
|
|
0381190a30 | ||
|
|
214505c4dc | ||
|
|
5d62a56ed8 | ||
|
|
d18c1602cd | ||
|
|
d29fdda779 | ||
|
|
5d5cd4fde5 | ||
|
|
f074564dc1 | ||
|
|
636fe73f40 | ||
|
|
fc9d219057 | ||
|
|
de0bbc8826 | ||
|
|
9b13834d28 | ||
|
|
d3e0e84c37 | ||
|
|
4526a1dd2f | ||
|
|
cd85cf1645 | ||
|
|
500ea12224 | ||
|
|
54f00de3e0 | ||
|
|
04a3c3d29c | ||
|
|
416aa49d99 | ||
|
|
c69a051292 | ||
|
|
aec0e9808b | ||
|
|
902bade5ae | ||
|
|
1834403148 | ||
|
|
8c09032d1d | ||
|
|
932ee968e0 | ||
|
|
76e1e4d668 | ||
|
|
4c0f54f5d3 | ||
|
|
e253855999 | ||
|
|
0be0929693 | ||
|
|
e44f1813fa | ||
|
|
e4ed050c87 | ||
|
|
2c58643fd1 | ||
|
|
3ece8c3a01 | ||
|
|
e6d9cd1905 | ||
|
|
c1726ed868 | ||
|
|
d59ccb7687 | ||
|
|
89225e222c | ||
|
|
71d25c1f8b | ||
|
|
7b9ca7171a | ||
|
|
dacc21d0b5 | ||
|
|
cd8155c201 | ||
|
|
07a96c3596 | ||
|
|
09bb3001d6 | ||
|
|
7ba0939239 | ||
|
|
91caa13f48 | ||
|
|
d69ecde5c1 | ||
|
|
7e11d8ed07 | ||
|
|
d1427fcd93 | ||
|
|
96b7f75905 | ||
|
|
a8186be2fa | ||
|
|
ad39bfb2ff | ||
|
|
361bee851a | ||
|
|
4ff2c6d85a | ||
|
|
7c9a6064cf | ||
|
|
4601eb9c7c | ||
|
|
6ffeaf8c2a | ||
|
|
38b0f743cb | ||
|
|
36ad6b3432 | ||
|
|
221aebc833 | ||
|
|
355edcb136 | ||
|
|
0543e34812 | ||
|
|
e0c7f32282 | ||
|
|
063733ad52 | ||
|
|
ab85b2c2d2 | ||
|
|
0080357153 | ||
|
|
0ba610f7db | ||
|
|
e7b43e50b6 | ||
|
|
eac69c1674 | ||
|
|
b434a0f395 | ||
|
|
1c84455a6d | ||
|
|
d86705fe7a | ||
|
|
1b3d69d617 | ||
|
|
c235462f7d | ||
|
|
b899229298 | ||
|
|
9ae503a5a8 | ||
|
|
20e19ec467 | ||
|
|
9071ba2f99 | ||
|
|
fcd532522d | ||
|
|
20d1b24e9c | ||
|
|
89e56707c3 | ||
|
|
baf0917524 | ||
|
|
0d20a4cb4a | ||
|
|
f948ef8f27 | ||
|
|
527c41520e | ||
|
|
6fc14976cf | ||
|
|
b3497191b1 | ||
|
|
87b738d48c | ||
|
|
b4f9b1590d | ||
|
|
3f3962f7a9 | ||
|
|
682b246441 | ||
|
|
718f6eb3fd | ||
|
|
b36593a76b | ||
|
|
0ffa720d3b | ||
|
|
c257f6617f | ||
|
|
7bc461aeb2 | ||
|
|
0e059cea56 | ||
|
|
11f35a5193 | ||
|
|
62746bbbac | ||
|
|
d0b70d15f0 | ||
|
|
cb25f2ab20 | ||
|
|
b83c949109 | ||
|
|
c29014f122 | ||
|
|
9c53e39394 | ||
|
|
1c100bbbc2 | ||
|
|
ee2d18afd8 | ||
|
|
b55817a5b2 | ||
|
|
ac77a8b8a8 | ||
|
|
2f58683f2d | ||
|
|
1d1149f4cd | ||
|
|
e786be06ae | ||
|
|
144d04f3ce | ||
|
|
7c6704a63f | ||
|
|
34280f90b0 | ||
|
|
a9a901d1e2 | ||
|
|
dafec3ceaa | ||
|
|
88db8f562d | ||
|
|
bc41c26354 | ||
|
|
bc1b50788a | ||
|
|
19918e2e57 | ||
|
|
30015ee995 | ||
|
|
9cfbe6feb7 | ||
|
|
b90dd89746 | ||
|
|
5a6e692807 | ||
|
|
9e3cc3b1b2 | ||
|
|
eb8a52ba8d | ||
|
|
419449fb8a | ||
|
|
2409a7899b | ||
|
|
a44aefa6c9 | ||
|
|
7a5d553dd2 | ||
|
|
d5d8b48218 | ||
|
|
3877f03a46 | ||
|
|
5c6f5b7b33 | ||
|
|
e072864948 | ||
|
|
439fe41b0a | ||
|
|
3fc085ff38 | ||
|
|
d0663e5c3a | ||
|
|
bf518f1c90 | ||
|
|
b0e255eb16 | ||
|
|
ab8dc27b26 | ||
|
|
727412b26b | ||
|
|
2804f5cba9 | ||
|
|
f99bf5755c | ||
|
|
f986c15200 | ||
|
|
0c213d0926 | ||
|
|
f12ebe88e6 | ||
|
|
5fa0dd719c | ||
|
|
9a397b6faf | ||
|
|
2e024c3c61 | ||
|
|
4c9de4574a | ||
|
|
2f459d9a72 | ||
|
|
70c302ffbe | ||
|
|
bf0febd9d2 | ||
|
|
9b2f69ca94 | ||
|
|
e96f942269 | ||
|
|
fbfbe70deb | ||
|
|
5a652ab3aa | ||
|
|
23d3343bfb | ||
|
|
8e126603b3 | ||
|
|
ae38bbe03b | ||
|
|
69913c053e | ||
|
|
d2dd19a293 | ||
|
|
fe4ae7e975 | ||
|
|
8fa3ffe125 | ||
|
|
bdfb81064d | ||
|
|
2a8a2832e2 | ||
|
|
d34992d36c | ||
|
|
35b955f694 | ||
|
|
f7a0c98cb3 | ||
|
|
613bf6dca6 | ||
|
|
1d9b3ec8b4 | ||
|
|
348fe8f2fc | ||
|
|
a9986ca72d | ||
|
|
fd27bde4b5 | ||
|
|
15e88471be | ||
|
|
a11d852054 | ||
|
|
183926d9fd | ||
|
|
876123315d | ||
|
|
77da4b0106 | ||
|
|
91fa12b1be | ||
|
|
830f8bfef6 | ||
|
|
24947f27b4 | ||
|
|
fbab8f8539 | ||
|
|
47470e08c3 | ||
|
|
cff56350e0 | ||
|
|
e2af176727 | ||
|
|
0674881ffd | ||
|
|
2a8060102d | ||
|
|
b228b7d17b | ||
|
|
be7d458dc6 | ||
|
|
b4a5346dc3 | ||
|
|
f8a1fb1c35 | ||
|
|
bfef1a200e | ||
|
|
b8e6ad8922 | ||
|
|
a5632b272e | ||
|
|
6cafb281b5 | ||
|
|
b775eb4cf7 | ||
|
|
25095f919e | ||
|
|
42fa3bdb81 | ||
|
|
3015dcd310 | ||
|
|
b620e02000 | ||
|
|
dde8d320f3 | ||
|
|
bd3de23c6e | ||
|
|
703336a77f | ||
|
|
e9aa63b670 | ||
|
|
8e5557eca3 | ||
|
|
d05d4e22ad | ||
|
|
1b285ee792 | ||
|
|
bfc6660795 | ||
|
|
01900d7ca2 | ||
|
|
deca7f3cd6 | ||
|
|
fc2fe6cccb | ||
|
|
fbb5d14263 | ||
|
|
bb6bd9168e | ||
|
|
2a7b4487f1 | ||
|
|
d8c9dba990 | ||
|
|
5f4016be76 | ||
|
|
c5f2c04f16 | ||
|
|
1b4a4ea2fa | ||
|
|
44c5d36e83 | ||
|
|
00cd0644f0 | ||
|
|
2f3cef177b | ||
|
|
c8901b62f5 | ||
|
|
2ba98da107 | ||
|
|
faad466aa8 | ||
|
|
07cfceee19 | ||
|
|
3c0867125b | ||
|
|
97f7cb4dc1 | ||
|
|
898d22d2f4 | ||
|
|
f24af58a60 | ||
|
|
3b666a5646 | ||
|
|
8848ee2d10 | ||
|
|
6bf9345258 | ||
|
|
cc952bd2a4 | ||
|
|
1ab36dc81f | ||
|
|
29dd8470d5 | ||
|
|
0da207a5f9 | ||
|
|
d80313be4f | ||
|
|
debb5691a1 | ||
|
|
fcb8124376 | ||
|
|
9ee893c9c1 | ||
|
|
77fcf3d8a2 | ||
|
|
c08ba1416d | ||
|
|
b1d45a6773 | ||
|
|
4b5a861ee6 | ||
|
|
9b99f56d44 | ||
|
|
ed27c8b13f | ||
|
|
16a2a60b9a | ||
|
|
928a382ad5 | ||
|
|
3db6069372 | ||
|
|
2752b4ba64 | ||
|
|
ff1d0cc4c7 | ||
|
|
66426bf0cc | ||
|
|
8400a3862b | ||
|
|
ce58514453 | ||
|
|
c0b31cbfe7 | ||
|
|
f235a28295 | ||
|
|
175e43d6f2 | ||
|
|
5f189a7e43 | ||
|
|
dcbae8b22b | ||
|
|
1edad03622 | ||
|
|
401e516654 | ||
|
|
26783b6ab0 | ||
|
|
9a9a57716c | ||
|
|
5fa3b17956 | ||
|
|
f33630aab6 | ||
|
|
48910d0597 | ||
|
|
1506ac09e5 | ||
|
|
0117a0fac1 | ||
|
|
0c9d46a7f9 | ||
|
|
32c54628f8 | ||
|
|
e5a703e49c | ||
|
|
15089c4117 | ||
|
|
7012bc05a2 | ||
|
|
54bd36def2 | ||
|
|
13d0efe96d | ||
|
|
805352945e | ||
|
|
10ab1d9b54 | ||
|
|
cd0d2a5692 | ||
|
|
7f5e5fcb99 | ||
|
|
3e715ff52d | ||
|
|
0d0ea0c5e1 | ||
|
|
4ba4de3d41 | ||
|
|
76b768f7e0 | ||
|
|
4631658e5e | ||
|
|
9e3b288f33 | ||
|
|
b26a90e1e6 | ||
|
|
1c4610c722 | ||
|
|
c106b09d49 | ||
|
|
c5595f4cbd | ||
|
|
86e33d9d79 | ||
|
|
03d8aeb7b6 | ||
|
|
a520a51d42 | ||
|
|
e8ea720650 | ||
|
|
dfdfd3c2b7 | ||
|
|
6dc0d691ac | ||
|
|
e3199fbbe2 | ||
|
|
36a4a5081e | ||
|
|
1bc7d68a50 | ||
|
|
12b985be87 | ||
|
|
3ad45f28c9 | ||
|
|
4cee67da75 | ||
|
|
d71adff079 | ||
|
|
b2d95e617d | ||
|
|
b10cf78e17 | ||
|
|
babe744a30 | ||
|
|
d591c519a8 | ||
|
|
4ee2f49f38 | ||
|
|
d8d8b45c6a | ||
|
|
0a17b04650 | ||
|
|
eaee5c2d87 | ||
|
|
3c9c79a550 | ||
|
|
5965035c09 | ||
|
|
a1ab5cc2b8 | ||
|
|
9c08467828 | ||
|
|
4d9f5be2bc | ||
|
|
84f1b11448 | ||
|
|
847687974f | ||
|
|
40cfbab335 | ||
|
|
f40b406a2d | ||
|
|
c9132ca6f8 | ||
|
|
7a229d9381 | ||
|
|
b3ab6efd1d | ||
|
|
3853da0969 | ||
|
|
ecab17a626 | ||
|
|
2dbd762bd9 | ||
|
|
580a24e982 | ||
|
|
46393c33ef | ||
|
|
a00bd7ae02 | ||
|
|
7f0209f72e | ||
|
|
7b003678a9 | ||
|
|
c069a5b4c6 | ||
|
|
258d04178f | ||
|
|
6545d0b53a | ||
|
|
d2d8377e88 | ||
|
|
b95cf94824 | ||
|
|
ec35e0d518 | ||
|
|
77aa9615c0 | ||
|
|
fd8034cd8c | ||
|
|
a8b4fb6fd0 | ||
|
|
72a80e3722 | ||
|
|
145ab17f6b | ||
|
|
f5a2603cc1 | ||
|
|
7a2b69feed | ||
|
|
2930128421 | ||
|
|
91debe8669 | ||
|
|
ba4da72b9e | ||
|
|
4ec78d04f8 | ||
|
|
79f32b0e26 | ||
|
|
289b9e62f9 | ||
|
|
f345e55951 | ||
|
|
673ff901fb | ||
|
|
905b04a6fb | ||
|
|
432a59185f | ||
|
|
cf12b65c80 | ||
|
|
80dcb8da4a | ||
|
|
737f4dff09 | ||
|
|
da805f8242 | ||
|
|
a19304a4a0 | ||
|
|
ac6d4aac9d | ||
|
|
d3412bb0ec | ||
|
|
85f0a627c4 | ||
|
|
c146e044ca | ||
|
|
b6b8a55b37 | ||
|
|
ac00e02855 | ||
|
|
3b5ff73862 | ||
|
|
dc91406ff0 | ||
|
|
e468d49b19 | ||
|
|
4fe2576b9a | ||
|
|
823ed447df |
15
.github/workflows/codeql-analysis.yml
vendored
15
.github/workflows/codeql-analysis.yml
vendored
@@ -2,7 +2,13 @@ name: "Code scanning - action"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- 'rc/*'
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- 'rc/*'
|
||||
schedule:
|
||||
- cron: '0 9 * * 1'
|
||||
|
||||
@@ -14,16 +20,7 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
# We must fetch at least the immediate parents so that if this is
|
||||
# a pull request then we can checkout the head.
|
||||
fetch-depth: 2
|
||||
|
||||
# If this run was triggered by a pull request event, then checkout
|
||||
# the head of the pull request instead of the merge commit.
|
||||
- run: git checkout HEAD^2
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -17,6 +17,9 @@
|
||||
# Byte-compiled python files
|
||||
*.pyc
|
||||
|
||||
# python virtual environment folder
|
||||
.venv/
|
||||
|
||||
# It's useful (though not required) to be able to unpack codeql in the ql checkout itself
|
||||
/codeql/
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
lgtm
|
||||
* A new query (`cpp/unsigned-difference-expression-compared-zero`) is run but not yet displayed on LGTM. The query finds unsigned subtractions used in relational comparisons with the value 0. This query was originally submitted as an experimental query by @ihsinme in https://github.com/github/codeql/pull/4745.
|
||||
@@ -10,6 +10,7 @@
|
||||
+ semmlecode-cpp-queries/Likely Bugs/Underspecified Functions/MistypedFunctionArguments.ql: /Correctness/Dangerous Conversions
|
||||
+ semmlecode-cpp-queries/Security/CWE/CWE-253/HResultBooleanConversion.ql: /Correctness/Dangerous Conversions
|
||||
+ semmlecode-cpp-queries/Likely Bugs/OO/UnsafeUseOfThis.ql: /Correctness/Dangerous Conversions
|
||||
+ semmlecode-cpp-queries/Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql: /Correctness/Dangerous Conversions
|
||||
# Consistent Use
|
||||
+ semmlecode-cpp-queries/Critical/ReturnValueIgnored.ql: /Correctness/Consistent Use
|
||||
+ semmlecode-cpp-queries/Likely Bugs/InconsistentCheckReturnNull.ql: /Correctness/Consistent Use
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
unsigned limit = get_limit();
|
||||
unsigned total = 0;
|
||||
while (limit - total > 0) { // wrong: if `total` is greater than `limit` this will underflow and continue executing the loop.
|
||||
total += get_data();
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>
|
||||
This rule finds relational comparisons between the result of an unsigned subtraction and the value <code>0</code>.
|
||||
Such comparisons are likely to be wrong as the value of an unsigned subtraction can never be negative. So the
|
||||
relational comparison ends up checking whether the result of the subtraction is equal to <code>0</code>.
|
||||
This is probably not what the programmer intended.
|
||||
</p>
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>If a relational comparison is intended, consider casting the result of the subtraction to a signed type.
|
||||
If the intention was to test for equality, consider replacing the relational comparison with an equality test.
|
||||
</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
<sample src="UnsignedDifferenceExpressionComparedZero.c" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>SEI CERT C Coding Standard:
|
||||
<a href="https://wiki.sei.cmu.edu/confluence/display/c/INT02-C.+Understand+integer+conversion+rules">INT02-C. Understand integer conversion rules</a>.
|
||||
</li>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* @name Unsigned difference expression compared to zero
|
||||
* @description A subtraction with an unsigned result can never be negative. Using such an expression in a relational comparison with `0` is likely to be wrong.
|
||||
* @kind problem
|
||||
* @id cpp/unsigned-difference-expression-compared-zero
|
||||
* @problem.severity warning
|
||||
* @precision medium
|
||||
* @tags security
|
||||
* correctness
|
||||
* external/cwe/cwe-191
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.commons.Exclusions
|
||||
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
|
||||
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
|
||||
import semmle.code.cpp.controlflow.Guards
|
||||
|
||||
/** Holds if `sub` is guarded by a condition which ensures that `left >= right`. */
|
||||
pragma[noinline]
|
||||
predicate isGuarded(SubExpr sub, Expr left, Expr right) {
|
||||
exists(GuardCondition guard |
|
||||
guard.controls(sub.getBasicBlock(), true) and
|
||||
guard.ensuresLt(left, right, 0, sub.getBasicBlock(), false)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `sub` will never be negative. */
|
||||
predicate nonNegative(SubExpr sub) {
|
||||
not exprMightOverflowNegatively(sub.getFullyConverted())
|
||||
or
|
||||
// The subtraction is guarded by a check of the form `left >= right`.
|
||||
exists(GVN left, GVN right |
|
||||
// This is basically a poor man's version of a directional unbind operator.
|
||||
strictcount([left, globalValueNumber(sub.getLeftOperand())]) = 1 and
|
||||
strictcount([right, globalValueNumber(sub.getRightOperand())]) = 1 and
|
||||
isGuarded(sub, left.getAnExpr(), right.getAnExpr())
|
||||
)
|
||||
}
|
||||
|
||||
from RelationalOperation ro, SubExpr sub
|
||||
where
|
||||
not isFromMacroDefinition(ro) and
|
||||
not isFromMacroDefinition(sub) and
|
||||
ro.getLesserOperand().getValue().toInt() = 0 and
|
||||
ro.getGreaterOperand() = sub and
|
||||
sub.getFullyConverted().getUnspecifiedType().(IntegralType).isUnsigned() and
|
||||
not nonNegative(sub)
|
||||
select ro, "Unsigned subtraction can never be negative."
|
||||
@@ -353,7 +353,9 @@ class InitializationFunction extends Function {
|
||||
// Destination range is zeroed out on failure, assuming first two parameters are valid
|
||||
"memcpy_s",
|
||||
// This zeroes the memory unconditionally
|
||||
"SeCreateAccessState"
|
||||
"SeCreateAccessState",
|
||||
// Argument initialization is optional, but always succeeds
|
||||
"KeGetCurrentProcessorNumberEx"
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
// BAD: the memset call will probably be removed.
|
||||
void getPassword(void) {
|
||||
char pwd[64];
|
||||
if (GetPassword(pwd, sizeof(pwd))) {
|
||||
/* Checking of password, secure operations, etc. */
|
||||
}
|
||||
memset(pwd, 0, sizeof(pwd));
|
||||
}
|
||||
// GOOD: in this case the memset will not be removed.
|
||||
void getPassword(void) {
|
||||
char pwd[64];
|
||||
|
||||
if (retrievePassword(pwd, sizeof(pwd))) {
|
||||
/* Checking of password, secure operations, etc. */
|
||||
}
|
||||
memset_s(pwd, 0, sizeof(pwd));
|
||||
}
|
||||
// GOOD: in this case the memset will not be removed.
|
||||
void getPassword(void) {
|
||||
char pwd[64];
|
||||
if (retrievePassword(pwd, sizeof(pwd))) {
|
||||
/* Checking of password, secure operations, etc. */
|
||||
}
|
||||
SecureZeroMemory(pwd, sizeof(pwd));
|
||||
}
|
||||
// GOOD: in this case the memset will not be removed.
|
||||
void getPassword(void) {
|
||||
char pwd[64];
|
||||
if (retrievePassword(pwd, sizeof(pwd))) {
|
||||
/* Checking of password, secure operations, etc. */
|
||||
}
|
||||
#pragma optimize("", off)
|
||||
memset(pwd, 0, sizeof(pwd));
|
||||
#pragma optimize("", on)
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>Compiler optimization will exclude the cleaning of private information.
|
||||
Using the <code>memset</code> function to clear private data in a variable that has no subsequent use is potentially dangerous, since the compiler can remove the call.
|
||||
For some compilers, optimization is also possible when using calls to free memory after the <code>memset</code> function.</p>
|
||||
|
||||
<p>It is possible to miss detection of vulnerabilities if used to clear fields of structures or parts of a buffer.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>We recommend to use the <code>RtlSecureZeroMemory</code> or <code>memset_s</code> functions, or compilation flags that exclude optimization of <code>memset</code> calls (e.g. -fno-builtin-memset).</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
<p>The following example demonstrates an erroneous and corrected use of the <code>memset</code> function.</p>
|
||||
<sample src="CompilerRemovalOfCodeToClearBuffers.c" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>
|
||||
CERT C Coding Standard:
|
||||
<a href="https://wiki.sei.cmu.edu/confluence/display/c/MSC06-C.+Beware+of+compiler+optimizations">MSC06-C. Beware of compiler optimizations</a>.
|
||||
</li>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,127 @@
|
||||
/**
|
||||
* @name Compiler Removal Of Code To Clear Buffers
|
||||
* @description Using <code>memset</code> the function to clear private data in a variable that has no subsequent use
|
||||
* is potentially dangerous because the compiler can remove the call.
|
||||
* @kind problem
|
||||
* @id cpp/compiler-removal-of-code-to-clear-buffers
|
||||
* @problem.severity warning
|
||||
* @precision medium
|
||||
* @tags security
|
||||
* external/cwe/cwe-14
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.dataflow.DataFlow
|
||||
import semmle.code.cpp.dataflow.StackAddress
|
||||
|
||||
/**
|
||||
* A call to `memset` of the form `memset(ptr, value, num)`, for some local variable `ptr`.
|
||||
*/
|
||||
class CompilerRemovaMemset extends FunctionCall {
|
||||
CompilerRemovaMemset() {
|
||||
this.getTarget().hasGlobalOrStdName("memset") and
|
||||
exists(DataFlow::Node source, DataFlow::Node sink, LocalVariable isv, Expr exp |
|
||||
DataFlow::localFlow(source, sink) and
|
||||
this.getArgument(0) = isv.getAnAccess() and
|
||||
(
|
||||
source.asExpr() = exp
|
||||
or
|
||||
// handle the case where exp is defined by an address being passed into some function.
|
||||
source.asDefiningArgument() = exp
|
||||
) and
|
||||
exp.getLocation().getEndLine() < this.getArgument(0).getLocation().getStartLine() and
|
||||
sink.asExpr() = this.getArgument(0)
|
||||
)
|
||||
}
|
||||
|
||||
predicate isExistsAllocForThisVariable() {
|
||||
exists(AllocationExpr alloc, Variable v |
|
||||
alloc = v.getAnAssignedValue() and
|
||||
this.getArgument(0) = v.getAnAccess() and
|
||||
alloc.getASuccessor+() = this
|
||||
)
|
||||
or
|
||||
not stackPointerFlowsToUse(this.getArgument(0), _, _, _)
|
||||
}
|
||||
|
||||
predicate isExistsFreeForThisVariable() {
|
||||
exists(DeallocationExpr free, Variable v |
|
||||
this.getArgument(0) = v.getAnAccess() and
|
||||
free.getFreedExpr() = v.getAnAccess() and
|
||||
this.getASuccessor+() = free
|
||||
)
|
||||
}
|
||||
|
||||
predicate isExistsCallWithThisVariableExcludingDeallocationCalls() {
|
||||
exists(FunctionCall fc, Variable v |
|
||||
not fc instanceof DeallocationExpr and
|
||||
this.getArgument(0) = v.getAnAccess() and
|
||||
fc.getAnArgument() = v.getAnAccess() and
|
||||
this.getASuccessor+() = fc
|
||||
)
|
||||
}
|
||||
|
||||
predicate isVariableUseAfterMemsetExcludingCalls() {
|
||||
exists(DataFlow::Node source, DataFlow::Node sink, LocalVariable isv, Expr exp |
|
||||
DataFlow::localFlow(source, sink) and
|
||||
this.getArgument(0) = isv.getAnAccess() and
|
||||
source.asExpr() = isv.getAnAccess() and
|
||||
exp.getLocation().getStartLine() > this.getArgument(2).getLocation().getEndLine() and
|
||||
not exp.getParent() instanceof FunctionCall and
|
||||
sink.asExpr() = exp
|
||||
)
|
||||
}
|
||||
|
||||
predicate isVariableUseBoundWithArgumentFunction() {
|
||||
exists(DataFlow::Node source, DataFlow::Node sink, LocalVariable isv, Parameter p, Expr exp |
|
||||
DataFlow::localFlow(source, sink) and
|
||||
this.getArgument(0) = isv.getAnAccess() and
|
||||
this.getEnclosingFunction().getAParameter() = p and
|
||||
exp.getAChild*() = p.getAnAccess() and
|
||||
source.asExpr() = exp and
|
||||
sink.asExpr() = isv.getAnAccess()
|
||||
)
|
||||
}
|
||||
|
||||
predicate isVariableUseBoundWithGlobalVariable() {
|
||||
exists(
|
||||
DataFlow::Node source, DataFlow::Node sink, LocalVariable isv, GlobalVariable gv, Expr exp
|
||||
|
|
||||
DataFlow::localFlow(source, sink) and
|
||||
this.getArgument(0) = isv.getAnAccess() and
|
||||
exp.getAChild*() = gv.getAnAccess() and
|
||||
source.asExpr() = exp and
|
||||
sink.asExpr() = isv.getAnAccess()
|
||||
)
|
||||
}
|
||||
|
||||
predicate isExistsCompilationFlagsBlockingRemoval() {
|
||||
exists(Compilation c |
|
||||
c.getAFileCompiled() = this.getFile() and
|
||||
c.getAnArgument() = "-fno-builtin-memset"
|
||||
)
|
||||
}
|
||||
|
||||
predicate isUseVCCompilation() {
|
||||
exists(Compilation c |
|
||||
c.getAFileCompiled() = this.getFile() and
|
||||
(
|
||||
c.getArgument(2).matches("%gcc%") or
|
||||
c.getArgument(2).matches("%g++%") or
|
||||
c.getArgument(2).matches("%clang%") or
|
||||
c.getArgument(2) = "--force-recompute"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
from CompilerRemovaMemset fc
|
||||
where
|
||||
not (fc.isExistsAllocForThisVariable() and not fc.isExistsFreeForThisVariable()) and
|
||||
not (fc.isExistsFreeForThisVariable() and not fc.isUseVCCompilation()) and
|
||||
not fc.isVariableUseAfterMemsetExcludingCalls() and
|
||||
not fc.isExistsCallWithThisVariableExcludingDeallocationCalls() and
|
||||
not fc.isVariableUseBoundWithArgumentFunction() and
|
||||
not fc.isVariableUseBoundWithGlobalVariable() and
|
||||
not fc.isExistsCompilationFlagsBlockingRemoval()
|
||||
select fc.getArgument(0), "This variable will not be cleared."
|
||||
@@ -1,11 +0,0 @@
|
||||
unsigned long sizeArray;
|
||||
|
||||
// BAD: let's consider several values, taking ULONG_MAX =18446744073709551615
|
||||
// sizeArray = 60; (sizeArray - 10) = 50; true
|
||||
// sizeArray = 10; (sizeArray - 10) = 0; false
|
||||
// sizeArray = 1; (sizeArray - 10) = 18446744073709551607; true
|
||||
// sizeArray = 0; (sizeArray - 10) = 18446744073709551606; true
|
||||
if (sizeArray - 10 > 0)
|
||||
|
||||
// GOOD: Prevent overflow by checking the input
|
||||
if (sizeArray > 10)
|
||||
@@ -1,33 +0,0 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>The code compares the unsigned difference with zero.
|
||||
It is highly probable that the condition is wrong if the difference expression has the unsigned type.
|
||||
The condition holds in all the cases when difference is not equal to zero.
|
||||
It means that we may use condition not equal. But the programmer probably wanted to compare the difference of elements.</p>
|
||||
|
||||
<p>False positives include code in which the first difference element is always greater than or equal to the second.
|
||||
For comparison, ">" such conditions are equivalent to "! =", And are recommended for replacement.
|
||||
For comparison "> =", the conditions are always true and are recommended to be excluded.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>Use a simple comparison of two elements, instead of comparing their difference to zero.</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
<p>The following example demonstrates an erroneous and corrected use of comparison.</p>
|
||||
<sample src="UnsignedDifferenceExpressionComparedZero.c" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>CERT C Coding Standard:
|
||||
<a href="https://wiki.sei.cmu.edu/confluence/display/c/INT02-C.+Understand+integer+conversion+rules">INT02-C. Understand integer conversion rules</a>.
|
||||
</li>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -1,23 +0,0 @@
|
||||
/**
|
||||
* @name Unsigned difference expression compared to zero
|
||||
* @description It is highly probable that the condition is wrong if the difference expression has the unsigned type.
|
||||
* The condition holds in all the cases when difference is not equal to zero. It means that we may use condition not equal.
|
||||
* But the programmer probably wanted to compare the difference of elements.
|
||||
* @kind problem
|
||||
* @id cpp/unsigned-difference-expression-compared-zero
|
||||
* @problem.severity warning
|
||||
* @precision medium
|
||||
* @tags security
|
||||
* external/cwe/cwe-191
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.commons.Exclusions
|
||||
|
||||
from RelationalOperation ro, SubExpr sub
|
||||
where
|
||||
not isFromMacroDefinition(ro) and
|
||||
ro.getLesserOperand().getValue().toInt() = 0 and
|
||||
ro.getGreaterOperand() = sub and
|
||||
sub.getFullyConverted().getUnspecifiedType().(IntegralType).isUnsigned()
|
||||
select ro, "Difference in condition is always greater than or equal to zero"
|
||||
@@ -16,6 +16,6 @@ import DataFlow::PathGraph
|
||||
|
||||
from WriteConfig b, DataFlow::PathNode source, DataFlow::PathNode sink
|
||||
where b.hasFlowPath(source, sink)
|
||||
select sink.getNode(),
|
||||
"This write into the external location '" + sink + "' may contain unencrypted data from $@",
|
||||
source, "this source."
|
||||
select sink.getNode(), source, sink,
|
||||
"This write into the external location '" + sink.getNode() +
|
||||
"' may contain unencrypted data from $@", source, "this source."
|
||||
|
||||
@@ -12,6 +12,21 @@
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.controlflow.Guards
|
||||
import semmle.code.cpp.valuenumbering.HashCons
|
||||
|
||||
/**
|
||||
* A function call that potentially does not return (such as `exit`).
|
||||
*/
|
||||
class CallMayNotReturn extends FunctionCall {
|
||||
CallMayNotReturn() {
|
||||
// call that is known to not return
|
||||
not exists(this.(ControlFlowNode).getASuccessor())
|
||||
or
|
||||
// call to another function that may not return
|
||||
exists(CallMayNotReturn exit | getTarget() = exit.getEnclosingFunction())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `realloc` of the form `v = realloc(v, size)`, for some variable `v`.
|
||||
@@ -20,50 +35,27 @@ class ReallocCallLeak extends FunctionCall {
|
||||
Variable v;
|
||||
|
||||
ReallocCallLeak() {
|
||||
exists(AssignExpr ex, VariableAccess va1, VariableAccess va2 |
|
||||
this.getTarget().hasName("realloc") and
|
||||
exists(AssignExpr ex |
|
||||
this.getTarget().hasGlobalOrStdName("realloc") and
|
||||
this = ex.getRValue() and
|
||||
va1 = ex.getLValue() and
|
||||
va2 = this.getArgument(0) and
|
||||
va1 = v.getAnAccess() and
|
||||
va2 = v.getAnAccess()
|
||||
hashCons(ex.getLValue()) = hashCons(this.getArgument(0)) and
|
||||
v.getAnAccess() = this.getArgument(0)
|
||||
)
|
||||
}
|
||||
|
||||
predicate isExistsIfWithExitCall() {
|
||||
exists(IfStmt ifc |
|
||||
this.getArgument(0) = v.getAnAccess() and
|
||||
ifc.getCondition().getAChild*() = v.getAnAccess() and
|
||||
ifc.getEnclosingFunction() = this.getEnclosingFunction() and
|
||||
ifc.getLocation().getStartLine() >= this.getArgument(0).getLocation().getStartLine() and
|
||||
exists(FunctionCall fc |
|
||||
fc.getTarget().hasName("exit") and
|
||||
fc.getEnclosingFunction() = this.getEnclosingFunction() and
|
||||
(ifc.getThen().getAChild*() = fc or ifc.getElse().getAChild*() = fc)
|
||||
)
|
||||
or
|
||||
exists(FunctionCall fc, FunctionCall ftmp1, FunctionCall ftmp2 |
|
||||
ftmp1.getTarget().hasName("exit") and
|
||||
ftmp2.(ControlFlowNode).getASuccessor*() = ftmp1 and
|
||||
fc = ftmp2.getEnclosingFunction().getACallToThisFunction() and
|
||||
fc.getEnclosingFunction() = this.getEnclosingFunction() and
|
||||
(ifc.getThen().getAChild*() = fc or ifc.getElse().getAChild*() = fc)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
predicate isExistsAssertWithArgumentCall() {
|
||||
exists(FunctionCall fc |
|
||||
fc.getTarget().hasName("__assert_fail") and
|
||||
this.getEnclosingFunction() = fc.getEnclosingFunction() and
|
||||
fc.getLocation().getStartLine() > this.getArgument(0).getLocation().getEndLine() and
|
||||
fc.getArgument(0).toString().matches("%" + this.getArgument(0).toString() + "%")
|
||||
/**
|
||||
* Holds if failure of this allocation may be handled by termination, for
|
||||
* example a call to `exit()`.
|
||||
*/
|
||||
predicate mayHandleByTermination() {
|
||||
exists(GuardCondition guard, CallMayNotReturn exit |
|
||||
this.(ControlFlowNode).getASuccessor*() = guard and
|
||||
guard.getAChild*() = v.getAnAccess() and
|
||||
guard.controls(exit.getBasicBlock(), _)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
from ReallocCallLeak rcl
|
||||
where
|
||||
not rcl.isExistsIfWithExitCall() and
|
||||
not rcl.isExistsAssertWithArgumentCall()
|
||||
where not rcl.mayHandleByTermination()
|
||||
select rcl, "possible loss of original pointer on unsuccessful call realloc"
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
// BAD: on memory allocation error, the program terminates.
|
||||
void badFunction(const int *source, std::size_t length) noexcept {
|
||||
int * dest = new int[length];
|
||||
std::memset(dest, 0, length);
|
||||
// ..
|
||||
}
|
||||
// GOOD: memory allocation error will be handled.
|
||||
void goodFunction(const int *source, std::size_t length) noexcept {
|
||||
try {
|
||||
int * dest = new int[length];
|
||||
} catch(std::bad_alloc) {
|
||||
// ...
|
||||
}
|
||||
std::memset(dest, 0, length);
|
||||
// ..
|
||||
}
|
||||
// BAD: memory allocation error will not be handled.
|
||||
void badFunction(const int *source, std::size_t length) noexcept {
|
||||
try {
|
||||
int * dest = new (std::nothrow) int[length];
|
||||
} catch(std::bad_alloc) {
|
||||
// ...
|
||||
}
|
||||
std::memset(dest, 0, length);
|
||||
// ..
|
||||
}
|
||||
// GOOD: memory allocation error will be handled.
|
||||
void goodFunction(const int *source, std::size_t length) noexcept {
|
||||
int * dest = new (std::nothrow) int[length];
|
||||
if (!dest) {
|
||||
return;
|
||||
}
|
||||
std::memset(dest, 0, length);
|
||||
// ..
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>When using the <code>new</code> operator to allocate memory, you need to pay attention to the different ways of detecting errors. <code>::operator new(std::size_t)</code> throws an exception on error, whereas <code>::operator new(std::size_t, const std::nothrow_t &)</code> returns zero on error. The programmer can get confused and check the error that occurs when allocating memory incorrectly. That can lead to an unhandled program termination or to a violation of the program logic.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>Use the correct error detection method corresponding with the memory allocation.</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
<p>The following example demonstrates various approaches to detecting memory allocation errors using the <code>new</code> operator.</p>
|
||||
<sample src="WrongInDetectingAndHandlingMemoryAllocationErrors.cpp" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>
|
||||
CERT C++ Coding Standard:
|
||||
<a href="https://wiki.sei.cmu.edu/confluence/display/cplusplus/MEM52-CPP.+Detect+and+handle+memory+allocation+errors">MEM52-CPP. Detect and handle memory allocation errors</a>.
|
||||
</li>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
* @name Detect And Handle Memory Allocation Errors
|
||||
* @description --::operator new(std::size_t) throws an exception on error, and ::operator new(std::size_t, const std::nothrow_t &) returns zero on error.
|
||||
* --the programmer can get confused when check the error that occurs when allocating memory incorrectly.
|
||||
* @kind problem
|
||||
* @id cpp/detect-and-handle-memory-allocation-errors
|
||||
* @problem.severity warning
|
||||
* @precision medium
|
||||
* @tags correctness
|
||||
* security
|
||||
* external/cwe/cwe-570
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
/**
|
||||
* Lookup if condition compare with 0
|
||||
*/
|
||||
class IfCompareWithZero extends IfStmt {
|
||||
IfCompareWithZero() {
|
||||
this.getCondition().(EQExpr).getAChild().getValue() = "0"
|
||||
or
|
||||
this.getCondition().(NEExpr).getAChild().getValue() = "0" and
|
||||
this.hasElse()
|
||||
or
|
||||
this.getCondition().(NEExpr).getAChild().getValue() = "0" and
|
||||
this.getThen().getAChild*() instanceof ReturnStmt
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* lookup for calls to `operator new`, with incorrect error handling.
|
||||
*/
|
||||
class WrongCheckErrorOperatorNew extends FunctionCall {
|
||||
Expr exp;
|
||||
|
||||
WrongCheckErrorOperatorNew() {
|
||||
this = exp.(NewOrNewArrayExpr).getAChild().(FunctionCall) and
|
||||
(
|
||||
this.getTarget().hasGlobalOrStdName("operator new")
|
||||
or
|
||||
this.getTarget().hasGlobalOrStdName("operator new[]")
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if handler `try ... catch` exists.
|
||||
*/
|
||||
predicate isExistsTryCatchBlock() {
|
||||
exists(TryStmt ts | this.getEnclosingStmt() = ts.getStmt().getAChild*())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if results call `operator new` check in `operator if`.
|
||||
*/
|
||||
predicate isExistsIfCondition() {
|
||||
exists(IfCompareWithZero ifc, AssignExpr aex, Initializer it |
|
||||
// call `operator new` directly from the condition of `operator if`.
|
||||
this = ifc.getCondition().getAChild*()
|
||||
or
|
||||
// check results call `operator new` with variable appropriation
|
||||
postDominates(ifc, this) and
|
||||
aex.getAChild() = exp and
|
||||
ifc.getCondition().getAChild().(VariableAccess).getTarget() =
|
||||
aex.getLValue().(VariableAccess).getTarget()
|
||||
or
|
||||
// check results call `operator new` with declaration variable
|
||||
postDominates(ifc, this) and
|
||||
exp = it.getExpr() and
|
||||
it.getDeclaration() = ifc.getCondition().getAChild().(VariableAccess).getTarget()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `(std::nothrow)` exists in call `operator new`.
|
||||
*/
|
||||
predicate isExistsNothrow() { this.getAChild().toString() = "nothrow" }
|
||||
}
|
||||
|
||||
from WrongCheckErrorOperatorNew op
|
||||
where
|
||||
// use call `operator new` with `(std::nothrow)` and checking error using `try ... catch` block and not `operator if`
|
||||
op.isExistsNothrow() and not op.isExistsIfCondition() and op.isExistsTryCatchBlock()
|
||||
or
|
||||
// use call `operator new` without `(std::nothrow)` and checking error using `operator if` and not `try ... catch` block
|
||||
not op.isExistsNothrow() and not op.isExistsTryCatchBlock() and op.isExistsIfCondition()
|
||||
select op, "memory allocation error check is incorrect or missing"
|
||||
@@ -0,0 +1,9 @@
|
||||
// BAD: if buffer does not have a terminal zero, then access outside the allocated memory is possible.
|
||||
|
||||
buffer[strlen(buffer)] = 0;
|
||||
|
||||
|
||||
// GOOD: we will eliminate dangerous behavior if we use a different method of calculating the length.
|
||||
size_t len;
|
||||
...
|
||||
buffer[len] = 0
|
||||
@@ -0,0 +1,31 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>Potentially dangerous use of the strlen function to calculate the length of a string.
|
||||
The expression <code>buffer[strlen(buffer)] = 0</code> is potentially dangerous, if the variable buffer does not have a terminal zero, then access beyond the bounds of the allocated memory is possible, which will lead to undefined behavior.
|
||||
If terminal zero is present, then the specified expression is meaningless.</p>
|
||||
|
||||
<p>False positives include heavily nested strlen. This situation is unlikely.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>We recommend using another method for calculating the string length</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
<p>The following example demonstrates an erroneous and corrected use of the strlen function.</p>
|
||||
<sample src="AccessOfMemoryLocationAfterEndOfBufferUsingStrlen.c" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>
|
||||
CERT C Coding Standard:
|
||||
<a href="https://wiki.sei.cmu.edu/confluence/display/c/STR32-C.+Do+not+pass+a+non-null-terminated+character+sequence+to+a+library+function+that+expects+a+string">STR32-C. Do not pass a non-null-terminated character sequence to a library function that expects a string</a>.
|
||||
</li>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* @name Access Of Memory Location After End Of Buffer
|
||||
* @description The expression `buffer [strlen (buffer)] = 0` is potentially dangerous, if the variable `buffer` does not have a terminal zero, then access beyond the bounds of the allocated memory is possible, which will lead to undefined behavior.
|
||||
* If terminal zero is present, then the specified expression is meaningless.
|
||||
* @kind problem
|
||||
* @id cpp/access-memory-location-after-end-buffer
|
||||
* @problem.severity warning
|
||||
* @precision medium
|
||||
* @tags correctness
|
||||
* security
|
||||
* external/cwe/cwe-788
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
|
||||
import semmle.code.cpp.dataflow.DataFlow
|
||||
|
||||
from StrlenCall fc, AssignExpr expr, ArrayExpr exprarr
|
||||
where
|
||||
exprarr = expr.getLValue() and
|
||||
expr.getRValue().getValue().toInt() = 0 and
|
||||
globalValueNumber(exprarr.getArrayOffset()) = globalValueNumber(fc) and
|
||||
not exists(Expr exptmp |
|
||||
(
|
||||
DataFlow::localExprFlow(fc, exptmp) or
|
||||
exptmp.getAChild*() = fc.getArgument(0).(VariableAccess).getTarget().getAnAccess()
|
||||
) and
|
||||
dominates(exptmp, expr) and
|
||||
postDominates(exptmp, fc) and
|
||||
not exptmp.getEnclosingStmt() = fc.getEnclosingStmt() and
|
||||
not exptmp.getEnclosingStmt() = expr.getEnclosingStmt()
|
||||
) and
|
||||
globalValueNumber(fc.getArgument(0)) = globalValueNumber(exprarr.getArrayBase())
|
||||
select expr, "potential unsafe or redundant assignment."
|
||||
@@ -0,0 +1,4 @@
|
||||
|
||||
strncat(dest, source, sizeof(dest) - strlen(dest)); // BAD: writes a zero byte past the `dest` buffer.
|
||||
|
||||
strncat(dest, source, sizeof(dest) - strlen(dest) -1); // GOOD: Reserves space for the zero byte.
|
||||
@@ -0,0 +1,32 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>The standard library function <code>strncat(dest, source, count)</code> appends the <code>source</code> string to the <code>dest</code> string. <code>count</code> specifies the maximum number of characters to append and must be less than the remaining space in the target buffer. Calls of the form <code> strncat (dest, source, sizeof (dest) - strlen (dest)) </code> set the third argument to one more than possible. So when the <code>dest</code> is full, the expression <code> sizeof (dest) - strlen (dest) </code> will be equal to one, and not zero as the programmer might think. Making a call of this type may result in a zero byte being written just outside the <code>dest</code> buffer.</p>
|
||||
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>We recommend subtracting one from the third argument. For example, replace <code>strncat(dest, source, sizeof(dest)-strlen(dest))</code> with <code>strncat(dest, source, sizeof(dest)-strlen(dest)-1)</code>.</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
<p>The following example demonstrates an erroneous and corrected use of the <code>strncat</code> function.</p>
|
||||
<sample src="AccessOfMemoryLocationAfterEndOfBufferUsingStrncat.c" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>
|
||||
CERT C Coding Standard:
|
||||
<a href="https://wiki.sei.cmu.edu/confluence/display/c/STR31-C.+Guarantee+that+storage+for+strings+has+sufficient+space+for+character+data+and+the+null+terminator">STR31-C. Guarantee that storage for strings has sufficient space for character data and the null terminator</a>.
|
||||
</li>
|
||||
<li>
|
||||
CERT C Coding Standard:
|
||||
<a href="https://wiki.sei.cmu.edu/confluence/display/c/ARR30-C.+Do+not+form+or+use+out-of-bounds+pointers+or+array+subscripts">ARR30-C. Do not form or use out-of-bounds pointers or array subscripts</a>.
|
||||
</li>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* @name Access Of Memory Location After The End Of A Buffer Using Strncat
|
||||
* @description Calls of the form `strncat(dest, source, sizeof (dest) - strlen (dest))` set the third argument to one more than possible. So when `dest` is full, the expression `sizeof(dest) - strlen (dest)` will be equal to one, and not zero as the programmer might think. Making a call of this type may result in a zero byte being written just outside the `dest` buffer.
|
||||
* @kind problem
|
||||
* @id cpp/access-memory-location-after-end-buffer
|
||||
* @problem.severity warning
|
||||
* @precision medium
|
||||
* @tags correctness
|
||||
* security
|
||||
* external/cwe/cwe-788
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
|
||||
|
||||
/**
|
||||
* A call to `strncat` of the form `strncat(buff, str, someExpr - strlen(buf))`, for some expression `someExpr` equal to `sizeof(buff)`.
|
||||
*/
|
||||
class WrongCallStrncat extends FunctionCall {
|
||||
Expr leftsomeExpr;
|
||||
|
||||
WrongCallStrncat() {
|
||||
this.getTarget().hasGlobalOrStdName("strncat") and
|
||||
// the expression of the first argument in `strncat` and `strnlen` is identical
|
||||
globalValueNumber(this.getArgument(0)) =
|
||||
globalValueNumber(this.getArgument(2).(SubExpr).getRightOperand().(StrlenCall).getStringExpr()) and
|
||||
// using a string constant often speaks of manually calculating the length of the required buffer.
|
||||
(
|
||||
not this.getArgument(1) instanceof StringLiteral and
|
||||
not this.getArgument(1) instanceof CharLiteral
|
||||
) and
|
||||
// for use in predicates
|
||||
leftsomeExpr = this.getArgument(2).(SubExpr).getLeftOperand()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the left side of the expression `someExpr` equal to `sizeof(buf)`.
|
||||
*/
|
||||
predicate isExpressionEqualSizeof() {
|
||||
// the left side of the expression `someExpr` is `sizeof(buf)`.
|
||||
globalValueNumber(this.getArgument(0)) =
|
||||
globalValueNumber(leftsomeExpr.(SizeofExprOperator).getExprOperand())
|
||||
or
|
||||
// value of the left side of the expression `someExpr` equal `sizeof(buf)` value, and `buf` is array.
|
||||
leftsomeExpr.getValue().toInt() = this.getArgument(0).getType().getSize()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the left side of the expression `someExpr` equal to variable containing the length of the memory allocated for the buffer.
|
||||
*/
|
||||
predicate isVariableEqualValueSizegBuffer() {
|
||||
// the left side of expression `someExpr` is the variable that was used in the function of allocating memory for the buffer`.
|
||||
exists(AllocationExpr alc |
|
||||
leftsomeExpr.(VariableAccess).getTarget() =
|
||||
alc.(FunctionCall).getArgument(0).(VariableAccess).getTarget()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
from WrongCallStrncat sc
|
||||
where
|
||||
sc.isExpressionEqualSizeof() or
|
||||
sc.isVariableEqualValueSizegBuffer()
|
||||
select sc, "if the used buffer is full, writing out of the buffer is possible"
|
||||
@@ -50,5 +50,5 @@ class CStyleComment extends Comment {
|
||||
* ```
|
||||
*/
|
||||
class CppStyleComment extends Comment {
|
||||
CppStyleComment() { this.getContents().prefix(2) = "//" }
|
||||
CppStyleComment() { this.getContents().matches("//%") }
|
||||
}
|
||||
|
||||
@@ -270,7 +270,12 @@ private predicate isFromUninstantiatedTemplateRec(Element e, Element template) {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++11 `static_assert` or C11 `_Static_assert` construct.
|
||||
* A C++11 `static_assert` or C11 `_Static_assert` construct. For example each
|
||||
* line in the following example contains a static assert:
|
||||
* ```
|
||||
* static_assert(sizeof(MyStruct) <= 4096);
|
||||
* static_assert(sizeof(MyStruct) <= 4096, "MyStruct is too big!");
|
||||
* ```
|
||||
*/
|
||||
class StaticAssert extends Locatable, @static_assert {
|
||||
override string toString() { result = "static_assert(..., \"" + getMessage() + "\")" }
|
||||
|
||||
@@ -334,6 +334,18 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the class of which this function, called `memberName`, is a member.
|
||||
*
|
||||
* Prefer to use `getDeclaringType()` or `getName()` directly if you do not
|
||||
* need to reason about both.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
Class getClassAndName(string memberName) {
|
||||
this.hasName(memberName) and
|
||||
this.getDeclaringType() = result
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements `ControlFlowNode.getControlFlowScope`. The `Function` is
|
||||
* used to represent the exit node of the control flow graph, so it is
|
||||
|
||||
@@ -7,8 +7,21 @@ import semmle.code.cpp.Type
|
||||
import semmle.code.cpp.metrics.MetricNamespace
|
||||
|
||||
/**
|
||||
* A C++ namespace.
|
||||
* A C++ namespace. For example the (single) namespace `A` in the following
|
||||
* code:
|
||||
* ```
|
||||
* namespace A
|
||||
* {
|
||||
* // ...
|
||||
* }
|
||||
*
|
||||
* // ...
|
||||
*
|
||||
* namespace A
|
||||
* {
|
||||
* // ...
|
||||
* }
|
||||
* ```
|
||||
* Note that namespaces are somewhat nebulous entities, as they do not in
|
||||
* general have a single well-defined location in the source code. The
|
||||
* related notion of a `NamespaceDeclarationEntry` is rather more concrete,
|
||||
@@ -96,10 +109,22 @@ class Namespace extends NameQualifyingElement, @namespace {
|
||||
}
|
||||
|
||||
/**
|
||||
* A declaration of (part of) a C++ namespace.
|
||||
* A declaration of (part of) a C++ namespace. This corresponds to a single
|
||||
* `namespace N { ... }` occurrence in the source code. For example the two
|
||||
* mentions of `A` in the following code:
|
||||
* ```
|
||||
* namespace A
|
||||
* {
|
||||
* // ...
|
||||
* }
|
||||
*
|
||||
* This corresponds to a single `namespace N { ... }` occurrence in the
|
||||
* source code.
|
||||
* // ...
|
||||
*
|
||||
* namespace A
|
||||
* {
|
||||
* // ...
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class NamespaceDeclarationEntry extends Locatable, @namespace_decl {
|
||||
/**
|
||||
@@ -143,8 +168,9 @@ class UsingEntry extends Locatable, @using {
|
||||
|
||||
/**
|
||||
* A C++ `using` declaration. For example:
|
||||
*
|
||||
* `using std::string;`
|
||||
* ```
|
||||
* using std::string;
|
||||
* ```
|
||||
*/
|
||||
class UsingDeclarationEntry extends UsingEntry {
|
||||
UsingDeclarationEntry() {
|
||||
@@ -162,8 +188,9 @@ class UsingDeclarationEntry extends UsingEntry {
|
||||
|
||||
/**
|
||||
* A C++ `using` directive. For example:
|
||||
*
|
||||
* `using namespace std;`
|
||||
* ```
|
||||
* using namespace std;
|
||||
* ```
|
||||
*/
|
||||
class UsingDirectiveEntry extends UsingEntry {
|
||||
UsingDirectiveEntry() {
|
||||
|
||||
@@ -2,9 +2,14 @@ import semmle.code.cpp.Location
|
||||
import semmle.code.cpp.Element
|
||||
|
||||
/**
|
||||
* A C/C++ preprocessor directive.
|
||||
*
|
||||
* For example: `#ifdef`, `#line`, or `#pragma`.
|
||||
* A C/C++ preprocessor directive. For example each of the following lines of
|
||||
* code contains a `PreprocessorDirective`:
|
||||
* ```
|
||||
* #pragma once
|
||||
* #ifdef MYDEFINE
|
||||
* #include "myfile.h"
|
||||
* #line 1 "source.c"
|
||||
* ```
|
||||
*/
|
||||
class PreprocessorDirective extends Locatable, @preprocdirect {
|
||||
override string toString() { result = "Preprocessor directive" }
|
||||
@@ -98,9 +103,9 @@ class PreprocessorBranchDirective extends PreprocessorDirective, TPreprocessorBr
|
||||
* A C/C++ preprocessor branching directive: `#if`, `#ifdef`, `#ifndef`, or
|
||||
* `#elif`.
|
||||
*
|
||||
* A branching directive can have its condition evaluated at compile-time,
|
||||
* and as a result, the preprocessor will either take the branch, or not
|
||||
* take the branch.
|
||||
* A branching directive has a condition and that condition may be evaluated
|
||||
* at compile-time. As a result, the preprocessor will either take the
|
||||
* branch, or not take the branch.
|
||||
*
|
||||
* However, there are also situations in which a branch's condition isn't
|
||||
* evaluated. The obvious case of this is when the directive is contained
|
||||
@@ -136,8 +141,13 @@ class PreprocessorBranch extends PreprocessorBranchDirective, @ppd_branch {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ preprocessor `#if` directive.
|
||||
*
|
||||
* A C/C++ preprocessor `#if` directive. For example there is a
|
||||
* `PreprocessorIf` on the first line of the following code:
|
||||
* ```
|
||||
* #if defined(MYDEFINE)
|
||||
* // ...
|
||||
* #endif
|
||||
* ```
|
||||
* For the related notion of a directive which causes branching (which
|
||||
* includes `#if`, plus also `#ifdef`, `#ifndef`, and `#elif`), see
|
||||
* `PreprocessorBranch`.
|
||||
@@ -147,8 +157,13 @@ class PreprocessorIf extends PreprocessorBranch, @ppd_if {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ preprocessor `#ifdef` directive.
|
||||
*
|
||||
* A C/C++ preprocessor `#ifdef` directive. For example there is a
|
||||
* `PreprocessorIfdef` on the first line of the following code:
|
||||
* ```
|
||||
* #ifdef MYDEFINE
|
||||
* // ...
|
||||
* #endif
|
||||
* ```
|
||||
* The syntax `#ifdef X` is shorthand for `#if defined(X)`.
|
||||
*/
|
||||
class PreprocessorIfdef extends PreprocessorBranch, @ppd_ifdef {
|
||||
@@ -158,8 +173,13 @@ class PreprocessorIfdef extends PreprocessorBranch, @ppd_ifdef {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ preprocessor `#ifndef` directive.
|
||||
*
|
||||
* A C/C++ preprocessor `#ifndef` directive. For example there is a
|
||||
* `PreprocessorIfndef` on the first line of the following code:
|
||||
* ```
|
||||
* #ifndef MYDEFINE
|
||||
* // ...
|
||||
* #endif
|
||||
* ```
|
||||
* The syntax `#ifndef X` is shorthand for `#if !defined(X)`.
|
||||
*/
|
||||
class PreprocessorIfndef extends PreprocessorBranch, @ppd_ifndef {
|
||||
@@ -167,42 +187,80 @@ class PreprocessorIfndef extends PreprocessorBranch, @ppd_ifndef {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ preprocessor `#else` directive.
|
||||
* A C/C++ preprocessor `#else` directive. For example there is a
|
||||
* `PreprocessorElse` on the fifth line of the following code:
|
||||
* ```
|
||||
* #ifdef MYDEFINE1
|
||||
* // ...
|
||||
* #elif MYDEFINE2
|
||||
* // ...
|
||||
* #else
|
||||
* // ...
|
||||
* #endif
|
||||
* ```
|
||||
*/
|
||||
class PreprocessorElse extends PreprocessorBranchDirective, @ppd_else {
|
||||
override string toString() { result = "#else" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ preprocessor `#elif` directive.
|
||||
* A C/C++ preprocessor `#elif` directive. For example there is a
|
||||
* `PreprocessorElif` on the third line of the following code:
|
||||
* ```
|
||||
* #ifdef MYDEFINE1
|
||||
* // ...
|
||||
* #elif MYDEFINE2
|
||||
* // ...
|
||||
* #else
|
||||
* // ...
|
||||
* #endif
|
||||
* ```
|
||||
*/
|
||||
class PreprocessorElif extends PreprocessorBranch, @ppd_elif {
|
||||
override string toString() { result = "#elif " + this.getHead() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ preprocessor `#endif` directive.
|
||||
* A C/C++ preprocessor `#endif` directive. For example there is a
|
||||
* `PreprocessorEndif` on the third line of the following code:
|
||||
* ```
|
||||
* #ifdef MYDEFINE
|
||||
* // ...
|
||||
* #endif
|
||||
* ```
|
||||
*/
|
||||
class PreprocessorEndif extends PreprocessorBranchDirective, @ppd_endif {
|
||||
override string toString() { result = "#endif" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ preprocessor `#warning` directive.
|
||||
* A C/C++ preprocessor `#warning` directive. For example:
|
||||
* ```
|
||||
* #warning "This configuration is not supported."
|
||||
* ```
|
||||
*/
|
||||
class PreprocessorWarning extends PreprocessorDirective, @ppd_warning {
|
||||
override string toString() { result = "#warning " + this.getHead() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ preprocessor `#error` directive.
|
||||
* A C/C++ preprocessor `#error` directive. For example:
|
||||
* ```
|
||||
* #error "This configuration is not implemented."
|
||||
* ```
|
||||
*/
|
||||
class PreprocessorError extends PreprocessorDirective, @ppd_error {
|
||||
override string toString() { result = "#error " + this.getHead() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ preprocessor `#undef` directive.
|
||||
* A C/C++ preprocessor `#undef` directive. For example there is a
|
||||
* `PreprocessorUndef` on the second line of the following code:
|
||||
* ```
|
||||
* #ifdef MYMACRO
|
||||
* #undef MYMACRO
|
||||
* #endif
|
||||
* ```
|
||||
*/
|
||||
class PreprocessorUndef extends PreprocessorDirective, @ppd_undef {
|
||||
override string toString() { result = "#undef " + this.getHead() }
|
||||
@@ -214,7 +272,10 @@ class PreprocessorUndef extends PreprocessorDirective, @ppd_undef {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ preprocessor `#pragma` directive.
|
||||
* A C/C++ preprocessor `#pragma` directive. For example:
|
||||
* ```
|
||||
* #pragma once
|
||||
* ```
|
||||
*/
|
||||
class PreprocessorPragma extends PreprocessorDirective, @ppd_pragma {
|
||||
override string toString() {
|
||||
@@ -223,7 +284,10 @@ class PreprocessorPragma extends PreprocessorDirective, @ppd_pragma {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ preprocessor `#line` directive.
|
||||
* A C/C++ preprocessor `#line` directive. For example:
|
||||
* ```
|
||||
* #line 1 "source.c"
|
||||
* ```
|
||||
*/
|
||||
class PreprocessorLine extends PreprocessorDirective, @ppd_line {
|
||||
override string toString() { result = "#line " + this.getHead() }
|
||||
|
||||
@@ -171,8 +171,11 @@ class StdAttribute extends Attribute, @stdattribute {
|
||||
}
|
||||
|
||||
/**
|
||||
* An attribute introduced by Microsoft's `__declspec(name)` syntax, for
|
||||
* example: `__declspec(dllimport)`.
|
||||
* An attribute introduced by Microsoft's `__declspec(name)` syntax. For
|
||||
* example the attribute on the following declaration:
|
||||
* ```
|
||||
* __declspec(dllimport) void myFunction();
|
||||
* ```
|
||||
*/
|
||||
class Declspec extends Attribute, @declspec { }
|
||||
|
||||
@@ -186,8 +189,13 @@ class MicrosoftAttribute extends Attribute, @msattribute {
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++11 `alignas` construct.
|
||||
*
|
||||
* A C++11 `alignas` construct. For example the attribute in the following
|
||||
* code:
|
||||
* ```
|
||||
* struct alignas(16) MyStruct {
|
||||
* int x;
|
||||
* };
|
||||
* ```
|
||||
* Though it doesn't use the attribute syntax, `alignas(...)` is presented
|
||||
* as an `Attribute` for consistency with the `[[align(...)]]` attribute.
|
||||
*/
|
||||
@@ -197,7 +205,11 @@ class AlignAs extends Attribute, @alignas {
|
||||
|
||||
/**
|
||||
* A GNU `format` attribute of the form `__attribute__((format(archetype, format-index, first-arg)))`
|
||||
* that declares a function to accept a `printf` style format string.
|
||||
* that declares a function to accept a `printf` style format string. For example the attribute
|
||||
* on the following declaration:
|
||||
* ```
|
||||
* int myPrintf(const char *format, ...) __attribute__((format(printf, 1, 2)));
|
||||
* ```
|
||||
*/
|
||||
class FormatAttribute extends GnuAttribute {
|
||||
FormatAttribute() { getName() = "format" }
|
||||
@@ -242,7 +254,11 @@ class FormatAttribute extends GnuAttribute {
|
||||
}
|
||||
|
||||
/**
|
||||
* An argument to an `Attribute`.
|
||||
* An argument to an `Attribute`. For example the argument "dllimport" on the
|
||||
* attribute in the following code:
|
||||
* ```
|
||||
* __declspec(dllimport) void myFunction();
|
||||
* ```
|
||||
*/
|
||||
class AttributeArgument extends Element, @attribute_arg {
|
||||
/**
|
||||
|
||||
@@ -274,7 +274,7 @@ class Type extends Locatable, @type {
|
||||
|
||||
/**
|
||||
* Gets this type with any typedefs resolved. For example, given
|
||||
* `typedef C T`, this would resolve `const T&` to `const C&`.
|
||||
* `typedef C T`, this would resolve `const T&` to `const C&`.
|
||||
* Note that this will only work if the resolved type actually appears
|
||||
* on its own elsewhere in the program.
|
||||
*/
|
||||
@@ -1544,9 +1544,9 @@ class FunctionPointerIshType extends DerivedType {
|
||||
/**
|
||||
* A C++ pointer to data member. See 15.5.
|
||||
* ```
|
||||
* class C { int m; };
|
||||
* class C { public: int m; };
|
||||
* int C::* p = &C::m; // pointer to data member m of class C
|
||||
* class C *;
|
||||
* class C c;
|
||||
* int val = c.*p; // access data member
|
||||
* ```
|
||||
*/
|
||||
|
||||
@@ -50,7 +50,10 @@ predicate primitiveVariadicFormatter(
|
||||
then formatParamIndex = f.getNumberOfParameters() - 3
|
||||
else formatParamIndex = f.getNumberOfParameters() - 2
|
||||
) and
|
||||
if type = "" then outputParamIndex = -1 else outputParamIndex = 0 // Conveniently, these buffer parameters are all at index 0.
|
||||
(
|
||||
if type = "" then outputParamIndex = -1 else outputParamIndex = 0 // Conveniently, these buffer parameters are all at index 0.
|
||||
) and
|
||||
not exists(f.getBlock()) // exclude functions with an implementation in the snapshot as they may not be standard implementations.
|
||||
}
|
||||
|
||||
private predicate callsVariadicFormatter(
|
||||
|
||||
@@ -92,7 +92,9 @@ class Snscanf extends ScanfFunction {
|
||||
this instanceof TopLevelFunction and
|
||||
(
|
||||
hasName("_snscanf") or // _snscanf(src, max_amount, format, args...)
|
||||
hasName("_snwscanf") // _snwscanf(src, max_amount, format, args...)
|
||||
hasName("_snwscanf") or // _snwscanf(src, max_amount, format, args...)
|
||||
hasName("_snscanf_l") or // _snscanf_l(src, max_amount, format, locale, args...)
|
||||
hasName("_snwscanf_l") // _snwscanf_l(src, max_amount, format, locale, args...)
|
||||
// note that the max_amount is not a limit on the output length, it's an input length
|
||||
// limit used with non null-terminated strings.
|
||||
)
|
||||
@@ -101,6 +103,12 @@ class Snscanf extends ScanfFunction {
|
||||
override int getInputParameterIndex() { result = 0 }
|
||||
|
||||
override int getFormatParameterIndex() { result = 2 }
|
||||
|
||||
/**
|
||||
* Gets the position at which the maximum number of characters in the
|
||||
* input string is specified.
|
||||
*/
|
||||
int getInputLengthParameterIndex() { result = 1 }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -87,7 +87,7 @@ abstract class MutexType extends Type {
|
||||
private Function mustlockCandidate() {
|
||||
exists(string name | name = result.getName() |
|
||||
name = "lock" or
|
||||
name.suffix(name.length() - 10) = "mutex_lock"
|
||||
name.matches("%mutex\\_lock")
|
||||
)
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ private Function mustlockCandidate() {
|
||||
private Function trylockCandidate() {
|
||||
exists(string name | name = result.getName() |
|
||||
name = "try_lock" or
|
||||
name.suffix(name.length() - 13) = "mutex_trylock"
|
||||
name.matches("%mutex\\_trylock")
|
||||
)
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@ private Function trylockCandidate() {
|
||||
private Function unlockCandidate() {
|
||||
exists(string name | name = result.getName() |
|
||||
name = "unlock" or
|
||||
name.suffix(name.length() - 12) = "mutex_unlock"
|
||||
name.matches("%mutex\\_unlock")
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -716,21 +716,15 @@ private predicate modelFlow(Operand opFrom, Instruction iTo) {
|
||||
iTo = call
|
||||
or
|
||||
exists(int index, WriteSideEffectInstruction outNode |
|
||||
modelOut.isParameterDeref(index) and
|
||||
modelOut.isParameterDerefOrQualifierObject(index) and
|
||||
iTo = outNode and
|
||||
outNode = getSideEffectFor(call, index)
|
||||
)
|
||||
or
|
||||
exists(WriteSideEffectInstruction outNode |
|
||||
modelOut.isQualifierObject() and
|
||||
iTo = outNode and
|
||||
outNode = getSideEffectFor(call, -1)
|
||||
)
|
||||
) and
|
||||
(
|
||||
exists(int index |
|
||||
modelIn.isParameter(index) and
|
||||
opFrom = call.getPositionalArgumentOperand(index)
|
||||
modelIn.isParameterOrQualifierAddress(index) and
|
||||
opFrom = call.getArgumentOperand(index)
|
||||
)
|
||||
or
|
||||
exists(int index, ReadSideEffectInstruction read |
|
||||
@@ -739,9 +733,6 @@ private predicate modelFlow(Operand opFrom, Instruction iTo) {
|
||||
opFrom = read.getSideEffectOperand()
|
||||
)
|
||||
or
|
||||
modelIn.isQualifierAddress() and
|
||||
opFrom = call.getThisArgumentOperand()
|
||||
or
|
||||
exists(ReadSideEffectInstruction read |
|
||||
modelIn.isQualifierObject() and
|
||||
read = getSideEffectFor(call, -1) and
|
||||
|
||||
@@ -341,7 +341,7 @@ module IRTypeConsistency {
|
||||
query predicate multipleIRTypes(Language::LanguageType type, string message) {
|
||||
strictcount(type.getIRType()) > 1 and
|
||||
message =
|
||||
"`LanguageType` " + type.getAQlClass() + " has multiple `IRType`s: " +
|
||||
"`LanguageType` " + type + " has multiple `IRType`s: " +
|
||||
concat(type.getIRType().toString(), ", ")
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,10 @@ private import implementations.Strcat
|
||||
private import implementations.Strcpy
|
||||
private import implementations.Strdup
|
||||
private import implementations.Strftime
|
||||
private import implementations.Strtok
|
||||
private import implementations.Strset
|
||||
private import implementations.Strcrement
|
||||
private import implementations.Strnextc
|
||||
private import implementations.StdContainer
|
||||
private import implementations.StdPair
|
||||
private import implementations.StdMap
|
||||
@@ -23,3 +27,4 @@ private import implementations.StdString
|
||||
private import implementations.Swap
|
||||
private import implementations.GetDelim
|
||||
private import implementations.SmartPointer
|
||||
private import implementations.Sscanf
|
||||
|
||||
@@ -32,5 +32,7 @@ private class IdentityFunction extends DataFlowFunction, SideEffectFunction, Ali
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
// These functions simply return the argument value.
|
||||
input.isParameter(0) and output.isReturnValue()
|
||||
or
|
||||
input.isParameterDeref(0) and output.isReturnValueDeref()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,6 +109,8 @@ private class IteratorCrementOperator extends Operator, DataFlowFunction {
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
input = iteratorInput and
|
||||
output.isReturnValue()
|
||||
or
|
||||
input.isParameterDeref(0) and output.isReturnValueDeref()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,6 +161,8 @@ private class IteratorAssignArithmeticOperator extends Operator, DataFlowFunctio
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isParameter(0) and
|
||||
output.isReturnValue()
|
||||
or
|
||||
input.isParameterDeref(0) and output.isReturnValueDeref()
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -201,6 +205,9 @@ private class IteratorCrementMemberOperator extends MemberFunction, DataFlowFunc
|
||||
or
|
||||
input.isReturnValueDeref() and
|
||||
output.isQualifierObject()
|
||||
or
|
||||
input.isQualifierObject() and
|
||||
output.isReturnValueDeref()
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
import semmle.code.cpp.Function
|
||||
import semmle.code.cpp.models.interfaces.ArrayFunction
|
||||
import semmle.code.cpp.models.interfaces.DataFlow
|
||||
import semmle.code.cpp.models.interfaces.Alias
|
||||
import semmle.code.cpp.models.interfaces.SideEffect
|
||||
import semmle.code.cpp.models.interfaces.Taint
|
||||
|
||||
@@ -13,32 +14,35 @@ import semmle.code.cpp.models.interfaces.Taint
|
||||
* The standard functions `memcpy`, `memmove` and `bcopy`; and the gcc variant
|
||||
* `__builtin___memcpy_chk`.
|
||||
*/
|
||||
private class MemcpyFunction extends ArrayFunction, DataFlowFunction, SideEffectFunction {
|
||||
private class MemcpyFunction extends ArrayFunction, DataFlowFunction, SideEffectFunction,
|
||||
AliasFunction {
|
||||
MemcpyFunction() {
|
||||
// memcpy(dest, src, num)
|
||||
// memmove(dest, src, num)
|
||||
// memmove(dest, src, num, remaining)
|
||||
this.hasName(["memcpy", "memmove", "__builtin___memcpy_chk"])
|
||||
this.hasGlobalOrStdName(["memcpy", "memmove"])
|
||||
or
|
||||
// bcopy(src, dest, num)
|
||||
this.hasGlobalOrStdName("bcopy")
|
||||
// mempcpy(dest, src, num)
|
||||
// memccpy(dest, src, c, n)
|
||||
this.hasGlobalName(["bcopy", mempcpy(), "memccpy", "__builtin___memcpy_chk"])
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the index of the parameter that is the source buffer for the copy.
|
||||
*/
|
||||
int getParamSrc() { if this.hasGlobalOrStdName("bcopy") then result = 0 else result = 1 }
|
||||
int getParamSrc() { if this.hasGlobalName("bcopy") then result = 0 else result = 1 }
|
||||
|
||||
/**
|
||||
* Gets the index of the parameter that is the destination buffer for the
|
||||
* copy.
|
||||
*/
|
||||
int getParamDest() { if this.hasGlobalOrStdName("bcopy") then result = 1 else result = 0 }
|
||||
int getParamDest() { if this.hasGlobalName("bcopy") then result = 1 else result = 0 }
|
||||
|
||||
/**
|
||||
* Gets the index of the parameter that is the size of the copy (in bytes).
|
||||
*/
|
||||
int getParamSize() { result = 2 }
|
||||
int getParamSize() { if this.hasGlobalName("memccpy") then result = 3 else result = 2 }
|
||||
|
||||
override predicate hasArrayInput(int bufParam) { bufParam = getParamSrc() }
|
||||
|
||||
@@ -68,7 +72,10 @@ private class MemcpyFunction extends ArrayFunction, DataFlowFunction, SideEffect
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
i = getParamDest() and buffer = true and mustWrite = true
|
||||
i = getParamDest() and
|
||||
buffer = true and
|
||||
// memccpy only writes until a given character `c` is found
|
||||
(if this.hasGlobalName("memccpy") then mustWrite = false else mustWrite = true)
|
||||
}
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
@@ -82,4 +89,21 @@ private class MemcpyFunction extends ArrayFunction, DataFlowFunction, SideEffect
|
||||
i = getParamSrc()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate parameterNeverEscapes(int index) {
|
||||
index = getParamSrc()
|
||||
or
|
||||
this.hasGlobalName("bcopy") and index = getParamDest()
|
||||
}
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) {
|
||||
not this.hasGlobalName("bcopy") and index = getParamDest()
|
||||
}
|
||||
|
||||
override predicate parameterIsAlwaysReturned(int index) {
|
||||
not this.hasGlobalName(["bcopy", mempcpy(), "memccpy"]) and
|
||||
index = getParamDest()
|
||||
}
|
||||
}
|
||||
|
||||
private string mempcpy() { result = ["mempcpy", "wmempcpy"] }
|
||||
|
||||
@@ -15,8 +15,9 @@ import semmle.code.cpp.models.interfaces.SideEffect
|
||||
private class MemsetFunction extends ArrayFunction, DataFlowFunction, AliasFunction,
|
||||
SideEffectFunction {
|
||||
MemsetFunction() {
|
||||
hasGlobalName(["memset", "wmemset", "bzero", "__builtin_memset", "__builtin_memset_chk"]) or
|
||||
hasQualifiedName("std", ["memset", "wmemset"])
|
||||
this.hasGlobalOrStdName(["memset", "wmemset"])
|
||||
or
|
||||
this.hasGlobalName([bzero(), "__builtin_memset", "__builtin_memset_chk"])
|
||||
}
|
||||
|
||||
override predicate hasArrayOutput(int bufParam) { bufParam = 0 }
|
||||
@@ -28,17 +29,17 @@ private class MemsetFunction extends ArrayFunction, DataFlowFunction, AliasFunct
|
||||
|
||||
override predicate hasArrayWithVariableSize(int bufParam, int countParam) {
|
||||
bufParam = 0 and
|
||||
(if hasGlobalName("bzero") then countParam = 1 else countParam = 2)
|
||||
(if hasGlobalName(bzero()) then countParam = 1 else countParam = 2)
|
||||
}
|
||||
|
||||
override predicate parameterNeverEscapes(int index) { hasGlobalName("bzero") and index = 0 }
|
||||
override predicate parameterNeverEscapes(int index) { hasGlobalName(bzero()) and index = 0 }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) {
|
||||
not hasGlobalName("bzero") and index = 0
|
||||
not hasGlobalName(bzero()) and index = 0
|
||||
}
|
||||
|
||||
override predicate parameterIsAlwaysReturned(int index) {
|
||||
not hasGlobalName("bzero") and index = 0
|
||||
not hasGlobalName(bzero()) and index = 0
|
||||
}
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
@@ -51,6 +52,8 @@ private class MemsetFunction extends ArrayFunction, DataFlowFunction, AliasFunct
|
||||
|
||||
override ParameterIndex getParameterSizeIndex(ParameterIndex i) {
|
||||
i = 0 and
|
||||
if hasGlobalName("bzero") then result = 1 else result = 2
|
||||
if hasGlobalName(bzero()) then result = 1 else result = 2
|
||||
}
|
||||
}
|
||||
|
||||
private string bzero() { result = ["bzero", "explicit_bzero"] }
|
||||
|
||||
@@ -8,9 +8,8 @@ private class PureStrFunction extends AliasFunction, ArrayFunction, TaintFunctio
|
||||
SideEffectFunction {
|
||||
PureStrFunction() {
|
||||
hasGlobalOrStdName([
|
||||
"atof", "atoi", "atol", "atoll", "strcasestr", "strchnul", "strchr", "strchrnul", "strstr",
|
||||
"strpbrk", "strcmp", "strcspn", "strncmp", "strrchr", "strspn", "strtod", "strtof",
|
||||
"strtol", "strtoll", "strtoq", "strtoul"
|
||||
atoi(), "strcasestr", "strchnul", "strchr", "strchrnul", "strstr", "strpbrk", "strrchr",
|
||||
"strspn", strtol(), strrev(), strcmp(), strlwr(), strupr()
|
||||
])
|
||||
}
|
||||
|
||||
@@ -24,11 +23,16 @@ private class PureStrFunction extends AliasFunction, ArrayFunction, TaintFunctio
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
exists(ParameterIndex i |
|
||||
input.isParameter(i) and
|
||||
exists(getParameter(i))
|
||||
or
|
||||
input.isParameterDeref(i) and
|
||||
getParameter(i).getUnspecifiedType() instanceof PointerType
|
||||
(
|
||||
input.isParameter(i) and
|
||||
exists(getParameter(i))
|
||||
or
|
||||
input.isParameterDeref(i) and
|
||||
getParameter(i).getUnspecifiedType() instanceof PointerType
|
||||
) and
|
||||
// Functions that end with _l also take a locale argument (always as the last argument),
|
||||
// and we don't want taint from those arguments.
|
||||
(not this.getName().matches("%\\_l") or exists(getParameter(i + 1)))
|
||||
) and
|
||||
(
|
||||
output.isReturnValueDeref() and
|
||||
@@ -60,6 +64,31 @@ private class PureStrFunction extends AliasFunction, ArrayFunction, TaintFunctio
|
||||
}
|
||||
}
|
||||
|
||||
private string atoi() { result = ["atof", "atoi", "atol", "atoll"] }
|
||||
|
||||
private string strtol() { result = ["strtod", "strtof", "strtol", "strtoll", "strtoq", "strtoul"] }
|
||||
|
||||
private string strlwr() {
|
||||
result = ["_strlwr", "_wcslwr", "_mbslwr", "_strlwr_l", "_wcslwr_l", "_mbslwr_l"]
|
||||
}
|
||||
|
||||
private string strupr() {
|
||||
result = ["_strupr", "_wcsupr", "_mbsupr", "_strupr_l", "_wcsupr_l", "_mbsupr_l"]
|
||||
}
|
||||
|
||||
private string strrev() { result = ["_strrev", "_wcsrev", "_mbsrev", "_mbsrev_l"] }
|
||||
|
||||
private string strcmp() {
|
||||
// NOTE: `strcoll` doesn't satisfy _all_ the definitions of purity: its behavior depends on
|
||||
// `LC_COLLATE` (which is set by `setlocale`). Not sure this behavior worth including in the model, so
|
||||
// for now we interpret the function as being pure.
|
||||
result =
|
||||
[
|
||||
"strcmp", "strcspn", "strncmp", "strcoll", "strverscmp", "_mbsnbcmp", "_mbsnbcmp_l",
|
||||
"_stricmp"
|
||||
]
|
||||
}
|
||||
|
||||
/** String standard `strlen` function, and related functions for computing string lengths. */
|
||||
private class StrLenFunction extends AliasFunction, ArrayFunction, SideEffectFunction {
|
||||
StrLenFunction() {
|
||||
@@ -114,7 +143,12 @@ private class PureFunction extends TaintFunction, SideEffectFunction {
|
||||
/** Pure raw-memory functions. */
|
||||
private class PureMemFunction extends AliasFunction, ArrayFunction, TaintFunction,
|
||||
SideEffectFunction {
|
||||
PureMemFunction() { hasGlobalOrStdName(["memchr", "memrchr", "rawmemchr", "memcmp", "memmem"]) }
|
||||
PureMemFunction() {
|
||||
hasGlobalOrStdName([
|
||||
"memchr", "__builtin_memchr", "memrchr", "rawmemchr", "memcmp", "__builtin_memcmp", "memmem"
|
||||
]) or
|
||||
this.hasGlobalName("memfrob")
|
||||
}
|
||||
|
||||
override predicate hasArrayInput(int bufParam) {
|
||||
getParameter(bufParam).getUnspecifiedType() instanceof PointerType
|
||||
@@ -122,11 +156,15 @@ private class PureMemFunction extends AliasFunction, ArrayFunction, TaintFunctio
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
exists(ParameterIndex i |
|
||||
input.isParameter(i) and
|
||||
exists(getParameter(i))
|
||||
or
|
||||
input.isParameterDeref(i) and
|
||||
getParameter(i).getUnspecifiedType() instanceof PointerType
|
||||
(
|
||||
input.isParameter(i) and
|
||||
exists(getParameter(i))
|
||||
or
|
||||
input.isParameterDeref(i) and
|
||||
getParameter(i).getUnspecifiedType() instanceof PointerType
|
||||
) and
|
||||
// `memfrob` should not have taint from the size argument.
|
||||
(not this.hasGlobalName("memfrob") or i = 0)
|
||||
) and
|
||||
(
|
||||
output.isReturnValueDeref() and
|
||||
|
||||
72
cpp/ql/src/semmle/code/cpp/models/implementations/Sscanf.qll
Normal file
72
cpp/ql/src/semmle/code/cpp/models/implementations/Sscanf.qll
Normal file
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* Provides implementation classes modeling `sscanf`, `fscanf` and various similar
|
||||
* functions. See `semmle.code.cpp.models.Models` for usage information.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.Function
|
||||
import semmle.code.cpp.commons.Scanf
|
||||
import semmle.code.cpp.models.interfaces.ArrayFunction
|
||||
import semmle.code.cpp.models.interfaces.Taint
|
||||
import semmle.code.cpp.models.interfaces.Alias
|
||||
import semmle.code.cpp.models.interfaces.SideEffect
|
||||
|
||||
/**
|
||||
* The standard function `sscanf`, `fscanf` and its assorted variants
|
||||
*/
|
||||
private class SscanfModel extends ArrayFunction, TaintFunction, AliasFunction, SideEffectFunction {
|
||||
SscanfModel() { this instanceof Sscanf or this instanceof Fscanf or this instanceof Snscanf }
|
||||
|
||||
override predicate hasArrayWithNullTerminator(int bufParam) {
|
||||
bufParam = this.(ScanfFunction).getFormatParameterIndex()
|
||||
or
|
||||
not this instanceof Fscanf and
|
||||
bufParam = this.(ScanfFunction).getInputParameterIndex()
|
||||
}
|
||||
|
||||
override predicate hasArrayInput(int bufParam) { hasArrayWithNullTerminator(bufParam) }
|
||||
|
||||
private int getLengthParameterIndex() { result = this.(Snscanf).getInputLengthParameterIndex() }
|
||||
|
||||
private int getLocaleParameterIndex() {
|
||||
this.getName().matches("%\\_l") and
|
||||
(
|
||||
if exists(getLengthParameterIndex())
|
||||
then result = getLengthParameterIndex() + 2
|
||||
else result = 2
|
||||
)
|
||||
}
|
||||
|
||||
private int getArgsStartPosition() { result = this.getNumberOfParameters() }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isParameterDeref(this.(ScanfFunction).getInputParameterIndex()) and
|
||||
output.isParameterDeref(any(int i | i >= getArgsStartPosition()))
|
||||
}
|
||||
|
||||
override predicate parameterNeverEscapes(int index) {
|
||||
index = [0 .. max(getACallToThisFunction().getNumberOfArguments())]
|
||||
}
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||
|
||||
override predicate parameterIsAlwaysReturned(int index) { none() }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
i >= getArgsStartPosition() and
|
||||
buffer = true and
|
||||
mustWrite = true
|
||||
}
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
buffer = true and
|
||||
i =
|
||||
[
|
||||
this.(ScanfFunction).getInputParameterIndex(),
|
||||
this.(ScanfFunction).getFormatParameterIndex(), getLocaleParameterIndex()
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -193,7 +193,7 @@ class StdVectorEmplace extends TaintFunction {
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from any parameter except the position iterator to qualifier and return value
|
||||
// (here we assume taint flow from any constructor parameter to the constructed object)
|
||||
input.isParameter([1 .. getNumberOfParameters() - 1]) and
|
||||
input.isParameterDeref([1 .. getNumberOfParameters() - 1]) and
|
||||
(
|
||||
output.isQualifierObject() or
|
||||
output.isReturnValue()
|
||||
@@ -210,7 +210,7 @@ class StdVectorEmplaceBack extends TaintFunction {
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from any parameter to qualifier
|
||||
// (here we assume taint flow from any constructor parameter to the constructed object)
|
||||
input.isParameter([0 .. getNumberOfParameters() - 1]) and
|
||||
input.isParameterDeref([0 .. getNumberOfParameters() - 1]) and
|
||||
output.isQualifierObject()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,9 +9,9 @@ import semmle.code.cpp.models.interfaces.Iterator
|
||||
import semmle.code.cpp.models.interfaces.DataFlow
|
||||
|
||||
/**
|
||||
* The `std::basic_string` template class.
|
||||
* The `std::basic_string` template class instantiations.
|
||||
*/
|
||||
private class StdBasicString extends TemplateClass {
|
||||
private class StdBasicString extends ClassTemplateInstantiation {
|
||||
StdBasicString() { this.hasQualifiedName("std", "basic_string") }
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ private class StdBasicString extends TemplateClass {
|
||||
* ```
|
||||
*/
|
||||
private class StdStringConstructor extends Constructor, TaintFunction {
|
||||
StdStringConstructor() { this.getDeclaringType().hasQualifiedName("std", "basic_string") }
|
||||
StdStringConstructor() { this.getDeclaringType() instanceof StdBasicString }
|
||||
|
||||
/**
|
||||
* Gets the index of a parameter to this function that is a string (or
|
||||
@@ -69,7 +69,7 @@ private class StdStringConstructor extends Constructor, TaintFunction {
|
||||
* The `std::string` function `c_str`.
|
||||
*/
|
||||
private class StdStringCStr extends TaintFunction {
|
||||
StdStringCStr() { this.hasQualifiedName("std", "basic_string", "c_str") }
|
||||
StdStringCStr() { this.getClassAndName("c_str") instanceof StdBasicString }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from string itself (qualifier) to return value
|
||||
@@ -82,7 +82,7 @@ private class StdStringCStr extends TaintFunction {
|
||||
* The `std::string` function `data`.
|
||||
*/
|
||||
private class StdStringData extends TaintFunction {
|
||||
StdStringData() { this.hasQualifiedName("std", "basic_string", "data") }
|
||||
StdStringData() { this.getClassAndName("data") instanceof StdBasicString }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from string itself (qualifier) to return value
|
||||
@@ -100,7 +100,7 @@ private class StdStringData extends TaintFunction {
|
||||
* The `std::string` function `push_back`.
|
||||
*/
|
||||
private class StdStringPush extends TaintFunction {
|
||||
StdStringPush() { this.hasQualifiedName("std", "basic_string", "push_back") }
|
||||
StdStringPush() { this.getClassAndName("push_back") instanceof StdBasicString }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from parameter to qualifier
|
||||
@@ -113,7 +113,7 @@ private class StdStringPush extends TaintFunction {
|
||||
* The `std::string` functions `front` and `back`.
|
||||
*/
|
||||
private class StdStringFrontBack extends TaintFunction {
|
||||
StdStringFrontBack() { this.hasQualifiedName("std", "basic_string", ["front", "back"]) }
|
||||
StdStringFrontBack() { this.getClassAndName(["front", "back"]) instanceof StdBasicString }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from object to returned reference
|
||||
@@ -123,12 +123,12 @@ private class StdStringFrontBack extends TaintFunction {
|
||||
}
|
||||
|
||||
/**
|
||||
* The `std::string` function `operator+`.
|
||||
* The (non-member) `std::string` function `operator+`.
|
||||
*/
|
||||
private class StdStringPlus extends TaintFunction {
|
||||
StdStringPlus() {
|
||||
this.hasQualifiedName("std", "operator+") and
|
||||
this.getUnspecifiedType() = any(StdBasicString s).getAnInstantiation()
|
||||
this.getUnspecifiedType() instanceof StdBasicString
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -148,7 +148,7 @@ private class StdStringPlus extends TaintFunction {
|
||||
*/
|
||||
private class StdStringAppend extends TaintFunction {
|
||||
StdStringAppend() {
|
||||
this.hasQualifiedName("std", "basic_string", ["operator+=", "append", "insert", "replace"])
|
||||
this.getClassAndName(["operator+=", "append", "insert", "replace"]) instanceof StdBasicString
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -190,7 +190,7 @@ private class StdStringAppend extends TaintFunction {
|
||||
* The standard function `std::string.assign`.
|
||||
*/
|
||||
private class StdStringAssign extends TaintFunction {
|
||||
StdStringAssign() { this.hasQualifiedName("std", "basic_string", "assign") }
|
||||
StdStringAssign() { this.getClassAndName("assign") instanceof StdBasicString }
|
||||
|
||||
/**
|
||||
* Gets the index of a parameter to this function that is a string (or
|
||||
@@ -230,7 +230,7 @@ private class StdStringAssign extends TaintFunction {
|
||||
* The standard function `std::string.copy`.
|
||||
*/
|
||||
private class StdStringCopy extends TaintFunction {
|
||||
StdStringCopy() { this.hasQualifiedName("std", "basic_string", "copy") }
|
||||
StdStringCopy() { this.getClassAndName("copy") instanceof StdBasicString }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// copy(dest, num, pos)
|
||||
@@ -243,7 +243,7 @@ private class StdStringCopy extends TaintFunction {
|
||||
* The standard function `std::string.substr`.
|
||||
*/
|
||||
private class StdStringSubstr extends TaintFunction {
|
||||
StdStringSubstr() { this.hasQualifiedName("std", "basic_string", "substr") }
|
||||
StdStringSubstr() { this.getClassAndName("substr") instanceof StdBasicString }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// substr(pos, num)
|
||||
@@ -252,11 +252,18 @@ private class StdStringSubstr extends TaintFunction {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The `std::basic_stringstream` template class instantiations.
|
||||
*/
|
||||
private class StdBasicStringStream extends ClassTemplateInstantiation {
|
||||
StdBasicStringStream() { this.hasQualifiedName("std", "basic_stringstream") }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `std::string` functions `at` and `operator[]`.
|
||||
*/
|
||||
private class StdStringAt extends TaintFunction {
|
||||
StdStringAt() { this.hasQualifiedName("std", "basic_string", ["at", "operator[]"]) }
|
||||
StdStringAt() { this.getClassAndName(["at", "operator[]"]) instanceof StdBasicString }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from qualifier to referenced return value
|
||||
@@ -270,9 +277,9 @@ private class StdStringAt extends TaintFunction {
|
||||
}
|
||||
|
||||
/**
|
||||
* The `std::basic_istream` template class.
|
||||
* The `std::basic_istream` template class instantiations.
|
||||
*/
|
||||
private class StdBasicIStream extends TemplateClass {
|
||||
private class StdBasicIStream extends ClassTemplateInstantiation {
|
||||
StdBasicIStream() { this.hasQualifiedName("std", "basic_istream") }
|
||||
}
|
||||
|
||||
@@ -280,12 +287,15 @@ private class StdBasicIStream extends TemplateClass {
|
||||
* The `std::istream` function `operator>>` (defined as a member function).
|
||||
*/
|
||||
private class StdIStreamIn extends DataFlowFunction, TaintFunction {
|
||||
StdIStreamIn() { this.hasQualifiedName("std", "basic_istream", "operator>>") }
|
||||
StdIStreamIn() { this.getClassAndName("operator>>") instanceof StdBasicIStream }
|
||||
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
// returns reference to `*this`
|
||||
input.isQualifierAddress() and
|
||||
output.isReturnValue()
|
||||
or
|
||||
input.isQualifierObject() and
|
||||
output.isReturnValueDeref()
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -305,14 +315,16 @@ private class StdIStreamIn extends DataFlowFunction, TaintFunction {
|
||||
private class StdIStreamInNonMember extends DataFlowFunction, TaintFunction {
|
||||
StdIStreamInNonMember() {
|
||||
this.hasQualifiedName("std", "operator>>") and
|
||||
this.getUnspecifiedType().(ReferenceType).getBaseType() =
|
||||
any(StdBasicIStream s).getAnInstantiation()
|
||||
this.getUnspecifiedType().(ReferenceType).getBaseType() instanceof StdBasicIStream
|
||||
}
|
||||
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from first parameter to return value
|
||||
input.isParameter(0) and
|
||||
output.isReturnValue()
|
||||
or
|
||||
input.isParameterDeref(0) and
|
||||
output.isReturnValueDeref()
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -331,7 +343,7 @@ private class StdIStreamInNonMember extends DataFlowFunction, TaintFunction {
|
||||
*/
|
||||
private class StdIStreamGet extends TaintFunction {
|
||||
StdIStreamGet() {
|
||||
this.hasQualifiedName("std", "basic_istream", ["get", "peek"]) and
|
||||
this.getClassAndName(["get", "peek"]) instanceof StdBasicIStream and
|
||||
this.getNumberOfParameters() = 0
|
||||
}
|
||||
|
||||
@@ -347,7 +359,7 @@ private class StdIStreamGet extends TaintFunction {
|
||||
*/
|
||||
private class StdIStreamRead extends DataFlowFunction, TaintFunction {
|
||||
StdIStreamRead() {
|
||||
this.hasQualifiedName("std", "basic_istream", ["get", "read"]) and
|
||||
this.getClassAndName(["get", "read"]) instanceof StdBasicIStream and
|
||||
this.getNumberOfParameters() > 0
|
||||
}
|
||||
|
||||
@@ -355,6 +367,9 @@ private class StdIStreamRead extends DataFlowFunction, TaintFunction {
|
||||
// returns reference to `*this`
|
||||
input.isQualifierAddress() and
|
||||
output.isReturnValue()
|
||||
or
|
||||
input.isQualifierObject() and
|
||||
output.isReturnValueDeref()
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -372,7 +387,7 @@ private class StdIStreamRead extends DataFlowFunction, TaintFunction {
|
||||
* The `std::istream` function `readsome`.
|
||||
*/
|
||||
private class StdIStreamReadSome extends TaintFunction {
|
||||
StdIStreamReadSome() { this.hasQualifiedName("std", "basic_istream", "readsome") }
|
||||
StdIStreamReadSome() { this.getClassAndName("readsome") instanceof StdBasicIStream }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from qualifier to first parameter
|
||||
@@ -385,12 +400,15 @@ private class StdIStreamReadSome extends TaintFunction {
|
||||
* The `std::istream` function `putback`.
|
||||
*/
|
||||
private class StdIStreamPutBack extends DataFlowFunction, TaintFunction {
|
||||
StdIStreamPutBack() { this.hasQualifiedName("std", "basic_istream", "putback") }
|
||||
StdIStreamPutBack() { this.getClassAndName("putback") instanceof StdBasicIStream }
|
||||
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
// returns reference to `*this`
|
||||
input.isQualifierAddress() and
|
||||
output.isReturnValue()
|
||||
or
|
||||
input.isQualifierObject() and
|
||||
output.isReturnValueDeref()
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -418,12 +436,15 @@ private class StdIStreamPutBack extends DataFlowFunction, TaintFunction {
|
||||
* The `std::istream` function `getline`.
|
||||
*/
|
||||
private class StdIStreamGetLine extends DataFlowFunction, TaintFunction {
|
||||
StdIStreamGetLine() { this.hasQualifiedName("std", "basic_istream", "getline") }
|
||||
StdIStreamGetLine() { this.getClassAndName("getline") instanceof StdBasicIStream }
|
||||
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
// returns reference to `*this`
|
||||
input.isQualifierAddress() and
|
||||
output.isReturnValue()
|
||||
or
|
||||
input.isQualifierObject() and
|
||||
output.isReturnValueDeref()
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -447,6 +468,9 @@ private class StdGetLine extends DataFlowFunction, TaintFunction {
|
||||
// flow from first parameter to return value
|
||||
input.isParameter(0) and
|
||||
output.isReturnValue()
|
||||
or
|
||||
input.isParameterDeref(0) and
|
||||
output.isReturnValueDeref()
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -461,9 +485,9 @@ private class StdGetLine extends DataFlowFunction, TaintFunction {
|
||||
}
|
||||
|
||||
/**
|
||||
* The `std::basic_ostream` template class.
|
||||
* The `std::basic_ostream` template class instantiations.
|
||||
*/
|
||||
private class StdBasicOStream extends TemplateClass {
|
||||
private class StdBasicOStream extends ClassTemplateInstantiation {
|
||||
StdBasicOStream() { this.hasQualifiedName("std", "basic_ostream") }
|
||||
}
|
||||
|
||||
@@ -472,12 +496,17 @@ private class StdBasicOStream extends TemplateClass {
|
||||
* `put` and `write`.
|
||||
*/
|
||||
private class StdOStreamOut extends DataFlowFunction, TaintFunction {
|
||||
StdOStreamOut() { this.hasQualifiedName("std", "basic_ostream", ["operator<<", "put", "write"]) }
|
||||
StdOStreamOut() {
|
||||
this.getClassAndName(["operator<<", "put", "write"]) instanceof StdBasicOStream
|
||||
}
|
||||
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
// returns reference to `*this`
|
||||
input.isQualifierAddress() and
|
||||
output.isReturnValue()
|
||||
or
|
||||
input.isQualifierObject() and
|
||||
output.isReturnValueDeref()
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -507,14 +536,16 @@ private class StdOStreamOut extends DataFlowFunction, TaintFunction {
|
||||
private class StdOStreamOutNonMember extends DataFlowFunction, TaintFunction {
|
||||
StdOStreamOutNonMember() {
|
||||
this.hasQualifiedName("std", "operator<<") and
|
||||
this.getUnspecifiedType().(ReferenceType).getBaseType() =
|
||||
any(StdBasicOStream s).getAnInstantiation()
|
||||
this.getUnspecifiedType().(ReferenceType).getBaseType() instanceof StdBasicOStream
|
||||
}
|
||||
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from first parameter to return value
|
||||
input.isParameter(0) and
|
||||
output.isReturnValue()
|
||||
or
|
||||
input.isParameterDeref(0) and
|
||||
output.isReturnValueDeref()
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -537,9 +568,7 @@ private class StdOStreamOutNonMember extends DataFlowFunction, TaintFunction {
|
||||
* input parameter.
|
||||
*/
|
||||
private class StdStringStreamConstructor extends Constructor, TaintFunction {
|
||||
StdStringStreamConstructor() {
|
||||
this.getDeclaringType().hasQualifiedName("std", "basic_stringstream")
|
||||
}
|
||||
StdStringStreamConstructor() { this.getDeclaringType() instanceof StdBasicStringStream }
|
||||
|
||||
/**
|
||||
* Gets the index of a parameter to this function that is a string.
|
||||
@@ -563,7 +592,7 @@ private class StdStringStreamConstructor extends Constructor, TaintFunction {
|
||||
* The `std::stringstream` function `str`.
|
||||
*/
|
||||
private class StdStringStreamStr extends TaintFunction {
|
||||
StdStringStreamStr() { this.hasQualifiedName("std", "basic_stringstream", "str") }
|
||||
StdStringStreamStr() { this.getClassAndName("str") instanceof StdBasicStringStream }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from qualifier to return value (if any)
|
||||
@@ -576,21 +605,33 @@ private class StdStringStreamStr extends TaintFunction {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The `std::basic_ios` template class instantiations.
|
||||
*/
|
||||
private class StdBasicIOS extends ClassTemplateInstantiation {
|
||||
StdBasicIOS() { this.hasQualifiedName("std", "basic_ios") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `std::` stream function that does not require a model, except that it
|
||||
* returns a reference to `*this` and thus could be used in a chain.
|
||||
*/
|
||||
private class StdStreamFunction extends DataFlowFunction, TaintFunction {
|
||||
StdStreamFunction() {
|
||||
this.hasQualifiedName("std", "basic_istream", ["ignore", "unget", "seekg"]) or
|
||||
this.hasQualifiedName("std", "basic_ostream", ["seekp", "flush"]) or
|
||||
this.hasQualifiedName("std", "basic_ios", "copyfmt")
|
||||
this.getClassAndName(["ignore", "unget", "seekg"]) instanceof StdBasicIStream
|
||||
or
|
||||
this.getClassAndName(["seekp", "flush"]) instanceof StdBasicOStream
|
||||
or
|
||||
this.getClassAndName("copyfmt") instanceof StdBasicIOS
|
||||
}
|
||||
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
// returns reference to `*this`
|
||||
input.isQualifierAddress() and
|
||||
output.isReturnValue()
|
||||
or
|
||||
input.isQualifierObject() and
|
||||
output.isReturnValueDeref()
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
|
||||
@@ -13,16 +13,20 @@ import semmle.code.cpp.models.interfaces.SideEffect
|
||||
*/
|
||||
class StrcatFunction extends TaintFunction, DataFlowFunction, ArrayFunction, SideEffectFunction {
|
||||
StrcatFunction() {
|
||||
getName() =
|
||||
[
|
||||
this.hasGlobalOrStdName([
|
||||
"strcat", // strcat(dst, src)
|
||||
"strncat", // strncat(dst, src, max_amount)
|
||||
"wcscat", // wcscat(dst, src)
|
||||
"wcsncat" // wcsncat(dst, src, max_amount)
|
||||
])
|
||||
or
|
||||
this.hasGlobalName([
|
||||
"_mbscat", // _mbscat(dst, src)
|
||||
"wcsncat", // wcsncat(dst, src, max_amount)
|
||||
"_mbsncat", // _mbsncat(dst, src, max_amount)
|
||||
"_mbsncat_l" // _mbsncat_l(dst, src, max_amount, locale)
|
||||
]
|
||||
"_mbsncat_l", // _mbsncat_l(dst, src, max_amount, locale)
|
||||
"_mbsnbcat", // _mbsnbcat(dest, src, count)
|
||||
"_mbsnbcat_l" // _mbsnbcat_l(dest, src, count, locale)
|
||||
])
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -50,7 +54,7 @@ class StrcatFunction extends TaintFunction, DataFlowFunction, ArrayFunction, Sid
|
||||
input.isParameter(2) and
|
||||
output.isParameterDeref(0)
|
||||
or
|
||||
getName() = "_mbsncat_l" and
|
||||
getName() = ["_mbsncat_l", "_mbsnbcat_l"] and
|
||||
input.isParameter(3) and
|
||||
output.isParameterDeref(0)
|
||||
or
|
||||
|
||||
@@ -13,25 +13,36 @@ import semmle.code.cpp.models.interfaces.SideEffect
|
||||
*/
|
||||
class StrcpyFunction extends ArrayFunction, DataFlowFunction, TaintFunction, SideEffectFunction {
|
||||
StrcpyFunction() {
|
||||
getName() =
|
||||
[
|
||||
this.hasGlobalOrStdName([
|
||||
"strcpy", // strcpy(dst, src)
|
||||
"wcscpy", // wcscpy(dst, src)
|
||||
"_mbscpy", // _mbscpy(dst, src)
|
||||
"strncpy", // strncpy(dst, src, max_amount)
|
||||
"_strncpy_l", // _strncpy_l(dst, src, max_amount, locale)
|
||||
"wcsncpy", // wcsncpy(dst, src, max_amount)
|
||||
"strxfrm", // strxfrm(dest, src, max_amount)
|
||||
"wcsxfrm" // wcsxfrm(dest, src, max_amount)
|
||||
])
|
||||
or
|
||||
this.hasGlobalName([
|
||||
"_mbscpy", // _mbscpy(dst, src)
|
||||
"_strncpy_l", // _strncpy_l(dst, src, max_amount, locale)
|
||||
"_wcsncpy_l", // _wcsncpy_l(dst, src, max_amount, locale)
|
||||
"_mbsncpy", // _mbsncpy(dst, src, max_amount)
|
||||
"_mbsncpy_l"
|
||||
] // _mbsncpy_l(dst, src, max_amount, locale)
|
||||
"_mbsncpy_l", // _mbsncpy_l(dst, src, max_amount, locale)
|
||||
"_strxfrm_l", // _strxfrm_l(dest, src, max_amount, locale)
|
||||
"wcsxfrm_l", // _strxfrm_l(dest, src, max_amount, locale)
|
||||
"_mbsnbcpy", // _mbsnbcpy(dest, src, max_amount)
|
||||
"stpcpy", // stpcpy(dest, src)
|
||||
"stpncpy" // stpcpy(dest, src, max_amount)
|
||||
])
|
||||
or
|
||||
getName() =
|
||||
[
|
||||
"strcpy_s", // strcpy_s(dst, max_amount, src)
|
||||
"wcscpy_s", // wcscpy_s(dst, max_amount, src)
|
||||
"_mbscpy_s"
|
||||
] and // _mbscpy_s(dst, max_amount, src)
|
||||
(
|
||||
this.hasGlobalOrStdName([
|
||||
"strcpy_s", // strcpy_s(dst, max_amount, src)
|
||||
"wcscpy_s" // wcscpy_s(dst, max_amount, src)
|
||||
])
|
||||
or
|
||||
this.hasGlobalName("_mbscpy_s") // _mbscpy_s(dst, max_amount, src)
|
||||
) and
|
||||
// exclude the 2-parameter template versions
|
||||
// that find the size of a fixed size destination buffer.
|
||||
getNumberOfParameters() = 3
|
||||
@@ -40,9 +51,7 @@ class StrcpyFunction extends ArrayFunction, DataFlowFunction, TaintFunction, Sid
|
||||
/**
|
||||
* Holds if this is one of the `strcpy_s` variants.
|
||||
*/
|
||||
private predicate isSVariant() {
|
||||
exists(string name | name = getName() | name.suffix(name.length() - 2) = "_s")
|
||||
}
|
||||
private predicate isSVariant() { getName().matches("%\\_s") }
|
||||
|
||||
/**
|
||||
* Gets the index of the parameter that is the maximum size of the copy (in characters).
|
||||
@@ -50,10 +59,10 @@ class StrcpyFunction extends ArrayFunction, DataFlowFunction, TaintFunction, Sid
|
||||
int getParamSize() {
|
||||
if isSVariant()
|
||||
then result = 1
|
||||
else
|
||||
if exists(getName().indexOf("ncpy"))
|
||||
then result = 2
|
||||
else none()
|
||||
else (
|
||||
getName().matches(["%ncpy%", "%nbcpy%", "%xfrm%"]) and
|
||||
result = 2
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* Provides implementation classes modeling `_strinc`, `_strdec` and their variants.
|
||||
* See `semmle.code.cpp.models.Models` for usage information.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.models.interfaces.ArrayFunction
|
||||
import semmle.code.cpp.models.interfaces.Taint
|
||||
import semmle.code.cpp.models.interfaces.SideEffect
|
||||
|
||||
/**
|
||||
* The function `_strinc`, `_strdec` and their variants.
|
||||
*/
|
||||
private class Strcrement extends ArrayFunction, TaintFunction, SideEffectFunction {
|
||||
Strcrement() {
|
||||
this.hasGlobalName([
|
||||
"_strinc", // _strinc(source, locale)
|
||||
"_wcsinc", // _strinc(source, locale)
|
||||
"_mbsinc", // _strinc(source)
|
||||
"_mbsinc_l", // _strinc(source, locale)
|
||||
"_strdec", // _strdec(start, source)
|
||||
"_wcsdec", // _wcsdec(start, source)
|
||||
"_mbsdec", // _mbsdec(start, source)
|
||||
"_mbsdec_l" // _mbsdec_l(start, source, locale)
|
||||
])
|
||||
}
|
||||
|
||||
override predicate hasArrayWithNullTerminator(int bufParam) {
|
||||
// Match all parameters that are not locales.
|
||||
this.getParameter(bufParam).getUnspecifiedType() instanceof PointerType
|
||||
}
|
||||
|
||||
override predicate hasArrayInput(int bufParam) { hasArrayWithNullTerminator(bufParam) }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
exists(int index | hasArrayInput(index) |
|
||||
input.isParameter(index) and output.isReturnValue()
|
||||
or
|
||||
input.isParameterDeref(index) and output.isReturnValueDeref()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
hasArrayInput(i) and buffer = true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Provides implementation classes modeling `strnextc` and various similar functions.
|
||||
* See `semmle.code.cpp.models.Models` for usage information.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.models.interfaces.ArrayFunction
|
||||
import semmle.code.cpp.models.interfaces.Taint
|
||||
import semmle.code.cpp.models.interfaces.Alias
|
||||
import semmle.code.cpp.models.interfaces.SideEffect
|
||||
|
||||
/**
|
||||
* The function `strnextc` and its variants.
|
||||
*/
|
||||
private class Strnextc extends TaintFunction, ArrayFunction, AliasFunction, SideEffectFunction {
|
||||
Strnextc() { this.hasGlobalName(["_strnextc", "_wcsnextc", "_mbsnextc", "_mbsnextc_l"]) }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isParameterDeref(0) and output.isReturnValue()
|
||||
}
|
||||
|
||||
override predicate hasArrayWithNullTerminator(int bufParam) { bufParam = 0 }
|
||||
|
||||
override predicate hasArrayInput(int bufParam) { bufParam = 0 }
|
||||
|
||||
override predicate parameterNeverEscapes(int index) { index = 0 }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||
|
||||
override predicate parameterIsAlwaysReturned(int index) { none() }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
i = 0 and buffer = true
|
||||
}
|
||||
}
|
||||
62
cpp/ql/src/semmle/code/cpp/models/implementations/Strset.qll
Normal file
62
cpp/ql/src/semmle/code/cpp/models/implementations/Strset.qll
Normal file
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* Provides implementation classes modeling `strset` and various similar
|
||||
* functions. See `semmle.code.cpp.models.Models` for usage information.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.Function
|
||||
import semmle.code.cpp.models.interfaces.ArrayFunction
|
||||
import semmle.code.cpp.models.interfaces.DataFlow
|
||||
import semmle.code.cpp.models.interfaces.Alias
|
||||
import semmle.code.cpp.models.interfaces.SideEffect
|
||||
|
||||
/**
|
||||
* The standard function `strset` and its assorted variants
|
||||
*/
|
||||
private class StrsetFunction extends ArrayFunction, DataFlowFunction, AliasFunction,
|
||||
SideEffectFunction {
|
||||
StrsetFunction() {
|
||||
hasGlobalName([
|
||||
"strset", "_strset", "_strset_l", "_wcsset", "_wcsset_l", "_mbsset", "_mbsset_l",
|
||||
"_mbsnbset", "_mbsnbset_l", "_strnset", "_strnset_l", "_wcsnset", "_wcsnset_l", "_mbsnset",
|
||||
"_mbsnset_l"
|
||||
])
|
||||
}
|
||||
|
||||
override predicate hasArrayWithNullTerminator(int bufParam) { bufParam = 0 }
|
||||
|
||||
override predicate hasArrayInput(int bufParam) { bufParam = 0 }
|
||||
|
||||
override predicate hasArrayOutput(int bufParam) { bufParam = 0 }
|
||||
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from the character that overrides the string
|
||||
input.isParameter(1) and
|
||||
(
|
||||
output.isReturnValueDeref()
|
||||
or
|
||||
output.isParameterDeref(0)
|
||||
)
|
||||
or
|
||||
// flow from the input string to the output string
|
||||
input.isParameter(0) and
|
||||
output.isReturnValue()
|
||||
}
|
||||
|
||||
override predicate parameterNeverEscapes(int index) { none() }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { index = 0 }
|
||||
|
||||
override predicate parameterIsAlwaysReturned(int index) { index = 0 }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
i = 0 and buffer = true and mustWrite = true
|
||||
}
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
i = 0 and buffer = true
|
||||
}
|
||||
}
|
||||
88
cpp/ql/src/semmle/code/cpp/models/implementations/Strtok.qll
Normal file
88
cpp/ql/src/semmle/code/cpp/models/implementations/Strtok.qll
Normal file
@@ -0,0 +1,88 @@
|
||||
/**
|
||||
* Provides implementation classes modeling `strtok` and various similar
|
||||
* functions. See `semmle.code.cpp.models.Models` for usage information.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.Function
|
||||
import semmle.code.cpp.models.interfaces.ArrayFunction
|
||||
import semmle.code.cpp.models.interfaces.ArrayFunction
|
||||
import semmle.code.cpp.models.interfaces.Alias
|
||||
import semmle.code.cpp.models.interfaces.SideEffect
|
||||
import semmle.code.cpp.models.interfaces.Taint
|
||||
|
||||
/**
|
||||
* The standard function `strtok` and its assorted variants
|
||||
*/
|
||||
private class Strtok extends ArrayFunction, AliasFunction, TaintFunction, SideEffectFunction {
|
||||
Strtok() {
|
||||
this.hasGlobalOrStdName("strtok") or
|
||||
this.hasGlobalName(["strtok_r", "_strtok_l", "wcstok", "_wcstok_l", "_mbstok", "_mbstok_l"])
|
||||
}
|
||||
|
||||
override predicate hasArrayWithNullTerminator(int bufParam) { bufParam = [0, 1] }
|
||||
|
||||
override predicate hasArrayInput(int bufParam) { bufParam = [0, 1] }
|
||||
|
||||
override predicate hasArrayOutput(int bufParam) { bufParam = 0 }
|
||||
|
||||
override predicate parameterNeverEscapes(int index) { index = 1 }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { index = 0 }
|
||||
|
||||
override predicate parameterIsAlwaysReturned(int index) { none() }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isParameter(0) and output.isReturnValue()
|
||||
}
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { none() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { none() }
|
||||
|
||||
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
i = 0 and buffer = true and mustWrite = false
|
||||
}
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
i = [0, 1] and buffer = true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The function `strtok` is a variant of `strtok` that takes a `char**` parameter instead of
|
||||
* a `char*` as the first parameter.
|
||||
*/
|
||||
private class Strsep extends ArrayFunction, AliasFunction, TaintFunction, SideEffectFunction {
|
||||
Strsep() { this.hasGlobalName("strsep") }
|
||||
|
||||
override predicate hasArrayWithNullTerminator(int bufParam) { bufParam = 1 }
|
||||
|
||||
override predicate hasArrayInput(int bufParam) { bufParam = 1 }
|
||||
|
||||
override predicate parameterNeverEscapes(int index) { index = [0, 1] }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||
|
||||
override predicate parameterIsAlwaysReturned(int index) { none() }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// NOTE: What we really want here is: (input.isParameterDerefDeref(0) or input.isParameterDeref(1))
|
||||
// as the first conjunct.
|
||||
input.isParameterDeref([0, 1]) and
|
||||
(output.isReturnValue() or output.isReturnValueDeref())
|
||||
}
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
i = 0 and buffer = false and mustWrite = false
|
||||
}
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
i = 0 and buffer = false
|
||||
or
|
||||
i = 1 and buffer = true
|
||||
}
|
||||
}
|
||||
@@ -24,5 +24,6 @@ abstract class DataFlowFunction extends Function {
|
||||
* represented by `input` to the return value or buffer represented by
|
||||
* `output`
|
||||
*/
|
||||
pragma[nomagic]
|
||||
abstract predicate hasDataFlow(FunctionInput input, FunctionOutput output);
|
||||
}
|
||||
|
||||
@@ -108,6 +108,16 @@ class FunctionInput extends TFunctionInput {
|
||||
*/
|
||||
predicate isQualifierAddress() { none() }
|
||||
|
||||
/**
|
||||
* Holds if `i >= 0` and `isParameter(i)` holds for this value, or
|
||||
* if `i = -1` and `isQualifierAddress()` holds for this value.
|
||||
*/
|
||||
final predicate isParameterOrQualifierAddress(ParameterIndex i) {
|
||||
i >= 0 and this.isParameter(i)
|
||||
or
|
||||
i = -1 and this.isQualifierAddress()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this is the input value pointed to by the return value of a
|
||||
* function, if the function returns a pointer, or the input value referred
|
||||
@@ -134,7 +144,7 @@ class FunctionInput extends TFunctionInput {
|
||||
predicate isReturnValueDeref() { none() }
|
||||
|
||||
/**
|
||||
* Holds if `i >= 0` and `isParameterDeref(i)` holds for this is value, or
|
||||
* Holds if `i >= 0` and `isParameterDeref(i)` holds for this value, or
|
||||
* if `i = -1` and `isQualifierObject()` holds for this value.
|
||||
*/
|
||||
final predicate isParameterDerefOrQualifierObject(ParameterIndex i) {
|
||||
|
||||
@@ -28,5 +28,6 @@ abstract class TaintFunction extends Function {
|
||||
* Holds if data passed into the argument, qualifier, or buffer represented by
|
||||
* `input` influences the return value or buffer represented by `output`
|
||||
*/
|
||||
pragma[nomagic]
|
||||
abstract predicate hasTaintFlow(FunctionInput input, FunctionOutput output);
|
||||
}
|
||||
|
||||
@@ -691,8 +691,28 @@ typedefbase(
|
||||
int type_id: @type ref
|
||||
);
|
||||
|
||||
/**
|
||||
* An instance of the C++11 `decltype` operator. For example:
|
||||
* ```
|
||||
* int a;
|
||||
* decltype(1+a) b;
|
||||
* ```
|
||||
* Here `expr` is `1+a`.
|
||||
*
|
||||
* Sometimes an additional pair of parentheses around the expression
|
||||
* would change the semantics of this decltype, e.g.
|
||||
* ```
|
||||
* struct A { double x; };
|
||||
* const A* a = new A();
|
||||
* decltype( a->x ); // type is double
|
||||
* decltype((a->x)); // type is const double&
|
||||
* ```
|
||||
* (Please consult the C++11 standard for more details).
|
||||
* `parentheses_would_change_meaning` is `true` iff that is the case.
|
||||
*/
|
||||
#keyset[id, expr]
|
||||
decltypes(
|
||||
unique int id: @decltype,
|
||||
int id: @decltype,
|
||||
int expr: @expr ref,
|
||||
int base_type: @type ref,
|
||||
boolean parentheses_would_change_meaning: boolean ref
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
| test.c:13:9:13:13 | buff1 | This variable will not be cleared. |
|
||||
| test.c:35:9:35:13 | buff1 | This variable will not be cleared. |
|
||||
| test.c:43:9:43:13 | buff1 | This variable will not be cleared. |
|
||||
@@ -0,0 +1 @@
|
||||
experimental/Security/CWE/CWE-14/CompilerRemovalOfCodeToClearBuffers.ql
|
||||
@@ -0,0 +1,201 @@
|
||||
struct buffers
|
||||
{
|
||||
unsigned char buff1[50];
|
||||
unsigned char *buff2;
|
||||
} globalBuff1,*globalBuff2;
|
||||
|
||||
unsigned char * globalBuff;
|
||||
void badFunc0_0(){
|
||||
unsigned char buff1[12];
|
||||
int i;
|
||||
for(i=0;i<12;i++)
|
||||
buff1[i]=13;
|
||||
memset(buff1,12,12);
|
||||
}
|
||||
void nobadFunc0_0(){
|
||||
unsigned char buff1[12];
|
||||
memset(buff1,12,12);
|
||||
}
|
||||
void nobadFunc0_1(){
|
||||
unsigned char buff1[12];
|
||||
int i;
|
||||
memset(buff1,12,12);
|
||||
for(i=0;i<12;i++)
|
||||
buff1[i]=13;
|
||||
free(buff1);
|
||||
}
|
||||
void nobadFunc1_0(){
|
||||
unsigned char * buff1;
|
||||
buff1 = (unsigned char *) malloc(12);
|
||||
memset(buff1,12,12);
|
||||
}
|
||||
void badFunc1_0(){
|
||||
unsigned char * buff1;
|
||||
buff1 = (unsigned char *) malloc(12);
|
||||
memset(buff1,12,12);
|
||||
free(buff1);
|
||||
}
|
||||
void badFunc1_1(){
|
||||
unsigned char buff1[12];
|
||||
int i;
|
||||
for(i=0;i<12;i++)
|
||||
buff1[i]=13;
|
||||
memset(buff1,12,12);
|
||||
free(buff1);
|
||||
}
|
||||
void nobadFunc2_0_0(){
|
||||
unsigned char buff1[12];
|
||||
int i;
|
||||
for(i=0;i<12;i++)
|
||||
buff1[i]=13;
|
||||
memset(buff1,12,12);
|
||||
printf(buff1);
|
||||
}
|
||||
|
||||
void nobadFunc2_0_1(){
|
||||
unsigned char buff1[12];
|
||||
int i;
|
||||
for(i=0;i<12;i++)
|
||||
buff1[i]=13;
|
||||
memset(buff1,12,12);
|
||||
printf(buff1+3);
|
||||
}
|
||||
|
||||
void nobadFunc2_0_2(){
|
||||
unsigned char buff1[12];
|
||||
int i;
|
||||
for(i=0;i<12;i++)
|
||||
buff1[i]=13;
|
||||
memset(buff1,12,12);
|
||||
printf(*buff1);
|
||||
}
|
||||
|
||||
void nobadFunc2_0_3(){
|
||||
unsigned char buff1[12];
|
||||
int i;
|
||||
for(i=0;i<12;i++)
|
||||
buff1[i]=13;
|
||||
memset(buff1,12,12);
|
||||
printf(*(buff1+3));
|
||||
}
|
||||
unsigned char * nobadFunc2_0_4(){
|
||||
unsigned char buff1[12];
|
||||
int i;
|
||||
for(i=0;i<12;i++)
|
||||
buff1[i]=13;
|
||||
memset(buff1,12,12);
|
||||
return buff1;
|
||||
}
|
||||
|
||||
unsigned char * nobadFunc2_0_5(){
|
||||
unsigned char buff1[12];
|
||||
int i;
|
||||
for(i=0;i<12;i++)
|
||||
buff1[i]=13;
|
||||
memset(buff1,12,12);
|
||||
return buff1+3;
|
||||
}
|
||||
unsigned char nobadFunc2_0_6(){
|
||||
unsigned char buff1[12];
|
||||
int i;
|
||||
for(i=0;i<12;i++)
|
||||
buff1[i]=13;
|
||||
memset(buff1,12,12);
|
||||
return *buff1;
|
||||
}
|
||||
|
||||
unsigned char nobadFunc2_0_7(){
|
||||
unsigned char buff1[12];
|
||||
int i;
|
||||
for(i=0;i<12;i++)
|
||||
buff1[i]=13;
|
||||
memset(buff1,12,12);
|
||||
return *(buff1+3);
|
||||
}
|
||||
void nobadFunc2_1_0(){
|
||||
unsigned char buff1[12];
|
||||
int i;
|
||||
for(i=0;i<12;i++)
|
||||
buff1[i]=13;
|
||||
memset(buff1,12,12);
|
||||
if(*buff1==0)
|
||||
printf("123123");
|
||||
}
|
||||
void nobadFunc2_1_1(){
|
||||
unsigned char buff1[12];
|
||||
int i;
|
||||
for(i=0;i<12;i++)
|
||||
buff1[i]=13;
|
||||
memset(buff1,12,12);
|
||||
if(*(buff1+3)==0)
|
||||
printf("123123");
|
||||
}
|
||||
void nobadFunc2_1_2(){
|
||||
unsigned char buff1[12];
|
||||
int i;
|
||||
for(i=0;i<12;i++)
|
||||
buff1[i]=13;
|
||||
memset(buff1,12,12);
|
||||
buff1[2]=5;
|
||||
}
|
||||
void nobadFunc3_0(unsigned char * buffAll){
|
||||
unsigned char * buff1 = buffAll;
|
||||
memset(buff1,12,12);
|
||||
}
|
||||
void nobadFunc3_1(unsigned char * buffAll){
|
||||
unsigned char * buff1 = buffAll+3;
|
||||
memset(buff1,12,12);
|
||||
}
|
||||
void nobadFunc3_2(struct buffers buffAll){
|
||||
unsigned char * buff1 = buffAll.buff1;
|
||||
memset(buff1,12,12);
|
||||
}
|
||||
void nobadFunc3_3(struct buffers buffAll){
|
||||
unsigned char * buff1 = buffAll.buff2;
|
||||
memset(buff1,12,12);
|
||||
}
|
||||
void nobadFunc3_4(struct buffers buffAll){
|
||||
unsigned char * buff1 = buffAll.buff2+3;
|
||||
memset(buff1,12,12);
|
||||
}
|
||||
void nobadFunc3_5(struct buffers * buffAll){
|
||||
unsigned char * buff1 = buffAll->buff1;
|
||||
memset(buff1,12,12);
|
||||
}
|
||||
void nobadFunc3_6(struct buffers *buffAll){
|
||||
unsigned char * buff1 = buffAll->buff2;
|
||||
memset(buff1,12,12);
|
||||
}
|
||||
void nobadFunc4(){
|
||||
unsigned char * buff1 = globalBuff;
|
||||
memset(buff1,12,12);
|
||||
}
|
||||
void nobadFunc4_0(){
|
||||
unsigned char * buff1 = globalBuff;
|
||||
memset(buff1,12,12);
|
||||
}
|
||||
void nobadFunc4_1(){
|
||||
unsigned char * buff1 = globalBuff+3;
|
||||
memset(buff1,12,12);
|
||||
}
|
||||
void nobadFunc4_2(){
|
||||
unsigned char * buff1 = globalBuff1.buff1;
|
||||
memset(buff1,12,12);
|
||||
}
|
||||
void nobadFunc4_3(){
|
||||
unsigned char * buff1 = globalBuff1.buff2;
|
||||
memset(buff1,12,12);
|
||||
}
|
||||
void nobadFunc4_4(){
|
||||
unsigned char * buff1 = globalBuff1.buff2+3;
|
||||
memset(buff1,12,12);
|
||||
}
|
||||
void nobadFunc4_5(){
|
||||
unsigned char * buff1 = globalBuff2->buff1;
|
||||
memset(buff1,12,12);
|
||||
}
|
||||
void nobadFunc4_6(){
|
||||
unsigned char * buff1 = globalBuff2->buff2;
|
||||
memset(buff1,12,12);
|
||||
}
|
||||
|
||||
@@ -13,9 +13,9 @@ nodes
|
||||
| test.cpp:96:37:96:46 | theZipcode | semmle.label | theZipcode |
|
||||
| test.cpp:99:42:99:51 | theZipcode | semmle.label | theZipcode |
|
||||
#select
|
||||
| test.cpp:57:9:57:18 | theZipcode | This write into the external location 'theZipcode' may contain unencrypted data from $@ | test.cpp:57:9:57:18 | theZipcode | this source. |
|
||||
| test.cpp:74:24:74:30 | medical | This write into the external location 'medical' may contain unencrypted data from $@ | test.cpp:74:24:74:30 | medical | this source. |
|
||||
| test.cpp:78:24:78:27 | temp | This write into the external location 'temp' may contain unencrypted data from $@ | test.cpp:77:16:77:22 | medical | this source. |
|
||||
| test.cpp:82:24:82:28 | buff5 | This write into the external location 'buff5' may contain unencrypted data from $@ | test.cpp:81:22:81:28 | medical | this source. |
|
||||
| test.cpp:96:37:96:46 | theZipcode | This write into the external location 'theZipcode' may contain unencrypted data from $@ | test.cpp:96:37:96:46 | theZipcode | this source. |
|
||||
| test.cpp:99:42:99:51 | theZipcode | This write into the external location 'theZipcode' may contain unencrypted data from $@ | test.cpp:99:42:99:51 | theZipcode | this source. |
|
||||
| test.cpp:57:9:57:18 | theZipcode | test.cpp:57:9:57:18 | theZipcode | test.cpp:57:9:57:18 | theZipcode | This write into the external location 'theZipcode' may contain unencrypted data from $@ | test.cpp:57:9:57:18 | theZipcode | this source. |
|
||||
| test.cpp:74:24:74:30 | medical | test.cpp:74:24:74:30 | medical | test.cpp:74:24:74:30 | medical | This write into the external location 'medical' may contain unencrypted data from $@ | test.cpp:74:24:74:30 | medical | this source. |
|
||||
| test.cpp:78:24:78:27 | temp | test.cpp:77:16:77:22 | medical | test.cpp:78:24:78:27 | temp | This write into the external location 'temp' may contain unencrypted data from $@ | test.cpp:77:16:77:22 | medical | this source. |
|
||||
| test.cpp:82:24:82:28 | buff5 | test.cpp:81:22:81:28 | medical | test.cpp:82:24:82:28 | buff5 | This write into the external location 'buff5' may contain unencrypted data from $@ | test.cpp:81:22:81:28 | medical | this source. |
|
||||
| test.cpp:96:37:96:46 | theZipcode | test.cpp:96:37:96:46 | theZipcode | test.cpp:96:37:96:46 | theZipcode | This write into the external location 'theZipcode' may contain unencrypted data from $@ | test.cpp:96:37:96:46 | theZipcode | this source. |
|
||||
| test.cpp:99:42:99:51 | theZipcode | test.cpp:99:42:99:51 | theZipcode | test.cpp:99:42:99:51 | theZipcode | This write into the external location 'theZipcode' may contain unencrypted data from $@ | test.cpp:99:42:99:51 | theZipcode | this source. |
|
||||
|
||||
@@ -2,3 +2,7 @@
|
||||
| test.c:63:29:63:35 | call to realloc | possible loss of original pointer on unsuccessful call realloc |
|
||||
| test.c:139:29:139:35 | call to realloc | possible loss of original pointer on unsuccessful call realloc |
|
||||
| test.c:186:29:186:35 | call to realloc | possible loss of original pointer on unsuccessful call realloc |
|
||||
| test.c:282:29:282:35 | call to realloc | possible loss of original pointer on unsuccessful call realloc |
|
||||
| test.c:299:26:299:32 | call to realloc | possible loss of original pointer on unsuccessful call realloc |
|
||||
| test.c:328:29:328:35 | call to realloc | possible loss of original pointer on unsuccessful call realloc |
|
||||
| test.c:342:29:342:35 | call to realloc | possible loss of original pointer on unsuccessful call realloc |
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#define NULL ((void*)0)
|
||||
|
||||
#define assert(x) if (!(x)) __assert_fail(#x,__FILE__,__LINE__)
|
||||
void __assert_fail(const char *assertion, const char *file, int line) { }
|
||||
void __assert_fail(const char *assertion, const char *file, int line);
|
||||
|
||||
void aFakeFailed_1(int file, int line)
|
||||
{
|
||||
@@ -272,3 +272,75 @@ unsigned char * noBadResize_2_7(unsigned char * buffer,size_t currentSize,size_t
|
||||
myASSERT_2(buffer);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
unsigned char *goodResize_3_1(unsigned char *buffer, size_t currentSize, size_t newSize)
|
||||
{
|
||||
// GOOD: this way we will exclude possible memory leak [FALSE POSITIVE]
|
||||
unsigned char *tmp = buffer;
|
||||
if (currentSize < newSize)
|
||||
{
|
||||
buffer = (unsigned char *)realloc(buffer, newSize);
|
||||
if (buffer == NULL)
|
||||
{
|
||||
free(tmp);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
unsigned char *goodResize_3_2(unsigned char *buffer, size_t currentSize, size_t newSize)
|
||||
{
|
||||
// GOOD: this way we will exclude possible memory leak [FALSE POSITIVE]
|
||||
unsigned char *tmp = buffer;
|
||||
if (currentSize < newSize)
|
||||
{
|
||||
tmp = (unsigned char *)realloc(tmp, newSize);
|
||||
if (tmp != 0)
|
||||
{
|
||||
buffer = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void abort(void);
|
||||
|
||||
unsigned char *noBadResize_4_1(unsigned char *buffer, size_t currentSize, size_t newSize)
|
||||
{
|
||||
// GOOD: program to end
|
||||
if (currentSize < newSize)
|
||||
{
|
||||
if (buffer = (unsigned char *)realloc(buffer, newSize))
|
||||
abort();
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
unsigned char * badResize_5_2(unsigned char *buffer, size_t currentSize, size_t newSize, int cond)
|
||||
{
|
||||
// BAD: on unsuccessful call to realloc, we will lose a pointer to a valid memory block
|
||||
if (currentSize < newSize)
|
||||
{
|
||||
buffer = (unsigned char *)realloc(buffer, newSize);
|
||||
}
|
||||
if (cond)
|
||||
{
|
||||
abort(); // irrelevant
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
unsigned char * badResize_5_1(unsigned char *buffer, size_t currentSize, size_t newSize, int cond)
|
||||
{
|
||||
// BAD: on unsuccessful call to realloc, we will lose a pointer to a valid memory block
|
||||
if (currentSize < newSize)
|
||||
{
|
||||
buffer = (unsigned char *)realloc(buffer, newSize);
|
||||
assert(cond); // irrelevant
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
| test.cpp:30:15:30:26 | call to operator new[] | memory allocation error check is incorrect or missing |
|
||||
| test.cpp:38:9:38:20 | call to operator new[] | memory allocation error check is incorrect or missing |
|
||||
| test.cpp:50:13:50:38 | call to operator new[] | memory allocation error check is incorrect or missing |
|
||||
| test.cpp:51:22:51:47 | call to operator new[] | memory allocation error check is incorrect or missing |
|
||||
| test.cpp:53:18:53:43 | call to operator new[] | memory allocation error check is incorrect or missing |
|
||||
@@ -0,0 +1 @@
|
||||
experimental/Security/CWE/CWE-570/WrongInDetectingAndHandlingMemoryAllocationErrors.ql
|
||||
@@ -0,0 +1,97 @@
|
||||
#define NULL ((void*)0)
|
||||
class exception {};
|
||||
|
||||
namespace std{
|
||||
struct nothrow_t {};
|
||||
typedef unsigned long size_t;
|
||||
class bad_alloc{
|
||||
const char* what() const throw();
|
||||
};
|
||||
extern const std::nothrow_t nothrow;
|
||||
}
|
||||
|
||||
using namespace std;
|
||||
|
||||
void* operator new(std::size_t _Size);
|
||||
void* operator new[](std::size_t _Size);
|
||||
void* operator new( std::size_t count, const std::nothrow_t& tag );
|
||||
void* operator new[]( std::size_t count, const std::nothrow_t& tag );
|
||||
|
||||
void badNew_0_0()
|
||||
{
|
||||
while (true) {
|
||||
new int[100]; // BAD [NOT DETECTED]
|
||||
if(!(new int[100])) // BAD [NOT DETECTED]
|
||||
return;
|
||||
}
|
||||
}
|
||||
void badNew_0_1()
|
||||
{
|
||||
int * i = new int[100]; // BAD
|
||||
if(i == 0)
|
||||
return;
|
||||
if(!i)
|
||||
return;
|
||||
if(i == NULL)
|
||||
return;
|
||||
int * j;
|
||||
j = new int[100]; // BAD
|
||||
if(j == 0)
|
||||
return;
|
||||
if(!j)
|
||||
return;
|
||||
if(j == NULL)
|
||||
return;
|
||||
}
|
||||
void badNew_1_0()
|
||||
{
|
||||
try {
|
||||
while (true) {
|
||||
new(std::nothrow) int[100]; // BAD
|
||||
int* p = new(std::nothrow) int[100]; // BAD
|
||||
int* p1;
|
||||
p1 = new(std::nothrow) int[100]; // BAD
|
||||
}
|
||||
} catch (const exception &){//const std::bad_alloc& e) {
|
||||
// std::cout << e.what() << '\n';
|
||||
}
|
||||
}
|
||||
void badNew_1_1()
|
||||
{
|
||||
while (true) {
|
||||
int* p = new(std::nothrow) int[100]; // BAD [NOT DETECTED]
|
||||
new(std::nothrow) int[100]; // BAD [NOT DETECTED]
|
||||
}
|
||||
}
|
||||
|
||||
void goodNew_0_0()
|
||||
{
|
||||
try {
|
||||
while (true) {
|
||||
new int[100]; // GOOD
|
||||
}
|
||||
} catch (const exception &){//const std::bad_alloc& e) {
|
||||
// std::cout << e.what() << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
void goodNew_1_0()
|
||||
{
|
||||
while (true) {
|
||||
int* p = new(std::nothrow) int[100]; // GOOD
|
||||
if (p == nullptr) {
|
||||
// std::cout << "Allocation returned nullptr\n";
|
||||
break;
|
||||
}
|
||||
int* p1;
|
||||
p1 = new(std::nothrow) int[100]; // GOOD
|
||||
if (p1 == nullptr) {
|
||||
// std::cout << "Allocation returned nullptr\n";
|
||||
break;
|
||||
}
|
||||
if (new(std::nothrow) int[100] == nullptr) { // GOOD
|
||||
// std::cout << "Allocation returned nullptr\n";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
| test.c:42:3:42:24 | ... = ... | potential unsafe or redundant assignment. |
|
||||
| test.c:43:3:43:40 | ... = ... | potential unsafe or redundant assignment. |
|
||||
| test.c:44:3:44:40 | ... = ... | potential unsafe or redundant assignment. |
|
||||
| test.c:45:3:45:44 | ... = ... | potential unsafe or redundant assignment. |
|
||||
| test.c:46:3:46:44 | ... = ... | potential unsafe or redundant assignment. |
|
||||
| test.c:47:3:47:48 | ... = ... | potential unsafe or redundant assignment. |
|
||||
| test.c:48:3:48:48 | ... = ... | potential unsafe or redundant assignment. |
|
||||
| test.c:49:3:49:50 | ... = ... | potential unsafe or redundant assignment. |
|
||||
| test.c:50:3:50:50 | ... = ... | potential unsafe or redundant assignment. |
|
||||
@@ -0,0 +1 @@
|
||||
experimental/Security/CWE/CWE-788/AccessOfMemoryLocationAfterEndOfBufferUsingStrlen.ql
|
||||
@@ -0,0 +1,3 @@
|
||||
| test.c:4:3:4:9 | call to strncat | if the used buffer is full, writing out of the buffer is possible |
|
||||
| test.c:11:3:11:9 | call to strncat | if the used buffer is full, writing out of the buffer is possible |
|
||||
| test.c:19:3:19:9 | call to strncat | if the used buffer is full, writing out of the buffer is possible |
|
||||
@@ -0,0 +1 @@
|
||||
experimental/Security/CWE/CWE-788/AccessOfMemoryLocationAfterEndOfBufferUsingStrncat.ql
|
||||
@@ -0,0 +1,73 @@
|
||||
void workFunction_0(char *s) {
|
||||
char buf[80];
|
||||
strncat(buf, s, sizeof(buf)-strlen(buf)-1); // GOOD
|
||||
strncat(buf, s, sizeof(buf)-strlen(buf)); // BAD
|
||||
strncat(buf, "fix", sizeof(buf)-strlen(buf)); // BAD [NOT DETECTED]
|
||||
}
|
||||
void workFunction_1(char *s) {
|
||||
#define MAX_SIZE 80
|
||||
char buf[MAX_SIZE];
|
||||
strncat(buf, s, MAX_SIZE-strlen(buf)-1); // GOOD
|
||||
strncat(buf, s, MAX_SIZE-strlen(buf)); // BAD
|
||||
strncat(buf, "fix", MAX_SIZE-strlen(buf)); // BAD [NOT DETECTED]
|
||||
}
|
||||
void workFunction_2_0(char *s) {
|
||||
char * buf;
|
||||
int len=80;
|
||||
buf = (char *) malloc(len);
|
||||
strncat(buf, s, len-strlen(buf)-1); // GOOD
|
||||
strncat(buf, s, len-strlen(buf)); // BAD
|
||||
strncat(buf, "fix", len-strlen(buf)); // BAD [NOT DETECTED]
|
||||
}
|
||||
void workFunction_2_1(char *s) {
|
||||
char * buf;
|
||||
int len=80;
|
||||
buf = (char *) malloc(len+1);
|
||||
strncat(buf, s, len-strlen(buf)-1); // GOOD
|
||||
strncat(buf, s, len-strlen(buf)); // GOOD
|
||||
}
|
||||
|
||||
struct buffers
|
||||
{
|
||||
unsigned char buff1[50];
|
||||
unsigned char *buff2;
|
||||
} globalBuff1,*globalBuff2,globalBuff1_c,*globalBuff2_c;
|
||||
|
||||
|
||||
void badFunc0(){
|
||||
unsigned char buff1[12];
|
||||
struct buffers buffAll;
|
||||
struct buffers * buffAll1;
|
||||
|
||||
buff1[strlen(buff1)]=0; // BAD
|
||||
buffAll.buff1[strlen(buffAll.buff1)]=0; // BAD
|
||||
buffAll.buff2[strlen(buffAll.buff2)]=0; // BAD
|
||||
buffAll1->buff1[strlen(buffAll1->buff1)]=0; // BAD
|
||||
buffAll1->buff2[strlen(buffAll1->buff2)]=0; // BAD
|
||||
globalBuff1.buff1[strlen(globalBuff1.buff1)]=0; // BAD
|
||||
globalBuff1.buff2[strlen(globalBuff1.buff2)]=0; // BAD
|
||||
globalBuff2->buff1[strlen(globalBuff2->buff1)]=0; // BAD
|
||||
globalBuff2->buff2[strlen(globalBuff2->buff2)]=0; // BAD
|
||||
}
|
||||
void noBadFunc0(){
|
||||
unsigned char buff1[12],buff1_c[12];
|
||||
struct buffers buffAll,buffAll_c;
|
||||
struct buffers * buffAll1,*buffAll1_c;
|
||||
|
||||
buff1[strlen(buff1_c)]=0; // GOOD
|
||||
buffAll.buff1[strlen(buffAll_c.buff1)]=0; // GOOD
|
||||
buffAll.buff2[strlen(buffAll.buff1)]=0; // GOOD
|
||||
buffAll1->buff1[strlen(buffAll1_c->buff1)]=0; // GOOD
|
||||
buffAll1->buff2[strlen(buffAll1->buff1)]=0; // GOOD
|
||||
globalBuff1.buff1[strlen(globalBuff1_c.buff1)]=0; // GOOD
|
||||
globalBuff1.buff2[strlen(globalBuff1.buff1)]=0; // GOOD
|
||||
globalBuff2->buff1[strlen(globalBuff2_c->buff1)]=0; // GOOD
|
||||
globalBuff2->buff2[strlen(globalBuff2->buff1)]=0; // GOOD
|
||||
}
|
||||
void goodFunc0(){
|
||||
unsigned char buffer[12];
|
||||
int i;
|
||||
for(i = 0; i < 6; i++)
|
||||
buffer[i] = 'A';
|
||||
buffer[i]=0;
|
||||
}
|
||||
@@ -123,7 +123,7 @@ void test1()
|
||||
{
|
||||
int i = 0;
|
||||
sink(sscanf(string::source(), "%i", &i));
|
||||
sink(i); // $ MISSING: ast,ir
|
||||
sink(i); // $ ast,ir
|
||||
}
|
||||
{
|
||||
char buffer[256] = {0};
|
||||
@@ -133,7 +133,7 @@ void test1()
|
||||
{
|
||||
char buffer[256] = {0};
|
||||
sink(sscanf(string::source(), "%s", &buffer));
|
||||
sink(buffer); // $ MISSING: ast,ir
|
||||
sink(buffer); // $ ast,ir
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -378,23 +378,27 @@
|
||||
| format.cpp:114:37:114:50 | call to source | format.cpp:114:18:114:23 | ref arg buffer | TAINT |
|
||||
| format.cpp:119:10:119:11 | 0 | format.cpp:120:29:120:29 | i | |
|
||||
| format.cpp:119:10:119:11 | 0 | format.cpp:121:8:121:8 | i | |
|
||||
| format.cpp:120:15:120:19 | 123 | format.cpp:120:28:120:29 | ref arg & ... | TAINT |
|
||||
| format.cpp:120:28:120:29 | ref arg & ... | format.cpp:120:29:120:29 | i [inner post update] | |
|
||||
| format.cpp:120:28:120:29 | ref arg & ... | format.cpp:121:8:121:8 | i | |
|
||||
| format.cpp:120:29:120:29 | i | format.cpp:120:28:120:29 | & ... | |
|
||||
| format.cpp:124:10:124:11 | 0 | format.cpp:125:40:125:40 | i | |
|
||||
| format.cpp:124:10:124:11 | 0 | format.cpp:126:8:126:8 | i | |
|
||||
| format.cpp:125:15:125:28 | call to source | format.cpp:125:39:125:40 | ref arg & ... | TAINT |
|
||||
| format.cpp:125:39:125:40 | ref arg & ... | format.cpp:125:40:125:40 | i [inner post update] | |
|
||||
| format.cpp:125:39:125:40 | ref arg & ... | format.cpp:126:8:126:8 | i | |
|
||||
| format.cpp:125:40:125:40 | i | format.cpp:125:39:125:40 | & ... | |
|
||||
| format.cpp:129:21:129:24 | {...} | format.cpp:130:32:130:37 | buffer | |
|
||||
| format.cpp:129:21:129:24 | {...} | format.cpp:131:8:131:13 | buffer | |
|
||||
| format.cpp:129:23:129:23 | 0 | format.cpp:129:21:129:24 | {...} | TAINT |
|
||||
| format.cpp:130:15:130:22 | Hello. | format.cpp:130:31:130:37 | ref arg & ... | TAINT |
|
||||
| format.cpp:130:31:130:37 | ref arg & ... | format.cpp:130:32:130:37 | buffer [inner post update] | |
|
||||
| format.cpp:130:31:130:37 | ref arg & ... | format.cpp:131:8:131:13 | buffer | |
|
||||
| format.cpp:130:32:130:37 | buffer | format.cpp:130:31:130:37 | & ... | |
|
||||
| format.cpp:134:21:134:24 | {...} | format.cpp:135:40:135:45 | buffer | |
|
||||
| format.cpp:134:21:134:24 | {...} | format.cpp:136:8:136:13 | buffer | |
|
||||
| format.cpp:134:23:134:23 | 0 | format.cpp:134:21:134:24 | {...} | TAINT |
|
||||
| format.cpp:135:15:135:28 | call to source | format.cpp:135:39:135:45 | ref arg & ... | TAINT |
|
||||
| format.cpp:135:39:135:45 | ref arg & ... | format.cpp:135:40:135:45 | buffer [inner post update] | |
|
||||
| format.cpp:135:39:135:45 | ref arg & ... | format.cpp:136:8:136:13 | buffer | |
|
||||
| format.cpp:135:40:135:45 | buffer | format.cpp:135:39:135:45 | & ... | |
|
||||
@@ -5861,6 +5865,183 @@
|
||||
| taint.cpp:483:18:483:19 | ref arg & ... | taint.cpp:483:19:483:19 | n [inner post update] | |
|
||||
| taint.cpp:483:19:483:19 | n | taint.cpp:483:18:483:19 | & ... | |
|
||||
| taint.cpp:483:28:483:34 | source1 | taint.cpp:483:11:483:15 | ref arg & ... | TAINT |
|
||||
| taint.cpp:492:24:492:29 | source | taint.cpp:494:27:494:32 | source | |
|
||||
| taint.cpp:493:22:493:29 | ,.-;:_ | taint.cpp:494:35:494:39 | delim | |
|
||||
| taint.cpp:493:22:493:29 | ,.-;:_ | taint.cpp:496:7:496:11 | delim | |
|
||||
| taint.cpp:494:20:494:25 | call to strtok | taint.cpp:495:7:495:15 | tokenized | |
|
||||
| taint.cpp:494:27:494:32 | source | taint.cpp:494:20:494:25 | call to strtok | TAINT |
|
||||
| taint.cpp:503:26:503:28 | ptr | taint.cpp:504:10:504:12 | ptr | |
|
||||
| taint.cpp:503:26:503:28 | ptr | taint.cpp:505:7:505:9 | ptr | |
|
||||
| taint.cpp:503:26:503:28 | ptr | taint.cpp:506:8:506:10 | ptr | |
|
||||
| taint.cpp:503:36:503:41 | source | taint.cpp:504:15:504:20 | source | |
|
||||
| taint.cpp:504:10:504:12 | ptr | taint.cpp:504:2:504:8 | call to _strset | |
|
||||
| taint.cpp:504:10:504:12 | ref arg ptr | taint.cpp:505:7:505:9 | ptr | |
|
||||
| taint.cpp:504:10:504:12 | ref arg ptr | taint.cpp:506:8:506:10 | ptr | |
|
||||
| taint.cpp:504:15:504:20 | source | taint.cpp:504:2:504:8 | call to _strset | TAINT |
|
||||
| taint.cpp:504:15:504:20 | source | taint.cpp:504:10:504:12 | ref arg ptr | |
|
||||
| taint.cpp:505:7:505:9 | ref arg ptr | taint.cpp:506:8:506:10 | ptr | |
|
||||
| taint.cpp:506:8:506:10 | ptr | taint.cpp:506:7:506:10 | * ... | TAINT |
|
||||
| taint.cpp:509:26:509:31 | source | taint.cpp:510:10:510:15 | source | |
|
||||
| taint.cpp:509:26:509:31 | source | taint.cpp:511:7:511:12 | source | |
|
||||
| taint.cpp:510:10:510:15 | ref arg source | taint.cpp:511:7:511:12 | source | |
|
||||
| taint.cpp:510:10:510:15 | source | taint.cpp:510:2:510:8 | call to _strset | |
|
||||
| taint.cpp:510:18:510:18 | 0 | taint.cpp:510:2:510:8 | call to _strset | TAINT |
|
||||
| taint.cpp:510:18:510:18 | 0 | taint.cpp:510:10:510:15 | ref arg source | |
|
||||
| taint.cpp:518:24:518:29 | source | taint.cpp:520:14:520:19 | source | |
|
||||
| taint.cpp:519:6:519:6 | x | taint.cpp:520:11:520:11 | x | |
|
||||
| taint.cpp:519:6:519:6 | x | taint.cpp:521:7:521:7 | x | |
|
||||
| taint.cpp:520:10:520:11 | & ... | taint.cpp:520:2:520:8 | call to mempcpy | |
|
||||
| taint.cpp:520:10:520:11 | ref arg & ... | taint.cpp:520:11:520:11 | x [inner post update] | |
|
||||
| taint.cpp:520:10:520:11 | ref arg & ... | taint.cpp:521:7:521:7 | x | |
|
||||
| taint.cpp:520:11:520:11 | x | taint.cpp:520:10:520:11 | & ... | |
|
||||
| taint.cpp:520:14:520:19 | source | taint.cpp:520:2:520:8 | call to mempcpy | TAINT |
|
||||
| taint.cpp:520:14:520:19 | source | taint.cpp:520:10:520:11 | ref arg & ... | TAINT |
|
||||
| taint.cpp:528:24:528:29 | source | taint.cpp:530:16:530:21 | source | |
|
||||
| taint.cpp:529:6:529:9 | dest | taint.cpp:530:10:530:13 | dest | |
|
||||
| taint.cpp:529:6:529:9 | dest | taint.cpp:530:35:530:38 | dest | |
|
||||
| taint.cpp:529:6:529:9 | dest | taint.cpp:531:7:531:10 | dest | |
|
||||
| taint.cpp:530:10:530:13 | dest | taint.cpp:530:2:530:8 | call to memccpy | |
|
||||
| taint.cpp:530:10:530:13 | ref arg dest | taint.cpp:531:7:531:10 | dest | |
|
||||
| taint.cpp:530:16:530:21 | source | taint.cpp:530:2:530:8 | call to memccpy | TAINT |
|
||||
| taint.cpp:530:16:530:21 | source | taint.cpp:530:10:530:13 | ref arg dest | TAINT |
|
||||
| taint.cpp:538:24:538:28 | dest1 | taint.cpp:539:9:539:13 | dest1 | |
|
||||
| taint.cpp:538:24:538:28 | dest1 | taint.cpp:540:7:540:11 | dest1 | |
|
||||
| taint.cpp:538:37:538:41 | dest2 | taint.cpp:542:9:542:13 | dest2 | |
|
||||
| taint.cpp:538:37:538:41 | dest2 | taint.cpp:543:7:543:11 | dest2 | |
|
||||
| taint.cpp:538:50:538:54 | clean | taint.cpp:542:16:542:20 | clean | |
|
||||
| taint.cpp:538:63:538:68 | source | taint.cpp:539:16:539:21 | source | |
|
||||
| taint.cpp:539:9:539:13 | dest1 | taint.cpp:539:2:539:7 | call to strcat | |
|
||||
| taint.cpp:539:9:539:13 | dest1 | taint.cpp:539:9:539:13 | ref arg dest1 | TAINT |
|
||||
| taint.cpp:539:9:539:13 | ref arg dest1 | taint.cpp:540:7:540:11 | dest1 | |
|
||||
| taint.cpp:539:16:539:21 | source | taint.cpp:539:9:539:13 | ref arg dest1 | TAINT |
|
||||
| taint.cpp:542:9:542:13 | dest2 | taint.cpp:542:2:542:7 | call to strcat | |
|
||||
| taint.cpp:542:9:542:13 | dest2 | taint.cpp:542:9:542:13 | ref arg dest2 | TAINT |
|
||||
| taint.cpp:542:9:542:13 | ref arg dest2 | taint.cpp:543:7:543:11 | dest2 | |
|
||||
| taint.cpp:542:16:542:20 | clean | taint.cpp:542:9:542:13 | ref arg dest2 | TAINT |
|
||||
| taint.cpp:550:37:550:41 | dest1 | taint.cpp:552:36:552:40 | dest1 | |
|
||||
| taint.cpp:550:37:550:41 | dest1 | taint.cpp:553:7:553:11 | dest1 | |
|
||||
| taint.cpp:550:37:550:41 | dest1 | taint.cpp:554:8:554:12 | dest1 | |
|
||||
| taint.cpp:550:65:550:67 | ptr | taint.cpp:552:43:552:45 | ptr | |
|
||||
| taint.cpp:550:65:550:67 | ptr | taint.cpp:558:43:558:45 | ptr | |
|
||||
| taint.cpp:550:85:550:89 | dest3 | taint.cpp:558:36:558:40 | dest3 | |
|
||||
| taint.cpp:550:85:550:89 | dest3 | taint.cpp:559:7:559:11 | dest3 | |
|
||||
| taint.cpp:550:85:550:89 | dest3 | taint.cpp:560:8:560:12 | dest3 | |
|
||||
| taint.cpp:551:32:551:36 | clean | taint.cpp:558:51:558:55 | clean | |
|
||||
| taint.cpp:551:49:551:54 | source | taint.cpp:552:51:552:56 | source | |
|
||||
| taint.cpp:551:61:551:61 | n | taint.cpp:552:48:552:48 | n | |
|
||||
| taint.cpp:551:61:551:61 | n | taint.cpp:558:48:558:48 | n | |
|
||||
| taint.cpp:552:25:552:34 | call to _mbsncat_l | taint.cpp:555:7:555:11 | dest2 | |
|
||||
| taint.cpp:552:25:552:34 | call to _mbsncat_l | taint.cpp:556:8:556:12 | dest2 | |
|
||||
| taint.cpp:552:36:552:40 | dest1 | taint.cpp:552:25:552:34 | call to _mbsncat_l | |
|
||||
| taint.cpp:552:36:552:40 | dest1 | taint.cpp:552:36:552:40 | ref arg dest1 | TAINT |
|
||||
| taint.cpp:552:36:552:40 | ref arg dest1 | taint.cpp:553:7:553:11 | dest1 | |
|
||||
| taint.cpp:552:36:552:40 | ref arg dest1 | taint.cpp:554:8:554:12 | dest1 | |
|
||||
| taint.cpp:552:43:552:45 | ptr | taint.cpp:552:36:552:40 | ref arg dest1 | TAINT |
|
||||
| taint.cpp:552:48:552:48 | n | taint.cpp:552:36:552:40 | ref arg dest1 | TAINT |
|
||||
| taint.cpp:552:51:552:56 | source | taint.cpp:552:36:552:40 | ref arg dest1 | TAINT |
|
||||
| taint.cpp:553:7:553:11 | ref arg dest1 | taint.cpp:554:8:554:12 | dest1 | |
|
||||
| taint.cpp:554:8:554:12 | dest1 | taint.cpp:554:7:554:12 | * ... | TAINT |
|
||||
| taint.cpp:555:7:555:11 | ref arg dest2 | taint.cpp:556:8:556:12 | dest2 | |
|
||||
| taint.cpp:556:8:556:12 | dest2 | taint.cpp:556:7:556:12 | * ... | TAINT |
|
||||
| taint.cpp:558:25:558:34 | call to _mbsncat_l | taint.cpp:561:7:561:11 | dest4 | |
|
||||
| taint.cpp:558:25:558:34 | call to _mbsncat_l | taint.cpp:562:8:562:12 | dest4 | |
|
||||
| taint.cpp:558:36:558:40 | dest3 | taint.cpp:558:25:558:34 | call to _mbsncat_l | |
|
||||
| taint.cpp:558:36:558:40 | dest3 | taint.cpp:558:36:558:40 | ref arg dest3 | TAINT |
|
||||
| taint.cpp:558:36:558:40 | ref arg dest3 | taint.cpp:559:7:559:11 | dest3 | |
|
||||
| taint.cpp:558:36:558:40 | ref arg dest3 | taint.cpp:560:8:560:12 | dest3 | |
|
||||
| taint.cpp:558:43:558:45 | ptr | taint.cpp:558:36:558:40 | ref arg dest3 | TAINT |
|
||||
| taint.cpp:558:48:558:48 | n | taint.cpp:558:36:558:40 | ref arg dest3 | TAINT |
|
||||
| taint.cpp:558:51:558:55 | clean | taint.cpp:558:36:558:40 | ref arg dest3 | TAINT |
|
||||
| taint.cpp:559:7:559:11 | ref arg dest3 | taint.cpp:560:8:560:12 | dest3 | |
|
||||
| taint.cpp:560:8:560:12 | dest3 | taint.cpp:560:7:560:12 | * ... | TAINT |
|
||||
| taint.cpp:561:7:561:11 | ref arg dest4 | taint.cpp:562:8:562:12 | dest4 | |
|
||||
| taint.cpp:562:8:562:12 | dest4 | taint.cpp:562:7:562:12 | * ... | TAINT |
|
||||
| taint.cpp:569:24:569:29 | source | taint.cpp:572:29:572:34 | source | |
|
||||
| taint.cpp:570:23:570:30 | ,.-;:_ | taint.cpp:572:37:572:41 | delim | |
|
||||
| taint.cpp:572:9:572:17 | tokenized | taint.cpp:572:9:572:42 | ... = ... | |
|
||||
| taint.cpp:572:21:572:26 | call to strsep | taint.cpp:572:9:572:42 | ... = ... | |
|
||||
| taint.cpp:572:21:572:26 | call to strsep | taint.cpp:573:10:573:18 | tokenized | |
|
||||
| taint.cpp:572:21:572:26 | call to strsep | taint.cpp:574:11:574:19 | tokenized | |
|
||||
| taint.cpp:572:28:572:34 | & ... | taint.cpp:572:21:572:26 | call to strsep | TAINT |
|
||||
| taint.cpp:572:28:572:34 | ref arg & ... | taint.cpp:572:29:572:34 | source | |
|
||||
| taint.cpp:572:28:572:34 | ref arg & ... | taint.cpp:572:29:572:34 | source [inner post update] | |
|
||||
| taint.cpp:572:29:572:34 | source | taint.cpp:572:21:572:26 | call to strsep | TAINT |
|
||||
| taint.cpp:572:29:572:34 | source | taint.cpp:572:28:572:34 | & ... | |
|
||||
| taint.cpp:572:37:572:41 | delim | taint.cpp:572:21:572:26 | call to strsep | TAINT |
|
||||
| taint.cpp:573:10:573:18 | ref arg tokenized | taint.cpp:574:11:574:19 | tokenized | |
|
||||
| taint.cpp:574:11:574:19 | tokenized | taint.cpp:574:10:574:19 | * ... | TAINT |
|
||||
| taint.cpp:584:25:584:30 | source | taint.cpp:585:18:585:23 | source | |
|
||||
| taint.cpp:584:39:584:43 | clean | taint.cpp:589:18:589:22 | clean | |
|
||||
| taint.cpp:584:82:584:87 | locale | taint.cpp:585:26:585:31 | locale | |
|
||||
| taint.cpp:584:82:584:87 | locale | taint.cpp:589:25:589:30 | locale | |
|
||||
| taint.cpp:585:10:585:16 | call to _strinc | taint.cpp:585:2:585:32 | ... = ... | |
|
||||
| taint.cpp:585:10:585:16 | call to _strinc | taint.cpp:586:7:586:11 | dest1 | |
|
||||
| taint.cpp:585:10:585:16 | call to _strinc | taint.cpp:587:8:587:12 | dest1 | |
|
||||
| taint.cpp:585:18:585:23 | source | taint.cpp:585:10:585:16 | call to _strinc | TAINT |
|
||||
| taint.cpp:585:26:585:31 | locale | taint.cpp:585:10:585:16 | call to _strinc | TAINT |
|
||||
| taint.cpp:585:26:585:31 | ref arg locale | taint.cpp:589:25:589:30 | locale | |
|
||||
| taint.cpp:586:7:586:11 | ref arg dest1 | taint.cpp:587:8:587:12 | dest1 | |
|
||||
| taint.cpp:587:8:587:12 | dest1 | taint.cpp:587:7:587:12 | * ... | TAINT |
|
||||
| taint.cpp:589:10:589:16 | call to _strinc | taint.cpp:589:2:589:31 | ... = ... | |
|
||||
| taint.cpp:589:10:589:16 | call to _strinc | taint.cpp:590:7:590:11 | dest2 | |
|
||||
| taint.cpp:589:10:589:16 | call to _strinc | taint.cpp:591:8:591:12 | dest2 | |
|
||||
| taint.cpp:589:18:589:22 | clean | taint.cpp:589:10:589:16 | call to _strinc | TAINT |
|
||||
| taint.cpp:589:25:589:30 | locale | taint.cpp:589:10:589:16 | call to _strinc | TAINT |
|
||||
| taint.cpp:590:7:590:11 | ref arg dest2 | taint.cpp:591:8:591:12 | dest2 | |
|
||||
| taint.cpp:591:8:591:12 | dest2 | taint.cpp:591:7:591:12 | * ... | TAINT |
|
||||
| taint.cpp:594:34:594:48 | source_unsigned | taint.cpp:595:26:595:40 | source_unsigned | |
|
||||
| taint.cpp:594:57:594:62 | source | taint.cpp:599:40:599:45 | source | |
|
||||
| taint.cpp:595:18:595:24 | call to _mbsinc | taint.cpp:595:2:595:41 | ... = ... | |
|
||||
| taint.cpp:595:18:595:24 | call to _mbsinc | taint.cpp:596:7:596:19 | dest_unsigned | |
|
||||
| taint.cpp:595:18:595:24 | call to _mbsinc | taint.cpp:597:8:597:20 | dest_unsigned | |
|
||||
| taint.cpp:595:26:595:40 | source_unsigned | taint.cpp:595:18:595:24 | call to _mbsinc | TAINT |
|
||||
| taint.cpp:596:7:596:19 | ref arg dest_unsigned | taint.cpp:597:8:597:20 | dest_unsigned | |
|
||||
| taint.cpp:597:8:597:20 | dest_unsigned | taint.cpp:597:7:597:20 | * ... | TAINT |
|
||||
| taint.cpp:599:16:599:22 | call to _mbsinc | taint.cpp:599:2:599:46 | ... = ... | |
|
||||
| taint.cpp:599:16:599:22 | call to _mbsinc | taint.cpp:600:7:600:10 | dest | |
|
||||
| taint.cpp:599:16:599:22 | call to _mbsinc | taint.cpp:601:8:601:11 | dest | |
|
||||
| taint.cpp:599:40:599:45 | source | taint.cpp:599:16:599:22 | call to _mbsinc | TAINT |
|
||||
| taint.cpp:600:7:600:10 | ref arg dest | taint.cpp:601:8:601:11 | dest | |
|
||||
| taint.cpp:601:8:601:11 | dest | taint.cpp:601:7:601:11 | * ... | TAINT |
|
||||
| taint.cpp:604:40:604:45 | source | taint.cpp:605:18:605:23 | source | |
|
||||
| taint.cpp:604:40:604:45 | source | taint.cpp:605:31:605:36 | source | |
|
||||
| taint.cpp:604:40:604:45 | source | taint.cpp:611:25:611:30 | source | |
|
||||
| taint.cpp:604:40:604:45 | source | taint.cpp:616:18:616:23 | source | |
|
||||
| taint.cpp:604:63:604:67 | clean | taint.cpp:611:18:611:22 | clean | |
|
||||
| taint.cpp:604:63:604:67 | clean | taint.cpp:616:26:616:30 | clean | |
|
||||
| taint.cpp:605:10:605:16 | call to _strdec | taint.cpp:605:2:605:37 | ... = ... | |
|
||||
| taint.cpp:605:10:605:16 | call to _strdec | taint.cpp:606:7:606:11 | dest1 | |
|
||||
| taint.cpp:605:10:605:16 | call to _strdec | taint.cpp:607:8:607:12 | dest1 | |
|
||||
| taint.cpp:605:18:605:23 | source | taint.cpp:605:18:605:28 | ... + ... | TAINT |
|
||||
| taint.cpp:605:18:605:28 | ... + ... | taint.cpp:605:10:605:16 | call to _strdec | TAINT |
|
||||
| taint.cpp:605:27:605:28 | 12 | taint.cpp:605:18:605:28 | ... + ... | TAINT |
|
||||
| taint.cpp:605:31:605:36 | source | taint.cpp:605:10:605:16 | call to _strdec | TAINT |
|
||||
| taint.cpp:606:7:606:11 | ref arg dest1 | taint.cpp:607:8:607:12 | dest1 | |
|
||||
| taint.cpp:607:8:607:12 | dest1 | taint.cpp:607:7:607:12 | * ... | TAINT |
|
||||
| taint.cpp:611:10:611:16 | call to _strdec | taint.cpp:611:2:611:31 | ... = ... | |
|
||||
| taint.cpp:611:10:611:16 | call to _strdec | taint.cpp:612:7:612:11 | dest2 | |
|
||||
| taint.cpp:611:10:611:16 | call to _strdec | taint.cpp:613:8:613:12 | dest2 | |
|
||||
| taint.cpp:611:18:611:22 | clean | taint.cpp:611:10:611:16 | call to _strdec | TAINT |
|
||||
| taint.cpp:611:25:611:30 | source | taint.cpp:611:10:611:16 | call to _strdec | TAINT |
|
||||
| taint.cpp:612:7:612:11 | ref arg dest2 | taint.cpp:613:8:613:12 | dest2 | |
|
||||
| taint.cpp:613:8:613:12 | dest2 | taint.cpp:613:7:613:12 | * ... | TAINT |
|
||||
| taint.cpp:616:10:616:16 | call to _strdec | taint.cpp:616:2:616:31 | ... = ... | |
|
||||
| taint.cpp:616:10:616:16 | call to _strdec | taint.cpp:617:7:617:11 | dest3 | |
|
||||
| taint.cpp:616:10:616:16 | call to _strdec | taint.cpp:618:8:618:12 | dest3 | |
|
||||
| taint.cpp:616:18:616:23 | source | taint.cpp:616:10:616:16 | call to _strdec | TAINT |
|
||||
| taint.cpp:616:26:616:30 | clean | taint.cpp:616:10:616:16 | call to _strdec | TAINT |
|
||||
| taint.cpp:617:7:617:11 | ref arg dest3 | taint.cpp:618:8:618:12 | dest3 | |
|
||||
| taint.cpp:618:8:618:12 | dest3 | taint.cpp:618:7:618:12 | * ... | TAINT |
|
||||
| taint.cpp:625:33:625:38 | source | taint.cpp:628:17:628:22 | source | |
|
||||
| taint.cpp:628:7:628:15 | call to _strnextc | taint.cpp:628:3:628:25 | ... = ... | |
|
||||
| taint.cpp:628:7:628:15 | call to _strnextc | taint.cpp:629:8:629:8 | c | |
|
||||
| taint.cpp:628:7:628:15 | call to _strnextc | taint.cpp:630:10:630:10 | c | |
|
||||
| taint.cpp:628:17:628:22 | source | taint.cpp:628:17:628:24 | ... ++ | |
|
||||
| taint.cpp:628:17:628:24 | ... ++ | taint.cpp:628:7:628:15 | call to _strnextc | TAINT |
|
||||
| taint.cpp:628:17:628:24 | ... ++ | taint.cpp:628:17:628:22 | source | TAINT |
|
||||
| taint.cpp:631:6:631:14 | call to _strnextc | taint.cpp:631:2:631:18 | ... = ... | |
|
||||
| taint.cpp:631:6:631:14 | call to _strnextc | taint.cpp:632:7:632:7 | c | |
|
||||
| taint.cpp:631:16:631:17 | | taint.cpp:631:6:631:14 | call to _strnextc | TAINT |
|
||||
| vector.cpp:16:43:16:49 | source1 | vector.cpp:17:26:17:32 | source1 | |
|
||||
| vector.cpp:16:43:16:49 | source1 | vector.cpp:31:38:31:44 | source1 | |
|
||||
| vector.cpp:17:21:17:33 | call to vector | vector.cpp:19:14:19:14 | v | |
|
||||
|
||||
@@ -38,13 +38,13 @@ public:
|
||||
|
||||
void test_typedefs(int_iterator_by_typedefs source1) {
|
||||
sink(*source1); // $ ast,ir
|
||||
sink(*(source1++)); // $ ast MISSING: ir
|
||||
sink(*(source1++)); // $ ast,ir
|
||||
sink(*(++source1)); // $ ast MISSING: ir
|
||||
}
|
||||
|
||||
void test_trait(int_iterator_by_trait source1) {
|
||||
sink(*source1); // $ ast,ir
|
||||
sink(*(source1++)); // $ ast MISSING: ir
|
||||
sink(*(source1++)); // $ ast,ir
|
||||
sink(*(++source1)); // $ ast MISSING: ir
|
||||
}
|
||||
|
||||
|
||||
@@ -415,10 +415,10 @@ void test_string_iterators() {
|
||||
sink(*i9); // $ ast,ir
|
||||
|
||||
i10 = i2;
|
||||
sink(*(i10++)); // $ ast MISSING: ir
|
||||
sink(*(i10++)); // $ ast,ir
|
||||
sink(i10); // $ ast,ir
|
||||
i11 = i2;
|
||||
sink(*(i11--)); // $ ast MISSING: ir
|
||||
sink(*(i11--)); // $ ast,ir
|
||||
sink(i11); // $ ast,ir
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ void test_stringstream_int(int source)
|
||||
sink(ss1 << 1234);
|
||||
sink(ss2 << source); // $ ast MISSING: ir
|
||||
sink(ss1 >> v1);
|
||||
sink(ss2 >> v2); // $ ast MISSING: ir
|
||||
sink(ss2 >> v2); // $ ast,ir
|
||||
|
||||
sink(ss1);
|
||||
sink(ss2); // $ ast,ir
|
||||
@@ -143,27 +143,27 @@ void test_stringstream_in()
|
||||
sink(ss2 << source()); // $ ast,ir
|
||||
|
||||
sink(ss1 >> s1);
|
||||
sink(ss2 >> s2); // $ ast MISSING: ir
|
||||
sink(ss2 >> s3 >> s4); // $ ast MISSING: ir
|
||||
sink(ss2 >> s2); // $ ast,ir
|
||||
sink(ss2 >> s3 >> s4); // $ ast,ir
|
||||
sink(s1);
|
||||
sink(s2); // $ ast,ir
|
||||
sink(s3); // $ ast,ir
|
||||
sink(s4); // $ ast MISSING: ir
|
||||
sink(s4); // $ ast,ir
|
||||
|
||||
sink(ss1 >> b1);
|
||||
sink(ss2 >> b2); // $ ast MISSING: ir
|
||||
sink(ss2 >> b3 >> b4); // $ ast MISSING: ir
|
||||
sink(ss2 >> b2); // $ ast,ir
|
||||
sink(ss2 >> b3 >> b4); // $ ast,ir
|
||||
sink(b1);
|
||||
sink(b2); // $ ast,ir
|
||||
sink(b3); // $ ast,ir
|
||||
sink(b4); // $ ast MISSING: ir
|
||||
sink(b4); // $ ast,ir
|
||||
|
||||
sink(ss1.read(b5, 100));
|
||||
sink(ss2.read(b6, 100)); // $ ast MISSING: ir
|
||||
sink(ss2.read(b6, 100)); // $ ast,ir
|
||||
sink(ss1.readsome(b7, 100));
|
||||
sink(ss2.readsome(b8, 100)); // (returns a length, not significantly tainted)
|
||||
sink(ss1.get(b9, 100));
|
||||
sink(ss2.get(b10, 100)); // $ ast MISSING: ir
|
||||
sink(ss2.get(b10, 100)); // $ ast,ir
|
||||
sink(b5);
|
||||
sink(b6); // $ ast,ir
|
||||
sink(b7);
|
||||
@@ -176,7 +176,7 @@ void test_stringstream_in()
|
||||
sink(c3 = ss1.peek());
|
||||
sink(c4 = ss2.peek()); // $ ast,ir
|
||||
sink(ss1.get(c5));
|
||||
sink(ss2.get(c6)); // $ ast MISSING: ir
|
||||
sink(ss2.get(c6)); // $ ast,ir
|
||||
sink(c1);
|
||||
sink(c2); // $ ast,ir
|
||||
sink(c3);
|
||||
@@ -212,44 +212,44 @@ void test_getline()
|
||||
std::string s1, s2, s3, s4, s5, s6, s7, s8;
|
||||
|
||||
sink(ss1.getline(b1, 1000));
|
||||
sink(ss2.getline(b2, 1000)); // $ ast MISSING: ir
|
||||
sink(ss2.getline(b3, 1000)); // $ ast MISSING: ir
|
||||
sink(ss2.getline(b2, 1000)); // $ ast,ir
|
||||
sink(ss2.getline(b3, 1000)); // $ ast,ir
|
||||
sink(ss1.getline(b3, 1000));
|
||||
sink(b1);
|
||||
sink(b2); // $ ast,ir
|
||||
sink(b3); // $ SPURIOUS: ast,ir
|
||||
|
||||
sink(ss1.getline(b4, 1000, ' '));
|
||||
sink(ss2.getline(b5, 1000, ' ')); // $ ast MISSING: ir
|
||||
sink(ss2.getline(b6, 1000, ' ')); // $ ast MISSING: ir
|
||||
sink(ss2.getline(b5, 1000, ' ')); // $ ast,ir
|
||||
sink(ss2.getline(b6, 1000, ' ')); // $ ast,ir
|
||||
sink(ss1.getline(b6, 1000, ' '));
|
||||
sink(b4);
|
||||
sink(b5); // $ ast,ir
|
||||
sink(b6); // $ SPURIOUS: ast,ir
|
||||
|
||||
sink(ss2.getline(b7, 1000).getline(b8, 1000)); // $ ast MISSING: ir
|
||||
sink(ss2.getline(b7, 1000).getline(b8, 1000)); // $ ast,ir
|
||||
sink(b7); // $ ast,ir
|
||||
sink(b8); // $ ast MISSING: ir
|
||||
|
||||
sink(getline(ss1, s1));
|
||||
sink(getline(ss2, s2)); // $ ast MISSING: ir
|
||||
sink(getline(ss2, s3)); // $ ast MISSING: ir
|
||||
sink(getline(ss2, s2)); // $ ast,ir
|
||||
sink(getline(ss2, s3)); // $ ast,ir
|
||||
sink(getline(ss1, s3));
|
||||
sink(s1);
|
||||
sink(s2); // $ ast,ir
|
||||
sink(s3); // $ SPURIOUS: ast,ir
|
||||
|
||||
sink(getline(ss1, s4, ' '));
|
||||
sink(getline(ss2, s5, ' ')); // $ ast MISSING: ir
|
||||
sink(getline(ss2, s6, ' ')); // $ ast MISSING: ir
|
||||
sink(getline(ss2, s5, ' ')); // $ ast,ir
|
||||
sink(getline(ss2, s6, ' ')); // $ ast,ir
|
||||
sink(getline(ss1, s6, ' '));
|
||||
sink(s4);
|
||||
sink(s5); // $ ast,ir
|
||||
sink(s6); // $ SPURIOUS: ast,ir
|
||||
|
||||
sink(getline(getline(ss2, s7), s8)); // $ ast MISSING: ir
|
||||
sink(getline(getline(ss2, s7), s8)); // $ ast,ir
|
||||
sink(s7); // $ ast,ir
|
||||
sink(s8); // $ ast MISSING: ir
|
||||
sink(s8); // $ ast,ir
|
||||
}
|
||||
|
||||
void test_chaining()
|
||||
@@ -259,7 +259,7 @@ void test_chaining()
|
||||
char b1[1000] = {0};
|
||||
char b2[1000] = {0};
|
||||
|
||||
sink(ss1.get(b1, 100).unget().get(b2, 100)); // $ ast MISSING: ir
|
||||
sink(ss1.get(b1, 100).unget().get(b2, 100)); // $ ast,ir
|
||||
sink(b1); // $ ast,ir
|
||||
sink(b2); // $ ast MISSING: ir
|
||||
|
||||
|
||||
@@ -484,3 +484,150 @@ void test_getdelim(FILE* source1) {
|
||||
|
||||
sink(line); // $ ir,ast
|
||||
}
|
||||
|
||||
// --- strtok ---
|
||||
|
||||
char *strtok(char *str, const char *delim);
|
||||
|
||||
void test_strtok(char *source) {
|
||||
const char* delim = ",.-;:_";
|
||||
char* tokenized = strtok(source, delim);
|
||||
sink(tokenized); // $ ast,ir
|
||||
sink(delim);
|
||||
}
|
||||
|
||||
// --- strset ---
|
||||
|
||||
char *_strset(char *str, int c);
|
||||
|
||||
void test_strset_1(char* ptr, char source) {
|
||||
_strset(ptr, source);
|
||||
sink(ptr); // $ SPURIOUS: ast,ir
|
||||
sink(*ptr); // $ ast,ir
|
||||
}
|
||||
|
||||
void test_strset_2(char* source) {
|
||||
_strset(source, 0);
|
||||
sink(source); // $ ast,ir
|
||||
}
|
||||
|
||||
// --- mempcpy ---
|
||||
|
||||
void *mempcpy(void *dest, const void *src, size_t n);
|
||||
|
||||
void test_mempcpy(int *source) {
|
||||
int x;
|
||||
mempcpy(&x, source, sizeof(int));
|
||||
sink(x); // $ ast=518:24 MISSING: ir SPURIOUS: ast=519:6
|
||||
}
|
||||
|
||||
// --- memccpy ---
|
||||
|
||||
void *memccpy(void *dest, const void *src, int c, size_t n);
|
||||
|
||||
void test_memccpy(int *source) {
|
||||
int dest[16];
|
||||
memccpy(dest, source, 42, sizeof(dest));
|
||||
sink(dest); // $ ast=528:24 MISSING: ir SPURIOUS: ast=529:6
|
||||
}
|
||||
|
||||
// --- strcat and related functions ---
|
||||
|
||||
char* strcat (char*, const char*);
|
||||
|
||||
void test_strcat(char* dest1, char* dest2, char* clean, char* source) {
|
||||
strcat(dest1, source);
|
||||
sink(dest1); // $ ast,ir
|
||||
|
||||
strcat(dest2, clean);
|
||||
sink(dest2);
|
||||
}
|
||||
|
||||
typedef void* _locale_t;
|
||||
|
||||
unsigned char *_mbsncat_l(unsigned char *, const unsigned char *, int, _locale_t);
|
||||
|
||||
void test__mbsncat_l(unsigned char* dest1, unsigned const char* ptr, unsigned char* dest3,
|
||||
_locale_t clean, _locale_t source, int n) {
|
||||
unsigned char* dest2 = _mbsncat_l(dest1, ptr, n, source);
|
||||
sink(dest1); // $ SPURIOUS: ast,ir
|
||||
sink(*dest1); // $ ast,ir
|
||||
sink(dest2); // $ SPURIOUS: ir
|
||||
sink(*dest2); // $ ir
|
||||
|
||||
unsigned char* dest4 = _mbsncat_l(dest3, ptr, n, clean);
|
||||
sink(dest3);
|
||||
sink(*dest3);
|
||||
sink(dest4);
|
||||
sink(*dest4);
|
||||
}
|
||||
|
||||
// --- strsep ---
|
||||
|
||||
char *strsep(char**, const char *);
|
||||
|
||||
void test_strsep(char *source) {
|
||||
const char* delim = ",.-;:_";
|
||||
char* tokenized;
|
||||
while(tokenized = strsep(&source, delim)) {
|
||||
sink(tokenized); // $ ast,ir
|
||||
sink(*tokenized); // $ ast,ir
|
||||
}
|
||||
}
|
||||
|
||||
// --- _strinc and related functions ---
|
||||
|
||||
char* _strinc(const char*, _locale_t);
|
||||
unsigned char* _mbsinc(const unsigned char*);
|
||||
unsigned char *_strdec(const unsigned char*, const unsigned char*);
|
||||
|
||||
void test__strinc(char* source, char* clean, char* dest1, char* dest2, _locale_t locale) {
|
||||
dest1 = _strinc(source, locale);
|
||||
sink(dest1); // $ ast,ir
|
||||
sink(*dest1); // $ ast,ir
|
||||
|
||||
dest2 = _strinc(clean, locale);
|
||||
sink(dest2);
|
||||
sink(*dest2);
|
||||
}
|
||||
|
||||
void test__mbsinc(unsigned char* source_unsigned, char* source, unsigned char* dest_unsigned, char* dest) {
|
||||
dest_unsigned = _mbsinc(source_unsigned);
|
||||
sink(dest_unsigned); // $ ast,ir
|
||||
sink(*dest_unsigned); // $ ast,ir
|
||||
|
||||
dest = (char*)_mbsinc((unsigned char*)source);
|
||||
sink(dest); // $ ast,ir
|
||||
sink(*dest); // $ ast,ir
|
||||
}
|
||||
|
||||
void test__strdec(const unsigned char* source, unsigned char* clean, unsigned char* dest1, unsigned char* dest2, unsigned char* dest3) {
|
||||
dest1 = _strdec(source + 12, source);
|
||||
sink(dest1); // $ ast,ir
|
||||
sink(*dest1); // $ ast,ir
|
||||
|
||||
// If `clean` does not precede `source` this technically breaks the precondition of _strdec.
|
||||
// We would still like to have taint, though.
|
||||
dest2 = _strdec(clean, source);
|
||||
sink(dest2); // $ ast,ir
|
||||
sink(*dest2); // $ ast,ir
|
||||
|
||||
// Also breaks the precondition on _strdec.
|
||||
dest3 = _strdec(source, clean);
|
||||
sink(dest3); // $ ast,ir
|
||||
sink(*dest3); // $ ast,ir
|
||||
}
|
||||
|
||||
// --- strnextc ---
|
||||
|
||||
unsigned int _strnextc(const char*);
|
||||
|
||||
void test__strnextc(const char* source) {
|
||||
unsigned c = 0;
|
||||
do {
|
||||
c = _strnextc(source++);
|
||||
sink(c); // $ ast,ir
|
||||
} while(c != '\0');
|
||||
c = _strnextc("");
|
||||
sink(c);
|
||||
}
|
||||
@@ -491,8 +491,8 @@ void test_vector_emplace() {
|
||||
std::vector<int> v1(10), v2(10);
|
||||
|
||||
v1.emplace_back(source());
|
||||
sink(v1); // $ ast MISSING: ir
|
||||
sink(v1); // $ ast,ir
|
||||
|
||||
v2.emplace(v2.begin(), source());
|
||||
sink(v2); // $ ast MISSING: ir
|
||||
sink(v2); // $ ast,ir
|
||||
}
|
||||
|
||||
@@ -889,10 +889,8 @@ ssa.cpp:
|
||||
# 207| m207_4(unknown) = Chi : total:m207_2, partial:m207_3
|
||||
# 207| r207_5(glval<int>) = VariableAddress[x] :
|
||||
# 207| m207_6(int) = InitializeParameter[x] : &:r207_5
|
||||
# 207| m207_7(unknown) = Chi : total:m207_4, partial:m207_6
|
||||
# 208| r208_1(glval<int>) = VariableAddress[y] :
|
||||
# 208| m208_2(int) = Uninitialized[y] : &:r208_1
|
||||
# 208| m208_3(unknown) = Chi : total:m207_7, partial:m208_2
|
||||
# 209| r209_1(glval<unknown>) = FunctionAddress[memcpy] :
|
||||
# 209| r209_2(glval<int>) = VariableAddress[y] :
|
||||
# 209| r209_3(int *) = CopyValue : r209_2
|
||||
@@ -904,15 +902,15 @@ ssa.cpp:
|
||||
# 209| r209_9(void *) = Call[memcpy] : func:r209_1, 0:r209_4, 1:r209_7, 2:r209_8
|
||||
# 209| v209_10(void) = ^SizedBufferReadSideEffect[1] : &:r209_7, r209_8, ~m207_6
|
||||
# 209| m209_11(unknown) = ^SizedBufferMustWriteSideEffect[0] : &:r209_4, r209_8
|
||||
# 209| m209_12(unknown) = Chi : total:m208_3, partial:m209_11
|
||||
# 209| m209_12(int) = Chi : total:m208_2, partial:m209_11
|
||||
# 210| r210_1(glval<int>) = VariableAddress[#return] :
|
||||
# 210| r210_2(glval<int>) = VariableAddress[y] :
|
||||
# 210| r210_3(int) = Load[y] : &:r210_2, ~m209_12
|
||||
# 210| r210_3(int) = Load[y] : &:r210_2, m209_12
|
||||
# 210| m210_4(int) = Store[#return] : &:r210_1, r210_3
|
||||
# 207| r207_8(glval<int>) = VariableAddress[#return] :
|
||||
# 207| v207_9(void) = ReturnValue : &:r207_8, m210_4
|
||||
# 207| v207_10(void) = AliasedUse : m207_3
|
||||
# 207| v207_11(void) = ExitFunction :
|
||||
# 207| r207_7(glval<int>) = VariableAddress[#return] :
|
||||
# 207| v207_8(void) = ReturnValue : &:r207_7, m210_4
|
||||
# 207| v207_9(void) = AliasedUse : m207_3
|
||||
# 207| v207_10(void) = ExitFunction :
|
||||
|
||||
# 213| void InitArray()
|
||||
# 213| Block 0
|
||||
@@ -1104,51 +1102,49 @@ ssa.cpp:
|
||||
# 247| m247_6(char *) = InitializeParameter[src] : &:r247_5
|
||||
# 247| r247_7(char *) = Load[src] : &:r247_5, m247_6
|
||||
# 247| m247_8(unknown) = InitializeIndirection[src] : &:r247_7
|
||||
# 247| m247_9(unknown) = Chi : total:m247_4, partial:m247_8
|
||||
# 247| r247_10(glval<int>) = VariableAddress[size] :
|
||||
# 247| m247_11(int) = InitializeParameter[size] : &:r247_10
|
||||
# 247| r247_9(glval<int>) = VariableAddress[size] :
|
||||
# 247| m247_10(int) = InitializeParameter[size] : &:r247_9
|
||||
# 248| r248_1(glval<char *>) = VariableAddress[dst] :
|
||||
# 248| r248_2(glval<unknown>) = FunctionAddress[operator new[]] :
|
||||
# 248| r248_3(glval<int>) = VariableAddress[size] :
|
||||
# 248| r248_4(int) = Load[size] : &:r248_3, m247_11
|
||||
# 248| r248_4(int) = Load[size] : &:r248_3, m247_10
|
||||
# 248| r248_5(unsigned long) = Convert : r248_4
|
||||
# 248| r248_6(unsigned long) = Constant[1] :
|
||||
# 248| r248_7(unsigned long) = Mul : r248_5, r248_6
|
||||
# 248| r248_8(void *) = Call[operator new[]] : func:r248_2, 0:r248_7
|
||||
# 248| m248_9(unknown) = ^CallSideEffect : ~m247_9
|
||||
# 248| m248_10(unknown) = Chi : total:m247_9, partial:m248_9
|
||||
# 248| m248_9(unknown) = ^CallSideEffect : ~m247_4
|
||||
# 248| m248_10(unknown) = Chi : total:m247_4, partial:m248_9
|
||||
# 248| m248_11(unknown) = ^InitializeDynamicAllocation : &:r248_8
|
||||
# 248| m248_12(unknown) = Chi : total:m248_10, partial:m248_11
|
||||
# 248| r248_13(char *) = Convert : r248_8
|
||||
# 248| m248_14(char *) = Store[dst] : &:r248_1, r248_13
|
||||
# 248| r248_12(char *) = Convert : r248_8
|
||||
# 248| m248_13(char *) = Store[dst] : &:r248_1, r248_12
|
||||
# 249| r249_1(char) = Constant[97] :
|
||||
# 249| r249_2(glval<char *>) = VariableAddress[src] :
|
||||
# 249| r249_3(char *) = Load[src] : &:r249_2, m247_6
|
||||
# 249| r249_4(glval<char>) = CopyValue : r249_3
|
||||
# 249| m249_5(char) = Store[?] : &:r249_4, r249_1
|
||||
# 249| m249_6(unknown) = Chi : total:m248_12, partial:m249_5
|
||||
# 249| m249_6(unknown) = Chi : total:m247_8, partial:m249_5
|
||||
# 250| r250_1(glval<unknown>) = FunctionAddress[memcpy] :
|
||||
# 250| r250_2(glval<char *>) = VariableAddress[dst] :
|
||||
# 250| r250_3(char *) = Load[dst] : &:r250_2, m248_14
|
||||
# 250| r250_3(char *) = Load[dst] : &:r250_2, m248_13
|
||||
# 250| r250_4(void *) = Convert : r250_3
|
||||
# 250| r250_5(glval<char *>) = VariableAddress[src] :
|
||||
# 250| r250_6(char *) = Load[src] : &:r250_5, m247_6
|
||||
# 250| r250_7(void *) = Convert : r250_6
|
||||
# 250| r250_8(glval<int>) = VariableAddress[size] :
|
||||
# 250| r250_9(int) = Load[size] : &:r250_8, m247_11
|
||||
# 250| r250_9(int) = Load[size] : &:r250_8, m247_10
|
||||
# 250| r250_10(void *) = Call[memcpy] : func:r250_1, 0:r250_4, 1:r250_7, 2:r250_9
|
||||
# 250| v250_11(void) = ^SizedBufferReadSideEffect[1] : &:r250_7, r250_9, ~m249_6
|
||||
# 250| m250_12(unknown) = ^SizedBufferMustWriteSideEffect[0] : &:r250_4, r250_9
|
||||
# 250| m250_13(unknown) = Chi : total:m249_6, partial:m250_12
|
||||
# 250| m250_13(unknown) = Chi : total:m248_11, partial:m250_12
|
||||
# 251| r251_1(glval<char *>) = VariableAddress[#return] :
|
||||
# 251| r251_2(glval<char *>) = VariableAddress[dst] :
|
||||
# 251| r251_3(char *) = Load[dst] : &:r251_2, m248_14
|
||||
# 251| r251_3(char *) = Load[dst] : &:r251_2, m248_13
|
||||
# 251| m251_4(char *) = Store[#return] : &:r251_1, r251_3
|
||||
# 247| v247_12(void) = ReturnIndirection[src] : &:r247_7, ~m250_13
|
||||
# 247| r247_13(glval<char *>) = VariableAddress[#return] :
|
||||
# 247| v247_14(void) = ReturnValue : &:r247_13, m251_4
|
||||
# 247| v247_15(void) = AliasedUse : ~m250_13
|
||||
# 247| v247_16(void) = ExitFunction :
|
||||
# 247| v247_11(void) = ReturnIndirection[src] : &:r247_7, m249_6
|
||||
# 247| r247_12(glval<char *>) = VariableAddress[#return] :
|
||||
# 247| v247_13(void) = ReturnValue : &:r247_12, m251_4
|
||||
# 247| v247_14(void) = AliasedUse : ~m248_10
|
||||
# 247| v247_15(void) = ExitFunction :
|
||||
|
||||
# 254| char StringLiteralAliasing2(bool)
|
||||
# 254| Block 0
|
||||
@@ -1206,39 +1202,37 @@ ssa.cpp:
|
||||
# 268| m268_6(void *) = InitializeParameter[s] : &:r268_5
|
||||
# 268| r268_7(void *) = Load[s] : &:r268_5, m268_6
|
||||
# 268| m268_8(unknown) = InitializeIndirection[s] : &:r268_7
|
||||
# 268| m268_9(unknown) = Chi : total:m268_4, partial:m268_8
|
||||
# 268| r268_10(glval<int>) = VariableAddress[size] :
|
||||
# 268| m268_11(int) = InitializeParameter[size] : &:r268_10
|
||||
# 268| r268_9(glval<int>) = VariableAddress[size] :
|
||||
# 268| m268_10(int) = InitializeParameter[size] : &:r268_9
|
||||
# 269| r269_1(glval<void *>) = VariableAddress[buf] :
|
||||
# 269| r269_2(glval<unknown>) = FunctionAddress[malloc] :
|
||||
# 269| r269_3(glval<int>) = VariableAddress[size] :
|
||||
# 269| r269_4(int) = Load[size] : &:r269_3, m268_11
|
||||
# 269| r269_4(int) = Load[size] : &:r269_3, m268_10
|
||||
# 269| r269_5(void *) = Call[malloc] : func:r269_2, 0:r269_4
|
||||
# 269| m269_6(unknown) = ^CallSideEffect : ~m268_9
|
||||
# 269| m269_7(unknown) = Chi : total:m268_9, partial:m269_6
|
||||
# 269| m269_6(unknown) = ^CallSideEffect : ~m268_4
|
||||
# 269| m269_7(unknown) = Chi : total:m268_4, partial:m269_6
|
||||
# 269| m269_8(unknown) = ^InitializeDynamicAllocation : &:r269_5
|
||||
# 269| m269_9(unknown) = Chi : total:m269_7, partial:m269_8
|
||||
# 269| m269_10(void *) = Store[buf] : &:r269_1, r269_5
|
||||
# 269| m269_9(void *) = Store[buf] : &:r269_1, r269_5
|
||||
# 270| r270_1(glval<unknown>) = FunctionAddress[memcpy] :
|
||||
# 270| r270_2(glval<void *>) = VariableAddress[buf] :
|
||||
# 270| r270_3(void *) = Load[buf] : &:r270_2, m269_10
|
||||
# 270| r270_3(void *) = Load[buf] : &:r270_2, m269_9
|
||||
# 270| r270_4(glval<void *>) = VariableAddress[s] :
|
||||
# 270| r270_5(void *) = Load[s] : &:r270_4, m268_6
|
||||
# 270| r270_6(glval<int>) = VariableAddress[size] :
|
||||
# 270| r270_7(int) = Load[size] : &:r270_6, m268_11
|
||||
# 270| r270_7(int) = Load[size] : &:r270_6, m268_10
|
||||
# 270| r270_8(void *) = Call[memcpy] : func:r270_1, 0:r270_3, 1:r270_5, 2:r270_7
|
||||
# 270| v270_9(void) = ^SizedBufferReadSideEffect[1] : &:r270_5, r270_7, ~m269_7
|
||||
# 270| v270_9(void) = ^SizedBufferReadSideEffect[1] : &:r270_5, r270_7, ~m268_8
|
||||
# 270| m270_10(unknown) = ^SizedBufferMustWriteSideEffect[0] : &:r270_3, r270_7
|
||||
# 270| m270_11(unknown) = Chi : total:m269_9, partial:m270_10
|
||||
# 270| m270_11(unknown) = Chi : total:m269_8, partial:m270_10
|
||||
# 271| r271_1(glval<void *>) = VariableAddress[#return] :
|
||||
# 271| r271_2(glval<void *>) = VariableAddress[buf] :
|
||||
# 271| r271_3(void *) = Load[buf] : &:r271_2, m269_10
|
||||
# 271| r271_3(void *) = Load[buf] : &:r271_2, m269_9
|
||||
# 271| m271_4(void *) = Store[#return] : &:r271_1, r271_3
|
||||
# 268| v268_12(void) = ReturnIndirection[s] : &:r268_7, ~m270_11
|
||||
# 268| r268_13(glval<void *>) = VariableAddress[#return] :
|
||||
# 268| v268_14(void) = ReturnValue : &:r268_13, m271_4
|
||||
# 268| v268_15(void) = AliasedUse : ~m270_11
|
||||
# 268| v268_16(void) = ExitFunction :
|
||||
# 268| v268_11(void) = ReturnIndirection[s] : &:r268_7, m268_8
|
||||
# 268| r268_12(glval<void *>) = VariableAddress[#return] :
|
||||
# 268| v268_13(void) = ReturnValue : &:r268_12, m271_4
|
||||
# 268| v268_14(void) = AliasedUse : ~m269_7
|
||||
# 268| v268_15(void) = ExitFunction :
|
||||
|
||||
# 275| void EscapedButNotConflated(bool, Point, int)
|
||||
# 275| Block 0
|
||||
|
||||
@@ -2,3 +2,4 @@ name: codeql-cpp-tests
|
||||
version: 0.0.0
|
||||
libraryPathDependencies: codeql-cpp
|
||||
extractor: cpp
|
||||
tests: .
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
| printf.cpp:33:31:33:37 | test | This argument should be of type 'char *' but is of type 'char16_t *' |
|
||||
| printf.cpp:45:29:45:35 | test | This argument should be of type 'char *' but is of type 'char16_t *' |
|
||||
| printf.cpp:52:29:52:35 | test | This argument should be of type 'char16_t *' but is of type 'wchar_t *' |
|
||||
| printf.cpp:31:31:31:37 | test | This argument should be of type 'char *' but is of type 'char16_t *' |
|
||||
| printf.cpp:43:29:43:35 | test | This argument should be of type 'char *' but is of type 'char16_t *' |
|
||||
| printf.cpp:50:29:50:35 | test | This argument should be of type 'char16_t *' but is of type 'wchar_t *' |
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
| printf.cpp:15:5:15:12 | swprintf | char | char16_t | char16_t |
|
||||
| printf.cpp:26:5:26:11 | sprintf | char | char16_t | char16_t |
|
||||
| printf.cpp:13:5:13:12 | swprintf | char | char16_t | char16_t |
|
||||
| printf.cpp:24:5:24:11 | sprintf | char | char16_t | char16_t |
|
||||
|
||||
@@ -8,9 +8,7 @@ typedef void *va_list;
|
||||
#define va_start(va, other)
|
||||
#define va_end(args)
|
||||
|
||||
int vswprintf(WCHAR *dest, WCHAR *format, va_list args) {
|
||||
return 0;
|
||||
}
|
||||
int vswprintf(WCHAR *dest, WCHAR *format, va_list args);
|
||||
|
||||
int swprintf(WCHAR *dest, WCHAR *format, ...) {
|
||||
va_list args;
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
| test.cpp:6:5:6:13 | ... > ... | Unsigned subtraction can never be negative. |
|
||||
| test.cpp:10:8:10:24 | ... > ... | Unsigned subtraction can never be negative. |
|
||||
| test.cpp:15:9:15:25 | ... > ... | Unsigned subtraction can never be negative. |
|
||||
| test.cpp:32:12:32:20 | ... > ... | Unsigned subtraction can never be negative. |
|
||||
| test.cpp:39:12:39:20 | ... > ... | Unsigned subtraction can never be negative. |
|
||||
| test.cpp:47:5:47:13 | ... > ... | Unsigned subtraction can never be negative. |
|
||||
| test.cpp:55:5:55:13 | ... > ... | Unsigned subtraction can never be negative. |
|
||||
| test.cpp:62:5:62:13 | ... > ... | Unsigned subtraction can never be negative. |
|
||||
| test.cpp:69:5:69:13 | ... > ... | Unsigned subtraction can never be negative. |
|
||||
| test.cpp:75:8:75:16 | ... > ... | Unsigned subtraction can never be negative. |
|
||||
@@ -0,0 +1 @@
|
||||
Security/CWE/CWE-191/UnsignedDifferenceExpressionComparedZero.ql
|
||||
@@ -0,0 +1,77 @@
|
||||
int getAnInt();
|
||||
|
||||
bool cond();
|
||||
|
||||
void test(unsigned x, unsigned y, bool unknown) {
|
||||
if(x - y > 0) { } // BAD
|
||||
|
||||
unsigned total = getAnInt();
|
||||
unsigned limit = getAnInt();
|
||||
while(limit - total > 0) { // BAD
|
||||
total += getAnInt();
|
||||
}
|
||||
|
||||
if(total <= limit) {
|
||||
while(limit - total > 0) { // GOOD [FALSE POSITIVE]
|
||||
total += getAnInt();
|
||||
if(total > limit) break;
|
||||
}
|
||||
}
|
||||
|
||||
if(x >= y) {
|
||||
bool b = x - y > 0; // GOOD
|
||||
}
|
||||
|
||||
if((int)(x - y) >= 0) { } // GOOD. Maybe an overflow happened, but the result is converted to the "likely intended" result before the comparison
|
||||
|
||||
if(unknown) {
|
||||
y = x & 0xFF;
|
||||
} else {
|
||||
y = x;
|
||||
}
|
||||
bool b1 = x - y > 0; // GOOD [FALSE POSITIVE]
|
||||
|
||||
x = getAnInt();
|
||||
y = getAnInt();
|
||||
if(y > x) {
|
||||
y = x - 1;
|
||||
}
|
||||
bool b2 = x - y > 0; // GOOD [FALSE POSITIVE]
|
||||
|
||||
int N = getAnInt();
|
||||
y = x;
|
||||
while(cond()) {
|
||||
if(unknown) { y--; }
|
||||
}
|
||||
|
||||
if(x - y > 0) { } // GOOD [FALSE POSITIVE]
|
||||
|
||||
x = y;
|
||||
while(cond()) {
|
||||
if(unknown) break;
|
||||
y--;
|
||||
}
|
||||
|
||||
if(x - y > 0) { } // GOOD [FALSE POSITIVE]
|
||||
|
||||
y = 0;
|
||||
for(int i = 0; i < x; ++i) {
|
||||
if(unknown) { ++y; }
|
||||
}
|
||||
|
||||
if(x - y > 0) { } // GOOD [FALSE POSITIVE]
|
||||
|
||||
x = y;
|
||||
while(cond()) {
|
||||
if(unknown) { x++; }
|
||||
}
|
||||
|
||||
if(x - y > 0) { } // GOOD [FALSE POSITIVE]
|
||||
|
||||
int n = getAnInt();
|
||||
if (n > x - y) { n = x - y; }
|
||||
if (n > 0) {
|
||||
y += n; // NOTE: `n` is at most `x - y` at this point.
|
||||
if (x - y > 0) {} // GOOD [FALSE POSITIVE]
|
||||
}
|
||||
}
|
||||
2125
cpp/upgrades/c82db4c596b8979eba9a8958e24353a5756d7a02/old.dbscheme
Normal file
2125
cpp/upgrades/c82db4c596b8979eba9a8958e24353a5756d7a02/old.dbscheme
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,3 @@
|
||||
description: Change decltype keysets
|
||||
compatibility: backwards
|
||||
|
||||
3
csharp/change-notes/2021-01-15-Relational-pattern.md
Normal file
3
csharp/change-notes/2021-01-15-Relational-pattern.md
Normal file
@@ -0,0 +1,3 @@
|
||||
lgtm,codescanning
|
||||
* The `RelationalPatternExpr` and its 4 sub class have been added to support C# 9
|
||||
relational `<`, `>`, `<=`, and `>=` patterns.
|
||||
3
csharp/change-notes/2021-01-19-Function-pointer.md
Normal file
3
csharp/change-notes/2021-01-19-Function-pointer.md
Normal file
@@ -0,0 +1,3 @@
|
||||
lgtm,codescanning
|
||||
* Function pointer types (`FunctionPointerType`) and call to function pointers
|
||||
(`FunctionPointerCall`) are extracted.
|
||||
2
csharp/change-notes/2021-01-25-Function-pointer-cil.md
Normal file
2
csharp/change-notes/2021-01-25-Function-pointer-cil.md
Normal file
@@ -0,0 +1,2 @@
|
||||
lgtm,codescanning
|
||||
* Function pointer types (`FunctionPointerType`) are extracted from IL. Also, `pinned` and `by-reference` (`cil_type_annotation`) type extraction is fixed.
|
||||
3
csharp/change-notes/2021-01-27-Add-binary-pattern.md
Normal file
3
csharp/change-notes/2021-01-27-Add-binary-pattern.md
Normal file
@@ -0,0 +1,3 @@
|
||||
lgtm,codescanning
|
||||
* The `BinaryPatternExpr` class has been added to support C# 9 `and` and `or`
|
||||
patterns.
|
||||
@@ -0,0 +1,8 @@
|
||||
lgtm,codescanning
|
||||
* The `PreprocessorDirective` class and its base classes have been added to support
|
||||
preprocessor directives, such as `#if`, `#define`, `#undef`, `#line`, `#region`,
|
||||
`#warning`, `#error`, `#pragma warning`, `#pragma checksum` and `#nullable`. Furthermore,
|
||||
`#line` directives are now taken into account when querying the location of any
|
||||
code construct. Files referenced in preprocessor directives are also included in the
|
||||
extraction sources. This change is expected to lead to better error reporting locations
|
||||
in generated code, such as generated code from `.cshtml` files in ASP.NET Core.
|
||||
2
csharp/change-notes/2021-02-04-Records.md
Normal file
2
csharp/change-notes/2021-02-04-Records.md
Normal file
@@ -0,0 +1,2 @@
|
||||
lgtm,codescanning
|
||||
* Record types (`Record`) are extracted.
|
||||
42
csharp/extractor/Semmle.Extraction.CIL/Entities/ByRefType.cs
Normal file
42
csharp/extractor/Semmle.Extraction.CIL/Entities/ByRefType.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace Semmle.Extraction.CIL.Entities
|
||||
{
|
||||
/// <summary>
|
||||
/// Types that are passed by reference are not written directly to trap files. Instead, the annotation is stored on
|
||||
/// the entity.
|
||||
/// </summary>
|
||||
internal sealed class ByRefType : Type
|
||||
{
|
||||
public ByRefType(Context cx, Type elementType) : base(cx)
|
||||
{
|
||||
ElementType = elementType;
|
||||
}
|
||||
|
||||
public override CilTypeKind Kind => throw new NotImplementedException();
|
||||
|
||||
public override Namespace? ContainingNamespace => throw new NotImplementedException();
|
||||
|
||||
public override Type? ContainingType => throw new NotImplementedException();
|
||||
|
||||
public override int ThisTypeParameterCount => throw new NotImplementedException();
|
||||
|
||||
public override IEnumerable<Type> TypeParameters => throw new NotImplementedException();
|
||||
|
||||
public override Type Construct(IEnumerable<Type> typeArguments) => throw new NotImplementedException();
|
||||
|
||||
public override string Name => $"{ElementType.Name}&";
|
||||
|
||||
public Type ElementType { get; }
|
||||
|
||||
public override void WriteAssemblyPrefix(TextWriter trapFile) => throw new NotImplementedException();
|
||||
|
||||
public override void WriteId(TextWriter trapFile, bool inContext)
|
||||
{
|
||||
ElementType.WriteId(trapFile, inContext);
|
||||
trapFile.Write('&');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ namespace Semmle.Extraction.CIL.Entities
|
||||
ValueOrRefType,
|
||||
TypeParameter,
|
||||
Array,
|
||||
Pointer
|
||||
Pointer,
|
||||
FunctionPointer
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection.Metadata;
|
||||
|
||||
namespace Semmle.Extraction.CIL.Entities
|
||||
@@ -36,12 +37,26 @@ namespace Semmle.Extraction.CIL.Entities
|
||||
}
|
||||
|
||||
var name = type.GetQualifiedName();
|
||||
|
||||
if (wellKnownEnums.TryGetValue(name, out var code))
|
||||
{
|
||||
cx.Cx.Extractor.Logger.Log(Util.Logging.Severity.Debug, $"Using hard coded underlying enum type for {name}");
|
||||
return code;
|
||||
}
|
||||
|
||||
cx.Cx.Extractor.Logger.Log(Util.Logging.Severity.Info, $"Couldn't get underlying enum type for {name}");
|
||||
|
||||
// We can't fall back to Int32, because the type returned here defines how many bytes are read from the
|
||||
// stream and how those bytes are interpreted.
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public bool IsSystemType(Type type) => type.GetQualifiedName() == "System.Type";
|
||||
|
||||
private static readonly Dictionary<string, PrimitiveTypeCode> wellKnownEnums = new Dictionary<string, PrimitiveTypeCode>
|
||||
{
|
||||
{ "System.AttributeTargets", PrimitiveTypeCode.Int32 },
|
||||
{ "System.ComponentModel.EditorBrowsableState", PrimitiveTypeCode.Int32 }
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,8 +121,19 @@ namespace Semmle.Extraction.CIL.Entities
|
||||
|
||||
for (var l = 0; l < this.locals.Length; ++l)
|
||||
{
|
||||
this.locals[l] = Cx.Populate(new LocalVariable(Cx, Implementation, l, localVariableTypes[l]));
|
||||
yield return this.locals[l];
|
||||
var t = localVariableTypes[l];
|
||||
if (t is ByRefType brt)
|
||||
{
|
||||
t = brt.ElementType;
|
||||
this.locals[l] = Cx.Populate(new LocalVariable(Cx, Implementation, l, t));
|
||||
yield return this.locals[l];
|
||||
yield return Tuples.cil_type_annotation(this.locals[l], TypeAnnotation.Ref);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.locals[l] = Cx.Populate(new LocalVariable(Cx, Implementation, l, t));
|
||||
yield return this.locals[l];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection.Metadata;
|
||||
|
||||
namespace Semmle.Extraction.CIL.Entities
|
||||
{
|
||||
internal sealed class FunctionPointerType : Type, IParameterizable, ICustomModifierReceiver
|
||||
{
|
||||
private readonly MethodSignature<Type> signature;
|
||||
|
||||
public FunctionPointerType(Context cx, MethodSignature<Type> signature) : base(cx)
|
||||
{
|
||||
this.signature = signature;
|
||||
}
|
||||
|
||||
public override CilTypeKind Kind => CilTypeKind.FunctionPointer;
|
||||
|
||||
public override string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
using var id = new StringWriter();
|
||||
WriteName(
|
||||
id.Write,
|
||||
t => id.Write(t.Name),
|
||||
signature
|
||||
);
|
||||
return id.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
public override Namespace? ContainingNamespace => Cx.GlobalNamespace;
|
||||
|
||||
public override Type? ContainingType => null;
|
||||
|
||||
public override int ThisTypeParameterCount => throw new System.NotImplementedException();
|
||||
|
||||
public override IEnumerable<Type> TypeParameters => throw new System.NotImplementedException();
|
||||
|
||||
public override Type Construct(IEnumerable<Type> typeArguments) => throw new System.NotImplementedException();
|
||||
|
||||
public override void WriteAssemblyPrefix(TextWriter trapFile) { }
|
||||
|
||||
public override void WriteId(TextWriter trapFile, bool inContext)
|
||||
{
|
||||
WriteName(
|
||||
trapFile.Write,
|
||||
t => t.WriteId(trapFile, inContext),
|
||||
signature
|
||||
);
|
||||
}
|
||||
|
||||
internal static void WriteName<TType>(Action<string> write, Action<TType> writeType, MethodSignature<TType> signature)
|
||||
{
|
||||
write("delegate* ");
|
||||
write(GetCallingConvention(signature.Header.CallingConvention));
|
||||
write("<");
|
||||
foreach (var pt in signature.ParameterTypes)
|
||||
{
|
||||
writeType(pt);
|
||||
write(",");
|
||||
}
|
||||
writeType(signature.ReturnType);
|
||||
write(">");
|
||||
}
|
||||
|
||||
internal static string GetCallingConvention(SignatureCallingConvention callingConvention)
|
||||
{
|
||||
if (callingConvention == SignatureCallingConvention.Default)
|
||||
{
|
||||
return "managed";
|
||||
}
|
||||
|
||||
if (callingConvention == SignatureCallingConvention.Unmanaged)
|
||||
{
|
||||
return "unmanaged";
|
||||
}
|
||||
|
||||
return $"unmanaged[{callingConvention}]";
|
||||
}
|
||||
|
||||
public override IEnumerable<IExtractionProduct> Contents
|
||||
{
|
||||
get
|
||||
{
|
||||
foreach (var c in base.Contents)
|
||||
{
|
||||
yield return c;
|
||||
}
|
||||
|
||||
var retType = signature.ReturnType;
|
||||
if (retType is ModifiedType mt)
|
||||
{
|
||||
retType = mt.Unmodified;
|
||||
yield return Tuples.cil_custom_modifiers(this, mt.Modifier, mt.IsRequired);
|
||||
}
|
||||
if (retType is ByRefType byRefType)
|
||||
{
|
||||
retType = byRefType.ElementType;
|
||||
yield return Tuples.cil_type_annotation(this, TypeAnnotation.Ref);
|
||||
}
|
||||
yield return Tuples.cil_function_pointer_return_type(this, retType);
|
||||
|
||||
yield return Tuples.cil_function_pointer_calling_conventions(this, signature.Header.CallingConvention);
|
||||
|
||||
foreach (var p in Method.GetParameterExtractionProducts(signature.ParameterTypes, this, this, Cx, 0))
|
||||
{
|
||||
yield return p;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user