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