Coverage Report

Created: 2026-04-12 06:59

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/fuzz_util.cc
Line
Count
Source
1
/* Copyright 2026 Google LLC
2
Licensed under the Apache License, Version 2.0 (the "License");
3
you may not use this file except in compliance with the License.
4
You may obtain a copy of the License at
5
      http://www.apache.org/licenses/LICENSE-2.0
6
Unless required by applicable law or agreed to in writing, software
7
distributed under the License is distributed on an "AS IS" BASIS,
8
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9
See the License for the specific language governing permissions and
10
limitations under the License.
11
*/
12
13
extern "C" {
14
#include "dnsmasq.h"
15
}
16
17
#include <fuzzer/FuzzedDataProvider.h>
18
#include <cstdlib>
19
#include <cstring>
20
#include <string>
21
#include <vector>
22
23
28.2k
#define STR_SIZE 75
24
25
static int init_daemon(FuzzedDataProvider &provider,
26
                       std::vector<void *> &allocs,
27
1.04k
                       std::vector<std::string> &strings) {
28
28.2k
  auto alloc = [&](size_t sz) -> void * {
29
28.2k
    void *p = calloc(1, sz);
30
28.2k
    if (p) allocs.push_back(p);
31
28.2k
    return p;
32
28.2k
  };
33
34
29.2k
  auto make_string = [&](size_t max_len) -> char * {
35
29.2k
    strings.push_back(provider.ConsumeRandomLengthString(max_len));
36
29.2k
    return strings.back().data();
37
29.2k
  };
38
39
  // Allocate daemon separately (not via alloc) so it can be freed last.
40
  // dnsmasq's free_real() dereferences daemon for metrics tracking.
41
1.04k
  daemon = (struct daemon *)calloc(1, sizeof(struct daemon));
42
1.04k
  if (!daemon) return -1;
43
44
1.04k
  daemon->max_ttl = provider.ConsumeIntegral<int>();
45
1.04k
  daemon->neg_ttl = provider.ConsumeIntegral<int>();
46
1.04k
  daemon->local_ttl = provider.ConsumeIntegral<int>();
47
1.04k
  daemon->min_cache_ttl = provider.ConsumeIntegral<int>();
48
49
1.04k
  daemon->namebuff = make_string(MAXDNAME);
50
51
  // daemon->naptr
52
1.04k
  struct naptr *naptr_ptr = (struct naptr *)alloc(sizeof(struct naptr));
53
1.04k
  if (!naptr_ptr) return -1;
54
1.04k
  naptr_ptr->name = make_string(STR_SIZE);
55
1.04k
  naptr_ptr->replace = make_string(STR_SIZE);
56
1.04k
  naptr_ptr->regexp = make_string(STR_SIZE);
57
1.04k
  naptr_ptr->services = make_string(STR_SIZE);
58
1.04k
  naptr_ptr->flags = make_string(STR_SIZE);
59
1.04k
  daemon->naptr = naptr_ptr;
60
61
  // daemon->int_names
62
1.04k
  struct interface_name *int_namses =
63
1.04k
      (struct interface_name *)alloc(sizeof(struct interface_name));
64
1.04k
  if (!int_namses) return -1;
65
1.04k
  int_namses->name = make_string(STR_SIZE);
66
1.04k
  int_namses->intr = make_string(STR_SIZE);
67
68
1.04k
  struct addrlist *d_addrlist = (struct addrlist *)alloc(sizeof(struct addrlist));
69
1.04k
  if (!d_addrlist) return -1;
70
1.04k
  d_addrlist->flags = provider.ConsumeIntegral<int>();
71
1.04k
  d_addrlist->prefixlen = provider.ConsumeIntegral<int>();
72
1.04k
  int_namses->addr = d_addrlist;
73
1.04k
  daemon->int_names = int_namses;
74
75
  // daemon->addrbuf
76
1.04k
  char *adbuf = (char *)alloc(200);
77
1.04k
  if (!adbuf) return -1;
78
1.04k
  daemon->addrbuff = adbuf;
79
80
  // daemon->auth_zones
81
1.04k
  struct auth_zone *d_az = (struct auth_zone *)alloc(sizeof(struct auth_zone));
82
1.04k
  if (!d_az) return -1;
83
1.04k
  d_az->domain = make_string(STR_SIZE);
84
1.04k
  daemon->auth_zones = d_az;
85
86
  // daemon->mxnames
87
1.04k
  struct mx_srv_record *mx_srv_rec =
88
1.04k
      (struct mx_srv_record *)alloc(sizeof(struct mx_srv_record));
89
1.04k
  if (!mx_srv_rec) return -1;
90
1.04k
  mx_srv_rec->next = daemon->mxnames;
91
1.04k
  daemon->mxnames = mx_srv_rec;
92
1.04k
  mx_srv_rec->name = make_string(STR_SIZE);
93
1.04k
  mx_srv_rec->target = make_string(STR_SIZE);
94
1.04k
  mx_srv_rec->issrv = provider.ConsumeIntegral<int>();
95
1.04k
  mx_srv_rec->weight = provider.ConsumeIntegral<int>();
96
1.04k
  mx_srv_rec->priority = provider.ConsumeIntegral<int>();
97
1.04k
  mx_srv_rec->srvport = provider.ConsumeIntegral<int>();
98
99
  // daemon->txt
100
1.04k
  struct txt_record *txt_record =
101
1.04k
      (struct txt_record *)alloc(sizeof(struct txt_record));
102
1.04k
  if (!txt_record) return -1;
103
1.04k
  txt_record->name = make_string(STR_SIZE);
104
1.04k
  txt_record->txt = (unsigned char *)make_string(STR_SIZE);
105
1.04k
  txt_record->class2 = provider.ConsumeIntegralInRange<short>(0, 9);
106
1.04k
  daemon->txt = txt_record;
107
108
  // daemon->rr
109
1.04k
  struct txt_record *rr_record =
110
1.04k
      (struct txt_record *)alloc(sizeof(struct txt_record));
111
1.04k
  if (!rr_record) return -1;
112
1.04k
  rr_record->name = make_string(STR_SIZE);
113
1.04k
  rr_record->txt = (unsigned char *)make_string(STR_SIZE);
114
1.04k
  rr_record->class2 = provider.ConsumeIntegralInRange<short>(0, 9);
115
1.04k
  daemon->rr = rr_record;
116
117
  // daemon->relay4
118
1.04k
  struct dhcp_relay *dr = (struct dhcp_relay *)alloc(sizeof(struct dhcp_relay));
119
1.04k
  if (!dr) return -1;
120
1.04k
  dr->interface = make_string(STR_SIZE);
121
1.04k
  dr->next = NULL;
122
1.04k
  daemon->relay4 = dr;
123
124
  // daemon->bridges
125
1.04k
  struct dhcp_bridge *db = (struct dhcp_bridge *)alloc(sizeof(struct dhcp_bridge));
126
1.04k
  if (!db) return -1;
127
1.04k
  {
128
1.04k
    std::string iface_str = provider.ConsumeRandomLengthString(IF_NAMESIZE - 1);
129
1.04k
    memcpy(db->iface, iface_str.c_str(), iface_str.size() + 1);
130
1.04k
  }
131
132
1.04k
  struct dhcp_bridge *db_alias =
133
1.04k
      (struct dhcp_bridge *)alloc(sizeof(struct dhcp_bridge));
134
1.04k
  if (!db_alias) return -1;
135
1.04k
  {
136
1.04k
    std::string alias_str = provider.ConsumeRandomLengthString(IF_NAMESIZE - 1);
137
1.04k
    memcpy(db_alias->iface, alias_str.c_str(), alias_str.size() + 1);
138
1.04k
  }
139
1.04k
  db->alias = db_alias;
140
1.04k
  daemon->bridges = db;
141
142
  // daemon->if_names, if_addrs, if_except, dhcp_except, authinterface
143
5.22k
  auto make_iname = [&]() -> struct iname * {
144
5.22k
    struct iname *in = (struct iname *)alloc(sizeof(struct iname));
145
5.22k
    if (!in) return nullptr;
146
5.22k
    in->name = make_string(STR_SIZE);
147
5.22k
    in->next = NULL;
148
5.22k
    return in;
149
5.22k
  };
150
151
1.04k
  daemon->if_names = make_iname();
152
1.04k
  daemon->if_addrs = make_iname();
153
1.04k
  daemon->if_except = make_iname();
154
1.04k
  daemon->dhcp_except = make_iname();
155
1.04k
  daemon->authinterface = make_iname();
156
1.04k
  if (!daemon->if_names || !daemon->if_addrs || !daemon->if_except ||
157
1.04k
      !daemon->dhcp_except || !daemon->authinterface)
158
0
    return -1;
159
160
  // daemon->cnames
161
1.04k
  struct cname *cn = (struct cname *)alloc(sizeof(struct cname));
162
1.04k
  if (!cn) return -1;
163
1.04k
  cn->alias = make_string(STR_SIZE);
164
1.04k
  cn->target = make_string(STR_SIZE);
165
1.04k
  daemon->cnames = cn;
166
167
  // daemon->ptr
168
1.04k
  struct ptr_record *ptr = (struct ptr_record *)alloc(sizeof(struct ptr_record));
169
1.04k
  if (!ptr) return -1;
170
1.04k
  ptr->name = make_string(STR_SIZE);
171
1.04k
  daemon->ptr = ptr;
172
173
  // daemon->dhcp
174
1.04k
  struct dhcp_context *dhcp_c =
175
1.04k
      (struct dhcp_context *)alloc(sizeof(struct dhcp_context));
176
1.04k
  if (!dhcp_c) return -1;
177
1.04k
  dhcp_c->next = NULL;
178
1.04k
  dhcp_c->current = NULL;
179
1.04k
  struct dhcp_netid *dhcp_c_netid =
180
1.04k
      (struct dhcp_netid *)alloc(sizeof(struct dhcp_netid));
181
1.04k
  if (!dhcp_c_netid) return -1;
182
1.04k
  dhcp_c_netid->net = make_string(STR_SIZE);
183
1.04k
  dhcp_c->filter = dhcp_c_netid;
184
1.04k
  dhcp_c->template_interface = make_string(STR_SIZE);
185
1.04k
  daemon->dhcp = dhcp_c;
186
187
  // daemon->dhcp6
188
1.04k
  struct dhcp_context *dhcp6_c =
189
1.04k
      (struct dhcp_context *)alloc(sizeof(struct dhcp_context));
190
1.04k
  if (!dhcp6_c) return -1;
191
1.04k
  dhcp6_c->next = NULL;
192
1.04k
  dhcp6_c->current = NULL;
193
1.04k
  struct dhcp_netid *dhcp6_c_netid =
194
1.04k
      (struct dhcp_netid *)alloc(sizeof(struct dhcp_netid));
195
1.04k
  if (!dhcp6_c_netid) return -1;
196
1.04k
  dhcp6_c_netid->net = make_string(STR_SIZE);
197
1.04k
  dhcp6_c->filter = dhcp6_c_netid;
198
1.04k
  dhcp6_c->template_interface = make_string(STR_SIZE);
199
1.04k
  daemon->dhcp6 = dhcp6_c;
200
201
1.04k
  daemon->doing_dhcp6 = 1;
202
203
  // daemon->dhcp_buffs
204
1.04k
  daemon->dhcp_buff = (char *)alloc(DHCP_BUFF_SZ);
205
1.04k
  daemon->dhcp_buff2 = (char *)alloc(DHCP_BUFF_SZ);
206
1.04k
  daemon->dhcp_buff3 = (char *)alloc(DHCP_BUFF_SZ);
207
1.04k
  if (!daemon->dhcp_buff || !daemon->dhcp_buff2 || !daemon->dhcp_buff3)
208
0
    return -1;
209
210
  // daemon->ignore_addr
211
1.04k
  struct bogus_addr *bb = (struct bogus_addr *)alloc(sizeof(struct bogus_addr));
212
1.04k
  if (!bb) return -1;
213
1.04k
  daemon->ignore_addr = bb;
214
215
  // daemon->doctors
216
1.04k
  struct doctor *doctors = (struct doctor *)alloc(sizeof(struct doctor));
217
1.04k
  if (!doctors) return -1;
218
1.04k
  doctors->next = NULL;
219
1.04k
  daemon->doctors = doctors;
220
221
1.04k
  return 0;
222
1.04k
}
223
224
1.04k
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
225
1.04k
  FuzzedDataProvider provider(data, size);
226
227
1.04k
  std::vector<void *> allocs;
228
1.04k
  std::vector<std::string> strings;
229
1.04k
  strings.reserve(40);
230
231
1.04k
  int succ = init_daemon(provider, allocs, strings);
232
1.04k
  if (succ == 0) {
233
1.04k
    std::string t1_str = provider.ConsumeRandomLengthString(MAXDNAME);
234
1.04k
    std::string t2_str = provider.ConsumeRandomLengthString(MAXDNAME);
235
1.04k
    if (t1_str.empty() || t2_str.empty())
236
387
      goto cleanup;
237
238
658
    char *t1 = t1_str.data();
239
658
    char *t2 = t2_str.data();
240
241
    // Util logic
242
658
    hostname_isequal(t1, t2);
243
244
658
    legal_hostname(t1);
245
658
    char *tmp = canonicalise(t2, NULL);
246
658
    if (tmp != NULL) {
247
306
      free(tmp);
248
306
    }
249
250
658
    char *tmp_out = (char *)malloc(30);
251
658
    if (tmp_out) {
252
658
      int mac_type;
253
658
      parse_hex(t1, (unsigned char *)tmp_out, 30, NULL, NULL);
254
658
      parse_hex(t1, (unsigned char *)tmp_out, 30, NULL, &mac_type);
255
658
      free(tmp_out);
256
658
    }
257
258
658
    wildcard_match(t1, t2);
259
658
    if (t1_str.size() < t2_str.size()) {
260
114
      wildcard_matchn(t1, t2, t1_str.size());
261
544
    } else {
262
544
      wildcard_matchn(t1, t2, t2_str.size());
263
544
    }
264
658
    hostname_issubdomain(t1, t2);
265
266
658
    union all_addr addr1;
267
658
    memset(&addr1, 0, sizeof(union all_addr));
268
658
    is_name_synthetic(0, t1, &addr1);
269
658
  }
270
271
1.04k
cleanup:
272
1.04k
  for (void *p : allocs)
273
28.2k
    free(p);
274
  // Free daemon last since dnsmasq's free_real() dereferences it.
275
1.04k
  free(daemon);
276
1.04k
  daemon = NULL;
277
278
1.04k
  return 0;
279
1.04k
}