[net-next:master] general protection fault in __nla_put()
From: <hidden>
Date: 2012-07-05 13:52:50
Hi David,
This is a very reproducible bug, introduced by the following commit
tree: git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next.git master
head: 36bdbcae2fa2a6dfa99344d4190fcea0aa7b7c25
commit: 97cac0821af4474ec4ba3a9e7a36b98ed9b6db88 [96/99] ipv6: Store route neighbour in rt6_info struct.
[ 80.878774] IPv6: ADDRCONF(NETDEV_UP): bond0: link is not ready
[ 80.880350] 8021q: adding VLAN 0 to HW filter on device bond0
[ 80.882116] general protection fault: 0000 [#1] SMP DEBUG_PAGEALLOC
[ 80.883606] CPU 0
[ 80.884216] Modules linked in:
[ 80.884296]
[ 80.884296] Pid: 1, comm: swapper/0 Not tainted 3.5.0-rc4+ #16 Bochs Bochs
[ 80.884296] RIP: 0010:[<ffffffff816ce77e>] [<ffffffff816ce77e>] __nla_put+0x1f/0x26
[ 80.884296] RSP: 0018:ffff88001d819910 EFLAGS: 00010282
[ 80.884296] RAX: ffff880012167c2c RBX: 5a5a5a5a5a5a5d3a RCX: 0000000000000010
[ 80.884296] RDX: ffff880012167c3c RSI: 5a5a5a5a5a5a5d3a RDI: ffff880012167c2c
[ 80.884296] RBP: ffff88001d819920 R08: ffff880012167c28 R09: ffff880012167bf0
[ 80.884296] R10: ffffffff840401a0 R11: ffffffff840401a0 R12: 0000000000000010
[ 80.884296] R13: ffff8800120c3f00 R14: 0000000000000000 R15: 0000000000000000
[ 80.884296] FS: 0000000000000000(0000) GS:ffff88001f200000(0000) knlGS:0000000000000000
[ 80.884296] CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b
[ 80.884296] CR2: 0000000000000000 CR3: 0000000004014000 CR4: 00000000000006f0
[ 80.884296] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
[ 80.884296] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400
[ 80.884296] Process swapper/0 (pid: 1, threadinfo ffff88001d818000, task ffff88001d814040)
[ 80.884296] Stack:
[ 80.884296] ffff8800120c3f00 ffff880012168e70 ffff88001d819960 ffffffff816ce7bf
[ 80.884296] ffff88001d819950 5a5a5a5a5a5a5d3a ffffffff00000010 ffff880000000005
[ 80.884296] ffff88001d819960 ffff880012167bf0 ffff88001d819a00 ffffffff82afa9b0
[ 80.884296] Call Trace:
[ 80.884296] [<ffffffff816ce7bf>] nla_put+0x3a/0x4a
[ 80.884296] [<ffffffff82afa9b0>] rt6_fill_node.constprop.35+0x325/0x486
[ 80.884296] [<ffffffff8117598d>] ? __kmalloc_node_track_caller+0x35/0x3e
[ 80.884296] [<ffffffff829d69cb>] ? __alloc_skb+0xaf/0x15a
[ 80.884296] [<ffffffff82afaf82>] inet6_rt_notify+0xb2/0x130
[ 80.884296] [<ffffffff82afc56f>] fib6_add+0x359/0x55a
[ 80.884296] [<ffffffff810dbeb0>] ? lock_acquired+0x1e4/0x219
[ 80.884296] [<ffffffff82af7ae5>] __ip6_ins_rt+0x3c/0x57
[ 80.884296] [<ffffffff82af9c10>] ip6_route_add+0x49e/0x58d
[ 80.884296] [<ffffffff82af0bcc>] addrconf_add_mroute+0x82/0x98
[ 80.884296] [<ffffffff82af24a8>] addrconf_add_dev+0x61/0x83
[ 80.884296] [<ffffffff82af429a>] addrconf_dev_config+0x78/0xd4
[ 80.884296] [<ffffffff82af5fb1>] addrconf_notify+0x164/0x283
[ 80.884296] [<ffffffff82e4fcc2>] notifier_call_chain+0x65/0x95
[ 80.884296] [<ffffffff810b54ab>] __raw_notifier_call_chain+0xe/0x10
[ 80.884296] [<ffffffff810b54c1>] raw_notifier_call_chain+0x14/0x16
[ 80.884296] [<ffffffff829df1c8>] call_netdevice_notifiers+0x4a/0x4f
[ 80.884296] [<ffffffff829e4934>] __dev_notify_flags+0x37/0x5b
[ 80.884296] [<ffffffff829e49a0>] dev_change_flags+0x48/0x54
[ 80.884296] [<ffffffff8467e322>] ip_auto_config.part.10+0x14a/0xd52
[ 80.884296] [<ffffffff82e4c452>] ? _raw_spin_unlock+0x28/0x3b
[ 80.884296] [<ffffffff811da21f>] ? proc_register+0x192/0x1a6
[ 80.884296] [<ffffffff8467c8c6>] ? tcp_v4_init+0x30/0x30
[ 80.884296] [<ffffffff8467ef2a>] ? ip_auto_config.part.10+0xd52/0xd52
[ 80.884296] [<ffffffff8467ef5d>] ip_auto_config+0x33/0x35
[ 80.884296] [<ffffffff81002099>] do_one_initcall+0x7f/0x13a
[ 80.884296] [<ffffffff845f1d0a>] kernel_init+0x141/0x1c5
[ 80.884296] [<ffffffff845f1590>] ? do_early_param+0x8c/0x8c
[ 80.884296] [<ffffffff82e545f4>] kernel_thread_helper+0x4/0x10
[ 80.884296] [<ffffffff82e4c830>] ? retint_restore_args+0x13/0x13
[ 80.884296] [<ffffffff845f1bc9>] ? start_kernel+0x3e7/0x3e7
[ 80.884296] [<ffffffff82e545f0>] ? gs_change+0x13/0x13
[ 80.884296] Code: 4c 89 c0 41 5b 5b 41 5c 41 5d 5d c3 55 48 89 e5 41 54 41 89 d4 53 48 89 cb e8 a2 ff ff ff 48 83 c0 04 49 63 cc 48 89 de 48 89 c7 <f3> a4 5b 41 5c 5d c3 55 48 89 e5 53 48 89 fb 48 83 ec 28 89 55
[ 80.884296] RIP [<ffffffff816ce77e>] __nla_put+0x1f/0x26
[ 80.884296] RSP <ffff88001d819910>
[ 80.964726] ---[ end trace 7265c51d764fcc58 ]---
[ 80.965811] Kernel panic - not syncing: Fatal exception in interrupt
commit 97cac0821af4474ec4ba3a9e7a36b98ed9b6db88
Author: David S. Miller [off-list ref]
Date: Mon Jul 2 22:43:47 2012 -0700
ipv6: Store route neighbour in rt6_info struct.
This makes for a simplified conversion away from dst_get_neighbour*().
All code outside of ipv6 will use neigh lookups via dst_neigh_lookup*().
Signed-off-by: David S. Miller [off-list ref]
diff --git a/include/net/ip6_fib.h b/include/net/ip6_fib.h
index a192f78..0fedbd8 100644
--- a/include/net/ip6_fib.h
+++ b/include/net/ip6_fib.h@@ -86,6 +86,8 @@ struct fib6_table; struct rt6_info { struct dst_entry dst; + struct neighbour *n; + /* * Tail elements of dst_entry (__refcnt etc.) * and these elements (rarely used in hot path) are in
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index c94e4aa..6d9c0ab 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c@@ -88,6 +88,7 @@ static int ip6_finish_output2(struct sk_buff *skb) struct dst_entry *dst = skb_dst(skb); struct net_device *dev = dst->dev; struct neighbour *neigh; + struct rt6_info *rt; skb->protocol = htons(ETH_P_IPV6); skb->dev = dev;
@@ -123,7 +124,8 @@ static int ip6_finish_output2(struct sk_buff *skb) } rcu_read_lock(); - neigh = dst_get_neighbour_noref(dst); + rt = (struct rt6_info *) dst; + neigh = rt->n; if (neigh) { int res = dst_neigh_output(dst, neigh, skb);
@@ -944,6 +946,7 @@ static int ip6_dst_lookup_tail(struct sock *sk, struct net *net = sock_net(sk); #ifdef CONFIG_IPV6_OPTIMISTIC_DAD struct neighbour *n; + struct rt6_info *rt; #endif int err;
@@ -972,7 +975,8 @@ static int ip6_dst_lookup_tail(struct sock *sk, * dst entry of the nexthop router */ rcu_read_lock(); - n = dst_get_neighbour_noref(*dst); + rt = (struct rt6_info *) dst; + n = rt->n; if (n && !(n->nud_state & NUD_VALID)) { struct inet6_ifaddr *ifp; struct flowi6 fl_gw6;
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index 34b2988..ceff71d 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c@@ -155,7 +155,7 @@ static int rt6_bind_neighbour(struct rt6_info *rt, struct net_device *dev) if (IS_ERR(n)) return PTR_ERR(n); } - dst_set_neighbour(&rt->dst, n); + rt->n = n; return 0; }
@@ -285,6 +285,9 @@ static void ip6_dst_destroy(struct dst_entry *dst) struct rt6_info *rt = (struct rt6_info *)dst; struct inet6_dev *idev = rt->rt6i_idev; + if (rt->n) + neigh_release(rt->n); + if (!(rt->dst.flags & DST_HOST)) dst_destroy_metrics_generic(dst);
@@ -335,12 +338,19 @@ static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev, struct net_device *loopback_dev = dev_net(dev)->loopback_dev; - if (dev != loopback_dev && idev && idev->dev == dev) { - struct inet6_dev *loopback_idev = - in6_dev_get(loopback_dev); - if (loopback_idev) { - rt->rt6i_idev = loopback_idev; - in6_dev_put(idev); + if (dev != loopback_dev) { + if (idev && idev->dev == dev) { + struct inet6_dev *loopback_idev = + in6_dev_get(loopback_dev); + if (loopback_idev) { + rt->rt6i_idev = loopback_idev; + in6_dev_put(idev); + } + } + if (rt->n && rt->n->dev == dev) { + rt->n->dev = loopback_dev; + dev_hold(loopback_dev); + dev_put(dev); } } }
@@ -430,7 +440,7 @@ static void rt6_probe(struct rt6_info *rt) * to no more than one per minute. */ rcu_read_lock(); - neigh = rt ? dst_get_neighbour_noref(&rt->dst) : NULL; + neigh = rt ? rt->n : NULL; if (!neigh || (neigh->nud_state & NUD_VALID)) goto out; read_lock_bh(&neigh->lock);
@@ -477,7 +487,7 @@ static inline int rt6_check_neigh(struct rt6_info *rt) int m; rcu_read_lock(); - neigh = dst_get_neighbour_noref(&rt->dst); + neigh = rt->n; if (rt->rt6i_flags & RTF_NONEXTHOP || !(rt->rt6i_flags & RTF_GATEWAY)) m = 1;
@@ -824,7 +834,7 @@ static struct rt6_info *rt6_alloc_clone(struct rt6_info *ort, if (rt) { rt->rt6i_flags |= RTF_CACHE; - dst_set_neighbour(&rt->dst, neigh_clone(dst_get_neighbour_noref_raw(&ort->dst))); + rt->n = neigh_clone(ort->n); } return rt; }
@@ -858,7 +868,7 @@ restart: dst_hold(&rt->dst); read_unlock_bh(&table->tb6_lock); - if (!dst_get_neighbour_noref_raw(&rt->dst) && !(rt->rt6i_flags & RTF_NONEXTHOP)) + if (!rt->n && !(rt->rt6i_flags & RTF_NONEXTHOP)) nrt = rt6_alloc_cow(rt, &fl6->daddr, &fl6->saddr); else if (!(rt->dst.flags & DST_HOST)) nrt = rt6_alloc_clone(rt, &fl6->daddr);
@@ -1178,7 +1188,7 @@ struct dst_entry *icmp6_dst_alloc(struct net_device *dev, rt->dst.flags |= DST_HOST; rt->dst.output = ip6_output; - dst_set_neighbour(&rt->dst, neigh); + rt->n = neigh; atomic_set(&rt->dst.__refcnt, 1); rt->rt6i_dst.addr = fl6->daddr; rt->rt6i_dst.plen = 128;
@@ -1715,7 +1725,7 @@ void rt6_redirect(const struct in6_addr *dest, const struct in6_addr *src, dst_confirm(&rt->dst); /* Duplicate redirect: silently ignore. */ - old_neigh = dst_get_neighbour_noref_raw(&rt->dst); + old_neigh = rt->n; if (neigh == old_neigh) goto out;
@@ -1728,7 +1738,7 @@ void rt6_redirect(const struct in6_addr *dest, const struct in6_addr *src, nrt->rt6i_flags &= ~RTF_GATEWAY; nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key; - dst_set_neighbour(&nrt->dst, neigh_clone(neigh)); + nrt->n = neigh_clone(neigh); if (ip6_ins_rt(nrt)) goto out;
@@ -2442,7 +2452,7 @@ static int rt6_fill_node(struct net *net, goto nla_put_failure; rcu_read_lock(); - n = dst_get_neighbour_noref(&rt->dst); + n = rt->n; if (n) { if (nla_put(skb, RTA_GATEWAY, 16, &n->primary_key) < 0) { rcu_read_unlock();
@@ -2666,7 +2676,7 @@ static int rt6_info_route(struct rt6_info *rt, void *p_arg) seq_puts(m, "00000000000000000000000000000000 00 "); #endif rcu_read_lock(); - n = dst_get_neighbour_noref(&rt->dst); + n = rt->n; if (n) { seq_printf(m, "%pi6", n->primary_key); } else {
diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c
index d749484..bb02038 100644
--- a/net/ipv6/xfrm6_policy.c
+++ b/net/ipv6/xfrm6_policy.c@@ -103,6 +103,7 @@ static int xfrm6_fill_dst(struct xfrm_dst *xdst, struct net_device *dev, /* Sheit... I remember I did this right. Apparently, * it was magically lost, so this code needs audit */ + xdst->u.rt6.n = neigh_clone(rt->n); xdst->u.rt6.rt6i_flags = rt->rt6i_flags & (RTF_ANYCAST | RTF_LOCAL); xdst->u.rt6.rt6i_metric = rt->rt6i_metric;