Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'signed-bpf-programs'

KP Singh says:

====================
Signed BPF programs

BPF Signing has gone over multiple discussions in various conferences with the
kernel and BPF community and the following patch series is a culmination
of the current of discussion and signed BPF programs. Once signing is
implemented, the next focus would be to implement the right security policies
for all BPF use-cases (dynamically generated bpf programs, simple non CO-RE
programs).

Signing also paves the way for allowing unrivileged users to
load vetted BPF programs and helps in adhering to the principle of least
privlege by avoiding unnecessary elevation of privileges to CAP_BPF and
CAP_SYS_ADMIN (ofcourse, with the appropriate security policy active).

A early version of this design was proposed in [1]:

The key idea of the design is to use a signing algorithm that allows
us to integrity-protect a number of future payloads, including their
order, by creating a chain of trust.

Consider that Alice needs to send messages M_1, M_2, ..., M_n to Bob.
We define blocks of data such that:

B_n = M_n || H(termination_marker)

(Each block contains its corresponding message and the hash of the
*next* block in the chain.)

B_{n-1} = M_{n-1} || H(B_n)
B_{n-2} = M_{n-2} || H(B_{n-1})

...

B_2 = M_2 || H(B_3)
B_1 = M_1 || H(B_2)

Alice does the following (e.g., on a build system where all payloads
are available):

* Assembles the blocks B_1, B_2, ..., B_n.
* Calculates H(B_1) and signs it, yielding Sig(H(B_1)).

Alice sends the following to Bob:

M_1, H(B_2), Sig(H(B_1))

Bob receives this payload and does the following:

* Reconstructs B_1 as B_1' using the received M_1 and H(B_2)
(i.e., B_1' = M_1 || H(B_2)).
* Recomputes H(B_1') and verifies the signature against the
received Sig(H(B_1)).
* If the signature verifies, it establishes the integrity of M_1
and H(B_2) (and transitively, the integrity of the entire chain). Bob
now stores the verified H(B_2) until it receives the next message.
* When Bob receives M_2 (and H(B_3) if n > 2), it reconstructs
B_2' (e.g., B_2' = M_2 || H(B_3), or if n=2, B_2' = M_2 ||
H(termination_marker)). Bob then computes H(B_2') and compares it
against the stored H(B_2) that was verified in the previous step.

This process continues until the last block is received and verified.

Now, applying this to the BPF signing use-case, we simplify to two messages:

M_1 = I_loader (the instructions of the loader program)
M_2 = M_metadata (the metadata for the loader program, passed in a
map, which includes the programs to be loaded and other context)

For this specific BPF case, we will directly sign a composite of the
first message and the hash of the second. Let H_meta = H(M_metadata).
The block to be signed is effectively:

B_signed = I_loader || H_meta

The signature generated is Sig(B_signed).

The process then follows a similar pattern to the Alice and Bob model,
where the kernel (Bob) verifies I_loader and H_meta using the
signature. Then, the trusted I_loader is responsible for verifying
M_metadata against the trusted H_meta.

From an implementation standpoint:

bpftool (or some other tool in a trusted build environment) knows
about the metadata (M_metadata) and the loader program (I_loader). It
first calculates H_meta = H(M_metadata). Then it constructs the object
to be signed and computes the signature:

Sig(I_loader || H_meta)

The loader program and the metadata are a hermetic representation of the source
of the eBPF program, its maps and context. The loader program is generated by
libbpf as a part of a standard API i.e. bpf_object__gen_loader.

While users can use light skeletons as a convenient method to use signing
support, they can directly use the loader program generation using libbpf
(bpf_object__gen_loader) into their own trusted toolchains.

libbpf, which has access to the program's instruction buffer is a key part of
the TCB of the build environment

An advanced threat model that does not intend to depend on libbpf (or any provenant
userspace BPF libraries) due to supply chain risks despite it being developed
in the kernel source and by the kernel community will require reimplmenting a
lot of the core BPF userspace support (like instruction relocation, map handling).

Such an advanced user would also need to integrate the generation of the loader
into their toolchain.

Given that many use-cases (e.g. Cilium) generate trusted BPF programs,
trusted loaders are an inevitability and a requirement for signing support, a
entrusting loader programs will be a fundamental requirement for an security
policy.

The initial instructions of the loader program verify the SHA256 hash
of the metadata (M_metadata) that will be passed in a map. These instructions
effectively embed the precomputed H_meta as immediate values.

ld_imm64 r1, const_ptr_to_map // insn[0].src_reg == BPF_PSEUDO_MAP_IDX
r2 = *(u64 *)(r1 + 0);
ld_imm64 r3, sha256_of_map_part1 // precomputed by bpf_object__gen_load/libbpf (H_meta_1)
if r2 != r3 goto out;

r2 = *(u64 *)(r1 + 8);
ld_imm64 r3, sha256_of_map_part2 // precomputed by bpf_object__gen_load/libbpf (H_meta_2)
if r2 != r3 goto out;

r2 = *(u64 *)(r1 + 16);
ld_imm64 r3, sha256_of_map_part3 // precomputed by bpf_object__gen_load/libbpf (H_meta_3)
if r2 != r3 goto out;

r2 = *(u64 *)(r1 + 24);
ld_imm64 r3, sha256_of_map_part4 // precomputed by bpf_object__gen_load/libbpf (H_meta_4)
if r2 != r3 goto out;
...

This implicitly makes the payload equivalent to the signed block (B_signed)

I_loader || H_meta

bpftool then generates the signature of this I_loader payload (which
now contains the expected H_meta) using a key and an identity:

This signature is stored in bpf_attr, which is extended as follows for
the BPF_PROG_LOAD command:

__aligned_u64 signature;
__u32 signature_size;
__u32 keyring_id;

The reasons for a simpler UAPI is that it's more future proof (e.g.) with more
stable instruction buffers, loader programs being directly into the compilers.
A simple API also allows simple programs e.g. for networking that don't need
loader programs to directly use signing.

OBJ_GET_INFO_BY_FD is used to get information about BPF objects (maps, programs, links) and
returning the hash of the map is a natural extension of the UAPI as it can be
helpful for debugging, fingerprinting etc.

Currently, it's only implemented for BPF_MAP_TYPE_ARRAY. It can be trivially
extended for BPF programs to return the complete SHA256 along with the tag.

The SHA is stored in struct bpf_map for exclusive and frozen maps

struct bpf_map {
+ u64 sha[4];
const struct bpf_map_ops *ops;
struct bpf_map *inner_map_meta;
};

Exclusivity ensures that the map can only be used by a future BPF
program whose SHA256 hash matches sha256_of_future_prog.

First, bpf_prog_calc_tag() is updated to compute the SHA256 instead of
SHA1, and this hash is stored in struct bpf_prog_aux:

@@ -1588,6 +1588,7 @@ struct bpf_prog_aux {
int cgroup_atype; /* enum cgroup_bpf_attach_type */
struct bpf_map *cgroup_storage[MAX_BPF_CGROUP_STORAGE_TYPE];
char name[BPF_OBJ_NAME_LEN];
+ u64 sha[4];
u64 (*bpf_exception_cb)(u64 cookie, u64 sp, u64 bp, u64, u64);
// ...
};

An exclusive is created by passing an excl_prog_hash
(and excl_prog_hash_size) in the BPF_MAP_CREATE command.
When a BPF program is subsequently loaded and it attempts to use this map,
the kernel will compare the program's own SHA256 hash against the one
registered with the map, if matching, it will be added to prog->used_maps[].

The program load will fail if the hashes do not match or if the map is
already in use by another (non-matching) exclusive program.

Exclusive maps ensure that no other BPF programs and compromise the intergity of
the map post the signature verification.

NOTE: Exclusive maps cannot be added as inner maps.

err = map_fd = skel_map_create(BPF_MAP_TYPE_ARRAY, "__loader.map",
opts->excl_prog_hash,
opts->excl_prog_hash_sz, 4,
opts->data_sz, 1);
err = skel_map_update_elem(map_fd, &key, opts->data, 0);

err = skel_map_freeze(map_fd);

// Kernel computes the hash of the map.
err = skel_obj_get_info_by_fd(map_fd);

memset(&attr, 0, prog_load_attr_sz);
attr.prog_type = BPF_PROG_TYPE_SYSCALL;
attr.insns = (long) opts->insns;
attr.insn_cnt = opts->insns_sz / sizeof(struct bpf_insn);
attr.signature = (long) opts->signature;
attr.signature_size = opts->signature_sz;
attr.keyring_id = opts->keyring_id;
attr.license = (long) "Dual BSD/GPL";

The kernel will:

* Compute the hash of the provided I_loader bytecode.
* Verify the signature against this computed hash.
* Check if the metadata map (now exclusive) is intended for this
program's hash.

The signature check happens in BPF_PROG_LOAD before the security_bpf_prog
LSM hook.

This ensures that the loaded loader program (I_loader), including the
embedded expected hash of the metadata (H_meta), is trusted.
Since the loader program is now trusted, it can be entrusted to verify
the actual metadata (M_metadata) read from the (now exclusive and
frozen) map against the embedded (and trusted) H_meta. There is no
Time-of-Check-Time-of-Use (TOCTOU) vulnerability here because:

* The signature covers the I_loader and its embedded H_meta.
* The metadata map M_metadata is frozen before the loader program is loaded
and associated with it.
* The map is made exclusive to the specific (signed and verified)
loader program.

[1] https://lore.kernel.org/bpf/CACYkzJ6VQUExfyt0=-FmXz46GHJh3d=FXh5j4KfexcEFbHV-vg@mail.gmail.com/
====================

Link: https://patch.msgid.link/20250921160120.9711-1-kpsingh@kernel.org
Signed-off-by: Alexei Starovoitov <ast@kernel.org>

+660 -33
+1
crypto/asymmetric_keys/pkcs7_verify.c
··· 429 429 /* Authattr presence checked in parser */ 430 430 break; 431 431 case VERIFYING_UNSPECIFIED_SIGNATURE: 432 + case VERIFYING_BPF_SIGNATURE: 432 433 if (pkcs7->data_type != OID_data) { 433 434 pr_warn("Invalid unspecified sig (not pkcs7-data)\n"); 434 435 return -EKEYREJECTED;
+1
include/linux/verification.h
··· 36 36 VERIFYING_KEY_SIGNATURE, 37 37 VERIFYING_KEY_SELF_SIGNATURE, 38 38 VERIFYING_UNSPECIFIED_SIGNATURE, 39 + VERIFYING_BPF_SIGNATURE, 39 40 NR__KEY_BEING_USED_FOR 40 41 }; 41 42 #ifdef CONFIG_SYSTEM_DATA_VERIFICATION
+10
include/uapi/linux/bpf.h
··· 1611 1611 * continuous. 1612 1612 */ 1613 1613 __u32 fd_array_cnt; 1614 + /* Pointer to a buffer containing the signature of the BPF 1615 + * program. 1616 + */ 1617 + __aligned_u64 signature; 1618 + /* Size of the signature buffer in bytes. */ 1619 + __u32 signature_size; 1620 + /* ID of the kernel keyring to be used for signature 1621 + * verification. 1622 + */ 1623 + __s32 keyring_id; 1614 1624 }; 1615 1625 1616 1626 struct { /* anonymous struct used by BPF_OBJ_* commands */
+1 -1
kernel/bpf/helpers.c
··· 3898 3898 3899 3899 return verify_pkcs7_signature(data, data_len, sig, sig_len, 3900 3900 trusted_keyring->key, 3901 - VERIFYING_UNSPECIFIED_SIGNATURE, NULL, 3901 + VERIFYING_BPF_SIGNATURE, NULL, 3902 3902 NULL); 3903 3903 #else 3904 3904 return -EOPNOTSUPP;
+44 -1
kernel/bpf/syscall.c
··· 39 39 #include <linux/tracepoint.h> 40 40 #include <linux/overflow.h> 41 41 #include <linux/cookie.h> 42 + #include <linux/verification.h> 42 43 43 44 #include <net/netfilter/nf_bpf_link.h> 44 45 #include <net/netkit.h> ··· 2786 2785 } 2787 2786 } 2788 2787 2788 + static int bpf_prog_verify_signature(struct bpf_prog *prog, union bpf_attr *attr, 2789 + bool is_kernel) 2790 + { 2791 + bpfptr_t usig = make_bpfptr(attr->signature, is_kernel); 2792 + struct bpf_dynptr_kern sig_ptr, insns_ptr; 2793 + struct bpf_key *key = NULL; 2794 + void *sig; 2795 + int err = 0; 2796 + 2797 + if (system_keyring_id_check(attr->keyring_id) == 0) 2798 + key = bpf_lookup_system_key(attr->keyring_id); 2799 + else 2800 + key = bpf_lookup_user_key(attr->keyring_id, 0); 2801 + 2802 + if (!key) 2803 + return -EINVAL; 2804 + 2805 + sig = kvmemdup_bpfptr(usig, attr->signature_size); 2806 + if (IS_ERR(sig)) { 2807 + bpf_key_put(key); 2808 + return -ENOMEM; 2809 + } 2810 + 2811 + bpf_dynptr_init(&sig_ptr, sig, BPF_DYNPTR_TYPE_LOCAL, 0, 2812 + attr->signature_size); 2813 + bpf_dynptr_init(&insns_ptr, prog->insnsi, BPF_DYNPTR_TYPE_LOCAL, 0, 2814 + prog->len * sizeof(struct bpf_insn)); 2815 + 2816 + err = bpf_verify_pkcs7_signature((struct bpf_dynptr *)&insns_ptr, 2817 + (struct bpf_dynptr *)&sig_ptr, key); 2818 + 2819 + bpf_key_put(key); 2820 + kvfree(sig); 2821 + return err; 2822 + } 2823 + 2789 2824 /* last field in 'union bpf_attr' used by this command */ 2790 - #define BPF_PROG_LOAD_LAST_FIELD fd_array_cnt 2825 + #define BPF_PROG_LOAD_LAST_FIELD keyring_id 2791 2826 2792 2827 static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size) 2793 2828 { ··· 2986 2949 2987 2950 /* eBPF programs must be GPL compatible to use GPL-ed functions */ 2988 2951 prog->gpl_compatible = license_is_gpl_compatible(license) ? 1 : 0; 2952 + 2953 + if (attr->signature) { 2954 + err = bpf_prog_verify_signature(prog, attr, uattr.is_kernel); 2955 + if (err) 2956 + goto free_prog; 2957 + } 2989 2958 2990 2959 prog->orig_prog = NULL; 2991 2960 prog->jited = 0;
+12 -1
tools/bpf/bpftool/Documentation/bpftool-gen.rst
··· 16 16 17 17 **bpftool** [*OPTIONS*] **gen** *COMMAND* 18 18 19 - *OPTIONS* := { |COMMON_OPTIONS| | { **-L** | **--use-loader** } } 19 + *OPTIONS* := { |COMMON_OPTIONS| | { **-L** | **--use-loader** } | [ { **-S** | **--sign** } {**-k** <private_key.pem>} **-i** <certificate.x509> ] } 20 20 21 21 *COMMAND* := { **object** | **skeleton** | **help** } 22 22 ··· 185 185 For skeletons, generate a "light" skeleton (also known as "loader" 186 186 skeleton). A light skeleton contains a loader eBPF program. It does not use 187 187 the majority of the libbpf infrastructure, and does not need libelf. 188 + 189 + -S, --sign 190 + For skeletons, generate a signed skeleton. This option must be used with 191 + **-k** and **-i**. Using this flag implicitly enables **--use-loader**. 192 + 193 + -k <private_key.pem> 194 + Path to the private key file in PEM format, required for signing. 195 + 196 + -i <certificate.x509> 197 + Path to the X.509 certificate file in PEM or DER format, required for 198 + signing. 188 199 189 200 EXAMPLES 190 201 ========
+13 -1
tools/bpf/bpftool/Documentation/bpftool-prog.rst
··· 18 18 19 19 *OPTIONS* := { |COMMON_OPTIONS| | 20 20 { **-f** | **--bpffs** } | { **-m** | **--mapcompat** } | { **-n** | **--nomount** } | 21 - { **-L** | **--use-loader** } } 21 + { **-L** | **--use-loader** } | [ { **-S** | **--sign** } **-k** <private_key.pem> **-i** <certificate.x509> ] } 22 22 23 23 *COMMANDS* := 24 24 { **show** | **list** | **dump xlated** | **dump jited** | **pin** | **load** | ··· 247 247 the **bpf_trace_printk**\ () helper to log each step of loading BTF, 248 248 creating the maps, and loading the programs (see **bpftool prog tracelog** 249 249 as a way to dump those messages). 250 + 251 + -S, --sign 252 + Enable signing of the BPF program before loading. This option must be 253 + used with **-k** and **-i**. Using this flag implicitly enables 254 + **--use-loader**. 255 + 256 + -k <private_key.pem> 257 + Path to the private key file in PEM format, required when signing. 258 + 259 + -i <certificate.x509> 260 + Path to the X.509 certificate file in PEM or DER format, required when 261 + signing. 250 262 251 263 EXAMPLES 252 264 ========
+3 -3
tools/bpf/bpftool/Makefile
··· 130 130 endif 131 131 endif 132 132 133 - LIBS = $(LIBBPF) -lelf -lz 134 - LIBS_BOOTSTRAP = $(LIBBPF_BOOTSTRAP) -lelf -lz 133 + LIBS = $(LIBBPF) -lelf -lz -lcrypto 134 + LIBS_BOOTSTRAP = $(LIBBPF_BOOTSTRAP) -lelf -lz -lcrypto 135 135 136 136 ifeq ($(feature-libelf-zstd),1) 137 137 LIBS += -lzstd ··· 194 194 195 195 BPFTOOL_BOOTSTRAP := $(BOOTSTRAP_OUTPUT)bpftool 196 196 197 - BOOTSTRAP_OBJS = $(addprefix $(BOOTSTRAP_OUTPUT),main.o common.o json_writer.o gen.o btf.o) 197 + BOOTSTRAP_OBJS = $(addprefix $(BOOTSTRAP_OUTPUT),main.o common.o json_writer.o gen.o btf.o sign.o) 198 198 $(BOOTSTRAP_OBJS): $(LIBBPF_BOOTSTRAP) 199 199 200 200 OBJS = $(patsubst %.c,$(OUTPUT)%.o,$(SRCS)) $(OUTPUT)disasm.o
+4
tools/bpf/bpftool/cgroup.c
··· 2 2 // Copyright (C) 2017 Facebook 3 3 // Author: Roman Gushchin <guro@fb.com> 4 4 5 + #undef GCC_VERSION 6 + #ifndef _GNU_SOURCE 7 + #define _GNU_SOURCE 8 + #endif 5 9 #define _XOPEN_SOURCE 500 6 10 #include <errno.h> 7 11 #include <fcntl.h>
+64 -4
tools/bpf/bpftool/gen.c
··· 688 688 static int gen_trace(struct bpf_object *obj, const char *obj_name, const char *header_guard) 689 689 { 690 690 DECLARE_LIBBPF_OPTS(gen_loader_opts, opts); 691 + struct bpf_load_and_run_opts sopts = {}; 692 + char sig_buf[MAX_SIG_SIZE]; 693 + __u8 prog_sha[SHA256_DIGEST_LENGTH]; 691 694 struct bpf_map *map; 695 + 692 696 char ident[256]; 693 697 int err = 0; 698 + 699 + if (sign_progs) 700 + opts.gen_hash = true; 694 701 695 702 err = bpf_object__gen_loader(obj, &opts); 696 703 if (err) ··· 708 701 p_err("failed to load object file"); 709 702 goto out; 710 703 } 704 + 711 705 /* If there was no error during load then gen_loader_opts 712 706 * are populated with the loader program. 713 707 */ ··· 788 780 print_hex(opts.insns, opts.insns_sz); 789 781 codegen("\ 790 782 \n\ 791 - \"; \n\ 792 - \n\ 783 + \";\n"); 784 + 785 + if (sign_progs) { 786 + sopts.insns = opts.insns; 787 + sopts.insns_sz = opts.insns_sz; 788 + sopts.excl_prog_hash = prog_sha; 789 + sopts.excl_prog_hash_sz = sizeof(prog_sha); 790 + sopts.signature = sig_buf; 791 + sopts.signature_sz = MAX_SIG_SIZE; 792 + 793 + err = bpftool_prog_sign(&sopts); 794 + if (err < 0) { 795 + p_err("failed to sign program"); 796 + goto out; 797 + } 798 + 799 + codegen("\ 800 + \n\ 801 + static const char opts_sig[] __attribute__((__aligned__(8))) = \"\\\n\ 802 + "); 803 + print_hex((const void *)sig_buf, sopts.signature_sz); 804 + codegen("\ 805 + \n\ 806 + \";\n"); 807 + 808 + codegen("\ 809 + \n\ 810 + static const char opts_excl_hash[] __attribute__((__aligned__(8))) = \"\\\n\ 811 + "); 812 + print_hex((const void *)prog_sha, sizeof(prog_sha)); 813 + codegen("\ 814 + \n\ 815 + \";\n"); 816 + 817 + codegen("\ 818 + \n\ 819 + opts.signature = (void *)opts_sig; \n\ 820 + opts.signature_sz = sizeof(opts_sig) - 1; \n\ 821 + opts.excl_prog_hash = (void *)opts_excl_hash; \n\ 822 + opts.excl_prog_hash_sz = sizeof(opts_excl_hash) - 1; \n\ 823 + opts.keyring_id = skel->keyring_id; \n\ 824 + "); 825 + } 826 + 827 + codegen("\ 828 + \n\ 793 829 opts.ctx = (struct bpf_loader_ctx *)skel; \n\ 794 830 opts.data_sz = sizeof(opts_data) - 1; \n\ 795 831 opts.data = (void *)opts_data; \n\ ··· 1292 1240 err = -errno; 1293 1241 libbpf_strerror(err, err_buf, sizeof(err_buf)); 1294 1242 p_err("failed to open BPF object file: %s", err_buf); 1295 - goto out; 1243 + goto out_obj; 1296 1244 } 1297 1245 1298 1246 bpf_object__for_each_map(map, obj) { ··· 1405 1353 } 1406 1354 1407 1355 printf("\t} links;\n"); 1356 + } 1357 + 1358 + if (sign_progs) { 1359 + codegen("\ 1360 + \n\ 1361 + __s32 keyring_id; \n\ 1362 + "); 1408 1363 } 1409 1364 1410 1365 if (btf) { ··· 1611 1552 err = 0; 1612 1553 out: 1613 1554 bpf_object__close(obj); 1555 + out_obj: 1614 1556 if (obj_data) 1615 1557 munmap(obj_data, mmap_sz); 1616 1558 close(fd); ··· 1990 1930 " %1$s %2$s help\n" 1991 1931 "\n" 1992 1932 " " HELP_SPEC_OPTIONS " |\n" 1993 - " {-L|--use-loader} }\n" 1933 + " {-L|--use-loader} | [ {-S|--sign } {-k} <private_key.pem> {-i} <certificate.x509> ]}\n" 1994 1934 "", 1995 1935 bin_name, "gen"); 1996 1936
+25 -1
tools/bpf/bpftool/main.c
··· 33 33 bool use_loader; 34 34 struct btf *base_btf; 35 35 struct hashmap *refs_table; 36 + bool sign_progs; 37 + const char *private_key_path; 38 + const char *cert_path; 36 39 37 40 static void __noreturn clean_and_exit(int i) 38 41 { ··· 451 448 { "nomount", no_argument, NULL, 'n' }, 452 449 { "debug", no_argument, NULL, 'd' }, 453 450 { "use-loader", no_argument, NULL, 'L' }, 451 + { "sign", no_argument, NULL, 'S' }, 454 452 { "base-btf", required_argument, NULL, 'B' }, 455 453 { 0 } 456 454 }; ··· 478 474 bin_name = "bpftool"; 479 475 480 476 opterr = 0; 481 - while ((opt = getopt_long(argc, argv, "VhpjfLmndB:l", 477 + while ((opt = getopt_long(argc, argv, "VhpjfLmndSi:k:B:l", 482 478 options, NULL)) >= 0) { 483 479 switch (opt) { 484 480 case 'V': ··· 524 520 case 'L': 525 521 use_loader = true; 526 522 break; 523 + case 'S': 524 + sign_progs = true; 525 + use_loader = true; 526 + break; 527 + case 'k': 528 + private_key_path = optarg; 529 + break; 530 + case 'i': 531 + cert_path = optarg; 532 + break; 527 533 default: 528 534 p_err("unrecognized option '%s'", argv[optind - 1]); 529 535 if (json_output) ··· 547 533 argv += optind; 548 534 if (argc < 0) 549 535 usage(); 536 + 537 + if (sign_progs && (private_key_path == NULL || cert_path == NULL)) { 538 + p_err("-i <identity_x509_cert> and -k <private_key> must be supplied with -S for signing"); 539 + return -EINVAL; 540 + } 541 + 542 + if (!sign_progs && (private_key_path != NULL || cert_path != NULL)) { 543 + p_err("--sign (or -S) must be explicitly passed with -i <identity_x509_cert> and -k <private_key> to sign the programs"); 544 + return -EINVAL; 545 + } 550 546 551 547 if (version_requested) 552 548 ret = do_version(argc, argv);
+11
tools/bpf/bpftool/main.h
··· 6 6 7 7 /* BFD and kernel.h both define GCC_VERSION, differently */ 8 8 #undef GCC_VERSION 9 + #ifndef _GNU_SOURCE 10 + #define _GNU_SOURCE 11 + #endif 9 12 #include <stdbool.h> 10 13 #include <stdio.h> 14 + #include <errno.h> 11 15 #include <stdlib.h> 16 + #include <bpf/skel_internal.h> 12 17 #include <linux/bpf.h> 13 18 #include <linux/compiler.h> 14 19 #include <linux/kernel.h> ··· 57 52 }) 58 53 59 54 #define ERR_MAX_LEN 1024 55 + #define MAX_SIG_SIZE 4096 60 56 61 57 #define BPF_TAG_FMT "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx" 62 58 ··· 91 85 extern bool use_loader; 92 86 extern struct btf *base_btf; 93 87 extern struct hashmap *refs_table; 88 + extern bool sign_progs; 89 + extern const char *private_key_path; 90 + extern const char *cert_path; 94 91 95 92 void __printf(1, 2) p_err(const char *fmt, ...); 96 93 void __printf(1, 2) p_info(const char *fmt, ...); ··· 293 284 int read_kernel_config(const struct kernel_config_option *requested_options, 294 285 size_t num_options, char **out_values, 295 286 const char *define_prefix); 287 + int bpftool_prog_sign(struct bpf_load_and_run_opts *opts); 288 + __u32 register_session_key(const char *key_der_path); 296 289 #endif
+28 -1
tools/bpf/bpftool/prog.c
··· 23 23 #include <linux/err.h> 24 24 #include <linux/perf_event.h> 25 25 #include <linux/sizes.h> 26 + #include <linux/keyctl.h> 26 27 27 28 #include <bpf/bpf.h> 28 29 #include <bpf/btf.h> ··· 1931 1930 { 1932 1931 struct bpf_load_and_run_opts opts = {}; 1933 1932 struct bpf_loader_ctx *ctx; 1933 + char sig_buf[MAX_SIG_SIZE]; 1934 + __u8 prog_sha[SHA256_DIGEST_LENGTH]; 1934 1935 int ctx_sz = sizeof(*ctx) + 64 * max(sizeof(struct bpf_map_desc), 1935 1936 sizeof(struct bpf_prog_desc)); 1936 1937 int log_buf_sz = (1u << 24) - 1; ··· 1956 1953 opts.insns = gen->insns; 1957 1954 opts.insns_sz = gen->insns_sz; 1958 1955 fds_before = count_open_fds(); 1956 + 1957 + if (sign_progs) { 1958 + opts.excl_prog_hash = prog_sha; 1959 + opts.excl_prog_hash_sz = sizeof(prog_sha); 1960 + opts.signature = sig_buf; 1961 + opts.signature_sz = MAX_SIG_SIZE; 1962 + opts.keyring_id = KEY_SPEC_SESSION_KEYRING; 1963 + 1964 + err = bpftool_prog_sign(&opts); 1965 + if (err < 0) { 1966 + p_err("failed to sign program"); 1967 + goto out; 1968 + } 1969 + 1970 + err = register_session_key(cert_path); 1971 + if (err < 0) { 1972 + p_err("failed to add session key"); 1973 + goto out; 1974 + } 1975 + } 1959 1976 err = bpf_load_and_run(&opts); 1960 1977 fd_delta = count_open_fds() - fds_before; 1961 1978 if (err < 0 || verifier_logs) { ··· 1984 1961 fprintf(stderr, "loader prog leaked %d FDs\n", 1985 1962 fd_delta); 1986 1963 } 1964 + out: 1987 1965 free(log_buf); 1988 1966 return err; 1989 1967 } ··· 2011 1987 p_err("failed to open object file"); 2012 1988 goto err_close_obj; 2013 1989 } 1990 + 1991 + if (sign_progs) 1992 + gen.gen_hash = true; 2014 1993 2015 1994 err = bpf_object__gen_loader(obj, &gen); 2016 1995 if (err) ··· 2589 2562 " METRIC := { cycles | instructions | l1d_loads | llc_misses | itlb_misses | dtlb_misses }\n" 2590 2563 " " HELP_SPEC_OPTIONS " |\n" 2591 2564 " {-f|--bpffs} | {-m|--mapcompat} | {-n|--nomount} |\n" 2592 - " {-L|--use-loader} }\n" 2565 + " {-L|--use-loader} | [ {-S|--sign } {-k} <private_key.pem> {-i} <certificate.x509> ] \n" 2593 2566 "", 2594 2567 bin_name, argv[-2]); 2595 2568
+212
tools/bpf/bpftool/sign.c
··· 1 + // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 2 + /* 3 + * Copyright (C) 2025 Google LLC. 4 + */ 5 + 6 + #ifndef _GNU_SOURCE 7 + #define _GNU_SOURCE 8 + #endif 9 + #include <stdio.h> 10 + #include <stdlib.h> 11 + #include <stdint.h> 12 + #include <stdbool.h> 13 + #include <string.h> 14 + #include <string.h> 15 + #include <getopt.h> 16 + #include <err.h> 17 + #include <openssl/opensslv.h> 18 + #include <openssl/bio.h> 19 + #include <openssl/evp.h> 20 + #include <openssl/pem.h> 21 + #include <openssl/err.h> 22 + #include <openssl/cms.h> 23 + #include <linux/keyctl.h> 24 + #include <errno.h> 25 + 26 + #include <bpf/skel_internal.h> 27 + 28 + #include "main.h" 29 + 30 + #define OPEN_SSL_ERR_BUF_LEN 256 31 + 32 + static void display_openssl_errors(int l) 33 + { 34 + char buf[OPEN_SSL_ERR_BUF_LEN]; 35 + const char *file; 36 + const char *data; 37 + unsigned long e; 38 + int flags; 39 + int line; 40 + 41 + while ((e = ERR_get_error_all(&file, &line, NULL, &data, &flags))) { 42 + ERR_error_string_n(e, buf, sizeof(buf)); 43 + if (data && (flags & ERR_TXT_STRING)) { 44 + p_err("OpenSSL %s: %s:%d: %s", buf, file, line, data); 45 + } else { 46 + p_err("OpenSSL %s: %s:%d", buf, file, line); 47 + } 48 + } 49 + } 50 + 51 + #define DISPLAY_OSSL_ERR(cond) \ 52 + do { \ 53 + bool __cond = (cond); \ 54 + if (__cond && ERR_peek_error()) \ 55 + display_openssl_errors(__LINE__);\ 56 + } while (0) 57 + 58 + static EVP_PKEY *read_private_key(const char *pkey_path) 59 + { 60 + EVP_PKEY *private_key = NULL; 61 + BIO *b; 62 + 63 + b = BIO_new_file(pkey_path, "rb"); 64 + private_key = PEM_read_bio_PrivateKey(b, NULL, NULL, NULL); 65 + BIO_free(b); 66 + DISPLAY_OSSL_ERR(!private_key); 67 + return private_key; 68 + } 69 + 70 + static X509 *read_x509(const char *x509_name) 71 + { 72 + unsigned char buf[2]; 73 + X509 *x509 = NULL; 74 + BIO *b; 75 + int n; 76 + 77 + b = BIO_new_file(x509_name, "rb"); 78 + if (!b) 79 + goto cleanup; 80 + 81 + /* Look at the first two bytes of the file to determine the encoding */ 82 + n = BIO_read(b, buf, 2); 83 + if (n != 2) 84 + goto cleanup; 85 + 86 + if (BIO_reset(b) != 0) 87 + goto cleanup; 88 + 89 + if (buf[0] == 0x30 && buf[1] >= 0x81 && buf[1] <= 0x84) 90 + /* Assume raw DER encoded X.509 */ 91 + x509 = d2i_X509_bio(b, NULL); 92 + else 93 + /* Assume PEM encoded X.509 */ 94 + x509 = PEM_read_bio_X509(b, NULL, NULL, NULL); 95 + 96 + cleanup: 97 + BIO_free(b); 98 + DISPLAY_OSSL_ERR(!x509); 99 + return x509; 100 + } 101 + 102 + __u32 register_session_key(const char *key_der_path) 103 + { 104 + unsigned char *der_buf = NULL; 105 + X509 *x509 = NULL; 106 + int key_id = -1; 107 + int der_len; 108 + 109 + if (!key_der_path) 110 + return key_id; 111 + x509 = read_x509(key_der_path); 112 + if (!x509) 113 + goto cleanup; 114 + der_len = i2d_X509(x509, &der_buf); 115 + if (der_len < 0) 116 + goto cleanup; 117 + key_id = syscall(__NR_add_key, "asymmetric", key_der_path, der_buf, 118 + (size_t)der_len, KEY_SPEC_SESSION_KEYRING); 119 + cleanup: 120 + X509_free(x509); 121 + OPENSSL_free(der_buf); 122 + DISPLAY_OSSL_ERR(key_id == -1); 123 + return key_id; 124 + } 125 + 126 + int bpftool_prog_sign(struct bpf_load_and_run_opts *opts) 127 + { 128 + BIO *bd_in = NULL, *bd_out = NULL; 129 + EVP_PKEY *private_key = NULL; 130 + CMS_ContentInfo *cms = NULL; 131 + long actual_sig_len = 0; 132 + X509 *x509 = NULL; 133 + int err = 0; 134 + 135 + bd_in = BIO_new_mem_buf(opts->insns, opts->insns_sz); 136 + if (!bd_in) { 137 + err = -ENOMEM; 138 + goto cleanup; 139 + } 140 + 141 + private_key = read_private_key(private_key_path); 142 + if (!private_key) { 143 + err = -EINVAL; 144 + goto cleanup; 145 + } 146 + 147 + x509 = read_x509(cert_path); 148 + if (!x509) { 149 + err = -EINVAL; 150 + goto cleanup; 151 + } 152 + 153 + cms = CMS_sign(NULL, NULL, NULL, NULL, 154 + CMS_NOCERTS | CMS_PARTIAL | CMS_BINARY | CMS_DETACHED | 155 + CMS_STREAM); 156 + if (!cms) { 157 + err = -EINVAL; 158 + goto cleanup; 159 + } 160 + 161 + if (!CMS_add1_signer(cms, x509, private_key, EVP_sha256(), 162 + CMS_NOCERTS | CMS_BINARY | CMS_NOSMIMECAP | 163 + CMS_USE_KEYID | CMS_NOATTR)) { 164 + err = -EINVAL; 165 + goto cleanup; 166 + } 167 + 168 + if (CMS_final(cms, bd_in, NULL, CMS_NOCERTS | CMS_BINARY) != 1) { 169 + err = -EIO; 170 + goto cleanup; 171 + } 172 + 173 + EVP_Digest(opts->insns, opts->insns_sz, opts->excl_prog_hash, 174 + &opts->excl_prog_hash_sz, EVP_sha256(), NULL); 175 + 176 + bd_out = BIO_new(BIO_s_mem()); 177 + if (!bd_out) { 178 + err = -ENOMEM; 179 + goto cleanup; 180 + } 181 + 182 + if (!i2d_CMS_bio_stream(bd_out, cms, NULL, 0)) { 183 + err = -EIO; 184 + goto cleanup; 185 + } 186 + 187 + actual_sig_len = BIO_get_mem_data(bd_out, NULL); 188 + if (actual_sig_len <= 0) { 189 + err = -EIO; 190 + goto cleanup; 191 + } 192 + 193 + if ((size_t)actual_sig_len > opts->signature_sz) { 194 + err = -ENOSPC; 195 + goto cleanup; 196 + } 197 + 198 + if (BIO_read(bd_out, opts->signature, actual_sig_len) != actual_sig_len) { 199 + err = -EIO; 200 + goto cleanup; 201 + } 202 + 203 + opts->signature_sz = actual_sig_len; 204 + cleanup: 205 + BIO_free(bd_out); 206 + CMS_ContentInfo_free(cms); 207 + X509_free(x509); 208 + EVP_PKEY_free(private_key); 209 + BIO_free(bd_in); 210 + DISPLAY_OSSL_ERR(err < 0); 211 + return err; 212 + }
+10
tools/include/uapi/linux/bpf.h
··· 1611 1611 * continuous. 1612 1612 */ 1613 1613 __u32 fd_array_cnt; 1614 + /* Pointer to a buffer containing the signature of the BPF 1615 + * program. 1616 + */ 1617 + __aligned_u64 signature; 1618 + /* Size of the signature buffer in bytes. */ 1619 + __u32 signature_size; 1620 + /* ID of the kernel keyring to be used for signature 1621 + * verification. 1622 + */ 1623 + __s32 keyring_id; 1614 1624 }; 1615 1625 1616 1626 struct { /* anonymous struct used by BPF_OBJ_* commands */
+1 -1
tools/lib/bpf/bpf.c
··· 240 240 const struct bpf_insn *insns, size_t insn_cnt, 241 241 struct bpf_prog_load_opts *opts) 242 242 { 243 - const size_t attr_sz = offsetofend(union bpf_attr, fd_array_cnt); 243 + const size_t attr_sz = offsetofend(union bpf_attr, keyring_id); 244 244 void *finfo = NULL, *linfo = NULL; 245 245 const char *func_info, *line_info; 246 246 __u32 log_size, log_level, attach_prog_fd, attach_btf_obj_fd;
+2
tools/lib/bpf/bpf_gen_internal.h
··· 4 4 #define __BPF_GEN_INTERNAL_H 5 5 6 6 #include "bpf.h" 7 + #include "libbpf_internal.h" 7 8 8 9 struct ksym_relo_desc { 9 10 const char *name; ··· 51 50 __u32 nr_ksyms; 52 51 int fd_array; 53 52 int nr_fd_array; 53 + int hash_insn_offset[SHA256_DWORD_SIZE]; 54 54 }; 55 55 56 56 void bpf_gen__init(struct bpf_gen *gen, int log_level, int nr_progs, int nr_maps);
+55
tools/lib/bpf/gen_loader.c
··· 110 110 111 111 static int add_data(struct bpf_gen *gen, const void *data, __u32 size); 112 112 static void emit_sys_close_blob(struct bpf_gen *gen, int blob_off); 113 + static void emit_signature_match(struct bpf_gen *gen); 113 114 114 115 void bpf_gen__init(struct bpf_gen *gen, int log_level, int nr_progs, int nr_maps) 115 116 { ··· 153 152 /* R7 contains the error code from sys_bpf. Copy it into R0 and exit. */ 154 153 emit(gen, BPF_MOV64_REG(BPF_REG_0, BPF_REG_7)); 155 154 emit(gen, BPF_EXIT_INSN()); 155 + if (OPTS_GET(gen->opts, gen_hash, false)) 156 + emit_signature_match(gen); 156 157 } 157 158 158 159 static int add_data(struct bpf_gen *gen, const void *data, __u32 size) ··· 371 368 __emit_sys_close(gen); 372 369 } 373 370 371 + static int compute_sha_udpate_offsets(struct bpf_gen *gen); 372 + 374 373 int bpf_gen__finish(struct bpf_gen *gen, int nr_progs, int nr_maps) 375 374 { 376 375 int i; ··· 399 394 blob_fd_array_off(gen, i)); 400 395 emit(gen, BPF_MOV64_IMM(BPF_REG_0, 0)); 401 396 emit(gen, BPF_EXIT_INSN()); 397 + if (OPTS_GET(gen->opts, gen_hash, false)) { 398 + gen->error = compute_sha_udpate_offsets(gen); 399 + if (gen->error) 400 + return gen->error; 401 + } 402 + 402 403 pr_debug("gen: finish %s\n", errstr(gen->error)); 403 404 if (!gen->error) { 404 405 struct gen_loader_opts *opts = gen->opts; ··· 456 445 } \ 457 446 _val; \ 458 447 }) 448 + 449 + static int compute_sha_udpate_offsets(struct bpf_gen *gen) 450 + { 451 + __u64 sha[SHA256_DWORD_SIZE]; 452 + __u64 sha_dw; 453 + int i, err; 454 + 455 + err = libbpf_sha256(gen->data_start, gen->data_cur - gen->data_start, sha, SHA256_DIGEST_LENGTH); 456 + if (err < 0) { 457 + pr_warn("sha256 computation of the metadata failed"); 458 + return err; 459 + } 460 + for (i = 0; i < SHA256_DWORD_SIZE; i++) { 461 + struct bpf_insn *insn = 462 + (struct bpf_insn *)(gen->insn_start + gen->hash_insn_offset[i]); 463 + sha_dw = tgt_endian(sha[i]); 464 + insn[0].imm = (__u32)sha_dw; 465 + insn[1].imm = sha_dw >> 32; 466 + } 467 + return 0; 468 + } 459 469 460 470 void bpf_gen__load_btf(struct bpf_gen *gen, const void *btf_raw_data, 461 471 __u32 btf_raw_size) ··· 587 555 } 588 556 if (close_inner_map_fd) 589 557 emit_sys_close_stack(gen, stack_off(inner_map_fd)); 558 + } 559 + 560 + static void emit_signature_match(struct bpf_gen *gen) 561 + { 562 + __s64 off; 563 + int i; 564 + 565 + for (i = 0; i < SHA256_DWORD_SIZE; i++) { 566 + emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_1, BPF_PSEUDO_MAP_IDX, 567 + 0, 0, 0, 0)); 568 + emit(gen, BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_1, i * sizeof(__u64))); 569 + gen->hash_insn_offset[i] = gen->insn_cur - gen->insn_start; 570 + emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_3, 0, 0, 0, 0, 0)); 571 + 572 + off = -(gen->insn_cur - gen->insn_start - gen->cleanup_label) / 8 - 1; 573 + if (is_simm16(off)) { 574 + emit(gen, BPF_MOV64_IMM(BPF_REG_7, -EINVAL)); 575 + emit(gen, BPF_JMP_REG(BPF_JNE, BPF_REG_2, BPF_REG_3, off)); 576 + } else { 577 + gen->error = -ERANGE; 578 + emit(gen, BPF_JMP_IMM(BPF_JA, 0, 0, -1)); 579 + } 580 + } 590 581 } 591 582 592 583 void bpf_gen__record_attach_target(struct bpf_gen *gen, const char *attach_name,
+2 -1
tools/lib/bpf/libbpf.h
··· 1857 1857 const char *insns; 1858 1858 __u32 data_sz; 1859 1859 __u32 insns_sz; 1860 + bool gen_hash; 1860 1861 }; 1861 1862 1862 - #define gen_loader_opts__last_field insns_sz 1863 + #define gen_loader_opts__last_field gen_hash 1863 1864 LIBBPF_API int bpf_object__gen_loader(struct bpf_object *obj, 1864 1865 struct gen_loader_opts *opts); 1865 1866
+72 -4
tools/lib/bpf/skel_internal.h
··· 13 13 #include <unistd.h> 14 14 #include <sys/syscall.h> 15 15 #include <sys/mman.h> 16 + #include <linux/keyctl.h> 16 17 #include <stdlib.h> 17 18 #include "bpf.h" 19 + #endif 20 + 21 + #ifndef SHA256_DIGEST_LENGTH 22 + #define SHA256_DIGEST_LENGTH 32 18 23 #endif 19 24 20 25 #ifndef __NR_bpf ··· 69 64 __u32 data_sz; 70 65 __u32 insns_sz; 71 66 const char *errstr; 67 + void *signature; 68 + __u32 signature_sz; 69 + __s32 keyring_id; 70 + void *excl_prog_hash; 71 + __u32 excl_prog_hash_sz; 72 72 }; 73 73 74 74 long kern_sys_bpf(__u32 cmd, void *attr, __u32 attr_size); ··· 230 220 const char *map_name, 231 221 __u32 key_size, 232 222 __u32 value_size, 233 - __u32 max_entries) 223 + __u32 max_entries, 224 + const void *excl_prog_hash, 225 + __u32 excl_prog_hash_sz) 234 226 { 235 - const size_t attr_sz = offsetofend(union bpf_attr, map_extra); 227 + const size_t attr_sz = offsetofend(union bpf_attr, excl_prog_hash_size); 236 228 union bpf_attr attr; 237 229 238 230 memset(&attr, 0, attr_sz); 239 231 240 232 attr.map_type = map_type; 233 + attr.excl_prog_hash = (unsigned long) excl_prog_hash; 234 + attr.excl_prog_hash_size = excl_prog_hash_sz; 235 + 241 236 strncpy(attr.map_name, map_name, sizeof(attr.map_name)); 242 237 attr.key_size = key_size; 243 238 attr.value_size = value_size; ··· 315 300 return skel_sys_bpf(BPF_LINK_CREATE, &attr, attr_sz); 316 301 } 317 302 303 + static inline int skel_obj_get_info_by_fd(int fd) 304 + { 305 + const size_t attr_sz = offsetofend(union bpf_attr, info); 306 + __u8 sha[SHA256_DIGEST_LENGTH]; 307 + struct bpf_map_info info; 308 + __u32 info_len = sizeof(info); 309 + union bpf_attr attr; 310 + 311 + memset(&info, 0, sizeof(info)); 312 + info.hash = (long) &sha; 313 + info.hash_size = SHA256_DIGEST_LENGTH; 314 + 315 + memset(&attr, 0, attr_sz); 316 + attr.info.bpf_fd = fd; 317 + attr.info.info = (long) &info; 318 + attr.info.info_len = info_len; 319 + return skel_sys_bpf(BPF_OBJ_GET_INFO_BY_FD, &attr, attr_sz); 320 + } 321 + 322 + static inline int skel_map_freeze(int fd) 323 + { 324 + const size_t attr_sz = offsetofend(union bpf_attr, map_fd); 325 + union bpf_attr attr; 326 + 327 + memset(&attr, 0, attr_sz); 328 + attr.map_fd = fd; 329 + 330 + return skel_sys_bpf(BPF_MAP_FREEZE, &attr, attr_sz); 331 + } 318 332 #ifdef __KERNEL__ 319 333 #define set_err 320 334 #else ··· 352 308 353 309 static inline int bpf_load_and_run(struct bpf_load_and_run_opts *opts) 354 310 { 355 - const size_t prog_load_attr_sz = offsetofend(union bpf_attr, fd_array); 311 + const size_t prog_load_attr_sz = offsetofend(union bpf_attr, keyring_id); 356 312 const size_t test_run_attr_sz = offsetofend(union bpf_attr, test); 357 313 int map_fd = -1, prog_fd = -1, key = 0, err; 358 314 union bpf_attr attr; 359 315 360 - err = map_fd = skel_map_create(BPF_MAP_TYPE_ARRAY, "__loader.map", 4, opts->data_sz, 1); 316 + err = map_fd = skel_map_create(BPF_MAP_TYPE_ARRAY, "__loader.map", 4, opts->data_sz, 1, 317 + opts->excl_prog_hash, opts->excl_prog_hash_sz); 361 318 if (map_fd < 0) { 362 319 opts->errstr = "failed to create loader map"; 363 320 set_err; ··· 372 327 goto out; 373 328 } 374 329 330 + #ifndef __KERNEL__ 331 + err = skel_map_freeze(map_fd); 332 + if (err < 0) { 333 + opts->errstr = "failed to freeze map"; 334 + set_err; 335 + goto out; 336 + } 337 + err = skel_obj_get_info_by_fd(map_fd); 338 + if (err < 0) { 339 + opts->errstr = "failed to fetch obj info"; 340 + set_err; 341 + goto out; 342 + } 343 + #endif 344 + 375 345 memset(&attr, 0, prog_load_attr_sz); 376 346 attr.prog_type = BPF_PROG_TYPE_SYSCALL; 377 347 attr.insns = (long) opts->insns; 378 348 attr.insn_cnt = opts->insns_sz / sizeof(struct bpf_insn); 379 349 attr.license = (long) "Dual BSD/GPL"; 350 + #ifndef __KERNEL__ 351 + attr.signature = (long) opts->signature; 352 + attr.signature_size = opts->signature_sz; 353 + #else 354 + if (opts->signature || opts->signature_sz) 355 + pr_warn("signatures are not supported from bpf_preload\n"); 356 + #endif 357 + attr.keyring_id = opts->keyring_id; 380 358 memcpy(attr.prog_name, "__loader.prog", sizeof("__loader.prog")); 381 359 attr.fd_array = (long) &map_fd; 382 360 attr.log_level = opts->ctx->log_level;
+1
tools/testing/selftests/bpf/.gitignore
··· 44 44 xdp_synproxy 45 45 xdp_hw_metadata 46 46 xdp_features 47 + verification_cert.h
+31 -4
tools/testing/selftests/bpf/Makefile
··· 496 496 test_subskeleton.skel.h test_subskeleton_lib.skel.h \ 497 497 test_usdt.skel.h 498 498 499 - LSKELS := fentry_test.c fexit_test.c fexit_sleep.c atomics.c \ 500 - trace_printk.c trace_vprintk.c map_ptr_kern.c \ 499 + LSKELS := fexit_sleep.c trace_printk.c trace_vprintk.c map_ptr_kern.c \ 501 500 core_kern.c core_kern_overflow.c test_ringbuf.c \ 502 501 test_ringbuf_n.c test_ringbuf_map_key.c test_ringbuf_write.c 502 + 503 + LSKELS_SIGNED := fentry_test.c fexit_test.c atomics.c 503 504 504 505 # Generate both light skeleton and libbpf skeleton for these 505 506 LSKELS_EXTRA := test_ksyms_module.c test_ksyms_weak.c kfunc_call_test.c \ 506 507 kfunc_call_test_subprog.c 507 - SKEL_BLACKLIST += $$(LSKELS) 508 + SKEL_BLACKLIST += $$(LSKELS) $$(LSKELS_SIGNED) 508 509 509 510 test_static_linked.skel.h-deps := test_static_linked1.bpf.o test_static_linked2.bpf.o 510 511 linked_funcs.skel.h-deps := linked_funcs1.bpf.o linked_funcs2.bpf.o ··· 536 535 # $2 - test runner extra "flavor" (e.g., no_alu32, cpuv4, bpf_gcc, etc) 537 536 define DEFINE_TEST_RUNNER 538 537 538 + LSKEL_SIGN := -S -k $(PRIVATE_KEY) -i $(VERIFICATION_CERT) 539 539 TRUNNER_OUTPUT := $(OUTPUT)$(if $2,/)$2 540 540 TRUNNER_BINARY := $1$(if $2,-)$2 541 541 TRUNNER_TEST_OBJS := $$(patsubst %.c,$$(TRUNNER_OUTPUT)/%.test.o, \ ··· 552 550 $$(TRUNNER_BPF_SRCS))) 553 551 TRUNNER_BPF_LSKELS := $$(patsubst %.c,$$(TRUNNER_OUTPUT)/%.lskel.h, $$(LSKELS) $$(LSKELS_EXTRA)) 554 552 TRUNNER_BPF_SKELS_LINKED := $$(addprefix $$(TRUNNER_OUTPUT)/,$(LINKED_SKELS)) 553 + TRUNNER_BPF_LSKELS_SIGNED := $$(patsubst %.c,$$(TRUNNER_OUTPUT)/%.lskel.h, $$(LSKELS_SIGNED)) 555 554 TEST_GEN_FILES += $$(TRUNNER_BPF_OBJS) 556 555 557 556 # Evaluate rules now with extra TRUNNER_XXX variables above already defined ··· 607 604 $(Q)$$(BPFTOOL) gen skeleton -L $$(<:.o=.llinked3.o) name $$(notdir $$(<:.bpf.o=_lskel)) > $$@ 608 605 $(Q)rm -f $$(<:.o=.llinked1.o) $$(<:.o=.llinked2.o) $$(<:.o=.llinked3.o) 609 606 607 + $(TRUNNER_BPF_LSKELS_SIGNED): %.lskel.h: %.bpf.o $(BPFTOOL) | $(TRUNNER_OUTPUT) 608 + $$(call msg,GEN-SKEL,$(TRUNNER_BINARY) (signed),$$@) 609 + $(Q)$$(BPFTOOL) gen object $$(<:.o=.llinked1.o) $$< 610 + $(Q)$$(BPFTOOL) gen object $$(<:.o=.llinked2.o) $$(<:.o=.llinked1.o) 611 + $(Q)$$(BPFTOOL) gen object $$(<:.o=.llinked3.o) $$(<:.o=.llinked2.o) 612 + $(Q)diff $$(<:.o=.llinked2.o) $$(<:.o=.llinked3.o) 613 + $(Q)$$(BPFTOOL) gen skeleton $(LSKEL_SIGN) $$(<:.o=.llinked3.o) name $$(notdir $$(<:.bpf.o=_lskel)) > $$@ 614 + $(Q)rm -f $$(<:.o=.llinked1.o) $$(<:.o=.llinked2.o) $$(<:.o=.llinked3.o) 615 + 610 616 $(LINKED_BPF_OBJS): %: $(TRUNNER_OUTPUT)/% 611 617 612 618 # .SECONDEXPANSION here allows to correctly expand %-deps variables as prerequisites ··· 665 653 $(TRUNNER_EXTRA_HDRS) \ 666 654 $(TRUNNER_BPF_SKELS) \ 667 655 $(TRUNNER_BPF_LSKELS) \ 656 + $(TRUNNER_BPF_LSKELS_SIGNED) \ 668 657 $(TRUNNER_BPF_SKELS_LINKED) \ 669 658 $$(BPFOBJ) | $(TRUNNER_OUTPUT) 670 659 ··· 680 667 $(TRUNNER_EXTRA_OBJS): $(TRUNNER_OUTPUT)/%.o: \ 681 668 %.c \ 682 669 $(TRUNNER_EXTRA_HDRS) \ 670 + $(VERIFY_SIG_HDR) \ 683 671 $(TRUNNER_TESTS_HDR) \ 684 672 $$(BPFOBJ) | $(TRUNNER_OUTPUT) 685 673 $$(call msg,EXT-OBJ,$(TRUNNER_BINARY),$$@) ··· 711 697 712 698 endef 713 699 700 + VERIFY_SIG_SETUP := $(CURDIR)/verify_sig_setup.sh 701 + VERIFY_SIG_HDR := verification_cert.h 702 + VERIFICATION_CERT := $(BUILD_DIR)/signing_key.der 703 + PRIVATE_KEY := $(BUILD_DIR)/signing_key.pem 704 + 705 + $(VERIFICATION_CERT) $(PRIVATE_KEY): $(VERIFY_SIG_SETUP) 706 + $(Q)mkdir -p $(BUILD_DIR) 707 + $(Q)$(VERIFY_SIG_SETUP) genkey $(BUILD_DIR) 708 + 709 + $(VERIFY_SIG_HDR): $(VERIFICATION_CERT) 710 + $(Q)xxd -i -n test_progs_verification_cert $< > $@ 711 + 714 712 # Define test_progs test runner. 715 713 TRUNNER_TESTS_DIR := prog_tests 716 714 TRUNNER_BPF_PROGS_DIR := progs ··· 742 716 disasm.c \ 743 717 disasm_helpers.c \ 744 718 json_writer.c \ 719 + $(VERIFY_SIG_HDR) \ 745 720 flow_dissector_load.h \ 746 721 ip_check_defrag_frags.h 747 722 TRUNNER_EXTRA_FILES := $(OUTPUT)/urandom_read \ ··· 752 725 $(OUTPUT)/uprobe_multi \ 753 726 $(TEST_KMOD_TARGETS) \ 754 727 ima_setup.sh \ 755 - verify_sig_setup.sh \ 728 + $(VERIFY_SIG_SETUP) \ 756 729 $(wildcard progs/btf_dump_test_case_*.c) \ 757 730 $(wildcard progs/*.bpf.o) 758 731 TRUNNER_BPF_BUILD_RULE := CLANG_BPF_BUILD_RULE
+8 -2
tools/testing/selftests/bpf/prog_tests/atomics.c
··· 165 165 void test_atomics(void) 166 166 { 167 167 struct atomics_lskel *skel; 168 + int err; 168 169 169 - skel = atomics_lskel__open_and_load(); 170 - if (!ASSERT_OK_PTR(skel, "atomics skeleton load")) 170 + skel = atomics_lskel__open(); 171 + if (!ASSERT_OK_PTR(skel, "atomics skeleton open")) 171 172 return; 173 + 174 + skel->keyring_id = KEY_SPEC_SESSION_KEYRING; 175 + err = atomics_lskel__load(skel); 176 + if (!ASSERT_OK(err, "atomics skeleton load")) 177 + goto cleanup; 172 178 173 179 if (skel->data->skip_tests) { 174 180 printf("%s:SKIP:no ENABLE_ATOMICS_TESTS (missing Clang BPF atomics support)",
+13 -2
tools/testing/selftests/bpf/prog_tests/fentry_fexit.c
··· 12 12 int err, prog_fd, i; 13 13 LIBBPF_OPTS(bpf_test_run_opts, topts); 14 14 15 - fentry_skel = fentry_test_lskel__open_and_load(); 15 + fentry_skel = fentry_test_lskel__open(); 16 16 if (!ASSERT_OK_PTR(fentry_skel, "fentry_skel_load")) 17 17 goto close_prog; 18 - fexit_skel = fexit_test_lskel__open_and_load(); 18 + 19 + fentry_skel->keyring_id = KEY_SPEC_SESSION_KEYRING; 20 + err = fentry_test_lskel__load(fentry_skel); 21 + if (!ASSERT_OK(err, "fentry_skel_load")) 22 + goto close_prog; 23 + 24 + fexit_skel = fexit_test_lskel__open(); 19 25 if (!ASSERT_OK_PTR(fexit_skel, "fexit_skel_load")) 26 + goto close_prog; 27 + 28 + fexit_skel->keyring_id = KEY_SPEC_SESSION_KEYRING; 29 + err = fexit_test_lskel__load(fexit_skel); 30 + if (!ASSERT_OK(err, "fexit_skel_load")) 20 31 goto close_prog; 21 32 22 33 err = fentry_test_lskel__attach(fentry_skel);
+7 -2
tools/testing/selftests/bpf/prog_tests/fentry_test.c
··· 43 43 struct fentry_test_lskel *fentry_skel = NULL; 44 44 int err; 45 45 46 - fentry_skel = fentry_test_lskel__open_and_load(); 47 - if (!ASSERT_OK_PTR(fentry_skel, "fentry_skel_load")) 46 + fentry_skel = fentry_test_lskel__open(); 47 + if (!ASSERT_OK_PTR(fentry_skel, "fentry_skel_open")) 48 + goto cleanup; 49 + 50 + fentry_skel->keyring_id = KEY_SPEC_SESSION_KEYRING; 51 + err = fentry_test_lskel__load(fentry_skel); 52 + if (!ASSERT_OK(err, "fentry_skel_load")) 48 53 goto cleanup; 49 54 50 55 err = fentry_test_common(fentry_skel);
+7 -2
tools/testing/selftests/bpf/prog_tests/fexit_test.c
··· 43 43 struct fexit_test_lskel *fexit_skel = NULL; 44 44 int err; 45 45 46 - fexit_skel = fexit_test_lskel__open_and_load(); 47 - if (!ASSERT_OK_PTR(fexit_skel, "fexit_skel_load")) 46 + fexit_skel = fexit_test_lskel__open(); 47 + if (!ASSERT_OK_PTR(fexit_skel, "fexit_skel_open")) 48 + goto cleanup; 49 + 50 + fexit_skel->keyring_id = KEY_SPEC_SESSION_KEYRING; 51 + err = fexit_test_lskel__load(fexit_skel); 52 + if (!ASSERT_OK(err, "fexit_skel_load")) 48 53 goto cleanup; 49 54 50 55 err = fexit_test_common(fexit_skel);
+13
tools/testing/selftests/bpf/test_progs.c
··· 14 14 #include <netinet/in.h> 15 15 #include <sys/select.h> 16 16 #include <sys/socket.h> 17 + #include <linux/keyctl.h> 17 18 #include <sys/un.h> 18 19 #include <bpf/btf.h> 19 20 #include <time.h> 20 21 #include "json_writer.h" 21 22 22 23 #include "network_helpers.h" 24 + #include "verification_cert.h" 23 25 24 26 /* backtrace() and backtrace_symbols_fd() are glibc specific, 25 27 * use header file when glibc is available and provide stub ··· 1930 1928 } 1931 1929 } 1932 1930 1931 + static __u32 register_session_key(const char *key_data, size_t key_data_size) 1932 + { 1933 + return syscall(__NR_add_key, "asymmetric", "libbpf_session_key", 1934 + (const void *)key_data, key_data_size, 1935 + KEY_SPEC_SESSION_KEYRING); 1936 + } 1937 + 1933 1938 int main(int argc, char **argv) 1934 1939 { 1935 1940 static const struct argp argp = { ··· 1970 1961 /* Use libbpf 1.0 API mode */ 1971 1962 libbpf_set_strict_mode(LIBBPF_STRICT_ALL); 1972 1963 libbpf_set_print(libbpf_print_fn); 1964 + err = register_session_key((const char *)test_progs_verification_cert, 1965 + test_progs_verification_cert_len); 1966 + if (err < 0) 1967 + return err; 1973 1968 1974 1969 traffic_monitor_set_print(traffic_monitor_print_fn); 1975 1970
+9 -2
tools/testing/selftests/bpf/verify_sig_setup.sh
··· 32 32 exit 1 33 33 } 34 34 35 - setup() 35 + genkey() 36 36 { 37 37 local tmp_dir="$1" 38 38 ··· 45 45 46 46 openssl x509 -in ${tmp_dir}/signing_key.pem -out \ 47 47 ${tmp_dir}/signing_key.der -outform der 48 + } 48 49 50 + setup() 51 + { 52 + local tmp_dir="$1" 53 + 54 + genkey "${tmp_dir}" 49 55 key_id=$(cat ${tmp_dir}/signing_key.der | keyctl padd asymmetric ebpf_testing_key @s) 50 - 51 56 keyring_id=$(keyctl newring ebpf_testing_keyring @s) 52 57 keyctl link $key_id $keyring_id 53 58 } ··· 110 105 111 106 if [[ "${action}" == "setup" ]]; then 112 107 setup "${tmp_dir}" 108 + elif [[ "${action}" == "genkey" ]]; then 109 + genkey "${tmp_dir}" 113 110 elif [[ "${action}" == "cleanup" ]]; then 114 111 cleanup "${tmp_dir}" 115 112 elif [[ "${action}" == "fsverity-create-sign" ]]; then