net: ipconfig: bound DHCP option construction

ic_dhcp_init_options() appends the hostname (option 12), vendor-class
(option 60) and client-ID (option 61) options into the fixed 312-byte
bootp_pkt.exten[] buffer.  Only the client-ID branch checked the
remaining space; the hostname and vendor-class writes were unbounded.

A 64-byte hostname together with the maximum 252-byte dhcpclass=
identifier needs 18 + (2 + 64) + (2 + 252) = 338 of the 312 available
bytes even before the terminating END marker, so the vendor-class memcpy
runs past the end of exten[].  With CONFIG_FORTIFY_SOURCE this is
reported as a field-spanning write and, when the kernel is booted with
panic_on_warn=1, aborts boot with a panic.

Route the optional options through a common helper that makes sure the
option, its 2-byte header and the END marker all fit and drops an option
that would not.  Configurations with short options keep sending exactly
the same bytes as before.

Fixes: 130c0f47fd ("ipconfig: send host-name in DHCP requests")
Cc: stable@vger.kernel.org
Reported-by: Vega <vega@nebusec.ai>
Assisted-by: LLM
Signed-off-by: Yuqi Xu <xuyuqiabc@gmail.com>
Reviewed-by: Ren Wei <weir@nebusec.ai>
Reviewed-by: Simon Horman <horms@kernel.org>
Link: https://patch.msgid.link/7808dfbfa2162dfd0b19f59aff5742d6e0db2abb.1789798023.git.xuyuqiabc@gmail.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
Yuqi Xu 2026-09-19 16:45:27 +08:00 committed by Paolo Abeni
parent 90c2e97ff8
commit e47a1958e1

View File

@ -676,6 +676,24 @@ static const u8 ic_bootp_cookie[4] = { 99, 130, 83, 99 };
#ifdef IPCONFIG_DHCP
static bool __init
ic_dhcp_add_option(u8 **options, const u8 *end, u8 type, const void *value,
int len)
{
u8 *e = *options;
/* leave room for the option header and the END marker */
if (len > U8_MAX || end - e < len + 3)
return false;
*e++ = type;
*e++ = len;
memcpy(e, value, len);
*options = e + len;
return true;
}
static void __init
ic_dhcp_init_options(u8 *options, struct ic_device *d)
{
@ -691,6 +709,7 @@ ic_dhcp_init_options(u8 *options, struct ic_device *d)
42, /* NTP servers */
};
u8 mt = (ic_servaddr == NONE) ? DHCPDISCOVER : DHCPREQUEST;
u8 *end = options + sizeof(((struct bootp_pkt *)0)->exten);
u8 *e = options;
int len;
@ -721,31 +740,19 @@ ic_dhcp_init_options(u8 *options, struct ic_device *d)
e += sizeof(ic_req_params);
if (ic_host_name_set) {
*e++ = 12; /* host-name */
len = strlen(utsname()->nodename);
*e++ = len;
memcpy(e, utsname()->nodename, len);
e += len;
ic_dhcp_add_option(&e, end, 12, utsname()->nodename, len);
}
if (*vendor_class_identifier) {
pr_info("DHCP: sending class identifier \"%s\"\n",
vendor_class_identifier);
*e++ = 60; /* Class-identifier */
len = strlen(vendor_class_identifier);
*e++ = len;
memcpy(e, vendor_class_identifier, len);
e += len;
if (ic_dhcp_add_option(&e, end, 60, vendor_class_identifier, len))
pr_info("DHCP: sending class identifier \"%s\"\n",
vendor_class_identifier);
}
len = strlen(dhcp_client_identifier + 1);
/* the minimum length of identifier is 2, include 1 byte type,
* and can not be larger than the length of options
*/
if (len >= 1 && len < 312 - (e - options) - 1) {
*e++ = 61;
*e++ = len + 1;
memcpy(e, dhcp_client_identifier, len + 1);
e += len + 1;
}
/* the minimum length of identifier is 2, include 1 byte type */
if (len >= 1)
ic_dhcp_add_option(&e, end, 61, dhcp_client_identifier, len + 1);
*e++ = 255; /* End of the list */
}