/src/uWebSockets/fuzzing/Http.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* This is a fuzz test of the http parser */ |
2 | | |
3 | | #define WIN32_EXPORT |
4 | | |
5 | | #include "helpers.h" |
6 | | |
7 | | /* We test the websocket parser */ |
8 | | #include "../src/HttpParser.h" |
9 | | |
10 | | /* And the router */ |
11 | | #include "../src/HttpRouter.h" |
12 | | |
13 | | /* Also ProxyParser */ |
14 | | #include "../src/ProxyParser.h" |
15 | | |
16 | | struct StaticData { |
17 | | |
18 | | struct RouterData { |
19 | | |
20 | | }; |
21 | | |
22 | | uWS::HttpRouter<RouterData> router; |
23 | | |
24 | 2 | StaticData() { |
25 | | |
26 | 2.87k | router.add({"get"}, "/:hello/:hi", [](auto *h) mutable { |
27 | 2.87k | auto [paramsTop, params] = h->getParameters(); |
28 | | |
29 | | /* Something is horribly wrong */ |
30 | 2.87k | if (paramsTop != 1 || !params[0].length() || !params[1].length()) { |
31 | 0 | exit(-1); |
32 | 0 | } |
33 | | |
34 | | /* This route did handle it */ |
35 | 2.87k | return true; |
36 | 2.87k | }); |
37 | | |
38 | 1.23k | router.add({"post"}, "/:hello/:hi/*", [](auto *h) mutable { |
39 | 1.23k | auto [paramsTop, params] = h->getParameters(); |
40 | | |
41 | | /* Something is horribly wrong */ |
42 | 1.23k | if (paramsTop != 1 || !params[0].length() || !params[1].length()) { |
43 | 0 | exit(-1); |
44 | 0 | } |
45 | | |
46 | | /* This route did handle it */ |
47 | 1.23k | return true; |
48 | 1.23k | }); |
49 | | |
50 | 122 | router.add({"get"}, "/*", [](auto *h) mutable { |
51 | 122 | auto [paramsTop, params] = h->getParameters(); |
52 | | |
53 | | /* Something is horribly wrong */ |
54 | 122 | if (paramsTop != -1) { |
55 | 0 | exit(-1); |
56 | 0 | } |
57 | | |
58 | | /* This route did not handle it */ |
59 | 122 | return false; |
60 | 122 | }); |
61 | | |
62 | 6.97k | router.add({"get"}, "/hi", [](auto *h) mutable { |
63 | 6.97k | auto [paramsTop, params] = h->getParameters(); |
64 | | |
65 | | /* Something is horribly wrong */ |
66 | 6.97k | if (paramsTop != -1) { |
67 | 0 | exit(-1); |
68 | 0 | } |
69 | | |
70 | | /* This route did handle it */ |
71 | 6.97k | return true; |
72 | 6.97k | }); |
73 | 2 | } |
74 | | } staticData; |
75 | | |
76 | 3.13k | extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { |
77 | | /* Create parser */ |
78 | 3.13k | uWS::HttpParser httpParser; |
79 | | /* User data */ |
80 | 3.13k | void *user = (void *) 13; |
81 | | |
82 | | /* If we are built with WITH_PROXY, pass a ProxyParser as reserved */ |
83 | 3.13k | void *reserved = nullptr; |
84 | | #ifdef UWS_WITH_PROXY |
85 | | uWS::ProxyParser pp; |
86 | | reserved = (void *) &pp; |
87 | | #endif |
88 | | |
89 | | /* Iterate the padded fuzz as chunks */ |
90 | 90.1k | makeChunked(makePadded(data, size), size, [&httpParser, &user, reserved](const uint8_t *data, size_t size) { |
91 | | /* We need at least 1 byte post padding */ |
92 | 90.1k | if (size) { |
93 | 90.0k | size--; |
94 | 90.0k | } else { |
95 | | /* We might be given zero length chunks */ |
96 | 22 | return; |
97 | 22 | } |
98 | | |
99 | | /* If user is null then ignore this chunk */ |
100 | 90.0k | if (!user) { |
101 | 349 | return; |
102 | 349 | } |
103 | | |
104 | | /* Parse it */ |
105 | 89.7k | auto [err, returnedUser] = httpParser.consumePostPadded((char *) data, size, user, reserved, [reserved](void *s, uWS::HttpRequest *httpRequest) -> void * { |
106 | | |
107 | 11.8k | readBytes(httpRequest->getHeader(httpRequest->getUrl())); |
108 | 11.8k | readBytes(httpRequest->getMethod()); |
109 | 11.8k | readBytes(httpRequest->getQuery()); |
110 | 11.8k | readBytes(httpRequest->getQuery("hello")); |
111 | 11.8k | readBytes(httpRequest->getQuery("")); |
112 | | //readBytes(httpRequest->getParameter(0)); |
113 | | |
114 | | #ifdef UWS_WITH_PROXY |
115 | | auto *pp = (uWS::ProxyParser *) reserved; |
116 | | readBytes(pp->getSourceAddress()); |
117 | | #endif |
118 | | |
119 | | /* Route the method and URL in two passes */ |
120 | 11.8k | staticData.router.getUserData() = {}; |
121 | 11.8k | if (!staticData.router.route(httpRequest->getMethod(), httpRequest->getUrl())) { |
122 | | /* It was not handled */ |
123 | 764 | return nullptr; |
124 | 764 | } |
125 | | |
126 | 23.6k | for (auto p : *httpRequest) { |
127 | | |
128 | 23.6k | } |
129 | | |
130 | | /* Return ok */ |
131 | 11.0k | return s; |
132 | | |
133 | 36.9k | }, [](void *user, std::string_view data, bool fin) -> void * { |
134 | | |
135 | | /* Return ok */ |
136 | 36.9k | return user; |
137 | | |
138 | 36.9k | }); |
139 | | |
140 | 89.7k | if (!returnedUser) { |
141 | | /* It is of uttermost importance that if and when we return nullptr from the httpParser we must not |
142 | | * ever use the httpParser ever again. It is in a broken state as returning nullptr is only used |
143 | | * for signalling early closure. You must absolutely must throw it away. Here we just mark user as |
144 | | * null so that we can ignore further chunks of data */ |
145 | 764 | user = nullptr; |
146 | 764 | } |
147 | 89.7k | }); |
148 | | |
149 | 3.13k | return 0; |
150 | 3.13k | } |
151 | | |