Coverage Report

Created: 2025-12-31 08:15

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wget/fuzz/wget_read_hunk_fuzzer.c
Line
Count
Source
1
/*
2
 * Copyright (c) 2019-2024 Free Software Foundation, Inc.
3
 *
4
 * This file is part of GNU Wget.
5
 *
6
 * GNU Wget is free software; you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation; either version 3 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * GNU Wget is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with Wget.  If not, see <https://www.gnu.org/licenses/>.
18
 */
19
20
#include <config.h>
21
22
#include <sys/types.h>
23
#include <stdint.h> // uint8_t
24
#include <stdio.h>  // fmemopen
25
#include <string.h>  // strncmp
26
#include <stdlib.h>  // free
27
#include <unistd.h>  // close
28
#include <fcntl.h>  // open flags
29
#include <unistd.h>  // close
30
#include <unistd.h>  // close
31
32
#include "wget.h"
33
#include "connect.h"
34
#undef fopen_wgetrc
35
36
#ifdef __cplusplus
37
  extern "C" {
38
#endif
39
  #include "retr.h"
40
41
  // declarations for wget internal functions
42
  int main_wget(int argc, const char **argv);
43
  void cleanup(void);
44
//  FILE *fopen_wget(const char *pathname, const char *mode);
45
//  FILE *fopen_wgetrc(const char *pathname, const char *mode);
46
  void exit_wget(int status);
47
#ifdef __cplusplus
48
  }
49
#endif
50
51
#include "fuzzer.h"
52
53
FILE *fopen_wget(const char *pathname, const char *mode)
54
0
{
55
0
  return fopen("/dev/null", mode);
56
0
}
57
58
FILE *fopen_wgetrc(const char *pathname, const char *mode)
59
0
{
60
0
  return NULL;
61
0
}
62
63
#ifdef FUZZING
64
void exit_wget(int status)
65
0
{
66
0
}
67
#endif
68
69
static const uint8_t *g_data;
70
static size_t g_size, g_read;
71
72
struct my_context {
73
  int peeklen;
74
  char peekbuf[512];
75
};
76
77
static int my_peek (int fd _GL_UNUSED, char *buf, int bufsize, void *arg, double d)
78
1.78k
{
79
1.78k
  (void) d;
80
1.78k
  if (g_read < g_size) {
81
1.78k
    struct my_context *ctx = (struct my_context *) arg;
82
1.78k
    int n = rand() % (g_size - g_read);
83
1.78k
    if (n > bufsize)
84
198
      n = bufsize;
85
1.78k
    if (n > (int) sizeof(ctx->peekbuf))
86
38
      n = sizeof(ctx->peekbuf);
87
1.78k
    memcpy(buf, g_data + g_read, n);
88
1.78k
    memcpy(ctx->peekbuf, g_data + g_read, n);
89
1.78k
    g_read += n;
90
1.78k
    ctx->peeklen=n;
91
1.78k
    return n;
92
1.78k
  }
93
0
  return 0;
94
1.78k
}
95
static int my_read (int fd _GL_UNUSED, char *buf, int bufsize, void *arg, double d)
96
1.39k
{
97
1.39k
  (void) d;
98
1.39k
  struct my_context *ctx = (struct my_context *) arg;
99
100
1.39k
  if (ctx->peeklen) {
101
      /* If we have any peek data, simply return that. */
102
960
      int copysize = MIN (bufsize, ctx->peeklen);
103
960
      memcpy (buf, ctx->peekbuf, copysize);
104
960
      ctx->peeklen -= copysize;
105
960
      if (ctx->peeklen)
106
90
        memmove (ctx->peekbuf, ctx->peekbuf + copysize, ctx->peeklen);
107
108
960
      return copysize;
109
960
  }
110
111
433
  if (g_read < g_size) {
112
433
    int n = rand() % (g_size - g_read);
113
433
    if (n > bufsize)
114
136
      n = bufsize;
115
433
    memcpy(buf, g_data + g_read, n);
116
433
    g_read += n;
117
433
    return n;
118
433
  }
119
120
0
  return 0;
121
433
}
122
static int my_write (int fd _GL_UNUSED, char *buf _GL_UNUSED, int bufsize, void *arg _GL_UNUSED)
123
0
{
124
0
  return bufsize;
125
0
}
126
static int my_poll (int fd _GL_UNUSED, double timeout _GL_UNUSED, int wait_for _GL_UNUSED, void *arg)
127
0
{
128
0
  struct my_context *ctx = (struct my_context *) arg;
129
130
0
   return ctx->peeklen || g_read < g_size;
131
0
}
132
static const char *my_errstr (int fd _GL_UNUSED, void *arg _GL_UNUSED)
133
0
{
134
0
  return "Success";
135
0
}
136
static void my_close (int fd _GL_UNUSED, void *arg _GL_UNUSED)
137
0
{
138
0
}
139
140
static struct transport_implementation my_transport =
141
{
142
  my_read, my_write, my_poll,
143
  my_peek, my_errstr, my_close
144
};
145
146
/* copied from wget's http.c */
147
static const char *
148
response_head_terminator (const char *start, const char *peeked, int peeklen)
149
1.78k
{
150
1.78k
  const char *p, *end;
151
152
  /* If at first peek, verify whether HUNK starts with "HTTP".  If
153
     not, this is a HTTP/0.9 request and we must bail out without
154
     reading anything.  */
155
1.78k
  if (start == peeked && 0 != memcmp (start, "HTTP", MIN (peeklen, 4)))
156
390
    return start;
157
158
  /* Look for "\n[\r]\n", and return the following position if found.
159
     Start two chars before the current to cover the possibility that
160
     part of the terminator (e.g. "\n\r") arrived in the previous
161
     batch.  */
162
1.39k
  p = peeked - start < 2 ? start : peeked - 2;
163
1.39k
  end = peeked + peeklen;
164
165
  /* Check for \n\r\n or \n\n anywhere in [p, end-2). */
166
129k
  for (; p < end - 2; p++)
167
128k
    if (*p == '\n')
168
7.13k
      {
169
7.13k
        if (p[1] == '\r' && p[2] == '\n')
170
40
          return p + 3;
171
7.09k
        else if (p[1] == '\n')
172
56
          return p + 2;
173
7.13k
      }
174
  /* p==end-2: check for \n\n directly preceding END. */
175
1.29k
  if (peeklen >= 2 && p[0] == '\n' && p[1] == '\n')
176
42
    return p + 2;
177
178
1.25k
  return NULL;
179
1.29k
}
180
181
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
182
302
{
183
302
  char *hunk;
184
185
302
  if (size > 4096) // same as max_len = ... in .options file
186
10
    return 0;
187
188
//  CLOSE_STDERR
189
190
292
  g_data = data;
191
292
  g_size = size;
192
292
  g_read = 0;
193
194
292
  struct my_context *ctx = (struct my_context *) calloc(1, sizeof(struct my_context));
195
292
  fd_register_transport(99, &my_transport, ctx);
196
197
961
  while ((hunk = fd_read_hunk(99, response_head_terminator, 512, 65536)))
198
669
    free(hunk);
199
200
292
   connect_cleanup();
201
292
  free(ctx);
202
203
//  RESTORE_STDERR
204
205
292
  return 0;
206
302
}