diff --git a/arch/x86/lib/error-inject.c b/arch/x86/lib/error-inject.c index be5b5fb1598bd81cbfa9fd3b05944a7804e22ee9..d257c17d417d2a9d66e917c5eb4d4c8a0b9904df 100644 --- a/arch/x86/lib/error-inject.c +++ b/arch/x86/lib/error-inject.c @@ -19,3 +19,16 @@ void override_function_with_return(struct pt_regs *regs) regs->ip = (unsigned long)&just_return_func; } NOKPROBE_SYMBOL(override_function_with_return); + +int regs_set_register(struct pt_regs *regs, const char *regs_name, + unsigned long value) +{ + int offset = 0; + offset = regs_query_register_offset(regs_name) >> 3; + if (offset >= 0) { + *((unsigned long *)((unsigned long *)regs + offset)) = value; + return 0; + } + return -1; +} +NOKPROBE_SYMBOL(regs_set_register); \ No newline at end of file diff --git a/include/asm-generic/error-injection.h b/include/asm-generic/error-injection.h index 7ddd9dc10ce9666f5c07f687c0c075cc81860751..7acb61c130f65caea64bbb57e596773f651f0472 100644 --- a/include/asm-generic/error-injection.h +++ b/include/asm-generic/error-injection.h @@ -32,6 +32,8 @@ static struct error_injection_entry __used \ }; void override_function_with_return(struct pt_regs *regs); +int regs_set_register(struct pt_regs *regs, const char *regs_name, + unsigned long value); #else #define ALLOW_ERROR_INJECTION(fname, _etype) diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index db585d960d64cb4ffe486b723e1811d645994ddd..15643c5972f8a3a7a34fcb31bb5141971269a0db 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -4070,6 +4070,24 @@ union bpf_attr { * *offset* * Return * 0 on success, or a negative error in case of failure. + * + * long bpf_override_reg(struct pt_regs *regs, const char* regs_name, u64 value) + * Description + * Also used for error injection, unlike bpf_override_return, this helper + * uses kprobes to overwrite the probed function's register and set it to + * *rc*. Instead of just modifying the return value, the first argument + * is the context *regs* where kprobe works,and the second parameter is + * the offset of the register. + * + * And the helper doesn't modify the PC (Program Counter). This means that + * the probed function will run and replace the function's register values. + * + * On the other hand, to avoid security implications, it is likewise only + * available when compiling the kernel with the **CONFIG_BPF_KPROBE_OVERRIDE** + * configuration option, and in this case it only applies to functions marked + * with **ALLOW_ERROR_INJECTION** in the kernel code. + * Return + * 0 on success, or a negative error in case of failure. */ #define __BPF_FUNC_MAPPER(FN) \ FN(unspec), \ @@ -4258,6 +4276,7 @@ union bpf_attr { FN(update_tcp_seq), \ FN(xdp_store_bytes), \ FN(xdp_load_bytes), \ + FN(override_reg), \ /* */ /* integer value in 'imm' field of BPF_CALL instruction selects which helper diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 82b7336ddc4521461fb048e3516265848f276255..0dbdd0538e6daecf21eb446497b48acf803521d8 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -12595,7 +12595,8 @@ static int do_misc_fixups(struct bpf_verifier_env *env) prog->dst_needed = 1; if (insn->imm == BPF_FUNC_get_prandom_u32) bpf_user_rnd_init_once(); - if (insn->imm == BPF_FUNC_override_return) + if (insn->imm == BPF_FUNC_override_return || + insn->imm == BPF_FUNC_override_reg) prog->kprobe_override = 1; if (insn->imm == BPF_FUNC_tail_call) { /* If we tail call into other programs, we diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c index c08a504573a3f91e5261b1e375e09e9dc56f1a82..832efbad42bd0b720144f657449203aed268eb26 100644 --- a/kernel/trace/bpf_trace.c +++ b/kernel/trace/bpf_trace.c @@ -144,6 +144,28 @@ static const struct bpf_func_proto bpf_override_return_proto = { .arg1_type = ARG_PTR_TO_CTX, .arg2_type = ARG_ANYTHING, }; + +BPF_CALL_3(bpf_override_regs, struct pt_regs *, regs, const char *, regs_name, + unsigned long, value) +{ + int ret = 0; + + #ifdef CONFIG_X86_64 + ret = regs_set_register(regs, regs_name, value); + if (unlikely(ret < 0)) + return ret; + #endif + return 0; +} + +static const struct bpf_func_proto bpf_override_reg_proto = { + .func = bpf_override_reg, + .gpl_only = true, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_CTX, + .arg2_type = ARG_ANYTHING, + .arg3_type = ARG_ANYTHING, +}; #endif static __always_inline int @@ -1056,6 +1078,8 @@ kprobe_prog_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) #ifdef CONFIG_BPF_KPROBE_OVERRIDE case BPF_FUNC_override_return: return &bpf_override_return_proto; + case BPF_FUNC_override_reg: + return &bpf_override_reg_proto; #endif default: return bpf_tracing_func_proto(func_id, prog); diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index 57b927e99092cbf3cd7dac8d470c4c25302891e4..f1dd4cc11b729913757b70d3515652651d75372d 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -4780,6 +4780,25 @@ union bpf_attr { * *offset* * Return * 0 on success, or a negative error in case of failure. + * + * long bpf_override_reg(struct pt_regs *regs, const char* regs_name, u64 value) + * Description + * Also used for error injection, unlike bpf_override_return, this helper + * uses kprobes to overwrite the probed function's register and set it to + * *rc*. Instead of just modifying the return value, the first argument + * is the context *regs* where kprobe works,and the second parameter is + * the offset of the register. + * + * And the helper doesn't modify the PC (Program Counter). This means that + * the probed function will run and replace the function's register values. + * + * On the other hand, to avoid security implications, it is likewise only + * available when compiling the kernel with the **CONFIG_BPF_KPROBE_OVERRIDE** + * configuration option, and in this case it only applies to functions marked + * with **ALLOW_ERROR_INJECTION** in the kernel code. + * + * Return + * 0 on success, or a negative error in case of failure. */ #define __BPF_FUNC_MAPPER(FN) \ FN(unspec), \ @@ -4968,6 +4987,7 @@ union bpf_attr { FN(update_tcp_seq), \ FN(xdp_store_bytes), \ FN(xdp_load_bytes), \ + FN(override_reg), \ /* */ /* integer value in 'imm' field of BPF_CALL instruction selects which helper