1441 lines · 38.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 <linux/btf_ids.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; 	/* avoid overflow on round_up(map->value_size) */	if (attr->value_size > INT_MAX)		return -E2BIG;	/* percpu map value size is bound by PCPU_MIN_UNIT_SIZE */	if (percpu && round_up(attr->value_size, 8) > PCPU_MIN_UNIT_SIZE)		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(NULL);	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;} static void *array_map_elem_ptr(struct bpf_array* array, u32 index){	return array->value + (u64)array->elem_size * index;} /* 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 + (u64)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 = array->elem_size;	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]);} /* emit BPF instructions equivalent to C code of percpu_array_map_lookup_elem() */static int percpu_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; 	if (!bpf_jit_supports_percpu_insn())		return -EOPNOTSUPP; 	if (map->map_flags & BPF_F_INNER_MAP)		return -EOPNOTSUPP; 	BUILD_BUG_ON(offsetof(struct bpf_array, map) != 0);	*insn++ = BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, offsetof(struct bpf_array, pptrs)); 	*insn++ = BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_2, 0);	if (!map->bypass_spec_v1) {		*insn++ = BPF_JMP_IMM(BPF_JGE, BPF_REG_0, map->max_entries, 6);		*insn++ = BPF_ALU32_IMM(BPF_AND, BPF_REG_0, array->index_mask);	} else {		*insn++ = BPF_JMP_IMM(BPF_JGE, BPF_REG_0, map->max_entries, 5);	} 	*insn++ = BPF_ALU64_IMM(BPF_LSH, BPF_REG_0, 3);	*insn++ = BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_1);	*insn++ = BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_0, 0);	*insn++ = BPF_MOV64_PERCPU_REG(BPF_REG_0, BPF_REG_0);	*insn++ = BPF_JMP_IMM(BPF_JA, 0, 0, 1);	*insn++ = BPF_MOV64_IMM(BPF_REG_0, 0);	return insn - insn_buf;} static void *percpu_array_map_lookup_percpu_elem(struct bpf_map *map, void *key, u32 cpu){	struct bpf_array *array = container_of(map, struct bpf_array, map);	u32 index = *(u32 *)key; 	if (cpu >= nr_cpu_ids)		return NULL; 	if (unlikely(index >= array->map.max_entries))		return NULL; 	return per_cpu_ptr(array->pptrs[index & array->index_mask], cpu);} 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 = array->elem_size;	rcu_read_lock();	pptr = array->pptrs[index & array->index_mask];	for_each_possible_cpu(cpu) {		copy_map_value_long(map, value + off, per_cpu_ptr(pptr, cpu));		check_and_init_map_value(map, value + off);		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;} /* Called from syscall or from eBPF program */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);	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) &&		     !btf_record_has_field(map->record, BPF_SPIN_LOCK)))		return -EINVAL; 	if (array->map.map_type == BPF_MAP_TYPE_PERCPU_ARRAY) {		val = this_cpu_ptr(array->pptrs[index & array->index_mask]);		copy_map_value(map, val, value);		bpf_obj_free_fields(array->map.record, val);	} else {		val = array->value +			(u64)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);		bpf_obj_free_fields(array->map.record, 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 = array->elem_size;	rcu_read_lock();	pptr = array->pptrs[index & array->index_mask];	for_each_possible_cpu(cpu) {		copy_map_value_long(map, per_cpu_ptr(pptr, cpu), value + off);		bpf_obj_free_fields(array->map.record, per_cpu_ptr(pptr, cpu));		off += size;	}	rcu_read_unlock();	return 0;} /* Called from syscall or from eBPF program */static long 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_wq(struct bpf_map *map){	struct bpf_array *array = container_of(map, struct bpf_array, map);	int i; 	/* We don't reset or free fields other than timer and workqueue	 * on uref dropping to zero.	 */	if (btf_record_has_field(map->record, BPF_TIMER | BPF_WORKQUEUE)) {		for (i = 0; i < array->map.max_entries; i++) {			if (btf_record_has_field(map->record, BPF_TIMER))				bpf_obj_free_timer(map->record, array_map_elem_ptr(array, i));			if (btf_record_has_field(map->record, BPF_WORKQUEUE))				bpf_obj_free_workqueue(map->record, array_map_elem_ptr(array, i));		}	}} /* 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);	int i; 	if (!IS_ERR_OR_NULL(map->record)) {		if (array->map.map_type == BPF_MAP_TYPE_PERCPU_ARRAY) {			for (i = 0; i < array->map.max_entries; i++) {				void __percpu *pptr = array->pptrs[i & array->index_mask];				int cpu; 				for_each_possible_cpu(cpu) {					bpf_obj_free_fields(map->record, per_cpu_ptr(pptr, cpu));					cond_resched();				}			}		} else {			for (i = 0; i < array->map.max_entries; i++)				bpf_obj_free_fields(map->record, array_map_elem_ptr(array, i));		}	} 	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_putc(m, '\n');

Lines 1–500 of 1441.