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>
struct rt6_info *leaf;
unsigned char state;
unsigned char prune;
+ unsigned int skip;
+ unsigned int count;
int (*func)(struct fib6_walker_t *);
void *args;
};
w->root = &table->tb6_root;
if (cb->args[4] == 0) {
+ w->count = 0;
+ w->skip = 0;
+
read_lock_bh(&table->tb6_lock);
res = fib6_walk(w);
read_unlock_bh(&table->tb6_lock);
- if (res > 0)
+ if (res > 0) {
cb->args[4] = 1;
+ cb->args[5] = w->root->fn_sernum;
+ }
} else {
+ if (cb->args[5] != w->root->fn_sernum) {
+ /* Begin at the root if the tree changed */
+ cb->args[5] = w->root->fn_sernum;
+ w->state = FWS_INIT;
+ w->node = w->root;
+ w->skip = w->count;
+ } else
+ w->skip = 0;
+
read_lock_bh(&table->tb6_lock);
res = fib6_walk_continue(w);
read_unlock_bh(&table->tb6_lock);
w->leaf = fn->leaf;
case FWS_C:
if (w->leaf && fn->fn_flags&RTN_RTINFO) {
- int err = w->func(w);
+ int err;
+
+ if (w->count < w->skip) {
+ w->count++;
+ continue;
+ }
+
+ err = w->func(w);
if (err)
return err;
+
+ w->count++;
continue;
}
w->state = FWS_U;
c.w.root = root;
c.w.func = fib6_clean_node;
c.w.prune = prune;
+ c.w.count = 0;
+ c.w.skip = 0;
c.func = func;
c.arg = arg;
c.net = net;