/* Copyright (c) 2011-2014 PLUMgrid, http://plumgrid.com * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. */#include <linux/bpf.h>#include <linux/bpf_trace.h>#include <linux/syscalls.h>#include <linux/slab.h>#include <linux/sched/signal.h>#include <linux/vmalloc.h>#include <linux/mmzone.h>#include <linux/anon_inodes.h>#include <linux/file.h>#include <linux/license.h>#include <linux/filter.h>#include <linux/version.h>#include <linux/kernel.h> DEFINE_PER_CPU(int, bpf_prog_active); int sysctl_unprivileged_bpf_disabled __read_mostly; static LIST_HEAD(bpf_map_types); static struct bpf_map *find_and_alloc_map(union bpf_attr *attr){ struct bpf_map_type_list *tl; struct bpf_map *map; list_for_each_entry(tl, &bpf_map_types, list_node) { if (tl->type == attr->map_type) { map = tl->ops->map_alloc(attr); if (IS_ERR(map)) return map; map->ops = tl->ops; map->map_type = attr->map_type; return map; } } return ERR_PTR(-EINVAL);} /* boot time registration of different map implementations */void bpf_register_map_type(struct bpf_map_type_list *tl){ list_add(&tl->list_node, &bpf_map_types);} void *bpf_map_area_alloc(size_t size){ /* We definitely need __GFP_NORETRY, so OOM killer doesn't * trigger under memory pressure as we really just want to * fail instead. */ const gfp_t flags = __GFP_NOWARN | __GFP_NORETRY | __GFP_ZERO; void *area; if (size <= (PAGE_SIZE << PAGE_ALLOC_COSTLY_ORDER)) { area = kmalloc(size, GFP_USER | flags); if (area != NULL) return area; } return __vmalloc(size, GFP_KERNEL | __GFP_HIGHMEM | flags, PAGE_KERNEL);} void bpf_map_area_free(void *area){ kvfree(area);} int bpf_map_precharge_memlock(u32 pages){ struct user_struct *user = get_current_user(); unsigned long memlock_limit, cur; memlock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT; cur = atomic_long_read(&user->locked_vm); free_uid(user); if (cur + pages > memlock_limit) return -EPERM; return 0;} static int bpf_map_charge_memlock(struct bpf_map *map){ struct user_struct *user = get_current_user(); unsigned long memlock_limit; memlock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT; atomic_long_add(map->pages, &user->locked_vm); if (atomic_long_read(&user->locked_vm) > memlock_limit) { atomic_long_sub(map->pages, &user->locked_vm); free_uid(user); return -EPERM; } map->user = user; return 0;} static void bpf_map_uncharge_memlock(struct bpf_map *map){ struct user_struct *user = map->user; atomic_long_sub(map->pages, &user->locked_vm); free_uid(user);} /* called from workqueue */static void bpf_map_free_deferred(struct work_struct *work){ struct bpf_map *map = container_of(work, struct bpf_map, work); bpf_map_uncharge_memlock(map); /* implementation dependent freeing */ map->ops->map_free(map);} static void bpf_map_put_uref(struct bpf_map *map){ if (atomic_dec_and_test(&map->usercnt)) { if (map->map_type == BPF_MAP_TYPE_PROG_ARRAY) bpf_fd_array_map_clear(map); }} /* decrement map refcnt and schedule it for freeing via workqueue * (unrelying map implementation ops->map_free() might sleep) */void bpf_map_put(struct bpf_map *map){ if (atomic_dec_and_test(&map->refcnt)) { INIT_WORK(&map->work, bpf_map_free_deferred); schedule_work(&map->work); }} void bpf_map_put_with_uref(struct bpf_map *map){ bpf_map_put_uref(map); bpf_map_put(map);} static int bpf_map_release(struct inode *inode, struct file *filp){ struct bpf_map *map = filp->private_data; if (map->ops->map_release) map->ops->map_release(map, filp); bpf_map_put_with_uref(map); return 0;} #ifdef CONFIG_PROC_FSstatic void bpf_map_show_fdinfo(struct seq_file *m, struct file *filp){ const struct bpf_map *map = filp->private_data; const struct bpf_array *array; u32 owner_prog_type = 0; if (map->map_type == BPF_MAP_TYPE_PROG_ARRAY) { array = container_of(map, struct bpf_array, map); owner_prog_type = array->owner_prog_type; } seq_printf(m, "map_type:\t%u\n" "key_size:\t%u\n" "value_size:\t%u\n" "max_entries:\t%u\n" "map_flags:\t%#x\n" "memlock:\t%llu\n", map->map_type, map->key_size, map->value_size, map->max_entries, map->map_flags, map->pages * 1ULL << PAGE_SHIFT); if (owner_prog_type) seq_printf(m, "owner_prog_type:\t%u\n", owner_prog_type);}#endif static const struct file_operations bpf_map_fops = {#ifdef CONFIG_PROC_FS .show_fdinfo = bpf_map_show_fdinfo,#endif .release = bpf_map_release,}; int bpf_map_new_fd(struct bpf_map *map){ return anon_inode_getfd("bpf-map", &bpf_map_fops, map, O_RDWR | O_CLOEXEC);} /* helper macro to check that unused fields 'union bpf_attr' are zero */#define CHECK_ATTR(CMD) \ memchr_inv((void *) &attr->CMD##_LAST_FIELD + \ sizeof(attr->CMD##_LAST_FIELD), 0, \ sizeof(*attr) - \ offsetof(union bpf_attr, CMD##_LAST_FIELD) - \ sizeof(attr->CMD##_LAST_FIELD)) != NULL #define BPF_MAP_CREATE_LAST_FIELD map_flags/* called via syscall */static int map_create(union bpf_attr *attr){ struct bpf_map *map; int err; err = CHECK_ATTR(BPF_MAP_CREATE); if (err) return -EINVAL; /* find map type and init map: hashtable vs rbtree vs bloom vs ... */ map = find_and_alloc_map(attr); if (IS_ERR(map)) return PTR_ERR(map); atomic_set(&map->refcnt, 1); atomic_set(&map->usercnt, 1); err = bpf_map_charge_memlock(map); if (err) goto free_map_nouncharge; err = bpf_map_new_fd(map); if (err < 0) /* failed to allocate fd */ goto free_map; trace_bpf_map_create(map, err); return err; free_map: bpf_map_uncharge_memlock(map);free_map_nouncharge: map->ops->map_free(map); return err;} /* if error is returned, fd is released. * On success caller should complete fd access with matching fdput() */struct bpf_map *__bpf_map_get(struct fd f){ if (!f.file) return ERR_PTR(-EBADF); if (f.file->f_op != &bpf_map_fops) { fdput(f); return ERR_PTR(-EINVAL); } return f.file->private_data;} /* prog's and map's refcnt limit */#define BPF_MAX_REFCNT 32768 struct bpf_map *bpf_map_inc(struct bpf_map *map, bool uref){ if (atomic_inc_return(&map->refcnt) > BPF_MAX_REFCNT) { atomic_dec(&map->refcnt); return ERR_PTR(-EBUSY); } if (uref) atomic_inc(&map->usercnt); return map;} struct bpf_map *bpf_map_get_with_uref(u32 ufd){ struct fd f = fdget(ufd); struct bpf_map *map; map = __bpf_map_get(f); if (IS_ERR(map)) return map; map = bpf_map_inc(map, true); fdput(f); return map;} int __weak bpf_stackmap_copy(struct bpf_map *map, void *key, void *value){ return -ENOTSUPP;} /* last field in 'union bpf_attr' used by this command */#define BPF_MAP_LOOKUP_ELEM_LAST_FIELD value static int map_lookup_elem(union bpf_attr *attr){ void __user *ukey = u64_to_user_ptr(attr->key); void __user *uvalue = u64_to_user_ptr(attr->value); int ufd = attr->map_fd; struct bpf_map *map; void *key, *value, *ptr; u32 value_size; struct fd f; int err; if (CHECK_ATTR(BPF_MAP_LOOKUP_ELEM)) return -EINVAL; f = fdget(ufd); map = __bpf_map_get(f); if (IS_ERR(map)) return PTR_ERR(map); err = -ENOMEM; key = kmalloc(map->key_size, GFP_USER); if (!key) goto err_put; err = -EFAULT; if (copy_from_user(key, ukey, map->key_size) != 0) goto free_key; if (map->map_type == BPF_MAP_TYPE_PERCPU_HASH || map->map_type == BPF_MAP_TYPE_LRU_PERCPU_HASH || map->map_type == BPF_MAP_TYPE_PERCPU_ARRAY) value_size = round_up(map->value_size, 8) * num_possible_cpus(); else value_size = map->value_size; err = -ENOMEM; value = kmalloc(value_size, GFP_USER | __GFP_NOWARN); if (!value) goto free_key; if (map->map_type == BPF_MAP_TYPE_PERCPU_HASH || map->map_type == BPF_MAP_TYPE_LRU_PERCPU_HASH) { err = bpf_percpu_hash_copy(map, key, value); } else if (map->map_type == BPF_MAP_TYPE_PERCPU_ARRAY) { err = bpf_percpu_array_copy(map, key, value); } else if (map->map_type == BPF_MAP_TYPE_STACK_TRACE) { err = bpf_stackmap_copy(map, key, value); } else { rcu_read_lock(); ptr = map->ops->map_lookup_elem(map, key); if (ptr) memcpy(value, ptr, value_size); rcu_read_unlock(); err = ptr ? 0 : -ENOENT; } if (err) goto free_value; err = -EFAULT; if (copy_to_user(uvalue, value, value_size) != 0) goto free_value; trace_bpf_map_lookup_elem(map, ufd, key, value); err = 0; free_value: kfree(value);free_key: kfree(key);err_put: fdput(f); return err;} #define BPF_MAP_UPDATE_ELEM_LAST_FIELD flags static int map_update_elem(union bpf_attr *attr){ void __user *ukey = u64_to_user_ptr(attr->key); void __user *uvalue = u64_to_user_ptr(attr->value); int ufd = attr->map_fd; struct bpf_map *map; void *key, *value; u32 value_size; struct fd f; int err; if (CHECK_ATTR(BPF_MAP_UPDATE_ELEM)) return -EINVAL; f = fdget(ufd); map = __bpf_map_get(f); if (IS_ERR(map)) return PTR_ERR(map); err = -ENOMEM; key = kmalloc(map->key_size, GFP_USER); if (!key) goto err_put; err = -EFAULT; if (copy_from_user(key, ukey, map->key_size) != 0) goto free_key; if (map->map_type == BPF_MAP_TYPE_PERCPU_HASH || map->map_type == BPF_MAP_TYPE_LRU_PERCPU_HASH || map->map_type == BPF_MAP_TYPE_PERCPU_ARRAY) value_size = round_up(map->value_size, 8) * num_possible_cpus(); else value_size = map->value_size; err = -ENOMEM; value = kmalloc(value_size, GFP_USER | __GFP_NOWARN); if (!value) goto free_key; err = -EFAULT; if (copy_from_user(value, uvalue, value_size) != 0) goto free_value; /* must increment bpf_prog_active to avoid kprobe+bpf triggering from * inside bpf map update or delete otherwise deadlocks are possible */ preempt_disable(); __this_cpu_inc(bpf_prog_active); if (map->map_type == BPF_MAP_TYPE_PERCPU_HASH || map->map_type == BPF_MAP_TYPE_LRU_PERCPU_HASH) { err = bpf_percpu_hash_update(map, key, value, attr->flags); } else if (map->map_type == BPF_MAP_TYPE_PERCPU_ARRAY) { err = bpf_percpu_array_update(map, key, value, attr->flags); } else if (map->map_type == BPF_MAP_TYPE_PERF_EVENT_ARRAY || map->map_type == BPF_MAP_TYPE_PROG_ARRAY || map->map_type == BPF_MAP_TYPE_CGROUP_ARRAY) { rcu_read_lock(); err = bpf_fd_array_map_update_elem(map, f.file, key, value, attr->flags); rcu_read_unlock(); } else { rcu_read_lock(); err = map->ops->map_update_elem(map, key, value, attr->flags); rcu_read_unlock(); } __this_cpu_dec(bpf_prog_active); preempt_enable(); if (!err) trace_bpf_map_update_elem(map, ufd, key, value);free_value: kfree(value);free_key: kfree(key);err_put: fdput(f); return err;} #define BPF_MAP_DELETE_ELEM_LAST_FIELD key static int map_delete_elem(union bpf_attr *attr){ void __user *ukey = u64_to_user_ptr(attr->key); int ufd = attr->map_fd; struct bpf_map *map; struct fd f; void *key; int err; if (CHECK_ATTR(BPF_MAP_DELETE_ELEM)) return -EINVAL; f = fdget(ufd); map = __bpf_map_get(f); if (IS_ERR(map)) return PTR_ERR(map); err = -ENOMEM; key = kmalloc(map->key_size, GFP_USER); if (!key) goto err_put; err = -EFAULT; if (copy_from_user(key, ukey, map->key_size) != 0) goto free_key; preempt_disable(); __this_cpu_inc(bpf_prog_active); rcu_read_lock(); err = map->ops->map_delete_elem(map, key); rcu_read_unlock(); __this_cpu_dec(bpf_prog_active); preempt_enable(); Lines 1–500 of 1113. Load more
/* Copyright (c) 2011-2014 PLUMgrid, http://plumgrid.com * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. */ #include <linux/bpf.h> #include <linux/bpf_trace.h> #include <linux/syscalls.h> #include <linux/slab.h> #include <linux/sched/signal.h> #include <linux/vmalloc.h> #include <linux/mmzone.h> #include <linux/anon_inodes.h> #include <linux/file.h> #include <linux/license.h> #include <linux/filter.h> #include <linux/version.h> #include <linux/kernel.h> DEFINE_PER_CPU(int, bpf_prog_active); int sysctl_unprivileged_bpf_disabled __read_mostly; static LIST_HEAD(bpf_map_types); static struct bpf_map *find_and_alloc_map(union bpf_attr *attr) { struct bpf_map_type_list *tl; struct bpf_map *map; list_for_each_entry(tl, &bpf_map_types, list_node) { if (tl->type == attr->map_type) { map = tl->ops->map_alloc(attr); if (IS_ERR(map)) return map; map->ops = tl->ops; map->map_type = attr->map_type; return map; } } return ERR_PTR(-EINVAL); } /* boot time registration of different map implementations */ void bpf_register_map_type(struct bpf_map_type_list *tl) { list_add(&tl->list_node, &bpf_map_types); } void *bpf_map_area_alloc(size_t size) { /* We definitely need __GFP_NORETRY, so OOM killer doesn't * trigger under memory pressure as we really just want to * fail instead. */ const gfp_t flags = __GFP_NOWARN | __GFP_NORETRY | __GFP_ZERO; void *area; if (size <= (PAGE_SIZE << PAGE_ALLOC_COSTLY_ORDER)) { area = kmalloc(size, GFP_USER | flags); if (area != NULL) return area; } return __vmalloc(size, GFP_KERNEL | __GFP_HIGHMEM | flags, PAGE_KERNEL); } void bpf_map_area_free(void *area) { kvfree(area); } int bpf_map_precharge_memlock(u32 pages) { struct user_struct *user = get_current_user(); unsigned long memlock_limit, cur; memlock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT; cur = atomic_long_read(&user->locked_vm); free_uid(user); if (cur + pages > memlock_limit) return -EPERM; return 0; } static int bpf_map_charge_memlock(struct bpf_map *map) { struct user_struct *user = get_current_user(); unsigned long memlock_limit; memlock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT; atomic_long_add(map->pages, &user->locked_vm); if (atomic_long_read(&user->locked_vm) > memlock_limit) { atomic_long_sub(map->pages, &user->locked_vm); free_uid(user); return -EPERM; } map->user = user; return 0; } static void bpf_map_uncharge_memlock(struct bpf_map *map) { struct user_struct *user = map->user; atomic_long_sub(map->pages, &user->locked_vm); free_uid(user); } /* called from workqueue */ static void bpf_map_free_deferred(struct work_struct *work) { struct bpf_map *map = container_of(work, struct bpf_map, work); bpf_map_uncharge_memlock(map); /* implementation dependent freeing */ map->ops->map_free(map); } static void bpf_map_put_uref(struct bpf_map *map) { if (atomic_dec_and_test(&map->usercnt)) { if (map->map_type == BPF_MAP_TYPE_PROG_ARRAY) bpf_fd_array_map_clear(map); } } /* decrement map refcnt and schedule it for freeing via workqueue * (unrelying map implementation ops->map_free() might sleep) */ void bpf_map_put(struct bpf_map *map) { if (atomic_dec_and_test(&map->refcnt)) { INIT_WORK(&map->work, bpf_map_free_deferred); schedule_work(&map->work); } } void bpf_map_put_with_uref(struct bpf_map *map) { bpf_map_put_uref(map); bpf_map_put(map); } static int bpf_map_release(struct inode *inode, struct file *filp) { struct bpf_map *map = filp->private_data; if (map->ops->map_release) map->ops->map_release(map, filp); bpf_map_put_with_uref(map); return 0; } #ifdef CONFIG_PROC_FS static void bpf_map_show_fdinfo(struct seq_file *m, struct file *filp) { const struct bpf_map *map = filp->private_data; const struct bpf_array *array; u32 owner_prog_type = 0; if (map->map_type == BPF_MAP_TYPE_PROG_ARRAY) { array = container_of(map, struct bpf_array, map); owner_prog_type = array->owner_prog_type; } seq_printf(m, "map_type:\t%u\n" "key_size:\t%u\n" "value_size:\t%u\n" "max_entries:\t%u\n" "map_flags:\t%#x\n" "memlock:\t%llu\n", map->map_type, map->key_size, map->value_size, map->max_entries, map->map_flags, map->pages * 1ULL << PAGE_SHIFT); if (owner_prog_type) seq_printf(m, "owner_prog_type:\t%u\n", owner_prog_type); } #endif static const struct file_operations bpf_map_fops = { #ifdef CONFIG_PROC_FS .show_fdinfo = bpf_map_show_fdinfo, #endif .release = bpf_map_release, }; int bpf_map_new_fd(struct bpf_map *map) { return anon_inode_getfd("bpf-map", &bpf_map_fops, map, O_RDWR | O_CLOEXEC); } /* helper macro to check that unused fields 'union bpf_attr' are zero */ #define CHECK_ATTR(CMD) \ memchr_inv((void *) &attr->CMD##_LAST_FIELD + \ sizeof(attr->CMD##_LAST_FIELD), 0, \ sizeof(*attr) - \ offsetof(union bpf_attr, CMD##_LAST_FIELD) - \ sizeof(attr->CMD##_LAST_FIELD)) != NULL #define BPF_MAP_CREATE_LAST_FIELD map_flags /* called via syscall */ static int map_create(union bpf_attr *attr) { struct bpf_map *map; int err; err = CHECK_ATTR(BPF_MAP_CREATE); if (err) return -EINVAL; /* find map type and init map: hashtable vs rbtree vs bloom vs ... */ map = find_and_alloc_map(attr); if (IS_ERR(map)) return PTR_ERR(map); atomic_set(&map->refcnt, 1); atomic_set(&map->usercnt, 1); err = bpf_map_charge_memlock(map); if (err) goto free_map_nouncharge; err = bpf_map_new_fd(map); if (err < 0) /* failed to allocate fd */ goto free_map; trace_bpf_map_create(map, err); return err; free_map: bpf_map_uncharge_memlock(map); free_map_nouncharge: map->ops->map_free(map); return err; } /* if error is returned, fd is released. * On success caller should complete fd access with matching fdput() */ struct bpf_map *__bpf_map_get(struct fd f) { if (!f.file) return ERR_PTR(-EBADF); if (f.file->f_op != &bpf_map_fops) { fdput(f); return ERR_PTR(-EINVAL); } return f.file->private_data; } /* prog's and map's refcnt limit */ #define BPF_MAX_REFCNT 32768 struct bpf_map *bpf_map_inc(struct bpf_map *map, bool uref) { if (atomic_inc_return(&map->refcnt) > BPF_MAX_REFCNT) { atomic_dec(&map->refcnt); return ERR_PTR(-EBUSY); } if (uref) atomic_inc(&map->usercnt); return map; } struct bpf_map *bpf_map_get_with_uref(u32 ufd) { struct fd f = fdget(ufd); struct bpf_map *map; map = __bpf_map_get(f); if (IS_ERR(map)) return map; map = bpf_map_inc(map, true); fdput(f); return map; } int __weak bpf_stackmap_copy(struct bpf_map *map, void *key, void *value) { return -ENOTSUPP; } /* last field in 'union bpf_attr' used by this command */ #define BPF_MAP_LOOKUP_ELEM_LAST_FIELD value static int map_lookup_elem(union bpf_attr *attr) { void __user *ukey = u64_to_user_ptr(attr->key); void __user *uvalue = u64_to_user_ptr(attr->value); int ufd = attr->map_fd; struct bpf_map *map; void *key, *value, *ptr; u32 value_size; struct fd f; int err; if (CHECK_ATTR(BPF_MAP_LOOKUP_ELEM)) return -EINVAL; f = fdget(ufd); map = __bpf_map_get(f); if (IS_ERR(map)) return PTR_ERR(map); err = -ENOMEM; key = kmalloc(map->key_size, GFP_USER); if (!key) goto err_put; err = -EFAULT; if (copy_from_user(key, ukey, map->key_size) != 0) goto free_key; if (map->map_type == BPF_MAP_TYPE_PERCPU_HASH || map->map_type == BPF_MAP_TYPE_LRU_PERCPU_HASH || map->map_type == BPF_MAP_TYPE_PERCPU_ARRAY) value_size = round_up(map->value_size, 8) * num_possible_cpus(); else value_size = map->value_size; err = -ENOMEM; value = kmalloc(value_size, GFP_USER | __GFP_NOWARN); if (!value) goto free_key; if (map->map_type == BPF_MAP_TYPE_PERCPU_HASH || map->map_type == BPF_MAP_TYPE_LRU_PERCPU_HASH) { err = bpf_percpu_hash_copy(map, key, value); } else if (map->map_type == BPF_MAP_TYPE_PERCPU_ARRAY) { err = bpf_percpu_array_copy(map, key, value); } else if (map->map_type == BPF_MAP_TYPE_STACK_TRACE) { err = bpf_stackmap_copy(map, key, value); } else { rcu_read_lock(); ptr = map->ops->map_lookup_elem(map, key); if (ptr) memcpy(value, ptr, value_size); rcu_read_unlock(); err = ptr ? 0 : -ENOENT; } if (err) goto free_value; err = -EFAULT; if (copy_to_user(uvalue, value, value_size) != 0) goto free_value; trace_bpf_map_lookup_elem(map, ufd, key, value); err = 0; free_value: kfree(value); free_key: kfree(key); err_put: fdput(f); return err; } #define BPF_MAP_UPDATE_ELEM_LAST_FIELD flags static int map_update_elem(union bpf_attr *attr) { void __user *ukey = u64_to_user_ptr(attr->key); void __user *uvalue = u64_to_user_ptr(attr->value); int ufd = attr->map_fd; struct bpf_map *map; void *key, *value; u32 value_size; struct fd f; int err; if (CHECK_ATTR(BPF_MAP_UPDATE_ELEM)) return -EINVAL; f = fdget(ufd); map = __bpf_map_get(f); if (IS_ERR(map)) return PTR_ERR(map); err = -ENOMEM; key = kmalloc(map->key_size, GFP_USER); if (!key) goto err_put; err = -EFAULT; if (copy_from_user(key, ukey, map->key_size) != 0) goto free_key; if (map->map_type == BPF_MAP_TYPE_PERCPU_HASH || map->map_type == BPF_MAP_TYPE_LRU_PERCPU_HASH || map->map_type == BPF_MAP_TYPE_PERCPU_ARRAY) value_size = round_up(map->value_size, 8) * num_possible_cpus(); else value_size = map->value_size; err = -ENOMEM; value = kmalloc(value_size, GFP_USER | __GFP_NOWARN); if (!value) goto free_key; err = -EFAULT; if (copy_from_user(value, uvalue, value_size) != 0) goto free_value; /* must increment bpf_prog_active to avoid kprobe+bpf triggering from * inside bpf map update or delete otherwise deadlocks are possible */ preempt_disable(); __this_cpu_inc(bpf_prog_active); if (map->map_type == BPF_MAP_TYPE_PERCPU_HASH || map->map_type == BPF_MAP_TYPE_LRU_PERCPU_HASH) { err = bpf_percpu_hash_update(map, key, value, attr->flags); } else if (map->map_type == BPF_MAP_TYPE_PERCPU_ARRAY) { err = bpf_percpu_array_update(map, key, value, attr->flags); } else if (map->map_type == BPF_MAP_TYPE_PERF_EVENT_ARRAY || map->map_type == BPF_MAP_TYPE_PROG_ARRAY || map->map_type == BPF_MAP_TYPE_CGROUP_ARRAY) { rcu_read_lock(); err = bpf_fd_array_map_update_elem(map, f.file, key, value, attr->flags); rcu_read_unlock(); } else { rcu_read_lock(); err = map->ops->map_update_elem(map, key, value, attr->flags); rcu_read_unlock(); } __this_cpu_dec(bpf_prog_active); preempt_enable(); if (!err) trace_bpf_map_update_elem(map, ufd, key, value); free_value: kfree(value); free_key: kfree(key); err_put: fdput(f); return err; } #define BPF_MAP_DELETE_ELEM_LAST_FIELD key static int map_delete_elem(union bpf_attr *attr) { void __user *ukey = u64_to_user_ptr(attr->key); int ufd = attr->map_fd; struct bpf_map *map; struct fd f; void *key; int err; if (CHECK_ATTR(BPF_MAP_DELETE_ELEM)) return -EINVAL; f = fdget(ufd); map = __bpf_map_get(f); if (IS_ERR(map)) return PTR_ERR(map); err = -ENOMEM; key = kmalloc(map->key_size, GFP_USER); if (!key) goto err_put; err = -EFAULT; if (copy_from_user(key, ukey, map->key_size) != 0) goto free_key; preempt_disable(); __this_cpu_inc(bpf_prog_active); rcu_read_lock(); err = map->ops->map_delete_elem(map, key); rcu_read_unlock(); __this_cpu_dec(bpf_prog_active); preempt_enable();