Coverage Report

Created: 2025-07-04 09:33

/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(&current_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, &current_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(), &current_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