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

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'bpf-fix-abuse-of-kprobe_write_ctx-via-freplace'

Leon Hwang says:

====================
bpf: Fix abuse of kprobe_write_ctx via freplace

The potential issue of kprobe_write_ctx+freplace was mentioned in
"bpf: Disallow !kprobe_write_ctx progs tail-calling kprobe_write_ctx progs" [1].

It is true issue, that the test in patch #2 verifies that kprobe_write_ctx=false
kprobe progs can be abused to modify struct pt_regs via kprobe_write_ctx=true
freplace progs.

When struct pt_regs is modified, bpf_prog_test_run_opts() gets -EFAULT instead
of 0.

test_freplace_kprobe_write_ctx:FAIL:bpf_prog_test_run_opts unexpected error: -14 (errno 14)

We will disallow attaching freplace programs on kprobe programs with different
kprobe_write_ctx values.

Links:
[1] https://lore.kernel.org/bpf/CAP01T74w4KVMn9bEwpQXrk+bqcUxzb6VW1SQ_QvNy0A4EY-9Jg@mail.gmail.com/

Changes:
v2 -> v3:
* Add comment to the rejection of kprobe_write_ctx (per Jiri).
* Use libbpf_get_error() instead of errno in test (per Jiri).
* Collect Acked-by tags from Jiri and Song, thanks.
v2: https://lore.kernel.org/bpf/20260326141718.17731-1-leon.hwang@linux.dev/

v1 -> v2:
* Drop patch #1 in v1, as it wasn't an issue (per Toke).
* Check kprobe_write_ctx value at attach time instead of at load time, to
prevent attaching kprobe_write_ctx=true freplace progs on
kprobe_write_ctx=false kprobe progs (per Gemini/sashiko).
* Move kprobe_write_ctx test code to attach_probe.c and kprobe_write_ctx.c.
v1: https://lore.kernel.org/bpf/20260324150444.68166-1-leon.hwang@linux.dev/
====================

Link: https://patch.msgid.link/20260331145353.87606-1-leon.hwang@linux.dev
Signed-off-by: Alexei Starovoitov <ast@kernel.org>

+100
+17
kernel/bpf/syscall.c
··· 3733 3733 tr = prog->aux->dst_trampoline; 3734 3734 tgt_prog = prog->aux->dst_prog; 3735 3735 } 3736 + /* 3737 + * It is to prevent modifying struct pt_regs via kprobe_write_ctx=true 3738 + * freplace prog. Without this check, kprobe_write_ctx=true freplace 3739 + * prog is allowed to attach to kprobe_write_ctx=false kprobe prog, and 3740 + * then modify the registers of the kprobe prog's target kernel 3741 + * function. 3742 + * 3743 + * This also blocks the combination of uprobe+freplace, because it is 3744 + * unable to recognize the use of the tgt_prog as an uprobe or a kprobe 3745 + * by tgt_prog itself. At attach time, uprobe/kprobe is recognized by 3746 + * the target perf event flags in __perf_event_set_bpf_prog(). 3747 + */ 3748 + if (prog->type == BPF_PROG_TYPE_EXT && 3749 + prog->aux->kprobe_write_ctx != tgt_prog->aux->kprobe_write_ctx) { 3750 + err = -EINVAL; 3751 + goto out_unlock; 3752 + } 3736 3753 3737 3754 err = bpf_link_prime(&link->link.link, &link_primer); 3738 3755 if (err)
+64
tools/testing/selftests/bpf/prog_tests/attach_probe.c
··· 220 220 221 221 kprobe_write_ctx__destroy(skel); 222 222 } 223 + 224 + static void test_freplace_kprobe_write_ctx(void) 225 + { 226 + struct bpf_program *prog_kprobe, *prog_ext, *prog_fentry; 227 + struct kprobe_write_ctx *skel_kprobe, *skel_ext = NULL; 228 + struct bpf_link *link_kprobe = NULL, *link_ext = NULL; 229 + int err, prog_fd; 230 + LIBBPF_OPTS(bpf_kprobe_opts, kprobe_opts); 231 + LIBBPF_OPTS(bpf_test_run_opts, topts); 232 + 233 + skel_kprobe = kprobe_write_ctx__open(); 234 + if (!ASSERT_OK_PTR(skel_kprobe, "kprobe_write_ctx__open kprobe")) 235 + return; 236 + 237 + prog_kprobe = skel_kprobe->progs.kprobe_dummy; 238 + bpf_program__set_autoload(prog_kprobe, true); 239 + 240 + prog_fentry = skel_kprobe->progs.fentry; 241 + bpf_program__set_autoload(prog_fentry, true); 242 + 243 + err = kprobe_write_ctx__load(skel_kprobe); 244 + if (!ASSERT_OK(err, "kprobe_write_ctx__load kprobe")) 245 + goto out; 246 + 247 + skel_ext = kprobe_write_ctx__open(); 248 + if (!ASSERT_OK_PTR(skel_ext, "kprobe_write_ctx__open ext")) 249 + goto out; 250 + 251 + prog_ext = skel_ext->progs.freplace_kprobe; 252 + bpf_program__set_autoload(prog_ext, true); 253 + 254 + prog_fd = bpf_program__fd(skel_kprobe->progs.kprobe_write_ctx); 255 + bpf_program__set_attach_target(prog_ext, prog_fd, "kprobe_write_ctx"); 256 + 257 + err = kprobe_write_ctx__load(skel_ext); 258 + if (!ASSERT_OK(err, "kprobe_write_ctx__load ext")) 259 + goto out; 260 + 261 + prog_fd = bpf_program__fd(prog_kprobe); 262 + link_ext = bpf_program__attach_freplace(prog_ext, prog_fd, "kprobe_dummy"); 263 + ASSERT_ERR_PTR(link_ext, "bpf_program__attach_freplace link"); 264 + ASSERT_EQ(libbpf_get_error(link_ext), -EINVAL, "bpf_program__attach_freplace error"); 265 + 266 + link_kprobe = bpf_program__attach_kprobe_opts(prog_kprobe, "bpf_fentry_test1", 267 + &kprobe_opts); 268 + if (!ASSERT_OK_PTR(link_kprobe, "bpf_program__attach_kprobe_opts")) 269 + goto out; 270 + 271 + err = bpf_prog_test_run_opts(bpf_program__fd(prog_fentry), &topts); 272 + ASSERT_OK(err, "bpf_prog_test_run_opts"); 273 + 274 + out: 275 + bpf_link__destroy(link_ext); 276 + bpf_link__destroy(link_kprobe); 277 + kprobe_write_ctx__destroy(skel_ext); 278 + kprobe_write_ctx__destroy(skel_kprobe); 279 + } 223 280 #else 224 281 static void test_attach_kprobe_write_ctx(void) 282 + { 283 + test__skip(); 284 + } 285 + 286 + static void test_freplace_kprobe_write_ctx(void) 225 287 { 226 288 test__skip(); 227 289 } ··· 496 434 test_attach_kprobe_long_event_name(); 497 435 if (test__start_subtest("kprobe-write-ctx")) 498 436 test_attach_kprobe_write_ctx(); 437 + if (test__start_subtest("freplace-kprobe-write-ctx")) 438 + test_freplace_kprobe_write_ctx(); 499 439 500 440 cleanup: 501 441 test_attach_probe__destroy(skel);
+19
tools/testing/selftests/bpf/progs/kprobe_write_ctx.c
··· 19 19 ctx->ax = 0; 20 20 return 0; 21 21 } 22 + 23 + SEC("?kprobe") 24 + int kprobe_dummy(struct pt_regs *regs) 25 + { 26 + return 0; 27 + } 28 + 29 + SEC("?freplace") 30 + int freplace_kprobe(struct pt_regs *regs) 31 + { 32 + regs->di = 0; 33 + return 0; 34 + } 35 + 36 + SEC("?fentry/bpf_fentry_test1") 37 + int BPF_PROG(fentry) 38 + { 39 + return 0; 40 + } 22 41 #endif