[Dnsmasq-discuss] [PATCH] A segmentation fault occurred in dnsmasq
renmingshuai
renmingshuai at huawei.com
Fri Aug 2 03:06:21 UTC 2024
This issue is caused by dnsmasq incorrectly handling the client's tag information. It is not related to adding the opts item.
This is a necessary issue, and the steps to reproduce it are as follows:
1. Run dnsmasq with --dhcp-hostsfile
# dnsmasq --dhcp-hostsfile=/var/run/test_dhcp/host --dhcp-range=192.168.0.0,192.168.0.240,infinite
# cat /var/run/test_dhcp/host
d0:c0:c4:66:25:81,host-192-168-0-159.dhcp-test, 192.168.0.159,set:port-cfc5b9f7-2760-42d6-899b-6817fe405721
2. Request a dynamic address for the client that comply with the host rule
# dhclient -cf /etc/dhcp/dhclient.conf -pf /tmp/eth1.pid -lf /tmp/eth1.lease eth1
# cat /etc/dhcp/dhclient.conf
send dhcp-client-identifier " host-192-168-0-159.dhcp-test";
# ip link show eth1
2: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
link/ether d0:c0:c4:66:25:81 brd ff:ff:ff:ff:ff:ff
3. Send SIGHUP to dnsmasq
# kill -1 446255
In addition to the above issue, dnsmasq may also be related to a UAF problem. Here is the patch I provided for fixing these issues.
>From 31b883b2bcaf70b74549128ded853f4942e5f6ae Mon Sep 17 00:00:00 2001
From: renmingshuai <renmingshuai at huawei.com>
Date: Thu, 1 Aug 2024 19:56:47 +0800
Subject: [PATCH] Fix a coredump and UAF caused by trying to free non-heap
objects.
There is an issue with the process of adding a netid in dhcp_deply for dnsmasq, which will trigger coredump and UAF.
When processing DHCP requests, dnsmasq uses local variables such as known_id, iface_id and cpewan_id, to store the client's tag information and add them to the netid list.
Afterwards, dnsmasq will call find_config() to find a dhcp config and add the netid list to the global daemon->dhcp_conf->netid list.
In addition, dnsmasq will also add the netid from other global variables to the config->netid list, such as daemon->dhcp_match->netid
When dnsmasq receives the hup signal, it will call clear_dynamic_conf() to free the netid in the config's netid list, which will trigger at least the following three issues:
1. config->netid->list may point to a stack address (such as the address of the local variable "known_id" mentioned above), and the content of this address may be assigned other values.
Attempting to free this illegal address value will trigger coredump.
2. config->netid->list->net may point to a string constant (such as known_id.net="knwon"), and attempting to free this address will also trigger coredump.
3. The address pointed to by config->netid->list may be also added to other global lists, such as daemon->dhcp_match->netid.
After the address is freed, it still exists in the daemon->dhcp_match list, which may trigger UAF issues.
Calling dhcp_netid_creat() to allocate new memory for the client's tag information can solve the above issues. These memories will be fully freed in clear_rynamic_comf().
Signed-off-by: renmingshuai <renmingshuai at huawei.com>
---
src/dnsmasq.h | 1 +
src/option.c | 2 +-
src/rfc2131.c | 48 ++++++++++++++----------------------------------
3 files changed, 16 insertions(+), 35 deletions(-)
diff --git a/src/dnsmasq.h b/src/dnsmasq.h
index e455c3f..3376655 100644
--- a/src/dnsmasq.h
+++ b/src/dnsmasq.h
@@ -1519,6 +1519,7 @@ char *parse_server(char *arg, struct server_details *sdetails);
char *parse_server_addr(struct server_details *sdetails);
int parse_server_next(struct server_details *sdetails);
int option_read_dynfile(char *file, int flags);
+struct dhcp_netid *dhcp_netid_create(const char *net, struct dhcp_netid *next);
/* forward.c */
void reply_query(int fd, time_t now);
diff --git a/src/option.c b/src/option.c
index f4ff7c0..480ef5f 100644
--- a/src/option.c
+++ b/src/option.c
@@ -1314,7 +1314,7 @@ static char *set_prefix(char *arg)
return arg;
}
-static struct dhcp_netid *dhcp_netid_create(const char *net, struct dhcp_netid *next)
+struct dhcp_netid *dhcp_netid_create(const char *net, struct dhcp_netid *next)
{
struct dhcp_netid *tt;
tt = opt_malloc(sizeof (struct dhcp_netid));
diff --git a/src/rfc2131.c b/src/rfc2131.c
index 68834ea..050d610 100644
--- a/src/rfc2131.c
+++ b/src/rfc2131.c
@@ -96,7 +96,6 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
unsigned char *agent_id = NULL, *uuid = NULL;
unsigned char *emac = NULL;
int vendor_class_len = 0, emac_len = 0;
- struct dhcp_netid known_id, iface_id, cpewan_id;
struct dhcp_opt *o;
unsigned char pxe_uuid[17];
unsigned char *oui = NULL, *serial = NULL;
@@ -107,9 +106,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
subnet_addr.s_addr = override.s_addr = 0;
/* set tag with name == interface */
- iface_id.net = iface_name;
- iface_id.next = NULL;
- netid = &iface_id;
+ netid = dhcp_netid_create(iface_name, NULL);
if (mess->op != BOOTREQUEST || mess->hlen > DHCP_CHADDR_MAX)
return 0;
@@ -173,9 +170,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
the gateway id back. Note that the device class is optional */
if (oui && serial)
{
- cpewan_id.net = "cpewan-id";
- cpewan_id.next = netid;
- netid = &cpewan_id;
+ netid = dhcp_netid_create("cpewan-id", netid);
}
break;
}
@@ -226,8 +221,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
vendor->len == option_len(sopt) &&
memcmp(option_ptr(sopt, 0), vendor->data, vendor->len) == 0)
{
- vendor->netid.next = netid;
- netid = &vendor->netid;
+ netid = dhcp_netid_create(vendor->netid.net, netid);
}
}
}
@@ -263,8 +257,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
(mac->hwaddr_type == mess->htype || mac->hwaddr_type == 0) &&
memcmp_masked(mac->hwaddr, mess->chaddr, mess->hlen, mac->mask))
{
- mac->netid.next = netid;
- netid = &mac->netid;
+ netid = dhcp_netid_create(mac->netid.net, netid);
}
/* Determine network for this packet. Our caller will have already linked all the
@@ -453,8 +446,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
if (match)
{
- o->netid->next = netid;
- netid = o->netid;
+ netid = dhcp_netid_create(o->netid->net, netid);
}
}
@@ -499,8 +491,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
for (i = 0; i <= (option_len(opt) - vendor->len); i++)
if (memcmp(vendor->data, option_ptr(opt, i), vendor->len) == 0)
{
- vendor->netid.next = netid;
- netid = &vendor->netid;
+ netid = dhcp_netid_create(vendor->netid.net, netid);
break;
}
}
@@ -531,16 +522,12 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
/* set "known" tag for known hosts */
if (config)
{
- known_id.net = "known";
- known_id.next = netid;
- netid = &known_id;
+ netid = dhcp_netid_create("known", netid);
}
else if (find_config(daemon->dhcp_conf, NULL, clid, clid_len,
mess->chaddr, mess->hlen, mess->htype, NULL, run_tag_if(netid)))
{
- known_id.net = "known-othernet";
- known_id.next = netid;
- netid = &known_id;
+ netid = dhcp_netid_create("known-othernet", netid);
}
if (mess_type == 0 && !pxe)
@@ -580,16 +567,12 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
{
memcpy(daemon->dhcp_buff2, mess->file, sizeof(mess->file));
daemon->dhcp_buff2[sizeof(mess->file) + 1] = 0; /* ensure zero term. */
- id.net = (char *)daemon->dhcp_buff2;
- id.next = netid;
- netid = &id;
+ netid = dhcp_netid_create((char *)daemon->dhcp_buff2, netid);
}
/* Add "bootp" as a tag to allow different options, address ranges etc
for BOOTP clients */
- bootp_id.net = "bootp";
- bootp_id.next = netid;
- netid = &bootp_id;
+ netid = dhcp_netid_create("bootp", netid);
tagif_netid = run_tag_if(netid);
@@ -634,8 +617,8 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
message = _("wrong network");
else if (context->netid.net)
{
- context->netid.next = netid;
- tagif_netid = run_tag_if(&context->netid);
+ netid = dhcp_netid_create(context->netid.net, netid);
+ tagif_netid = run_tag_if(netid);
}
log_tags(tagif_netid, ntohl(mess->xid));
@@ -770,8 +753,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
if (hostname_isequal(client_hostname, m->name) &&
(save == 0 || m->wildcard))
{
- m->netid->next = netid;
- netid = m->netid;
+ netid = dhcp_netid_create(m->netid->net, netid);
}
if (save != 0)
@@ -808,9 +790,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
{
config = new;
/* set "known" tag for known hosts */
- known_id.net = "known";
- known_id.next = netid;
- netid = &known_id;
+ netid = dhcp_netid_create("known", netid);
}
}
}
--
2.33.0
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.thekelleys.org.uk/pipermail/dnsmasq-discuss/attachments/20240802/f0cabfc0/attachment-0001.htm>
More information about the Dnsmasq-discuss
mailing list