1353 lines · 35.7 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* Copyright (c) 2011-2014 PLUMgrid, http://plumgrid.com * Copyright (c) 2016,2017 Facebook */#include <linux/bpf.h>#include <linux/btf.h>#include <linux/err.h>#include <linux/slab.h>#include <linux/mm.h>#include <linux/filter.h>#include <linux/perf_event.h>#include <uapi/linux/btf.h>#include <linux/rcupdate_trace.h> #include "map_in_map.h" #define ARRAY_CREATE_FLAG_MASK \	(BPF_F_NUMA_NODE | BPF_F_MMAPABLE | BPF_F_ACCESS_MASK | \	 BPF_F_PRESERVE_ELEMS | BPF_F_INNER_MAP) static void bpf_array_free_percpu(struct bpf_array *array){	int i; 	for (i = 0; i < array->map.max_entries; i++) {		free_percpu(array->pptrs[i]);		cond_resched();	}} static int bpf_array_alloc_percpu(struct bpf_array *array){	void __percpu *ptr;	int i; 	for (i = 0; i < array->map.max_entries; i++) {		ptr = bpf_map_alloc_percpu(&array->map, array->elem_size, 8,					   GFP_USER | __GFP_NOWARN);		if (!ptr) {			bpf_array_free_percpu(array);			return -ENOMEM;		}		array->pptrs[i] = ptr;		cond_resched();	} 	return 0;} /* Called from syscall */int array_map_alloc_check(union bpf_attr *attr){	bool percpu = attr->map_type == BPF_MAP_TYPE_PERCPU_ARRAY;	int numa_node = bpf_map_attr_numa_node(attr); 	/* check sanity of attributes */	if (attr->max_entries == 0 || attr->key_size != 4 ||	    attr->value_size == 0 ||	    attr->map_flags & ~ARRAY_CREATE_FLAG_MASK ||	    !bpf_map_flags_access_ok(attr->map_flags) ||	    (percpu && numa_node != NUMA_NO_NODE))		return -EINVAL; 	if (attr->map_type != BPF_MAP_TYPE_ARRAY &&	    attr->map_flags & (BPF_F_MMAPABLE | BPF_F_INNER_MAP))		return -EINVAL; 	if (attr->map_type != BPF_MAP_TYPE_PERF_EVENT_ARRAY &&	    attr->map_flags & BPF_F_PRESERVE_ELEMS)		return -EINVAL; 	if (attr->value_size > KMALLOC_MAX_SIZE)		/* if value_size is bigger, the user space won't be able to		 * access the elements.		 */		return -E2BIG; 	return 0;} static struct bpf_map *array_map_alloc(union bpf_attr *attr){	bool percpu = attr->map_type == BPF_MAP_TYPE_PERCPU_ARRAY;	int numa_node = bpf_map_attr_numa_node(attr);	u32 elem_size, index_mask, max_entries;	bool bypass_spec_v1 = bpf_bypass_spec_v1();	u64 array_size, mask64;	struct bpf_array *array; 	elem_size = round_up(attr->value_size, 8); 	max_entries = attr->max_entries; 	/* On 32 bit archs roundup_pow_of_two() with max_entries that has	 * upper most bit set in u32 space is undefined behavior due to	 * resulting 1U << 32, so do it manually here in u64 space.	 */	mask64 = fls_long(max_entries - 1);	mask64 = 1ULL << mask64;	mask64 -= 1; 	index_mask = mask64;	if (!bypass_spec_v1) {		/* round up array size to nearest power of 2,		 * since cpu will speculate within index_mask limits		 */		max_entries = index_mask + 1;		/* Check for overflows. */		if (max_entries < attr->max_entries)			return ERR_PTR(-E2BIG);	} 	array_size = sizeof(*array);	if (percpu) {		array_size += (u64) max_entries * sizeof(void *);	} else {		/* rely on vmalloc() to return page-aligned memory and		 * ensure array->value is exactly page-aligned		 */		if (attr->map_flags & BPF_F_MMAPABLE) {			array_size = PAGE_ALIGN(array_size);			array_size += PAGE_ALIGN((u64) max_entries * elem_size);		} else {			array_size += (u64) max_entries * elem_size;		}	} 	/* allocate all map elements and zero-initialize them */	if (attr->map_flags & BPF_F_MMAPABLE) {		void *data; 		/* kmalloc'ed memory can't be mmap'ed, use explicit vmalloc */		data = bpf_map_area_mmapable_alloc(array_size, numa_node);		if (!data)			return ERR_PTR(-ENOMEM);		array = data + PAGE_ALIGN(sizeof(struct bpf_array))			- offsetof(struct bpf_array, value);	} else {		array = bpf_map_area_alloc(array_size, numa_node);	}	if (!array)		return ERR_PTR(-ENOMEM);	array->index_mask = index_mask;	array->map.bypass_spec_v1 = bypass_spec_v1; 	/* copy mandatory map attributes */	bpf_map_init_from_attr(&array->map, attr);	array->elem_size = elem_size; 	if (percpu && bpf_array_alloc_percpu(array)) {		bpf_map_area_free(array);		return ERR_PTR(-ENOMEM);	} 	return &array->map;} /* Called from syscall or from eBPF program */static void *array_map_lookup_elem(struct bpf_map *map, void *key){	struct bpf_array *array = container_of(map, struct bpf_array, map);	u32 index = *(u32 *)key; 	if (unlikely(index >= array->map.max_entries))		return NULL; 	return array->value + array->elem_size * (index & array->index_mask);} static int array_map_direct_value_addr(const struct bpf_map *map, u64 *imm,				       u32 off){	struct bpf_array *array = container_of(map, struct bpf_array, map); 	if (map->max_entries != 1)		return -ENOTSUPP;	if (off >= map->value_size)		return -EINVAL; 	*imm = (unsigned long)array->value;	return 0;} static int array_map_direct_value_meta(const struct bpf_map *map, u64 imm,				       u32 *off){	struct bpf_array *array = container_of(map, struct bpf_array, map);	u64 base = (unsigned long)array->value;	u64 range = array->elem_size; 	if (map->max_entries != 1)		return -ENOTSUPP;	if (imm < base || imm >= base + range)		return -ENOENT; 	*off = imm - base;	return 0;} /* emit BPF instructions equivalent to C code of array_map_lookup_elem() */static int array_map_gen_lookup(struct bpf_map *map, struct bpf_insn *insn_buf){	struct bpf_array *array = container_of(map, struct bpf_array, map);	struct bpf_insn *insn = insn_buf;	u32 elem_size = round_up(map->value_size, 8);	const int ret = BPF_REG_0;	const int map_ptr = BPF_REG_1;	const int index = BPF_REG_2; 	if (map->map_flags & BPF_F_INNER_MAP)		return -EOPNOTSUPP; 	*insn++ = BPF_ALU64_IMM(BPF_ADD, map_ptr, offsetof(struct bpf_array, value));	*insn++ = BPF_LDX_MEM(BPF_W, ret, index, 0);	if (!map->bypass_spec_v1) {		*insn++ = BPF_JMP_IMM(BPF_JGE, ret, map->max_entries, 4);		*insn++ = BPF_ALU32_IMM(BPF_AND, ret, array->index_mask);	} else {		*insn++ = BPF_JMP_IMM(BPF_JGE, ret, map->max_entries, 3);	} 	if (is_power_of_2(elem_size)) {		*insn++ = BPF_ALU64_IMM(BPF_LSH, ret, ilog2(elem_size));	} else {		*insn++ = BPF_ALU64_IMM(BPF_MUL, ret, elem_size);	}	*insn++ = BPF_ALU64_REG(BPF_ADD, ret, map_ptr);	*insn++ = BPF_JMP_IMM(BPF_JA, 0, 0, 1);	*insn++ = BPF_MOV64_IMM(ret, 0);	return insn - insn_buf;} /* Called from eBPF program */static void *percpu_array_map_lookup_elem(struct bpf_map *map, void *key){	struct bpf_array *array = container_of(map, struct bpf_array, map);	u32 index = *(u32 *)key; 	if (unlikely(index >= array->map.max_entries))		return NULL; 	return this_cpu_ptr(array->pptrs[index & array->index_mask]);} int bpf_percpu_array_copy(struct bpf_map *map, void *key, void *value){	struct bpf_array *array = container_of(map, struct bpf_array, map);	u32 index = *(u32 *)key;	void __percpu *pptr;	int cpu, off = 0;	u32 size; 	if (unlikely(index >= array->map.max_entries))		return -ENOENT; 	/* per_cpu areas are zero-filled and bpf programs can only	 * access 'value_size' of them, so copying rounded areas	 * will not leak any kernel data	 */	size = round_up(map->value_size, 8);	rcu_read_lock();	pptr = array->pptrs[index & array->index_mask];	for_each_possible_cpu(cpu) {		bpf_long_memcpy(value + off, per_cpu_ptr(pptr, cpu), size);		off += size;	}	rcu_read_unlock();	return 0;} /* Called from syscall */static int array_map_get_next_key(struct bpf_map *map, void *key, void *next_key){	struct bpf_array *array = container_of(map, struct bpf_array, map);	u32 index = key ? *(u32 *)key : U32_MAX;	u32 *next = (u32 *)next_key; 	if (index >= array->map.max_entries) {		*next = 0;		return 0;	} 	if (index == array->map.max_entries - 1)		return -ENOENT; 	*next = index + 1;	return 0;} static void check_and_free_timer_in_array(struct bpf_array *arr, void *val){	if (unlikely(map_value_has_timer(&arr->map)))		bpf_timer_cancel_and_free(val + arr->map.timer_off);} /* Called from syscall or from eBPF program */static int 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);	u32 index = *(u32 *)key;	char *val; 	if (unlikely((map_flags & ~BPF_F_LOCK) > BPF_EXIST))		/* unknown flags */		return -EINVAL; 	if (unlikely(index >= array->map.max_entries))		/* all elements were pre-allocated, cannot insert a new one */		return -E2BIG; 	if (unlikely(map_flags & BPF_NOEXIST))		/* all elements already exist */		return -EEXIST; 	if (unlikely((map_flags & BPF_F_LOCK) &&		     !map_value_has_spin_lock(map)))		return -EINVAL; 	if (array->map.map_type == BPF_MAP_TYPE_PERCPU_ARRAY) {		memcpy(this_cpu_ptr(array->pptrs[index & array->index_mask]),		       value, map->value_size);	} else {		val = array->value +			array->elem_size * (index & array->index_mask);		if (map_flags & BPF_F_LOCK)			copy_map_value_locked(map, val, value, false);		else			copy_map_value(map, val, value);		check_and_free_timer_in_array(array, val);	}	return 0;} int bpf_percpu_array_update(struct bpf_map *map, void *key, void *value,			    u64 map_flags){	struct bpf_array *array = container_of(map, struct bpf_array, map);	u32 index = *(u32 *)key;	void __percpu *pptr;	int cpu, off = 0;	u32 size; 	if (unlikely(map_flags > BPF_EXIST))		/* unknown flags */		return -EINVAL; 	if (unlikely(index >= array->map.max_entries))		/* all elements were pre-allocated, cannot insert a new one */		return -E2BIG; 	if (unlikely(map_flags == BPF_NOEXIST))		/* all elements already exist */		return -EEXIST; 	/* the user space will provide round_up(value_size, 8) bytes that	 * will be copied into per-cpu area. bpf programs can only access	 * value_size of it. During lookup the same extra bytes will be	 * returned or zeros which were zero-filled by percpu_alloc,	 * so no kernel data leaks possible	 */	size = round_up(map->value_size, 8);	rcu_read_lock();	pptr = array->pptrs[index & array->index_mask];	for_each_possible_cpu(cpu) {		bpf_long_memcpy(per_cpu_ptr(pptr, cpu), value + off, size);		off += size;	}	rcu_read_unlock();	return 0;} /* Called from syscall or from eBPF program */static int array_map_delete_elem(struct bpf_map *map, void *key){	return -EINVAL;} static void *array_map_vmalloc_addr(struct bpf_array *array){	return (void *)round_down((unsigned long)array, PAGE_SIZE);} static void array_map_free_timers(struct bpf_map *map){	struct bpf_array *array = container_of(map, struct bpf_array, map);	int i; 	if (likely(!map_value_has_timer(map)))		return; 	for (i = 0; i < array->map.max_entries; i++)		bpf_timer_cancel_and_free(array->value + array->elem_size * i +					  map->timer_off);} /* Called when map->refcnt goes to zero, either from workqueue or from syscall */static void array_map_free(struct bpf_map *map){	struct bpf_array *array = container_of(map, struct bpf_array, map); 	if (array->map.map_type == BPF_MAP_TYPE_PERCPU_ARRAY)		bpf_array_free_percpu(array); 	if (array->map.map_flags & BPF_F_MMAPABLE)		bpf_map_area_free(array_map_vmalloc_addr(array));	else		bpf_map_area_free(array);} static void array_map_seq_show_elem(struct bpf_map *map, void *key,				    struct seq_file *m){	void *value; 	rcu_read_lock(); 	value = array_map_lookup_elem(map, key);	if (!value) {		rcu_read_unlock();		return;	} 	if (map->btf_key_type_id)		seq_printf(m, "%u: ", *(u32 *)key);	btf_type_seq_show(map->btf, map->btf_value_type_id, value, m);	seq_puts(m, "\n"); 	rcu_read_unlock();} static void percpu_array_map_seq_show_elem(struct bpf_map *map, void *key,					   struct seq_file *m){	struct bpf_array *array = container_of(map, struct bpf_array, map);	u32 index = *(u32 *)key;	void __percpu *pptr;	int cpu; 	rcu_read_lock(); 	seq_printf(m, "%u: {\n", *(u32 *)key);	pptr = array->pptrs[index & array->index_mask];	for_each_possible_cpu(cpu) {		seq_printf(m, "\tcpu%d: ", cpu);		btf_type_seq_show(map->btf, map->btf_value_type_id,				  per_cpu_ptr(pptr, cpu), m);		seq_puts(m, "\n");	}	seq_puts(m, "}\n"); 	rcu_read_unlock();} static int array_map_check_btf(const struct bpf_map *map,			       const struct btf *btf,			       const struct btf_type *key_type,			       const struct btf_type *value_type){	u32 int_data; 	/* One exception for keyless BTF: .bss/.data/.rodata map */	if (btf_type_is_void(key_type)) {		if (map->map_type != BPF_MAP_TYPE_ARRAY ||		    map->max_entries != 1)			return -EINVAL; 		if (BTF_INFO_KIND(value_type->info) != BTF_KIND_DATASEC)			return -EINVAL; 		return 0;	} 	if (BTF_INFO_KIND(key_type->info) != BTF_KIND_INT)		return -EINVAL; 	int_data = *(u32 *)(key_type + 1);	/* bpf array can only take a u32 key. This check makes sure	 * that the btf matches the attr used during map_create.	 */	if (BTF_INT_BITS(int_data) != 32 || BTF_INT_OFFSET(int_data))		return -EINVAL; 	return 0;} static int array_map_mmap(struct bpf_map *map, struct vm_area_struct *vma){	struct bpf_array *array = container_of(map, struct bpf_array, map);	pgoff_t pgoff = PAGE_ALIGN(sizeof(*array)) >> PAGE_SHIFT; 	if (!(map->map_flags & BPF_F_MMAPABLE))		return -EINVAL; 	if (vma->vm_pgoff * PAGE_SIZE + (vma->vm_end - vma->vm_start) >	    PAGE_ALIGN((u64)array->map.max_entries * array->elem_size))		return -EINVAL; 	return remap_vmalloc_range(vma, array_map_vmalloc_addr(array),				   vma->vm_pgoff + pgoff);

Lines 1–500 of 1353.