aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorDaniel Borkmann <daniel@iogearbox.net>2019-04-09 17:20:05 -0400
committerAlexei Starovoitov <ast@kernel.org>2019-04-09 20:05:46 -0400
commit591fe9888d7809d9ee5c828020b6c6ae27c37229 (patch)
tree7032fc4e23d63b9cd0b301338e1bc49965eb175b /kernel
parentbe70bcd53de66e86f2726e576307cbdaebd3b1a5 (diff)
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 <daniel@iogearbox.net> Acked-by: Martin KaFai Lau <kafai@fb.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Diffstat (limited to 'kernel')
-rw-r--r--kernel/bpf/arraymap.c6
-rw-r--r--kernel/bpf/hashtab.c6
-rw-r--r--kernel/bpf/local_storage.c6
-rw-r--r--kernel/bpf/lpm_trie.c3
-rw-r--r--kernel/bpf/queue_stack_maps.c6
-rw-r--r--kernel/bpf/syscall.c2
-rw-r--r--kernel/bpf/verifier.c46
7 files changed, 62 insertions, 13 deletions
diff --git a/kernel/bpf/arraymap.c b/kernel/bpf/arraymap.c
index 1a6e9861d554..217b10bd9f48 100644
--- a/kernel/bpf/arraymap.c
+++ b/kernel/bpf/arraymap.c
@@ -22,7 +22,7 @@
22#include "map_in_map.h" 22#include "map_in_map.h"
23 23
24#define ARRAY_CREATE_FLAG_MASK \ 24#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)
26 26
27static void bpf_array_free_percpu(struct bpf_array *array) 27static void bpf_array_free_percpu(struct bpf_array *array)
28{ 28{
@@ -63,6 +63,7 @@ int array_map_alloc_check(union bpf_attr *attr)
63 if (attr->max_entries == 0 || attr->key_size != 4 || 63 if (attr->max_entries == 0 || attr->key_size != 4 ||
64 attr->value_size == 0 || 64 attr->value_size == 0 ||
65 attr->map_flags & ~ARRAY_CREATE_FLAG_MASK || 65 attr->map_flags & ~ARRAY_CREATE_FLAG_MASK ||
66 !bpf_map_flags_access_ok(attr->map_flags) ||
66 (percpu && numa_node != NUMA_NO_NODE)) 67 (percpu && numa_node != NUMA_NO_NODE))
67 return -EINVAL; 68 return -EINVAL;
68 69
@@ -472,6 +473,9 @@ static int fd_array_map_alloc_check(union bpf_attr *attr)
472 /* only file descriptors can be stored in this type of map */ 473 /* only file descriptors can be stored in this type of map */
473 if (attr->value_size != sizeof(u32)) 474 if (attr->value_size != sizeof(u32))
474 return -EINVAL; 475 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;
475 return array_map_alloc_check(attr); 479 return array_map_alloc_check(attr);
476} 480}
477 481
diff --git a/kernel/bpf/hashtab.c b/kernel/bpf/hashtab.c
index fed15cf94dca..192d32e77db3 100644
--- a/kernel/bpf/hashtab.c
+++ b/kernel/bpf/hashtab.c
@@ -23,7 +23,7 @@
23 23
24#define HTAB_CREATE_FLAG_MASK \ 24#define HTAB_CREATE_FLAG_MASK \
25 (BPF_F_NO_PREALLOC | BPF_F_NO_COMMON_LRU | BPF_F_NUMA_NODE | \ 25 (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)
27 27
28struct bucket { 28struct bucket {
29 struct hlist_nulls_head head; 29 struct hlist_nulls_head head;
@@ -262,8 +262,8 @@ static int htab_map_alloc_check(union bpf_attr *attr)
262 /* Guard against local DoS, and discourage production use. */ 262 /* Guard against local DoS, and discourage production use. */
263 return -EPERM; 263 return -EPERM;
264 264
265 if (attr->map_flags & ~HTAB_CREATE_FLAG_MASK) 265 if (attr->map_flags & ~HTAB_CREATE_FLAG_MASK ||
266 /* reserved bits should not be used */ 266 !bpf_map_flags_access_ok(attr->map_flags))
267 return -EINVAL; 267 return -EINVAL;
268 268
269 if (!lru && percpu_lru) 269 if (!lru && percpu_lru)
diff --git a/kernel/bpf/local_storage.c b/kernel/bpf/local_storage.c
index 6b572e2de7fb..980e8f1f6cb5 100644
--- a/kernel/bpf/local_storage.c
+++ b/kernel/bpf/local_storage.c
@@ -14,7 +14,7 @@ DEFINE_PER_CPU(struct bpf_cgroup_storage*, bpf_cgroup_storage[MAX_BPF_CGROUP_STO
14#ifdef CONFIG_CGROUP_BPF 14#ifdef CONFIG_CGROUP_BPF
15 15
16#define LOCAL_STORAGE_CREATE_FLAG_MASK \ 16#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)
18 18
19struct bpf_cgroup_storage_map { 19struct bpf_cgroup_storage_map {
20 struct bpf_map map; 20 struct bpf_map map;
@@ -282,8 +282,8 @@ static struct bpf_map *cgroup_storage_map_alloc(union bpf_attr *attr)
282 if (attr->value_size > PAGE_SIZE) 282 if (attr->value_size > PAGE_SIZE)
283 return ERR_PTR(-E2BIG); 283 return ERR_PTR(-E2BIG);
284 284
285 if (attr->map_flags & ~LOCAL_STORAGE_CREATE_FLAG_MASK) 285 if (attr->map_flags & ~LOCAL_STORAGE_CREATE_FLAG_MASK ||
286 /* reserved bits should not be used */ 286 !bpf_map_flags_access_ok(attr->map_flags))
287 return ERR_PTR(-EINVAL); 287 return ERR_PTR(-EINVAL);
288 288
289 if (attr->max_entries) 289 if (attr->max_entries)
diff --git a/kernel/bpf/lpm_trie.c b/kernel/bpf/lpm_trie.c
index 93a5cbbde421..e61630c2e50b 100644
--- a/kernel/bpf/lpm_trie.c
+++ b/kernel/bpf/lpm_trie.c
@@ -538,7 +538,7 @@ out:
538#define LPM_KEY_SIZE_MIN LPM_KEY_SIZE(LPM_DATA_SIZE_MIN) 538#define LPM_KEY_SIZE_MIN LPM_KEY_SIZE(LPM_DATA_SIZE_MIN)
539 539
540#define LPM_CREATE_FLAG_MASK (BPF_F_NO_PREALLOC | BPF_F_NUMA_NODE | \ 540#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)
542 542
543static struct bpf_map *trie_alloc(union bpf_attr *attr) 543static struct bpf_map *trie_alloc(union bpf_attr *attr)
544{ 544{
@@ -553,6 +553,7 @@ static struct bpf_map *trie_alloc(union bpf_attr *attr)
553 if (attr->max_entries == 0 || 553 if (attr->max_entries == 0 ||
554 !(attr->map_flags & BPF_F_NO_PREALLOC) || 554 !(attr->map_flags & BPF_F_NO_PREALLOC) ||
555 attr->map_flags & ~LPM_CREATE_FLAG_MASK || 555 attr->map_flags & ~LPM_CREATE_FLAG_MASK ||
556 !bpf_map_flags_access_ok(attr->map_flags) ||
556 attr->key_size < LPM_KEY_SIZE_MIN || 557 attr->key_size < LPM_KEY_SIZE_MIN ||
557 attr->key_size > LPM_KEY_SIZE_MAX || 558 attr->key_size > LPM_KEY_SIZE_MAX ||
558 attr->value_size < LPM_VAL_SIZE_MIN || 559 attr->value_size < LPM_VAL_SIZE_MIN ||
diff --git a/kernel/bpf/queue_stack_maps.c b/kernel/bpf/queue_stack_maps.c
index b384ea9f3254..0b140d236889 100644
--- a/kernel/bpf/queue_stack_maps.c
+++ b/kernel/bpf/queue_stack_maps.c
@@ -11,8 +11,7 @@
11#include "percpu_freelist.h" 11#include "percpu_freelist.h"
12 12
13#define QUEUE_STACK_CREATE_FLAG_MASK \ 13#define QUEUE_STACK_CREATE_FLAG_MASK \
14 (BPF_F_NUMA_NODE | BPF_F_RDONLY | BPF_F_WRONLY) 14 (BPF_F_NUMA_NODE | BPF_F_ACCESS_MASK)
15
16 15
17struct bpf_queue_stack { 16struct bpf_queue_stack {
18 struct bpf_map map; 17 struct bpf_map map;
@@ -52,7 +51,8 @@ static int queue_stack_map_alloc_check(union bpf_attr *attr)
52 /* check sanity of attributes */ 51 /* check sanity of attributes */
53 if (attr->max_entries == 0 || attr->key_size != 0 || 52 if (attr->max_entries == 0 || attr->key_size != 0 ||
54 attr->value_size == 0 || 53 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))
56 return -EINVAL; 56 return -EINVAL;
57 57
58 if (attr->value_size > KMALLOC_MAX_SIZE) 58 if (attr->value_size > KMALLOC_MAX_SIZE)
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 56b4b0e08b3b..0c9276b54c88 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -501,6 +501,8 @@ static int map_check_btf(struct bpf_map *map, const struct btf *btf,
501 map->spin_lock_off = btf_find_spin_lock(btf, value_type); 501 map->spin_lock_off = btf_find_spin_lock(btf, value_type);
502 502
503 if (map_value_has_spin_lock(map)) { 503 if (map_value_has_spin_lock(map)) {
504 if (map->map_flags & BPF_F_RDONLY_PROG)
505 return -EACCES;
504 if (map->map_type != BPF_MAP_TYPE_HASH && 506 if (map->map_type != BPF_MAP_TYPE_HASH &&
505 map->map_type != BPF_MAP_TYPE_ARRAY && 507 map->map_type != BPF_MAP_TYPE_ARRAY &&
506 map->map_type != BPF_MAP_TYPE_CGROUP_STORAGE) 508 map->map_type != BPF_MAP_TYPE_CGROUP_STORAGE)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 6ab7a23fc924..b747434df89c 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -1439,6 +1439,28 @@ static int check_stack_access(struct bpf_verifier_env *env,
1439 return 0; 1439 return 0;
1440} 1440}
1441 1441
1442static 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
1442/* check read/write into map element returned by bpf_map_lookup_elem() */ 1464/* check read/write into map element returned by bpf_map_lookup_elem() */
1443static int __check_map_access(struct bpf_verifier_env *env, u32 regno, int off, 1465static int __check_map_access(struct bpf_verifier_env *env, u32 regno, int off,
1444 int size, bool zero_size_allowed) 1466 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
2024 verbose(env, "R%d leaks addr into map\n", value_regno); 2046 verbose(env, "R%d leaks addr into map\n", value_regno);
2025 return -EACCES; 2047 return -EACCES;
2026 } 2048 }
2027 2049 err = check_map_access_type(env, regno, off, size, t);
2050 if (err)
2051 return err;
2028 err = check_map_access(env, regno, off, size, false); 2052 err = check_map_access(env, regno, off, size, false);
2029 if (!err && t == BPF_READ && value_regno >= 0) 2053 if (!err && t == BPF_READ && value_regno >= 0)
2030 mark_reg_unknown(env, regs, value_regno); 2054 mark_reg_unknown(env, regs, value_regno);
@@ -2327,6 +2351,10 @@ static int check_helper_mem_access(struct bpf_verifier_env *env, int regno,
2327 return check_packet_access(env, regno, reg->off, access_size, 2351 return check_packet_access(env, regno, reg->off, access_size,
2328 zero_size_allowed); 2352 zero_size_allowed);
2329 case PTR_TO_MAP_VALUE: 2353 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;
2330 return check_map_access(env, regno, reg->off, access_size, 2358 return check_map_access(env, regno, reg->off, access_size,
2331 zero_size_allowed); 2359 zero_size_allowed);
2332 default: /* scalar_value|ptr_to_stack or invalid ptr */ 2360 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,
3059 int func_id, int insn_idx) 3087 int func_id, int insn_idx)
3060{ 3088{
3061 struct bpf_insn_aux_data *aux = &env->insn_aux_data[insn_idx]; 3089 struct bpf_insn_aux_data *aux = &env->insn_aux_data[insn_idx];
3090 struct bpf_map *map = meta->map_ptr;
3062 3091
3063 if (func_id != BPF_FUNC_tail_call && 3092 if (func_id != BPF_FUNC_tail_call &&
3064 func_id != BPF_FUNC_map_lookup_elem && 3093 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,
3069 func_id != BPF_FUNC_map_peek_elem) 3098 func_id != BPF_FUNC_map_peek_elem)
3070 return 0; 3099 return 0;
3071 3100
3072 if (meta->map_ptr == NULL) { 3101 if (map == NULL) {
3073 verbose(env, "kernel subsystem misconfigured verifier\n"); 3102 verbose(env, "kernel subsystem misconfigured verifier\n");
3074 return -EINVAL; 3103 return -EINVAL;
3075 } 3104 }
3076 3105
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
3077 if (!BPF_MAP_PTR(aux->map_state)) 3119 if (!BPF_MAP_PTR(aux->map_state))
3078 bpf_map_ptr_store(aux, meta->map_ptr, 3120 bpf_map_ptr_store(aux, meta->map_ptr,
3079 meta->map_ptr->unpriv_array); 3121 meta->map_ptr->unpriv_array);