mirror of https://github.com/google/oss-fuzz.git
580 lines
15 KiB
C
580 lines
15 KiB
C
/* Copyright 2021 Google LLC
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
#include "dnsmasq.h"
|
|
|
|
extern void fuzz_blockdata_cleanup();
|
|
|
|
// Simple garbage collector
|
|
#define GB_SIZE 100
|
|
|
|
void *pointer_arr[GB_SIZE];
|
|
static int pointer_idx = 0;
|
|
|
|
// If the garbage collector is used then this must be called as first thing
|
|
// during a fuzz run.
|
|
void gb_init() {
|
|
pointer_idx = 0;
|
|
|
|
for (int i = 0; i < GB_SIZE; i++) {
|
|
pointer_arr[i] = NULL;
|
|
}
|
|
}
|
|
|
|
void gb_cleanup() {
|
|
for(int i = 0; i < GB_SIZE; i++) {
|
|
if (pointer_arr[i] != NULL) {
|
|
free(pointer_arr[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
char *get_null_terminated(const uint8_t **data, size_t *size) {
|
|
#define STR_SIZE 75
|
|
if (*size < STR_SIZE || (int)*size < 0) {
|
|
return NULL;
|
|
}
|
|
|
|
char *new_s = malloc(STR_SIZE + 1);
|
|
memcpy(new_s, *data, STR_SIZE);
|
|
new_s[STR_SIZE] = '\0';
|
|
|
|
*data = *data+STR_SIZE;
|
|
*size -= STR_SIZE;
|
|
return new_s;
|
|
}
|
|
|
|
char *gb_get_random_data(const uint8_t **data, size_t *size, size_t to_get) {
|
|
if (*size < to_get || (int)*size < 0) {
|
|
return NULL;
|
|
}
|
|
|
|
char *new_s = malloc(to_get);
|
|
memcpy(new_s, *data, to_get);
|
|
|
|
pointer_arr[pointer_idx++] = (void*)new_s;
|
|
|
|
*data = *data + to_get;
|
|
*size -= to_get;
|
|
|
|
return new_s;
|
|
}
|
|
|
|
char *gb_get_null_terminated(const uint8_t **data, size_t *size) {
|
|
|
|
char *nstr = get_null_terminated(data, size);
|
|
if (nstr == NULL) {
|
|
return NULL;
|
|
}
|
|
pointer_arr[pointer_idx++] = (void*)nstr;
|
|
return nstr;
|
|
}
|
|
|
|
char *gb_alloc_data(size_t len) {
|
|
char *ptr = calloc(1, len);
|
|
pointer_arr[pointer_idx++] = (void*)ptr;
|
|
|
|
return ptr;
|
|
}
|
|
|
|
short get_short(const uint8_t **data, size_t *size) {
|
|
if (*size <= 0) return 0;
|
|
short c = (short)(*data)[0];
|
|
*data += 1;
|
|
*size-=1;
|
|
return c;
|
|
}
|
|
|
|
int get_int(const uint8_t **data, size_t *size) {
|
|
if (*size <= 4) return 0;
|
|
const uint8_t *ptr = *data;
|
|
int val = *((int*)ptr);
|
|
*data += 4;
|
|
*size -= 4;
|
|
return val;
|
|
}
|
|
// end simple garbage collector.
|
|
|
|
const uint8_t *syscall_data = NULL;
|
|
size_t syscall_size = 0;
|
|
|
|
|
|
int fuzz_ioctl(int fd, unsigned long request, void *arg) {
|
|
int fd2 = fd;
|
|
unsigned long request2 = request;
|
|
void *arg_ptr = arg;
|
|
|
|
// SIOCGSTAMP
|
|
if (request == SIOCGSTAMP) {
|
|
struct timeval *tv = (struct timeval*)arg_ptr;
|
|
if (tv == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
char *rand_tv = gb_get_random_data(&syscall_data, &syscall_size, sizeof(struct timeval));
|
|
if (rand_tv == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
memcpy(tv, rand_tv, sizeof(struct timeval));
|
|
return 0;
|
|
}
|
|
|
|
if (request == SIOCGIFNAME) {
|
|
//printf("We got a SIOCGIFNAME\n");
|
|
struct ifreq *ifr = (struct ifreq*)arg_ptr;
|
|
if (ifr == NULL) {
|
|
return -1;
|
|
}
|
|
for (int i = 0; i < IF_NAMESIZE; i++) {
|
|
if (syscall_size > 0 && syscall_data != NULL) {
|
|
ifr->ifr_name[i] = (char)*syscall_data;
|
|
syscall_data += 1;
|
|
syscall_size -= 1;
|
|
}
|
|
else {
|
|
ifr->ifr_name[i] = 'A';
|
|
}
|
|
}
|
|
ifr->ifr_name[IF_NAMESIZE-1] = '\0';
|
|
return 0;
|
|
//return -1;
|
|
}
|
|
if (request == SIOCGIFFLAGS) {
|
|
return 0;
|
|
}
|
|
if (request == SIOCGIFADDR) {
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
int retval = ioctl(fd2, request2, arg_ptr);
|
|
return retval;
|
|
}
|
|
|
|
|
|
// Sysytem call wrappers
|
|
static char v = 0;
|
|
ssize_t fuzz_recvmsg(int sockfd, struct msghdr *msg, int flags) {
|
|
|
|
struct iovec *target = msg->msg_iov;
|
|
|
|
//printf("recvmsg 1 \n");
|
|
if (syscall_size > 1) {
|
|
char r = *syscall_data;
|
|
syscall_data += 1;
|
|
syscall_size -= 1;
|
|
|
|
if (r == 12) {
|
|
//printf("recvmsg 2\n");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
int j = 0;
|
|
if (msg->msg_control != NULL) {
|
|
for (;j < CMSG_SPACE(sizeof(struct in_pktinfo)); j++)
|
|
{
|
|
if (syscall_size > 0 && syscall_data != NULL) {
|
|
((char*)msg->msg_control)[j] = *syscall_data;
|
|
syscall_data += 1;
|
|
syscall_size -= 1;
|
|
}
|
|
else {
|
|
((char*)msg->msg_control)[j] = 'A';
|
|
}
|
|
}
|
|
}
|
|
|
|
int i = 0;
|
|
for (; i < target->iov_len; i++) {
|
|
if (syscall_size > 0 && syscall_data != NULL) {
|
|
((char*)target->iov_base)[i] = *syscall_data;
|
|
syscall_data += 1;
|
|
syscall_size -= 1;
|
|
}
|
|
else {
|
|
((char*)target->iov_base)[i] = 'A';
|
|
}
|
|
}
|
|
|
|
if (msg->msg_namelen > 0) {
|
|
memset(msg->msg_name, 0, msg->msg_namelen);
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
|
|
// dnsmasq specific stuff
|
|
int init_daemon(const uint8_t **data2, size_t *size2) {
|
|
const uint8_t *data = *data2;
|
|
size_t size = *size2;
|
|
|
|
int retval = 0;
|
|
|
|
#define CLEAN_IF_NULL(arg) if (arg == NULL) goto cleanup;
|
|
|
|
// Initialize daemon
|
|
daemon = (struct daemon*)gb_alloc_data(sizeof(struct daemon));
|
|
CLEAN_IF_NULL(daemon)
|
|
|
|
// daemon misc
|
|
daemon->max_ttl = get_int(&data, &size);
|
|
daemon->neg_ttl = get_int(&data, &size);
|
|
daemon->local_ttl = get_int(&data, &size);
|
|
daemon->min_cache_ttl = get_int(&data, &size);
|
|
|
|
// daemon->namebuff.
|
|
char *daemon_namebuff = gb_get_null_terminated(&data, &size);
|
|
daemon->namebuff = daemon_namebuff;
|
|
|
|
// daemon->naptr
|
|
struct naptr *naptr_ptr = (struct naptr*)gb_alloc_data(sizeof(struct naptr));
|
|
char *naptr_name = gb_get_null_terminated(&data, &size);
|
|
char *naptr_replace = gb_get_null_terminated(&data, &size);
|
|
char *naptr_regexp = gb_get_null_terminated(&data, &size);
|
|
char *naptr_services = gb_get_null_terminated(&data, &size);
|
|
char *naptr_flags = gb_get_null_terminated(&data, &size);
|
|
|
|
CLEAN_IF_NULL(naptr_ptr)
|
|
CLEAN_IF_NULL(naptr_name)
|
|
CLEAN_IF_NULL(naptr_replace)
|
|
CLEAN_IF_NULL(naptr_regexp)
|
|
CLEAN_IF_NULL(naptr_services)
|
|
CLEAN_IF_NULL(naptr_flags)
|
|
|
|
naptr_ptr->name = naptr_name;
|
|
naptr_ptr->replace = naptr_replace;
|
|
naptr_ptr->regexp = naptr_regexp;
|
|
naptr_ptr->services = naptr_services;
|
|
naptr_ptr->flags = naptr_flags;
|
|
|
|
daemon->naptr = naptr_ptr;
|
|
|
|
// daemon->int_names
|
|
struct interface_name *int_namses = (struct interface_name*)gb_alloc_data(sizeof(struct interface_name));
|
|
|
|
char *int_name = gb_get_null_terminated(&data, &size);
|
|
char *int_intr = gb_get_null_terminated(&data, &size);
|
|
CLEAN_IF_NULL(int_namses)
|
|
CLEAN_IF_NULL(int_name)
|
|
CLEAN_IF_NULL(int_intr)
|
|
int_namses->name = int_name;
|
|
int_namses->intr = int_intr;
|
|
|
|
struct addrlist *d_addrlist = (struct addrlist*)gb_alloc_data(sizeof(struct addrlist));
|
|
CLEAN_IF_NULL(d_addrlist)
|
|
d_addrlist->flags = get_int(&data, &size);
|
|
d_addrlist->prefixlen = get_int(&data, &size);
|
|
int_namses->addr = d_addrlist;
|
|
|
|
daemon->int_names = int_namses;
|
|
|
|
if (size > *size2) {
|
|
goto cleanup;
|
|
}
|
|
|
|
// daemon->addrbuf
|
|
char *adbuf = gb_alloc_data(200);
|
|
CLEAN_IF_NULL(adbuf)
|
|
daemon->addrbuff = adbuf;
|
|
|
|
// daemon->auth_zones
|
|
struct auth_zone *d_az = (struct auth_zone*)gb_alloc_data(sizeof(struct auth_zone));
|
|
char *auth_domain = gb_get_null_terminated(&data, &size);
|
|
|
|
CLEAN_IF_NULL(d_az)
|
|
CLEAN_IF_NULL(auth_domain)
|
|
d_az->domain = auth_domain;
|
|
daemon->auth_zones = d_az;
|
|
|
|
// deamon->mxnames
|
|
struct mx_srv_record *mx_srv_rec = (struct mx_srv_record*)gb_alloc_data(sizeof(struct mx_srv_record));
|
|
char *mx_name = gb_get_null_terminated(&data, &size);
|
|
char *mx_target = gb_get_null_terminated(&data, &size);
|
|
|
|
CLEAN_IF_NULL(mx_srv_rec)
|
|
CLEAN_IF_NULL(mx_target)
|
|
CLEAN_IF_NULL(mx_name)
|
|
|
|
mx_srv_rec->next = daemon->mxnames;
|
|
daemon->mxnames = mx_srv_rec;
|
|
mx_srv_rec->name = mx_name;
|
|
mx_srv_rec->target = mx_target;
|
|
mx_srv_rec->issrv = get_int(&data, &size);
|
|
mx_srv_rec->weight = get_int(&data, &size);
|
|
mx_srv_rec->priority = get_int(&data, &size);
|
|
mx_srv_rec->srvport = get_int(&data, &size);
|
|
//data += 40;
|
|
//size -= 40;
|
|
|
|
if (size > *size2) {
|
|
goto cleanup;
|
|
}
|
|
|
|
// daemon->txt
|
|
struct txt_record *txt_record = (struct txt_record *)gb_alloc_data(sizeof(struct txt_record));
|
|
char *txt_record_name = gb_get_null_terminated(&data, &size);
|
|
char *txt_record_txt = gb_get_null_terminated(&data, &size);
|
|
|
|
CLEAN_IF_NULL(txt_record)
|
|
CLEAN_IF_NULL(txt_record_name)
|
|
CLEAN_IF_NULL(txt_record_txt)
|
|
|
|
txt_record->name = txt_record_name;
|
|
txt_record->txt = (unsigned char*)txt_record_txt;
|
|
txt_record->class2 = (get_short(&data, &size) % 10);
|
|
daemon->txt = txt_record;
|
|
|
|
// daemon->rr
|
|
struct txt_record *rr_record = (struct txt_record *)gb_alloc_data(sizeof(struct txt_record));
|
|
char *rr_record_name = gb_get_null_terminated(&data, &size);
|
|
char *rr_record_txt = gb_get_null_terminated(&data, &size);
|
|
|
|
CLEAN_IF_NULL(rr_record)
|
|
CLEAN_IF_NULL(rr_record_name)
|
|
CLEAN_IF_NULL(rr_record_txt)
|
|
|
|
rr_record->name = rr_record_name;
|
|
rr_record->txt = (unsigned char*)rr_record_txt;
|
|
rr_record->class2 = (get_short(&data, &size) % 10);
|
|
daemon->rr = rr_record;
|
|
|
|
if (size > *size2) {
|
|
goto cleanup;
|
|
}
|
|
|
|
// daemon->relay4
|
|
//struct dhcp_relay *dr = (struct dhcp_relay*)gb_alloc_data(sizeof(struct dhcp_relay));
|
|
struct dhcp_relay *dr = (struct dhcp_relay*)gb_get_random_data(&data, &size, sizeof(struct dhcp_relay));
|
|
char *dr_interface = gb_get_null_terminated(&data, &size);
|
|
|
|
CLEAN_IF_NULL(dr)
|
|
CLEAN_IF_NULL(dr_interface)
|
|
dr->interface = dr_interface;
|
|
dr->next = NULL;
|
|
dr->current = NULL;
|
|
daemon->relay4 = dr;
|
|
|
|
// deamon->bridges
|
|
struct dhcp_bridge *db = (struct dhcp_bridge*)gb_alloc_data(sizeof(struct dhcp_bridge));
|
|
char *db_interface = gb_get_null_terminated(&data, &size);
|
|
|
|
CLEAN_IF_NULL(db)
|
|
CLEAN_IF_NULL(db_interface)
|
|
|
|
if (strlen(db_interface) > IF_NAMESIZE) {
|
|
for (int i = 0; i < IF_NAMESIZE; i++) {
|
|
db->iface[i] = db_interface[i];
|
|
}
|
|
} else {
|
|
for (int i = 0; i < strlen(db_interface); i++) {
|
|
db->iface[i] = db_interface[i];
|
|
}
|
|
}
|
|
|
|
|
|
struct dhcp_bridge *db_alias = (struct dhcp_bridge*)gb_alloc_data(sizeof(struct dhcp_bridge));
|
|
//struct dhcp_bridge *db_alias = (struct dhcp_bridge*)gb_get_random_data(&data, &size, sizeof(struct dhcp_bridge));
|
|
char *db_alias_interface = gb_get_null_terminated(&data, &size);
|
|
|
|
CLEAN_IF_NULL(db_alias)
|
|
CLEAN_IF_NULL(db_alias_interface)
|
|
|
|
if (strlen(db_alias_interface) > IF_NAMESIZE) {
|
|
for (int i = 0; i < IF_NAMESIZE; i++) {
|
|
db_alias->iface[i] = db_alias_interface[i];
|
|
}
|
|
} else {
|
|
for (int i = 0; i < strlen(db_alias_interface); i++) {
|
|
db_alias->iface[i] = db_alias_interface[i];
|
|
}
|
|
}
|
|
db->alias = db_alias;
|
|
daemon->bridges = db;
|
|
|
|
// daemon->if_names
|
|
struct iname *in = (struct iname*)gb_get_random_data(&data, &size, sizeof(struct iname));
|
|
char *iname_name = gb_get_null_terminated(&data, &size);
|
|
|
|
CLEAN_IF_NULL(in)
|
|
CLEAN_IF_NULL(iname_name)
|
|
|
|
in->name = iname_name;
|
|
in->next = NULL;
|
|
|
|
daemon->if_names = in;
|
|
|
|
// daemon->if_addrs
|
|
struct iname *in_addr = (struct iname*)gb_get_random_data(&data, &size, sizeof(struct iname));
|
|
char *iname_name_addr = gb_get_null_terminated(&data, &size);
|
|
|
|
CLEAN_IF_NULL(in_addr)
|
|
CLEAN_IF_NULL(iname_name_addr)
|
|
|
|
in_addr->name = iname_name_addr;
|
|
in_addr->next = NULL;
|
|
|
|
daemon->if_addrs = in_addr;
|
|
|
|
// daemon->if_except
|
|
struct iname *in_except = (struct iname*)gb_get_random_data(&data, &size, sizeof(struct iname));
|
|
char *iname_name_except = gb_get_null_terminated(&data, &size);
|
|
|
|
CLEAN_IF_NULL(in_except)
|
|
CLEAN_IF_NULL(iname_name_except)
|
|
|
|
in_except->name = iname_name_except;
|
|
in_except->next = NULL;
|
|
|
|
daemon->if_except = in_except;
|
|
|
|
// daemon->dhcp_except
|
|
struct iname *except = (struct iname*)gb_get_random_data(&data, &size, sizeof(struct iname));
|
|
char *name_except = gb_get_null_terminated(&data, &size);
|
|
|
|
CLEAN_IF_NULL(except)
|
|
CLEAN_IF_NULL(name_except)
|
|
|
|
except->name = name_except;
|
|
except->next = NULL;
|
|
|
|
daemon->dhcp_except = except;
|
|
|
|
// daemon->authinterface
|
|
struct iname *auth_interface = (struct iname*)gb_get_random_data(&data, &size, sizeof(struct iname));
|
|
char *auth_name = gb_get_null_terminated(&data, &size);
|
|
|
|
CLEAN_IF_NULL(auth_interface)
|
|
CLEAN_IF_NULL(auth_name)
|
|
|
|
auth_interface->name = auth_name;
|
|
auth_interface->next = NULL;
|
|
|
|
daemon->authinterface = auth_interface;
|
|
|
|
|
|
// daemon->cnames
|
|
struct cname *cn = (struct cname*)gb_alloc_data(sizeof(struct cname));
|
|
char *cname_alias = gb_get_null_terminated(&data, &size);
|
|
char *cname_target = gb_get_null_terminated(&data, &size);
|
|
|
|
CLEAN_IF_NULL(cn)
|
|
CLEAN_IF_NULL(cname_alias)
|
|
CLEAN_IF_NULL(cname_target)
|
|
|
|
cn->alias = cname_alias;
|
|
cn->target = cname_target;
|
|
daemon->cnames = cn;
|
|
|
|
|
|
// daemon->ptr
|
|
struct ptr_record *ptr = (struct ptr_record *)gb_alloc_data(sizeof(struct ptr_record));
|
|
CLEAN_IF_NULL(ptr)
|
|
|
|
char *ptr_name = gb_get_null_terminated(&data, &size);
|
|
CLEAN_IF_NULL(ptr_name)
|
|
ptr->name = ptr_name;
|
|
daemon->ptr = ptr;
|
|
|
|
if (size > *size2) {
|
|
goto cleanup;
|
|
}
|
|
|
|
// daemon->dhcp
|
|
struct dhcp_context *dhcp_c = (struct dhcp_context *) gb_get_random_data(&data, &size, sizeof(struct dhcp_context));
|
|
|
|
char *dhcp_c_temp_in = gb_get_null_terminated(&data, &size);
|
|
|
|
struct dhcp_netid *dhcp_c_netid = (struct dhcp_netid *) gb_alloc_data(sizeof(struct dhcp_netid));
|
|
char *dhcp_netid_net = gb_get_null_terminated(&data, &size);
|
|
|
|
CLEAN_IF_NULL(dhcp_c)
|
|
CLEAN_IF_NULL(dhcp_c_temp_in)
|
|
CLEAN_IF_NULL(dhcp_c_netid)
|
|
CLEAN_IF_NULL(dhcp_netid_net)
|
|
|
|
dhcp_c->next = NULL;
|
|
dhcp_c->current = NULL;
|
|
dhcp_c_netid->net = dhcp_netid_net;
|
|
dhcp_c->filter = dhcp_c_netid;
|
|
dhcp_c->template_interface = dhcp_c_temp_in;
|
|
|
|
daemon->dhcp = dhcp_c;
|
|
|
|
|
|
// daemon->dhcp6
|
|
struct dhcp_context *dhcp6_c = (struct dhcp_context *) gb_get_random_data(&data, &size, sizeof(struct dhcp_context));
|
|
|
|
char *dhcp6_c_temp_in = gb_get_null_terminated(&data, &size);
|
|
|
|
struct dhcp_netid *dhcp6_c_netid = (struct dhcp_netid *) gb_alloc_data(sizeof(struct dhcp_netid));
|
|
char *dhcp6_netid_net = gb_get_null_terminated(&data, &size);
|
|
|
|
CLEAN_IF_NULL(dhcp6_c)
|
|
CLEAN_IF_NULL(dhcp6_c_temp_in)
|
|
CLEAN_IF_NULL(dhcp6_c_netid)
|
|
CLEAN_IF_NULL(dhcp6_netid_net)
|
|
|
|
dhcp6_c->next = NULL;
|
|
dhcp6_c->current = NULL;
|
|
dhcp6_c_netid->net = dhcp6_netid_net;
|
|
dhcp6_c->filter = dhcp6_c_netid;
|
|
dhcp6_c->template_interface = dhcp6_c_temp_in;
|
|
|
|
daemon->dhcp6 = dhcp6_c;
|
|
|
|
// daemon->doing_dhcp6
|
|
daemon->doing_dhcp6 = 1;
|
|
|
|
// daemon->dhcp_buffs
|
|
char *dhcp_buff = gb_alloc_data(DHCP_BUFF_SZ);
|
|
char *dhcp_buff2 = gb_alloc_data(DHCP_BUFF_SZ);
|
|
char *dhcp_buff3 = gb_alloc_data(DHCP_BUFF_SZ);
|
|
|
|
CLEAN_IF_NULL(dhcp_buff)
|
|
CLEAN_IF_NULL(dhcp_buff2)
|
|
CLEAN_IF_NULL(dhcp_buff3)
|
|
|
|
daemon->dhcp_buff = dhcp_buff;
|
|
daemon->dhcp_buff2 = dhcp_buff2;
|
|
daemon->dhcp_buff3 = dhcp_buff3;
|
|
|
|
|
|
|
|
// daemon->ignore_addr
|
|
struct bogus_addr *bb = (struct bogus_addr *)gb_alloc_data(sizeof(struct bogus_addr));
|
|
CLEAN_IF_NULL(bb)
|
|
|
|
daemon->ignore_addr = bb;
|
|
|
|
// daemon->doctors
|
|
if (size > *size2) {
|
|
goto cleanup;
|
|
}
|
|
|
|
struct doctor *doctors = (struct doctor *)gb_alloc_data(sizeof(struct doctor));
|
|
CLEAN_IF_NULL(doctors)
|
|
|
|
doctors->next = NULL;
|
|
daemon->doctors = doctors;
|
|
|
|
retval = 0;
|
|
goto ret;
|
|
cleanup:
|
|
retval = -1;
|
|
|
|
ret:
|
|
return retval;
|
|
}
|