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)