Files
wget2/fuzz/libwget_http_client_fuzzer.c
2024-01-07 12:41:02 +01:00

227 lines
6.2 KiB
C

/*
* Copyright (c) 2017-2024 Free Software Foundation, Inc.
*
* This file is part of libwget.
*
* Libwget is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Libwget is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with libwget. If not, see <https://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <sys/types.h>
#include <dirent.h> // opendir, readdir
#include <stdint.h> // uint8_t
#include <stdio.h> // fmemopen
#include <string.h> // strncmp
#include "wget.h"
#include "fuzzer.h"
static uint8_t *g_data;
static size_t g_size;
static bool fuzzing;
static int mode;
static int connect_fd;
#if defined HAVE_DLFCN_H && defined HAVE_FMEMOPEN
#include <dlfcn.h>
#include <sys/socket.h>
#include <netdb.h>
#if defined __OpenBSD__ || defined __FreeBSD__
#include <netinet/in.h>
#endif
#ifdef RTLD_NEXT /* Not defined e.g. on CygWin */
struct combined {
struct addrinfo ai;
struct sockaddr_in in_addr;
};
int getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res)
{
if (fuzzing) {
// glibc 2.24 does no extra free() for in_addr
struct combined aic = {
.in_addr.sin_family = AF_INET,
.in_addr.sin_port = 80,
.ai.ai_flags = 0,
.ai.ai_family = AF_INET,
.ai.ai_socktype = SOCK_STREAM,
.ai.ai_protocol = 0,
.ai.ai_canonname = NULL,
.ai.ai_next = NULL,
.ai.ai_addrlen = sizeof(struct sockaddr_in),
};
aic.ai.ai_addr = (struct sockaddr *) &aic.in_addr;
*res = (struct addrinfo *) wget_memdup(&aic, sizeof(aic));
return 0;
}
int(*libc_getaddrinfo)(const char *, const char *, const struct addrinfo *, struct addrinfo **) =
(int(*)(const char *, const char *, const struct addrinfo *, struct addrinfo **)) dlsym (RTLD_NEXT, "getaddrinfo");
return libc_getaddrinfo(node, service, hints, res);
}
void freeaddrinfo(struct addrinfo *res)
{
struct addrinfo *ai, *cur;
if (fuzzing) {
ai = res;
while (ai) {
cur = ai;
ai = ai->ai_next;
wget_free(cur);
}
return;
}
void (*libc_freeaddrinfo)(struct addrinfo *res) =
(void(*)(struct addrinfo *res)) dlsym (RTLD_NEXT, "getaddrinfo");
libc_freeaddrinfo(res);
}
#if defined __OpenBSD__ || defined __FreeBSD__
int getnameinfo(const struct sockaddr *addr, socklen_t addrlen, char *host, size_t hostlen, char *serv, size_t servlen, int flags)
#else
int getnameinfo(const struct sockaddr *addr, socklen_t addrlen, char *host, socklen_t hostlen, char *serv, socklen_t servlen, int flags)
#endif
{
if (fuzzing)
return -1;
int(*libc_getnameinfo)(const struct sockaddr *, socklen_t, char *, socklen_t, char *, socklen_t, int) =
(int(*)(const struct sockaddr *, socklen_t, char *, socklen_t, char *, socklen_t, int)) dlsym (RTLD_NEXT, "getnameinfo");
return libc_getnameinfo(addr, addrlen, host, hostlen, serv, servlen, flags);
}
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
{
if (connect_fd == -1) {
connect_fd = sockfd;
return 0;
}
int(*libc_connect)(int, const struct sockaddr *, socklen_t) =
(int(*)(int, const struct sockaddr *, socklen_t)) dlsym (RTLD_NEXT, "connect");
return libc_connect(sockfd, addr, addrlen);
}
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen)
{
if (sockfd == connect_fd) {
if (mode == 1 && len > 1) {
len = 1;
}
if (len > g_size)
len = g_size;
memcpy(buf, g_data, len);
g_size -= len;
return len;
}
int(*libc_recvfrom)(int, void *, size_t, int, struct sockaddr *, socklen_t *) =
(int(*)(int, void *, size_t, int, struct sockaddr *, socklen_t *)) dlsym (RTLD_NEXT, "recvfrom");
return libc_recvfrom(sockfd, buf, len, flags, src_addr, addrlen);
}
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen)
{
if (connect_fd == -1) {
connect_fd = sockfd;
return (ssize_t) len;
}
int(*libc_sendto)(int, const void *, size_t, int, const struct sockaddr *, socklen_t) =
(int(*)(int, const void *, size_t, int, const struct sockaddr *, socklen_t)) dlsym (RTLD_NEXT, "sendto");
return libc_sendto(sockfd, buf, len, flags, dest_addr, addrlen);
}
ssize_t send(int sockfd, const void *buf, size_t len, int flags)
{
if (sockfd == connect_fd)
return (ssize_t) len;
int(*libc_send)(int, const void *, size_t, int) =
(int(*)(int, const void *, size_t, int)) dlsym (RTLD_NEXT, "sendto");
return libc_send(sockfd, buf, len, flags);
}
#endif
#endif
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
{
static const char *header[3] = {
"HTTP/1.1 200 OK\r\n"
"Host: a\r\n"
"Transfer-Encoding: chunked\r\n"
"Connection: keep-alive\r\n\r\n"
,
"HTTP/1.1 200 OK\r\n"
"Host: a\r\n"
"Content-Length: 10\r\n"
"Connection: keep-alive\r\n\r\n"
,
("HTTP/1.1 200 OK\r\n"
"Host: a\r\n\r\n")
};
if (size > 256) // same as max_len = 4096 in .options file
return 0;
fuzzing = 1;
wget_iri *uri = wget_iri_parse("http://example.com", NULL);
wget_tcp_set_timeout(NULL, 0); // avoid to call select or poll
wget_tcp_set_connect_timeout(NULL, 0); // avoid to call select or poll
for (mode = 0; mode < 2; mode++) {
for (int type = 0; type < 3; type++) {
size_t hlen = strlen(header[type]);
g_size = hlen + size;
g_data = (uint8_t *) malloc(g_size);
memcpy(g_data, header[type], hlen);
memcpy(g_data + hlen, data, size);
connect_fd = -1;
wget_http_request *req = wget_http_create_request(uri, "GET");
wget_http_connection *conn = NULL;
// wget_http_add_header(req, "User-Agent", "TheUserAgent/0.5");
if (wget_http_open(&conn, uri) == WGET_E_SUCCESS) {
if (wget_http_send_request(conn, req) == WGET_E_SUCCESS) {
wget_http_response *resp = wget_http_get_response(conn);
wget_http_free_response(&resp);
}
wget_http_close(&conn);
}
wget_http_free_request(&req);
free(g_data);
}
}
wget_iri_free(&uri);
fuzzing = 0;
return 0;
}