We have identified a 100% reproducible kernel panic in the bridge driver (
net/bridge/br_arp_nd_proxy.c) on kernels up to at least 5.15 and 6.x.
*Description:* The crash occurs when the kernel is booted with
ipv6.disable=1 and a bridge port has neigh_suppress enabled. When the
bridge receives an ICMPv6 Neighbor Solicitation, it attempts to perform a
lookup in the neighbor table via ipv6_stub->nd_tbl. Because IPv6 was
disabled at boot, nd_tbl is NULL, leading to an immediate dereference panic.
*Reproduction Matrix:*
-
ipv6.disable=1 + neigh_suppress=on -> *PANIC*
-
ipv6.disable=1 + neigh_suppress=off -> Stable
-
IPv6 enabled at boot + neigh_suppress=on -> Stable (even if
disable_ipv6=1 via sysctl)
*Stack Trace Summary:* The RIP usually points to neigh_lookup+0x16.
*Proposed Fix:* A NULL pointer check for ipv6_stub->nd_tbl in
br_do_suppress_nd() before the neigh_lookup call would prevent the panic.
I have attached a bash script that sets up a full-stack topology (Bridge +
VXLAN + Guest VM + Namespace) to reproduce the crash.
Best regards,
Guruprasad
On 2/26/26 12:14 AM, Guruprasad C P wrote:
We have identified a 100% reproducible kernel panic in the bridge driver
(|net/bridge/br_arp_nd_proxy.c|) on kernels up to at least 5.15 and 6.x.
*Description:* The crash occurs when the kernel is booted with |
ipv6.disable=1| and a bridge port has |neigh_suppress| enabled. When the
bridge receives an ICMPv6 Neighbor Solicitation, it attempts to perform
a lookup in the neighbor table via |ipv6_stub->nd_tbl|. Because IPv6 was
disabled at boot, |nd_tbl| is NULL, leading to an immediate dereference
panic.
This is a nice catch. The key here is that inet6_init() is exiting
before ndisc_init() is called.
Hm I wonder if there are more parts of the kernel where this might be a
problem. I noticed several uses of ipv6_stub->nd_tbl that relies on
IS_ENABLED(CONFIG_IPV6) check..
I am writing a patch for this.
Thank you!
On 2/26/26 4:11 PM, Fernando Fernandez Mancera wrote:
On 2/26/26 12:14 AM, Guruprasad C P wrote:
quoted
We have identified a 100% reproducible kernel panic in the bridge
driver (|net/bridge/br_arp_nd_proxy.c|) on kernels up to at least 5.15
and 6.x.
*Description:* The crash occurs when the kernel is booted with |
ipv6.disable=1| and a bridge port has |neigh_suppress| enabled. When
the bridge receives an ICMPv6 Neighbor Solicitation, it attempts to
perform a lookup in the neighbor table via |ipv6_stub->nd_tbl|.
Because IPv6 was disabled at boot, |nd_tbl| is NULL, leading to an
immediate dereference panic.
This is a nice catch. The key here is that inet6_init() is exiting
before ndisc_init() is called.
Hm I wonder if there are more parts of the kernel where this might be a
problem. I noticed several uses of ipv6_stub->nd_tbl that relies on
IS_ENABLED(CONFIG_IPV6) check..
I am writing a patch for this.
Thank you!
Hi,
I have sent a patch fixing this [1].
FWIW, I have created a much simpler reproducer that triggers this
immediately. Check it out below.
[1] https://lore.kernel.org/netdev/20260226234059.19402-1-fmancera@suse.de/
$ cat nd_crash.sh
#!/bin/bash
if [ "$EUID" -ne 0 ]; then
echo "Root is required"
exit 1
fi
ip link add name br-crash type bridge
ip link set dev br-crash up
ip link add name dummy-supp type dummy
ip link set dev dummy-supp master br-crash
ip link set dev dummy-supp up
if ! ip link set dev dummy-supp type bridge_slave neigh_suppress 1
2>/dev/null; then
bridge link set dev dummy-supp neigh_suppress on
fi
ip link add veth-in type veth peer name veth-out
ip link set dev veth-in master br-crash
ip link set dev veth-in up
ip link set dev veth-out up
python3 - <<EOF
from scapy.all import Ether, IPv6, ICMPv6ND_NS, sendp
import time
time.sleep(1)
eth = Ether(dst="33:33:ff:00:00:01", src="00:11:22:33:44:55", type=0x86dd)
ip6 = IPv6(src="fe80::1", dst="ff02::1:ff00:1")
nd = ICMPv6ND_NS(tgt="fe80::2")
packet = eth / ip6 / nd
sendp(packet, iface="veth-out", verbose=False)
EOF
ip link del dev br-crash 2>/dev/null
ip link del dev veth-in 2>/dev/null
ip link del dev dummy-supp 2>/dev/null