[PATCH bpf-next v4 0/9] Verify BPF signed loader at load time

Daniel Borkmann daniel at iogearbox.net
Mon Jul 6 17:47:11 UTC 2026


Hi Paul,

On 7/6/26 7:13 PM, Paul Moore wrote:
> On Mon, Jul 6, 2026 at 9:56 AM Daniel Borkmann <daniel at iogearbox.net> wrote:
>>
>> The BPF signing scheme signs a light skeleton's loader program and lets
>> the loader vouch for everything else: bpftool bakes the SHA256 of the
>> metadata map into the loader's instructions, signs the instructions, and
>> the loader compares the (frozen, exclusive) map against that hash from
>> within BPF once it runs. The construction is sound as a trusted hash
>> chain, but the kernel itself never attests the metadata, and that split
>> has been the recurring objection from the LSM / integrity side since the
>> scheme was proposed.
>>
>> This proposal closes both gaps by having the kernel verify the metadata
>> at BPF_PROG_LOAD time, before the LSM admission hook and before the
>> verifier, /without/ growing the UAPI. A signed loader binds its metadata
>> map(s) through the existing fd_array/fd_array_cnt, and exclusive maps
>> are already bound to the loader's digest via excl_prog_hash. When a
>> signature is present, the kernel collects the exclusive maps from the
>> fd_array and appends their frozen contents to the instructions before
>> PKCS#7 verification, so the signature covers ...
>>
>>      insns || metadata_0 || metadata_1 || [...]
>>
>> ... in fd_array order. The in-loader hash check is dropped from the
>> gen_loader entirely: generated loaders carry no verification logic
>> anymore, and signing or verifying a skeleton becomes an ordinary CMS
>> operation over bytes that sit verbatim in the skeleton, reproducible
>> offline. A signed program is either BPF_SIG_UNSIGNED or BPF_SIG_VERIFIED
>> with nothing in between.
>>
>> There is no new UAPI, we now have a single signature scheme, no LSM
>> code reaching into BPF internals, no new LSM hook, and unsigned loads
>> are completely unaffected. It is also less complex since the loader
>> does not need to deal with BTF, an extra kfunc, etc, as proposed in
>> an earlier series [0]. Tested against full BPF CI which came back
>> green. For more details and examples, see the documentation patch in
>> this series.
>>
>>    [0] https://lore.kernel.org/bpf/20260522023234.3778588-1-kpsingh@kernel.org/
>>
>> v3 -> v4:
>>    - Fix upper limit in MAX_FD_ARRAY_CNT (Anton)
>>    - Reject !fd_array && attr->fd_array_cnt (Anton)
>>    - Add bpftool patch wrt ignored return value of EVP_Digest() (sashiko)
>>    - Fix setting of gen_loader_fixture_init (sashiko)
>>    - Fix unused map_fd cleanup branch in selftest (bot+bpf-ci)
>>    - Remove now unused map->excl member and adjust selftests
>>    - Added more BPF signed_loader corner case selftest coverage
>>    - Added Paul's Nack wrt bpf_prog_load LSM hook dispute
>>    - Added patch 2 to move bigger allocations below fd_array
>>      resolution (Paul)
> 
> If you want to squash patch 2/9 and 3/9 together so that one can't
> easily merge 3/9 without the vzalloc(program) relocation I'll gladly
> drop my NACK on patch 3/9.

Okay, I can do that and send a v5. Btw, I saw one sashiko complaint
about the security_bpf_prog_load() internally not needing the:

   [...]
         if (unlikely(rc))
                 security_bpf_prog_free(prog);
   [...]

since the security_bpf_prog_free() is already called via the regular
teardown path now. While there are no in-tree LSM users that are
affected by this, I'll include this as well into the squashed patch,
so its really only called once and not twice.

>> v2 -> v3:
>>    - Added first commit to cache and work on objects in fd_array
>>      which was the most recent issue sashiko rightfully complained
>>    - Added more BPF signed_loader selftest coverage to cover that
>>      usage of sparse fd_array or map fds gets rejected
>>    - I left the security_bpf_prog_load as in v2 given preference
>>      from BPF side over adding new hook
>> v1 -> v2:
>>    - Addressed both sashiko complaints, the TOCTOU bug regarding
>>      fd_array processing, as well as exclusive map checking to
>>      only allow array maps. The validation is now moved into the
>>      verifier before the main verification work happens. This also
>>      gives the opportunity to utilize the verifier log.
>>
>> Daniel Borkmann (9):
>>    bpf: Resolve and cache fd_array objects at load time
>>    bpf: Move bigger allocations below fd_array resolution
>>    bpf: Verify signed loader metadata at load time
>>    libbpf: Drop in-loader metadata check for load-time verification
>>    bpftool: Check EVP_Digest when computing excl_prog_hash
>>    bpftool: Cover loader metadata with the program signature
>>    selftests/bpf: Adjust bpf_map layout in verifier_map_ptr
>>    selftests/bpf: Verify load-time signed loader metadata
>>    Documentation/bpf: Add BPF signing and enforcement doc
>>
>>   Documentation/bpf/index.rst                   |    1 +
>>   Documentation/bpf/signing.rst                 |  496 ++++++++
>>   include/linux/bpf.h                           |    1 -
>>   include/linux/bpf_verifier.h                  |   23 +-
>>   kernel/bpf/syscall.c                          |   83 +-
>>   kernel/bpf/verifier.c                         |  450 ++++++--
>>   tools/bpf/bpftool/gen.c                       |    2 +
>>   tools/bpf/bpftool/sign.c                      |   24 +-
>>   tools/lib/bpf/bpf_gen_internal.h              |    1 -
>>   tools/lib/bpf/gen_loader.c                    |   76 +-
>>   tools/lib/bpf/libbpf_internal.h               |    1 -
>>   tools/lib/bpf/skel_internal.h                 |   31 +-
>>   .../selftests/bpf/prog_tests/signed_loader.c  | 1004 ++++++++++++++---
>>   .../selftests/bpf/progs/test_signed_loader.c  |    9 +-
>>   .../selftests/bpf/progs/verifier_map_ptr.c    |   23 +-
>>   15 files changed, 1786 insertions(+), 439 deletions(-)
>>   create mode 100644 Documentation/bpf/signing.rst

Thanks,
Daniel




More information about the Linux-security-module-archive mailing list