Commit 02adf9e9 authored by Alexei Starovoitov's avatar Alexei Starovoitov
Browse files

Merge branch 'error checking where helpers call bpf_map_ops'



JP Kobryn says:

====================

Within bpf programs, the bpf helper functions can make inline calls to
kernel functions. In this scenario there can be a disconnect between the
register the kernel function writes a return value to and the register the
bpf program uses to evaluate that return value.

As an example, this bpf code:

long err = bpf_map_update_elem(...);
if (err && err != -EEXIST)
	// got some error other than -EEXIST

...can result in the bpf assembly:

; err = bpf_map_update_elem(&mymap, &key, &val, BPF_NOEXIST);
  37:	movabs $0xffff976a10730400,%rdi
  41:	mov    $0x1,%ecx
  46:	call   0xffffffffe103291c	; htab_map_update_elem
; if (err && err != -EEXIST) {
  4b:	cmp    $0xffffffffffffffef,%rax ; cmp -EEXIST,%rax
  4f:	je     0x000000000000008e
  51:	test   %rax,%rax
  54:	je     0x000000000000008e

The compare operation here evaluates %rax, while in the preceding call to
htab_map_update_elem the corresponding assembly returns -EEXIST via %eax
(the lower 32 bits of %rax):

movl $0xffffffef, %r9d
...
movl %r9d, %eax

...since it's returning int (32-bit). So the resulting comparison becomes:

cmp $0xffffffffffffffef, $0x00000000ffffffef

...making it not possible to check for negative errors or specific errors,
since the sign value is left at the 32nd bit. It means in the original
example, the conditional branch will be entered even when the error is
-EEXIST, which was not intended.

The selftests added cover these cases for the different bpf_map_ops
functions. When the second patch is applied, changing the return type of
those functions to long, the comparison works as intended and the tests
pass.
====================

Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
parents d9d93f3b d7ba4cc9
Loading
Loading
Loading
Loading
+7 −7
Original line number Diff line number Diff line
@@ -96,11 +96,11 @@ struct bpf_map_ops {

	/* funcs callable from userspace and from eBPF programs */
	void *(*map_lookup_elem)(struct bpf_map *map, void *key);
	int (*map_update_elem)(struct bpf_map *map, void *key, void *value, u64 flags);
	int (*map_delete_elem)(struct bpf_map *map, void *key);
	int (*map_push_elem)(struct bpf_map *map, void *value, u64 flags);
	int (*map_pop_elem)(struct bpf_map *map, void *value);
	int (*map_peek_elem)(struct bpf_map *map, void *value);
	long (*map_update_elem)(struct bpf_map *map, void *key, void *value, u64 flags);
	long (*map_delete_elem)(struct bpf_map *map, void *key);
	long (*map_push_elem)(struct bpf_map *map, void *value, u64 flags);
	long (*map_pop_elem)(struct bpf_map *map, void *value);
	long (*map_peek_elem)(struct bpf_map *map, void *value);
	void *(*map_lookup_percpu_elem)(struct bpf_map *map, void *key, u32 cpu);

	/* funcs called by prog_array and perf_event_array map */
@@ -139,7 +139,7 @@ struct bpf_map_ops {
	struct bpf_local_storage __rcu ** (*map_owner_storage_ptr)(void *owner);

	/* Misc helpers.*/
	int (*map_redirect)(struct bpf_map *map, u64 key, u64 flags);
	long (*map_redirect)(struct bpf_map *map, u64 key, u64 flags);

	/* map_meta_equal must be implemented for maps that can be
	 * used as an inner map.  It is a runtime check to ensure
@@ -157,7 +157,7 @@ struct bpf_map_ops {
	int (*map_set_for_each_callback_args)(struct bpf_verifier_env *env,
					      struct bpf_func_state *caller,
					      struct bpf_func_state *callee);
	int (*map_for_each_callback)(struct bpf_map *map,
	long (*map_for_each_callback)(struct bpf_map *map,
				     bpf_callback_t callback_fn,
				     void *callback_ctx, u64 flags);

+3 −3
Original line number Diff line number Diff line
@@ -1504,7 +1504,7 @@ static inline bool bpf_sk_lookup_run_v6(struct net *net, int protocol,
}
#endif /* IS_ENABLED(CONFIG_IPV6) */

static __always_inline int __bpf_xdp_redirect_map(struct bpf_map *map, u64 index,
static __always_inline long __bpf_xdp_redirect_map(struct bpf_map *map, u64 index,
						   u64 flags, const u64 flag_mask,
						   void *lookup_elem(struct bpf_map *map, u32 key))
{
+6 −6
Original line number Diff line number Diff line
@@ -307,7 +307,7 @@ static int array_map_get_next_key(struct bpf_map *map, void *key, void *next_key
}

/* Called from syscall or from eBPF program */
static int array_map_update_elem(struct bpf_map *map, void *key, void *value,
static long array_map_update_elem(struct bpf_map *map, void *key, void *value,
				  u64 map_flags)
{
	struct bpf_array *array = container_of(map, struct bpf_array, map);
@@ -386,7 +386,7 @@ int bpf_percpu_array_update(struct bpf_map *map, void *key, void *value,
}

/* Called from syscall or from eBPF program */
static int array_map_delete_elem(struct bpf_map *map, void *key)
static long array_map_delete_elem(struct bpf_map *map, void *key)
{
	return -EINVAL;
}
@@ -686,7 +686,7 @@ static const struct bpf_iter_seq_info iter_seq_info = {
	.seq_priv_size		= sizeof(struct bpf_iter_seq_array_map_info),
};

static int bpf_for_each_array_elem(struct bpf_map *map, bpf_callback_t callback_fn,
static long bpf_for_each_array_elem(struct bpf_map *map, bpf_callback_t callback_fn,
				    void *callback_ctx, u64 flags)
{
	u32 i, key, num_elems = 0;
@@ -871,7 +871,7 @@ int bpf_fd_array_map_update_elem(struct bpf_map *map, struct file *map_file,
	return 0;
}

static int fd_array_map_delete_elem(struct bpf_map *map, void *key)
static long fd_array_map_delete_elem(struct bpf_map *map, void *key)
{
	struct bpf_array *array = container_of(map, struct bpf_array, map);
	void *old_ptr;
+6 −6
Original line number Diff line number Diff line
@@ -41,7 +41,7 @@ static u32 hash(struct bpf_bloom_filter *bloom, void *value,
	return h & bloom->bitset_mask;
}

static int bloom_map_peek_elem(struct bpf_map *map, void *value)
static long bloom_map_peek_elem(struct bpf_map *map, void *value)
{
	struct bpf_bloom_filter *bloom =
		container_of(map, struct bpf_bloom_filter, map);
@@ -56,7 +56,7 @@ static int bloom_map_peek_elem(struct bpf_map *map, void *value)
	return 0;
}

static int bloom_map_push_elem(struct bpf_map *map, void *value, u64 flags)
static long bloom_map_push_elem(struct bpf_map *map, void *value, u64 flags)
{
	struct bpf_bloom_filter *bloom =
		container_of(map, struct bpf_bloom_filter, map);
@@ -73,12 +73,12 @@ static int bloom_map_push_elem(struct bpf_map *map, void *value, u64 flags)
	return 0;
}

static int bloom_map_pop_elem(struct bpf_map *map, void *value)
static long bloom_map_pop_elem(struct bpf_map *map, void *value)
{
	return -EOPNOTSUPP;
}

static int bloom_map_delete_elem(struct bpf_map *map, void *value)
static long bloom_map_delete_elem(struct bpf_map *map, void *value)
{
	return -EOPNOTSUPP;
}
@@ -177,7 +177,7 @@ static void *bloom_map_lookup_elem(struct bpf_map *map, void *key)
	return ERR_PTR(-EINVAL);
}

static int bloom_map_update_elem(struct bpf_map *map, void *key,
static long bloom_map_update_elem(struct bpf_map *map, void *key,
				  void *value, u64 flags)
{
	/* The eBPF program should use map_push_elem instead */
+3 −3
Original line number Diff line number Diff line
@@ -93,7 +93,7 @@ static void *bpf_cgrp_storage_lookup_elem(struct bpf_map *map, void *key)
	return sdata ? sdata->data : NULL;
}

static int bpf_cgrp_storage_update_elem(struct bpf_map *map, void *key,
static long bpf_cgrp_storage_update_elem(struct bpf_map *map, void *key,
					 void *value, u64 map_flags)
{
	struct bpf_local_storage_data *sdata;
@@ -125,7 +125,7 @@ static int cgroup_storage_delete(struct cgroup *cgroup, struct bpf_map *map)
	return 0;
}

static int bpf_cgrp_storage_delete_elem(struct bpf_map *map, void *key)
static long bpf_cgrp_storage_delete_elem(struct bpf_map *map, void *key)
{
	struct cgroup *cgroup;
	int err, fd;
Loading