sphinx.addnodesdocument)}( rawsourcechildren]( translations LanguagesNode)}(hhh](h pending_xref)}(hhh]docutils.nodesTextChinese (Simplified)}parenthsba attributes}(ids]classes]names]dupnames]backrefs] refdomainstdreftypedoc reftarget/translations/zh_CN/bpf/signingmodnameN classnameN refexplicitutagnamehhh ubh)}(hhh]hChinese (Traditional)}hh2sbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget/translations/zh_TW/bpf/signingmodnameN classnameN refexplicituh1hhh ubh)}(hhh]hItalian}hhFsbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget/translations/it_IT/bpf/signingmodnameN classnameN refexplicituh1hhh ubh)}(hhh]hJapanese}hhZsbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget/translations/ja_JP/bpf/signingmodnameN classnameN refexplicituh1hhh ubh)}(hhh]hKorean}hhnsbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget/translations/ko_KR/bpf/signingmodnameN classnameN refexplicituh1hhh ubh)}(hhh]hPortuguese (Brazilian)}hhsbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget/translations/pt_BR/bpf/signingmodnameN classnameN refexplicituh1hhh ubh)}(hhh]hSpanish}hhsbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget/translations/sp_SP/bpf/signingmodnameN classnameN refexplicituh1hhh ubeh}(h]h ]h"]h$]h&]current_languageEnglishuh1h hh _documenthsourceNlineNubhcomment)}(h SPDX-License-Identifier: GPL-2.0h]h SPDX-License-Identifier: GPL-2.0}hhsbah}(h]h ]h"]h$]h&] xml:spacepreserveuh1hhhhhh9/var/lib/git/docbuild/linux/Documentation/bpf/signing.rsthKubhsection)}(hhh](htitle)}(h BPF signingh]h BPF signing}(hhhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhhhhhKubh paragraph)}(hXvThis document describes how BPF programs are cryptographically signed, how the kernel verifies them at load time, and how Linux Security Modules (LSMs) - including the BPF LSM - use the resulting verdict to enforce policy. It is written for developers who want to produce signed BPF objects, understand what the signature actually guarantees, or build a policy on top of it.h]hXvThis document describes how BPF programs are cryptographically signed, how the kernel verifies them at load time, and how Linux Security Modules (LSMs) - including the BPF LSM - use the resulting verdict to enforce policy. It is written for developers who want to produce signed BPF objects, understand what the signature actually guarantees, or build a policy on top of it.}(hhhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhhhhubh)}(hhh](h)}(h Motivationh]h Motivation}(hhhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhhhhhKubh)}(hXA signed BPF program lets the kernel establish that the bytecode being loaded originates from a trusted producer and was not modified in transit. On its own the kernel does not *require* signatures - an unsigned program loads exactly as before - but it records a verdict (see `The verdict`_) that an LSM can gate on. This is the building block for policies such as "only run BPF that was signed by a key in the trusted keyring", as could in the future be enforced by an LSM such as IPE.h](hA signed BPF program lets the kernel establish that the bytecode being loaded originates from a trusted producer and was not modified in transit. On its own the kernel does not }(hhhhhNhNubhemphasis)}(h *require*h]hrequire}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhhubhZ signatures - an unsigned program loads exactly as before - but it records a verdict (see }(hhhhhNhNubh reference)}(h`The verdict`_h]h The verdict}(hjhhhNhNubah}(h]h ]h"]h$]h&]name The verdictrefid the-verdictuh1jhhresolvedKubh) that an LSM can gate on. This is the building block for policies such as “only run BPF that was signed by a key in the trusted keyring”, as could in the future be enforced by an LSM such as IPE.}(hhhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhhhhubh)}(hXSigning is orthogonal to the existing permission model: it does not replace the capability checks or the verifier. A signed load still requires the usual privileges (``CAP_BPF`` and any program-type-specific capability, subject to ``kernel.unprivileged_bpf_disabled``), and the loader's instructions are still checked by the verifier like any other program. A valid signature establishes *origin and integrity*, not safety - it lets a policy trust where the bytecode came from, it does not let a load skip any check it would otherwise face.h](hSigning is orthogonal to the existing permission model: it does not replace the capability checks or the verifier. A signed load still requires the usual privileges (}(hj9hhhNhNubhliteral)}(h ``CAP_BPF``h]hCAP_BPF}(hjChhhNhNubah}(h]h ]h"]h$]h&]uh1jAhj9ubh6 and any program-type-specific capability, subject to }(hj9hhhNhNubjB)}(h$``kernel.unprivileged_bpf_disabled``h]h kernel.unprivileged_bpf_disabled}(hjUhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhj9ubh{), and the loader’s instructions are still checked by the verifier like any other program. A valid signature establishes }(hj9hhhNhNubj)}(h*origin and integrity*h]horigin and integrity}(hjghhhNhNubah}(h]h ]h"]h$]h&]uh1jhj9ubh, not safety - it lets a policy trust where the bytecode came from, it does not let a load skip any check it would otherwise face.}(hj9hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhhhhubh)}(hXiThe hard part is *what* gets signed. A naive scheme would sign a program's instruction buffer at build time and verify that signature at ``BPF_PROG_LOAD``. That does not survive contact with real BPF objects, because the bytes the kernel finally loads are not the bytes the developer built and signed. Between the two, libbpf and the kernel rewrite the program:h](hThe hard part is }(hjhhhNhNubj)}(h*what*h]hwhat}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubht gets signed. A naive scheme would sign a program’s instruction buffer at build time and verify that signature at }(hjhhhNhNubjB)}(h``BPF_PROG_LOAD``h]h BPF_PROG_LOAD}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubh. That does not survive contact with real BPF objects, because the bytes the kernel finally loads are not the bytes the developer built and signed. Between the two, libbpf and the kernel rewrite the program:}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhK hhhhubh bullet_list)}(hhh](h list_item)}(h**map file descriptors** are patched into ``ld_imm64`` instructions (``BPF_PSEUDO_MAP_FD``), and a map's fd is assigned at load time, so it differs on every run;h]h)}(h**map file descriptors** are patched into ``ld_imm64`` instructions (``BPF_PSEUDO_MAP_FD``), and a map's fd is assigned at load time, so it differs on every run;h](hstrong)}(h**map file descriptors**h]hmap file descriptors}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh are patched into }(hjhhhNhNubjB)}(h ``ld_imm64``h]hld_imm64}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubh instructions (}(hjhhhNhNubjB)}(h``BPF_PSEUDO_MAP_FD``h]hBPF_PSEUDO_MAP_FD}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubhI), and a map’s fd is assigned at load time, so it differs on every run;}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhK&hjubah}(h]h ]h"]h$]h&]uh1jhjhhhhhNubj)}(h**CO-RE relocations** rewrite field offsets, sizes and existence flags against the *running* kernel's BTF, so the result differs from one kernel to the next;h]h)}(h**CO-RE relocations** rewrite field offsets, sizes and existence flags against the *running* kernel's BTF, so the result differs from one kernel to the next;h](j)}(h**CO-RE relocations**h]hCO-RE relocations}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh> rewrite field offsets, sizes and existence flags against the }(hjhhhNhNubj)}(h *running*h]hrunning}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubhC kernel’s BTF, so the result differs from one kernel to the next;}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhK)hjubah}(h]h ]h"]h$]h&]uh1jhjhhhhhNubj)}(hR**kfunc and ksym references** are resolved to ids/addresses in the running kernel;h]h)}(hR**kfunc and ksym references** are resolved to ids/addresses in the running kernel;h](j)}(h**kfunc and ksym references**h]hkfunc and ksym references}(hjDhhhNhNubah}(h]h ]h"]h$]h&]uh1jhj@ubh5 are resolved to ids/addresses in the running kernel;}(hj@hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhK+hj<ubah}(h]h ]h"]h$]h&]uh1jhjhhhhhNubj)}(hX**global data** (``.rodata``/``.data``/``.bss``) is created and seeded as maps at load. h]h)}(hW**global data** (``.rodata``/``.data``/``.bss``) is created and seeded as maps at load.h](j)}(h**global data**h]h global data}(hjjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjfubh (}(hjfhhhNhNubjB)}(h ``.rodata``h]h.rodata}(hj|hhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjfubh/}(hjfhhhNhNubjB)}(h ``.data``h]h.data}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjfubh/}hjfsbjB)}(h``.bss``h]h.bss}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjfubh() is created and seeded as maps at load.}(hjfhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhK-hjbubah}(h]h ]h"]h$]h&]uh1jhjhhhhhNubeh}(h]h ]h"]h$]h&]bullet-uh1jhhhK&hhhhubh)}(hXSo a signature over the original instructions cannot match the relocated instructions the verifier ends up checking, and the relocated form cannot be produced ahead of time because it depends on the target kernel. There is no fixed byte string that is both signable at build time and what the kernel actually loads - which is why a program cannot simply be signed and loaded directly.h]hXSo a signature over the original instructions cannot match the relocated instructions the verifier ends up checking, and the relocated form cannot be produced ahead of time because it depends on the target kernel. There is no fixed byte string that is both signable at build time and what the kernel actually loads - which is why a program cannot simply be signed and loaded directly.}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK0hhhhubeh}(h] motivationah ]h"] motivationah$]h&]uh1hhhhhhhhKubh)}(hhh](h)}(hThe trusted loaderh]hThe trusted loader}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhjhhhhhK8ubh)}(hX'The solution is to move that setup work *into* a small BPF program - the **loader** - and sign the loader instead of the individual programs. libbpf's ``gen_loader`` machinery (``bpftool gen skeleton -L``, the "light skeleton") emits a ``BPF_PROG_TYPE_SYSCALL`` program whose body performs the bpf() syscalls that create maps, apply relocations, and load the real programs. The payload it installs - the serialized programs, map descriptions, relocation data and initial values - lives in a separate array map, the **metadata map** (``__loader.map``).h](h(The solution is to move that setup work }(hjhhhNhNubj)}(h*into*h]hinto}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh a small BPF program - the }(hjhhhNhNubj)}(h **loader**h]hloader}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubhF - and sign the loader instead of the individual programs. libbpf’s }(hjhhhNhNubjB)}(h``gen_loader``h]h gen_loader}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubh machinery (}(hjhhhNhNubjB)}(h``bpftool gen skeleton -L``h]hbpftool gen skeleton -L}(hj+hhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubh$, the “light skeleton”) emits a }(hjhhhNhNubjB)}(h``BPF_PROG_TYPE_SYSCALL``h]hBPF_PROG_TYPE_SYSCALL}(hj=hhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubh program whose body performs the bpf() syscalls that create maps, apply relocations, and load the real programs. The payload it installs - the serialized programs, map descriptions, relocation data and initial values - lives in a separate array map, the }(hjhhhNhNubj)}(h**metadata map**h]h metadata map}(hjOhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh (}(hjhhhNhNubjB)}(h``__loader.map``h]h __loader.map}(hjahhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubh).}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhK:hjhhubh)}(hASo the unit of trust is the loader, and the signing contract is::h]h@So the unit of trust is the loader, and the signing contract is:}(hjyhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKChjhhubh literal_block)}(hSig(I_loader || D_meta)h]hSig(I_loader || D_meta)}hjsbah}(h]h ]h"]h$]h&]hhuh1jhhhKEhjhhubh)}(hXcwhere ``I_loader`` is the loader's instruction stream and ``D_meta`` is the content of the metadata map. Verifying the loader's signature establishes that both the loader *and* the payload it is about to install are authentic. The loader is reproducible: ``gen_loader`` builds it from primitives so the same object yields the same bytes on any build host.h](hwhere }(hjhhhNhNubjB)}(h ``I_loader``h]hI_loader}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubh* is the loader’s instruction stream and }(hjhhhNhNubjB)}(h ``D_meta``h]hD_meta}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubhi is the content of the metadata map. Verifying the loader’s signature establishes that both the loader }(hjhhhNhNubj)}(h*and*h]hand}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubhO the payload it is about to install are authentic. The loader is reproducible: }(hjhhhNhNubjB)}(h``gen_loader``h]h gen_loader}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubhV builds it from primitives so the same object yields the same bytes on any build host.}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKGhjhhubh)}(hhh](h)}(h2Why the loader is signable when the program is noth]h2Why the loader is signable when the program is not}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhjhhhhhKNubh)}(hnThe loader sidesteps every rewrite listed above, because the bytes that are signed are *relocation-invariant*:h](hWThe loader sidesteps every rewrite listed above, because the bytes that are signed are }(hjhhhNhNubj)}(h*relocation-invariant*h]hrelocation-invariant}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh:}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKPhjhhubj)}(hhh](j)}(hXThe loader's own instructions are a fixed sequence of bpf() syscalls emitted by ``gen_loader``; they carry no CO-RE relocations and resolve no ksyms, so they are identical on every kernel. The metadata map is referenced by *index* into ``fd_array`` (``BPF_PSEUDO_MAP_IDX_VALUE``), not by a baked-in file descriptor, so even that reference does not change between build and load. The loader instruction bytes the kernel verifies are exactly the bytes that were signed.h]h)}(hXThe loader's own instructions are a fixed sequence of bpf() syscalls emitted by ``gen_loader``; they carry no CO-RE relocations and resolve no ksyms, so they are identical on every kernel. The metadata map is referenced by *index* into ``fd_array`` (``BPF_PSEUDO_MAP_IDX_VALUE``), not by a baked-in file descriptor, so even that reference does not change between build and load. The loader instruction bytes the kernel verifies are exactly the bytes that were signed.h](hRThe loader’s own instructions are a fixed sequence of bpf() syscalls emitted by }(hj%hhhNhNubjB)}(h``gen_loader``h]h gen_loader}(hj-hhhNhNubah}(h]h ]h"]h$]h&]uh1jAhj%ubh; they carry no CO-RE relocations and resolve no ksyms, so they are identical on every kernel. The metadata map is referenced by }(hj%hhhNhNubj)}(h*index*h]hindex}(hj?hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj%ubh into }(hj%hhhNhNubjB)}(h ``fd_array``h]hfd_array}(hjQhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhj%ubh (}(hj%hhhNhNubjB)}(h``BPF_PSEUDO_MAP_IDX_VALUE``h]hBPF_PSEUDO_MAP_IDX_VALUE}(hjchhhNhNubah}(h]h ]h"]h$]h&]uh1jAhj%ubh), not by a baked-in file descriptor, so even that reference does not change between build and load. The loader instruction bytes the kernel verifies are exactly the bytes that were signed.}(hj%hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKShj!ubah}(h]h ]h"]h$]h&]uh1jhjhhhhhNubj)}(hXOThe metadata map is opaque, frozen data - the serialized target programs, their relocation records, map descriptions and initial values. Its bytes are identical at build time and at load time, so they are simply appended to the instructions and covered by the same signature (there is no separate metadata hash to compute or compare). h]h)}(hXNThe metadata map is opaque, frozen data - the serialized target programs, their relocation records, map descriptions and initial values. Its bytes are identical at build time and at load time, so they are simply appended to the instructions and covered by the same signature (there is no separate metadata hash to compute or compare).h]hXNThe metadata map is opaque, frozen data - the serialized target programs, their relocation records, map descriptions and initial values. Its bytes are identical at build time and at load time, so they are simply appended to the instructions and covered by the same signature (there is no separate metadata hash to compute or compare).}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKZhjubah}(h]h ]h"]h$]h&]uh1jhjhhhhhNubeh}(h]h ]h"]h$]h&]jjuh1jhhhKShjhhubh)}(hXAll the host-specific rewriting - creating maps, patching their fds into the target programs, applying CO-RE, resolving ksyms, seeding global data - still happens, but it happens *inside the loader at runtime*, on the verified metadata, **after** the kernel has verified the ``insns || metadata`` signature. The kernel never has to verify the relocated target programs: it verifies the loader and its inputs once, and trust transfers to whatever that now-trusted, deterministic loader installs. The relocation step is moved from "before the signature can be checked" to "after a trusted program runs" - which is exactly what makes it signable.h](hAll the host-specific rewriting - creating maps, patching their fds into the target programs, applying CO-RE, resolving ksyms, seeding global data - still happens, but it happens }(hjhhhNhNubj)}(h*inside the loader at runtime*h]hinside the loader at runtime}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh, on the verified metadata, }(hjhhhNhNubj)}(h **after**h]hafter}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh the kernel has verified the }(hjhhhNhNubjB)}(h``insns || metadata``h]hinsns || metadata}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubhXc signature. The kernel never has to verify the relocated target programs: it verifies the loader and its inputs once, and trust transfers to whatever that now-trusted, deterministic loader installs. The relocation step is moved from “before the signature can be checked” to “after a trusted program runs” - which is exactly what makes it signable.}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhK`hjhhubh)}(hBecause the metadata map is the loader's only untrusted input, two existing map properties are reused to keep it trustworthy across the load:h]hBecause the metadata map is the loader’s only untrusted input, two existing map properties are reused to keep it trustworthy across the load:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKjhjhhubhdefinition_list)}(hhh](hdefinition_list_item)}(hX^Exclusive maps A map created with ``excl_prog_hash`` (see ``BPF_MAP_CREATE``) may only be accessed by a program whose digest matches that hash. The verifier enforces ``map->excl_prog_sha == prog->digest`` for every map a program uses, so the metadata map is bound to exactly the signed loader and cannot be shared with or mutated by another program. h](hterm)}(hExclusive mapsh]hExclusive maps}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhhhKrhjubh definition)}(hhh]h)}(hXNA map created with ``excl_prog_hash`` (see ``BPF_MAP_CREATE``) may only be accessed by a program whose digest matches that hash. The verifier enforces ``map->excl_prog_sha == prog->digest`` for every map a program uses, so the metadata map is bound to exactly the signed loader and cannot be shared with or mutated by another program.h](hA map created with }(hjhhhNhNubjB)}(h``excl_prog_hash``h]hexcl_prog_hash}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubh (see }(hjhhhNhNubjB)}(h``BPF_MAP_CREATE``h]hBPF_MAP_CREATE}(hj+hhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubhZ) may only be accessed by a program whose digest matches that hash. The verifier enforces }(hjhhhNhNubjB)}(h&``map->excl_prog_sha == prog->digest``h]h"map->excl_prog_sha == prog->digest}(hj=hhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubh for every map a program uses, so the metadata map is bound to exactly the signed loader and cannot be shared with or mutated by another program.}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKnhjubah}(h]h ]h"]h$]h&]uh1j hjubeh}(h]h ]h"]h$]h&]uh1jhhhKrhjubj)}(hXhFrozen maps The metadata map is frozen (``BPF_MAP_FREEZE``) before the loader is loaded. Freezing blocks further userspace writes, so the bytes folded into the signature cannot change before the loader runs. (Freezing does not make the map read-only to the loader program itself, which still writes created file descriptors back into the blob's scratch area.) h](j)}(h Frozen mapsh]h Frozen maps}(hjehhhNhNubah}(h]h ]h"]h$]h&]uh1jhhhKyhjaubj )}(hhh]h)}(hX[The metadata map is frozen (``BPF_MAP_FREEZE``) before the loader is loaded. Freezing blocks further userspace writes, so the bytes folded into the signature cannot change before the loader runs. (Freezing does not make the map read-only to the loader program itself, which still writes created file descriptors back into the blob's scratch area.)h](hThe metadata map is frozen (}(hjvhhhNhNubjB)}(h``BPF_MAP_FREEZE``h]hBPF_MAP_FREEZE}(hj~hhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjvubhX/) before the loader is loaded. Freezing blocks further userspace writes, so the bytes folded into the signature cannot change before the loader runs. (Freezing does not make the map read-only to the loader program itself, which still writes created file descriptors back into the blob’s scratch area.)}(hjvhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKuhjsubah}(h]h ]h"]h$]h&]uh1j hjaubeh}(h]h ]h"]h$]h&]uh1jhhhKyhjhhubeh}(h]h ]h"]h$]h&]uh1jhjhhhhhNubeh}(h]2why-the-loader-is-signable-when-the-program-is-notah ]h"]2why the loader is signable when the program is notah$]h&]uh1hhjhhhhhKNubeh}(h]the-trusted-loaderah ]h"]the trusted loaderah$]h&]uh1hhhhhhhhK8ubh)}(hhh](h)}(hLoad-time verificationh]hLoad-time verification}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhjhhhhhK|ubh)}(hRather than have the loader check its own metadata from within BPF, the kernel verifies it directly at ``BPF_PROG_LOAD``, with no new UAPI. The mechanism reuses the existing ``fd_array``:h](hgRather than have the loader check its own metadata from within BPF, the kernel verifies it directly at }(hjhhhNhNubjB)}(h``BPF_PROG_LOAD``h]h BPF_PROG_LOAD}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubh6, with no new UAPI. The mechanism reuses the existing }(hjhhhNhNubjB)}(h ``fd_array``h]hfd_array}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubh:}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhK~hjhhubhenumerated_list)}(hhh](j)}(htUserspace creates the metadata map with ``excl_prog_hash`` set to the loader's digest, populates it, and freezes it.h]h)}(htUserspace creates the metadata map with ``excl_prog_hash`` set to the loader's digest, populates it, and freezes it.h](h(Userspace creates the metadata map with }(hjhhhNhNubjB)}(h``excl_prog_hash``h]hexcl_prog_hash}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubh< set to the loader’s digest, populates it, and freezes it.}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjubah}(h]h ]h"]h$]h&]uh1jhjhhhhhNubj)}(hThe loader is loaded with ``signature``/``signature_size``/``keyring_id`` set, the metadata map referenced through ``fd_array``, and ``fd_array_cnt`` set so the kernel knows the array's length.h]h)}(hThe loader is loaded with ``signature``/``signature_size``/``keyring_id`` set, the metadata map referenced through ``fd_array``, and ``fd_array_cnt`` set so the kernel knows the array's length.h](hThe loader is loaded with }(hj.hhhNhNubjB)}(h ``signature``h]h signature}(hj6hhhNhNubah}(h]h ]h"]h$]h&]uh1jAhj.ubh/}(hj.hhhNhNubjB)}(h``signature_size``h]hsignature_size}(hjHhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhj.ubh/}hj.sbjB)}(h``keyring_id``h]h keyring_id}(hjZhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhj.ubh* set, the metadata map referenced through }(hj.hhhNhNubjB)}(h ``fd_array``h]hfd_array}(hjlhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhj.ubh, and }(hj.hhhNhNubjB)}(h``fd_array_cnt``h]h fd_array_cnt}(hj~hhhNhNubah}(h]h ]h"]h$]h&]uh1jAhj.ubh. set so the kernel knows the array’s length.}(hj.hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhj*ubah}(h]h ]h"]h$]h&]uh1jhjhhhhhNubj)}(hXSignature verification runs inside the verifier (``bpf_check()``), once it has resolved the ``fd_array`` entries into the program's ``used_maps``. The maps folded into the signature are therefore the very objects the program binds - a single resolution of ``fd_array``, not a separate read, so the verified bytes cannot be swapped for a different map after the check (no time-of-check/time-of-use window). Each folded map must be exclusive (carry ``excl_prog_sha``) and a plain array map (``BPF_MAP_TYPE_ARRAY``); only an array map exposes its value buffer through ``map_direct_value_addr()`` as a kernel address spanning ``value_size`` bytes. A map that is not exclusive, not frozen, or not a plain array is rejected, with a verifier log message naming the offending map. The kernel appends each map's frozen contents to the instruction buffer and verifies the PKCS#7 signature over the concatenation ``insns || metadata_0 || metadata_1 || ...`` in ``used_maps`` order, before it rewrites the (signed) instructions. h]h)}(hXSignature verification runs inside the verifier (``bpf_check()``), once it has resolved the ``fd_array`` entries into the program's ``used_maps``. The maps folded into the signature are therefore the very objects the program binds - a single resolution of ``fd_array``, not a separate read, so the verified bytes cannot be swapped for a different map after the check (no time-of-check/time-of-use window). Each folded map must be exclusive (carry ``excl_prog_sha``) and a plain array map (``BPF_MAP_TYPE_ARRAY``); only an array map exposes its value buffer through ``map_direct_value_addr()`` as a kernel address spanning ``value_size`` bytes. A map that is not exclusive, not frozen, or not a plain array is rejected, with a verifier log message naming the offending map. The kernel appends each map's frozen contents to the instruction buffer and verifies the PKCS#7 signature over the concatenation ``insns || metadata_0 || metadata_1 || ...`` in ``used_maps`` order, before it rewrites the (signed) instructions.h](h1Signature verification runs inside the verifier (}(hjhhhNhNubjB)}(h``bpf_check()``h]h bpf_check()}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubh), once it has resolved the }(hjhhhNhNubjB)}(h ``fd_array``h]hfd_array}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubh entries into the program’s }(hjhhhNhNubjB)}(h ``used_maps``h]h used_maps}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubho. The maps folded into the signature are therefore the very objects the program binds - a single resolution of }(hjhhhNhNubjB)}(h ``fd_array``h]hfd_array}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubh, not a separate read, so the verified bytes cannot be swapped for a different map after the check (no time-of-check/time-of-use window). Each folded map must be exclusive (carry }(hjhhhNhNubjB)}(h``excl_prog_sha``h]h excl_prog_sha}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubh) and a plain array map (}(hjhhhNhNubjB)}(h``BPF_MAP_TYPE_ARRAY``h]hBPF_MAP_TYPE_ARRAY}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubh6); only an array map exposes its value buffer through }(hjhhhNhNubjB)}(h``map_direct_value_addr()``h]hmap_direct_value_addr()}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubh as a kernel address spanning }(hjhhhNhNubjB)}(h``value_size``h]h value_size}(hj&hhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubhX  bytes. A map that is not exclusive, not frozen, or not a plain array is rejected, with a verifier log message naming the offending map. The kernel appends each map’s frozen contents to the instruction buffer and verifies the PKCS#7 signature over the concatenation }(hjhhhNhNubjB)}(h,``insns || metadata_0 || metadata_1 || ...``h]h(insns || metadata_0 || metadata_1 || ...}(hj8hhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubh in }(hjhhhNhNubjB)}(h ``used_maps``h]h used_maps}(hjJhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubh5 order, before it rewrites the (signed) instructions.}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjubah}(h]h ]h"]h$]h&]uh1jhjhhhhhNubeh}(h]h ]h"]h$]h&]enumtypearabicprefixhsuffix.uh1jhjhhhhhKubh)}(hQA signed program therefore takes one of exactly two shapes, both fully supported:h]hQA signed program therefore takes one of exactly two shapes, both fully supported:}(hjshhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubj)}(hhh](j)}(hXq**No bound maps** (``fd_array_cnt == 0``): there is nothing to append, so the kernel verifies the signature over the instructions alone. A valid signature yields ``BPF_SIG_VERIFIED`` and the program loads. This is the ordinary case for a directly-loaded signed program with no separate payload; it is *not* rejected for "missing" metadata, because it has none to cover.h]h)}(hXq**No bound maps** (``fd_array_cnt == 0``): there is nothing to append, so the kernel verifies the signature over the instructions alone. A valid signature yields ``BPF_SIG_VERIFIED`` and the program loads. This is the ordinary case for a directly-loaded signed program with no separate payload; it is *not* rejected for "missing" metadata, because it has none to cover.h](j)}(h**No bound maps**h]h No bound maps}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh (}(hjhhhNhNubjB)}(h``fd_array_cnt == 0``h]hfd_array_cnt == 0}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubhz): there is nothing to append, so the kernel verifies the signature over the instructions alone. A valid signature yields }(hjhhhNhNubjB)}(h``BPF_SIG_VERIFIED``h]hBPF_SIG_VERIFIED}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubhw and the program loads. This is the ordinary case for a directly-loaded signed program with no separate payload; it is }(hjhhhNhNubj)}(h*not*h]hnot}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubhC rejected for “missing” metadata, because it has none to cover.}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjubah}(h]h ]h"]h$]h&]uh1jhjhhhhhNubj)}(h**Exclusive bound maps** (``fd_array_cnt > 0``): every entry is exclusive and folded, so the signature covers ``insns || metadata``. h]h)}(h**Exclusive bound maps** (``fd_array_cnt > 0``): every entry is exclusive and folded, so the signature covers ``insns || metadata``.h](j)}(h**Exclusive bound maps**h]hExclusive bound maps}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh (}(hjhhhNhNubjB)}(h``fd_array_cnt > 0``h]hfd_array_cnt > 0}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubh@): every entry is exclusive and folded, so the signature covers }(hjhhhNhNubjB)}(h``insns || metadata``h]hinsns || metadata}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubh.}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjubah}(h]h ]h"]h$]h&]uh1jhjhhhhhNubeh}(h]h ]h"]h$]h&]jjuh1jhhhKhjhhubh)}(hThere is no third shape: a non-exclusive map in a signed program's ``fd_array`` is rejected rather than silently left out of the signature, so a signed loader never binds a map its signature does not cover.h](hEThere is no third shape: a non-exclusive map in a signed program’s }(hj0hhhNhNubjB)}(h ``fd_array``h]hfd_array}(hj8hhhNhNubah}(h]h ]h"]h$]h&]uh1jAhj0ubh is rejected rather than silently left out of the signature, so a signed loader never binds a map its signature does not cover.}(hj0hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(hXMThe digest binding (``excl_prog_sha == prog->digest``) is enforced by the verifier as usual; because that check runs while ``fd_array`` is resolved - before the verifier would otherwise compute the tag - ``prog->digest`` is computed up front in the verifier, over the unmodified (signature-covered) instructions, for any signed load.h](hThe digest binding (}(hjPhhhNhNubjB)}(h!``excl_prog_sha == prog->digest``h]hexcl_prog_sha == prog->digest}(hjXhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjPubhF) is enforced by the verifier as usual; because that check runs while }(hjPhhhNhNubjB)}(h ``fd_array``h]hfd_array}(hjjhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjPubhE is resolved - before the verifier would otherwise compute the tag - }(hjPhhhNhNubjB)}(h``prog->digest``h]h prog->digest}(hj|hhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjPubhq is computed up front in the verifier, over the unmodified (signature-covered) instructions, for any signed load.}(hjPhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(hXzCoverage is then enforced as the verifier resolves instructions, at the point each object is bound rather than by a count taken afterwards. Once the signature has been verified, binding any further map is refused: a map reached by a directly-referenced fd, or a map swapped into an ``fd_array`` slot the loader reads, is not among those already folded, so it is rejected the moment the verifier tries to bind it. A BTF is refused outright for a signed program - a ksym or a BTF fd in ``fd_array``, whether resolved up front or lazily for a module kfunc, is rejected when it would be bound. Together with the fold rule above this keeps the verdict binary: a signed program cannot use a map its signature does not cover, and a different but equally digest-bound map cannot be substituted at an ``fd_array`` slot. Non-exclusive maps are never folded, so a signed program cannot use one at all.h](hXCoverage is then enforced as the verifier resolves instructions, at the point each object is bound rather than by a count taken afterwards. Once the signature has been verified, binding any further map is refused: a map reached by a directly-referenced fd, or a map swapped into an }(hjhhhNhNubjB)}(h ``fd_array``h]hfd_array}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubh slot the loader reads, is not among those already folded, so it is rejected the moment the verifier tries to bind it. A BTF is refused outright for a signed program - a ksym or a BTF fd in }(hjhhhNhNubjB)}(h ``fd_array``h]hfd_array}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubhX(, whether resolved up front or lazily for a module kfunc, is rejected when it would be bound. Together with the fold rule above this keeps the verdict binary: a signed program cannot use a map its signature does not cover, and a different but equally digest-bound map cannot be substituted at an }(hjhhhNhNubjB)}(h ``fd_array``h]hfd_array}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubhV slot. Non-exclusive maps are never folded, so a signed program cannot use one at all.}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjhhubeh}(h]load-time-verificationah ]h"]load-time verificationah$]h&]uh1hhhhhhhhK|ubh)}(hhh](h)}(h The verdicth]h The verdict}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhjhhhhhKubh)}(hA program is either unsigned or fully verified - there is no intermediate state. The outcome is recorded in ``prog->aux->sig.verdict``:h](hlA program is either unsigned or fully verified - there is no intermediate state. The outcome is recorded in }(hjhhhNhNubjB)}(h``prog->aux->sig.verdict``h]hprog->aux->sig.verdict}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubh:}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjhhubj)}(hQenum bpf_sig_verdict { BPF_SIG_UNSIGNED = 0, BPF_SIG_VERIFIED, };h]hQenum bpf_sig_verdict { BPF_SIG_UNSIGNED = 0, BPF_SIG_VERIFIED, };}hj sbah}(h]h ]h"]h$]h&]hhƌforcelanguagechighlight_args}uh1jhhhKhjhhubh)}(h``BPF_SIG_VERIFIED`` means the signature is valid and covers the instructions *and* the frozen contents of every exclusive map the program uses:h](jB)}(h``BPF_SIG_VERIFIED``h]hBPF_SIG_VERIFIED}(hj( hhhNhNubah}(h]h ]h"]h$]h&]uh1jAhj$ ubh: means the signature is valid and covers the instructions }(hj$ hhhNhNubj)}(h*and*h]hand}(hj: hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj$ ubh= the frozen contents of every exclusive map the program uses:}(hj$ hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjhhubj)}(hhh](j)}(hFor an ordinary, directly-loaded signed program the instructions are the whole artifact and it uses no exclusive maps, so a valid instruction signature is the complete verification.h]h)}(hFor an ordinary, directly-loaded signed program the instructions are the whole artifact and it uses no exclusive maps, so a valid instruction signature is the complete verification.h]hFor an ordinary, directly-loaded signed program the instructions are the whole artifact and it uses no exclusive maps, so a valid instruction signature is the complete verification.}(hjY hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjU ubah}(h]h ]h"]h$]h&]uh1jhjR hhhhhNubj)}(hFor a signed loader the metadata map is exclusive, so its contents are folded in and the signature covers ``insns || metadata``. h]h)}(hFor a signed loader the metadata map is exclusive, so its contents are folded in and the signature covers ``insns || metadata``.h](hjFor a signed loader the metadata map is exclusive, so its contents are folded in and the signature covers }(hjq hhhNhNubjB)}(h``insns || metadata``h]hinsns || metadata}(hjy hhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjq ubh.}(hjq hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjm ubah}(h]h ]h"]h$]h&]uh1jhjR hhhhhNubeh}(h]h ]h"]h$]h&]jjuh1jhhhKhjhhubh)}(hXUThere is deliberately no "instructions verified but metadata not" verdict: a signed loader that fails to cover its metadata is *rejected* (see above), not recorded with a weaker verdict. ``BPF_SIG_VERIFIED`` therefore always means the program and everything the signature is responsible for are authentic, which is what a policy can rely on.h](hThere is deliberately no “instructions verified but metadata not” verdict: a signed loader that fails to cover its metadata is }(hj hhhNhNubj)}(h *rejected*h]hrejected}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj ubh2 (see above), not recorded with a weaker verdict. }(hj hhhNhNubjB)}(h``BPF_SIG_VERIFIED``h]hBPF_SIG_VERIFIED}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jAhj ubh therefore always means the program and everything the signature is responsible for are authentic, which is what a policy can rely on.}(hj hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(h`Alongside the verdict the kernel records which keyring validated the signature; see `Keyrings`_.h](hTAlongside the verdict the kernel records which keyring validated the signature; see }(hj hhhNhNubj)}(h `Keyrings`_h]hKeyrings}(hj hhhNhNubah}(h]h ]h"]h$]h&]nameKeyringsj,keyringsuh1jhj j.Kubh.}(hj hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjhhubeh}(h]j-ah ]h"] the verdictah$]h&]uh1hhhhhhhhK referencedKubh)}(hhh](h)}(hEnforcement via LSMsh]hEnforcement via LSMs}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhj hhhhhKubh)}(hXSigning only *records* a verdict; an LSM turns it into policy. The verdict and keyring fields live in ``struct bpf_prog_aux``, so a BPF LSM program can read them directly (see Documentation/bpf/prog_lsm.rst for writing and attaching BPF LSM programs); the same fields are equally available to in-tree LSMs. Two hooks are useful at different points of the load: the dedicated ``security_bpf_prog_load()`` gates admission before the main verification work, and the existing ``security_bpf_prog()`` observes a program that has fully loaded.h](h Signing only }(hj hhhNhNubj)}(h *records*h]hrecords}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj ubhP a verdict; an LSM turns it into policy. The verdict and keyring fields live in }(hj hhhNhNubjB)}(h``struct bpf_prog_aux``h]hstruct bpf_prog_aux}(hj% hhhNhNubah}(h]h ]h"]h$]h&]uh1jAhj ubh, so a BPF LSM program can read them directly (see Documentation/bpf/prog_lsm.rst for writing and attaching BPF LSM programs); the same fields are equally available to in-tree LSMs. Two hooks are useful at different points of the load: the dedicated }(hj hhhNhNubjB)}(h``security_bpf_prog_load()``h]hsecurity_bpf_prog_load()}(hj7 hhhNhNubah}(h]h ]h"]h$]h&]uh1jAhj ubhE gates admission before the main verification work, and the existing }(hj hhhNhNubjB)}(h``security_bpf_prog()``h]hsecurity_bpf_prog()}(hjI hhhNhNubah}(h]h ]h"]h$]h&]uh1jAhj ubh* observes a program that has fully loaded.}(hj hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhj hhubh)}(hhh](h)}(h'Admission: ``security_bpf_prog_load()``h](h Admission: }(hjd hhhNhNubjB)}(h``security_bpf_prog_load()``h]hsecurity_bpf_prog_load()}(hjl hhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjd ubeh}(h]h ]h"]h$]h&]uh1hhja hhhhhKubh)}(hX/This hook gates admission **for every load**, from a single call site inside the verifier (``bpf_check()``), before the main verification work. It runs after the optional signature verification, so the verdict and keyring fields are final - the hook can see whether, and how strongly, the program was signed, which keyring validated it, the load ``attr``, the BPF token and whether the load came from the kernel. For a signed load the verdict is ``BPF_SIG_VERIFIED`` here (the signature has just been checked); for an unsigned load it is ``BPF_SIG_UNSIGNED``.h](hThis hook gates admission }(hj hhhNhNubj)}(h**for every load**h]hfor every load}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj ubh/, from a single call site inside the verifier (}(hj hhhNhNubjB)}(h``bpf_check()``h]h bpf_check()}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jAhj ubh), before the main verification work. It runs after the optional signature verification, so the verdict and keyring fields are final - the hook can see whether, and how strongly, the program was signed, which keyring validated it, the load }(hj hhhNhNubjB)}(h``attr``h]hattr}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jAhj ubh\, the BPF token and whether the load came from the kernel. For a signed load the verdict is }(hj hhhNhNubjB)}(h``BPF_SIG_VERIFIED``h]hBPF_SIG_VERIFIED}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jAhj ubhH here (the signature has just been checked); for an unsigned load it is }(hj hhhNhNubjB)}(h``BPF_SIG_UNSIGNED``h]hBPF_SIG_UNSIGNED}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jAhj ubh.}(hj hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhja hhubh)}(hXThis is the place for *coarse admission* that must also see unsigned and not-yet-verified loads: require a signature at all, restrict the acceptable keyring, restrict which token/credentials may load BPF, apply per-program-type rules, or audit every load attempt that makes it past signature verification - attempts failing the signature or the metadata binding abort before this hook fires. It is the primary deny point.h](hThis is the place for }(hj hhhNhNubj)}(h*coarse admission*h]hcoarse admission}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj ubhX} that must also see unsigned and not-yet-verified loads: require a signature at all, restrict the acceptable keyring, restrict which token/credentials may load BPF, apply per-program-type rules, or audit every load attempt that makes it past signature verification - attempts failing the signature or the metadata binding abort before this hook fires. It is the primary deny point.}(hj hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhja hhubh)}(hX#One subtlety: this hook runs *before* the verifier finishes its work, so ``BPF_SIG_VERIFIED`` *here* means only "validly signed" - not "loaded". Allowing a load at this point lets it *proceed*; it does not guarantee the program will load. A validly signed program can still be rejected afterwards on two independent grounds: the verifier may reject it like any other program (unsafe memory access, bad control flow, resource limits, ...), and the kernel separately refuses - as the verifier resolves instructions and binds each object - any map the signature does not cover or any BTF at all, regardless of what this hook returned. Only after the program has fully loaded, at the next hook (``security_bpf_prog()``), does ``BPF_SIG_VERIFIED`` carry its full meaning: validly signed *and* fully verified.h](hOne subtlety: this hook runs }(hj hhhNhNubj)}(h*before*h]hbefore}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj ubh$ the verifier finishes its work, so }(hj hhhNhNubjB)}(h``BPF_SIG_VERIFIED``h]hBPF_SIG_VERIFIED}(hj" hhhNhNubah}(h]h ]h"]h$]h&]uh1jAhj ubh }(hj hhhNhNubj)}(h*here*h]hhere}(hj4 hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj ubh[ means only “validly signed” - not “loaded”. Allowing a load at this point lets it }(hj hhhNhNubj)}(h *proceed*h]hproceed}(hjF hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj ubhX; it does not guarantee the program will load. A validly signed program can still be rejected afterwards on two independent grounds: the verifier may reject it like any other program (unsafe memory access, bad control flow, resource limits, ...), and the kernel separately refuses - as the verifier resolves instructions and binds each object - any map the signature does not cover or any BTF at all, regardless of what this hook returned. Only after the program has fully loaded, at the next hook (}(hj hhhNhNubjB)}(h``security_bpf_prog()``h]hsecurity_bpf_prog()}(hjX hhhNhNubah}(h]h ]h"]h$]h&]uh1jAhj ubh), does }(hj hhhNhNubjB)}(h``BPF_SIG_VERIFIED``h]hBPF_SIG_VERIFIED}(hjj hhhNhNubah}(h]h ]h"]h$]h&]uh1jAhj ubh( carry its full meaning: validly signed }(hj hhhNhNubj)}(h*and*h]hand}(hj| hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj ubh fully verified.}(hj hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhja hhubh)}(hX]A more realistic admission policy than "is it signed at all": accept programs signed by a system keyring, accept a user-keyring signature only if the key/keyring it was verified against is on an explicit allowlist, and emit a tamper-evident record of every decision so that even denied attempts are auditable. (Illustrative - error checking elided.)h]hXaA more realistic admission policy than “is it signed at all”: accept programs signed by a system keyring, accept a user-keyring signature only if the key/keyring it was verified against is on an explicit allowlist, and emit a tamper-evident record of every decision so that even denied attempts are auditable. (Illustrative - error checking elided.)}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhja hhubj)}(hXg/* Serials of user keys/keyrings we additionally trust. */ struct { __uint(type, BPF_MAP_TYPE_HASH); __type(key, __s32); /* keyring_serial */ __type(value, __u8); __uint(max_entries, 64); } trusted_user_keys SEC(".maps"); /* Audit stream consumed by a userspace logger. */ struct { __uint(type, BPF_MAP_TYPE_RINGBUF); __uint(max_entries, 1 << 16); } audit SEC(".maps"); struct decision { __u32 prog_type, verdict, ktype; __s32 serial, ret; }; SEC("lsm/bpf_prog_load") int BPF_PROG(admit, struct bpf_prog *prog, union bpf_attr *attr, struct bpf_token *token, bool kernel) { __u32 verdict = prog->aux->sig.verdict; __u32 ktype = prog->aux->sig.keyring_type; __s32 serial = prog->aux->sig.keyring_serial; struct decision *d; int ret = 0; if (kernel) return 0; /* trust in-kernel loads */ if (verdict != BPF_SIG_VERIFIED) ret = -EPERM; /* must be validly signed */ else if (ktype == BPF_SIG_KEYRING_USER && !bpf_map_lookup_elem(&trusted_user_keys, &serial)) ret = -EPERM; /* key/keyring not allowlisted */ d = bpf_ringbuf_reserve(&audit, sizeof(*d), 0); if (d) { d->prog_type = attr->prog_type; d->verdict = verdict; d->ktype = ktype; d->serial = serial; d->ret = ret; bpf_ringbuf_submit(d, 0); /* record allow *and* deny */ } return ret; }h]hXg/* Serials of user keys/keyrings we additionally trust. */ struct { __uint(type, BPF_MAP_TYPE_HASH); __type(key, __s32); /* keyring_serial */ __type(value, __u8); __uint(max_entries, 64); } trusted_user_keys SEC(".maps"); /* Audit stream consumed by a userspace logger. */ struct { __uint(type, BPF_MAP_TYPE_RINGBUF); __uint(max_entries, 1 << 16); } audit SEC(".maps"); struct decision { __u32 prog_type, verdict, ktype; __s32 serial, ret; }; SEC("lsm/bpf_prog_load") int BPF_PROG(admit, struct bpf_prog *prog, union bpf_attr *attr, struct bpf_token *token, bool kernel) { __u32 verdict = prog->aux->sig.verdict; __u32 ktype = prog->aux->sig.keyring_type; __s32 serial = prog->aux->sig.keyring_serial; struct decision *d; int ret = 0; if (kernel) return 0; /* trust in-kernel loads */ if (verdict != BPF_SIG_VERIFIED) ret = -EPERM; /* must be validly signed */ else if (ktype == BPF_SIG_KEYRING_USER && !bpf_map_lookup_elem(&trusted_user_keys, &serial)) ret = -EPERM; /* key/keyring not allowlisted */ d = bpf_ringbuf_reserve(&audit, sizeof(*d), 0); if (d) { d->prog_type = attr->prog_type; d->verdict = verdict; d->ktype = ktype; d->serial = serial; d->ret = ret; bpf_ringbuf_submit(d, 0); /* record allow *and* deny */ } return ret; }}hj sbah}(h]h ]h"]h$]h&]hhj j j! j" }uh1jhhhMhja hhubeh}(h] admission-security-bpf-prog-loadah ]h"]#admission: security_bpf_prog_load()ah$]h&]uh1hhj hhhhhKubh)}(hhh](h)}(h2Observing a verified load: ``security_bpf_prog()``h](hObserving a verified load: }(hj hhhNhNubjB)}(h``security_bpf_prog()``h]hsecurity_bpf_prog()}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jAhj ubeh}(h]h ]h"]h$]h&]uh1hhj hhhhhM9ubh)}(hX3There is deliberately no separate "metadata attested" hook. The coverage check above is enforced by the kernel unconditionally, so a signed loader that fails to cover its metadata never loads and an LSM never has to re-establish that fact. To *act on* a program that has successfully and fully loaded, use the existing ``security_bpf_prog()`` hook (``lsm/bpf_prog``), which fires from ``bpf_prog_new_fd()`` - after the verifier, after the coverage check, and after ``bpf_prog_alloc_id()``. Relative to the admission hook this point is strictly later and stronger:h](hThere is deliberately no separate “metadata attested” hook. The coverage check above is enforced by the kernel unconditionally, so a signed loader that fails to cover its metadata never loads and an LSM never has to re-establish that fact. To }(hj hhhNhNubj)}(h*act on*h]hact on}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj ubhD a program that has successfully and fully loaded, use the existing }(hj hhhNhNubjB)}(h``security_bpf_prog()``h]hsecurity_bpf_prog()}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jAhj ubh hook (}(hj hhhNhNubjB)}(h``lsm/bpf_prog``h]h lsm/bpf_prog}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jAhj ubh), which fires from }(hj hhhNhNubjB)}(h``bpf_prog_new_fd()``h]hbpf_prog_new_fd()}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jAhj ubh; - after the verifier, after the coverage check, and after }(hj hhhNhNubjB)}(h``bpf_prog_alloc_id()``h]hbpf_prog_alloc_id()}(hj( hhhNhNubah}(h]h ]h"]h$]h&]uh1jAhj ubhK. Relative to the admission hook this point is strictly later and stronger:}(hj hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhM;hj hhubj)}(hhh](j)}(hathe program has an id (``prog->aux->id``), so it can be recorded or correlated with later events;h]h)}(hathe program has an id (``prog->aux->id``), so it can be recorded or correlated with later events;h](hthe program has an id (}(hjG hhhNhNubjB)}(h``prog->aux->id``h]h prog->aux->id}(hjO hhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjG ubh9), so it can be recorded or correlated with later events;}(hjG hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMDhjC ubah}(h]h ]h"]h$]h&]uh1jhj@ hhhhhNubj)}(h``verdict == BPF_SIG_VERIFIED`` *here* means **fully** verified - a program that used a map the signature does not cover was already rejected, so it cannot reach this point;h]h)}(h``verdict == BPF_SIG_VERIFIED`` *here* means **fully** verified - a program that used a map the signature does not cover was already rejected, so it cannot reach this point;h](jB)}(h``verdict == BPF_SIG_VERIFIED``h]hverdict == BPF_SIG_VERIFIED}(hju hhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjq ubh }(hjq hhhNhNubj)}(h*here*h]hhere}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jhjq ubh means }(hjq hhhNhNubj)}(h **fully**h]hfully}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jhjq ubhw verified - a program that used a map the signature does not cover was already rejected, so it cannot reach this point;}(hjq hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMFhjm ubah}(h]h ]h"]h$]h&]uh1jhj@ hhhhhNubj)}(hpit observes only programs that actually loaded; a failed load never mints an fd, so it never reaches this hook. h]h)}(hoit observes only programs that actually loaded; a failed load never mints an fd, so it never reaches this hook.h]hoit observes only programs that actually loaded; a failed load never mints an fd, so it never reaches this hook.}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMIhj ubah}(h]h ]h"]h$]h&]uh1jhj@ hhhhhNubeh}(h]h ]h"]h$]h&]jjuh1jhhhMDhj hhubh)}(hXIt takes only the ``prog`` and a non-zero return still aborts (the fd is not handed out), so it can veto as well as observe. One wrinkle: it also fires on other paths that mint a new program fd - notably ``bpf_prog_get_fd_by_id()`` - not just on a fresh load. Because the program already has its id here, an LSM can tell the two apart with a small hash map: the *first* time an id is seen is the load; a later sighting of the same id is just another fd to a program that already exists.h](hIt takes only the }(hj hhhNhNubjB)}(h``prog``h]hprog}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jAhj ubh and a non-zero return still aborts (the fd is not handed out), so it can veto as well as observe. One wrinkle: it also fires on other paths that mint a new program fd - notably }(hj hhhNhNubjB)}(h``bpf_prog_get_fd_by_id()``h]hbpf_prog_get_fd_by_id()}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jAhj ubh - not just on a fresh load. Because the program already has its id here, an LSM can tell the two apart with a small hash map: the }(hj hhhNhNubj)}(h*first*h]hfirst}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj ubhu time an id is seen is the load; a later sighting of the same id is just another fd to a program that already exists.}(hj hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMLhj hhubh)}(hXrTo bound the map and let a reused id read as a fresh load, this can be paired with ``security_bpf_prog_free()`` (``lsm/bpf_prog_free``), which deletes the entry on teardown - keyed by the same ``prog`` pointer, since ``bpf_prog_free_id()`` has already cleared ``prog->aux->id`` to ``0`` by the time that hook runs. (Illustrative - privileged LSM, error checking elided.)h](hSTo bound the map and let a reused id read as a fresh load, this can be paired with }(hj hhhNhNubjB)}(h``security_bpf_prog_free()``h]hsecurity_bpf_prog_free()}(hj! hhhNhNubah}(h]h ]h"]h$]h&]uh1jAhj ubh (}(hj hhhNhNubjB)}(h``lsm/bpf_prog_free``h]hlsm/bpf_prog_free}(hj3 hhhNhNubah}(h]h ]h"]h$]h&]uh1jAhj ubh;), which deletes the entry on teardown - keyed by the same }(hj hhhNhNubjB)}(h``prog``h]hprog}(hjE hhhNhNubah}(h]h ]h"]h$]h&]uh1jAhj ubh pointer, since }(hj hhhNhNubjB)}(h``bpf_prog_free_id()``h]hbpf_prog_free_id()}(hjW hhhNhNubah}(h]h ]h"]h$]h&]uh1jAhj ubh has already cleared }(hj hhhNhNubjB)}(h``prog->aux->id``h]h prog->aux->id}(hji hhhNhNubah}(h]h ]h"]h$]h&]uh1jAhj ubh to }(hj hhhNhNubjB)}(h``0``h]h0}(hj{ hhhNhNubah}(h]h ]h"]h$]h&]uh1jAhj ubhT by the time that hook runs. (Illustrative - privileged LSM, error checking elided.)}(hj hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMThj hhubj)}(hX struct rec { __u32 id, ktype; __s32 serial; }; struct { __uint(type, BPF_MAP_TYPE_HASH); __type(key, __u64); /* struct bpf_prog * -- stable id */ __type(value, struct rec); __uint(max_entries, 4096); } live SEC(".maps"); SEC("lsm/bpf_prog") /* fires after load and on every later fd */ int BPF_PROG(observe, struct bpf_prog *prog) { __u64 key = (__u64)(unsigned long)prog; struct rec r; if (prog->aux->sig.verdict != BPF_SIG_VERIFIED) return 0; if (bpf_map_lookup_elem(&live, &key)) return 0; /* seen before: a later fd, not a load */ /* First sighting == this program just loaded; id is valid here. */ r.id = prog->aux->id; r.ktype = prog->aux->sig.keyring_type; r.serial = prog->aux->sig.keyring_serial; bpf_map_update_elem(&live, &key, &r, BPF_NOEXIST); /* ... newly-loaded verified-program action, e.g. record r.id ... */ return 0; } {h]hX struct rec { __u32 id, ktype; __s32 serial; }; struct { __uint(type, BPF_MAP_TYPE_HASH); __type(key, __u64); /* struct bpf_prog * -- stable id */ __type(value, struct rec); __uint(max_entries, 4096); } live SEC(".maps"); SEC("lsm/bpf_prog") /* fires after load and on every later fd */ int BPF_PROG(observe, struct bpf_prog *prog) { __u64 key = (__u64)(unsigned long)prog; struct rec r; if (prog->aux->sig.verdict != BPF_SIG_VERIFIED) return 0; if (bpf_map_lookup_elem(&live, &key)) return 0; /* seen before: a later fd, not a load */ /* First sighting == this program just loaded; id is valid here. */ r.id = prog->aux->id; r.ktype = prog->aux->sig.keyring_type; r.serial = prog->aux->sig.keyring_serial; bpf_map_update_elem(&live, &key, &r, BPF_NOEXIST); /* ... newly-loaded verified-program action, e.g. record r.id ... */ return 0; }}hj sbah}(h]h ]h"]h$]h&]hhj j j! j" }uh1jhhhMZhj hhubh)}(hXPutting them together: to *require* verified BPF, deny at the admission hook unless the verdict is ``BPF_SIG_VERIFIED`` (and, if desired, restrict the keyring). The kernel then guarantees that any program which actually loads with that verdict covered all of its exclusive maps, rejecting any that did not - so a deny-by-default admission policy needs no second enforcement point. Use ``security_bpf_prog()`` to record or finally gate the verified programs once they carry an id. The ``verdict``, ``keyring_type`` and ``keyring_serial`` fields let a policy distinguish, for example, "verified and signed by a builtin key" from "verified by a user key". A policy LSM such as IPE could consume the same hooks to enforce system policy without writing any BPF, though none implements this today.h](hPutting them together: to }(hj hhhNhNubj)}(h *require*h]hrequire}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jhj ubh@ verified BPF, deny at the admission hook unless the verdict is }(hj hhhNhNubjB)}(h``BPF_SIG_VERIFIED``h]hBPF_SIG_VERIFIED}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jAhj ubhX  (and, if desired, restrict the keyring). The kernel then guarantees that any program which actually loads with that verdict covered all of its exclusive maps, rejecting any that did not - so a deny-by-default admission policy needs no second enforcement point. Use }(hj hhhNhNubjB)}(h``security_bpf_prog()``h]hsecurity_bpf_prog()}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jAhj ubhL to record or finally gate the verified programs once they carry an id. The }(hj hhhNhNubjB)}(h ``verdict``h]hverdict}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jAhj ubh, }(hj hhhNhNubjB)}(h``keyring_type``h]h keyring_type}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jAhj ubh and }(hj hhhNhNubjB)}(h``keyring_serial``h]hkeyring_serial}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhj ubhX fields let a policy distinguish, for example, “verified and signed by a builtin key” from “verified by a user key”. A policy LSM such as IPE could consume the same hooks to enforce system policy without writing any BPF, though none implements this today.}(hj hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMyhj hhubeh}(h]+observing-a-verified-load-security-bpf-progah ]h"].observing a verified load: security_bpf_prog()ah$]h&]uh1hhj hhhhhM9ubeh}(h]enforcement-via-lsmsah ]h"]enforcement via lsmsah$]h&]uh1hhhhhhhhKubh)}(hhh](h)}(hKeyringsh]hKeyrings}(hj/hhhNhNubah}(h]h ]h"]h$]h&]uh1hhj,hhhhhMubh)}(hXO``keyring_id`` selects the trusted keyring the PKCS#7 signature is verified against. The well-known ids ``0`` (builtin), ``VERIFY_USE_SECONDARY_KEYRING`` and ``VERIFY_USE_PLATFORM_KEYRING`` select the corresponding system keyrings; any other value is treated as the serial of a user/session key or keyring. The keyring is looked up first, before the signature bytes are examined, so a signature naming a non-existent keyring is rejected up front, and a failed verification aborts the load - so a program that loads successfully with a signature always has consistent keyring fields recorded.h](jB)}(h``keyring_id``h]h keyring_id}(hjAhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhj=ubhZ selects the trusted keyring the PKCS#7 signature is verified against. The well-known ids }(hj=hhhNhNubjB)}(h``0``h]h0}(hjShhhNhNubah}(h]h ]h"]h$]h&]uh1jAhj=ubh (builtin), }(hj=hhhNhNubjB)}(h ``VERIFY_USE_SECONDARY_KEYRING``h]hVERIFY_USE_SECONDARY_KEYRING}(hjehhhNhNubah}(h]h ]h"]h$]h&]uh1jAhj=ubh and }(hj=hhhNhNubjB)}(h``VERIFY_USE_PLATFORM_KEYRING``h]hVERIFY_USE_PLATFORM_KEYRING}(hjwhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhj=ubhX select the corresponding system keyrings; any other value is treated as the serial of a user/session key or keyring. The keyring is looked up first, before the signature bytes are examined, so a signature naming a non-existent keyring is rejected up front, and a failed verification aborts the load - so a program that loads successfully with a signature always has consistent keyring fields recorded.}(hj=hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhj,hhubh)}(hDTwo fields are recorded in ``prog->aux->sig`` for an LSM to inspect:h](hTwo fields are recorded in }(hjhhhNhNubjB)}(h``prog->aux->sig``h]hprog->aux->sig}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubh for an LSM to inspect:}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhj,hhubj)}(hhh](j)}(hX'``keyring_type`` (``enum bpf_sig_keyring``) Classified purely from ``keyring_id`` whenever the program is signed: ``BPF_SIG_KEYRING_BUILTIN``, ``_SECONDARY``, ``_PLATFORM`` for the system keyrings, or ``_USER`` for a user/session keyring. It is ``BPF_SIG_KEYRING_NONE`` for an unsigned program. h](j)}(h+``keyring_type`` (``enum bpf_sig_keyring``)h](jB)}(h``keyring_type``h]h keyring_type}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubh (}(hjhhhNhNubjB)}(h``enum bpf_sig_keyring``h]henum bpf_sig_keyring}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubh)}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1jhhhMhjubj )}(hhh]h)}(hClassified purely from ``keyring_id`` whenever the program is signed: ``BPF_SIG_KEYRING_BUILTIN``, ``_SECONDARY``, ``_PLATFORM`` for the system keyrings, or ``_USER`` for a user/session keyring. It is ``BPF_SIG_KEYRING_NONE`` for an unsigned program.h](hClassified purely from }(hjhhhNhNubjB)}(h``keyring_id``h]h keyring_id}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubh! whenever the program is signed: }(hjhhhNhNubjB)}(h``BPF_SIG_KEYRING_BUILTIN``h]hBPF_SIG_KEYRING_BUILTIN}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubh, }(hjhhhNhNubjB)}(h``_SECONDARY``h]h _SECONDARY}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubh, }hjsbjB)}(h ``_PLATFORM``h]h _PLATFORM}(hj%hhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubh for the system keyrings, or }(hjhhhNhNubjB)}(h ``_USER``h]h_USER}(hj7hhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubh# for a user/session keyring. It is }(hjhhhNhNubjB)}(h``BPF_SIG_KEYRING_NONE``h]hBPF_SIG_KEYRING_NONE}(hjIhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubh for an unsigned program.}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhjubah}(h]h ]h"]h$]h&]uh1j hjubeh}(h]h ]h"]h$]h&]uh1jhhhMhjubj)}(hX``keyring_serial`` (``s32``) Set **only** on a successful verification, to the serial of the **user/session key or keyring** that ``keyring_id`` resolved to - the object the signature was verified against, not the individual asymmetric key inside it that matched the signer. Passing ``KEY_SPEC_SESSION_KEYRING``, for example, records the session keyring's serial. The system keyrings are trusted as a whole and expose no serial here, so the serial is ``0`` for builtin, secondary and platform signatures, and ``0`` for unsigned programs. In other words, a non-zero ``keyring_serial`` is exactly "verified against the user key/keyring with this serial". h](j)}(h``keyring_serial`` (``s32``)h](jB)}(h``keyring_serial``h]hkeyring_serial}(hjuhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjqubh (}(hjqhhhNhNubjB)}(h``s32``h]hs32}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjqubh)}(hjqhhhNhNubeh}(h]h ]h"]h$]h&]uh1jhhhMhjmubj )}(hhh]h)}(hXoSet **only** on a successful verification, to the serial of the **user/session key or keyring** that ``keyring_id`` resolved to - the object the signature was verified against, not the individual asymmetric key inside it that matched the signer. Passing ``KEY_SPEC_SESSION_KEYRING``, for example, records the session keyring's serial. The system keyrings are trusted as a whole and expose no serial here, so the serial is ``0`` for builtin, secondary and platform signatures, and ``0`` for unsigned programs. In other words, a non-zero ``keyring_serial`` is exactly "verified against the user key/keyring with this serial".h](hSet }(hjhhhNhNubj)}(h**only**h]honly}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh4 on a successful verification, to the serial of the }(hjhhhNhNubj)}(h**user/session key or keyring**h]huser/session key or keyring}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh that }(hjhhhNhNubjB)}(h``keyring_id``h]h keyring_id}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubh resolved to - the object the signature was verified against, not the individual asymmetric key inside it that matched the signer. Passing }(hjhhhNhNubjB)}(h``KEY_SPEC_SESSION_KEYRING``h]hKEY_SPEC_SESSION_KEYRING}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubh, for example, records the session keyring’s serial. The system keyrings are trusted as a whole and expose no serial here, so the serial is }(hjhhhNhNubjB)}(h``0``h]h0}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubh5 for builtin, secondary and platform signatures, and }(hjhhhNhNubjB)}(h``0``h]h0}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubh3 for unsigned programs. In other words, a non-zero }(hjhhhNhNubjB)}(h``keyring_serial``h]hkeyring_serial}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubhI is exactly “verified against the user key/keyring with this serial”.}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhjubah}(h]h ]h"]h$]h&]uh1j hjmubeh}(h]h ]h"]h$]h&]uh1jhhhMhjhhubeh}(h]h ]h"]h$]h&]uh1jhj,hhhhhNubhtable)}(hhh]htgroup)}(hhh](hcolspec)}(hhh]h}(h]h ]h"]h$]h&]colwidthK!uh1jJhjGubjK)}(hhh]h}(h]h ]h"]h$]h&]jUK!uh1jJhjGubjK)}(hhh]h}(h]h ]h"]h$]h&]jUK!uh1jJhjGubhthead)}(hhh]hrow)}(hhh](hentry)}(hhh]h)}(h``keyring_id``h]jB)}(hjyh]h keyring_id}(hj{hhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjwubah}(h]h ]h"]h$]h&]uh1hhhhMhjtubah}(h]h ]h"]h$]h&]uh1jrhjoubjs)}(hhh]h)}(h``keyring_type``h]jB)}(hjh]h keyring_type}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubah}(h]h ]h"]h$]h&]uh1hhhhMhjubah}(h]h ]h"]h$]h&]uh1jrhjoubjs)}(hhh]h)}(h``keyring_serial``h]jB)}(hjh]hkeyring_serial}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubah}(h]h ]h"]h$]h&]uh1hhhhMhjubah}(h]h ]h"]h$]h&]uh1jrhjoubeh}(h]h ]h"]h$]h&]uh1jmhjjubah}(h]h ]h"]h$]h&]uh1jhhjGubhtbody)}(hhh](jn)}(hhh](js)}(hhh]h)}(h(no signature)h]h(no signature)}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjubah}(h]h ]h"]h$]h&]uh1jrhjubjs)}(hhh]h)}(h``BPF_SIG_KEYRING_NONE``h]jB)}(hjh]hBPF_SIG_KEYRING_NONE}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubah}(h]h ]h"]h$]h&]uh1hhhhMhjubah}(h]h ]h"]h$]h&]uh1jrhjubjs)}(hhh]h)}(h``0``h]jB)}(hj$h]h0}(hj&hhhNhNubah}(h]h ]h"]h$]h&]uh1jAhj"ubah}(h]h ]h"]h$]h&]uh1hhhhMhjubah}(h]h ]h"]h$]h&]uh1jrhjubeh}(h]h ]h"]h$]h&]uh1jmhjubjn)}(hhh](js)}(hhh]h)}(h``0``h]jB)}(hjMh]h0}(hjOhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjKubah}(h]h ]h"]h$]h&]uh1hhhhMhjHubah}(h]h ]h"]h$]h&]uh1jrhjEubjs)}(hhh]h)}(h``BPF_SIG_KEYRING_BUILTIN``h]jB)}(hjmh]hBPF_SIG_KEYRING_BUILTIN}(hjohhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjkubah}(h]h ]h"]h$]h&]uh1hhhhMhjhubah}(h]h ]h"]h$]h&]uh1jrhjEubjs)}(hhh]h)}(h``0``h]jB)}(hjh]h0}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubah}(h]h ]h"]h$]h&]uh1hhhhMhjubah}(h]h ]h"]h$]h&]uh1jrhjEubeh}(h]h ]h"]h$]h&]uh1jmhjubjn)}(hhh](js)}(hhh]h)}(h ``VERIFY_USE_SECONDARY_KEYRING``h]jB)}(hjh]hVERIFY_USE_SECONDARY_KEYRING}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubah}(h]h ]h"]h$]h&]uh1hhhhMhjubah}(h]h ]h"]h$]h&]uh1jrhjubjs)}(hhh]h)}(h``BPF_SIG_KEYRING_SECONDARY``h]jB)}(hjh]hBPF_SIG_KEYRING_SECONDARY}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubah}(h]h ]h"]h$]h&]uh1hhhhMhjubah}(h]h ]h"]h$]h&]uh1jrhjubjs)}(hhh]h)}(h``0``h]jB)}(hjh]h0}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubah}(h]h ]h"]h$]h&]uh1hhhhMhjubah}(h]h ]h"]h$]h&]uh1jrhjubeh}(h]h ]h"]h$]h&]uh1jmhjubjn)}(hhh](js)}(hhh]h)}(h``VERIFY_USE_PLATFORM_KEYRING``h]jB)}(hjh]hVERIFY_USE_PLATFORM_KEYRING}(hj!hhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubah}(h]h ]h"]h$]h&]uh1hhhhMhjubah}(h]h ]h"]h$]h&]uh1jrhjubjs)}(hhh]h)}(h``BPF_SIG_KEYRING_PLATFORM``h]jB)}(hj?h]hBPF_SIG_KEYRING_PLATFORM}(hjAhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhj=ubah}(h]h ]h"]h$]h&]uh1hhhhMhj:ubah}(h]h ]h"]h$]h&]uh1jrhjubjs)}(hhh]h)}(h``0``h]jB)}(hj_h]h0}(hjahhhNhNubah}(h]h ]h"]h$]h&]uh1jAhj]ubah}(h]h ]h"]h$]h&]uh1hhhhMhjZubah}(h]h ]h"]h$]h&]uh1jrhjubeh}(h]h ]h"]h$]h&]uh1jmhjubjn)}(hhh](js)}(hhh]h)}(h!other (a user/session key serial)h]h!other (a user/session key serial)}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjubah}(h]h ]h"]h$]h&]uh1jrhjubjs)}(hhh]h)}(h``BPF_SIG_KEYRING_USER``h]jB)}(hjh]hBPF_SIG_KEYRING_USER}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubah}(h]h ]h"]h$]h&]uh1hhhhMhjubah}(h]h ]h"]h$]h&]uh1jrhjubjs)}(hhh]h)}(h"serial of the resolved key/keyringh]h"serial of the resolved key/keyring}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjubah}(h]h ]h"]h$]h&]uh1jrhjubeh}(h]h ]h"]h$]h&]uh1jmhjubeh}(h]h ]h"]h$]h&]uh1jhjGubeh}(h]h ]h"]h$]h&]colsKuh1jEhjBubah}(h]h ]h"]h$]h&]uh1j@hj,hhhNhNubeh}(h]j ah ]h"]keyringsah$]h&]uh1hhhhhhhhMj Kubh)}(hhh](h)}(hProducing a signed objecth]hProducing a signed object}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhjhhhhhMubh)}(h>``bpftool`` generates and signs a light skeleton in one step::h](jB)}(h ``bpftool``h]hbpftool}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubh2 generates and signs a light skeleton in one step:}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhjhhubj)}(hgbpftool gen skeleton -L -S -k -i \ obj.bpf.o > obj.lskel.hh]hgbpftool gen skeleton -L -S -k -i \ obj.bpf.o > obj.lskel.h}hjsbah}(h]h ]h"]h$]h&]hhuh1jhhhMhjhhubh)}(hX?``-L`` selects the light-skeleton (``gen_loader``) backend and ``-S`` enables signing; ``-k`` and ``-i`` supply the signing key and its X.509 certificate. ``bpftool`` signs ``insns || metadata`` - the exact bytes the kernel reconstructs - and also computes ``excl_prog_hash`` as the digest of the loader instructions so the metadata map can be bound to the loader. The signature and hash are embedded in the generated header; the certificate is used only for signing and is not included. Loading the skeleton performs the create/populate/freeze/load sequence described above.h](jB)}(h``-L``h]h-L}(hj0hhhNhNubah}(h]h ]h"]h$]h&]uh1jAhj,ubh selects the light-skeleton (}(hj,hhhNhNubjB)}(h``gen_loader``h]h gen_loader}(hjBhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhj,ubh) backend and }(hj,hhhNhNubjB)}(h``-S``h]h-S}(hjThhhNhNubah}(h]h ]h"]h$]h&]uh1jAhj,ubh enables signing; }(hj,hhhNhNubjB)}(h``-k``h]h-k}(hjfhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhj,ubh and }(hj,hhhNhNubjB)}(h``-i``h]h-i}(hjxhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhj,ubh3 supply the signing key and its X.509 certificate. }(hj,hhhNhNubjB)}(h ``bpftool``h]hbpftool}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhj,ubh signs }(hj,hhhNhNubjB)}(h``insns || metadata``h]hinsns || metadata}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhj,ubh? - the exact bytes the kernel reconstructs - and also computes }(hj,hhhNhNubjB)}(h``excl_prog_hash``h]hexcl_prog_hash}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhj,ubhX, as the digest of the loader instructions so the metadata map can be bound to the loader. The signature and hash are embedded in the generated header; the certificate is used only for signing and is not included. Loading the skeleton performs the create/populate/freeze/load sequence described above.}(hj,hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhjhhubh)}(hAt runtime the trusted public key must be present in the chosen keyring (for example added to the session keyring, or built into the kernel's builtin trusted keyring) for verification to succeed.h]hAt runtime the trusted public key must be present in the chosen keyring (for example added to the session keyring, or built into the kernel’s builtin trusted keyring) for verification to succeed.}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjhhubeh}(h]producing-a-signed-objectah ]h"]producing a signed objectah$]h&]uh1hhhhhhhhMubh)}(hhh](h)}(hUAPI referenceh]hUAPI reference}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhjhhhhhMubh)}(h'``BPF_PROG_LOAD`` (``union bpf_attr``):h](jB)}(h``BPF_PROG_LOAD``h]h BPF_PROG_LOAD}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubh (}(hjhhhNhNubjB)}(h``union bpf_attr``h]hunion bpf_attr}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubh):}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhjhhubj)}(hhh](j)}(hV``signature``, ``signature_size`` Pointer to and length of the PKCS#7 signature blob. h](j)}(h!``signature``, ``signature_size``h](jB)}(h ``signature``h]h signature}(hj&hhhNhNubah}(h]h ]h"]h$]h&]uh1jAhj"ubh, }(hj"hhhNhNubjB)}(h``signature_size``h]hsignature_size}(hj8hhhNhNubah}(h]h ]h"]h$]h&]uh1jAhj"ubeh}(h]h ]h"]h$]h&]uh1jhhhMhjubj )}(hhh]h)}(h3Pointer to and length of the PKCS#7 signature blob.h]h3Pointer to and length of the PKCS#7 signature blob.}(hjOhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjLubah}(h]h ]h"]h$]h&]uh1j hjubeh}(h]h ]h"]h$]h&]uh1jhhhMhjubj)}(h;``keyring_id`` Trusted keyring selector (see `Keyrings`_). h](j)}(h``keyring_id``h]jB)}(hjoh]h keyring_id}(hjqhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjmubah}(h]h ]h"]h$]h&]uh1jhhhMhjiubj )}(hhh]h)}(h+Trusted keyring selector (see `Keyrings`_).h](hTrusted keyring selector (see }(hjhhhNhNubj)}(h `Keyrings`_h]hKeyrings}(hjhhhNhNubah}(h]h ]h"]h$]h&]nameKeyringsj,j uh1jhjj.Kubh).}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhjubah}(h]h ]h"]h$]h&]uh1j hjiubeh}(h]h ]h"]h$]h&]uh1jhhhMhjhhubj)}(hXc``fd_array``, ``fd_array_cnt`` Array of map (and module BTF) file descriptors bound to the program. ``fd_array_cnt`` must be set for the kernel to scan the array. When a signature is present, a BTF entry is rejected outright, and every map must be exclusive; its frozen contents are folded into the verified buffer, and a non-exclusive entry is rejected. h](j)}(h``fd_array``, ``fd_array_cnt``h](jB)}(h ``fd_array``h]hfd_array}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubh, }(hjhhhNhNubjB)}(h``fd_array_cnt``h]h fd_array_cnt}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubeh}(h]h ]h"]h$]h&]uh1jhhhMhjubj )}(hhh]h)}(hXCArray of map (and module BTF) file descriptors bound to the program. ``fd_array_cnt`` must be set for the kernel to scan the array. When a signature is present, a BTF entry is rejected outright, and every map must be exclusive; its frozen contents are folded into the verified buffer, and a non-exclusive entry is rejected.h](hEArray of map (and module BTF) file descriptors bound to the program. }(hjhhhNhNubjB)}(h``fd_array_cnt``h]h fd_array_cnt}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubh must be set for the kernel to scan the array. When a signature is present, a BTF entry is rejected outright, and every map must be exclusive; its frozen contents are folded into the verified buffer, and a non-exclusive entry is rejected.}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhjubah}(h]h ]h"]h$]h&]uh1j hjubeh}(h]h ]h"]h$]h&]uh1jhhhMhjhhubeh}(h]h ]h"]h$]h&]uh1jhjhhhhhNubh)}(h(``BPF_MAP_CREATE`` (``union bpf_attr``):h](jB)}(h``BPF_MAP_CREATE``h]hBPF_MAP_CREATE}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubh (}(hjhhhNhNubjB)}(h``union bpf_attr``h]hunion bpf_attr}(hj.hhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubh):}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhjhhubj)}(hhh]j)}(hX+``excl_prog_hash``, ``excl_prog_hash_size`` SHA-256 digest of the program permitted to access this (exclusive) map. This binds the metadata map to the loader; it is not a hash of the map *content*. The map content is not hashed separately at all - it is covered, as bytes, by the program signature. h](j)}(h+``excl_prog_hash``, ``excl_prog_hash_size``h](jB)}(h``excl_prog_hash``h]hexcl_prog_hash}(hjQhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjMubh, }(hjMhhhNhNubjB)}(h``excl_prog_hash_size``h]hexcl_prog_hash_size}(hjchhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjMubeh}(h]h ]h"]h$]h&]uh1jhhhMhjIubj )}(hhh]h)}(hSHA-256 digest of the program permitted to access this (exclusive) map. This binds the metadata map to the loader; it is not a hash of the map *content*. The map content is not hashed separately at all - it is covered, as bytes, by the program signature.h](hSHA-256 digest of the program permitted to access this (exclusive) map. This binds the metadata map to the loader; it is not a hash of the map }(hjzhhhNhNubj)}(h *content*h]hcontent}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjzubhf. The map content is not hashed separately at all - it is covered, as bytes, by the program signature.}(hjzhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhjwubah}(h]h ]h"]h$]h&]uh1j hjIubeh}(h]h ]h"]h$]h&]uh1jhhhMhjFubah}(h]h ]h"]h$]h&]uh1jhjhhhhhNubeh}(h]uapi-referenceah ]h"]uapi referenceah$]h&]uh1hhhhhhhhMubh)}(hhh](h)}(hNotes and limitationsh]hNotes and limitations}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhjhhhhhMubj)}(hhh](j)}(hThe instructions plus folded metadata are verified as one ``bpf_dynptr``, which bounds the combined size (currently ~16 MiB); very large objects can exceed it.h]h)}(hThe instructions plus folded metadata are verified as one ``bpf_dynptr``, which bounds the combined size (currently ~16 MiB); very large objects can exceed it.h](h:The instructions plus folded metadata are verified as one }(hjhhhNhNubjB)}(h``bpf_dynptr``h]h bpf_dynptr}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubhW, which bounds the combined size (currently ~16 MiB); very large objects can exceed it.}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhjubah}(h]h ]h"]h$]h&]uh1jhjhhhhhNubj)}(haThe metadata container is a single-element array map, accessed through ``map_direct_value_addr``.h]h)}(haThe metadata container is a single-element array map, accessed through ``map_direct_value_addr``.h](hGThe metadata container is a single-element array map, accessed through }(hjhhhNhNubjB)}(h``map_direct_value_addr``h]hmap_direct_value_addr}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jAhjubh.}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhjubah}(h]h ]h"]h$]h&]uh1jhjhhhhhNubeh}(h]h ]h"]h$]h&]jjuh1jhhhMhjhhubeh}(h]notes-and-limitationsah ]h"]notes and limitationsah$]h&]uh1hhhhhhhhMubeh}(h] bpf-signingah ]h"] bpf signingah$]h&]uh1hhhhhhhhKubeh}(h]h ]h"]h$]h&]sourcehuh1hcurrent_sourceN current_lineNsettingsdocutils.frontendValues)}(hN generatorN datestampN source_linkN source_urlN toc_backlinksjrfootnote_backlinksK sectnum_xformKstrip_commentsNstrip_elements_with_classesN strip_classesN report_levelK halt_levelKexit_status_levelKdebugNwarning_streamN tracebackinput_encoding utf-8-siginput_encoding_error_handlerstrictoutput_encodingutf-8output_encoding_error_handlerjTerror_encodingutf-8error_encoding_error_handlerbackslashreplace language_codeenrecord_dependenciesNconfigN id_prefixhauto_id_prefixid dump_settingsNdump_internalsNdump_transformsNdump_pseudo_xmlNexpose_internalsNstrict_visitorN_disable_configN_sourcehnj _destinationN _config_files]7/var/lib/git/docbuild/linux/Documentation/docutils.confafile_insertion_enabled raw_enabledKline_length_limitM'pep_referencesN pep_base_urlhttps://peps.python.org/pep_file_url_templatepep-%04drfc_referencesN rfc_base_url&https://datatracker.ietf.org/doc/html/ tab_widthKtrim_footnote_reference_spacesyntax_highlightlong smart_quotessmartquotes_locales]character_level_inline_markupdoctitle_xform docinfo_xformKsectsubtitle_xform image_loadinglinkembed_stylesheetcloak_email_addressessection_self_linkenvNubreporterNindirect_targets]substitution_defs}substitution_names}refnames}( the verdict]jakeyrings](j jeurefids}nameids}(j/j,jjjjjjjjj j-j)j&j j j!jjj jjjjj'j$u nametypes}(j/jjjjj j)j j!jjjj'uh}(j,hjhjjjjjjj-jj&j j ja jj j j,jjjjj$ju footnote_refs} citation_refs} autofootnotes]autofootnote_refs]symbol_footnotes]symbol_footnote_refs] footnotes] citations]autofootnote_startKsymbol_footnote_startK id_counter collectionsCounter}Rparse_messages]transform_messages] transformerN include_log] decorationNhhub.