|
@@ -1576,25 +1576,41 @@ int bpf_prog_array_copy_to_user(struct bpf_prog_array __rcu *progs,
|
|
__u32 __user *prog_ids, u32 cnt)
|
|
__u32 __user *prog_ids, u32 cnt)
|
|
{
|
|
{
|
|
struct bpf_prog **prog;
|
|
struct bpf_prog **prog;
|
|
- u32 i = 0, id;
|
|
|
|
-
|
|
|
|
|
|
+ unsigned long err = 0;
|
|
|
|
+ u32 i = 0, *ids;
|
|
|
|
+ bool nospc;
|
|
|
|
+
|
|
|
|
+ /* users of this function are doing:
|
|
|
|
+ * cnt = bpf_prog_array_length();
|
|
|
|
+ * if (cnt > 0)
|
|
|
|
+ * bpf_prog_array_copy_to_user(..., cnt);
|
|
|
|
+ * so below kcalloc doesn't need extra cnt > 0 check, but
|
|
|
|
+ * bpf_prog_array_length() releases rcu lock and
|
|
|
|
+ * prog array could have been swapped with empty or larger array,
|
|
|
|
+ * so always copy 'cnt' prog_ids to the user.
|
|
|
|
+ * In a rare race the user will see zero prog_ids
|
|
|
|
+ */
|
|
|
|
+ ids = kcalloc(cnt, sizeof(u32), GFP_USER);
|
|
|
|
+ if (!ids)
|
|
|
|
+ return -ENOMEM;
|
|
rcu_read_lock();
|
|
rcu_read_lock();
|
|
prog = rcu_dereference(progs)->progs;
|
|
prog = rcu_dereference(progs)->progs;
|
|
for (; *prog; prog++) {
|
|
for (; *prog; prog++) {
|
|
if (*prog == &dummy_bpf_prog.prog)
|
|
if (*prog == &dummy_bpf_prog.prog)
|
|
continue;
|
|
continue;
|
|
- id = (*prog)->aux->id;
|
|
|
|
- if (copy_to_user(prog_ids + i, &id, sizeof(id))) {
|
|
|
|
- rcu_read_unlock();
|
|
|
|
- return -EFAULT;
|
|
|
|
- }
|
|
|
|
|
|
+ ids[i] = (*prog)->aux->id;
|
|
if (++i == cnt) {
|
|
if (++i == cnt) {
|
|
prog++;
|
|
prog++;
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+ nospc = !!(*prog);
|
|
rcu_read_unlock();
|
|
rcu_read_unlock();
|
|
- if (*prog)
|
|
|
|
|
|
+ err = copy_to_user(prog_ids, ids, cnt * sizeof(u32));
|
|
|
|
+ kfree(ids);
|
|
|
|
+ if (err)
|
|
|
|
+ return -EFAULT;
|
|
|
|
+ if (nospc)
|
|
return -ENOSPC;
|
|
return -ENOSPC;
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|