Coverage Report

Created: 2025-03-18 06:55

/src/wget2/fuzz/libwget_http_client_fuzzer.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (c) 2017-2024 Free Software Foundation, Inc.
3
 *
4
 * This file is part of libwget.
5
 *
6
 * Libwget is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU Lesser 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
 * Libwget 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 Lesser General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Lesser General Public License
17
 * along with libwget.  If not, see <https://www.gnu.org/licenses/>.
18
 */
19
20
#include <config.h>
21
22
#include <sys/types.h>
23
#include <dirent.h> // opendir, readdir
24
#include <stdint.h> // uint8_t
25
#include <stdio.h>  // fmemopen
26
#include <string.h>  // strncmp
27
28
#include "wget.h"
29
#include "fuzzer.h"
30
31
static uint8_t *g_data;
32
static size_t g_size;
33
static bool fuzzing;
34
static int mode;
35
static int connect_fd;
36
37
#if defined HAVE_DLFCN_H && defined HAVE_FMEMOPEN
38
#include <dlfcn.h>
39
#include <sys/socket.h>
40
#include <netdb.h>
41
#if defined __OpenBSD__ || defined __FreeBSD__
42
#include <netinet/in.h>
43
#endif
44
#ifdef RTLD_NEXT /* Not defined e.g. on CygWin */
45
struct combined {
46
  struct addrinfo ai;
47
  struct sockaddr_in in_addr;
48
};
49
int getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res)
50
7.68k
{
51
7.68k
  if (fuzzing) {
52
    // glibc 2.24 does no extra free() for in_addr
53
7.68k
    struct combined aic = {
54
7.68k
      .in_addr.sin_family = AF_INET,
55
7.68k
      .in_addr.sin_port = 80,
56
7.68k
      .ai.ai_flags = 0,
57
7.68k
      .ai.ai_family = AF_INET,
58
7.68k
      .ai.ai_socktype = SOCK_STREAM,
59
7.68k
      .ai.ai_protocol = 0,
60
7.68k
      .ai.ai_canonname = NULL,
61
7.68k
      .ai.ai_next = NULL,
62
7.68k
      .ai.ai_addrlen = sizeof(struct sockaddr_in),
63
7.68k
    };
64
65
7.68k
    aic.ai.ai_addr = (struct sockaddr *) &aic.in_addr;
66
7.68k
    *res = (struct addrinfo *) wget_memdup(&aic, sizeof(aic));
67
7.68k
    return 0;
68
7.68k
  }
69
70
0
  int(*libc_getaddrinfo)(const char *, const char *, const struct addrinfo *, struct addrinfo **) =
71
0
    (int(*)(const char *, const char *, const struct addrinfo *, struct addrinfo **)) dlsym (RTLD_NEXT, "getaddrinfo");
72
73
0
  return libc_getaddrinfo(node, service, hints, res);
74
7.68k
}
75
void freeaddrinfo(struct addrinfo *res)
76
3.84k
{
77
3.84k
  struct addrinfo *ai, *cur;
78
3.84k
  if (fuzzing) {
79
3.84k
    ai = res;
80
11.5k
    while (ai) {
81
7.68k
      cur = ai;
82
7.68k
      ai = ai->ai_next;
83
7.68k
      wget_free(cur);
84
7.68k
    }
85
3.84k
    return;
86
3.84k
  }
87
88
0
  void (*libc_freeaddrinfo)(struct addrinfo *res) =
89
0
    (void(*)(struct addrinfo *res)) dlsym (RTLD_NEXT, "getaddrinfo");
90
91
0
  libc_freeaddrinfo(res);
92
0
}
93
94
#if defined __OpenBSD__ || defined __FreeBSD__
95
int getnameinfo(const struct sockaddr *addr, socklen_t addrlen, char *host, size_t hostlen, char *serv, size_t servlen, int flags)
96
#else
97
int getnameinfo(const struct sockaddr *addr, socklen_t addrlen, char *host, socklen_t hostlen, char *serv, socklen_t servlen, int flags)
98
#endif
99
3.84k
{
100
3.84k
  if (fuzzing)
101
3.84k
    return -1;
102
103
0
  int(*libc_getnameinfo)(const struct sockaddr *, socklen_t, char *, socklen_t, char *, socklen_t, int) =
104
0
    (int(*)(const struct sockaddr *, socklen_t, char *, socklen_t, char *, socklen_t, int)) dlsym (RTLD_NEXT, "getnameinfo");
105
106
0
  return libc_getnameinfo(addr, addrlen, host, hostlen, serv, servlen, flags);
107
3.84k
}
108
109
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
110
3.84k
{
111
3.84k
  if (connect_fd == -1) {
112
3.84k
    connect_fd = sockfd;
113
3.84k
    return 0;
114
3.84k
  }
115
116
0
  int(*libc_connect)(int, const struct sockaddr *, socklen_t) =
117
0
    (int(*)(int, const struct sockaddr *, socklen_t)) dlsym (RTLD_NEXT, "connect");
118
119
0
  return libc_connect(sockfd, addr, addrlen);
120
3.84k
}
121
122
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen)
123
170k
{
124
170k
  if (sockfd == connect_fd) {
125
170k
    if (mode == 1 && len > 1) {
126
166k
      len = 1;
127
166k
    }
128
129
170k
    if (len > g_size)
130
5.06k
      len = g_size;
131
170k
    memcpy(buf, g_data, len);
132
170k
    g_size -= len;
133
170k
    return len;
134
170k
  }
135
136
0
  int(*libc_recvfrom)(int, void *, size_t, int, struct sockaddr *, socklen_t *) =
137
0
    (int(*)(int, void *, size_t, int, struct sockaddr *, socklen_t *)) dlsym (RTLD_NEXT, "recvfrom");
138
139
0
  return libc_recvfrom(sockfd, buf, len, flags, src_addr, addrlen);
140
170k
}
141
142
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen)
143
0
{
144
0
  if (connect_fd == -1) {
145
0
    connect_fd = sockfd;
146
0
    return (ssize_t) len;
147
0
  }
148
149
0
  int(*libc_sendto)(int, const void *, size_t, int, const struct sockaddr *, socklen_t) =
150
0
    (int(*)(int, const void *, size_t, int, const struct sockaddr *, socklen_t)) dlsym (RTLD_NEXT, "sendto");
151
152
0
  return libc_sendto(sockfd, buf, len, flags, dest_addr, addrlen);
153
0
}
154
155
ssize_t send(int sockfd, const void *buf, size_t len, int flags)
156
3.84k
{
157
3.84k
  if (sockfd == connect_fd)
158
3.84k
    return (ssize_t) len;
159
160
0
  int(*libc_send)(int, const void *, size_t, int) =
161
0
    (int(*)(int, const void *, size_t, int)) dlsym (RTLD_NEXT, "sendto");
162
163
0
  return libc_send(sockfd, buf, len, flags);
164
3.84k
}
165
#endif
166
#endif
167
168
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
169
654
{
170
654
  static const char *header[3] = {
171
654
    "HTTP/1.1 200 OK\r\n"
172
654
    "Host: a\r\n"
173
654
    "Transfer-Encoding: chunked\r\n"
174
654
    "Connection: keep-alive\r\n\r\n"
175
654
    ,
176
654
    "HTTP/1.1 200 OK\r\n"
177
654
    "Host: a\r\n"
178
654
    "Content-Length: 10\r\n"
179
654
    "Connection: keep-alive\r\n\r\n"
180
654
    ,
181
654
    ("HTTP/1.1 200 OK\r\n"
182
654
    "Host: a\r\n\r\n")
183
654
  };
184
185
654
  if (size > 256) // same as max_len = 4096 in .options file
186
14
    return 0;
187
188
640
  fuzzing = 1;
189
640
  wget_iri *uri = wget_iri_parse("http://example.com", NULL);
190
640
  wget_tcp_set_timeout(NULL, 0); // avoid to call select or poll
191
640
  wget_tcp_set_connect_timeout(NULL, 0); // avoid to call select or poll
192
193
1.92k
  for (mode = 0; mode < 2; mode++) {
194
5.12k
    for (int type = 0; type < 3; type++) {
195
3.84k
      size_t hlen = strlen(header[type]);
196
197
3.84k
      g_size = hlen + size;
198
3.84k
      g_data = (uint8_t *) malloc(g_size);
199
3.84k
      memcpy(g_data, header[type], hlen);
200
3.84k
      memcpy(g_data + hlen, data, size);
201
202
3.84k
      connect_fd = -1;
203
204
3.84k
      wget_http_request *req = wget_http_create_request(uri, "GET");
205
3.84k
      wget_http_connection *conn = NULL;
206
207
      // wget_http_add_header(req, "User-Agent", "TheUserAgent/0.5");
208
209
3.84k
      if (wget_http_open(&conn, uri) == WGET_E_SUCCESS) {
210
3.84k
        if (wget_http_send_request(conn, req) == WGET_E_SUCCESS) {
211
3.84k
          wget_http_response *resp = wget_http_get_response(conn);
212
3.84k
          wget_http_free_response(&resp);
213
3.84k
        }
214
3.84k
        wget_http_close(&conn);
215
3.84k
      }
216
217
3.84k
      wget_http_free_request(&req);
218
3.84k
      free(g_data);
219
3.84k
    }
220
1.28k
  }
221
222
640
  wget_iri_free(&uri);
223
640
  fuzzing = 0;
224
225
640
  return 0;
226
654
}