Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Documentation/bpf/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ that goes into great technical depth about the BPF Architecture.
classic_vs_extended.rst
bpf_iterators
bpf_licensing
signing
test_debug
clang-notes
linux-notes
Expand Down
537 changes: 537 additions & 0 deletions Documentation/bpf/signing.rst

Large diffs are not rendered by default.

169 changes: 157 additions & 12 deletions kernel/bpf/syscall.c
Original file line number Diff line number Diff line change
Expand Up @@ -1600,11 +1600,6 @@ static int map_create_alloc(union bpf_attr *attr, bpfptr_t uattr, struct bpf_ver
goto free_map;
}

/* See libbpf: emit_signature_match() */
BUILD_BUG_ON(offsetof(struct bpf_map, excl) != SHA256_DIGEST_SIZE);
BUILD_BUG_ON(!__same_type(map->excl, u32));
BUILD_BUG_ON(offsetof(struct bpf_map, sha) != 0);
BUILD_BUG_ON(!__same_type(map->sha, u8[SHA256_DIGEST_SIZE]));
map->excl = 1;
} else if (attr->excl_prog_hash_size) {
bpf_log(log, "Invalid excl_prog_hash_size.\n");
Expand Down Expand Up @@ -2900,13 +2895,81 @@ static enum bpf_sig_keyring bpf_classify_keyring(s32 keyring_id)
}
}

static void bpf_prog_put_excl_maps(struct bpf_map **maps, u32 cnt)
{
u32 i;

if (!maps)
return;
for (i = 0; i < cnt; i++)
bpf_map_put(maps[i]);
kfree(maps);
}

static int bpf_prog_collect_excl_maps(union bpf_attr *attr, bool is_kernel,
struct bpf_map ***mapsp, u32 *cntp)
{
bpfptr_t fds = make_bpfptr(attr->fd_array, is_kernel);
struct bpf_map **maps;
u32 i, n = 0;
int fd, err;

*mapsp = NULL;
*cntp = 0;

if (!attr->fd_array || !attr->fd_array_cnt)
return 0;
/*
* Stricter than the verifier, which dedups fd_array entries against
* used_maps: every entry here is folded into the signed data
* individually, so cap the raw count.
*/
if (attr->fd_array_cnt > MAX_USED_MAPS)
return -E2BIG;

maps = kcalloc(attr->fd_array_cnt, sizeof(*maps), GFP_KERNEL);
if (!maps)
return -ENOMEM;

for (i = 0; i < attr->fd_array_cnt; i++) {
struct bpf_map *map;

if (copy_from_bpfptr_offset(&fd, fds, i * sizeof(int),
sizeof(int))) {
err = -EFAULT;
goto err_put;
}
map = bpf_map_get(fd);
if (IS_ERR(map)) {
err = PTR_ERR(map);
goto err_put;
}
if (!map->excl_prog_sha) {
bpf_map_put(map);
err = -EINVAL;
goto err_put;
}
maps[n++] = map;
}

*mapsp = maps;
*cntp = n;
return 0;
err_put:
bpf_prog_put_excl_maps(maps, n);
return err;
}

static int bpf_prog_verify_signature(struct bpf_prog *prog, union bpf_attr *attr,
bool is_kernel, s32 *keyring_serial)
bool is_kernel, s32 *keyring_serial,
struct bpf_map **excl_maps, u32 excl_cnt)
{
bpfptr_t usig = make_bpfptr(attr->signature, is_kernel);
struct bpf_dynptr_kern sig_ptr, insns_ptr;
struct bpf_dynptr_kern sig_ptr, data_ptr;
struct bpf_key *key = NULL;
void *sig;
void *sig, *data = NULL;
u32 i, off, insns_sz;
u64 data_sz;
int err = 0;

/*
Expand All @@ -2930,20 +2993,78 @@ static int bpf_prog_verify_signature(struct bpf_prog *prog, union bpf_attr *attr
return PTR_ERR(sig);
}

insns_sz = prog->len * sizeof(struct bpf_insn);
data_sz = insns_sz;
for (i = 0; i < excl_cnt; i++) {
if (!READ_ONCE(excl_maps[i]->frozen) ||
!excl_maps[i]->ops->map_direct_value_addr) {
err = -EPERM;
goto out;
}
data_sz += excl_maps[i]->value_size;
}

if (bpf_dynptr_check_size(data_sz)) {
err = -E2BIG;
goto out;
}
data = kvmalloc(data_sz, GFP_KERNEL);
if (!data) {
err = -ENOMEM;
goto out;
}
memcpy(data, prog->insnsi, insns_sz);
off = insns_sz;
for (i = 0; i < excl_cnt; i++) {
struct bpf_map *map = excl_maps[i];
u64 addr;

err = map->ops->map_direct_value_addr(map, &addr, 0);
if (err)
goto out;
memcpy(data + off, (void *)(unsigned long)addr, map->value_size);
off += map->value_size;
}

bpf_dynptr_init(&data_ptr, data, BPF_DYNPTR_TYPE_LOCAL, 0, data_sz);
bpf_dynptr_init(&sig_ptr, sig, BPF_DYNPTR_TYPE_LOCAL, 0,
attr->signature_size);
bpf_dynptr_init(&insns_ptr, prog->insnsi, BPF_DYNPTR_TYPE_LOCAL, 0,
prog->len * sizeof(struct bpf_insn));

err = bpf_verify_pkcs7_signature((struct bpf_dynptr *)&insns_ptr,
err = bpf_verify_pkcs7_signature((struct bpf_dynptr *)&data_ptr,
(struct bpf_dynptr *)&sig_ptr, key);
if (!err)
*keyring_serial = bpf_key_serial(key);
out:
kvfree(data);
bpf_key_put(key);
kvfree(sig);
return err;
}

static int bpf_prog_check_excl_used_maps(struct bpf_prog *prog,
struct bpf_map **excl_maps, u32 excl_cnt)
{
u32 i, j;

for (i = 0; i < prog->aux->used_map_cnt; i++) {
struct bpf_map *map = prog->aux->used_maps[i];
bool folded = false;

if (!map->excl_prog_sha)
continue;
for (j = 0; j < excl_cnt; j++) {
if (excl_maps[j] == map) {
folded = true;
break;
}
}
if (!folded)
return -EACCES;
}

return 0;
}

static int bpf_prog_mark_insn_arrays_ready(struct bpf_prog *prog)
{
int err;
Expand Down Expand Up @@ -2973,8 +3094,10 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, struct bpf_log_at
{
enum bpf_prog_type type = attr->prog_type;
struct bpf_prog *prog, *dst_prog = NULL;
struct bpf_map **excl_maps = NULL;
struct btf *attach_btf = NULL;
struct bpf_token *token = NULL;
u32 excl_cnt = 0;
bool bpf_cap;
int err;
char license[128];
Expand Down Expand Up @@ -3134,10 +3257,17 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, struct bpf_log_at
/* eBPF programs must be GPL compatible to use GPL-ed functions */
prog->gpl_compatible = license_is_gpl_compatible(license) ? 1 : 0;
if (attr->signature) {
err = bpf_prog_collect_excl_maps(attr, uattr.is_kernel,
&excl_maps, &excl_cnt);
if (err)
goto free_prog;

err = bpf_prog_verify_signature(prog, attr, uattr.is_kernel,
&prog->aux->sig.keyring_serial);
&prog->aux->sig.keyring_serial,
excl_maps, excl_cnt);
if (err)
goto free_prog;

prog->aux->sig.keyring_type = bpf_classify_keyring(attr->keyring_id);
prog->aux->sig.verdict = BPF_SIG_VERIFIED;
} else {
Expand Down Expand Up @@ -3192,12 +3322,25 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, struct bpf_log_at
err = security_bpf_prog_load(prog, attr, token, uattr.is_kernel);
if (err)
goto free_prog;
if (excl_cnt) {
err = bpf_prog_calc_tag(prog);
if (err < 0)
goto free_prog;
}

/* run eBPF verifier */
err = bpf_check(&prog, attr, uattr, attr_log);
if (err < 0)
goto free_used_maps;

if (prog->aux->sig.verdict == BPF_SIG_VERIFIED) {
err = bpf_prog_check_excl_used_maps(prog, excl_maps, excl_cnt);
if (err < 0)
goto free_used_maps;
}
bpf_prog_put_excl_maps(excl_maps, excl_cnt);
excl_maps = NULL;

err = bpf_prog_mark_insn_arrays_ready(prog);
if (err < 0)
goto free_used_maps;
Expand Down Expand Up @@ -3230,6 +3373,7 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, struct bpf_log_at
return err;

free_used_maps:
bpf_prog_put_excl_maps(excl_maps, excl_cnt);
/* In case we have subprogs, we need to wait for a grace
* period before we can tear down JIT memory since symbols
* are already exposed under kallsyms.
Expand All @@ -3238,6 +3382,7 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, struct bpf_log_at
return err;

free_prog:
bpf_prog_put_excl_maps(excl_maps, excl_cnt);
free_uid(prog->aux->user);
if (prog->aux->attach_btf)
btf_put(prog->aux->attach_btf);
Expand Down
2 changes: 2 additions & 0 deletions tools/bpf/bpftool/gen.c
Original file line number Diff line number Diff line change
Expand Up @@ -793,6 +793,8 @@ static int gen_trace(struct bpf_object *obj, const char *obj_name, const char *h
if (sign_progs) {
sopts.insns = opts.insns;
sopts.insns_sz = opts.insns_sz;
sopts.data = opts.data;
sopts.data_sz = opts.data_sz;
sopts.excl_prog_hash = prog_sha;
sopts.excl_prog_hash_sz = sizeof(prog_sha);
sopts.signature = sig_buf;
Expand Down
15 changes: 14 additions & 1 deletion tools/bpf/bpftool/sign.c
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,21 @@ int bpftool_prog_sign(struct bpf_load_and_run_opts *opts)
CMS_ContentInfo *cms = NULL;
long actual_sig_len = 0;
X509 *x509 = NULL;
void *data = NULL;
size_t data_sz;
int err = 0;

bd_in = BIO_new_mem_buf(opts->insns, opts->insns_sz);
data_sz = (size_t)opts->insns_sz + opts->data_sz;
data = malloc(data_sz);
if (!data) {
err = -ENOMEM;
goto cleanup;
}
memcpy(data, opts->insns, opts->insns_sz);
if (opts->data_sz)
memcpy((char *)data + opts->insns_sz, opts->data, opts->data_sz);

bd_in = BIO_new_mem_buf(data, data_sz);
if (!bd_in) {
err = -ENOMEM;
goto cleanup;
Expand Down Expand Up @@ -212,6 +224,7 @@ int bpftool_prog_sign(struct bpf_load_and_run_opts *opts)
X509_free(x509);
EVP_PKEY_free(private_key);
BIO_free(bd_in);
free(data);
DISPLAY_OSSL_ERR(err < 0);
return err;
}
1 change: 0 additions & 1 deletion tools/lib/bpf/bpf_gen_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ struct bpf_gen {
__u32 nr_ksyms;
int fd_array;
int nr_fd_array;
int hash_insn_offset[SHA256_DWORD_SIZE];
};

void bpf_gen__init(struct bpf_gen *gen, int log_level, int nr_progs, int nr_maps);
Expand Down
Loading
Loading