AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/07/19 14:19 flow patch-triage
0m Args:
null

Results:
{
  "EnableConfigs": [
    "BRIDGE",
    "IPV6"
  ],
  "FocusSymbols": [
    "ndisc_check_ns_na",
    "br_is_nd_neigh_msg",
    "br_nd_send"
  ],
  "Reasoning": "The patch introduces a new function `ndisc_check_ns_na` to validate IPv6 Neighbor Solicitation and Advertisement packets, and updates the bridge ND proxy code to use it. This is functional core networking logic that processes untrusted network packets and should be fuzzed.",
  "WorthFuzzing": true
}

1/1 2026/07/19 14:19 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit deeea621825273aeb596fb2216ee4a1d0542b3b0\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Sun Jul 19 14:19:23 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/include/net/ndisc.h b/include/net/ndisc.h\nindex 3da1a6f8d3f920..9e5379ad2d8e00 100644\n--- a/include/net/ndisc.h\n+++ b/include/net/ndisc.h\n@@ -430,6 +430,8 @@ void ndisc_update(const struct net_device *dev, struct neighbour *neigh,\n \t\t  const u8 *lladdr, u8 new, u32 flags, u8 icmp6_type,\n \t\t  struct ndisc_options *ndopts);\n \n+int ndisc_check_ns_na(struct sk_buff *skb);\n+\n /*\n  *\tIGMP\n  */\ndiff --git a/net/bridge/br_arp_nd_proxy.c b/net/bridge/br_arp_nd_proxy.c\nindex 23eb6931a2b4ad..7863beae0c555d 100644\n--- a/net/bridge/br_arp_nd_proxy.c\n+++ b/net/bridge/br_arp_nd_proxy.c\n@@ -19,6 +19,7 @@\n #include \u003cnet/addrconf.h\u003e\n #if IS_ENABLED(CONFIG_IPV6)\n #include \u003cnet/ip6_checksum.h\u003e\n+#include \u003cnet/ndisc.h\u003e\n #endif\n \n #include \"br_private.h\"\n@@ -234,21 +235,15 @@ void br_do_proxy_suppress_arp(struct sk_buff *skb, struct net_bridge *br,\n #endif\n \n #if IS_ENABLED(CONFIG_IPV6)\n-struct nd_msg *br_is_nd_neigh_msg(const struct sk_buff *skb, struct nd_msg *msg)\n+struct nd_msg *br_is_nd_neigh_msg(struct sk_buff *skb)\n {\n-\tstruct nd_msg *m;\n-\n-\tm = skb_header_pointer(skb, skb_network_offset(skb) +\n-\t\t\t       sizeof(struct ipv6hdr), sizeof(*msg), msg);\n-\tif (!m)\n+\tif (ndisc_check_ns_na(skb))\n \t\treturn NULL;\n \n-\tif (m-\u003eicmph.icmp6_code != 0 ||\n-\t    (m-\u003eicmph.icmp6_type != NDISC_NEIGHBOUR_SOLICITATION \u0026\u0026\n-\t     m-\u003eicmph.icmp6_type != NDISC_NEIGHBOUR_ADVERTISEMENT))\n+\tif (skb_linearize(skb))\n \t\treturn NULL;\n \n-\treturn m;\n+\treturn (struct nd_msg *)skb_transport_header(skb);\n }\n \n static void br_nd_send(struct net_bridge *br, struct net_bridge_port *p,\n@@ -257,17 +252,18 @@ static void br_nd_send(struct net_bridge *br, struct net_bridge_port *p,\n {\n \tstruct net_device *dev = request-\u003edev;\n \tstruct net_bridge_vlan_group *vg;\n+\tstruct ndisc_options ndopts;\n \tstruct nd_msg *na, *ns;\n \tstruct sk_buff *reply;\n \tstruct ipv6hdr *pip6;\n \tint na_olen = 8; /* opt hdr + ETH_ALEN for target */\n \tint ns_olen;\n-\tint i, len;\n \tu8 *daddr;\n \tbool dad;\n \tu16 pvid;\n+\tint len;\n \n-\tif (!dev || skb_linearize(request))\n+\tif (!dev)\n \t\treturn;\n \n \tlen = LL_RESERVED_SPACE(dev) + sizeof(struct ipv6hdr) +\n@@ -284,23 +280,23 @@ static void br_nd_send(struct net_bridge *br, struct net_bridge_port *p,\n \tskb_set_mac_header(reply, 0);\n \n \tdaddr = eth_hdr(request)-\u003eh_source;\n-\tns = (struct nd_msg *)(skb_network_header(request) +\n-\t\t\t       sizeof(struct ipv6hdr));\n-\n-\t/* Do we need option processing ? */\n-\tns_olen = request-\u003elen - (skb_network_offset(request) +\n-\t\t\t\t  sizeof(struct ipv6hdr)) - sizeof(*ns);\n-\tfor (i = 0; i \u003c ns_olen - 1; i += (ns-\u003eopt[i + 1] \u003c\u003c 3)) {\n-\t\tif (!ns-\u003eopt[i + 1] || i + (ns-\u003eopt[i + 1] \u003c\u003c 3) \u003e ns_olen) {\n-\t\t\tkfree_skb(reply);\n-\t\t\treturn;\n-\t\t}\n-\t\tif (ns-\u003eopt[i] == ND_OPT_SOURCE_LL_ADDR) {\n-\t\t\tif ((ns-\u003eopt[i + 1] \u003c\u003c 3) \u003e=\n-\t\t\t    sizeof(struct nd_opt_hdr) + ETH_ALEN)\n-\t\t\t\tdaddr = ns-\u003eopt + i + sizeof(struct nd_opt_hdr);\n-\t\t\tbreak;\n-\t\t}\n+\tns = (struct nd_msg *)skb_transport_header(request);\n+\n+\t/* Derive the option length from the IPv6 payload length so that any\n+\t * trailing L2 padding in the skb is not parsed as ND options.\n+\t */\n+\tns_olen = ntohs(ipv6_hdr(request)-\u003epayload_len) - sizeof(*ns);\n+\tif (!ndisc_parse_options(dev, ns-\u003eopt, ns_olen, \u0026ndopts)) {\n+\t\tkfree_skb(reply);\n+\t\treturn;\n+\t}\n+\n+\tif (ndopts.nd_opts_src_lladdr) {\n+\t\tu8 *lladdr;\n+\n+\t\tlladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr, dev);\n+\t\tif (lladdr)\n+\t\t\tdaddr = lladdr;\n \t}\n \n \tdad = ipv6_addr_any(\u0026ipv6_hdr(request)-\u003esaddr);\ndiff --git a/net/bridge/br_device.c b/net/bridge/br_device.c\nindex e7f343ab22d379..ff55dab736326b 100644\n--- a/net/bridge/br_device.c\n+++ b/net/bridge/br_device.c\n@@ -80,9 +80,9 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev)\n \t\t   pskb_may_pull(skb, sizeof(struct ipv6hdr) +\n \t\t\t\t sizeof(struct nd_msg)) \u0026\u0026\n \t\t   ipv6_hdr(skb)-\u003enexthdr == IPPROTO_ICMPV6) {\n-\t\t\tstruct nd_msg *msg, _msg;\n+\t\t\tstruct nd_msg *msg;\n \n-\t\t\tmsg = br_is_nd_neigh_msg(skb, \u0026_msg);\n+\t\t\tmsg = br_is_nd_neigh_msg(skb);\n \t\t\tif (msg)\n \t\t\t\tbr_do_suppress_nd(skb, br, vid, NULL, msg);\n \t}\ndiff --git a/net/bridge/br_input.c b/net/bridge/br_input.c\nindex ddb8f002a40e82..d87a5f9fa92b70 100644\n--- a/net/bridge/br_input.c\n+++ b/net/bridge/br_input.c\n@@ -176,9 +176,9 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb\n \t\t   pskb_may_pull(skb, sizeof(struct ipv6hdr) +\n \t\t\t\t sizeof(struct nd_msg)) \u0026\u0026\n \t\t   ipv6_hdr(skb)-\u003enexthdr == IPPROTO_ICMPV6) {\n-\t\t\tstruct nd_msg *msg, _msg;\n+\t\t\tstruct nd_msg *msg;\n \n-\t\t\tmsg = br_is_nd_neigh_msg(skb, \u0026_msg);\n+\t\t\tmsg = br_is_nd_neigh_msg(skb);\n \t\t\tif (msg)\n \t\t\t\tbr_do_suppress_nd(skb, br, vid, p, msg);\n \t}\ndiff --git a/net/bridge/br_private.h b/net/bridge/br_private.h\nindex d55ea9516e3e36..16ed09ebffc3cf 100644\n--- a/net/bridge/br_private.h\n+++ b/net/bridge/br_private.h\n@@ -2361,7 +2361,7 @@ void br_do_proxy_suppress_arp(struct sk_buff *skb, struct net_bridge *br,\n \t\t\t      u16 vid, struct net_bridge_port *p);\n void br_do_suppress_nd(struct sk_buff *skb, struct net_bridge *br,\n \t\t       u16 vid, struct net_bridge_port *p, struct nd_msg *msg);\n-struct nd_msg *br_is_nd_neigh_msg(const struct sk_buff *skb, struct nd_msg *m);\n+struct nd_msg *br_is_nd_neigh_msg(struct sk_buff *skb);\n bool br_is_neigh_suppress_enabled(const struct net_bridge_port *p, u16 vid);\n bool br_is_neigh_forward_grat_enabled(const struct net_bridge_port *p, u16 vid);\n #endif\ndiff --git a/net/ipv6/Makefile b/net/ipv6/Makefile\nindex 5b0cd648802124..cf5e01f83ce3d0 100644\n--- a/net/ipv6/Makefile\n+++ b/net/ipv6/Makefile\n@@ -51,7 +51,7 @@ obj-$(subst m,y,$(CONFIG_IPV6)) += inet6_hashtables.o\n \n ifneq ($(CONFIG_IPV6),)\n obj-$(CONFIG_NET_UDP_TUNNEL) += ip6_udp_tunnel.o\n-obj-y += mcast_snoop.o\n+obj-y += mcast_snoop.o ndisc_snoop.o\n obj-$(CONFIG_TCP_AO) += tcp_ao.o\n endif\n \ndiff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c\nindex f867ec8d3d9051..d8b4588f6bff92 100644\n--- a/net/ipv6/ndisc.c\n+++ b/net/ipv6/ndisc.c\n@@ -283,6 +283,7 @@ struct ndisc_options *ndisc_parse_options(const struct net_device *dev,\n \t}\n \treturn ndopts;\n }\n+EXPORT_SYMBOL(ndisc_parse_options);\n \n int ndisc_mc_map(const struct in6_addr *addr, char *buf, struct net_device *dev, int dir)\n {\ndiff --git a/net/ipv6/ndisc_snoop.c b/net/ipv6/ndisc_snoop.c\nnew file mode 100644\nindex 00000000000000..a93eed27a1f6d7\n--- /dev/null\n+++ b/net/ipv6/ndisc_snoop.c\n@@ -0,0 +1,190 @@\n+// SPDX-License-Identifier: GPL-2.0-only\n+\n+#include \u003clinux/skbuff.h\u003e\n+#include \u003cnet/addrconf.h\u003e\n+#include \u003cnet/ip6_checksum.h\u003e\n+#include \u003cnet/ipv6.h\u003e\n+#include \u003cnet/ndisc.h\u003e\n+\n+static int ndisc_check_ip6hdr(struct sk_buff *skb)\n+{\n+\tconst struct ipv6hdr *ip6h;\n+\tunsigned int offset, len;\n+\n+\toffset = skb_network_offset(skb) + sizeof(*ip6h);\n+\tif (!pskb_may_pull(skb, offset))\n+\t\treturn -EINVAL;\n+\n+\tip6h = ipv6_hdr(skb);\n+\n+\tif (ip6h-\u003eversion != 6)\n+\t\treturn -EINVAL;\n+\n+\tif (ip6h-\u003enexthdr != IPPROTO_ICMPV6)\n+\t\treturn -ENOMSG;\n+\n+\t/* RFC 4861 7.1.1 / 7.1.2: must not have been forwarded by a router */\n+\tif (ip6h-\u003ehop_limit != 255)\n+\t\treturn -EINVAL;\n+\n+\tlen = offset + ntohs(ip6h-\u003epayload_len);\n+\tif (skb-\u003elen \u003c len || len \u003c= offset)\n+\t\treturn -EINVAL;\n+\n+\tskb_set_transport_header(skb, offset);\n+\n+\treturn 0;\n+}\n+\n+static __sum16 ndisc_validate_checksum(struct sk_buff *skb)\n+{\n+\treturn skb_checksum_validate(skb, IPPROTO_ICMPV6, ip6_compute_pseudo);\n+}\n+\n+static int ndisc_check_icmpv6(struct sk_buff *skb)\n+{\n+\tunsigned int len = skb_transport_offset(skb) + sizeof(struct icmp6hdr);\n+\tunsigned int transport_len = ipv6_transport_len(skb);\n+\tstruct sk_buff *skb_chk;\n+\tstruct icmp6hdr *hdr;\n+\n+\tif (!pskb_may_pull(skb, len))\n+\t\treturn -EINVAL;\n+\n+\t/* RFC 4861 7.1.1 / 7.1.2: the ICMPv6 checksum must be valid */\n+\tskb_chk = skb_checksum_trimmed(skb, transport_len,\n+\t\t\t\t       ndisc_validate_checksum);\n+\tif (!skb_chk)\n+\t\treturn -EINVAL;\n+\n+\tif (skb_chk != skb)\n+\t\tkfree_skb(skb_chk);\n+\n+\t/* RFC 4861 7.1.1 / 7.1.2: Code must be 0 */\n+\thdr = (struct icmp6hdr *)skb_transport_header(skb);\n+\tif (hdr-\u003eicmp6_code != 0)\n+\t\treturn -EINVAL;\n+\n+\treturn 0;\n+}\n+\n+static int ndisc_check_options(struct sk_buff *skb, unsigned int opts_len,\n+\t\t\t       bool reject_slla)\n+{\n+\tunsigned int offset = skb_transport_offset(skb) + sizeof(struct nd_msg);\n+\tstruct nd_opt_hdr *opt, _opt;\n+\n+\twhile (opts_len \u003e 0) {\n+\t\tif (opts_len \u003c sizeof(*opt))\n+\t\t\treturn -EINVAL;\n+\n+\t\topt = skb_header_pointer(skb, offset, sizeof(_opt), \u0026_opt);\n+\t\tif (!opt)\n+\t\t\treturn -EINVAL;\n+\n+\t\t/* RFC 4861 7.1.1 / 7.1.2: all option lengths must be \u003e 0 */\n+\t\tif (!opt-\u003end_opt_len)\n+\t\t\treturn -EINVAL;\n+\n+\t\t/* RFC 4861 7.1.1: DAD NS must not contain a source link-layer\n+\t\t * address option\n+\t\t */\n+\t\tif (reject_slla \u0026\u0026 opt-\u003end_opt_type == ND_OPT_SOURCE_LL_ADDR)\n+\t\t\treturn -EINVAL;\n+\n+\t\tif (opt-\u003end_opt_len * 8 \u003e opts_len)\n+\t\t\treturn -EINVAL;\n+\n+\t\toffset += opt-\u003end_opt_len * 8;\n+\t\topts_len -= opt-\u003end_opt_len * 8;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int ndisc_check_nd_msg(struct sk_buff *skb)\n+{\n+\tunsigned int len = skb_transport_offset(skb) + sizeof(struct nd_msg);\n+\tunsigned int transport_len = ipv6_transport_len(skb);\n+\tbool reject_slla = false;\n+\tconst struct nd_msg *msg;\n+\n+\tif (!pskb_may_pull(skb, len))\n+\t\treturn -EINVAL;\n+\n+\t/* RFC 4861 7.1.1 / 7.1.2: ICMP length is at least sizeof(nd_msg) */\n+\tif (transport_len \u003c sizeof(struct nd_msg))\n+\t\treturn -EINVAL;\n+\n+\tmsg = (struct nd_msg *)skb_transport_header(skb);\n+\n+\t/* RFC 4861 7.1.1 / 7.1.2: Target Address must not be a\n+\t * multicast address\n+\t */\n+\tif (ipv6_addr_is_multicast(\u0026msg-\u003etarget))\n+\t\treturn -EINVAL;\n+\n+\tswitch (msg-\u003eicmph.icmp6_type) {\n+\tcase NDISC_NEIGHBOUR_SOLICITATION:\n+\t\tif (ipv6_addr_any(\u0026ipv6_hdr(skb)-\u003esaddr)) {\n+\t\t\t/* RFC 4861 7.1.1: DAD NS destination must be a\n+\t\t\t * solicited-node multicast address\n+\t\t\t */\n+\t\t\tif (!ipv6_addr_is_solict_mult(\u0026ipv6_hdr(skb)-\u003edaddr))\n+\t\t\t\treturn -EINVAL;\n+\t\t\t/* RFC 4861 7.1.1: DAD NS must not contain a source\n+\t\t\t * link-layer address option\n+\t\t\t */\n+\t\t\treject_slla = true;\n+\t\t}\n+\t\tbreak;\n+\tcase NDISC_NEIGHBOUR_ADVERTISEMENT:\n+\t\t/* RFC 4861 7.1.2: Solicited flag must be 0 for\n+\t\t * multicast destinations\n+\t\t */\n+\t\tif (ipv6_addr_is_multicast(\u0026ipv6_hdr(skb)-\u003edaddr) \u0026\u0026\n+\t\t    msg-\u003eicmph.icmp6_solicited)\n+\t\t\treturn -EINVAL;\n+\t\tbreak;\n+\tdefault:\n+\t\treturn -ENODATA;\n+\t}\n+\n+\treturn ndisc_check_options(skb, transport_len - sizeof(struct nd_msg),\n+\t\t\t\t   reject_slla);\n+}\n+\n+/**\n+ * ndisc_check_ns_na - validate an NS/NA packet and set its transport header\n+ * @skb: the skb to validate\n+ *\n+ * Validates an IPv6 packet for compliance with RFC 4861 sections 7.1.1\n+ * (Neighbor Solicitation) and 7.1.2 (Neighbor Advertisement). If valid,\n+ * sets the skb transport header.\n+ *\n+ * Caller needs to set the skb network header.\n+ *\n+ * Return:\n+ * * 0        - valid NS/NA; the skb transport header has been set.\n+ * * -EINVAL  - a broken packet was detected, i.e. it violates some\n+ *              internet standard.\n+ * * -ENOMSG  - IP header validation succeeded but it is not an ICMPv6\n+ *              packet.\n+ * * -ENODATA - IP+ICMPv6 header validation succeeded but it is not a\n+ *              Neighbor Solicitation or Neighbor Advertisement.\n+ */\n+int ndisc_check_ns_na(struct sk_buff *skb)\n+{\n+\tint ret;\n+\n+\tret = ndisc_check_ip6hdr(skb);\n+\tif (ret \u003c 0)\n+\t\treturn ret;\n+\n+\tret = ndisc_check_icmpv6(skb);\n+\tif (ret \u003c 0)\n+\t\treturn ret;\n+\n+\treturn ndisc_check_nd_msg(skb);\n+}\n+EXPORT_SYMBOL(ndisc_check_ns_na);\n"
}

2/1 2026/07/19 14:19 agent patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
{
  "EnableConfigs": [
    "BRIDGE",
    "IPV6"
  ],
  "FocusSymbols": [
    "ndisc_check_ns_na",
    "br_is_nd_neigh_msg",
    "br_nd_send"
  ],
  "Reasoning": "The patch introduces a new function `ndisc_check_ns_na` to validate IPv6 Neighbor Solicitation and Advertisement packets, and updates the bridge ND proxy code to use it. This is functional core networking logic that processes untrusted network packets and should be fuzzed.",
  "WorthFuzzing": true
}

Instruction:
You are an expert Linux kernel maintainer.
Your job is to review a provided patch series and determine
if it makes functional changes to the kernel that should be fuzzed.

IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively
use your code access tools to examine the actual source code and confirm any assumptions.

Return WorthFuzzing=false if the patch only contains:
- Modifications to Documentation/, Kconfig files, or code comments.
- Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints.
- Changes to numeric constants or macros that do not functionally alter execution flow.
- Code paths that are impossible to reach in virtualized environments like GCE or QEMU,
even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim).

If it modifies reachable core kernel logic, drivers, or architectures, use your code search
tools to verify the code can be executed, then return WorthFuzzing=true.

When returning WorthFuzzing=true, you MUST ALSO:
1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols.
   Avoid listing generic hot-path functions to prevent skewed test distributions.
2. Identify any specific CONFIG_ options required to properly test this new/modified feature.
   Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions.
   Do not list too generic configs (we already have them enabled). Only list those that
   specifically cover the modified code. List them in the EnableConfigs output array,
   and DO NOT add a 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4").
Prefer calling several tools at the same time to save round-trips.


Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.

Prompt:
For your convenience, here is the diff of the changes:
commit deeea621825273aeb596fb2216ee4a1d0542b3b0
Author: syz-cluster <triage@syzkaller.com>
Date:   Sun Jul 19 14:19:23 2026 +0000

    syz-cluster: applied patch under review

diff --git a/include/net/ndisc.h b/include/net/ndisc.h
index 3da1a6f8d3f920..9e5379ad2d8e00 100644
--- a/include/net/ndisc.h
+++ b/include/net/ndisc.h
@@ -430,6 +430,8 @@ void ndisc_update(const struct net_device *dev, struct neighbour *neigh,
 		  const u8 *lladdr, u8 new, u32 flags, u8 icmp6_type,
 		  struct ndisc_options *ndopts);
 
+int ndisc_check_ns_na(struct sk_buff *skb);
+
 /*
  *	IGMP
  */
diff --git a/net/bridge/br_arp_nd_proxy.c b/net/bridge/br_arp_nd_proxy.c
index 23eb6931a2b4ad..7863beae0c555d 100644
--- a/net/bridge/br_arp_nd_proxy.c
+++ b/net/bridge/br_arp_nd_proxy.c
@@ -19,6 +19,7 @@
 #include <net/addrconf.h>
 #if IS_ENABLED(CONFIG_IPV6)
 #include <net/ip6_checksum.h>
+#include <net/ndisc.h>
 #endif
 
 #include "br_private.h"
@@ -234,21 +235,15 @@ void br_do_proxy_suppress_arp(struct sk_buff *skb, struct net_bridge *br,
 #endif
 
 #if IS_ENABLED(CONFIG_IPV6)
-struct nd_msg *br_is_nd_neigh_msg(const struct sk_buff *skb, struct nd_msg *msg)
+struct nd_msg *br_is_nd_neigh_msg(struct sk_buff *skb)
 {
-	struct nd_msg *m;
-
-	m = skb_header_pointer(skb, skb_network_offset(skb) +
-			       sizeof(struct ipv6hdr), sizeof(*msg), msg);
-	if (!m)
+	if (ndisc_check_ns_na(skb))
 		return NULL;
 
-	if (m->icmph.icmp6_code != 0 ||
-	    (m->icmph.icmp6_type != NDISC_NEIGHBOUR_SOLICITATION &&
-	     m->icmph.icmp6_type != NDISC_NEIGHBOUR_ADVERTISEMENT))
+	if (skb_linearize(skb))
 		return NULL;
 
-	return m;
+	return (struct nd_msg *)skb_transport_header(skb);
 }
 
 static void br_nd_send(struct net_bridge *br, struct net_bridge_port *p,
@@ -257,17 +252,18 @@ static void br_nd_send(struct net_bridge *br, struct net_bridge_port *p,
 {
 	struct net_device *dev = request->dev;
 	struct net_bridge_vlan_group *vg;
+	struct ndisc_options ndopts;
 	struct nd_msg *na, *ns;
 	struct sk_buff *reply;
 	struct ipv6hdr *pip6;
 	int na_olen = 8; /* opt hdr + ETH_ALEN for target */
 	int ns_olen;
-	int i, len;
 	u8 *daddr;
 	bool dad;
 	u16 pvid;
+	int len;
 
-	if (!dev || skb_linearize(request))
+	if (!dev)
 		return;
 
 	len = LL_RESERVED_SPACE(dev) + sizeof(struct ipv6hdr) +
@@ -284,23 +280,23 @@ static void br_nd_send(struct net_bridge *br, struct net_bridge_port *p,
 	skb_set_mac_header(reply, 0);
 
 	daddr = eth_hdr(request)->h_source;
-	ns = (struct nd_msg *)(skb_network_header(request) +
-			       sizeof(struct ipv6hdr));
-
-	/* Do we need option processing ? */
-	ns_olen = request->len - (skb_network_offset(request) +
-				  sizeof(struct ipv6hdr)) - sizeof(*ns);
-	for (i = 0; i < ns_olen - 1; i += (ns->opt[i + 1] << 3)) {
-		if (!ns->opt[i + 1] || i + (ns->opt[i + 1] << 3) > ns_olen) {
-			kfree_skb(reply);
-			return;
-		}
-		if (ns->opt[i] == ND_OPT_SOURCE_LL_ADDR) {
-			if ((ns->opt[i + 1] << 3) >=
-			    sizeof(struct nd_opt_hdr) + ETH_ALEN)
-				daddr = ns->opt + i + sizeof(struct nd_opt_hdr);
-			break;
-		}
+	ns = (struct nd_msg *)skb_transport_header(request);
+
+	/* Derive the option length from the IPv6 payload length so that any
+	 * trailing L2 padding in the skb is not parsed as ND options.
+	 */
+	ns_olen = ntohs(ipv6_hdr(request)->payload_len) - sizeof(*ns);
+	if (!ndisc_parse_options(dev, ns->opt, ns_olen, &ndopts)) {
+		kfree_skb(reply);
+		return;
+	}
+
+	if (ndopts.nd_opts_src_lladdr) {
+		u8 *lladdr;
+
+		lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr, dev);
+		if (lladdr)
+			daddr = lladdr;
 	}
 
 	dad = ipv6_addr_any(&ipv6_hdr(request)->saddr);
diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
index e7f343ab22d379..ff55dab736326b 100644
--- a/net/bridge/br_device.c
+++ b/net/bridge/br_device.c
@@ -80,9 +80,9 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
 		   pskb_may_pull(skb, sizeof(struct ipv6hdr) +
 				 sizeof(struct nd_msg)) &&
 		   ipv6_hdr(skb)->nexthdr == IPPROTO_ICMPV6) {
-			struct nd_msg *msg, _msg;
+			struct nd_msg *msg;
 
-			msg = br_is_nd_neigh_msg(skb, &_msg);
+			msg = br_is_nd_neigh_msg(skb);
 			if (msg)
 				br_do_suppress_nd(skb, br, vid, NULL, msg);
 	}
diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c
index ddb8f002a40e82..d87a5f9fa92b70 100644
--- a/net/bridge/br_input.c
+++ b/net/bridge/br_input.c
@@ -176,9 +176,9 @@ int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb
 		   pskb_may_pull(skb, sizeof(struct ipv6hdr) +
 				 sizeof(struct nd_msg)) &&
 		   ipv6_hdr(skb)->nexthdr == IPPROTO_ICMPV6) {
-			struct nd_msg *msg, _msg;
+			struct nd_msg *msg;
 
-			msg = br_is_nd_neigh_msg(skb, &_msg);
+			msg = br_is_nd_neigh_msg(skb);
 			if (msg)
 				br_do_suppress_nd(skb, br, vid, p, msg);
 	}
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index d55ea9516e3e36..16ed09ebffc3cf 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -2361,7 +2361,7 @@ void br_do_proxy_suppress_arp(struct sk_buff *skb, struct net_bridge *br,
 			      u16 vid, struct net_bridge_port *p);
 void br_do_suppress_nd(struct sk_buff *skb, struct net_bridge *br,
 		       u16 vid, struct net_bridge_port *p, struct nd_msg *msg);
-struct nd_msg *br_is_nd_neigh_msg(const struct sk_buff *skb, struct nd_msg *m);
+struct nd_msg *br_is_nd_neigh_msg(struct sk_buff *skb);
 bool br_is_neigh_suppress_enabled(const struct net_bridge_port *p, u16 vid);
 bool br_is_neigh_forward_grat_enabled(const struct net_bridge_port *p, u16 vid);
 #endif
diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile
index 5b0cd648802124..cf5e01f83ce3d0 100644
--- a/net/ipv6/Makefile
+++ b/net/ipv6/Makefile
@@ -51,7 +51,7 @@ obj-$(subst m,y,$(CONFIG_IPV6)) += inet6_hashtables.o
 
 ifneq ($(CONFIG_IPV6),)
 obj-$(CONFIG_NET_UDP_TUNNEL) += ip6_udp_tunnel.o
-obj-y += mcast_snoop.o
+obj-y += mcast_snoop.o ndisc_snoop.o
 obj-$(CONFIG_TCP_AO) += tcp_ao.o
 endif
 
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index f867ec8d3d9051..d8b4588f6bff92 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -283,6 +283,7 @@ struct ndisc_options *ndisc_parse_options(const struct net_device *dev,
 	}
 	return ndopts;
 }
+EXPORT_SYMBOL(ndisc_parse_options);
 
 int ndisc_mc_map(const struct in6_addr *addr, char *buf, struct net_device *dev, int dir)
 {
diff --git a/net/ipv6/ndisc_snoop.c b/net/ipv6/ndisc_snoop.c
new file mode 100644
index 00000000000000..a93eed27a1f6d7
--- /dev/null
+++ b/net/ipv6/ndisc_snoop.c
@@ -0,0 +1,190 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/skbuff.h>
+#include <net/addrconf.h>
+#include <net/ip6_checksum.h>
+#include <net/ipv6.h>
+#include <net/ndisc.h>
+
+static int ndisc_check_ip6hdr(struct sk_buff *skb)
+{
+	const struct ipv6hdr *ip6h;
+	unsigned int offset, len;
+
+	offset = skb_network_offset(skb) + sizeof(*ip6h);
+	if (!pskb_may_pull(skb, offset))
+		return -EINVAL;
+
+	ip6h = ipv6_hdr(skb);
+
+	if (ip6h->version != 6)
+		return -EINVAL;
+
+	if (ip6h->nexthdr != IPPROTO_ICMPV6)
+		return -ENOMSG;
+
+	/* RFC 4861 7.1.1 / 7.1.2: must not have been forwarded by a router */
+	if (ip6h->hop_limit != 255)
+		return -EINVAL;
+
+	len = offset + ntohs(ip6h->payload_len);
+	if (skb->len < len || len <= offset)
+		return -EINVAL;
+
+	skb_set_transport_header(skb, offset);
+
+	return 0;
+}
+
+static __sum16 ndisc_validate_checksum(struct sk_buff *skb)
+{
+	return skb_checksum_validate(skb, IPPROTO_ICMPV6, ip6_compute_pseudo);
+}
+
+static int ndisc_check_icmpv6(struct sk_buff *skb)
+{
+	unsigned int len = skb_transport_offset(skb) + sizeof(struct icmp6hdr);
+	unsigned int transport_len = ipv6_transport_len(skb);
+	struct sk_buff *skb_chk;
+	struct icmp6hdr *hdr;
+
+	if (!pskb_may_pull(skb, len))
+		return -EINVAL;
+
+	/* RFC 4861 7.1.1 / 7.1.2: the ICMPv6 checksum must be valid */
+	skb_chk = skb_checksum_trimmed(skb, transport_len,
+				       ndisc_validate_checksum);
+	if (!skb_chk)
+		return -EINVAL;
+
+	if (skb_chk != skb)
+		kfree_skb(skb_chk);
+
+	/* RFC 4861 7.1.1 / 7.1.2: Code must be 0 */
+	hdr = (struct icmp6hdr *)skb_transport_header(skb);
+	if (hdr->icmp6_code != 0)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int ndisc_check_options(struct sk_buff *skb, unsigned int opts_len,
+			       bool reject_slla)
+{
+	unsigned int offset = skb_transport_offset(skb) + sizeof(struct nd_msg);
+	struct nd_opt_hdr *opt, _opt;
+
+	while (opts_len > 0) {
+		if (opts_len < sizeof(*opt))
+			return -EINVAL;
+
+		opt = skb_header_pointer(skb, offset, sizeof(_opt), &_opt);
+		if (!opt)
+			return -EINVAL;
+
+		/* RFC 4861 7.1.1 / 7.1.2: all option lengths must be > 0 */
+		if (!opt->nd_opt_len)
+			return -EINVAL;
+
+		/* RFC 4861 7.1.1: DAD NS must not contain a source link-layer
+		 * address option
+		 */
+		if (reject_slla && opt->nd_opt_type == ND_OPT_SOURCE_LL_ADDR)
+			return -EINVAL;
+
+		if (opt->nd_opt_len * 8 > opts_len)
+			return -EINVAL;
+
+		offset += opt->nd_opt_len * 8;
+		opts_len -= opt->nd_opt_len * 8;
+	}
+
+	return 0;
+}
+
+static int ndisc_check_nd_msg(struct sk_buff *skb)
+{
+	unsigned int len = skb_transport_offset(skb) + sizeof(struct nd_msg);
+	unsigned int transport_len = ipv6_transport_len(skb);
+	bool reject_slla = false;
+	const struct nd_msg *msg;
+
+	if (!pskb_may_pull(skb, len))
+		return -EINVAL;
+
+	/* RFC 4861 7.1.1 / 7.1.2: ICMP length is at least sizeof(nd_msg) */
+	if (transport_len < sizeof(struct nd_msg))
+		return -EINVAL;
+
+	msg = (struct nd_msg *)skb_transport_header(skb);
+
+	/* RFC 4861 7.1.1 / 7.1.2: Target Address must not be a
+	 * multicast address
+	 */
+	if (ipv6_addr_is_multicast(&msg->target))
+		return -EINVAL;
+
+	switch (msg->icmph.icmp6_type) {
+	case NDISC_NEIGHBOUR_SOLICITATION:
+		if (ipv6_addr_any(&ipv6_hdr(skb)->saddr)) {
+			/* RFC 4861 7.1.1: DAD NS destination must be a
+			 * solicited-node multicast address
+			 */
+			if (!ipv6_addr_is_solict_mult(&ipv6_hdr(skb)->daddr))
+				return -EINVAL;
+			/* RFC 4861 7.1.1: DAD NS must not contain a source
+			 * link-layer address option
+			 */
+			reject_slla = true;
+		}
+		break;
+	case NDISC_NEIGHBOUR_ADVERTISEMENT:
+		/* RFC 4861 7.1.2: Solicited flag must be 0 for
+		 * multicast destinations
+		 */
+		if (ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr) &&
+		    msg->icmph.icmp6_solicited)
+			return -EINVAL;
+		break;
+	default:
+		return -ENODATA;
+	}
+
+	return ndisc_check_options(skb, transport_len - sizeof(struct nd_msg),
+				   reject_slla);
+}
+
+/**
+ * ndisc_check_ns_na - validate an NS/NA packet and set its transport header
+ * @skb: the skb to validate
+ *
+ * Validates an IPv6 packet for compliance with RFC 4861 sections 7.1.1
+ * (Neighbor Solicitation) and 7.1.2 (Neighbor Advertisement). If valid,
+ * sets the skb transport header.
+ *
+ * Caller needs to set the skb network header.
+ *
+ * Return:
+ * * 0        - valid NS/NA; the skb transport header has been set.
+ * * -EINVAL  - a broken packet was detected, i.e. it violates some
+ *              internet standard.
+ * * -ENOMSG  - IP header validation succeeded but it is not an ICMPv6
+ *              packet.
+ * * -ENODATA - IP+ICMPv6 header validation succeeded but it is not a
+ *              Neighbor Solicitation or Neighbor Advertisement.
+ */
+int ndisc_check_ns_na(struct sk_buff *skb)
+{
+	int ret;
+
+	ret = ndisc_check_ip6hdr(skb);
+	if (ret < 0)
+		return ret;
+
+	ret = ndisc_check_icmpv6(skb);
+	if (ret < 0)
+		return ret;
+
+	return ndisc_check_nd_msg(skb);
+}
+EXPORT_SYMBOL(ndisc_check_ns_na);

3/2 2026/07/19 14:19 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 6673
							output: 32
							thoughts: 428
						

Thoughts:
**Fuzzing the IPv6 Neighbor Discovery Snooping Code**

Okay, so I've got a patch here that's making changes across several files related to bridging and IPv6 neighbor discovery. Specifically, it's touching `net/bridge/br_arp_nd_proxy.c`, `net/bridge/br_device.c`, `net/bridge/br_input.c`, `net/bridge/br_private.h`, `net/ipv6/Makefile`, `net/ipv6/ndisc.c`, and importantly, *adds* a new file `net/ipv6/ndisc_snoop.c`. This strongly suggests new functionality, and with the changes to the existing files I can assume this is a pretty thorough rework of the way bridge devices handle IPv6 ND packets.

The patch seems to introduce a new validation function, `ndisc_check_ns_na`, which, based on the name, is meant to validate IPv6 Neighbor Solicitation and Neighbor Advertisement packets. Furthermore, the old `br_is_nd_neigh_msg` logic is replaced by this new function. The function `ndisc_parse_options` is also being exported, and used in `br_nd_send`.

This is all functional code, it's reachable via bridge devices, and IPv6 ND packets. Looks like a great candidate for fuzzing. The fact that they've replaced a previous block of code is a green light to start.

Now, let's nail down the configuration requirements. The new code is in `net/ipv6/ndisc_snoop.c`, and that's compiled if `CONFIG_IPV6` is enabled. Let's make sure, looking at `net/ipv6/Makefile`:

```makefile
ifneq ($(CONFIG_IPV6),)
obj-y += mcast_snoop.o ndisc_snoop.o
```

Yep, `ndisc_snoop.o` is being included when `CONFIG_IPV6` is set.

But what about the bridge side? The changes to `br_arp_nd_proxy.c` are key. Looking at that file, I see `#if IS_ENABLED(CONFIG_IPV6)` around the ND proxy logic. That means we absolutely need `CONFIG_BRIDGE` *and* `CONFIG_IPV6` enabled.

To be extra certain, I should check the Makefile in `net/bridge` to make sure `br_arp_nd_proxy.c` is compiled conditionally. I'll just use `read-file` on `net/bridge/Makefile`.



4/2 2026/07/19 14:19 tool read-file
0m Args:
{
  "File": "net/bridge/Makefile",
  "FirstLine": 1,
  "LineCount": 100
}

Results:
{
  "Contents": "   1:\t# SPDX-License-Identifier: GPL-2.0\n   2:\t#\n   3:\t# Makefile for the IEEE 802.1d ethernet bridging layer.\n   4:\t#\n   5:\t\n   6:\tobj-$(CONFIG_BRIDGE) += bridge.o\n   7:\t\n   8:\tbridge-y\t:= br.o br_device.o br_fdb.o br_forward.o br_if.o br_input.o \\\n   9:\t\t\t\tbr_ioctl.o br_stp.o br_stp_bpdu.o \\\n  10:\t\t\t\tbr_stp_if.o br_stp_timer.o br_netlink.o \\\n  11:\t\t\t\tbr_netlink_tunnel.o br_arp_nd_proxy.o\n  12:\t\n  13:\tbridge-$(CONFIG_SYSFS) += br_sysfs_if.o br_sysfs_br.o\n  14:\t\n  15:\tbridge-$(subst m,y,$(CONFIG_BRIDGE_NETFILTER)) += br_nf_core.o\n  16:\t\n  17:\tbr_netfilter-y := br_netfilter_hooks.o\n  18:\tbr_netfilter-$(subst m,y,$(CONFIG_IPV6)) += br_netfilter_ipv6.o\n  19:\tobj-$(CONFIG_BRIDGE_NETFILTER) += br_netfilter.o\n  20:\t\n  21:\tbridge-$(CONFIG_BRIDGE_IGMP_SNOOPING) += br_multicast.o br_mdb.o br_multicast_eht.o\n  22:\t\n  23:\tbridge-$(CONFIG_BRIDGE_VLAN_FILTERING) += br_vlan.o br_vlan_tunnel.o br_vlan_options.o br_mst.o\n  24:\t\n  25:\tbridge-$(CONFIG_NET_SWITCHDEV) += br_switchdev.o\n  26:\t\n  27:\tobj-$(CONFIG_NETFILTER) += netfilter/\n  28:\t\n  29:\tbridge-$(CONFIG_BRIDGE_MRP)\t+= br_mrp_switchdev.o br_mrp.o br_mrp_netlink.o\n  30:\t\n  31:\tbridge-$(CONFIG_BRIDGE_CFM)\t+= br_cfm.o br_cfm_netlink.o\n  32:\t\n  33:\tifdef CONFIG_GCOV_PROFILE_NETFILTER\n  34:\tGCOV_PROFILE_br_nf_core.o := y\n  35:\tGCOV_PROFILE_br_netfilter_hooks.o := y\n  36:\tGCOV_PROFILE_br_netfilter_ipv6.o := y\n  37:\tendif\n"
}

5/2 2026/07/19 14:19 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 8383
							output: 23
							thoughts: 465
						

Thoughts:
**Analyzing the Kernel Configuration for ND Proxying in Bridges**

Okay, let's break this down systematically. My initial observation is that `br_arp_nd_proxy.o` is always built when `CONFIG_BRIDGE` is enabled. That makes sense. Diving into `br_arp_nd_proxy.c`, I see conditional compilation with `#if IS_ENABLED(CONFIG_IPV6)` for the ND proxy functionality. Therefore, we clearly need both `BRIDGE` and `IPV6` configured for this to work as intended.

Now, let's zoom in on the relevant symbols. I had a list starting with `ndisc_check_ns_na`, `br_is_nd_neigh_msg`, `br_nd_send`, `ndisc_check_ip6hdr`, `ndisc_check_icmpv6`, `ndisc_check_options`, and `ndisc_check_nd_msg`. However, wait a minute... I now recall that the `ndisc_check_*` functions, specifically `ndisc_check_ip6hdr`, `ndisc_check_icmpv6`, `ndisc_check_options`, and `ndisc_check_nd_msg` are all static functions within `net/ipv6/ndisc_snoop.c`. So they aren't part of the exposed interface. Therefore, I can safely remove them from the list, because they are not externally linked.

I *can* list `ndisc_check_ns_na` because that's exported. Also, `br_is_nd_neigh_msg` and `br_nd_send` are located in `net/bridge/br_arp_nd_proxy.c`, so those are relevant as well.

To verify my assumptions, let me reconfirm the configuration dependencies. The patch alters `net/ipv6/ndisc_snoop.c`, which, as we know, requires `CONFIG_IPV6`. Additionally, the patch also modifies `net/bridge/br_arp_nd_proxy.c`, and that obviously demands `CONFIG_BRIDGE`. So, my initial assessment stands: `BRIDGE` and `IPV6` are the necessary configurations.

Let's check the function signatures for a second. `br_nd_send` is static, which is good to know, and `br_is_nd_neigh_msg` is not static. That's a good distinction to make. `ndisc_check_ns_na` is definitely exported.

Therefore, for this particular analysis, I will list only the following three symbols:

*   `ndisc_check_ns_na`
*   `br_is_nd_neigh_msg`
*   `br_nd_send`

Finally, let's just quickly double-check that `br_is_nd_neigh_msg` isn't called from anywhere unexpected. That's always a good practice.



6/2 2026/07/19 14:19 tool grepper
0m Args:
{
  "Expression": "br_is_nd_neigh_msg"
}

Results:
{
  "Output": "net/bridge/br_arp_nd_proxy.c=125=void br_do_proxy_suppress_arp(struct sk_buff *skb, struct net_bridge *br,\n--\nnet/bridge/br_arp_nd_proxy.c-237-#if IS_ENABLED(CONFIG_IPV6)\nnet/bridge/br_arp_nd_proxy.c:238:struct nd_msg *br_is_nd_neigh_msg(struct sk_buff *skb)\nnet/bridge/br_arp_nd_proxy.c-239-{\n--\nnet/bridge/br_device.c=30=netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev)\n--\nnet/bridge/br_device.c-84-\nnet/bridge/br_device.c:85:\t\t\tmsg = br_is_nd_neigh_msg(skb);\nnet/bridge/br_device.c-86-\t\t\tif (msg)\n--\nnet/bridge/br_input.c=76=int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb)\n--\nnet/bridge/br_input.c-180-\nnet/bridge/br_input.c:181:\t\t\tmsg = br_is_nd_neigh_msg(skb);\nnet/bridge/br_input.c-182-\t\t\tif (msg)\n--\nnet/bridge/br_private.h=2362=void br_do_suppress_nd(struct sk_buff *skb, struct net_bridge *br,\nnet/bridge/br_private.h-2363-\t\t       u16 vid, struct net_bridge_port *p, struct nd_msg *msg);\nnet/bridge/br_private.h:2364:struct nd_msg *br_is_nd_neigh_msg(struct sk_buff *skb);\nnet/bridge/br_private.h-2365-bool br_is_neigh_suppress_enabled(const struct net_bridge_port *p, u16 vid);\n"
}

7/2 2026/07/19 14:19 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 10034
							output: 19
							thoughts: 44
						

Thoughts:
Okay, here's my interpretation of those thoughts, tailored for an expert audience:

**Analyzing Reachability in the Bridging Code**

Alright, so the call stack has led me here. It's confirmed: the function in question, that's it, I see, I've got it. It's called from both `br_dev_xmit` and `br_handle_frame_finish`. Good, that narrows down the scope of where this functionality is utilized. We've got a definite path, which means this code path is definitely live.

Now, the next logical step to understand this interaction is to examine the `br_nd_send` function. I will check the implementation to confirm its operation and interaction.



8/2 2026/07/19 14:19 tool grepper
0m Args:
{
  "Expression": "br_nd_send"
}

Results:
{
  "Output": "net/bridge/br_arp_nd_proxy.c=238=struct nd_msg *br_is_nd_neigh_msg(struct sk_buff *skb)\n--\nnet/bridge/br_arp_nd_proxy.c-248-\nnet/bridge/br_arp_nd_proxy.c:249:static void br_nd_send(struct net_bridge *br, struct net_bridge_port *p,\nnet/bridge/br_arp_nd_proxy.c-250-\t\t       struct sk_buff *request, struct neighbour *n,\n--\nnet/bridge/br_arp_nd_proxy.c=410=void br_do_suppress_nd(struct sk_buff *skb, struct net_bridge *br,\n--\nnet/bridge/br_arp_nd_proxy.c-483-\t\t\t\tif (vid != 0)\nnet/bridge/br_arp_nd_proxy.c:484:\t\t\t\t\tbr_nd_send(br, p, skb, n,\nnet/bridge/br_arp_nd_proxy.c-485-\t\t\t\t\t\t   skb-\u003evlan_proto,\n--\nnet/bridge/br_arp_nd_proxy.c-487-\t\t\t\telse\nnet/bridge/br_arp_nd_proxy.c:488:\t\t\t\t\tbr_nd_send(br, p, skb, n, 0, 0);\nnet/bridge/br_arp_nd_proxy.c-489-\t\t\t\treplied = true;\n"
}

9/2 2026/07/19 14:19 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 10632
							output: 126
							thoughts: 201
						

Thoughts:
**Fuzzing Rationale: IPv6 Neighbor Discovery Snooping**

Okay, so I'm looking at a patch that's been triggered by `br_do_suppress_nd`. I see it introduces `net/ipv6/ndisc_snoop.c` which includes the new function `ndisc_check_ns_na`. This is interesting; it's a dedicated validation function for IPv6 Neighbor Solicitation (NS) and Advertisement (NA) packets. The patch also touches `br_is_nd_neigh_msg` and `br_nd_send` in `net/bridge/br_arp_nd_proxy.c`, so it's integrating this new validation logic into the bridge ND proxy.

Given that this involves new, core networking code that's going to be handling potentially untrusted network traffic, my assessment is that this is *definitely* WorthFuzzing. The potential attack surface is clear: malformed NS/NA packets could lead to various issues. My focus will be on `ndisc_check_ns_na`, `br_is_nd_neigh_msg`, and `br_nd_send`. The fuzzing configuration requires that both `BRIDGE` and `IPV6` are enabled, given the functional scope of the patch and the nature of the network protocols. It's a prime target for finding subtle memory corruption or logic errors related to IPv6 ND proxy behaviour.



10/2 2026/07/19 14:19 tool set-results
0m Args:
{
  "EnableConfigs": [
    "BRIDGE",
    "IPV6"
  ],
  "FocusSymbols": [
    "ndisc_check_ns_na",
    "br_is_nd_neigh_msg",
    "br_nd_send"
  ],
  "Reasoning": "The patch introduces a new function `ndisc_check_ns_na` to validate IPv6 Neighbor Solicitation and Advertisement packets, and updates the bridge ND proxy code to use it. This is functional core networking logic that processes untrusted network packets and should be fuzzed.",
  "WorthFuzzing": true
}

Results:
{
  "EnableConfigs": [
    "BRIDGE",
    "IPV6"
  ],
  "FocusSymbols": [
    "ndisc_check_ns_na",
    "br_is_nd_neigh_msg",
    "br_nd_send"
  ],
  "Reasoning": "The patch introduces a new function `ndisc_check_ns_na` to validate IPv6 Neighbor Solicitation and Advertisement packets, and updates the bridge ND proxy code to use it. This is functional core networking logic that processes untrusted network packets and should be fuzzed.",
  "WorthFuzzing": true
}

LLM Calls Summary:
Total Calls Total Tokens Avg Tokens Total Duration (Seconds) Avg Duration (Seconds)
Tool Calls Summary:
Total Calls Total Duration (Seconds) Avg Duration (Seconds)