Signing the immutable parts of BPF programs
-------------------------------------------

An object with BPF bytecode is comprised of multiple ELF sections that follow a
naming convention to tell the libbpf kernel loader where to attach it.

Each of these ELF sections contains BPF bytecode that has to first have
relocations and maps populated, which makes the original object file produced
by clang to be modified in user space, by libbpf, before loading it to the
kernel.

Each of these ELF sections contains BPF bytecode that requires relocations
applied and maps populated, before being used. Thus the original object file
produced by clang has to be modified in user space, by libbpf, before being
loaded into the kernel.

The kernel completely distrusts this bytecode and the BPF verifier will
validate it before proceeding.

In order to sign the BPF bytecode the bpftool utility should have a new
subcommand: 'sign', that will reuse what is in the scripts/sign-file.c program
in the kernel sources.

What differs from kernel modules is that it will first find the mutable parts
in all of the BPF bytecode ELF sections and zero them in a copy of the
bytecode, then sign it using the same methods as used with kernel modules[1].

The signature has to be stored in a new ELF section in the BPF object. This new
ELF section should be named as the signed ELF name + ".sign".

Alternatively we can add a BPF mode to scripts/sign-file.c, so that it does
this process.

Then libbpf needs to be made aware of these new signature ELF sections and add
then to the bpf_attr struct used with sys_bpf(PROG_LOAD).

The relocations should also be made available to the kernel in a new bpf_attr
member.

The BPF syscall handler in the kernel should now have logic similar to what is in
the load_module function in the kernel, i.e. a counterpart to:

   err = module_sig_check(info, flags);

That should be:

   err = bpf_sig_check(info, flags);

Where info has the (BPF bytecode, sig, relocations) tuple, so that it can:

1. In a copy of the bytecode zero the mutable parts (CO-RE relocations, etc)

2. Reuse the module checking mechanism

If this returns zero, then the BPF syscall is allowed to follow the unmodified
course of action, passing it to the BPF verifier, etc.

Alternatively we can altogether forget about touching kernel/bpf/ files and do
it instead in a LSM, as this is in sys_bpf():

        /* copy attributes from user space, may be less than sizeof(bpf_attr) */
        memset(&attr, 0, sizeof(attr));
        if (copy_from_user(&attr, uattr, size) != 0)
                return -EFAULT;

        err = security_bpf(cmd, &attr, size);
        if (err < 0)
                return err;

The BPF verifier shouldn't even know that this module signature checking took
place and doesn't/shouldn't trust it was even performed, i.e. it remains
unchanged.


[1] https://www.kernel.org/doc/html/v5.6/admin-guide/module-signing.html


Thanks to Luis Cláudio, Daniel Bristot, Jiri Olsa and Clark Williams for a
quick review of this text on such a short notice.
