Skip to content

Commit feac784

Browse files
Tao ChenKernel Patches Daemon
authored andcommitted
bpf: Hold the perf callchain entry until used completely
As Alexei noted, get_perf_callchain() return values may be reused if a task is preempted after the BPF program enters migrate disable mode. The perf_callchain_entres has a small stack of entries, and we can reuse it as follows: 1. get the perf callchain entry 2. BPF use... 3. put the perf callchain entry Signed-off-by: Tao Chen <[email protected]>
1 parent 43580de commit feac784

File tree

1 file changed

+50
-12
lines changed

1 file changed

+50
-12
lines changed

kernel/bpf/stackmap.c

Lines changed: 50 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -210,13 +210,12 @@ static void stack_map_get_build_id_offset(struct bpf_stack_build_id *id_offs,
210210
}
211211

212212
static struct perf_callchain_entry *
213-
get_callchain_entry_for_task(struct task_struct *task, u32 max_depth)
213+
get_callchain_entry_for_task(int *rctx, struct task_struct *task, u32 max_depth)
214214
{
215215
#ifdef CONFIG_STACKTRACE
216216
struct perf_callchain_entry *entry;
217-
int rctx;
218217

219-
entry = get_callchain_entry(&rctx);
218+
entry = get_callchain_entry(rctx);
220219

221220
if (!entry)
222221
return NULL;
@@ -238,8 +237,6 @@ get_callchain_entry_for_task(struct task_struct *task, u32 max_depth)
238237
to[i] = (u64)(from[i]);
239238
}
240239

241-
put_callchain_entry(rctx);
242-
243240
return entry;
244241
#else /* CONFIG_STACKTRACE */
245242
return NULL;
@@ -320,6 +317,31 @@ static long __bpf_get_stackid(struct bpf_map *map,
320317
return id;
321318
}
322319

320+
static struct perf_callchain_entry *
321+
bpf_get_perf_callchain(int *rctx, struct pt_regs *regs, bool kernel, bool user,
322+
int max_stack, bool crosstask)
323+
{
324+
struct perf_callchain_entry_ctx ctx;
325+
struct perf_callchain_entry *entry;
326+
327+
entry = get_callchain_entry(rctx);
328+
if (unlikely(!entry))
329+
return NULL;
330+
331+
__init_perf_callchain_ctx(&ctx, entry, max_stack, false);
332+
if (kernel)
333+
__get_perf_callchain_kernel(&ctx, regs);
334+
if (user && !crosstask)
335+
__get_perf_callchain_user(&ctx, regs);
336+
337+
return entry;
338+
}
339+
340+
static void bpf_put_perf_callchain(int rctx)
341+
{
342+
put_callchain_entry(rctx);
343+
}
344+
323345
BPF_CALL_3(bpf_get_stackid, struct pt_regs *, regs, struct bpf_map *, map,
324346
u64, flags)
325347
{
@@ -328,20 +350,24 @@ BPF_CALL_3(bpf_get_stackid, struct pt_regs *, regs, struct bpf_map *, map,
328350
struct perf_callchain_entry *trace;
329351
bool kernel = !user;
330352
u32 max_depth;
353+
int rctx, ret;
331354

332355
if (unlikely(flags & ~(BPF_F_SKIP_FIELD_MASK | BPF_F_USER_STACK |
333356
BPF_F_FAST_STACK_CMP | BPF_F_REUSE_STACKID)))
334357
return -EINVAL;
335358

336359
max_depth = stack_map_calculate_max_depth(map->value_size, elem_size, flags);
337-
trace = get_perf_callchain(regs, kernel, user, max_depth,
338-
false, false);
360+
trace = bpf_get_perf_callchain(&rctx, regs, kernel, user, max_depth,
361+
false);
339362

340363
if (unlikely(!trace))
341364
/* couldn't fetch the stack trace */
342365
return -EFAULT;
343366

344-
return __bpf_get_stackid(map, trace, flags);
367+
ret = __bpf_get_stackid(map, trace, flags);
368+
bpf_put_perf_callchain(rctx);
369+
370+
return ret;
345371
}
346372

347373
const struct bpf_func_proto bpf_get_stackid_proto = {
@@ -435,6 +461,7 @@ static long __bpf_get_stack(struct pt_regs *regs, struct task_struct *task,
435461
bool kernel = !user;
436462
int err = -EINVAL;
437463
u64 *ips;
464+
int rctx;
438465

439466
if (unlikely(flags & ~(BPF_F_SKIP_FIELD_MASK | BPF_F_USER_STACK |
440467
BPF_F_USER_BUILD_ID)))
@@ -467,18 +494,26 @@ static long __bpf_get_stack(struct pt_regs *regs, struct task_struct *task,
467494
trace = trace_in;
468495
trace->nr = min_t(u32, trace->nr, max_depth);
469496
} else if (kernel && task) {
470-
trace = get_callchain_entry_for_task(task, max_depth);
497+
trace = get_callchain_entry_for_task(&rctx, task, max_depth);
471498
} else {
472-
trace = get_perf_callchain(regs, kernel, user, max_depth,
473-
crosstask, false);
499+
trace = bpf_get_perf_callchain(&rctx, regs, kernel, user, max_depth,
500+
crosstask);
474501
}
475502

476-
if (unlikely(!trace) || trace->nr < skip) {
503+
if (unlikely(!trace)) {
477504
if (may_fault)
478505
rcu_read_unlock();
479506
goto err_fault;
480507
}
481508

509+
if (trace->nr < skip) {
510+
if (may_fault)
511+
rcu_read_unlock();
512+
if (!trace_in)
513+
bpf_put_perf_callchain(rctx);
514+
goto err_fault;
515+
}
516+
482517
trace_nr = trace->nr - skip;
483518
copy_len = trace_nr * elem_size;
484519

@@ -497,6 +532,9 @@ static long __bpf_get_stack(struct pt_regs *regs, struct task_struct *task,
497532
if (may_fault)
498533
rcu_read_unlock();
499534

535+
if (!trace_in)
536+
bpf_put_perf_callchain(rctx);
537+
500538
if (user_build_id)
501539
stack_map_get_build_id_offset(buf, trace_nr, user, may_fault);
502540

0 commit comments

Comments
 (0)