Skip to content

Commit ceeaa71

Browse files
author
Alexei Starovoitov
committed
Merge branch 'uprobe-bpf-allow-to-change-app-registers-from-uprobe-registers'
Jiri Olsa says: ==================== we recently had several requests for tetragon to be able to change user application function return value or divert its execution through instruction pointer change. This patchset adds support for uprobe program to change app's registers including instruction pointer. v4 changes: - rebased on bpf-next/master, we will handle the future simple conflict with tip/perf/core - changed condition in kprobe_prog_is_valid_access [Andrii] - added acks ==================== Link: https://patch.msgid.link/[email protected] Signed-off-by: Alexei Starovoitov <[email protected]>
2 parents 34f033a + 3d23746 commit ceeaa71

File tree

9 files changed

+289
-3
lines changed

9 files changed

+289
-3
lines changed

include/linux/bpf.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1639,6 +1639,7 @@ struct bpf_prog_aux {
16391639
bool priv_stack_requested;
16401640
bool changes_pkt_data;
16411641
bool might_sleep;
1642+
bool kprobe_write_ctx;
16421643
u64 prog_array_member_cnt; /* counts how many times as member of prog_array */
16431644
struct mutex ext_mutex; /* mutex for is_extended and prog_array_member_cnt */
16441645
struct bpf_arena *arena;

kernel/events/core.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11232,6 +11232,10 @@ static int __perf_event_set_bpf_prog(struct perf_event *event,
1123211232
if (prog->kprobe_override && !is_kprobe)
1123311233
return -EINVAL;
1123411234

11235+
/* Writing to context allowed only for uprobes. */
11236+
if (prog->aux->kprobe_write_ctx && !is_uprobe)
11237+
return -EINVAL;
11238+
1123511239
if (is_tracepoint || is_syscall_tp) {
1123611240
int off = trace_event_get_offsets(event->tp_event);
1123711241

kernel/events/uprobes.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2741,6 +2741,13 @@ static void handle_swbp(struct pt_regs *regs)
27412741

27422742
handler_chain(uprobe, regs);
27432743

2744+
/*
2745+
* If user decided to take execution elsewhere, it makes little sense
2746+
* to execute the original instruction, so let's skip it.
2747+
*/
2748+
if (instruction_pointer(regs) != bp_vaddr)
2749+
goto out;
2750+
27442751
if (arch_uprobe_skip_sstep(&uprobe->arch, regs))
27452752
goto out;
27462753

kernel/trace/bpf_trace.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1338,8 +1338,6 @@ static bool kprobe_prog_is_valid_access(int off, int size, enum bpf_access_type
13381338
{
13391339
if (off < 0 || off >= sizeof(struct pt_regs))
13401340
return false;
1341-
if (type != BPF_READ)
1342-
return false;
13431341
if (off % size != 0)
13441342
return false;
13451343
/*
@@ -1349,6 +1347,9 @@ static bool kprobe_prog_is_valid_access(int off, int size, enum bpf_access_type
13491347
if (off + size > sizeof(struct pt_regs))
13501348
return false;
13511349

1350+
if (type == BPF_WRITE)
1351+
prog->aux->kprobe_write_ctx = true;
1352+
13521353
return true;
13531354
}
13541355

@@ -2735,6 +2736,10 @@ int bpf_kprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *pr
27352736
if (!is_kprobe_multi(prog))
27362737
return -EINVAL;
27372738

2739+
/* Writing to context is not allowed for kprobes. */
2740+
if (prog->aux->kprobe_write_ctx)
2741+
return -EINVAL;
2742+
27382743
flags = attr->link_create.kprobe_multi.flags;
27392744
if (flags & ~BPF_F_KPROBE_MULTI_RETURN)
27402745
return -EINVAL;

tools/testing/selftests/bpf/prog_tests/attach_probe.c

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "test_attach_kprobe_sleepable.skel.h"
44
#include "test_attach_probe_manual.skel.h"
55
#include "test_attach_probe.skel.h"
6+
#include "kprobe_write_ctx.skel.h"
67

78
/* this is how USDT semaphore is actually defined, except volatile modifier */
89
volatile unsigned short uprobe_ref_ctr __attribute__((unused)) __attribute((section(".probes")));
@@ -201,6 +202,31 @@ static void test_attach_kprobe_long_event_name(void)
201202
test_attach_probe_manual__destroy(skel);
202203
}
203204

205+
#ifdef __x86_64__
206+
/* attach kprobe/kretprobe long event name testings */
207+
static void test_attach_kprobe_write_ctx(void)
208+
{
209+
struct kprobe_write_ctx *skel = NULL;
210+
struct bpf_link *link = NULL;
211+
212+
skel = kprobe_write_ctx__open_and_load();
213+
if (!ASSERT_OK_PTR(skel, "kprobe_write_ctx__open_and_load"))
214+
return;
215+
216+
link = bpf_program__attach_kprobe_opts(skel->progs.kprobe_write_ctx,
217+
"bpf_fentry_test1", NULL);
218+
if (!ASSERT_ERR_PTR(link, "bpf_program__attach_kprobe_opts"))
219+
bpf_link__destroy(link);
220+
221+
kprobe_write_ctx__destroy(skel);
222+
}
223+
#else
224+
static void test_attach_kprobe_write_ctx(void)
225+
{
226+
test__skip();
227+
}
228+
#endif
229+
204230
static void test_attach_probe_auto(struct test_attach_probe *skel)
205231
{
206232
struct bpf_link *uprobe_err_link;
@@ -406,6 +432,8 @@ void test_attach_probe(void)
406432
test_attach_uprobe_long_event_name();
407433
if (test__start_subtest("kprobe-long_name"))
408434
test_attach_kprobe_long_event_name();
435+
if (test__start_subtest("kprobe-write-ctx"))
436+
test_attach_kprobe_write_ctx();
409437

410438
cleanup:
411439
test_attach_probe__destroy(skel);

tools/testing/selftests/bpf/prog_tests/kprobe_multi_test.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "kprobe_multi_session.skel.h"
88
#include "kprobe_multi_session_cookie.skel.h"
99
#include "kprobe_multi_verifier.skel.h"
10+
#include "kprobe_write_ctx.skel.h"
1011
#include "bpf/libbpf_internal.h"
1112
#include "bpf/hashmap.h"
1213

@@ -539,6 +540,30 @@ static void test_attach_override(void)
539540
kprobe_multi_override__destroy(skel);
540541
}
541542

543+
#ifdef __x86_64__
544+
static void test_attach_write_ctx(void)
545+
{
546+
struct kprobe_write_ctx *skel = NULL;
547+
struct bpf_link *link = NULL;
548+
549+
skel = kprobe_write_ctx__open_and_load();
550+
if (!ASSERT_OK_PTR(skel, "kprobe_write_ctx__open_and_load"))
551+
return;
552+
553+
link = bpf_program__attach_kprobe_opts(skel->progs.kprobe_multi_write_ctx,
554+
"bpf_fentry_test1", NULL);
555+
if (!ASSERT_ERR_PTR(link, "bpf_program__attach_kprobe_opts"))
556+
bpf_link__destroy(link);
557+
558+
kprobe_write_ctx__destroy(skel);
559+
}
560+
#else
561+
static void test_attach_write_ctx(void)
562+
{
563+
test__skip();
564+
}
565+
#endif
566+
542567
void serial_test_kprobe_multi_bench_attach(void)
543568
{
544569
if (test__start_subtest("kernel"))
@@ -578,5 +603,7 @@ void test_kprobe_multi_test(void)
578603
test_session_cookie_skel_api();
579604
if (test__start_subtest("unique_match"))
580605
test_unique_match();
606+
if (test__start_subtest("attach_write_ctx"))
607+
test_attach_write_ctx();
581608
RUN_TESTS(kprobe_multi_verifier);
582609
}

tools/testing/selftests/bpf/prog_tests/uprobe.c

Lines changed: 155 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
/* Copyright (c) 2023 Hengqi Chen */
33

44
#include <test_progs.h>
5+
#include <asm/ptrace.h>
56
#include "test_uprobe.skel.h"
67

78
static FILE *urand_spawn(int *pid)
@@ -33,7 +34,7 @@ static int urand_trigger(FILE **urand_pipe)
3334
return exit_code;
3435
}
3536

36-
void test_uprobe(void)
37+
static void test_uprobe_attach(void)
3738
{
3839
LIBBPF_OPTS(bpf_uprobe_opts, uprobe_opts);
3940
struct test_uprobe *skel;
@@ -93,3 +94,156 @@ void test_uprobe(void)
9394
pclose(urand_pipe);
9495
test_uprobe__destroy(skel);
9596
}
97+
98+
#ifdef __x86_64__
99+
__naked __maybe_unused unsigned long uprobe_regs_change_trigger(void)
100+
{
101+
asm volatile (
102+
"ret\n"
103+
);
104+
}
105+
106+
static __naked void uprobe_regs_change(struct pt_regs *before, struct pt_regs *after)
107+
{
108+
asm volatile (
109+
"movq %r11, 48(%rdi)\n"
110+
"movq %r10, 56(%rdi)\n"
111+
"movq %r9, 64(%rdi)\n"
112+
"movq %r8, 72(%rdi)\n"
113+
"movq %rax, 80(%rdi)\n"
114+
"movq %rcx, 88(%rdi)\n"
115+
"movq %rdx, 96(%rdi)\n"
116+
"movq %rsi, 104(%rdi)\n"
117+
"movq %rdi, 112(%rdi)\n"
118+
119+
/* save 2nd argument */
120+
"pushq %rsi\n"
121+
"call uprobe_regs_change_trigger\n"
122+
123+
/* save return value and load 2nd argument pointer to rax */
124+
"pushq %rax\n"
125+
"movq 8(%rsp), %rax\n"
126+
127+
"movq %r11, 48(%rax)\n"
128+
"movq %r10, 56(%rax)\n"
129+
"movq %r9, 64(%rax)\n"
130+
"movq %r8, 72(%rax)\n"
131+
"movq %rcx, 88(%rax)\n"
132+
"movq %rdx, 96(%rax)\n"
133+
"movq %rsi, 104(%rax)\n"
134+
"movq %rdi, 112(%rax)\n"
135+
136+
/* restore return value and 2nd argument */
137+
"pop %rax\n"
138+
"pop %rsi\n"
139+
140+
"movq %rax, 80(%rsi)\n"
141+
"ret\n"
142+
);
143+
}
144+
145+
static void regs_common(void)
146+
{
147+
struct pt_regs before = {}, after = {}, expected = {
148+
.rax = 0xc0ffe,
149+
.rcx = 0xbad,
150+
.rdx = 0xdead,
151+
.r8 = 0x8,
152+
.r9 = 0x9,
153+
.r10 = 0x10,
154+
.r11 = 0x11,
155+
.rdi = 0x12,
156+
.rsi = 0x13,
157+
};
158+
LIBBPF_OPTS(bpf_uprobe_opts, uprobe_opts);
159+
struct test_uprobe *skel;
160+
161+
skel = test_uprobe__open_and_load();
162+
if (!ASSERT_OK_PTR(skel, "skel_open"))
163+
return;
164+
165+
skel->bss->my_pid = getpid();
166+
skel->bss->regs = expected;
167+
168+
uprobe_opts.func_name = "uprobe_regs_change_trigger";
169+
skel->links.test_regs_change = bpf_program__attach_uprobe_opts(skel->progs.test_regs_change,
170+
-1,
171+
"/proc/self/exe",
172+
0 /* offset */,
173+
&uprobe_opts);
174+
if (!ASSERT_OK_PTR(skel->links.test_regs_change, "bpf_program__attach_uprobe_opts"))
175+
goto cleanup;
176+
177+
uprobe_regs_change(&before, &after);
178+
179+
ASSERT_EQ(after.rax, expected.rax, "ax");
180+
ASSERT_EQ(after.rcx, expected.rcx, "cx");
181+
ASSERT_EQ(after.rdx, expected.rdx, "dx");
182+
ASSERT_EQ(after.r8, expected.r8, "r8");
183+
ASSERT_EQ(after.r9, expected.r9, "r9");
184+
ASSERT_EQ(after.r10, expected.r10, "r10");
185+
ASSERT_EQ(after.r11, expected.r11, "r11");
186+
ASSERT_EQ(after.rdi, expected.rdi, "rdi");
187+
ASSERT_EQ(after.rsi, expected.rsi, "rsi");
188+
189+
cleanup:
190+
test_uprobe__destroy(skel);
191+
}
192+
193+
static noinline unsigned long uprobe_regs_change_ip_1(void)
194+
{
195+
return 0xc0ffee;
196+
}
197+
198+
static noinline unsigned long uprobe_regs_change_ip_2(void)
199+
{
200+
return 0xdeadbeef;
201+
}
202+
203+
static void regs_ip(void)
204+
{
205+
LIBBPF_OPTS(bpf_uprobe_opts, uprobe_opts);
206+
struct test_uprobe *skel;
207+
unsigned long ret;
208+
209+
skel = test_uprobe__open_and_load();
210+
if (!ASSERT_OK_PTR(skel, "skel_open"))
211+
return;
212+
213+
skel->bss->my_pid = getpid();
214+
skel->bss->ip = (unsigned long) uprobe_regs_change_ip_2;
215+
216+
uprobe_opts.func_name = "uprobe_regs_change_ip_1";
217+
skel->links.test_regs_change_ip = bpf_program__attach_uprobe_opts(
218+
skel->progs.test_regs_change_ip,
219+
-1,
220+
"/proc/self/exe",
221+
0 /* offset */,
222+
&uprobe_opts);
223+
if (!ASSERT_OK_PTR(skel->links.test_regs_change_ip, "bpf_program__attach_uprobe_opts"))
224+
goto cleanup;
225+
226+
ret = uprobe_regs_change_ip_1();
227+
ASSERT_EQ(ret, 0xdeadbeef, "ret");
228+
229+
cleanup:
230+
test_uprobe__destroy(skel);
231+
}
232+
233+
static void test_uprobe_regs_change(void)
234+
{
235+
if (test__start_subtest("regs_change_common"))
236+
regs_common();
237+
if (test__start_subtest("regs_change_ip"))
238+
regs_ip();
239+
}
240+
#else
241+
static void test_uprobe_regs_change(void) { }
242+
#endif
243+
244+
void test_uprobe(void)
245+
{
246+
if (test__start_subtest("attach"))
247+
test_uprobe_attach();
248+
test_uprobe_regs_change();
249+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
#include "vmlinux.h"
3+
#include <bpf/bpf_helpers.h>
4+
#include <bpf/bpf_tracing.h>
5+
6+
char _license[] SEC("license") = "GPL";
7+
8+
#if defined(__TARGET_ARCH_x86)
9+
SEC("kprobe")
10+
int kprobe_write_ctx(struct pt_regs *ctx)
11+
{
12+
ctx->ax = 0;
13+
return 0;
14+
}
15+
16+
SEC("kprobe.multi")
17+
int kprobe_multi_write_ctx(struct pt_regs *ctx)
18+
{
19+
ctx->ax = 0;
20+
return 0;
21+
}
22+
#endif

tools/testing/selftests/bpf/progs/test_uprobe.c

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,41 @@ int BPF_UPROBE(test4)
5959
test4_result = 1;
6060
return 0;
6161
}
62+
63+
#if defined(__TARGET_ARCH_x86)
64+
struct pt_regs regs;
65+
66+
SEC("uprobe")
67+
int BPF_UPROBE(test_regs_change)
68+
{
69+
pid_t pid = bpf_get_current_pid_tgid() >> 32;
70+
71+
if (pid != my_pid)
72+
return 0;
73+
74+
ctx->ax = regs.ax;
75+
ctx->cx = regs.cx;
76+
ctx->dx = regs.dx;
77+
ctx->r8 = regs.r8;
78+
ctx->r9 = regs.r9;
79+
ctx->r10 = regs.r10;
80+
ctx->r11 = regs.r11;
81+
ctx->di = regs.di;
82+
ctx->si = regs.si;
83+
return 0;
84+
}
85+
86+
unsigned long ip;
87+
88+
SEC("uprobe")
89+
int BPF_UPROBE(test_regs_change_ip)
90+
{
91+
pid_t pid = bpf_get_current_pid_tgid() >> 32;
92+
93+
if (pid != my_pid)
94+
return 0;
95+
96+
ctx->ip = ip;
97+
return 0;
98+
}
99+
#endif

0 commit comments

Comments
 (0)