Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

bpf: Disable migration when freeing stashed local kptr using obj drop

When a local kptr is stashed in a map and freed when the map goes away,
currently an error like the below appears:

[ 39.195695] BUG: using smp_processor_id() in preemptible [00000000] code: kworker/u32:15/2875
[ 39.196549] caller is bpf_mem_free+0x56/0xc0
[ 39.196958] CPU: 15 PID: 2875 Comm: kworker/u32:15 Tainted: G O 6.2.0-13016-g22df776a9a86 #4477
[ 39.197897] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.12.0-59-gc9ba5276e321-prebuilt.qemu.org 04/01/2014
[ 39.198949] Workqueue: events_unbound bpf_map_free_deferred
[ 39.199470] Call Trace:
[ 39.199703] <TASK>
[ 39.199911] dump_stack_lvl+0x60/0x70
[ 39.200267] check_preemption_disabled+0xbf/0xe0
[ 39.200704] bpf_mem_free+0x56/0xc0
[ 39.201032] ? bpf_obj_new_impl+0xa0/0xa0
[ 39.201430] bpf_obj_free_fields+0x1cd/0x200
[ 39.201838] array_map_free+0xad/0x220
[ 39.202193] ? finish_task_switch+0xe5/0x3c0
[ 39.202614] bpf_map_free_deferred+0xea/0x210
[ 39.203006] ? lockdep_hardirqs_on_prepare+0xe/0x220
[ 39.203460] process_one_work+0x64f/0xbe0
[ 39.203822] ? pwq_dec_nr_in_flight+0x110/0x110
[ 39.204264] ? do_raw_spin_lock+0x107/0x1c0
[ 39.204662] ? lockdep_hardirqs_on_prepare+0xe/0x220
[ 39.205107] worker_thread+0x74/0x7a0
[ 39.205451] ? process_one_work+0xbe0/0xbe0
[ 39.205818] kthread+0x171/0x1a0
[ 39.206111] ? kthread_complete_and_exit+0x20/0x20
[ 39.206552] ret_from_fork+0x1f/0x30
[ 39.206886] </TASK>

This happens because the call to __bpf_obj_drop_impl I added in the patch
adding support for stashing local kptrs doesn't disable migration. Prior
to that patch, __bpf_obj_drop_impl logic only ran when called by a BPF
progarm, whereas now it can be called from map free path, so it's
necessary to explicitly disable migration.

Also, refactor a bit to just call __bpf_obj_drop_impl directly instead
of bothering w/ dtor union and setting pointer-to-obj_drop.

Fixes: c8e187540914 ("bpf: Support __kptr to local kptrs")
Reported-by: Alexei Starovoitov <ast@kernel.org>
Signed-off-by: Dave Marchevsky <davemarchevsky@fb.com>
Link: https://lore.kernel.org/r/20230313214641.3731908-1-davemarchevsky@fb.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>

authored by

Dave Marchevsky and committed by
Alexei Starovoitov
9e36a204 22df776a

+12 -14
+4 -8
include/linux/bpf.h
··· 190 190 }; 191 191 192 192 typedef void (*btf_dtor_kfunc_t)(void *); 193 - typedef void (*btf_dtor_obj_drop)(void *, const struct btf_record *); 194 193 195 194 struct btf_field_kptr { 196 195 struct btf *btf; 197 196 struct module *module; 198 - union { 199 - /* dtor used if btf_is_kernel(btf), otherwise the type 200 - * is program-allocated and obj_drop is used 201 - */ 202 - btf_dtor_kfunc_t dtor; 203 - btf_dtor_obj_drop obj_drop; 204 - }; 197 + /* dtor used if btf_is_kernel(btf), otherwise the type is 198 + * program-allocated, dtor is NULL, and __bpf_obj_drop_impl is used 199 + */ 200 + btf_dtor_kfunc_t dtor; 205 201 u32 btf_id; 206 202 }; 207 203
+1 -3
kernel/bpf/btf.c
··· 3551 3551 return -EINVAL; 3552 3552 } 3553 3553 3554 - extern void __bpf_obj_drop_impl(void *p, const struct btf_record *rec); 3555 - 3556 3554 static int btf_parse_kptr(const struct btf *btf, struct btf_field *field, 3557 3555 struct btf_field_info *info) 3558 3556 { ··· 3576 3578 /* Type exists only in program BTF. Assume that it's a MEM_ALLOC 3577 3579 * kptr allocated via bpf_obj_new 3578 3580 */ 3579 - field->kptr.dtor = (void *)&__bpf_obj_drop_impl; 3581 + field->kptr.dtor = NULL; 3580 3582 id = info->kptr.type_id; 3581 3583 kptr_btf = (struct btf *)btf; 3582 3584 btf_get(kptr_btf);
+7 -3
kernel/bpf/syscall.c
··· 650 650 bpf_timer_cancel_and_free(obj + rec->timer_off); 651 651 } 652 652 653 + extern void __bpf_obj_drop_impl(void *p, const struct btf_record *rec); 654 + 653 655 void bpf_obj_free_fields(const struct btf_record *rec, void *obj) 654 656 { 655 657 const struct btf_field *fields; ··· 681 679 pointee_struct_meta = btf_find_struct_meta(field->kptr.btf, 682 680 field->kptr.btf_id); 683 681 WARN_ON_ONCE(!pointee_struct_meta); 684 - field->kptr.obj_drop(xchgd_field, pointee_struct_meta ? 685 - pointee_struct_meta->record : 686 - NULL); 682 + migrate_disable(); 683 + __bpf_obj_drop_impl(xchgd_field, pointee_struct_meta ? 684 + pointee_struct_meta->record : 685 + NULL); 686 + migrate_enable(); 687 687 } else { 688 688 field->kptr.dtor(xchgd_field); 689 689 }