Andrey Ignatov <rdna@xxxxxx> [Fri, 2020-04-17 12:41 -0700]: > Christoph Hellwig <hch@xxxxxx> [Thu, 2020-04-16 23:42 -0700]: ... > > @@ -564,27 +564,36 @@ static ssize_t proc_sys_call_handler(struct file *filp, void __user *buf, > > if (!table->proc_handler) > > goto out; > > > > - error = BPF_CGROUP_RUN_PROG_SYSCTL(head, table, write, buf, &count, > > - ppos, &new_buf); > > + if (write) { > > + kbuf = memdup_user_nul(ubuf, count); > > + if (IS_ERR(kbuf)) { > > + error = PTR_ERR(kbuf); > > + goto out; > > + } > > + } else { > > + error = -ENOMEM; > > + kbuf = kzalloc(count, GFP_KERNEL); > > + if (!kbuf) > > + goto out; > > + } > > + > > + error = BPF_CGROUP_RUN_PROG_SYSCTL(head, table, write, &kbuf, &count, > > + ppos); > > if (error) > > - goto out; > > + goto out_free_buf; > > > > /* careful: calling conventions are nasty here */ > > - if (new_buf) { > > - mm_segment_t old_fs; > > - > > - old_fs = get_fs(); > > - set_fs(KERNEL_DS); > > - error = table->proc_handler(table, write, (void __user *)new_buf, > > - &count, ppos); > > - set_fs(old_fs); > > - kfree(new_buf); > > - } else { > > - error = table->proc_handler(table, write, buf, &count, ppos); > > - } > > + error = table->proc_handler(table, write, kbuf, &count, ppos); > > + if (error) > > + goto out_free_buf; > > + > > + error = -EFAULT; > > + if (copy_to_user(ubuf, kbuf, count)) > > + goto out_free_buf; This copy_to_user is where the last failing test I mentioned in the previous email was failing: > Test case: sysctl_set_new_value sysctl:write ok .. [FAIL] What the test does is it attaches BPF program that overrides the value that user is trying to write to sysctl net/ipv4/route/mtu_expires. User tries to write "606", BPF program overrides it with "600" using bpf_sysctl_set_new_value() helper. This leads to kbuf being replaced in BPF_CGROUP_RUN_PROG_SYSCTL call above with a new buffer allocated inside __cgroup_bpf_run_filter_sysctl. And when this new buffer is tried to be copied to user here it fails. In `strace -e ./test_sysctl` it can be seen as: write(5, "606", 3) = -1 EFAULT (Bad address) I also verified same with printk. Changing it to: if (!write && copy_to_user(ubuf, kbuf, count)) (basically what Matthew Wilcox suggested earlier) fixes the problem. > > > > - if (!error) > > - error = count; > > + error = count; > > +out_free_buf: > > + kfree(kbuf); > > out: > > sysctl_head_finish(head); > > ... > I applied the whole patchset to bpf-next tree and run selftests. This > patch breaks 4 of them: > > % cd tools/testing/selftests/bpf/ > % ./test_sysctl > ... > Test case: sysctl_get_new_value sysctl:write ok .. [FAIL] > Test case: sysctl_get_new_value sysctl:write ok long .. [FAIL] > Test case: sysctl_get_new_value sysctl:write E2BIG .. [FAIL] > Test case: sysctl_set_new_value sysctl:read EINVAL .. [PASS] > Test case: sysctl_set_new_value sysctl:write ok .. [FAIL] > ... > Summary: 36 PASSED, 4 FAILED > > I applied both changes I suggested above and it reduces number of broken > selftests to one: > > Test case: sysctl_set_new_value sysctl:write ok .. [FAIL] > > I haven't debugged this last one though yet .. -- Andrey Ignatov