/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 | } |