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
12 changes: 7 additions & 5 deletions kernel/bpf/trampoline.c
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,13 @@ static int register_fentry(struct bpf_trampoline *tr, void *new_addr)

if (tr->func.ftrace_managed) {
ftrace_set_filter_ip(tr->fops, (unsigned long)ip, 0, 1);
/*
* Clearing fops->trampoline_mutex and fops->NULL is
* needed by the "goto again" case in
* bpf_trampoline_update().
*/
tr->fops->trampoline = 0;
tr->fops->func = NULL;
ret = register_ftrace_direct(tr->fops, (long)new_addr);
} else {
ret = bpf_arch_text_poke(ip, BPF_MOD_CALL, NULL, new_addr);
Expand Down Expand Up @@ -479,11 +486,6 @@ static int bpf_trampoline_update(struct bpf_trampoline *tr, bool lock_direct_mut
* BPF_TRAMP_F_SHARE_IPMODIFY is set, we can generate the
* trampoline again, and retry register.
*/
/* reset fops->func and fops->trampoline for re-register */
tr->fops->func = NULL;
tr->fops->trampoline = 0;

/* free im memory and reallocate later */
bpf_tramp_image_free(im);
goto again;
}
Expand Down
14 changes: 12 additions & 2 deletions kernel/trace/ftrace.c
Original file line number Diff line number Diff line change
Expand Up @@ -2020,8 +2020,6 @@ static int __ftrace_hash_update_ipmodify(struct ftrace_ops *ops,
if (is_ipmodify)
goto rollback;

FTRACE_WARN_ON(rec->flags & FTRACE_FL_DIRECT);

/*
* Another ops with IPMODIFY is already
* attached. We are now attaching a direct
Expand Down Expand Up @@ -6048,6 +6046,8 @@ int register_ftrace_direct(struct ftrace_ops *ops, unsigned long addr)
ops->direct_call = addr;

err = register_ftrace_function_nolock(ops);
if (err)
remove_direct_functions_hash(hash, addr);

out_unlock:
mutex_unlock(&direct_mutex);
Expand Down Expand Up @@ -6126,6 +6126,15 @@ __modify_ftrace_direct(struct ftrace_ops *ops, unsigned long addr)
if (err)
return err;

/*
* Call ftrace_hash_ipmodify_enable() here, so that we can call
* ops->ops_func for the ops. This is needed because the above
* register_ftrace_function_nolock() worked on tmp_ops.
*/
err = ftrace_hash_ipmodify_enable(ops);
if (err)
goto out;

/*
* Now the ftrace_ops_list_func() is called to do the direct callers.
* We can safely change the direct functions attached to each entry.
Expand All @@ -6147,6 +6156,7 @@ __modify_ftrace_direct(struct ftrace_ops *ops, unsigned long addr)

mutex_unlock(&ftrace_lock);

out:
/* Removing the tmp_ops will add the updated direct callers to the functions */
unregister_ftrace_function(&tmp_ops);

Expand Down
3 changes: 3 additions & 0 deletions tools/testing/selftests/bpf/config
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ CONFIG_IPV6_SIT=y
CONFIG_IPV6_TUNNEL=y
CONFIG_KEYS=y
CONFIG_LIRC=y
CONFIG_LIVEPATCH=y
CONFIG_LWTUNNEL=y
CONFIG_MODULE_SIG=y
CONFIG_MODULE_SRCVERSION_ALL=y
Expand Down Expand Up @@ -111,6 +112,8 @@ CONFIG_IP6_NF_FILTER=y
CONFIG_NF_NAT=y
CONFIG_PACKET=y
CONFIG_RC_CORE=y
CONFIG_SAMPLES=y
CONFIG_SAMPLE_LIVEPATCH=m
CONFIG_SECURITY=y
CONFIG_SECURITYFS=y
CONFIG_SYN_COOKIES=y
Expand Down
113 changes: 113 additions & 0 deletions tools/testing/selftests/bpf/prog_tests/livepatch_trampoline.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */

#include <test_progs.h>
#include "testing_helpers.h"
#include "livepatch_trampoline.skel.h"

static int load_livepatch(void)
{
const char *livepatch_paths[] = {
"../../../../samples/livepatch/livepatch-sample.ko",
/* This is the path used by CI */
"/tmp/work/bpf/bpf/kbuild-output/samples/livepatch/livepatch-sample.ko",
};
int ret, i;

for (i = 0; i < sizeof(livepatch_paths) / sizeof(char *); i++) {
ret = load_module(livepatch_paths[i], env_verbosity > VERBOSE_NONE);
if (!ret)
break;
}
return ret;
}

static void unload_livepatch(void)
{
/* Disable the livepatch before unloading the module */
system("echo 0 > /sys/kernel/livepatch/livepatch_sample/enabled");

unload_module("livepatch_sample", env_verbosity > VERBOSE_NONE);
}

static void read_proc_cmdline(void)
{
char buf[4096];
int fd, ret;

fd = open("/proc/cmdline", O_RDONLY);
if (!ASSERT_OK_FD(fd, "open /proc/cmdline"))
return;

ret = read(fd, buf, sizeof(buf));
if (!ASSERT_GT(ret, 0, "read /proc/cmdline"))
goto out;

ASSERT_OK(strncmp(buf, "this has been live patched", 26), "strncmp");

out:
close(fd);
}

static void __test_livepatch_trampoline(bool fexit_first)
{
struct livepatch_trampoline *skel = NULL;
int err;

skel = livepatch_trampoline__open_and_load();
if (!ASSERT_OK_PTR(skel, "skel_open_and_load"))
goto out;

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

if (!fexit_first) {
/* fentry program is loaded first by default */
err = livepatch_trampoline__attach(skel);
if (!ASSERT_OK(err, "skel_attach"))
goto out;
} else {
/* Manually load fexit program first. */
skel->links.fexit_cmdline = bpf_program__attach(skel->progs.fexit_cmdline);
if (!ASSERT_OK_PTR(skel->links.fexit_cmdline, "attach_fexit"))
goto out;

skel->links.fentry_cmdline = bpf_program__attach(skel->progs.fentry_cmdline);
if (!ASSERT_OK_PTR(skel->links.fentry_cmdline, "attach_fentry"))
goto out;
}

read_proc_cmdline();

ASSERT_EQ(skel->bss->fentry_hit, 1, "fentry_hit");
ASSERT_EQ(skel->bss->fexit_hit, 1, "fexit_hit");
out:
livepatch_trampoline__destroy(skel);
}

void test_livepatch_trampoline(void)
{
int retry_cnt = 0;

retry:
if (load_livepatch()) {
if (retry_cnt) {
ASSERT_OK(1, "load_livepatch");
goto out;
}
/*
* Something else (previous run of the same test?) loaded
* the KLP module. Unload the KLP module and retry.
*/
unload_livepatch();
retry_cnt++;
goto retry;
}

if (test__start_subtest("fentry_first"))
__test_livepatch_trampoline(false);

if (test__start_subtest("fexit_first"))
__test_livepatch_trampoline(true);
out:
unload_livepatch();
}
30 changes: 30 additions & 0 deletions tools/testing/selftests/bpf/progs/livepatch_trampoline.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */

#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>

int fentry_hit;
int fexit_hit;
int my_pid;

SEC("fentry/cmdline_proc_show")
int BPF_PROG(fentry_cmdline)
{
if (my_pid != (bpf_get_current_pid_tgid() >> 32))
return 0;

fentry_hit = 1;
return 0;
}

SEC("fexit/cmdline_proc_show")
int BPF_PROG(fexit_cmdline)
{
if (my_pid != (bpf_get_current_pid_tgid() >> 32))
return 0;

fexit_hit = 1;
return 0;
}
Loading