On Thu, Mar 7, 2024 at 1:55 AM Christian Brauner <brauner@xxxxxxxxxx> wrote: > > > > > > > So, looking at this series you're now asking us to expose: > > > > > > (1) mmgrab() > > > (2) mmput() > > > (3) fput() > > > (5) get_mm_exe_file() > > > (4) get_task_exe_file() > > > (7) get_task_fs_pwd() > > > (6) get_task_fs_root() > > > (8) path_get() > > > (9) path_put() > > > > > > in one go and the justification in all patches amounts to "This is > > > common in some BPF LSM programs". > > > > > > So, broken stuff got exposed to users or at least a broken BPF LSM > > > program was written somewhere out there that is susceptible to UAFs > > > becauase you didn't restrict bpf_d_path() to trusted pointer arguments. > > > So you're now scrambling to fix this by asking for a bunch of low-level > > > exports. > > > > > > What is the guarantee that you don't end up writing another BPF LSM that > > > abuses these exports in a way that causes even more issues and then > > > someone else comes back asking for the next round of bpf funcs to be > > > exposed to fix it. > > > > There is no guarantee. > > We made a safety mistake with bpf_d_path() though > > we restricted it very tight. And that UAF is tricky. > > I'm still amazed how Jann managed to find it. > > We all make mistakes. > > It's not the first one and not going to be the last. > > > > What Matt is doing is an honest effort to fix it > > in the upstream kernel for all bpf users to benefit. > > He could have done it with a kernel module. > > The above "low level" helpers are all either static inline > > in .h or they call EXPORT_SYMBOL[_GPL] or simply inc/dec refcnt. > > > > One can implement such kfuncs in an out of tree kernel module > > and be done with it, but in the bpf community we encourage > > everyone to upstream their work. > > > > So kudos to Matt for working on these patches. > > > > His bpf-lsm use case is not special. > > It just needs a safe way to call d_path. > > > > +SEC("lsm.s/file_open") > > +__failure __msg("R1 must be referenced or trusted") > > +int BPF_PROG(path_d_path_kfunc_untrusted_from_current) > > +{ > > + struct path *pwd; > > + struct task_struct *current; > > + > > + current = bpf_get_current_task_btf(); > > + /* Walking a trusted pointer returned from bpf_get_current_task_btf() > > + * yields and untrusted pointer. */ > > + pwd = ¤t->fs->pwd; > > + bpf_path_d_path(pwd, buf, sizeof(buf)); > > + return 0; > > +} > > > > This test checks that such an access pattern is unsafe and > > the verifier will catch it. > > > > To make it safe one needs to do: > > > > current = bpf_get_current_task_btf(); > > pwd = bpf_get_task_fs_pwd(current); > > if (!pwd) // error path > > bpf_path_d_path(pwd, ...); > > bpf_put_path(pwd); > > > > these are the kfuncs from patch 6. > > > > And notice that they have KF_ACQUIRE and KF_RELEASE flags. > > > > They tell the verifier to recognize that bpf_get_task_fs_pwd() > > kfunc acquires 'struct path *'. > > Meaning that bpf prog cannot just return without releasing it. > > > > The bpf prog cannot use-after-free that 'pwd' either > > after it was released by bpf_put_path(pwd). > > > > The verifier static analysis catches such UAF-s. > > It didn't catch Jann's UAF earlier, because we didn't have > > these kfuncs! Hence the fix is to add such kfuncs with > > acquire/release semantics. > > > > > The difference between a regular LSM asking about this and a BPF LSM > > > program is that we can see in the hook implementation what the LSM > > > intends to do with this and we can judge whether that's safe or not. > > > > See above example. > > The verifier is doing a much better job than humans when it comes > > to safety. > > > > > Here you're asking us to do this blindfolded. > > > > If you don't trust the verifier to enforce safety, > > you shouldn't trust Rust compiler to generate safe code either. > > > > In another reply you've compared kfuncs to EXPORT_SYMBOL_GPL. > > Such analogy is correct to some extent, > > but unlike exported symbols kfuncs are restricted to particular > > program types. They don't accept arbitrary pointers, > > and reference count is enforced as well. > > That's a pretty big difference vs EXPORT_SYMBOL. > > There's one fundamental question here that we'll need an official answer to: > > Is it ok for an out-of-tree BPF LSM program, that nobody has ever seen > to request access to various helpers in the kernel? obviously not. > Because fundamentally this is what this patchset is asking to be done. Pardon ? > If the ZFS out-of-tree kernel module were to send us a similar patch > series asking us for a list of 9 functions that they'd like us to export > what would the answer to that be? This patch set doesn't request any new EXPORT_SYMBOL. Quoting previous reply: " The above "low level" helpers are all either static inline in .h or they call EXPORT_SYMBOL[_GPL] or simply inc/dec refcnt. " Let's look at your list: > > > (1) mmgrab() > > > (2) mmput() > > > (3) fput() > > > (5) get_mm_exe_file() > > > (4) get_task_exe_file() > > > (7) get_task_fs_pwd() > > > (6) get_task_fs_root() > > > (8) path_get() > > > (9) path_put() First of all, there is no such thing as get_task_fs_pwd/root in the kernel. The hypothetical out-of-tree ZFS can use the rest just fine. One can argue that get_mm_exe_file() is not exported, but it's nothing but rcu_lock-wrap plus get_file_rcu() which is EXPORT_SYMBOL. kfunc-s in patch 6 wrap get_fs_root/pwd from linux/fs_struct.h that calls EXPORT_SYMBOL(path_get). So upon close examination no new EXPORT_SYMBOL-s were requested. Christian, I understand your irritation with anything bpf related due to our mistake with fd=0, but as I said earlier it was an honest mistake. There was no malicious intent. Time to move on.