Skip to content

Commit f1e3d2a

Browse files
committed
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]>
1 parent 05c43fc commit f1e3d2a

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
@@ -6668,6 +6668,8 @@ struct bpf_map_info {
66686668
__u32 btf_value_type_id;
66696669
__u32 btf_vmlinux_id;
66706670
__u64 map_extra;
6671+
__aligned_u64 hash;
6672+
__u32 hash_size;
66716673
} __attribute__((aligned(8)));
66726674

66736675
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>
@@ -5185,6 +5186,9 @@ static int bpf_map_get_info_by_fd(struct file *file,
51855186
info_len = min_t(u32, sizeof(info), info_len);
51865187

51875188
memset(&info, 0, sizeof(info));
5189+
if (copy_from_user(&info, uinfo, info_len))
5190+
return -EFAULT;
5191+
51885192
info.type = map->map_type;
51895193
info.id = map->id;
51905194
info.key_size = map->key_size;
@@ -5209,6 +5213,25 @@ static int bpf_map_get_info_by_fd(struct file *file,
52095213
return err;
52105214
}
52115215

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

tools/include/uapi/linux/bpf.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6668,6 +6668,8 @@ struct bpf_map_info {
66686668
__u32 btf_value_type_id;
66696669
__u32 btf_vmlinux_id;
66706670
__u64 map_extra;
6671+
__aligned_u64 hash;
6672+
__u32 hash_size;
66716673
} __attribute__((aligned(8)));
66726674

66736675
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)