Skip to content

Commit 2a9d291

Browse files
jrfastabkernel-patches-bot
authored andcommitted
bpf: track subprog poke correctly, fix use-after-free
Subprograms are calling map_poke_track but on program release there is no hook to call map_poke_untrack. But on prog release the aux memory is freed even though we still have a reference to it in the element list of the map aux data. So when we run map_poke_run() we end up accessing free'd memory. This triggers with KASAN in prog_array_map_poke_run() shown here. [ 402.824686] ================================================================== [ 402.824689] BUG: KASAN: use-after-free in prog_array_map_poke_run+0xc2/0x34e [ 402.824698] Read of size 4 at addr ffff8881905a7940 by task hubble-fgs/4337 [ 402.824705] CPU: 1 PID: 4337 Comm: hubble-fgs Tainted: G I 5.12.0+ #399 [ 402.824715] Call Trace: [ 402.824719] dump_stack+0x93/0xc2 [ 402.824727] print_address_description.constprop.0+0x1a/0x140 [ 402.824736] ? prog_array_map_poke_run+0xc2/0x34e [ 402.824740] ? prog_array_map_poke_run+0xc2/0x34e [ 402.824744] kasan_report.cold+0x7c/0xd8 [ 402.824752] ? prog_array_map_poke_run+0xc2/0x34e [ 402.824757] prog_array_map_poke_run+0xc2/0x34e [ 402.824765] bpf_fd_array_map_update_elem+0x124/0x1a0 The elements concerned are walked like this, for (i = 0; i < elem->aux->size_poke_tab; i++) { poke = &elem->aux->poke_tab[i]; So the access to size_poke_tab is the 4B read, verified by checking offsets in the KASAN dump, [ 402.825004] The buggy address belongs to the object at ffff8881905a7800 which belongs to the cache kmalloc-1k of size 1024 [ 402.825008] The buggy address is located 320 bytes inside of 1024-byte region [ffff8881905a7800, ffff8881905a7c00) With pahol output, struct bpf_prog_aux { ... /* --- cacheline 5 boundary (320 bytes) --- */ u32 size_poke_tab; /* 320 4 */ ... In general subprograms do not manage their own data structures. For example btf func_info, linfo, etc are just pointers to the prog structure. This allows reference counting and cleanup to be done on the main prog. The aux->poke_tab struct however did not follow this logic. The initial fix for above use after free further embedded subprogram tracking of poke data tracking into the subprogram with proper reference counting. However, Daniel and Alexei questioned why we were treating these objects specially. I agree its unnecessary. The fix here removes the per subprogram poke data structure alloc and map tracking and instead simply points the aux->poke_tab pointer at the main programs poke_tab. This way map tracking is done on the origin program and we do not need to manage them per subprogram. A couple small complication arise here. First on bpf_prog_free_deferred(), where we unwind the prog reference counting and kfree objects, we need to ensure that we don't try to double free the poke_tab when free'ing the subprog structures. This is easily solved by NULL'ing the poke_tab pointer. The second detail is to ensure that per subprog jit logic only does fixups on poke_tab[] entries it owns. To do this we add a pointer in the poke structure to point at the subprog value so JITs can easily check while walking the poke_tab structure if the current entry belongs to the current program. This change is necessary per JIT. See x86/net/bpf_jit_compo.c func bpf_tail_call_direct_fixup() for the details. Only x86 is currently using the poke_tab struct so we only need to fixup the x86 JIT. Fixes: a748c69 ("bpf: propagate poke descriptors to subprograms") Signed-off-by: John Fastabend <[email protected]>
1 parent 4f6017f commit 2a9d291

File tree

4 files changed

+18
-33
lines changed

4 files changed

+18
-33
lines changed

arch/x86/net/bpf_jit_comp.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,10 @@ static void bpf_tail_call_direct_fixup(struct bpf_prog *prog)
576576

577577
for (i = 0; i < prog->aux->size_poke_tab; i++) {
578578
poke = &prog->aux->poke_tab[i];
579+
580+
if (poke->aux && poke->aux != prog->aux)
581+
continue;
582+
579583
WARN_ON_ONCE(READ_ONCE(poke->tailcall_target_stable));
580584

581585
if (poke->reason != BPF_POKE_REASON_TAIL_CALL)

include/linux/bpf.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -777,6 +777,7 @@ struct bpf_jit_poke_descriptor {
777777
void *tailcall_target;
778778
void *tailcall_bypass;
779779
void *bypass_addr;
780+
void *aux;
780781
union {
781782
struct {
782783
struct bpf_map *map;

kernel/bpf/core.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2211,8 +2211,13 @@ static void bpf_prog_free_deferred(struct work_struct *work)
22112211
#endif
22122212
if (aux->dst_trampoline)
22132213
bpf_trampoline_put(aux->dst_trampoline);
2214-
for (i = 0; i < aux->func_cnt; i++)
2214+
for (i = 0; i < aux->func_cnt; i++) {
2215+
/* poke_tab in subprogs are links to main prog and are
2216+
* freed above so delete link without kfree.
2217+
*/
2218+
aux->func[i]->aux->poke_tab = NULL;
22152219
bpf_jit_free(aux->func[i]);
2220+
}
22162221
if (aux->func_cnt) {
22172222
kfree(aux->func);
22182223
bpf_prog_unlock_free(aux->prog);

kernel/bpf/verifier.c

Lines changed: 7 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -12109,30 +12109,17 @@ static int jit_subprogs(struct bpf_verifier_env *env)
1210912109
/* the btf and func_info will be freed only at prog->aux */
1211012110
func[i]->aux->btf = prog->aux->btf;
1211112111
func[i]->aux->func_info = prog->aux->func_info;
12112+
func[i]->aux->poke_tab = prog->aux->poke_tab;
12113+
func[i]->aux->size_poke_tab = prog->aux->size_poke_tab;
1211212114

1211312115
for (j = 0; j < prog->aux->size_poke_tab; j++) {
12114-
u32 insn_idx = prog->aux->poke_tab[j].insn_idx;
12115-
int ret;
12116+
struct bpf_jit_poke_descriptor *poke;
1211612117

12117-
if (!(insn_idx >= subprog_start &&
12118-
insn_idx <= subprog_end))
12119-
continue;
12120-
12121-
ret = bpf_jit_add_poke_descriptor(func[i],
12122-
&prog->aux->poke_tab[j]);
12123-
if (ret < 0) {
12124-
verbose(env, "adding tail call poke descriptor failed\n");
12125-
goto out_free;
12126-
}
12118+
poke = &prog->aux->poke_tab[j];
1212712119

12128-
func[i]->insnsi[insn_idx - subprog_start].imm = ret + 1;
12129-
12130-
map_ptr = func[i]->aux->poke_tab[ret].tail_call.map;
12131-
ret = map_ptr->ops->map_poke_track(map_ptr, func[i]->aux);
12132-
if (ret < 0) {
12133-
verbose(env, "tracking tail call prog failed\n");
12134-
goto out_free;
12135-
}
12120+
if (poke->insn_idx < subprog_end &&
12121+
poke->insn_idx >= subprog_start)
12122+
poke->aux = func[i]->aux;
1213612123
}
1213712124

1213812125
/* Use bpf_prog_F_tag to indicate functions in stack traces.
@@ -12163,18 +12150,6 @@ static int jit_subprogs(struct bpf_verifier_env *env)
1216312150
cond_resched();
1216412151
}
1216512152

12166-
/* Untrack main program's aux structs so that during map_poke_run()
12167-
* we will not stumble upon the unfilled poke descriptors; each
12168-
* of the main program's poke descs got distributed across subprogs
12169-
* and got tracked onto map, so we are sure that none of them will
12170-
* be missed after the operation below
12171-
*/
12172-
for (i = 0; i < prog->aux->size_poke_tab; i++) {
12173-
map_ptr = prog->aux->poke_tab[i].tail_call.map;
12174-
12175-
map_ptr->ops->map_poke_untrack(map_ptr, prog->aux);
12176-
}
12177-
1217812153
/* at this point all bpf functions were successfully JITed
1217912154
* now populate all bpf_calls with correct addresses and
1218012155
* run last pass of JIT

0 commit comments

Comments
 (0)