Skip to content

Commit 9802d86

Browse files
Josef BacikAlexei Starovoitov
authored andcommitted
bpf: add a bpf_override_function helper
Error injection is sloppy and very ad-hoc. BPF could fill this niche perfectly with it's kprobe functionality. We could make sure errors are only triggered in specific call chains that we care about with very specific situations. Accomplish this with the bpf_override_funciton helper. This will modify the probe'd callers return value to the specified value and set the PC to an override function that simply returns, bypassing the originally probed function. This gives us a nice clean way to implement systematic error injection for all of our code paths. Acked-by: Alexei Starovoitov <ast@kernel.org> Acked-by: Ingo Molnar <mingo@kernel.org> Signed-off-by: Josef Bacik <jbacik@fb.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org>
1 parent 8556e50 commit 9802d86

15 files changed

Lines changed: 154 additions & 9 deletions

File tree

arch/Kconfig

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,9 @@ config HAVE_OPTPROBES
196196
config HAVE_KPROBES_ON_FTRACE
197197
bool
198198

199+
config HAVE_KPROBE_OVERRIDE
200+
bool
201+
199202
config HAVE_NMI
200203
bool
201204

arch/x86/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ config X86
154154
select HAVE_KERNEL_XZ
155155
select HAVE_KPROBES
156156
select HAVE_KPROBES_ON_FTRACE
157+
select HAVE_KPROBE_OVERRIDE
157158
select HAVE_KRETPROBES
158159
select HAVE_KVM
159160
select HAVE_LIVEPATCH if X86_64

arch/x86/include/asm/kprobes.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@ extern const int kretprobe_blacklist_size;
6767
void arch_remove_kprobe(struct kprobe *p);
6868
asmlinkage void kretprobe_trampoline(void);
6969

70+
#ifdef CONFIG_KPROBES_ON_FTRACE
71+
extern void arch_ftrace_kprobe_override_function(struct pt_regs *regs);
72+
#endif
73+
7074
/* Architecture specific copy of original instruction*/
7175
struct arch_specific_insn {
7276
/* copy of the original instruction */

arch/x86/include/asm/ptrace.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,11 @@ static inline unsigned long regs_return_value(struct pt_regs *regs)
109109
return regs->ax;
110110
}
111111

112+
static inline void regs_set_return_value(struct pt_regs *regs, unsigned long rc)
113+
{
114+
regs->ax = rc;
115+
}
116+
112117
/*
113118
* user_mode(regs) determines whether a register set came from user
114119
* mode. On x86_32, this is true if V8086 mode was enabled OR if the

arch/x86/kernel/kprobes/ftrace.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,3 +97,17 @@ int arch_prepare_kprobe_ftrace(struct kprobe *p)
9797
p->ainsn.boostable = false;
9898
return 0;
9999
}
100+
101+
asmlinkage void override_func(void);
102+
asm(
103+
".type override_func, @function\n"
104+
"override_func:\n"
105+
" ret\n"
106+
".size override_func, .-override_func\n"
107+
);
108+
109+
void arch_ftrace_kprobe_override_function(struct pt_regs *regs)
110+
{
111+
regs->ip = (unsigned long)&override_func;
112+
}
113+
NOKPROBE_SYMBOL(arch_ftrace_kprobe_override_function);

include/linux/filter.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -458,7 +458,8 @@ struct bpf_prog {
458458
locked:1, /* Program image locked? */
459459
gpl_compatible:1, /* Is filter GPL compatible? */
460460
cb_access:1, /* Is control block accessed? */
461-
dst_needed:1; /* Do we need dst entry? */
461+
dst_needed:1, /* Do we need dst entry? */
462+
kprobe_override:1; /* Do we override a kprobe? */
462463
enum bpf_prog_type type; /* Type of BPF program */
463464
u32 len; /* Number of filter blocks */
464465
u32 jited_len; /* Size of jited insns in bytes */

include/linux/trace_events.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,7 @@ do { \
528528
struct perf_event;
529529

530530
DECLARE_PER_CPU(struct pt_regs, perf_trace_regs);
531+
DECLARE_PER_CPU(int, bpf_kprobe_override);
531532

532533
extern int perf_trace_init(struct perf_event *event);
533534
extern void perf_trace_destroy(struct perf_event *event);

include/uapi/linux/bpf.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -677,6 +677,10 @@ union bpf_attr {
677677
* @buf: buf to fill
678678
* @buf_size: size of the buf
679679
* Return : 0 on success or negative error code
680+
*
681+
* int bpf_override_return(pt_regs, rc)
682+
* @pt_regs: pointer to struct pt_regs
683+
* @rc: the return value to set
680684
*/
681685
#define __BPF_FUNC_MAPPER(FN) \
682686
FN(unspec), \
@@ -736,7 +740,8 @@ union bpf_attr {
736740
FN(xdp_adjust_meta), \
737741
FN(perf_event_read_value), \
738742
FN(perf_prog_read_value), \
739-
FN(getsockopt),
743+
FN(getsockopt), \
744+
FN(override_return),
740745

741746
/* integer value in 'imm' field of BPF_CALL instruction selects which helper
742747
* function eBPF program intends to call

kernel/bpf/core.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1320,6 +1320,9 @@ EVAL4(PROG_NAME_LIST, 416, 448, 480, 512)
13201320
bool bpf_prog_array_compatible(struct bpf_array *array,
13211321
const struct bpf_prog *fp)
13221322
{
1323+
if (fp->kprobe_override)
1324+
return false;
1325+
13231326
if (!array->owner_prog_type) {
13241327
/* There's no owner yet where we could check for
13251328
* compatibility.

kernel/bpf/verifier.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4413,6 +4413,8 @@ static int fixup_bpf_calls(struct bpf_verifier_env *env)
44134413
prog->dst_needed = 1;
44144414
if (insn->imm == BPF_FUNC_get_prandom_u32)
44154415
bpf_user_rnd_init_once();
4416+
if (insn->imm == BPF_FUNC_override_return)
4417+
prog->kprobe_override = 1;
44164418
if (insn->imm == BPF_FUNC_tail_call) {
44174419
/* If we tail call into other programs, we
44184420
* cannot make any assumptions since they can

0 commit comments

Comments
 (0)