Skip to content

Commit 591fe98

Browse files
borkmannAlexei Starovoitov
authored and
Alexei Starovoitov
committed
bpf: add program side {rd, wr}only support for maps
This work adds two new map creation flags BPF_F_RDONLY_PROG and BPF_F_WRONLY_PROG in order to allow for read-only or write-only BPF maps from a BPF program side. Today we have BPF_F_RDONLY and BPF_F_WRONLY, but this only applies to system call side, meaning the BPF program has full read/write access to the map as usual while bpf(2) calls with map fd can either only read or write into the map depending on the flags. BPF_F_RDONLY_PROG and BPF_F_WRONLY_PROG allows for the exact opposite such that verifier is going to reject program loads if write into a read-only map or a read into a write-only map is detected. For read-only map case also some helpers are forbidden for programs that would alter the map state such as map deletion, update, etc. As opposed to the two BPF_F_RDONLY / BPF_F_WRONLY flags, BPF_F_RDONLY_PROG as well as BPF_F_WRONLY_PROG really do correspond to the map lifetime. We've enabled this generic map extension to various non-special maps holding normal user data: array, hash, lru, lpm, local storage, queue and stack. Further generic map types could be followed up in future depending on use-case. Main use case here is to forbid writes into .rodata map values from verifier side. Signed-off-by: Daniel Borkmann <[email protected]> Acked-by: Martin KaFai Lau <[email protected]> Signed-off-by: Alexei Starovoitov <[email protected]>
1 parent be70bcd commit 591fe98

File tree

9 files changed

+96
-14
lines changed

9 files changed

+96
-14
lines changed

include/linux/bpf.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,35 @@ struct bpf_array {
430430
#define BPF_COMPLEXITY_LIMIT_INSNS 1000000 /* yes. 1M insns */
431431
#define MAX_TAIL_CALL_CNT 32
432432

433+
#define BPF_F_ACCESS_MASK (BPF_F_RDONLY | \
434+
BPF_F_RDONLY_PROG | \
435+
BPF_F_WRONLY | \
436+
BPF_F_WRONLY_PROG)
437+
438+
#define BPF_MAP_CAN_READ BIT(0)
439+
#define BPF_MAP_CAN_WRITE BIT(1)
440+
441+
static inline u32 bpf_map_flags_to_cap(struct bpf_map *map)
442+
{
443+
u32 access_flags = map->map_flags & (BPF_F_RDONLY_PROG | BPF_F_WRONLY_PROG);
444+
445+
/* Combination of BPF_F_RDONLY_PROG | BPF_F_WRONLY_PROG is
446+
* not possible.
447+
*/
448+
if (access_flags & BPF_F_RDONLY_PROG)
449+
return BPF_MAP_CAN_READ;
450+
else if (access_flags & BPF_F_WRONLY_PROG)
451+
return BPF_MAP_CAN_WRITE;
452+
else
453+
return BPF_MAP_CAN_READ | BPF_MAP_CAN_WRITE;
454+
}
455+
456+
static inline bool bpf_map_flags_access_ok(u32 access_flags)
457+
{
458+
return (access_flags & (BPF_F_RDONLY_PROG | BPF_F_WRONLY_PROG)) !=
459+
(BPF_F_RDONLY_PROG | BPF_F_WRONLY_PROG);
460+
}
461+
433462
struct bpf_event_entry {
434463
struct perf_event *event;
435464
struct file *perf_file;

include/uapi/linux/bpf.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,7 @@ enum bpf_attach_type {
294294

295295
#define BPF_OBJ_NAME_LEN 16U
296296

297-
/* Flags for accessing BPF object */
297+
/* Flags for accessing BPF object from syscall side. */
298298
#define BPF_F_RDONLY (1U << 3)
299299
#define BPF_F_WRONLY (1U << 4)
300300

@@ -304,6 +304,10 @@ enum bpf_attach_type {
304304
/* Zero-initialize hash function seed. This should only be used for testing. */
305305
#define BPF_F_ZERO_SEED (1U << 6)
306306

307+
/* Flags for accessing BPF object from program side. */
308+
#define BPF_F_RDONLY_PROG (1U << 7)
309+
#define BPF_F_WRONLY_PROG (1U << 8)
310+
307311
/* flags for BPF_PROG_QUERY */
308312
#define BPF_F_QUERY_EFFECTIVE (1U << 0)
309313

kernel/bpf/arraymap.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
#include "map_in_map.h"
2323

2424
#define ARRAY_CREATE_FLAG_MASK \
25-
(BPF_F_NUMA_NODE | BPF_F_RDONLY | BPF_F_WRONLY)
25+
(BPF_F_NUMA_NODE | BPF_F_ACCESS_MASK)
2626

2727
static void bpf_array_free_percpu(struct bpf_array *array)
2828
{
@@ -63,6 +63,7 @@ int array_map_alloc_check(union bpf_attr *attr)
6363
if (attr->max_entries == 0 || attr->key_size != 4 ||
6464
attr->value_size == 0 ||
6565
attr->map_flags & ~ARRAY_CREATE_FLAG_MASK ||
66+
!bpf_map_flags_access_ok(attr->map_flags) ||
6667
(percpu && numa_node != NUMA_NO_NODE))
6768
return -EINVAL;
6869

@@ -472,6 +473,9 @@ static int fd_array_map_alloc_check(union bpf_attr *attr)
472473
/* only file descriptors can be stored in this type of map */
473474
if (attr->value_size != sizeof(u32))
474475
return -EINVAL;
476+
/* Program read-only/write-only not supported for special maps yet. */
477+
if (attr->map_flags & (BPF_F_RDONLY_PROG | BPF_F_WRONLY_PROG))
478+
return -EINVAL;
475479
return array_map_alloc_check(attr);
476480
}
477481

kernel/bpf/hashtab.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323

2424
#define HTAB_CREATE_FLAG_MASK \
2525
(BPF_F_NO_PREALLOC | BPF_F_NO_COMMON_LRU | BPF_F_NUMA_NODE | \
26-
BPF_F_RDONLY | BPF_F_WRONLY | BPF_F_ZERO_SEED)
26+
BPF_F_ACCESS_MASK | BPF_F_ZERO_SEED)
2727

2828
struct bucket {
2929
struct hlist_nulls_head head;
@@ -262,8 +262,8 @@ static int htab_map_alloc_check(union bpf_attr *attr)
262262
/* Guard against local DoS, and discourage production use. */
263263
return -EPERM;
264264

265-
if (attr->map_flags & ~HTAB_CREATE_FLAG_MASK)
266-
/* reserved bits should not be used */
265+
if (attr->map_flags & ~HTAB_CREATE_FLAG_MASK ||
266+
!bpf_map_flags_access_ok(attr->map_flags))
267267
return -EINVAL;
268268

269269
if (!lru && percpu_lru)

kernel/bpf/local_storage.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ DEFINE_PER_CPU(struct bpf_cgroup_storage*, bpf_cgroup_storage[MAX_BPF_CGROUP_STO
1414
#ifdef CONFIG_CGROUP_BPF
1515

1616
#define LOCAL_STORAGE_CREATE_FLAG_MASK \
17-
(BPF_F_NUMA_NODE | BPF_F_RDONLY | BPF_F_WRONLY)
17+
(BPF_F_NUMA_NODE | BPF_F_ACCESS_MASK)
1818

1919
struct bpf_cgroup_storage_map {
2020
struct bpf_map map;
@@ -282,8 +282,8 @@ static struct bpf_map *cgroup_storage_map_alloc(union bpf_attr *attr)
282282
if (attr->value_size > PAGE_SIZE)
283283
return ERR_PTR(-E2BIG);
284284

285-
if (attr->map_flags & ~LOCAL_STORAGE_CREATE_FLAG_MASK)
286-
/* reserved bits should not be used */
285+
if (attr->map_flags & ~LOCAL_STORAGE_CREATE_FLAG_MASK ||
286+
!bpf_map_flags_access_ok(attr->map_flags))
287287
return ERR_PTR(-EINVAL);
288288

289289
if (attr->max_entries)

kernel/bpf/lpm_trie.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -538,7 +538,7 @@ static int trie_delete_elem(struct bpf_map *map, void *_key)
538538
#define LPM_KEY_SIZE_MIN LPM_KEY_SIZE(LPM_DATA_SIZE_MIN)
539539

540540
#define LPM_CREATE_FLAG_MASK (BPF_F_NO_PREALLOC | BPF_F_NUMA_NODE | \
541-
BPF_F_RDONLY | BPF_F_WRONLY)
541+
BPF_F_ACCESS_MASK)
542542

543543
static struct bpf_map *trie_alloc(union bpf_attr *attr)
544544
{
@@ -553,6 +553,7 @@ static struct bpf_map *trie_alloc(union bpf_attr *attr)
553553
if (attr->max_entries == 0 ||
554554
!(attr->map_flags & BPF_F_NO_PREALLOC) ||
555555
attr->map_flags & ~LPM_CREATE_FLAG_MASK ||
556+
!bpf_map_flags_access_ok(attr->map_flags) ||
556557
attr->key_size < LPM_KEY_SIZE_MIN ||
557558
attr->key_size > LPM_KEY_SIZE_MAX ||
558559
attr->value_size < LPM_VAL_SIZE_MIN ||

kernel/bpf/queue_stack_maps.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@
1111
#include "percpu_freelist.h"
1212

1313
#define QUEUE_STACK_CREATE_FLAG_MASK \
14-
(BPF_F_NUMA_NODE | BPF_F_RDONLY | BPF_F_WRONLY)
15-
14+
(BPF_F_NUMA_NODE | BPF_F_ACCESS_MASK)
1615

1716
struct bpf_queue_stack {
1817
struct bpf_map map;
@@ -52,7 +51,8 @@ static int queue_stack_map_alloc_check(union bpf_attr *attr)
5251
/* check sanity of attributes */
5352
if (attr->max_entries == 0 || attr->key_size != 0 ||
5453
attr->value_size == 0 ||
55-
attr->map_flags & ~QUEUE_STACK_CREATE_FLAG_MASK)
54+
attr->map_flags & ~QUEUE_STACK_CREATE_FLAG_MASK ||
55+
!bpf_map_flags_access_ok(attr->map_flags))
5656
return -EINVAL;
5757

5858
if (attr->value_size > KMALLOC_MAX_SIZE)

kernel/bpf/syscall.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -501,6 +501,8 @@ static int map_check_btf(struct bpf_map *map, const struct btf *btf,
501501
map->spin_lock_off = btf_find_spin_lock(btf, value_type);
502502

503503
if (map_value_has_spin_lock(map)) {
504+
if (map->map_flags & BPF_F_RDONLY_PROG)
505+
return -EACCES;
504506
if (map->map_type != BPF_MAP_TYPE_HASH &&
505507
map->map_type != BPF_MAP_TYPE_ARRAY &&
506508
map->map_type != BPF_MAP_TYPE_CGROUP_STORAGE)

kernel/bpf/verifier.c

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1439,6 +1439,28 @@ static int check_stack_access(struct bpf_verifier_env *env,
14391439
return 0;
14401440
}
14411441

1442+
static int check_map_access_type(struct bpf_verifier_env *env, u32 regno,
1443+
int off, int size, enum bpf_access_type type)
1444+
{
1445+
struct bpf_reg_state *regs = cur_regs(env);
1446+
struct bpf_map *map = regs[regno].map_ptr;
1447+
u32 cap = bpf_map_flags_to_cap(map);
1448+
1449+
if (type == BPF_WRITE && !(cap & BPF_MAP_CAN_WRITE)) {
1450+
verbose(env, "write into map forbidden, value_size=%d off=%d size=%d\n",
1451+
map->value_size, off, size);
1452+
return -EACCES;
1453+
}
1454+
1455+
if (type == BPF_READ && !(cap & BPF_MAP_CAN_READ)) {
1456+
verbose(env, "read from map forbidden, value_size=%d off=%d size=%d\n",
1457+
map->value_size, off, size);
1458+
return -EACCES;
1459+
}
1460+
1461+
return 0;
1462+
}
1463+
14421464
/* check read/write into map element returned by bpf_map_lookup_elem() */
14431465
static int __check_map_access(struct bpf_verifier_env *env, u32 regno, int off,
14441466
int size, bool zero_size_allowed)
@@ -2024,7 +2046,9 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn
20242046
verbose(env, "R%d leaks addr into map\n", value_regno);
20252047
return -EACCES;
20262048
}
2027-
2049+
err = check_map_access_type(env, regno, off, size, t);
2050+
if (err)
2051+
return err;
20282052
err = check_map_access(env, regno, off, size, false);
20292053
if (!err && t == BPF_READ && value_regno >= 0)
20302054
mark_reg_unknown(env, regs, value_regno);
@@ -2327,6 +2351,10 @@ static int check_helper_mem_access(struct bpf_verifier_env *env, int regno,
23272351
return check_packet_access(env, regno, reg->off, access_size,
23282352
zero_size_allowed);
23292353
case PTR_TO_MAP_VALUE:
2354+
if (check_map_access_type(env, regno, reg->off, access_size,
2355+
meta && meta->raw_mode ? BPF_WRITE :
2356+
BPF_READ))
2357+
return -EACCES;
23302358
return check_map_access(env, regno, reg->off, access_size,
23312359
zero_size_allowed);
23322360
default: /* scalar_value|ptr_to_stack or invalid ptr */
@@ -3059,6 +3087,7 @@ record_func_map(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
30593087
int func_id, int insn_idx)
30603088
{
30613089
struct bpf_insn_aux_data *aux = &env->insn_aux_data[insn_idx];
3090+
struct bpf_map *map = meta->map_ptr;
30623091

30633092
if (func_id != BPF_FUNC_tail_call &&
30643093
func_id != BPF_FUNC_map_lookup_elem &&
@@ -3069,11 +3098,24 @@ record_func_map(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,
30693098
func_id != BPF_FUNC_map_peek_elem)
30703099
return 0;
30713100

3072-
if (meta->map_ptr == NULL) {
3101+
if (map == NULL) {
30733102
verbose(env, "kernel subsystem misconfigured verifier\n");
30743103
return -EINVAL;
30753104
}
30763105

3106+
/* In case of read-only, some additional restrictions
3107+
* need to be applied in order to prevent altering the
3108+
* state of the map from program side.
3109+
*/
3110+
if ((map->map_flags & BPF_F_RDONLY_PROG) &&
3111+
(func_id == BPF_FUNC_map_delete_elem ||
3112+
func_id == BPF_FUNC_map_update_elem ||
3113+
func_id == BPF_FUNC_map_push_elem ||
3114+
func_id == BPF_FUNC_map_pop_elem)) {
3115+
verbose(env, "write into map forbidden\n");
3116+
return -EACCES;
3117+
}
3118+
30773119
if (!BPF_MAP_PTR(aux->map_state))
30783120
bpf_map_ptr_store(aux, meta->map_ptr,
30793121
meta->map_ptr->unpriv_array);

0 commit comments

Comments
 (0)