aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPatrick McHardy <kaber@trash.net>2010-02-08 00:19:03 -0500
committerDavid S. Miller <davem@davemloft.net>2010-02-12 15:06:35 -0500
commit2bec5a369ee79576a3eea2c23863325089785a2c (patch)
tree7a9fd4bce3ece4636b50de6579f95b5bae54264c
parent69a6a0b38a139ccceef32222108caca8a9b0b795 (diff)
ipv6: fib: fix crash when changing large fib while dumping it
When the fib size exceeds what can be dumped in a single skb, the dump is suspended and resumed once the last skb has been received by userspace. When the fib is changed while the dump is suspended, the walker might contain stale pointers, causing a crash when the dump is resumed. BUG: unable to handle kernel NULL pointer dereference at 0000000000000018 IP: [<ffffffffa01bce04>] fib6_walk_continue+0xbb/0x124 [ipv6] PGD 5347a067 PUD 65c7067 PMD 0 Oops: 0000 [#1] PREEMPT SMP ... RIP: 0010:[<ffffffffa01bce04>] [<ffffffffa01bce04>] fib6_walk_continue+0xbb/0x124 [ipv6] ... Call Trace: [<ffffffff8104aca3>] ? mutex_spin_on_owner+0x59/0x71 [<ffffffffa01bd105>] inet6_dump_fib+0x11b/0x1b9 [ipv6] [<ffffffff81371af4>] netlink_dump+0x5b/0x19e [<ffffffff8134f288>] ? consume_skb+0x28/0x2a [<ffffffff81373b69>] netlink_recvmsg+0x1ab/0x2c6 [<ffffffff81372781>] ? netlink_unicast+0xfa/0x151 [<ffffffff813483e0>] __sock_recvmsg+0x6d/0x79 [<ffffffff81348a53>] sock_recvmsg+0xca/0xe3 [<ffffffff81066d4b>] ? autoremove_wake_function+0x0/0x38 [<ffffffff811ed1f8>] ? radix_tree_lookup_slot+0xe/0x10 [<ffffffff810b3ed7>] ? find_get_page+0x90/0xa5 [<ffffffff810b5dc5>] ? filemap_fault+0x201/0x34f [<ffffffff810ef152>] ? fget_light+0x2f/0xac [<ffffffff813519e7>] ? verify_iovec+0x4f/0x94 [<ffffffff81349a65>] sys_recvmsg+0x14d/0x223 Store the serial number when beginning to walk the fib and reload pointers when continuing to walk after a change occured. Similar to other dumping functions, this might cause unrelated entries to be missed when entries are deleted. Tested-by: Ben Greear <greearb@candelatech.com> Signed-off-by: Patrick McHardy <kaber@trash.net> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--include/net/ip6_fib.h2
-rw-r--r--net/ipv6/ip6_fib.c29
2 files changed, 29 insertions, 2 deletions
diff --git a/include/net/ip6_fib.h b/include/net/ip6_fib.h
index 257808188add..8f279ddb3593 100644
--- a/include/net/ip6_fib.h
+++ b/include/net/ip6_fib.h
@@ -129,6 +129,8 @@ struct fib6_walker_t {
129 struct rt6_info *leaf; 129 struct rt6_info *leaf;
130 unsigned char state; 130 unsigned char state;
131 unsigned char prune; 131 unsigned char prune;
132 unsigned int skip;
133 unsigned int count;
132 int (*func)(struct fib6_walker_t *); 134 int (*func)(struct fib6_walker_t *);
133 void *args; 135 void *args;
134}; 136};
diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c
index f626ea2b304f..77e122f53ea6 100644
--- a/net/ipv6/ip6_fib.c
+++ b/net/ipv6/ip6_fib.c
@@ -319,12 +319,26 @@ static int fib6_dump_table(struct fib6_table *table, struct sk_buff *skb,
319 w->root = &table->tb6_root; 319 w->root = &table->tb6_root;
320 320
321 if (cb->args[4] == 0) { 321 if (cb->args[4] == 0) {
322 w->count = 0;
323 w->skip = 0;
324
322 read_lock_bh(&table->tb6_lock); 325 read_lock_bh(&table->tb6_lock);
323 res = fib6_walk(w); 326 res = fib6_walk(w);
324 read_unlock_bh(&table->tb6_lock); 327 read_unlock_bh(&table->tb6_lock);
325 if (res > 0) 328 if (res > 0) {
326 cb->args[4] = 1; 329 cb->args[4] = 1;
330 cb->args[5] = w->root->fn_sernum;
331 }
327 } else { 332 } else {
333 if (cb->args[5] != w->root->fn_sernum) {
334 /* Begin at the root if the tree changed */
335 cb->args[5] = w->root->fn_sernum;
336 w->state = FWS_INIT;
337 w->node = w->root;
338 w->skip = w->count;
339 } else
340 w->skip = 0;
341
328 read_lock_bh(&table->tb6_lock); 342 read_lock_bh(&table->tb6_lock);
329 res = fib6_walk_continue(w); 343 res = fib6_walk_continue(w);
330 read_unlock_bh(&table->tb6_lock); 344 read_unlock_bh(&table->tb6_lock);
@@ -1250,9 +1264,18 @@ static int fib6_walk_continue(struct fib6_walker_t *w)
1250 w->leaf = fn->leaf; 1264 w->leaf = fn->leaf;
1251 case FWS_C: 1265 case FWS_C:
1252 if (w->leaf && fn->fn_flags&RTN_RTINFO) { 1266 if (w->leaf && fn->fn_flags&RTN_RTINFO) {
1253 int err = w->func(w); 1267 int err;
1268
1269 if (w->count < w->skip) {
1270 w->count++;
1271 continue;
1272 }
1273
1274 err = w->func(w);
1254 if (err) 1275 if (err)
1255 return err; 1276 return err;
1277
1278 w->count++;
1256 continue; 1279 continue;
1257 } 1280 }
1258 w->state = FWS_U; 1281 w->state = FWS_U;
@@ -1346,6 +1369,8 @@ static void fib6_clean_tree(struct net *net, struct fib6_node *root,
1346 c.w.root = root; 1369 c.w.root = root;
1347 c.w.func = fib6_clean_node; 1370 c.w.func = fib6_clean_node;
1348 c.w.prune = prune; 1371 c.w.prune = prune;
1372 c.w.count = 0;
1373 c.w.skip = 0;
1349 c.func = func; 1374 c.func = func;
1350 c.arg = arg; 1375 c.arg = arg;
1351 c.net = net; 1376 c.net = net;