| 
 | 1 | +// SPDX-License-Identifier: GPL-2.0  | 
 | 2 | +/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */  | 
 | 3 | + | 
 | 4 | +#include <test_progs.h>  | 
 | 5 | +#include "testing_helpers.h"  | 
 | 6 | +#include "livepatch_trampoline.skel.h"  | 
 | 7 | + | 
 | 8 | +static int load_livepatch(void)  | 
 | 9 | +{  | 
 | 10 | +	char path[4096];  | 
 | 11 | + | 
 | 12 | +	/* CI will set KBUILD_OUTPUT */  | 
 | 13 | +	snprintf(path, sizeof(path), "%s/samples/livepatch/livepatch-sample.ko",  | 
 | 14 | +		 getenv("KBUILD_OUTPUT") ? : "../../../..");  | 
 | 15 | + | 
 | 16 | +	return load_module(path, env_verbosity > VERBOSE_NONE);  | 
 | 17 | +}  | 
 | 18 | + | 
 | 19 | +static void unload_livepatch(void)  | 
 | 20 | +{  | 
 | 21 | +	/* Disable the livepatch before unloading the module */  | 
 | 22 | +	system("echo 0 > /sys/kernel/livepatch/livepatch_sample/enabled");  | 
 | 23 | + | 
 | 24 | +	unload_module("livepatch_sample", env_verbosity > VERBOSE_NONE);  | 
 | 25 | +}  | 
 | 26 | + | 
 | 27 | +static void read_proc_cmdline(void)  | 
 | 28 | +{  | 
 | 29 | +	char buf[4096];  | 
 | 30 | +	int fd, ret;  | 
 | 31 | + | 
 | 32 | +	fd = open("/proc/cmdline", O_RDONLY);  | 
 | 33 | +	if (!ASSERT_OK_FD(fd, "open /proc/cmdline"))  | 
 | 34 | +		return;  | 
 | 35 | + | 
 | 36 | +	ret = read(fd, buf, sizeof(buf));  | 
 | 37 | +	if (!ASSERT_GT(ret, 0, "read /proc/cmdline"))  | 
 | 38 | +		goto out;  | 
 | 39 | + | 
 | 40 | +	ASSERT_OK(strncmp(buf, "this has been live patched", 26), "strncmp");  | 
 | 41 | + | 
 | 42 | +out:  | 
 | 43 | +	close(fd);  | 
 | 44 | +}  | 
 | 45 | + | 
 | 46 | +static void __test_livepatch_trampoline(bool fexit_first)  | 
 | 47 | +{  | 
 | 48 | +	struct livepatch_trampoline *skel = NULL;  | 
 | 49 | +	int err;  | 
 | 50 | + | 
 | 51 | +	skel = livepatch_trampoline__open_and_load();  | 
 | 52 | +	if (!ASSERT_OK_PTR(skel, "skel_open_and_load"))  | 
 | 53 | +		goto out;  | 
 | 54 | + | 
 | 55 | +	skel->bss->my_pid = getpid();  | 
 | 56 | + | 
 | 57 | +	if (!fexit_first) {  | 
 | 58 | +		/* fentry program is loaded first by default */  | 
 | 59 | +		err = livepatch_trampoline__attach(skel);  | 
 | 60 | +		if (!ASSERT_OK(err, "skel_attach"))  | 
 | 61 | +			goto out;  | 
 | 62 | +	} else {  | 
 | 63 | +		/* Manually load fexit program first. */  | 
 | 64 | +		skel->links.fexit_cmdline = bpf_program__attach(skel->progs.fexit_cmdline);  | 
 | 65 | +		if (!ASSERT_OK_PTR(skel->links.fexit_cmdline, "attach_fexit"))  | 
 | 66 | +			goto out;  | 
 | 67 | + | 
 | 68 | +		skel->links.fentry_cmdline = bpf_program__attach(skel->progs.fentry_cmdline);  | 
 | 69 | +		if (!ASSERT_OK_PTR(skel->links.fentry_cmdline, "attach_fentry"))  | 
 | 70 | +			goto out;  | 
 | 71 | +	}  | 
 | 72 | + | 
 | 73 | +	read_proc_cmdline();  | 
 | 74 | + | 
 | 75 | +	ASSERT_EQ(skel->bss->fentry_hit, 1, "fentry_hit");  | 
 | 76 | +	ASSERT_EQ(skel->bss->fexit_hit, 1, "fexit_hit");  | 
 | 77 | +out:  | 
 | 78 | +	livepatch_trampoline__destroy(skel);  | 
 | 79 | +}  | 
 | 80 | + | 
 | 81 | +void test_livepatch_trampoline(void)  | 
 | 82 | +{  | 
 | 83 | +	int retry_cnt = 0;  | 
 | 84 | + | 
 | 85 | +retry:  | 
 | 86 | +	if (load_livepatch()) {  | 
 | 87 | +		if (retry_cnt) {  | 
 | 88 | +			ASSERT_OK(1, "load_livepatch");  | 
 | 89 | +			goto out;  | 
 | 90 | +		}  | 
 | 91 | +		/*  | 
 | 92 | +		 * Something else (previous run of the same test?) loaded  | 
 | 93 | +		 * the KLP module. Unload the KLP module and retry.  | 
 | 94 | +		 */  | 
 | 95 | +		unload_livepatch();  | 
 | 96 | +		retry_cnt++;  | 
 | 97 | +		goto retry;  | 
 | 98 | +	}  | 
 | 99 | + | 
 | 100 | +	if (test__start_subtest("fentry_first"))  | 
 | 101 | +		__test_livepatch_trampoline(false);  | 
 | 102 | + | 
 | 103 | +	if (test__start_subtest("fexit_first"))  | 
 | 104 | +		__test_livepatch_trampoline(true);  | 
 | 105 | +out:  | 
 | 106 | +	unload_livepatch();  | 
 | 107 | +}  | 
0 commit comments