Message ID | 20220220134813.3411982-11-memxor@gmail.com (mailing list archive) |
---|---|
State | Changes Requested |
Delegated to: | BPF |
Headers | show |
Series | Introduce typed pointer support in BPF maps | expand |
Hi Kumar, Thank you for the patch! Yet something to improve: [auto build test ERROR on next-20220217] [cannot apply to bpf-next/master bpf/master linus/master v5.17-rc4 v5.17-rc3 v5.17-rc2 v5.17-rc4] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch] url: https://github.com/0day-ci/linux/commits/Kumar-Kartikeya-Dwivedi/Introduce-typed-pointer-support-in-BPF-maps/20220220-215105 base: 3c30cf91b5ecc7272b3d2942ae0505dd8320b81c config: openrisc-randconfig-s032-20220220 (https://download.01.org/0day-ci/archive/20220221/202202210547.JnjWSpPA-lkp@intel.com/config) compiler: or1k-linux-gcc (GCC) 11.2.0 reproduce: wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # apt-get install sparse # sparse version: v0.6.4-dirty # https://github.com/0day-ci/linux/commit/09a47522ec608218eb6aabd5011316d78ad245e0 git remote add linux-review https://github.com/0day-ci/linux git fetch --no-tags linux-review Kumar-Kartikeya-Dwivedi/Introduce-typed-pointer-support-in-BPF-maps/20220220-215105 git checkout 09a47522ec608218eb6aabd5011316d78ad245e0 # save the config file to linux build tree mkdir build_dir COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.2.0 make.cross C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__' O=build_dir ARCH=openrisc SHELL=/bin/bash kernel/bpf/ If you fix the issue, kindly add following tag as appropriate Reported-by: kernel test robot <lkp@intel.com> All error/warnings (new ones prefixed by >>): kernel/bpf/syscall.c: In function 'bpf_map_free_ptr_to_btf_id': >> kernel/bpf/syscall.c:669:32: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast] 669 | off_desc->dtor((void *)old_ptr); | ^ In file included from arch/openrisc/include/asm/atomic.h:131, from include/linux/atomic.h:7, from include/asm-generic/bitops/lock.h:5, from arch/openrisc/include/asm/bitops.h:41, from include/linux/bitops.h:33, from include/linux/log2.h:12, from include/asm-generic/div64.h:55, from ./arch/openrisc/include/generated/asm/div64.h:1, from include/linux/math.h:6, from include/linux/math64.h:6, from include/linux/time.h:6, from include/linux/ktime.h:24, from include/linux/timer.h:6, from include/linux/workqueue.h:9, from include/linux/bpf.h:9, from kernel/bpf/syscall.c:4: In function '__xchg', inlined from 'bpf_map_free_ptr_to_btf_id' at kernel/bpf/syscall.c:668:13: >> arch/openrisc/include/asm/cmpxchg.h:160:24: error: call to '__xchg_called_with_bad_pointer' declared with attribute error: Bad argument size for xchg 160 | return __xchg_called_with_bad_pointer(); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ vim +669 kernel/bpf/syscall.c 640 641 /* Caller must ensure map_value_has_ptr_to_btf_id is true. Note that this 642 * function can be called on a map value while the map_value is visible to BPF 643 * programs, as it ensures the correct synchronization, and we already enforce 644 * the same using the verifier on the BPF program side, esp. for referenced 645 * pointers. 646 */ 647 void bpf_map_free_ptr_to_btf_id(struct bpf_map *map, void *map_value) 648 { 649 struct bpf_map_value_off *tab = map->ptr_off_tab; 650 u64 *btf_id_ptr; 651 int i; 652 653 for (i = 0; i < tab->nr_off; i++) { 654 struct bpf_map_value_off_desc *off_desc = &tab->off[i]; 655 u64 old_ptr; 656 657 btf_id_ptr = map_value + off_desc->offset; 658 if (!(off_desc->flags & BPF_MAP_VALUE_OFF_F_REF)) { 659 /* On 32-bit platforms, WRITE_ONCE 64-bit store tearing 660 * into two 32-bit stores is fine for us, as we only 661 * permit pointer values to be stored at this address, 662 * which are word sized, so the other half of 64-bit 663 * value will always be zeroed. 664 */ 665 WRITE_ONCE(*btf_id_ptr, 0); 666 continue; 667 } 668 old_ptr = xchg(btf_id_ptr, 0); > 669 off_desc->dtor((void *)old_ptr); 670 } 671 } 672 --- 0-DAY CI Kernel Test Service, Intel Corporation https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
Hi Kumar,
Thank you for the patch! Yet something to improve:
[auto build test ERROR on next-20220217]
[cannot apply to bpf-next/master bpf/master linus/master v5.17-rc4 v5.17-rc3 v5.17-rc2 v5.17-rc5]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]
url: https://github.com/0day-ci/linux/commits/Kumar-Kartikeya-Dwivedi/Introduce-typed-pointer-support-in-BPF-maps/20220220-215105
base: 3c30cf91b5ecc7272b3d2942ae0505dd8320b81c
config: mips-randconfig-r012-20220220 (https://download.01.org/0day-ci/archive/20220221/202202210651.wyTgHcwt-lkp@intel.com/config)
compiler: clang version 15.0.0 (https://github.com/llvm/llvm-project d271fc04d5b97b12e6b797c6067d3c96a8d7470e)
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# install mips cross compiling tool for clang build
# apt-get install binutils-mips-linux-gnu
# https://github.com/0day-ci/linux/commit/09a47522ec608218eb6aabd5011316d78ad245e0
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Kumar-Kartikeya-Dwivedi/Introduce-typed-pointer-support-in-BPF-maps/20220220-215105
git checkout 09a47522ec608218eb6aabd5011316d78ad245e0
# save the config file to linux build tree
mkdir build_dir
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=mips SHELL=/bin/bash kernel/bpf/
If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>
All errors (new ones prefixed by >>):
In file included from kernel/bpf/syscall.c:4:
In file included from include/linux/bpf.h:9:
In file included from include/linux/workqueue.h:9:
In file included from include/linux/timer.h:6:
In file included from include/linux/ktime.h:24:
In file included from include/linux/time.h:60:
In file included from include/linux/time32.h:13:
In file included from include/linux/timex.h:65:
In file included from arch/mips/include/asm/timex.h:19:
In file included from arch/mips/include/asm/cpu-type.h:12:
In file included from include/linux/smp.h:13:
In file included from include/linux/cpumask.h:13:
In file included from include/linux/atomic.h:7:
In file included from arch/mips/include/asm/atomic.h:23:
>> arch/mips/include/asm/cmpxchg.h:83:11: error: call to __xchg_called_with_bad_pointer declared with 'error' attribute: Bad argument size for xchg
return __xchg_called_with_bad_pointer();
^
1 error generated.
Kconfig warnings: (for reference only)
WARNING: unmet direct dependencies detected for OMAP_GPMC
Depends on MEMORY && OF_ADDRESS
Selected by
- MTD_NAND_OMAP2 && MTD && MTD_RAW_NAND && (ARCH_OMAP2PLUS || ARCH_KEYSTONE || ARCH_K3 || COMPILE_TEST && HAS_IOMEM
vim +/error +83 arch/mips/include/asm/cmpxchg.h
5154f3b4194910 Paul Burton 2017-06-09 66
b70eb30056dc84 Paul Burton 2017-06-09 67 extern unsigned long __xchg_small(volatile void *ptr, unsigned long val,
b70eb30056dc84 Paul Burton 2017-06-09 68 unsigned int size);
b70eb30056dc84 Paul Burton 2017-06-09 69
46f1619500d022 Thomas Bogendoerfer 2019-10-09 70 static __always_inline
46f1619500d022 Thomas Bogendoerfer 2019-10-09 71 unsigned long __xchg(volatile void *ptr, unsigned long x, int size)
b81947c646bfef David Howells 2012-03-28 72 {
b81947c646bfef David Howells 2012-03-28 73 switch (size) {
b70eb30056dc84 Paul Burton 2017-06-09 74 case 1:
b70eb30056dc84 Paul Burton 2017-06-09 75 case 2:
b70eb30056dc84 Paul Burton 2017-06-09 76 return __xchg_small(ptr, x, size);
b70eb30056dc84 Paul Burton 2017-06-09 77
b81947c646bfef David Howells 2012-03-28 78 case 4:
62c6081dca75d6 Paul Burton 2017-06-09 79 return __xchg_asm("ll", "sc", (volatile u32 *)ptr, x);
62c6081dca75d6 Paul Burton 2017-06-09 80
b81947c646bfef David Howells 2012-03-28 81 case 8:
62c6081dca75d6 Paul Burton 2017-06-09 82 if (!IS_ENABLED(CONFIG_64BIT))
62c6081dca75d6 Paul Burton 2017-06-09 @83 return __xchg_called_with_bad_pointer();
62c6081dca75d6 Paul Burton 2017-06-09 84
62c6081dca75d6 Paul Burton 2017-06-09 85 return __xchg_asm("lld", "scd", (volatile u64 *)ptr, x);
62c6081dca75d6 Paul Burton 2017-06-09 86
d15dc68c1143e2 Paul Burton 2017-06-09 87 default:
d15dc68c1143e2 Paul Burton 2017-06-09 88 return __xchg_called_with_bad_pointer();
b81947c646bfef David Howells 2012-03-28 89 }
b81947c646bfef David Howells 2012-03-28 90 }
b81947c646bfef David Howells 2012-03-28 91
---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
Hi Kumar,
Thank you for the patch! Yet something to improve:
[auto build test ERROR on next-20220217]
[cannot apply to bpf-next/master bpf/master linus/master v5.17-rc4 v5.17-rc3 v5.17-rc2 v5.17-rc5]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]
url: https://github.com/0day-ci/linux/commits/Kumar-Kartikeya-Dwivedi/Introduce-typed-pointer-support-in-BPF-maps/20220220-215105
base: 3c30cf91b5ecc7272b3d2942ae0505dd8320b81c
config: microblaze-randconfig-r022-20220220 (https://download.01.org/0day-ci/archive/20220221/202202210811.0jZyZUP1-lkp@intel.com/config)
compiler: microblaze-linux-gcc (GCC) 11.2.0
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# https://github.com/0day-ci/linux/commit/09a47522ec608218eb6aabd5011316d78ad245e0
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Kumar-Kartikeya-Dwivedi/Introduce-typed-pointer-support-in-BPF-maps/20220220-215105
git checkout 09a47522ec608218eb6aabd5011316d78ad245e0
# save the config file to linux build tree
mkdir build_dir
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.2.0 make.cross O=build_dir ARCH=microblaze SHELL=/bin/bash
If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>
All errors (new ones prefixed by >>):
microblaze-linux-ld: kernel/bpf/syscall.o: in function `bpf_map_free_ptr_to_btf_id':
>> (.text+0x555c): undefined reference to `__generic_xchg_called_with_bad_pointer'
---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 5d845ca02eba..744f1886cf91 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -23,6 +23,7 @@ #include <linux/slab.h> #include <linux/percpu-refcount.h> #include <linux/bpfptr.h> +#include <linux/btf.h> struct bpf_verifier_env; struct bpf_verifier_log; @@ -171,6 +172,7 @@ struct bpf_map_value_off_desc { u32 btf_id; struct btf *btf; struct module *module; + btf_dtor_kfunc_t dtor; /* only set when flags & BPF_MAP_VALUE_OFF_F_REF is true */ int flags; }; @@ -1568,6 +1570,7 @@ struct bpf_map_value_off_desc *bpf_map_ptr_off_contains(struct bpf_map *map, u32 void bpf_map_free_ptr_off_tab(struct bpf_map *map); struct bpf_map_value_off *bpf_map_copy_ptr_off_tab(const struct bpf_map *map); bool bpf_map_equal_ptr_off_tab(const struct bpf_map *map_a, const struct bpf_map *map_b); +void bpf_map_free_ptr_to_btf_id(struct bpf_map *map, void *map_value); struct bpf_map *bpf_map_get(u32 ufd); struct bpf_map *bpf_map_get_with_uref(u32 ufd); diff --git a/include/linux/btf.h b/include/linux/btf.h index a304a1ea39d9..c7e75be9637f 100644 --- a/include/linux/btf.h +++ b/include/linux/btf.h @@ -46,6 +46,8 @@ struct btf_id_dtor_kfunc { u32 kfunc_btf_id; }; +typedef void (*btf_dtor_kfunc_t)(void *); + extern const struct file_operations btf_fops; void btf_get(struct btf *btf); diff --git a/kernel/bpf/arraymap.c b/kernel/bpf/arraymap.c index 7f145aefbff8..de4baca3edd7 100644 --- a/kernel/bpf/arraymap.c +++ b/kernel/bpf/arraymap.c @@ -287,10 +287,12 @@ static int array_map_get_next_key(struct bpf_map *map, void *key, void *next_key return 0; } -static void check_and_free_timer_in_array(struct bpf_array *arr, void *val) +static void check_and_free_timer_and_ptr_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); + if (unlikely(map_value_has_ptr_to_btf_id(&arr->map))) + bpf_map_free_ptr_to_btf_id(&arr->map, val); } /* Called from syscall or from eBPF program */ @@ -327,7 +329,7 @@ static int array_map_update_elem(struct bpf_map *map, void *key, void *value, copy_map_value_locked(map, val, value, false); else copy_map_value(map, val, value); - check_and_free_timer_in_array(array, val); + check_and_free_timer_and_ptr_in_array(array, val); } return 0; } @@ -398,6 +400,13 @@ static void array_map_free_timers(struct bpf_map *map) static void array_map_free(struct bpf_map *map) { struct bpf_array *array = container_of(map, struct bpf_array, map); + int i; + + if (unlikely(map_value_has_ptr_to_btf_id(map))) { + for (i = 0; i < array->map.max_entries; i++) + bpf_map_free_ptr_to_btf_id(map, array->value + array->elem_size * i); + bpf_map_free_ptr_off_tab(map); + } if (array->map.map_type == BPF_MAP_TYPE_PERCPU_ARRAY) bpf_array_free_percpu(array); diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index 8a6ec1847f17..f322967da54b 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -3170,7 +3170,7 @@ static int btf_find_field_kptr(const struct btf *btf, const struct btf_type *t, int nr_off, ret, flags = 0; struct module *mod = NULL; struct btf *kernel_btf; - s32 id; + s32 id, dtor_btf_id; /* For PTR, sz is always == 8 */ if (!btf_type_is_ptr(t)) @@ -3291,9 +3291,79 @@ static int btf_find_field_kptr(const struct btf *btf, const struct btf_type *t, tab->off[nr_off].btf = kernel_btf; tab->off[nr_off].module = mod; tab->off[nr_off].flags = flags; + + /* Find and stash the function pointer for the destruction function that + * needs to be eventually invoked from the map free path. + * + * Note that we already took module reference, and the map free path + * always invoked the destructor for BTF ID before freeing ptr_off_tab, + * so calling the function should be safe in that context. + */ + if (ref_tag) { + const struct btf_type *dtor_func, *dtor_func_proto, *t; + const struct btf_param *args; + const char *dtor_func_name; + unsigned long addr; + u32 nr_args; + + /* This call also serves as a whitelist of allowed objects that + * can be used as a referenced pointer and be stored in a map at + * the same time. + */ + dtor_btf_id = btf_find_dtor_kfunc(kernel_btf, id); + if (dtor_btf_id < 0) { + ret = dtor_btf_id; + goto end_mod; + } + + dtor_func = btf_type_by_id(kernel_btf, dtor_btf_id); + if (!dtor_func || !btf_type_is_func(dtor_func)) { + ret = -EINVAL; + goto end_mod; + } + + dtor_func_proto = btf_type_by_id(kernel_btf, dtor_func->type); + if (!dtor_func_proto || !btf_type_is_func_proto(dtor_func_proto)) { + ret = -EINVAL; + goto end_mod; + } + + /* Make sure the prototype of the destructor kfunc is 'void func(type *)' */ + t = btf_type_by_id(kernel_btf, dtor_func_proto->type); + if (!t || !btf_type_is_void(t)) { + ret = -EINVAL; + goto end_mod; + } + + nr_args = btf_type_vlen(dtor_func_proto); + args = btf_params(dtor_func_proto); + + t = NULL; + if (nr_args) + t = btf_type_by_id(kernel_btf, args[0].type); + /* Allow any pointer type, as width on targets Linux supports + * will be same for all pointer types (i.e. sizeof(void *)) + */ + if (nr_args != 1 || !t || !btf_type_is_ptr(t)) { + ret = -EINVAL; + goto end_mod; + } + + dtor_func_name = __btf_name_by_offset(kernel_btf, dtor_func->name_off); + addr = kallsyms_lookup_name(dtor_func_name); + if (!addr) { + ret = -EINVAL; + goto end_mod; + } + tab->off[nr_off].dtor = (void *)addr; + } + tab->nr_off++; return 0; +end_mod: + if (mod) + module_put(mod); end_btf: /* Reference is only raised for module BTF */ if (btf_is_module(kernel_btf)) diff --git a/kernel/bpf/hashtab.c b/kernel/bpf/hashtab.c index d29af9988f37..3c33b58e8d3e 100644 --- a/kernel/bpf/hashtab.c +++ b/kernel/bpf/hashtab.c @@ -725,12 +725,15 @@ static int htab_lru_map_gen_lookup(struct bpf_map *map, return insn - insn_buf; } -static void check_and_free_timer(struct bpf_htab *htab, struct htab_elem *elem) +static void check_and_free_timer_and_ptr(struct bpf_htab *htab, + struct htab_elem *elem, bool free_ptr) { + void *map_value = elem->key + round_up(htab->map.key_size, 8); + if (unlikely(map_value_has_timer(&htab->map))) - bpf_timer_cancel_and_free(elem->key + - round_up(htab->map.key_size, 8) + - htab->map.timer_off); + bpf_timer_cancel_and_free(map_value + htab->map.timer_off); + if (unlikely(map_value_has_ptr_to_btf_id(&htab->map)) && free_ptr) + bpf_map_free_ptr_to_btf_id(&htab->map, map_value); } /* It is called from the bpf_lru_list when the LRU needs to delete @@ -757,7 +760,7 @@ static bool htab_lru_map_delete_node(void *arg, struct bpf_lru_node *node) hlist_nulls_for_each_entry_rcu(l, n, head, hash_node) if (l == tgt_l) { hlist_nulls_del_rcu(&l->hash_node); - check_and_free_timer(htab, l); + check_and_free_timer_and_ptr(htab, l, true); break; } @@ -829,7 +832,7 @@ static void htab_elem_free(struct bpf_htab *htab, struct htab_elem *l) { if (htab->map.map_type == BPF_MAP_TYPE_PERCPU_HASH) free_percpu(htab_elem_get_ptr(l, htab->map.key_size)); - check_and_free_timer(htab, l); + check_and_free_timer_and_ptr(htab, l, true); kfree(l); } @@ -857,7 +860,7 @@ static void free_htab_elem(struct bpf_htab *htab, struct htab_elem *l) htab_put_fd_value(htab, l); if (htab_is_prealloc(htab)) { - check_and_free_timer(htab, l); + check_and_free_timer_and_ptr(htab, l, true); __pcpu_freelist_push(&htab->freelist, &l->fnode); } else { atomic_dec(&htab->count); @@ -1104,7 +1107,7 @@ static int htab_map_update_elem(struct bpf_map *map, void *key, void *value, if (!htab_is_prealloc(htab)) free_htab_elem(htab, l_old); else - check_and_free_timer(htab, l_old); + check_and_free_timer_and_ptr(htab, l_old, true); } ret = 0; err: @@ -1114,7 +1117,7 @@ static int htab_map_update_elem(struct bpf_map *map, void *key, void *value, static void htab_lru_push_free(struct bpf_htab *htab, struct htab_elem *elem) { - check_and_free_timer(htab, elem); + check_and_free_timer_and_ptr(htab, elem, true); bpf_lru_push_free(&htab->lru, &elem->lru_node); } @@ -1420,7 +1423,10 @@ static void htab_free_malloced_timers(struct bpf_htab *htab) struct htab_elem *l; hlist_nulls_for_each_entry(l, n, head, hash_node) - check_and_free_timer(htab, l); + /* We are called from map_release_uref, so we don't free + * ref'd pointers. + */ + check_and_free_timer_and_ptr(htab, l, false); cond_resched_rcu(); } rcu_read_unlock(); @@ -1458,6 +1464,7 @@ static void htab_map_free(struct bpf_map *map) else prealloc_destroy(htab); + bpf_map_free_ptr_off_tab(map); free_percpu(htab->extra_elems); bpf_map_area_free(htab->buckets); for (i = 0; i < HASHTAB_MAP_LOCK_COUNT; i++) diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 83d71d6912f5..925e8c615ad2 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -638,15 +638,48 @@ bool bpf_map_equal_ptr_off_tab(const struct bpf_map *map_a, const struct bpf_map return !memcmp(tab_a, tab_b, size); } +/* Caller must ensure map_value_has_ptr_to_btf_id is true. Note that this + * function can be called on a map value while the map_value is visible to BPF + * programs, as it ensures the correct synchronization, and we already enforce + * the same using the verifier on the BPF program side, esp. for referenced + * pointers. + */ +void bpf_map_free_ptr_to_btf_id(struct bpf_map *map, void *map_value) +{ + struct bpf_map_value_off *tab = map->ptr_off_tab; + u64 *btf_id_ptr; + int i; + + for (i = 0; i < tab->nr_off; i++) { + struct bpf_map_value_off_desc *off_desc = &tab->off[i]; + u64 old_ptr; + + btf_id_ptr = map_value + off_desc->offset; + if (!(off_desc->flags & BPF_MAP_VALUE_OFF_F_REF)) { + /* On 32-bit platforms, WRITE_ONCE 64-bit store tearing + * into two 32-bit stores is fine for us, as we only + * permit pointer values to be stored at this address, + * which are word sized, so the other half of 64-bit + * value will always be zeroed. + */ + WRITE_ONCE(*btf_id_ptr, 0); + continue; + } + old_ptr = xchg(btf_id_ptr, 0); + off_desc->dtor((void *)old_ptr); + } +} + /* called from workqueue */ static void bpf_map_free_deferred(struct work_struct *work) { struct bpf_map *map = container_of(work, struct bpf_map, work); security_bpf_map_free(map); - bpf_map_free_ptr_off_tab(map); bpf_map_release_memcg(map); - /* implementation dependent freeing */ + /* implementation dependent freeing, map_free callback also does + * bpf_map_free_ptr_off_tab, if needed. + */ map->ops->map_free(map); }
A destructor kfunc can be defined as void func(type *), where type may be void or any other pointer type as per convenience. The kfunc doesn't have to take care about the map side pointer width, as it will be passed a pointer after converting the u64 address embedded in the map. In this patch, we ensure that the type is sane and capture the function pointer into off_desc of ptr_off_tab for the specific pointer offset, with the invariant that the dtor pointer is always set when 'ref' tag is applied to the pointer's pointee type, which is indicated by the flag BPF_MAP_VALUE_OFF_F_REF. Note that only BTF IDs whose destructor kfunc is registered, thus become the allowed BTF IDs for embedding as referenced PTR_TO_BTF_ID. Hence btf_find_dtor_kfunc serves the purpose of finding dtor kfunc BTF ID, as well acting as a check against the whitelist of allowed BTF IDs for this purpose. Finally, wire up the actual freeing of the referenced pointer if any at all available offsets, so that no references are leaked after the BPF map goes away and the BPF program previously moved the ownership a referenced pointer into it. The behavior is similar to BPF timers, where bpf_map_{update,delete}_elem will free any existing referenced PTR_TO_BTF_ID. The same case is with LRU map's bpf_lru_push_free/htab_lru_push_free functions, which are extended to reset and free referenced pointers. Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com> --- include/linux/bpf.h | 3 ++ include/linux/btf.h | 2 ++ kernel/bpf/arraymap.c | 13 ++++++-- kernel/bpf/btf.c | 72 ++++++++++++++++++++++++++++++++++++++++++- kernel/bpf/hashtab.c | 27 ++++++++++------ kernel/bpf/syscall.c | 37 ++++++++++++++++++++-- 6 files changed, 139 insertions(+), 15 deletions(-)