diff options
| author | Francois Berder <[email protected]> | 2026-05-15 18:53:32 +0200 |
|---|---|---|
| committer | Jerome Forissier <[email protected]> | 2026-06-03 17:22:24 +0200 |
| commit | 2b612de8952d448ab6345c5af6e28fecea1a2f1e (patch) | |
| tree | 086e52d955a0d5897d8f811f97a866684fa09533 /net | |
| parent | 4ba29d709419a567832276f80592d28f42e965b2 (diff) | |
net: dhcpv6: Prevent out-of-bounds reads while parsing options
dhcp6_parse_options() verifies that an option's declared data fits
within the packet, but does not check that option_len is large
enough for the fixed-size read each case performs. A malicious
DHCP server can send an ADVERTISE with a zero-length IA_NA,
STATUS_CODE, SOL_MAX_RT, or BOOTFILE_PARAM option, causing the
parser to read 2-4 bytes past the option's declared data.
Check option_len value before each dereference of option_ptr.
Signed-off-by: Francois Berder <[email protected]>
Diffstat (limited to 'net')
| -rw-r--r-- | net/dhcpv6.c | 27 |
1 files changed, 27 insertions, 0 deletions
diff --git a/net/dhcpv6.c b/net/dhcpv6.c index 51f44979f8e..640f089a2e1 100644 --- a/net/dhcpv6.c +++ b/net/dhcpv6.c @@ -339,6 +339,11 @@ static void dhcp6_parse_options(uchar *rx_pkt, unsigned int len) break; case DHCP6_OPTION_IA_TA: case DHCP6_OPTION_IA_NA: + if (option_len < sizeof(u32)) { + debug("Invalid IA_NA/IA_TA option length\n"); + break; + } + /* check the IA_ID */ if (*((u32 *)option_ptr) != htonl(sm_params.ia_id)) { debug("IA_ID mismatch 0x%08x 0x%08x\n", @@ -347,6 +352,10 @@ static void dhcp6_parse_options(uchar *rx_pkt, unsigned int len) } if (ntohs(option_hdr->option_id) == DHCP6_OPTION_IA_NA) { + if (option_len < 3 * sizeof(u32)) { + debug("Invalid IA_NA option length\n"); + break; + } /* skip past IA_ID/T1/T2 */ option_ptr += 3 * sizeof(u32); } else if (ntohs(option_hdr->option_id) == DHCP6_OPTION_IA_TA) { @@ -358,12 +367,20 @@ static void dhcp6_parse_options(uchar *rx_pkt, unsigned int len) break; case DHCP6_OPTION_STATUS_CODE: debug("DHCP6_OPTION_STATUS_CODE FOUND\n"); + if (option_len < sizeof(u16)) { + debug("Invalid status code option length\n"); + break; + } sm_params.rx_status.status_code = ntohs(*((u16 *)option_ptr)); debug("DHCP6 top-level status code %d\n", sm_params.rx_status.status_code); debug("DHCP6 status message: %.*s\n", len, option_ptr + 2); break; case DHCP6_OPTION_SOL_MAX_RT: debug("DHCP6_OPTION_SOL_MAX_RT FOUND\n"); + if (option_len != sizeof(u32)) { + debug("Invalid SOL_MAX_RT option length\n"); + break; + } sol_max_rt_sec = ntohl(*((u32 *)option_ptr)); /* A DHCP client MUST ignore any SOL_MAX_RT option values that are less @@ -394,6 +411,12 @@ static void dhcp6_parse_options(uchar *rx_pkt, unsigned int len) case DHCP6_OPTION_OPT_BOOTFILE_PARAM: if (IS_ENABLED(CONFIG_DHCP6_PXE_DHCP_OPTION)) { debug("DHCP6_OPTION_OPT_BOOTFILE_PARAM FOUND\n"); + + if (option_len < sizeof(u16)) { + debug("Invalid BOOTFILE_PARAM option length\n"); + break; + } + /* if CONFIG_DHCP6_PXE_DHCP_OPTION is set the PXE config file path * is contained in the first OPT_BOOTFILE_PARAM argument */ @@ -419,6 +442,10 @@ static void dhcp6_parse_options(uchar *rx_pkt, unsigned int len) break; case DHCP6_OPTION_PREFERENCE: debug("DHCP6_OPTION_PREFERENCE FOUND\n"); + if (option_len != 1) { + debug("Invalid preference option length\n"); + break; + } sm_params.rx_status.preference = *option_ptr; break; default: |
