/src/node/test/fuzzers/fuzz_tls_socket_request.cc
Line | Count | Source |
1 | | /* |
2 | | * A fuzzer focused on the node::LoadEnvironment() function. |
3 | | * |
4 | | * Code here has been inspired by the cctest test case. |
5 | | */ |
6 | | |
7 | | #include <stdlib.h> |
8 | | #include "node.h" |
9 | | #include "node_platform.h" |
10 | | #include "node_internals.h" |
11 | | #include "env-inl.h" |
12 | | #include "util-inl.h" |
13 | | #include "v8.h" |
14 | | #include "libplatform/libplatform.h" |
15 | | #include "aliased_buffer.h" |
16 | | #include "fuzz_helper.h" |
17 | | #include <fuzzer/FuzzedDataProvider.h> |
18 | | #include <cstdio> |
19 | | |
20 | | using node::AliasedBufferBase; |
21 | | |
22 | | /* General set up */ |
23 | | using ArrayBufferUniquePtr = std::unique_ptr<node::ArrayBufferAllocator, |
24 | | decltype(&node::FreeArrayBufferAllocator)>; |
25 | | using TracingAgentUniquePtr = std::unique_ptr<node::tracing::Agent>; |
26 | | using NodePlatformUniquePtr = std::unique_ptr<node::NodePlatform>; |
27 | | |
28 | | static TracingAgentUniquePtr tracing_agent; |
29 | | static NodePlatformUniquePtr platform; |
30 | | static uv_loop_t current_loop; |
31 | | |
32 | 132k | extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) { |
33 | 132k | uv_os_unsetenv("NODE_OPTIONS"); |
34 | 132k | std::vector<std::string> node_argv{ "fuzz_env" }; |
35 | 132k | std::vector<std::string> exec_argv; |
36 | 132k | std::vector<std::string> errors; |
37 | | |
38 | 132k | node::InitializeNodeWithArgs(&node_argv, &exec_argv, &errors); |
39 | | |
40 | 132k | tracing_agent = std::make_unique<node::tracing::Agent>(); |
41 | 132k | node::tracing::TraceEventHelper::SetAgent(tracing_agent.get()); |
42 | 132k | node::tracing::TracingController* tracing_controller = |
43 | 132k | tracing_agent->GetTracingController(); |
44 | 132k | CHECK_EQ(0, uv_loop_init(¤t_loop)); |
45 | 132k | static constexpr int kV8ThreadPoolSize = 4; |
46 | 132k | platform.reset( |
47 | 132k | new node::NodePlatform(kV8ThreadPoolSize, tracing_controller)); |
48 | 132k | v8::V8::InitializePlatform(platform.get()); |
49 | 132k | cppgc::InitializeProcess(platform->GetPageAllocator()); |
50 | 132k | v8::V8::Initialize(); |
51 | 132k | return 0; |
52 | 132k | } |
53 | | |
54 | 115k | void EnvTest(v8::Isolate* isolate_, char* env_string) { |
55 | 115k | const v8::HandleScope handle_scope(isolate_); |
56 | 115k | Argv argv; |
57 | | |
58 | 115k | node::EnvironmentFlags::Flags flags = node::EnvironmentFlags::kDefaultFlags; |
59 | 115k | auto isolate = handle_scope.GetIsolate(); |
60 | 115k | v8::Local<v8::Context> context_ = node::NewContext(isolate); |
61 | 115k | context_->Enter(); |
62 | | |
63 | 115k | node::IsolateData* isolate_data_ = node::CreateIsolateData(isolate, ¤t_loop, |
64 | 115k | platform.get()); |
65 | 115k | std::vector<std::string> args(*argv, *argv + 1); |
66 | 115k | std::vector<std::string> exec_args(*argv, *argv + 1); |
67 | 115k | node::Environment* environment_ = node::CreateEnvironment(isolate_data_, |
68 | 115k | context_, args, exec_args, flags); |
69 | 115k | node::Environment* envi = environment_; |
70 | 115k | SetProcessExitHandler(envi, [&](node::Environment* env_, int exit_code) { |
71 | 2.25k | node::Stop(envi); |
72 | 2.25k | }); |
73 | 115k | node::LoadEnvironment(envi, env_string); |
74 | | |
75 | | // Cleanup! |
76 | 115k | node::FreeEnvironment(environment_); |
77 | 115k | node::FreeIsolateData(isolate_data_); |
78 | 115k | context_->Exit(); |
79 | 115k | } |
80 | | |
81 | | std::string script_header = |
82 | | "const tls = require('tls');\n" |
83 | | "const https = require('https');\n" |
84 | | "const { setEnvironmentData } = require('worker_threads');\n" |
85 | | "const { send } = require('process');\n" |
86 | | "const key = `-----BEGIN EC PARAMETERS-----\n" |
87 | | "BggqhkjOPQMBBw==\n" |
88 | | "-----END EC PARAMETERS-----\n" |
89 | | "-----BEGIN EC PRIVATE KEY-----\n" |
90 | | "MHcCAQEEIDKfHHbiJMdu2STyHL11fWC7psMY19/gUNpsUpkwgGACoAoGCCqGSM49\n" |
91 | | "AwEHoUQDQgAEItqm+pYj3Ca8bi5mBs+H8xSMxuW2JNn4I+kw3aREsetLk8pn3o81\n" |
92 | | "PWBiTdSZrGBGQSy+UAlQvYeE6Z/QXQk8aw==\n" |
93 | | "-----END EC PRIVATE KEY-----`\n" |
94 | | "\n" |
95 | | "const cert = `-----BEGIN CERTIFICATE-----\n" |
96 | | "MIIBhjCCASsCFDJU1tCo88NYU//pE+DQKO9hUDsFMAoGCCqGSM49BAMCMEUxCzAJ\n" |
97 | | "BgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5l\n" |
98 | | "dCBXaWRnaXRzIFB0eSBMdGQwHhcNMjAwOTIyMDg1NDU5WhcNNDgwMjA3MDg1NDU5\n" |
99 | | "WjBFMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwY\n" |
100 | | "SW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcD\n" |
101 | | "QgAEItqm+pYj3Ca8bi5mBs+H8xSMxuW2JNn4I+kw3aREsetLk8pn3o81PWBiTdSZ\n" |
102 | | "rGBGQSy+UAlQvYeE6Z/QXQk8azAKBggqhkjOPQQDAgNJADBGAiEA7Bdn4F87KqIe\n" |
103 | | "Y/ABy/XIXXpFUb2nyv3zV7POQi2lPcECIQC3UWLmfiedpiIKsf9YRIyO0uEood7+\n" |
104 | | "glj2R1NNr1X68w==\n" |
105 | | "-----END CERTIFICATE-----`\n" |
106 | | "\n" |
107 | | "const options = {\n" |
108 | | " key: key,\n" |
109 | | " cert: cert,\n" |
110 | | "};\n" |
111 | | "\n" |
112 | | "var srv;\n" |
113 | | "var socket;\n" |
114 | | "var receivedResponse = 0;\n" |
115 | | "\n" |
116 | | "async function send_requests() {\n" |
117 | | " socket = tls.connect(4444, 'localhost', { rejectUnauthorized: false }, () => {\n" |
118 | | " const httpRequest = `GET / HTTP/1.1\\r\\nHost: localhost\\r\\nConnection: Keep-alive\\r\\n\\r\\n`;\n"; |
119 | | std::string send_get_request = " socket.write(httpRequest);\n"; |
120 | | |
121 | | std::string pr_decl1 = " var postRequest = `POST / HTTP/1.1\\r\\nHost: localhost\\r\\nContent-Type: application/json\\r\\nContent-Length: ${Buffer.from('"; |
122 | | std::string pr_decl2 = "').length}\\r\\n\\r\\n\n"; |
123 | | std::string pr_decl3 = "`;\n"; |
124 | | std::string send_post_request = " socket.write(postRequest);\n"; |
125 | | std::string script_footer = |
126 | | " })\n" |
127 | | " socket.on('data', (data) => {\n" |
128 | | " receivedResponse++;\n" |
129 | | " if (receivedResponse === 6) {\n" |
130 | | " socket.end();\n" |
131 | | " }\n" |
132 | | " });\n" |
133 | | "\n" |
134 | | " socket.on('end', () => {\n" |
135 | | " srv.close(() => {\n" |
136 | | " })\n" |
137 | | " });\n" |
138 | | "}\n" |
139 | | "function run_server() {\n" |
140 | | " srv = https.createServer(options, function (req, res) {\n" |
141 | | " res.writeHead(200, { 'Content-Type': 'text/plain' });\n" |
142 | | " res.end(\"end\");\n" |
143 | | " });\n" |
144 | | " srv.listen(4444, () => {\n" |
145 | | "\n" |
146 | | " });\n" |
147 | | "}\n" |
148 | | "\n" |
149 | | "run_server();\n" |
150 | | "send_requests();\n"; |
151 | | |
152 | | class FuzzerFixtureHelper { |
153 | | public: |
154 | | v8::Isolate* isolate_; |
155 | | ArrayBufferUniquePtr allocator; |
156 | | |
157 | | FuzzerFixtureHelper() |
158 | 134k | : allocator(ArrayBufferUniquePtr(node::CreateArrayBufferAllocator(), |
159 | 134k | &node::FreeArrayBufferAllocator)) { |
160 | 134k | isolate_ = NewIsolate(allocator.get(), ¤t_loop, platform.get()); |
161 | 134k | CHECK_NOT_NULL(isolate_); |
162 | 134k | isolate_->Enter(); |
163 | 134k | }; |
164 | | |
165 | 134k | void Teardown() { |
166 | 134k | platform->DrainTasks(isolate_); |
167 | 134k | isolate_->Exit(); |
168 | 134k | platform->UnregisterIsolate(isolate_); |
169 | 134k | isolate_->Dispose(); |
170 | 134k | isolate_ = nullptr; |
171 | 134k | } |
172 | | }; |
173 | | |
174 | | // checks for unescaped backtics and double quotes which would break the script |
175 | 9.40k | bool isInvalid(std::string s) { |
176 | 9.40k | char backtick = '`'; |
177 | 286M | for (int i=0; i<s.length(); i++) { |
178 | 286M | if(s[i]==backtick) { |
179 | | // Found a backtick. Check if it is escaped |
180 | 36 | if (i == 0) { |
181 | 5 | return true; |
182 | 5 | } |
183 | 31 | if(s.at(i-1) != '\\') { |
184 | 24 | return true; |
185 | 24 | } |
186 | 31 | } |
187 | 286M | if(s[i]=='"') { |
188 | | // Found a double quote. Check if it is escaped |
189 | 45 | if (i == 0) { |
190 | 6 | return true; |
191 | 6 | } |
192 | 39 | if(s.at(i-1) != '\\') { |
193 | 21 | return true; |
194 | 21 | } |
195 | 39 | } |
196 | 286M | } |
197 | 9.34k | return false; |
198 | 9.40k | } |
199 | | |
200 | 12.2k | extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data2, size_t size) { |
201 | 12.2k | FuzzedDataProvider prov(data2, size); |
202 | 12.2k | std::string post_request1_body = prov.ConsumeRandomLengthString(); |
203 | 12.2k | std::string post_request2_body = prov.ConsumeRandomLengthString(); |
204 | 12.2k | std::string post_request3_body = prov.ConsumeRandomLengthString(); |
205 | | |
206 | 12.2k | if (isInvalid(post_request1_body)) { |
207 | 96 | return 0; |
208 | 96 | } |
209 | 12.1k | if (isInvalid(post_request2_body)) { |
210 | 61 | return 0; |
211 | 61 | } |
212 | 12.0k | if (isInvalid(post_request3_body)) { |
213 | 50 | return 0; |
214 | 50 | } |
215 | | |
216 | 12.0k | std::stringstream post_request1; |
217 | 12.0k | post_request1 << pr_decl1 << post_request1_body << pr_decl2 << post_request1_body << pr_decl3 << send_post_request << send_get_request << std::endl; |
218 | 12.0k | std::string pr1_str = post_request1.str(); |
219 | | |
220 | 12.0k | std::stringstream post_request2; |
221 | 12.0k | post_request2 << pr_decl1 << post_request2_body << pr_decl2 << post_request2_body << pr_decl3 << send_post_request << send_get_request << std::endl; |
222 | 12.0k | std::string pr2_str = post_request2.str(); |
223 | | |
224 | 12.0k | std::stringstream post_request3; |
225 | 12.0k | post_request3 << pr_decl1 << post_request3_body << pr_decl2 << post_request3_body << pr_decl3 << send_post_request << send_get_request << std::endl; |
226 | 12.0k | std::string pr3_str = post_request3.str(); |
227 | | |
228 | 12.0k | std::stringstream stream; |
229 | 12.0k | stream << script_header << pr1_str << pr2_str << pr3_str << script_footer << std::endl; |
230 | 12.0k | std::string js_code = stream.str(); |
231 | 12.0k | FuzzerFixtureHelper ffh; |
232 | 12.0k | EnvTest(ffh.isolate_, (char*)js_code.c_str()); |
233 | 12.0k | ffh.Teardown(); |
234 | 12.0k | return 0; |
235 | 12.0k | } |
236 | | |