https://lwn.net/Articles/870269/ LWN.net Logo LWN .net News from the source LWN * Content + Weekly Edition + Archives + Search + Kernel + Security + Distributions + Events calendar + Unread comments + ------------------------------------------------------------- + LWN FAQ + Write for us * Edition + Return to the Front page User: [ ] Password: [ ] [Log in] | [Subscribe] | [Register] Subscribe / Log in / New account Taming the BPF superpowers By Jonathan Corbet September 29, 2021 --------------------------------------------------------------------- LPC Work toward the signing of BPF programs has been finding its way into recent mainline kernel releases; it is intended to improve security by limiting the BPF programs that can be successfully loaded into the kernel. As John Fastabend described in his "Watching the super powers" session at the 2021 Linux Plumbers Conference, this new feature has the potential to completely break his tools. But rather than just complain, he decided to investigate solutions; the result is an outline for an auditing mechanism that brings greater flexibility to the problem of controlling which programs can be run. The kernel has had the ability to enforce signatures on loadable modules for years, so it makes sense to consider creating the same mechanism for BPF programs. But, while kernel modules and BPF programs look similar -- both are code loaded into the kernel from user space, after all -- there are some significant differences between them. The safety of kernel modules is entirely dependent on the diligence of developers. They are built and distributed via the usual channels, are tied to specific kernel versions, and can last for years; they present a stable API to user space. BPF programs, instead, benefit from safety built into (and enforced by) the loader. They are often dynamically built and optimized, they are patched at run time to avoid being tied to kernel versions, and they have a different lifetime; often, they are created on the fly and quickly thrown away. These differences suggest that the same signing mechanism might not work equally well for both types of program. Fastabend covered the BPF signing scheme; curious readers can find a more complete description in this article. In short: BPF program loading is a complicated, multi-step process involving numerous system calls; the signature is meant to cover this entire process. [John Fastabend] That is done by loading yet another BPF program to handle the process; this mechanism is mostly implemented, but there are some details left to be worked out. There are certainly advantages to this mechanism, he said. If Alice and Bob have signed BPF programs, they can use them as usual. If Eve comes along with an unsigned program meant to eavesdrop on the kernel, that program will not be loaded and Eve will be frustrated. But there is also a cost: if Alice is generating programs on the fly, those programs will not be signed and will no longer be loadable. The keys used to sign programs should not be present on the system, so signing cannot be done on the fly and Alice's workflow will be blocked. Alice, too, will be frustrated despite being a legitimate user. This is not just a hypothetical case; a lot of tooling works that way now. Perhaps the best-known example is bpftrace, but it's not the only one. The P4 system defines a domain-specific language for the management of networking data paths. Some of Fastabend's work on Cilium is aimed at run-time optimization of BPF programs. PcapRecorder is an XDP-based clone of the venerable tcpdump utility. And so on. None of these tools can work in an environment where BPF programs must be signed. A lot of the security goals can be achieved, he said, by just making use of the fs-verity mechanism supported by Linux now. With fs-verity, read-only files can be signed and the kernel will check the signature on each access. If the file has been corrupted somehow, the signature will not match and access to the file will be blocked. So one thing that can be done is to use fs-verity to sign the program that loads BPF programs into the kernel. The system will automatically ensure that this program is not tampered with, and the set of keys that can create valid signatures can be restricted. But it is possible to go further than that, Fastabend said. Using some sort of policy engine, which could be another BPF program or a Linux security module, the kernel can look at the key that was used to sign any given program and associate a set of privileges with it. At its most basic, there could be a single "can load BPF programs" privilege, which would be similar in effect to attaching the CAP_BPF capability to the program. The system could be more fine-grained than that, even, by controlling actions like access to maps. With this sort of mechanism, he said, signature checking on the BPF objects themselves will be unnecessary. Consider the case where Alice's BPF-using process is somehow corrupted at run time. Signing of BPF programs will not save the system in this case; the corrupted user-space code can still do things like change values in BPF maps, change the attachment points for programs, and more. In other words, signing a BPF program gives little assurance that said program will run correctly in a hostile environment. A more flexible policy mechanism might do better, though, and could block attempts by a program to exceed its boundaries. Perhaps unsigned programs could be allowed to load, but they would not have the ability to write to user space or access kernel memory, for example. Access to pinned maps could be denied as well. This mechanism is not yet implemented, but he has some ideas about how it could be done. The LLVM compiler can attach attributes to objects; it could be taught to record all of the helper functions that a program calls, all of its map operations, and so on. The BPF verifier would then confirm that the program stays within those limits, and the supervisor mechanism could allow or deny a specific program based on the attributes. All that's left is to figure out how all this would actually work. Fastabend concluded by reiterating his goal of ensuring that dynamically generated BPF programs keep working. Program signing seems like the wrong solution; it is only useful in cases where the signed programs won't change. With an appropriate set of policy rules, it should be possible to safely allow a system to run unsigned BPF programs. In the brief discussion period that followed, Alexei Starovoitov (the author of the existing signing work) noted with enthusiasm that there are many other types of permissions that could be added. The maximum number of instructions allowed in a program would be one example. So there appears to be interest in this idea, but the real proof, as always, is in the code. The video of this session is available on YouTube. Index entries for this article Kernel Berkeley Packet Filter Security Linux kernel/BPF Conference Linux Plumbers Conference/2021 ----------------------------------------- (Log in to post comments) Taming the BPF superpowers Posted Sep 30, 2021 0:33 UTC (Thu) by zhe.zhao (subscriber, #95857) [ Link] Signing program which direct loaded into kernel space maybe a good way to protect the kernel integrity, there will always has some vuls existed within kernel's code, even for BPF which running within a VM like sandbox and has limited functionalities. Is that possible to try to just use userspace daemon process which can verify the signing of BPF and constaints within kernel space to only allow certificated userspace daemon process to loading the BPF from userspace, then it can limit the changes from kernel space's BPF infra at the same time not break the things which already works, and at the same time it can support both signed and unsigned BPF at same time. Actually if can achieve to constaint daemon process's capabilities in userspace within kernel, it may also works no only for BPF loading usage, but as some common way which can sandboxing process in userspace in Linux kernel. [Reply to this comment] Taming the BPF superpowers Posted Oct 1, 2021 6:59 UTC (Fri) by wtarreau (subscriber, #51152) [ Link] One concern I'm having is that over the last few years, for many improvements proposed in the network stack we got the response "do it in BPF instead". BPF has been the de-facto standard response to proposals to improve/fix syscalls. It's already extremely complicated to use to do simple things. Now if it becomes required to go through even more painful processes like signing etc, it will quickly look like the french administration where you need to fill a form to get the new form that you have to fill to get to the new one ... without seeing any useful work being done during all this time. We need to be extremely careful to keep the kernel *usable*. It's nice to preventively protect against imaginary threats but that has never prevented them from occurring and it must not come at the expense of usability. I'm not saying this lightly, especially at an era where the amount of people who still go through the process of configuring and compiling their kernels is now probably limited to mostly developers, and where others just have to live with the options chosen by their distros. When you're a userland developer, you don't often have the luxury to choose your end-users' kernel config options. [Reply to this comment] Taming the BPF superpowers Posted Oct 1, 2021 17:28 UTC (Fri) by Wol (subscriber, #4433) [Link] > where the amount of people who still go through the process of configuring and compiling their kernels is now probably limited to mostly developers, Or those fools running gentoo :-) Cheers, Wol [Reply to this comment] Taming the BPF superpowers Posted Oct 1, 2021 13:53 UTC (Fri) by mathstuf (subscriber, #69389) [ Link] How does fs-verity help if the BPF loading logic comes from a library used by the program? Is it just the executable which is verified before BPF is allowed or are all loaded libraries verified before BPF is allowed to be used? [Reply to this comment] Copyright (c) 2021, Eklektix, Inc. This article may be redistributed under the terms of the Creative Commons CC BY-SA 4.0 license Comments and public postings are copyrighted by their creators. Linux is a registered trademark of Linus Torvalds