{
  "root_cause": "bond_setup_by_slave() copies a non-Ethernet slave's header_ops verbatim onto the bond net device (bond_dev->header_ops = slave_dev->header_ops). When the network stack later calls dev_hard_header(bond_dev), the slave's header-create callback runs with dev=bond_dev and dereferences netdev_priv(dev) expecting the slave's private struct (struct ip_tunnel for ipgre, struct ip6_tnl for ip6gre) but receives struct bonding -- a type confusion. The confused hlen field (read from the wrong offset inside struct bonding) can hold a sign-bit-set value, making needed = t->hlen + sizeof(*iph|*ipv6h) overflow to a negative int; the unsigned skb_headroom(skb) < needed test is then satisfied and pskb_expand_head() is called with a negative nhead, hitting BUG_ON(nhead < 0) and panicking the kernel.",
  "original_trigger": {
    "entry": "enslave an IPv4 GRE tunnel (gre1, created with `ip link add gre1 type gre local 10.0.0.1`, no remote -> ipgre_header_ops assigned) to an active-backup bond (bond1); trigger dev_hard_header(bond1) via AF_PACKET SOCK_DGRAM sendto on bond1",
    "sink": "net/ipv4/ip_gre.c:ipgre_header()",
    "confused_struct": "struct ip_tunnel",
    "confused_field": "ip_tunnel.hlen (offset 160 inside struct bonding on this build)",
    "header_size_constant": "sizeof(struct iphdr) = 20"
  },
  "variant_trigger": {
    "entry": "enslave an IPv6 GRE tunnel (ip6gre1, created with `ip link add ip6gre1 type ip6gre local fd00::1`, NO remote -> ip6gre_header_ops assigned only when ipv6_addr_any(&parms.raddr)) to an active-backup bond (bond1); trigger dev_hard_header(bond1) via AF_PACKET SOCK_DGRAM sendto on bond1",
    "sink": "net/ipv6/ip6_gre.c:ip6gre_header()",
    "confused_struct": "struct ip6_tnl",
    "confused_field": "ip6_tnl.hlen (offset 264 inside struct bonding on this build)",
    "header_size_constant": "sizeof(struct ipv6hdr) = 40"
  },
  "same_root_cause_confidence": "high",
  "same_root_cause_rationale": "Both triggers share the identical root cause: bond_setup_by_slave() blindly inheriting a non-Ethernet slave's header_ops, causing dev_hard_header(bond) to invoke the slave's header-create callback with dev=bond so netdev_priv() returns struct bonding reinterpreted as the tunnel struct. The only differences are (a) the slave tunnel type (IPv4 GRE vs IPv6 GRE), (b) the sink function/file (ipgre_header/ip_gre.c vs ip6gre_header/ip6_gre.c), (c) the confused private struct (struct ip_tunnel vs struct ip6_tnl), (d) the confused-field offset (160 vs 264), and (e) the header-size constant added to hlen (20 vs 40). The BUG_ON(nhead<0) sink in pskb_expand_head is identical.",
  "same_surface_confidence": "medium",
  "same_surface_rationale": "The entry surface is identical (bond_setup_by_slave header_ops inheritance + AF_PACKET SOCK_DGRAM -> dev_hard_header -> *gre_header -> pskb_expand_head -> BUG_ON). The sink surface differs: a different function in a different file (ip6gre_header in net/ipv6/ip6_gre.c vs ipgre_header in net/ipv4/ip_gre.c) reading a different confused struct. Same trust boundary (privileged local user with CAP_NET_ADMIN).",
  "differential_evidence": {
    "vulnerable_kernel": "ip6gre_header: dev=bond1 hlen=-1776655412 needed=-1776655372 headroom=288 -> kernel BUG at net/core/skbuff.c:2306! -> Oops: invalid opcode -> RIP: 0010:pskb_expand_head -> Call Trace: ip6gre_header+0x14a/0x430 [ip6_gre] -> Kernel panic",
    "fixed_kernel": "ip6gre_header: dev=ip6gre1 hlen=4 needed=44 headroom=288 (bond_header_ops delegated to the slave ip6gre1 device) -> RESULT: NOT VULNERABLE (no crash)"
  },
  "fix_equivalence": "The upstream fix (950803f7) introduces bond_header_ops, whose bond_header_create() delegates the active slave's header_ops->create to the slave's own device. This is GENERIC: it does not hardcode ipgre, so it covers ip6gre_header identically -- the slave device passed to ip6gre_header is ip6gre1, whose netdev_priv() is the correct struct ip6_tnl (hlen=4). The fix covers both the original ipgre and this ip6gre variant; the variant is NOT a bypass."
}
