include/linux/kernel_read_file.h | 1 + include/uapi/linux/bpf.h | 7 + kernel/bpf/syscall.c | 341 ++++++++++++++++- security/selinux/hooks.c | 22 +- security/selinux/include/classmap.h | 4 +- tools/include/uapi/linux/bpf.h | 7 + tools/testing/selftests/bpf/Makefile | 8 +- tools/testing/selftests/bpf/loader_setup.sh | 68 ++++ .../selftests/bpf/prog_tests/loader_load_fd.c | 361 ++++++++++++++++++ .../testing/selftests/bpf/progs/test_loader.c | 21 + 10 files changed, 824 insertions(+), 16 deletions(-) create mode 100755 tools/testing/selftests/bpf/loader_setup.sh create mode 100644 tools/testing/selftests/bpf/prog_tests/loader_load_fd.c create mode 100644 tools/testing/selftests/bpf/progs/test_loader.c
The bpf subsystem supports a signed-bpf infrastructure to guarantee the authenticity of programs [1, 2]. While this infrastructure is ideal for dynamic environments or enterprise deployments where untrusted binaries are loaded post-boot, it introduces unnecessary complexity for static platform use cases. In the Android ecosystem, platform BPF programs reside exclusively on read-only partitions that are strictly verified at the block level via dm-verity. Because the kernel has already guaranteed the authenticity of the underlying file, parsing and validating a secondary signature inside the BPF subsystem is redundant, complex (because it requires X509 and PKCS#7 parsing) and necessitates introducing additional signing flows during build. Furthermore, relying on signatures forces the kernel to manage a dedicated public key keyring. In a decentralized ecosystem comprising various OEMs and SoC vendors, managing these keys specifically for infrastructure BPF programs presents an operational hurdle. Thanks to light skeletons, it is possible to embed the loading steps within a wrapping BPF program (also known as loader). In turns, the loading of the loader only requires a limited, well-defined number of steps. This series introduces the bpf command LOADER_LOAD_FD for the kernel to directly load an ELF file which contains a loader with its data. By moving the loading within the kernel, the authenticity of the loader and its content can be guaranteed by existing kernel mechanisms. Kernel Modules Precedent ------------------------ Verification of authenticity for kernel modules is supported through multiple options. Deployments may implement PKCS#7 signature validation or, alternatively, leverage the fact that finit_module(2) retrieves module data via kernel_read_file(). Android utilizes this latter method for kernel module verification. By integrating kernel_read_file() with LSM hooks, security policies can ensure that any loaded module is sourced from a read-only partition protected by dm-verity, and therefore trusted. This patch series adopts a similar design for BPF by introducing the READING_BPF_LOADER constant to the kernel_read_file() enumeration. The availability of multiple verification paths for kernel modules reflects the diverse requirements of different environments. Providing analogous options for the BPF subsystem maintains consistency with this established kernel precedent. This proposal again? -------------------- Similar approaches were proposed in the past [3, 4]. There are differences that make this proposal worth sharing. Namely, the loading relies on light skeletons for the heavy lifting. It means that the UAPI exposed by the kernel is limited: one file descriptor of an ELF with 3 sections; and an opaque context. It also means that the processing done by the kernel is limited: the majority of the code in this patchset is doing basic ELF validation and calling the BPF interface that is already exposed to the rest of the kernel (kern_sys_bpf). Design ------ The BPF_LOADER_LOAD_FD command provides a method for the kernel to load and set up BPF programs and maps directly from an ELF file, guaranteeing the authenticity of the loaded objects. It builds upon the light skeleton approach which transfers the loading logic (i.e., CO-RE, BTF parsing, etc) to a wrapping BPF program (called loader), generated by libbpf. By relying on these, it is possible to drastically reduce the expectations on the kernel interface that needs to be declared and supported. When loading a light skeleton, libbpf performs 4 actions: 1. Create a map array. 2. Populate the map array with the light skeleton’s data. 3. Load the light skeleton program. 4. Execute the light skeleton program. These exact same steps are implemented in LOADER_LOAD_FD. This new command takes a file descriptor and a context. The file descriptor is expected to refer to an ELF file which contains 3 sections: __loader.prog, __loader.map and license. These sections are used as-is in the steps above. The context is passed directly to BPF_PROG_TEST_RUN. In the current libbpf implementation, that context contains the file descriptors to the programs and maps that have been created by the loader (for further processing by userland, such as pinning). This context is opaque to the kernel in BPF_LOADER_LOAD_FD. Compared to previous approaches [3] and because this approach relies on light skeletons, only basic processing is done by the kernel when loading the programs. CO-RE or BTF are explicitly not supported as these are handled by the light skeleton loader. This series also includes the corresponding SELinux changes. Namely, a new loader_load_fd permission is added to gate the use of the syscall. It can be used to guarantee that any userspace caller migrates to this new command instead of the existing BPF_PROG_LOAD. Another permission, named bpf_load is added to the system class. This permission can be used to restrict the file allowed to be loaded. Effectively, these permissions can be used to guarantee that the kernel loads BPF programs originating from known locations only. Notes ----- - There are some limited duplications of the ELF validation between this patchset and the existing kernel module loading logic. This can be refactored. - ELF was picked as a container for the loader’s instructions and map. There are very few expectations on the ELF, it is mainly a container for these opaque bytes. References ---------- [1] https://lore.kernel.org/bpf/20250921160120.9711-1-kpsingh@kernel.org/ [2] https://lore.kernel.org/bpf/20260708075343.358712-1-daniel@iogearbox.net/ [3] https://lore.kernel.org/bpf/20250109214617.485144-1-bboscaccy@linux.microsoft.com/ [4] https://bpfconf.ebpf.io/bpfconf2024/bpfconf2024_material/LSFMMBPF24_kapron_verified_boot.pdf Thiébaud Weksteen (5): fs/kernel_read_file,selinux: Add BPF_LOADER constant bpf: Introduce BPF_LOADER_LOAD_FD command selinux: use kernel sid in security_bpf_* selinux: Add BPF_LOADER_LOAD_FD syscall permission selftests/bpf: add loader_load_fd tests include/linux/kernel_read_file.h | 1 + include/uapi/linux/bpf.h | 7 + kernel/bpf/syscall.c | 341 ++++++++++++++++- security/selinux/hooks.c | 22 +- security/selinux/include/classmap.h | 4 +- tools/include/uapi/linux/bpf.h | 7 + tools/testing/selftests/bpf/Makefile | 8 +- tools/testing/selftests/bpf/loader_setup.sh | 68 ++++ .../selftests/bpf/prog_tests/loader_load_fd.c | 361 ++++++++++++++++++ .../testing/selftests/bpf/progs/test_loader.c | 21 + 10 files changed, 824 insertions(+), 16 deletions(-) create mode 100755 tools/testing/selftests/bpf/loader_setup.sh create mode 100644 tools/testing/selftests/bpf/prog_tests/loader_load_fd.c create mode 100644 tools/testing/selftests/bpf/progs/test_loader.c -- 2.55.0.691.gc56d675ccc-goog
On 8/13/26 2:26 AM, Thiébaud Weksteen wrote: > The bpf subsystem supports a signed-bpf infrastructure to guarantee the > authenticity of programs [1, 2]. While this infrastructure is ideal for > dynamic environments or enterprise deployments where untrusted binaries > are loaded post-boot, it introduces unnecessary complexity for static > platform use cases. > > In the Android ecosystem, platform BPF programs reside exclusively on > read-only partitions that are strictly verified at the block level via > dm-verity. Because the kernel has already guaranteed the authenticity of > the underlying file, parsing and validating a secondary signature inside > the BPF subsystem is redundant, complex (because it requires X509 and > PKCS#7 parsing) and necessitates introducing additional signing flows > during build. Note that the BPF subsys doesn't implement any X.509 or PKCS#7 parsing, rather we reuse the same mechanism as the module loader has been using. > Furthermore, relying on signatures forces the kernel to manage a > dedicated public key keyring. In a decentralized ecosystem comprising > various OEMs and SoC vendors, managing these keys specifically for > infrastructure BPF programs presents an operational hurdle. There is no dedicated BPF keyring.. an OEM key built into a kernel image has no additional runtime key management. > Thanks to light skeletons, it is possible to embed the loading steps > within a wrapping BPF program (also known as loader). In turns, the > loading of the loader only requires a limited, well-defined number of > steps. This series introduces the bpf command LOADER_LOAD_FD for the > kernel to directly load an ELF file which contains a loader with its > data. By moving the loading within the kernel, the authenticity of the > loader and its content can be guaranteed by existing kernel mechanisms. Same issue still holds as described in [0]. Also, this sounds more like convenience for Android offloaded to the kernel.. (you stated redundant given the signature validation and complexity wrt keyring.. so what? don't you have the latter anyway for vendor kernel modules?) We're not adding yet another scheme/alternative for addressing signed BPF via separate loader mechanism. Also, if you don't want to use existing signed BPF infra, then implement it as a policy via BPF LSM + xattr? [0] https://lore.kernel.org/bpf/CAADnVQLxgD_7GYWZZ49aY2LqVYOy4uGvK2ikm7MJ1Cj60VPNaw@mail.gmail.com/ Thanks, Daniel
On Tue, Aug 18, 2026 at 2:11 AM Daniel Borkmann <daniel@iogearbox.net> wrote: > > On 8/13/26 2:26 AM, Thiébaud Weksteen wrote: > > The bpf subsystem supports a signed-bpf infrastructure to guarantee the > > authenticity of programs [1, 2]. While this infrastructure is ideal for > > dynamic environments or enterprise deployments where untrusted binaries > > are loaded post-boot, it introduces unnecessary complexity for static > > platform use cases. > > > > In the Android ecosystem, platform BPF programs reside exclusively on > > read-only partitions that are strictly verified at the block level via > > dm-verity. Because the kernel has already guaranteed the authenticity of > > the underlying file, parsing and validating a secondary signature inside > > the BPF subsystem is redundant, complex (because it requires X509 and > > PKCS#7 parsing) and necessitates introducing additional signing flows > > during build. > > Note that the BPF subsys doesn't implement any X.509 or PKCS#7 parsing, > rather we reuse the same mechanism as the module loader has been using. > > > Furthermore, relying on signatures forces the kernel to manage a > > dedicated public key keyring. In a decentralized ecosystem comprising > > various OEMs and SoC vendors, managing these keys specifically for > > infrastructure BPF programs presents an operational hurdle. > > There is no dedicated BPF keyring.. an OEM key built into a kernel image > has no additional runtime key management. > > > Thanks to light skeletons, it is possible to embed the loading steps > > within a wrapping BPF program (also known as loader). In turns, the > > loading of the loader only requires a limited, well-defined number of > > steps. This series introduces the bpf command LOADER_LOAD_FD for the > > kernel to directly load an ELF file which contains a loader with its > > data. By moving the loading within the kernel, the authenticity of the > > loader and its content can be guaranteed by existing kernel mechanisms. > > Same issue still holds as described in [0]. Also, this sounds more like > convenience for Android offloaded to the kernel.. (you stated redundant > given the signature validation and complexity wrt keyring.. so what? > don't you have the latter anyway for vendor kernel modules?) Thanks for the feedback Daniel. Vendor kernel modules are verified as described in the 'precedent' section: there are no signatures per module. Instead the combination of dm-verity and SELinux policy ensures the modules' authenticity. > We're not > adding yet another scheme/alternative for addressing signed BPF via > separate loader mechanism. I understand your position. Thanks for taking the time to reply.
On Mon, Aug 17, 2026 at 06:11:10PM +0200, Daniel Borkmann wrote: > On 8/13/26 2:26 AM, Thiébaud Weksteen wrote: > > The bpf subsystem supports a signed-bpf infrastructure to guarantee the > > authenticity of programs [1, 2]. While this infrastructure is ideal for > > dynamic environments or enterprise deployments where untrusted binaries > > are loaded post-boot, it introduces unnecessary complexity for static > > platform use cases. > > > > In the Android ecosystem, platform BPF programs reside exclusively on > > read-only partitions that are strictly verified at the block level via > > dm-verity. Because the kernel has already guaranteed the authenticity of > > the underlying file, parsing and validating a secondary signature inside > > the BPF subsystem is redundant, complex (because it requires X509 and > > PKCS#7 parsing) and necessitates introducing additional signing flows > > during build. > > Note that the BPF subsys doesn't implement any X.509 or PKCS#7 parsing, > rather we reuse the same mechanism as the module loader has been using. BPF made the mistake of using X.509 and PKCS#7 again, but that doesn't mean it wasn't a mistake. These formats are highly complex, with each "signature" actually being an ASN.1 object containing multiple certificates and signatures for arbitrary algorithms, and many other unnecessary complexities. The kernel's X.509, PKCS#7, and ASN.1 code has had regular bugs ever since it was added to the kernel over a decade ago. Just in the last week two more vulnerabilities have gone by: https://lore.kernel.org/all/20260821192502.3942767-2-Jeremy.Jean@oss.cyber.gouv.fr/ and https://lore.kernel.org/all/20260822212703.1019792-2-Jeremy.Jean@oss.cyber.gouv.fr/ Just because it's not in the BPF subsystem doesn't mean it's not BPF's responsibility for choosing to depend on this unsupportable code. I've mostly given up even trying to review X.509 and PKCS#7 kernel patches, as they are difficult to understand and have little to do with actual cryptography. And anyone building a secure system shouldn't be using them anyway. It's nearly impossible to implement these data formats correctly; it's best left for userspace libraries that absolutely *have* to support these formats and have better testing and fuzzing tools available. These formats are not necessary to do signatures either, as the signature algorithms can just be used directly, e.g. https://docs.kernel.org/crypto/libcrypto-signature.html#ml-dsa But in most cases module authentication has never needed signatures in the first place, as either hash-based authentication (https://lore.kernel.org/linux-modules/20260505-module-hashes-v5-0-e174a5a49fce@weissschuh.net/) could be used, or modules can be trusted based on being loaded from a filesystem that is already authenticated itself. We should start moving on from the mistake of building X.509 and PKCS#7 based systems in the kernel. It's not working. If there is a use case for authenticating BPF programs based on where they are loaded from, that seems very reasonable to support directly instead of forcing the use of unnecessary signatures and data formats. - Eric
© 2016 - 2026 Red Hat, Inc.