Skip to content

Commit ea2e646

Browse files
sinkapAlexei Starovoitov
authored andcommitted
bpf: Return hashes of maps in BPF_OBJ_GET_INFO_BY_FD
Currently only array maps are supported, but the implementation can be extended for other maps and objects. The hash is memoized only for exclusive and frozen maps as their content is stable until the exclusive program modifies the map. This is required for BPF signing, enabling a trusted loader program to verify a map's integrity. The loader retrieves the map's runtime hash from the kernel and compares it against an expected hash computed at build time. Signed-off-by: KP Singh <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Alexei Starovoitov <[email protected]>
1 parent 6c850cb commit ea2e646

File tree

6 files changed

+48
-2
lines changed

6 files changed

+48
-2
lines changed

include/linux/bpf.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <uapi/linux/bpf.h>
88
#include <uapi/linux/filter.h>
99

10+
#include <crypto/sha2.h>
1011
#include <linux/workqueue.h>
1112
#include <linux/file.h>
1213
#include <linux/percpu.h>
@@ -110,6 +111,7 @@ struct bpf_map_ops {
110111
long (*map_pop_elem)(struct bpf_map *map, void *value);
111112
long (*map_peek_elem)(struct bpf_map *map, void *value);
112113
void *(*map_lookup_percpu_elem)(struct bpf_map *map, void *key, u32 cpu);
114+
int (*map_get_hash)(struct bpf_map *map, u32 hash_buf_size, void *hash_buf);
113115

114116
/* funcs called by prog_array and perf_event_array map */
115117
void *(*map_fd_get_ptr)(struct bpf_map *map, struct file *map_file,
@@ -289,6 +291,7 @@ struct bpf_map_owner {
289291
};
290292

291293
struct bpf_map {
294+
u8 sha[SHA256_DIGEST_SIZE];
292295
const struct bpf_map_ops *ops;
293296
struct bpf_map *inner_map_meta;
294297
#ifdef CONFIG_SECURITY

include/uapi/linux/bpf.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6672,6 +6672,8 @@ struct bpf_map_info {
66726672
__u32 btf_value_type_id;
66736673
__u32 btf_vmlinux_id;
66746674
__u64 map_extra;
6675+
__aligned_u64 hash;
6676+
__u32 hash_size;
66756677
} __attribute__((aligned(8)));
66766678

66776679
struct bpf_btf_info {

kernel/bpf/arraymap.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <uapi/linux/btf.h>
1313
#include <linux/rcupdate_trace.h>
1414
#include <linux/btf_ids.h>
15+
#include <crypto/sha2.h>
1516

1617
#include "map_in_map.h"
1718

@@ -174,6 +175,17 @@ static void *array_map_lookup_elem(struct bpf_map *map, void *key)
174175
return array->value + (u64)array->elem_size * (index & array->index_mask);
175176
}
176177

178+
static int array_map_get_hash(struct bpf_map *map, u32 hash_buf_size,
179+
void *hash_buf)
180+
{
181+
struct bpf_array *array = container_of(map, struct bpf_array, map);
182+
183+
sha256(array->value, (u64)array->elem_size * array->map.max_entries,
184+
hash_buf);
185+
memcpy(array->map.sha, hash_buf, sizeof(array->map.sha));
186+
return 0;
187+
}
188+
177189
static int array_map_direct_value_addr(const struct bpf_map *map, u64 *imm,
178190
u32 off)
179191
{
@@ -800,6 +812,7 @@ const struct bpf_map_ops array_map_ops = {
800812
.map_mem_usage = array_map_mem_usage,
801813
.map_btf_id = &array_map_btf_ids[0],
802814
.iter_seq_info = &iter_seq_info,
815+
.map_get_hash = &array_map_get_hash,
803816
};
804817

805818
const struct bpf_map_ops percpu_array_map_ops = {

kernel/bpf/syscall.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// SPDX-License-Identifier: GPL-2.0-only
22
/* Copyright (c) 2011-2014 PLUMgrid, http://plumgrid.com
33
*/
4+
#include <crypto/sha2.h>
45
#include <linux/bpf.h>
56
#include <linux/bpf-cgroup.h>
67
#include <linux/bpf_trace.h>
@@ -5184,6 +5185,9 @@ static int bpf_map_get_info_by_fd(struct file *file,
51845185
info_len = min_t(u32, sizeof(info), info_len);
51855186

51865187
memset(&info, 0, sizeof(info));
5188+
if (copy_from_user(&info, uinfo, info_len))
5189+
return -EFAULT;
5190+
51875191
info.type = map->map_type;
51885192
info.id = map->id;
51895193
info.key_size = map->key_size;
@@ -5208,6 +5212,25 @@ static int bpf_map_get_info_by_fd(struct file *file,
52085212
return err;
52095213
}
52105214

5215+
if (info.hash) {
5216+
char __user *uhash = u64_to_user_ptr(info.hash);
5217+
5218+
if (!map->ops->map_get_hash)
5219+
return -EINVAL;
5220+
5221+
if (info.hash_size != SHA256_DIGEST_SIZE)
5222+
return -EINVAL;
5223+
5224+
err = map->ops->map_get_hash(map, SHA256_DIGEST_SIZE, map->sha);
5225+
if (err != 0)
5226+
return err;
5227+
5228+
if (copy_to_user(uhash, map->sha, SHA256_DIGEST_SIZE) != 0)
5229+
return -EFAULT;
5230+
} else if (info.hash_size) {
5231+
return -EINVAL;
5232+
}
5233+
52115234
if (copy_to_user(uinfo, &info, info_len) ||
52125235
put_user(info_len, &uattr->info.info_len))
52135236
return -EFAULT;

tools/include/uapi/linux/bpf.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6672,6 +6672,8 @@ struct bpf_map_info {
66726672
__u32 btf_value_type_id;
66736673
__u32 btf_vmlinux_id;
66746674
__u64 map_extra;
6675+
__aligned_u64 hash;
6676+
__u32 hash_size;
66756677
} __attribute__((aligned(8)));
66766678

66776679
struct bpf_btf_info {

tools/testing/selftests/bpf/progs/verifier_map_ptr.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,13 @@ __naked void bpf_map_ptr_write_rejected(void)
7070
: __clobber_all);
7171
}
7272

73+
/* The first element of struct bpf_map is a SHA256 hash of 32 bytes, accessing
74+
* into this array is valid. The opts field is now at offset 33.
75+
*/
7376
SEC("socket")
7477
__description("bpf_map_ptr: read non-existent field rejected")
7578
__failure
76-
__msg("cannot access ptr member ops with moff 0 in struct bpf_map with off 1 size 4")
79+
__msg("cannot access ptr member ops with moff 32 in struct bpf_map with off 33 size 4")
7780
__failure_unpriv
7881
__msg_unpriv("access is allowed only to CAP_PERFMON and CAP_SYS_ADMIN")
7982
__flag(BPF_F_ANY_ALIGNMENT)
@@ -82,7 +85,7 @@ __naked void read_non_existent_field_rejected(void)
8285
asm volatile (" \
8386
r6 = 0; \
8487
r1 = %[map_array_48b] ll; \
85-
r6 = *(u32*)(r1 + 1); \
88+
r6 = *(u32*)(r1 + 33); \
8689
r0 = 1; \
8790
exit; \
8891
" :

0 commit comments

Comments
 (0)