Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions include/linux/bpf.h
Original file line number Diff line number Diff line change
Expand Up @@ -1609,6 +1609,7 @@ struct bpf_prog_aux {
bool priv_stack_requested;
bool changes_pkt_data;
bool might_sleep;
bool kprobe_write_ctx;
u64 prog_array_member_cnt; /* counts how many times as member of prog_array */
struct mutex ext_mutex; /* mutex for is_extended and prog_array_member_cnt */
struct bpf_arena *arena;
Expand Down
4 changes: 4 additions & 0 deletions kernel/events/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -11205,6 +11205,10 @@ static int __perf_event_set_bpf_prog(struct perf_event *event,
if (prog->kprobe_override && !is_kprobe)
return -EINVAL;

/* Writing to context allowed only for uprobes. */
if (prog->aux->kprobe_write_ctx && !is_uprobe)
return -EINVAL;

if (is_tracepoint || is_syscall_tp) {
int off = trace_event_get_offsets(event->tp_event);

Expand Down
3 changes: 3 additions & 0 deletions kernel/events/uprobes.c
Original file line number Diff line number Diff line change
Expand Up @@ -2742,6 +2742,9 @@ static void handle_swbp(struct pt_regs *regs)

handler_chain(uprobe, regs);

if (instruction_pointer(regs) != bp_vaddr)
goto out;

if (arch_uprobe_skip_sstep(&uprobe->arch, regs))
goto out;

Expand Down
3 changes: 1 addition & 2 deletions kernel/trace/bpf_trace.c
Original file line number Diff line number Diff line change
Expand Up @@ -1521,8 +1521,6 @@ static bool kprobe_prog_is_valid_access(int off, int size, enum bpf_access_type
{
if (off < 0 || off >= sizeof(struct pt_regs))
return false;
if (type != BPF_READ)
return false;
if (off % size != 0)
return false;
/*
Expand All @@ -1532,6 +1530,7 @@ static bool kprobe_prog_is_valid_access(int off, int size, enum bpf_access_type
if (off + size > sizeof(struct pt_regs))
return false;

prog->aux->kprobe_write_ctx |= type == BPF_WRITE;
return true;
}

Expand Down
162 changes: 161 additions & 1 deletion tools/testing/selftests/bpf/prog_tests/uprobe.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
/* Copyright (c) 2023 Hengqi Chen */

#include <test_progs.h>
#include <asm/ptrace.h>
#include "test_uprobe.skel.h"

static FILE *urand_spawn(int *pid)
Expand Down Expand Up @@ -33,7 +34,7 @@ static int urand_trigger(FILE **urand_pipe)
return exit_code;
}

void test_uprobe(void)
static void test_uprobe_attach(void)
{
LIBBPF_OPTS(bpf_uprobe_opts, uprobe_opts);
struct test_uprobe *skel;
Expand Down Expand Up @@ -93,3 +94,162 @@ void test_uprobe(void)
pclose(urand_pipe);
test_uprobe__destroy(skel);
}

#ifdef __x86_64__
__naked __maybe_unused unsigned long uprobe_regs_change_trigger(void)
{
asm volatile (
"ret\n"
);
}

static __naked void uprobe_regs_change(struct pt_regs *before, struct pt_regs *after)
{
asm volatile (
"movq %r11, 48(%rdi)\n"
"movq %r10, 56(%rdi)\n"
"movq %r9, 64(%rdi)\n"
"movq %r8, 72(%rdi)\n"
"movq %rax, 80(%rdi)\n"
"movq %rcx, 88(%rdi)\n"
"movq %rdx, 96(%rdi)\n"
"movq %rsi, 104(%rdi)\n"
"movq %rdi, 112(%rdi)\n"

/* save 2nd argument */
"pushq %rsi\n"
"call uprobe_regs_change_trigger\n"

/* save return value and load 2nd argument pointer to rax */
"pushq %rax\n"
"movq 8(%rsp), %rax\n"

"movq %r11, 48(%rax)\n"
"movq %r10, 56(%rax)\n"
"movq %r9, 64(%rax)\n"
"movq %r8, 72(%rax)\n"
"movq %rcx, 88(%rax)\n"
"movq %rdx, 96(%rax)\n"
"movq %rsi, 104(%rax)\n"
"movq %rdi, 112(%rax)\n"

/* restore return value and 2nd argument */
"pop %rax\n"
"pop %rsi\n"

"movq %rax, 80(%rsi)\n"
"ret\n"
);
}

static void regs_common(void)
{
struct pt_regs before = {}, after = {}, expected = {
.rax = 0xc0ffe,
.rcx = 0xbad,
.rdx = 0xdead,
.r8 = 0x8,
.r9 = 0x9,
.r10 = 0x10,
.r11 = 0x11,
.rdi = 0x12,
.rsi = 0x13,
};
LIBBPF_OPTS(bpf_uprobe_opts, uprobe_opts);
struct test_uprobe *skel;

skel = test_uprobe__open_and_load();
if (!ASSERT_OK_PTR(skel, "skel_open"))
return;

skel->bss->my_pid = getpid();
skel->bss->regs = expected;

uprobe_opts.func_name = "uprobe_regs_change_trigger";
skel->links.test_regs_change = bpf_program__attach_uprobe_opts(skel->progs.test_regs_change,
-1,
"/proc/self/exe",
0 /* offset */,
&uprobe_opts);
if (!ASSERT_OK_PTR(skel->links.test_regs_change, "bpf_program__attach_uprobe_opts"))
goto cleanup;

uprobe_regs_change(&before, &after);

ASSERT_EQ(after.rax, expected.rax, "ax");
ASSERT_EQ(after.rcx, expected.rcx, "cx");
ASSERT_EQ(after.rdx, expected.rdx, "dx");
ASSERT_EQ(after.r8, expected.r8, "r8");
ASSERT_EQ(after.r9, expected.r9, "r9");
ASSERT_EQ(after.r10, expected.r10, "r10");
ASSERT_EQ(after.r11, expected.r11, "r11");
ASSERT_EQ(after.rdi, expected.rdi, "rdi");
ASSERT_EQ(after.rsi, expected.rsi, "rsi");

cleanup:
test_uprobe__destroy(skel);
}

static __naked unsigned long uprobe_regs_change_ip_1(void)
{
asm volatile (
"movq $0xc0ffee, %rax\n"
"ret\n"
);
}

static __naked unsigned long uprobe_regs_change_ip_2(void)
{
asm volatile (
"movq $0xdeadbeef, %rax\n"
"ret\n"
);
}

static void regs_ip(void)
{
LIBBPF_OPTS(bpf_uprobe_opts, uprobe_opts);
struct test_uprobe *skel;
unsigned long ret;

skel = test_uprobe__open_and_load();
if (!ASSERT_OK_PTR(skel, "skel_open"))
return;

skel->bss->my_pid = getpid();
skel->bss->ip = (unsigned long) uprobe_regs_change_ip_2;

uprobe_opts.func_name = "uprobe_regs_change_ip_1";
skel->links.test_regs_change_ip = bpf_program__attach_uprobe_opts(
skel->progs.test_regs_change_ip,
-1,
"/proc/self/exe",
0 /* offset */,
&uprobe_opts);
if (!ASSERT_OK_PTR(skel->links.test_regs_change_ip, "bpf_program__attach_uprobe_opts"))
goto cleanup;

ret = uprobe_regs_change_ip_1();
ASSERT_EQ(ret, 0xdeadbeef, "ret");

cleanup:
test_uprobe__destroy(skel);
}

static void test_uprobe_regs_change(void)
{
if (test__start_subtest("regs_change_common"))
regs_common();
if (test__start_subtest("regs_change_ip"))
regs_ip();
}
#else
static void test_uprobe_regs_change(void) { }
#endif

void test_uprobe(void)
{
if (test__start_subtest("attach"))
test_uprobe_attach();
test_uprobe_regs_change();
}
38 changes: 38 additions & 0 deletions tools/testing/selftests/bpf/progs/test_uprobe.c
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,41 @@ int BPF_UPROBE(test4)
test4_result = 1;
return 0;
}

#if defined(__TARGET_ARCH_x86)
struct pt_regs regs;

SEC("uprobe")
int BPF_UPROBE(test_regs_change)
{
pid_t pid = bpf_get_current_pid_tgid() >> 32;

if (pid != my_pid)
return 0;

ctx->ax = regs.ax;
ctx->cx = regs.cx;
ctx->dx = regs.dx;
ctx->r8 = regs.r8;
ctx->r9 = regs.r9;
ctx->r10 = regs.r10;
ctx->r11 = regs.r11;
ctx->di = regs.di;
ctx->si = regs.si;
return 0;
}

unsigned long ip;

SEC("uprobe")
int BPF_UPROBE(test_regs_change_ip)
{
pid_t pid = bpf_get_current_pid_tgid() >> 32;

if (pid != my_pid)
return 0;

ctx->ip = ip;
return 0;
}
#endif
Loading