This is an automated email from the ASF dual-hosted git repository.

xiaoxiang781216 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git


The following commit(s) were added to refs/heads/master by this push:
     new 58db158fcd1 net/icmpv6: validate router advertisement option bounds
58db158fcd1 is described below

commit 58db158fcd1c04efd29a1a530f0298d8d56cfa8d
Author: chao an <[email protected]>
AuthorDate: Thu Oct 22 22:48:26 2026 +0800

    net/icmpv6: validate router advertisement option bounds
    
    The Router Advertisement parser validated the total option area length,
    but individual option handlers trusted each option's claimed length before
    reading fixed fields.  A malformed on-link RA could place a truncated
    option at the end of the option area and make the PREFIX, MTU, source
    link-layer address, or RDNSS handler read past the received packet buffer.
    
    Validate that each option header is present, reject zero-length options,
    and ensure the claimed option length stays within the remaining option
    area before dispatching.  Also validate the fixed fields required by each
    handler before reading them.
    
    Signed-off-by: chao an <[email protected]>
---
 net/icmpv6/icmpv6_input.c | 52 +++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 43 insertions(+), 9 deletions(-)

diff --git a/net/icmpv6/icmpv6_input.c b/net/icmpv6/icmpv6_input.c
index f7572e8d8a3..799bafc6140 100644
--- a/net/icmpv6/icmpv6_input.c
+++ b/net/icmpv6/icmpv6_input.c
@@ -26,6 +26,7 @@
 
 #include <nuttx/config.h>
 
+#include <stddef.h>
 #include <stdint.h>
 #include <string.h>
 #include <nuttx/debug.h>
@@ -425,8 +426,15 @@ void icmpv6_input(FAR struct net_driver_s *dev, unsigned 
int iplen)
 
         for (ndx = 0; ndx < optlen; )
           {
-           FAR struct icmpv6_generic_s *opt =
-                                (FAR struct icmpv6_generic_s *)&options[ndx];
+            FAR struct icmpv6_generic_s *opt;
+            uint16_t optbytes;
+
+            if (optlen - ndx < 2)
+              {
+                goto icmpv6_drop_packet;
+              }
+
+            opt = (FAR struct icmpv6_generic_s *)&options[ndx];
 
             /* RFC 4861 4.6: "The value 0 is invalid. Nodes MUST silently
              * discard a packet that contains an option with length zero."
@@ -437,12 +445,24 @@ void icmpv6_input(FAR struct net_driver_s *dev, unsigned 
int iplen)
                 goto icmpv6_drop_packet;
               }
 
+            optbytes = opt->optlen << 3;
+            if (optbytes > optlen - ndx)
+              {
+                goto icmpv6_drop_packet;
+              }
+
             switch (opt->opttype)
               {
                 case ICMPv6_OPT_SRCLLADDR:
                   {
                     FAR struct icmpv6_srclladdr_s *sllopt =
                                       (FAR struct icmpv6_srclladdr_s *)opt;
+
+                    if (optbytes < sizeof(*sllopt))
+                      {
+                        goto icmpv6_drop_packet;
+                      }
+
                     neighbor_add(dev, ipv6->srcipaddr, sllopt->srclladdr);
                   }
                   break;
@@ -452,7 +472,12 @@ void icmpv6_input(FAR struct net_driver_s *dev, unsigned 
int iplen)
                     FAR struct icmpv6_prefixinfo_s *prefixopt =
                                       (FAR struct icmpv6_prefixinfo_s *)opt;
 
-                   /* Is the "A" flag set? */
+                    if (optbytes < sizeof(*prefixopt))
+                      {
+                        goto icmpv6_drop_packet;
+                      }
+
+                    /* Is the "A" flag set? */
 
                     if ((prefixopt->flags & ICMPv6_PRFX_FLAG_A) != 0)
                       {
@@ -469,11 +494,11 @@ void icmpv6_input(FAR struct net_driver_s *dev, unsigned 
int iplen)
                                           ipv6->srcipaddr);
                       }
 
-                      /* Notify any waiting threads */
+                    /* Notify any waiting threads */
 
-                      icmpv6_rnotify(dev, (adv->flags & ICMPv6_RADV_FLAG_M) ?
-                                          -EADDRNOTAVAIL : OK);
-                      prefix = true;
+                    icmpv6_rnotify(dev, (adv->flags & ICMPv6_RADV_FLAG_M) ?
+                                        -EADDRNOTAVAIL : OK);
+                    prefix = true;
                   }
                   break;
 
@@ -481,6 +506,12 @@ void icmpv6_input(FAR struct net_driver_s *dev, unsigned 
int iplen)
                   {
                     FAR struct icmpv6_mtu_s *mtuopt =
                                         (FAR struct icmpv6_mtu_s *)opt;
+
+                    if (optbytes < sizeof(*mtuopt))
+                      {
+                        goto icmpv6_drop_packet;
+                      }
+
                     dev->d_pktsize = MIN(dev->d_pktsize,
                         NTOHS(mtuopt->mtu[1]) + dev->d_llhdrlen);
                   }
@@ -496,7 +527,10 @@ void icmpv6_input(FAR struct net_driver_s *dev, unsigned 
int iplen)
                     int nservers;
                     int ret;
 
-                    if (rdnss->optlen < 3)
+                    if (rdnss->optlen < 3 ||
+                        optbytes < offsetof(struct icmpv6_rdnss_s,
+                                            servers) +
+                                   sizeof(struct in6_addr))
                       {
                         nerr("rdnss error length %d\n", rdnss->optlen);
                         break;
@@ -536,7 +570,7 @@ void icmpv6_input(FAR struct net_driver_s *dev, unsigned 
int iplen)
 
             /* Skip to the next option (units of octets) */
 
-            ndx += (opt->optlen << 3);
+            ndx += optbytes;
           }
 
         if (prefix)

Reply via email to