On 15/05/2026 18:53, Francois Berder wrote:
> 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]>
> ---
>  net/dhcpv6.c | 27 +++++++++++++++++++++++++++
>  1 file changed, 27 insertions(+)
> 
> 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:

Ackeded-by: Jerome Forissier <[email protected]>

...and added to the net queue. Thanks!

-- 
Jerome

Reply via email to