Coverage Report

Created: 2025-10-10 06:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/civetweb/fuzztest/fuzzmain.c
Line
Count
Source
1
/********************************************************/
2
/*                                                      */
3
/*   FUZZ TEST for civetweb.c                           */
4
/*                                                      */
5
/*   Copyright (c) 2015-2020 the CivetWeb developers    */
6
/*                                                      */
7
/*   This file contains test code for fuzz tests.       */
8
/*   It should not be used in production code.          */
9
/*                                                      */
10
/********************************************************/
11
12
#include "civetweb.h"
13
#include <errno.h>
14
#include <stdint.h>
15
#include <stdlib.h>
16
#include <string.h>
17
18
#if defined(_WIN32)
19
#error "Currently not supported"
20
#else
21
22
#include <unistd.h>
23
4
#define test_sleep(x) (sleep(x))
24
#include <arpa/inet.h>
25
#include <netinet/in.h>
26
#include <netinet/ip.h>
27
#include <pthread.h>
28
#include <sys/socket.h>
29
#include <sys/types.h>
30
typedef int SOCKET;
31
#define closesocket(a) (close(a))
32
33
#endif // not _WIN32
34
35
36
/* Port configuration */
37
unsigned short PORT_NUM_HTTP = 0; /* set dynamically */
38
39
40
#define TESTabort()                                                            \
41
0
  {                                                                          \
42
0
    fprintf(stderr, "!!! Precondition in test environment not met !!!\n"); \
43
0
    fprintf(stderr, "!!! aborting fuzz test in line %u !!!", __LINE__);    \
44
0
    abort();                                                               \
45
0
  }
46
47
48
/********************************************************/
49
/* Init CivetWeb server ... test with mock client       */
50
/********************************************************/
51
#if defined(TEST_FUZZ1) || defined(TEST_FUZZ2)
52
53
static struct mg_context *ctx = 0;
54
static const char *OPTIONS[] = {"listening_ports",
55
                                "0", /* port: auto */
56
                                "document_root",
57
                                "fuzztest/docroot",
58
                                NULL,
59
                                NULL};
60
61
static void
62
civetweb_exit(void)
63
2
{
64
2
  printf("CivetWeb server exit\n");
65
2
  mg_stop(ctx);
66
2
  ctx = 0;
67
2
  test_sleep(5);
68
2
}
69
70
71
static void
72
civetweb_init(void)
73
2
{
74
2
  struct mg_callbacks callbacks;
75
2
  struct mg_server_port ports[8];
76
2
  memset(&callbacks, 0, sizeof(callbacks));
77
2
  memset(&ports, 0, sizeof(ports));
78
79
2
  ctx = mg_start(&callbacks, 0, OPTIONS);
80
81
2
  if (!ctx) {
82
0
    fprintf(stderr, "\nCivetWeb test server failed to start\n");
83
0
    TESTabort();
84
0
  }
85
86
2
  int ret = mg_get_server_ports(ctx, 8, ports);
87
2
  if (ret != 1) {
88
0
    fprintf(stderr,
89
0
            "\nCivetWeb test server: cannot determine port number\n");
90
0
    TESTabort();
91
0
  }
92
2
  if (ports[0].is_ssl != 0) {
93
0
    fprintf(stderr,
94
0
            "\nCivetWeb fuzz test works on HTTP, not HTTPS.\n"
95
0
            "TLS library should be fuzzed separately.\n");
96
0
    TESTabort();
97
0
  }
98
2
  PORT_NUM_HTTP = ports[0].port;
99
100
2
  printf("CivetWeb server running on port %i\n", (int)PORT_NUM_HTTP);
101
102
  /* Give server 5 seconds to start, before flooding with requests.
103
   * Don't know if this is required for fuzz-tests, but it was helpful
104
   * when testing starting/stopping the server multiple times in test
105
   * container environments. */
106
2
  test_sleep(5);
107
2
  atexit(civetweb_exit);
108
2
}
109
110
int LLVMFuzzerInitialize(int *argc, char ***argv);
111
112
int
113
LLVMFuzzerInitialize(int *argc, char ***argv)
114
2
{
115
  // Silence unused args warning.
116
2
  (void)(argc);
117
2
  (void)(argv);
118
119
2
  civetweb_init();
120
2
  return 0;
121
2
}
122
123
#if defined(TEST_FUZZ1)
124
static int
125
test_civetweb_client(const char *server,
126
                     uint16_t port,
127
                     int use_ssl,
128
                     const char *uri)
129
1
{
130
  /* Client var */
131
1
  struct mg_connection *client;
132
1
  char client_err_buf[256];
133
1
  char client_data_buf[4096];
134
1
  const struct mg_response_info *client_ri;
135
1
  int64_t data_read;
136
1
  int r;
137
138
1
  client = mg_connect_client(
139
1
      server, port, use_ssl, client_err_buf, sizeof(client_err_buf));
140
141
1
  if ((client == NULL) || (0 != strcmp(client_err_buf, ""))) {
142
0
    fprintf(stderr,
143
0
            "%s connection to server [%s] port [%u] failed: [%s]\n",
144
0
            use_ssl ? "HTTPS" : "HTTP",
145
0
            server,
146
0
            port,
147
0
            client_err_buf);
148
0
    if (client) {
149
0
      mg_close_connection(client);
150
0
    }
151
152
    /* In heavy fuzz testing, sometimes we run out of available sockets.
153
     * Wait for some seconds, and retry. */
154
0
    test_sleep(5);
155
156
    /* retry once */
157
0
    client = mg_connect_client(
158
0
        server, port, use_ssl, client_err_buf, sizeof(client_err_buf));
159
0
    if (!client) {
160
0
      fprintf(stderr, "Retry: error\n");
161
0
      return 1;
162
0
    }
163
0
    fprintf(stderr, "Retry: success\n");
164
0
  }
165
166
1
  mg_printf(client, "GET %s HTTP/1.0\r\n\r\n", uri);
167
168
1
  r = mg_get_response(client, client_err_buf, sizeof(client_err_buf), 10000);
169
170
1
  if ((r < 0) || (0 != strcmp(client_err_buf, ""))) {
171
0
    mg_close_connection(client);
172
0
    return 1;
173
0
  }
174
175
1
  client_ri = mg_get_response_info(client);
176
1
  if (client_ri == NULL) {
177
0
    mg_close_connection(client);
178
0
    return 1;
179
0
  }
180
181
1
  data_read = 0;
182
1
  while (data_read < client_ri->content_length) {
183
    /* store the first sizeof(client_data_buf) bytes
184
     * of the HTTP response. */
185
0
    r = mg_read(client,
186
0
                client_data_buf + data_read,
187
0
                sizeof(client_data_buf) - (size_t)data_read);
188
0
    if (r > 0) {
189
0
      data_read += r;
190
0
    }
191
192
    /* buffer filled? */
193
0
    if (sizeof(client_data_buf) == (size_t)data_read) {
194
      /* ignore the rest */
195
0
      while (r > 0) {
196
0
        char trash[1024];
197
0
        r = mg_read(client, trash, sizeof(trash));
198
0
      }
199
0
      break;
200
0
    }
201
0
  }
202
203
  /* Nothing left to read */
204
1
  r = mg_read(client, client_data_buf, sizeof(client_data_buf));
205
1
  if (r != 0) {
206
1
    mg_close_connection(client);
207
1
    return 1;
208
1
  }
209
210
0
  mg_close_connection(client);
211
0
  return 0;
212
1
}
213
214
static int
215
LLVMFuzzerTestOneInput_URI(const uint8_t *data, size_t size)
216
1
{
217
1
  static char URI[1024 * 64]; /* static, to avoid stack overflow */
218
219
1
  if (size + 1 < sizeof(URI)) {
220
1
    memcpy(URI, data, size);
221
1
    URI[size] = 0;
222
1
  } else {
223
0
    return 1;
224
0
  }
225
226
1
  return test_civetweb_client("127.0.0.1", PORT_NUM_HTTP, 0, URI);
227
1
}
228
#endif
229
230
231
#if defined(TEST_FUZZ2)
232
static int
233
LLVMFuzzerTestOneInput_REQUEST(const uint8_t *data, size_t size)
234
{
235
  int r;
236
  SOCKET sock = socket(AF_INET, SOCK_STREAM, 6);
237
  if (sock == -1) {
238
    r = errno;
239
    fprintf(stderr, "Error: Cannot create socket [%s]\n", strerror(r));
240
    return 1;
241
  }
242
  struct sockaddr_in sin;
243
  memset(&sin, 0, sizeof(sin));
244
  sin.sin_family = AF_INET;
245
  sin.sin_addr.s_addr = inet_addr("127.0.0.1");
246
  sin.sin_port = htons(PORT_NUM_HTTP);
247
  r = connect(sock, (struct sockaddr *)&sin, sizeof(sin));
248
  if (r != 0) {
249
    r = errno;
250
    fprintf(stderr, "Error: Cannot connect [%s]\n", strerror(r));
251
    closesocket(sock);
252
    return 1;
253
  }
254
255
  char trash[1024];
256
  r = send(sock, data, size, 0);
257
  if (r != (int)size) {
258
    fprintf(stderr, "Warning: %i bytes sent (TODO: Repeat)\n", r);
259
  }
260
261
  int data_read = 0;
262
  while ((r = recv(sock, trash, sizeof(trash), 0)) > 0) {
263
    data_read += r;
264
  };
265
266
  shutdown(sock, SHUT_RDWR);
267
  closesocket(sock);
268
269
  static int max_data_read = 0;
270
  if (data_read > max_data_read) {
271
    max_data_read = data_read;
272
    printf("GOT data: %i\n", data_read);
273
  }
274
  return 0;
275
}
276
#endif
277
278
#endif // defined(TEST_FUZZ1) || defined(TEST_FUZZ2)
279
280
281
/********************************************************/
282
/* Init mock server ... test with CivetWeb client       */
283
/********************************************************/
284
#if defined(TEST_FUZZ3)
285
286
struct tcp_func_prm {
287
  SOCKET sock;
288
};
289
290
struct tRESPONSE {
291
  char data[4096];
292
  size_t size;
293
} RESPONSE;
294
295
296
volatile int mock_server_stop_flag = 0;
297
298
static void
299
mock_server_exit(void)
300
{
301
  printf("MOCK server exit\n");
302
  mock_server_stop_flag = 1;
303
  test_sleep(5);
304
}
305
306
307
static void *
308
mock_server_thread(void *arg)
309
{
310
  char req[1024 * 16];
311
  SOCKET svr = (SOCKET)(-1);
312
313
  /* Get thread parameters and free arg */
314
  {
315
    struct tcp_func_prm *ptcp_func_prm = (struct tcp_func_prm *)arg;
316
    svr = ptcp_func_prm->sock;
317
    free(arg);
318
  }
319
320
  mock_server_stop_flag = 0;
321
  printf("MOCK server ready, sock %i\n", svr);
322
323
next_request:
324
  while (!mock_server_stop_flag) {
325
    struct sockaddr_in cliadr;
326
    socklen_t adrlen = sizeof(cliadr);
327
    int buf_filled = 0;
328
    int req_ready = 0;
329
330
    memset(&cliadr, 0, sizeof(cliadr));
331
332
    SOCKET cli = accept(svr, (struct sockaddr *)&cliadr, &adrlen);
333
334
    if (cli == -1) {
335
      int er = errno;
336
      fprintf(stderr, "Error: Accept failed [%s]\n", strerror(er));
337
      test_sleep(1);
338
      goto next_request;
339
    }
340
341
    /* Read request */
342
    do {
343
      int r =
344
          recv(cli, req + buf_filled, sizeof(req) - buf_filled - 1, 0);
345
      if (r > 0) {
346
        buf_filled += r;
347
        req[buf_filled] = 0;
348
        if (strstr(req, "\r\n\r\n") != NULL) {
349
          req_ready = 1;
350
        }
351
      } else {
352
        /* some error */
353
        int er = errno;
354
        fprintf(stderr, "Error: Recv failed [%s]\n", strerror(er));
355
        test_sleep(1);
356
        goto next_request;
357
      }
358
    } while (!req_ready);
359
360
    /* Request is complete here.
361
     * Now send response */
362
    send(cli, RESPONSE.data, RESPONSE.size, MSG_NOSIGNAL);
363
364
    /* Close connection. */
365
    shutdown(cli, SHUT_RDWR);
366
    closesocket(cli);
367
  }
368
  return 0;
369
}
370
371
372
static void
373
mock_server_init(void)
374
{
375
  int r;
376
  int bind_success = 0;
377
  SOCKET sock = socket(AF_INET, SOCK_STREAM, 6);
378
  if (sock == -1) {
379
    r = errno;
380
    fprintf(stderr, "Error: Cannot create socket [%s]\n", strerror(r));
381
    TESTabort();
382
  }
383
384
  for (PORT_NUM_HTTP = 1024; PORT_NUM_HTTP != 0; PORT_NUM_HTTP++) {
385
    struct sockaddr_in sin;
386
    memset(&sin, 0, sizeof(sin));
387
    sin.sin_family = AF_INET;
388
    sin.sin_addr.s_addr = inet_addr("127.0.0.1");
389
    sin.sin_port = htons(PORT_NUM_HTTP);
390
    r = bind(sock, (struct sockaddr *)&sin, sizeof(sin));
391
    if (r == 0) {
392
      bind_success = 1;
393
      break;
394
    }
395
    r = errno;
396
    fprintf(stderr, "Warning: Cannot bind [%s]\n", strerror(r));
397
  }
398
399
  if (!bind_success) {
400
    fprintf(stderr, "Error: Cannot bind to any port\n");
401
    closesocket(sock);
402
    TESTabort();
403
  }
404
405
  printf("MOCK server running on port %i\n", (int)PORT_NUM_HTTP);
406
407
  r = listen(sock, 128);
408
  if (r != 0) {
409
    r = errno;
410
    fprintf(stderr, "Error: Cannot listen [%s]\n", strerror(r));
411
    closesocket(sock);
412
    TESTabort();
413
  }
414
415
  pthread_t thread_id;
416
  pthread_attr_t attr;
417
  int result;
418
  struct tcp_func_prm *thread_prm;
419
420
  thread_prm = (struct tcp_func_prm *)malloc(sizeof(struct tcp_func_prm));
421
  if (!thread_prm) {
422
    fprintf(stderr, "Error: Out of memory\n");
423
    closesocket(sock);
424
    TESTabort();
425
  }
426
  thread_prm->sock = sock;
427
428
  (void)pthread_attr_init(&attr);
429
  (void)pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
430
  result = pthread_create(&thread_id,
431
                          &attr,
432
                          mock_server_thread,
433
                          (void *)thread_prm);
434
  (void)pthread_attr_destroy(&attr);
435
  if (result != 0) {
436
    r = errno;
437
    fprintf(stderr, "Error: Cannot create thread [%s]\n", strerror(r));
438
    closesocket(sock);
439
    TESTabort();
440
  }
441
442
  test_sleep(3);
443
  atexit(mock_server_exit);
444
}
445
446
int LLVMFuzzerInitialize(int *argc, char ***argv);
447
448
int
449
LLVMFuzzerInitialize(int *argc, char ***argv)
450
{
451
  // Silence unused args warning.
452
  (void)(argc);
453
  (void)(argv);
454
455
  mock_server_init();
456
  return 0;
457
}
458
459
460
static int
461
LLVMFuzzerTestOneInput_RESPONSE(const uint8_t *data, size_t size)
462
{
463
  if (size > sizeof(RESPONSE.data)) {
464
    return 1;
465
  }
466
467
  memcpy(RESPONSE.data, data, size);
468
  RESPONSE.size = size;
469
470
  char errbuf[256];
471
472
  struct mg_connection *conn = mg_connect_client(
473
      "127.0.0.1", PORT_NUM_HTTP, 0, errbuf, sizeof(errbuf));
474
  if (!conn) {
475
    printf("Connect error: %s\n", errbuf);
476
    test_sleep(1);
477
    return 1;
478
  }
479
  mg_printf(conn, "GET / HTTP/1.0\r\n\r\n");
480
481
  int r = mg_get_response(conn, errbuf, sizeof(errbuf), 1000);
482
  const struct mg_response_info *ri = mg_get_response_info(conn);
483
484
  (void)r;
485
  (void)ri;
486
487
  mg_close_connection(conn);
488
489
  return 0;
490
}
491
492
#endif // defined(TEST_FUZZ3)
493
494
/********************************************************/
495
/* MAIN for fuzztest                                    */
496
/********************************************************/
497
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
498
// warning: no previous prototype for function 'LLVMFuzzerTestOneInput'
499
500
int
501
LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
502
1
{
503
1
#if defined(TEST_FUZZ1)
504
  /* fuzz target 1: different URI for HTTP/1 server */
505
1
  LLVMFuzzerTestOneInput_URI(data, size);
506
1
  return 0;
507
#elif defined(TEST_FUZZ2)
508
  /* fuzz target 2: different requests for HTTP/1 server */
509
  LLVMFuzzerTestOneInput_REQUEST(data, size);
510
  return 0;
511
#elif defined(TEST_FUZZ3)
512
  /* fuzz target 3: different responses for HTTP/1 client */
513
  LLVMFuzzerTestOneInput_RESPONSE(data, size);
514
  return 0;
515
#elif defined(TEST_FUZZ4)
516
#error "Only useful in HTTP/2 feature branch"
517
  /* fuzz target 4: different requests for HTTP/2 server */
518
  LLVMFuzzerTestOneInput_REQUEST_HTTP2(data, size);
519
  return 0;
520
#elif defined(TEST_FUZZ5)
521
  /* fuzz target 5: calling an internal server test function,
522
   *                bypassing network sockets */
523
  LLVMFuzzerTestOneInput_process_new_connection(data, size);
524
  return 0;
525
#else
526
/* planned targets */
527
#error "Unknown fuzz target"
528
#endif
529
1
}