Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

bpf: fix bpf_prog_array_copy_to_user() issues

1. move copy_to_user out of rcu section to fix the following issue:

./include/linux/rcupdate.h:302 Illegal context switch in RCU read-side critical section!
stack backtrace:
__dump_stack lib/dump_stack.c:17 [inline]
dump_stack+0x194/0x257 lib/dump_stack.c:53
lockdep_rcu_suspicious+0x123/0x170 kernel/locking/lockdep.c:4592
rcu_preempt_sleep_check include/linux/rcupdate.h:301 [inline]
___might_sleep+0x385/0x470 kernel/sched/core.c:6079
__might_sleep+0x95/0x190 kernel/sched/core.c:6067
__might_fault+0xab/0x1d0 mm/memory.c:4532
_copy_to_user+0x2c/0xc0 lib/usercopy.c:25
copy_to_user include/linux/uaccess.h:155 [inline]
bpf_prog_array_copy_to_user+0x217/0x4d0 kernel/bpf/core.c:1587
bpf_prog_array_copy_info+0x17b/0x1c0 kernel/bpf/core.c:1685
perf_event_query_prog_array+0x196/0x280 kernel/trace/bpf_trace.c:877
_perf_ioctl kernel/events/core.c:4737 [inline]
perf_ioctl+0x3e1/0x1480 kernel/events/core.c:4757

2. move *prog under rcu, since it's not ok to dereference it afterwards

3. in a rare case of prog array being swapped between bpf_prog_array_length()
and bpf_prog_array_copy_to_user() calls make sure to copy zeros to user space,
so the user doesn't walk over uninited prog_ids while kernel reported
uattr->query.prog_cnt > 0

Reported-by: syzbot+7dbcd2d3b85f9b608b23@syzkaller.appspotmail.com
Fixes: 468e2f64d220 ("bpf: introduce BPF_PROG_QUERY command")
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>

authored by

Alexei Starovoitov and committed by
Daniel Borkmann
0911287c 6215ea6b

+23 -7
+23 -7
kernel/bpf/core.c
··· 1576 1576 __u32 __user *prog_ids, u32 cnt) 1577 1577 { 1578 1578 struct bpf_prog **prog; 1579 - u32 i = 0, id; 1579 + unsigned long err = 0; 1580 + u32 i = 0, *ids; 1581 + bool nospc; 1580 1582 1583 + /* users of this function are doing: 1584 + * cnt = bpf_prog_array_length(); 1585 + * if (cnt > 0) 1586 + * bpf_prog_array_copy_to_user(..., cnt); 1587 + * so below kcalloc doesn't need extra cnt > 0 check, but 1588 + * bpf_prog_array_length() releases rcu lock and 1589 + * prog array could have been swapped with empty or larger array, 1590 + * so always copy 'cnt' prog_ids to the user. 1591 + * In a rare race the user will see zero prog_ids 1592 + */ 1593 + ids = kcalloc(cnt, sizeof(u32), GFP_USER); 1594 + if (!ids) 1595 + return -ENOMEM; 1581 1596 rcu_read_lock(); 1582 1597 prog = rcu_dereference(progs)->progs; 1583 1598 for (; *prog; prog++) { 1584 1599 if (*prog == &dummy_bpf_prog.prog) 1585 1600 continue; 1586 - id = (*prog)->aux->id; 1587 - if (copy_to_user(prog_ids + i, &id, sizeof(id))) { 1588 - rcu_read_unlock(); 1589 - return -EFAULT; 1590 - } 1601 + ids[i] = (*prog)->aux->id; 1591 1602 if (++i == cnt) { 1592 1603 prog++; 1593 1604 break; 1594 1605 } 1595 1606 } 1607 + nospc = !!(*prog); 1596 1608 rcu_read_unlock(); 1597 - if (*prog) 1609 + err = copy_to_user(prog_ids, ids, cnt * sizeof(u32)); 1610 + kfree(ids); 1611 + if (err) 1612 + return -EFAULT; 1613 + if (nospc) 1598 1614 return -ENOSPC; 1599 1615 return 0; 1600 1616 }