On Jul 19, 2022, at 3:45 PM, Axel Rasmussen <axelrasmussen@xxxxxxxxxx> wrote: > On Tue, Jul 19, 2022 at 3:32 PM Nadav Amit <namit@xxxxxxxxxx> wrote: >> On Jul 19, 2022, at 12:56 PM, Axel Rasmussen <axelrasmussen@xxxxxxxxxx> wrote: >> >>> Historically, it has been shown that intercepting kernel faults with >>> userfaultfd (thereby forcing the kernel to wait for an arbitrary amount >>> of time) can be exploited, or at least can make some kinds of exploits >>> easier. So, in 37cd0575b8 "userfaultfd: add UFFD_USER_MODE_ONLY" we >>> changed things so, in order for kernel faults to be handled by >>> userfaultfd, either the process needs CAP_SYS_PTRACE, or this sysctl >>> must be configured so that any unprivileged user can do it. >>> >>> In a typical implementation of a hypervisor with live migration (take >>> QEMU/KVM as one such example), we do indeed need to be able to handle >>> kernel faults. But, both options above are less than ideal: >>> >>> - Toggling the sysctl increases attack surface by allowing any >>> unprivileged user to do it. >>> >>> - Granting the live migration process CAP_SYS_PTRACE gives it this >>> ability, but *also* the ability to "observe and control the >>> execution of another process [...], and examine and change [its] >>> memory and registers" (from ptrace(2)). This isn't something we need >>> or want to be able to do, so granting this permission violates the >>> "principle of least privilege". >>> >>> This is all a long winded way to say: we want a more fine-grained way to >>> grant access to userfaultfd, without granting other additional >>> permissions at the same time. >>> >>> To achieve this, add a /dev/userfaultfd misc device. This device >>> provides an alternative to the userfaultfd(2) syscall for the creation >>> of new userfaultfds. The idea is, any userfaultfds created this way will >>> be able to handle kernel faults, without the caller having any special >>> capabilities. Access to this mechanism is instead restricted using e.g. >>> standard filesystem permissions. >> >> Are there any other “devices" that when opened by different processes >> provide such isolated interfaces in each process? I.e., devices that if you >> read from them in different processes you get completely unrelated data? >> (putting aside namespaces). >> >> It all sounds so wrong to me, that I am going to try again to pushback >> (sorry). > > No need to be sorry. :) > >> From a semantic point of view - userfaultfd is process specific. It is >> therefore similar to /proc/[pid]/mem (or /proc/[pid]/pagemap and so on). >> >> So why can’t we put it there? I saw that you argued against it in your >> cover-letter, and I think that your argument is you would need >> CAP_SYS_PTRACE if you want to access userfaultfd of other processes. But >> this is EXACTLY the way opening /proc/[pid]/mem is performed - see >> proc_mem_open(). >> >> So instead of having some strange device that behaves differently in the >> context of each process, you can just have /proc/[pid]/userfaultfd and then >> use mm_access() to check if you have permissions to access userfaultfd (just >> like proc_mem_open() does). This would be more intuitive for users as it is >> similar to other /proc/[pid]/X, and would cover both local and remote >> use-cases. > > Ah, so actually I find this argument much more compelling. > > I don't find it persuasive that we should put it in /proc for the > purpose of supporting cross-process memory manipulation, because I > think the syscall works better for that, and in that case we don't > mind depending on CAP_SYS_PTRACE. > > But, what you've argued here I do find persuasive. :) You are right, I > can't think of any other example of a device node in /dev that works > like this, where it is completely independent on a per-process basis. > The closest I could come up with was /dev/zero or /dev/null or > similar. You won't affect any other process by touching these, but I > don't think these are good examples. > > I'll send a v5 which does this. I do worry that cross-process support > is probably complex to get right, so I might leave that out and only > allow a process to open its own device for now. So I didn’t want to get into it, and I am fine that you leave it out, since such an interface would still enable to support it later. Anyhow, I do want to clarify a bit about the “cross-process support” userfaultfd situation. Basically, you can already get cross-process support today, by using calling userfaultfd() on the controlled process and calling pidfd_open() from another process. It does work and I do not remember any issues that it introduced (in contrast, for instance, to io-uring, that would break if you use userfaultfd+iouring+fork today). Thanks for your reconsideration. Regards, Nadav