brintos

brintos / linux-shallow public Read only

0
0
Text · 6.9 KiB · 24de0e5 Raw
276 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * test_fprobe.c - simple sanity test for fprobe4 */5 6#include <linux/kernel.h>7#include <linux/fprobe.h>8#include <linux/random.h>9#include <kunit/test.h>10 11#define div_factor 312 13static struct kunit *current_test;14 15static u32 rand1, entry_val, exit_val;16 17/* Use indirect calls to avoid inlining the target functions */18static u32 (*target)(u32 value);19static u32 (*target2)(u32 value);20static u32 (*target_nest)(u32 value, u32 (*nest)(u32));21static unsigned long target_ip;22static unsigned long target2_ip;23static unsigned long target_nest_ip;24static int entry_return_value;25 26static noinline u32 fprobe_selftest_target(u32 value)27{28	return (value / div_factor);29}30 31static noinline u32 fprobe_selftest_target2(u32 value)32{33	return (value / div_factor) + 1;34}35 36static noinline u32 fprobe_selftest_nest_target(u32 value, u32 (*nest)(u32))37{38	return nest(value + 2);39}40 41static notrace int fp_entry_handler(struct fprobe *fp, unsigned long ip,42				    unsigned long ret_ip,43				    struct pt_regs *regs, void *data)44{45	KUNIT_EXPECT_FALSE(current_test, preemptible());46	/* This can be called on the fprobe_selftest_target and the fprobe_selftest_target2 */47	if (ip != target_ip)48		KUNIT_EXPECT_EQ(current_test, ip, target2_ip);49	entry_val = (rand1 / div_factor);50	if (fp->entry_data_size) {51		KUNIT_EXPECT_NOT_NULL(current_test, data);52		if (data)53			*(u32 *)data = entry_val;54	} else55		KUNIT_EXPECT_NULL(current_test, data);56 57	return entry_return_value;58}59 60static notrace void fp_exit_handler(struct fprobe *fp, unsigned long ip,61				    unsigned long ret_ip,62				    struct pt_regs *regs, void *data)63{64	unsigned long ret = regs_return_value(regs);65 66	KUNIT_EXPECT_FALSE(current_test, preemptible());67	if (ip != target_ip) {68		KUNIT_EXPECT_EQ(current_test, ip, target2_ip);69		KUNIT_EXPECT_EQ(current_test, ret, (rand1 / div_factor) + 1);70	} else71		KUNIT_EXPECT_EQ(current_test, ret, (rand1 / div_factor));72	KUNIT_EXPECT_EQ(current_test, entry_val, (rand1 / div_factor));73	exit_val = entry_val + div_factor;74	if (fp->entry_data_size) {75		KUNIT_EXPECT_NOT_NULL(current_test, data);76		if (data)77			KUNIT_EXPECT_EQ(current_test, *(u32 *)data, entry_val);78	} else79		KUNIT_EXPECT_NULL(current_test, data);80}81 82static notrace int nest_entry_handler(struct fprobe *fp, unsigned long ip,83				      unsigned long ret_ip,84				      struct pt_regs *regs, void *data)85{86	KUNIT_EXPECT_FALSE(current_test, preemptible());87	return 0;88}89 90static notrace void nest_exit_handler(struct fprobe *fp, unsigned long ip,91				      unsigned long ret_ip,92				      struct pt_regs *regs, void *data)93{94	KUNIT_EXPECT_FALSE(current_test, preemptible());95	KUNIT_EXPECT_EQ(current_test, ip, target_nest_ip);96}97 98/* Test entry only (no rethook) */99static void test_fprobe_entry(struct kunit *test)100{101	struct fprobe fp_entry = {102		.entry_handler = fp_entry_handler,103	};104 105	current_test = test;106 107	/* Before register, unregister should be failed. */108	KUNIT_EXPECT_NE(test, 0, unregister_fprobe(&fp_entry));109	KUNIT_EXPECT_EQ(test, 0, register_fprobe(&fp_entry, "fprobe_selftest_target*", NULL));110 111	entry_val = 0;112	exit_val = 0;113	target(rand1);114	KUNIT_EXPECT_NE(test, 0, entry_val);115	KUNIT_EXPECT_EQ(test, 0, exit_val);116 117	entry_val = 0;118	exit_val = 0;119	target2(rand1);120	KUNIT_EXPECT_NE(test, 0, entry_val);121	KUNIT_EXPECT_EQ(test, 0, exit_val);122 123	KUNIT_EXPECT_EQ(test, 0, unregister_fprobe(&fp_entry));124}125 126static void test_fprobe(struct kunit *test)127{128	struct fprobe fp = {129		.entry_handler = fp_entry_handler,130		.exit_handler = fp_exit_handler,131	};132 133	current_test = test;134	KUNIT_EXPECT_EQ(test, 0, register_fprobe(&fp, "fprobe_selftest_target*", NULL));135 136	entry_val = 0;137	exit_val = 0;138	target(rand1);139	KUNIT_EXPECT_NE(test, 0, entry_val);140	KUNIT_EXPECT_EQ(test, entry_val + div_factor, exit_val);141 142	entry_val = 0;143	exit_val = 0;144	target2(rand1);145	KUNIT_EXPECT_NE(test, 0, entry_val);146	KUNIT_EXPECT_EQ(test, entry_val + div_factor, exit_val);147 148	KUNIT_EXPECT_EQ(test, 0, unregister_fprobe(&fp));149}150 151static void test_fprobe_syms(struct kunit *test)152{153	static const char *syms[] = {"fprobe_selftest_target", "fprobe_selftest_target2"};154	struct fprobe fp = {155		.entry_handler = fp_entry_handler,156		.exit_handler = fp_exit_handler,157	};158 159	current_test = test;160	KUNIT_EXPECT_EQ(test, 0, register_fprobe_syms(&fp, syms, 2));161 162	entry_val = 0;163	exit_val = 0;164	target(rand1);165	KUNIT_EXPECT_NE(test, 0, entry_val);166	KUNIT_EXPECT_EQ(test, entry_val + div_factor, exit_val);167 168	entry_val = 0;169	exit_val = 0;170	target2(rand1);171	KUNIT_EXPECT_NE(test, 0, entry_val);172	KUNIT_EXPECT_EQ(test, entry_val + div_factor, exit_val);173 174	KUNIT_EXPECT_EQ(test, 0, unregister_fprobe(&fp));175}176 177/* Test private entry_data */178static void test_fprobe_data(struct kunit *test)179{180	struct fprobe fp = {181		.entry_handler = fp_entry_handler,182		.exit_handler = fp_exit_handler,183		.entry_data_size = sizeof(u32),184	};185 186	current_test = test;187	KUNIT_EXPECT_EQ(test, 0, register_fprobe(&fp, "fprobe_selftest_target", NULL));188 189	target(rand1);190 191	KUNIT_EXPECT_EQ(test, 0, unregister_fprobe(&fp));192}193 194/* Test nr_maxactive */195static void test_fprobe_nest(struct kunit *test)196{197	static const char *syms[] = {"fprobe_selftest_target", "fprobe_selftest_nest_target"};198	struct fprobe fp = {199		.entry_handler = nest_entry_handler,200		.exit_handler = nest_exit_handler,201		.nr_maxactive = 1,202	};203 204	current_test = test;205	KUNIT_EXPECT_EQ(test, 0, register_fprobe_syms(&fp, syms, 2));206 207	target_nest(rand1, target);208	KUNIT_EXPECT_EQ(test, 1, fp.nmissed);209 210	KUNIT_EXPECT_EQ(test, 0, unregister_fprobe(&fp));211}212 213static void test_fprobe_skip(struct kunit *test)214{215	struct fprobe fp = {216		.entry_handler = fp_entry_handler,217		.exit_handler = fp_exit_handler,218	};219 220	current_test = test;221	KUNIT_EXPECT_EQ(test, 0, register_fprobe(&fp, "fprobe_selftest_target", NULL));222 223	entry_return_value = 1;224	entry_val = 0;225	exit_val = 0;226	target(rand1);227	KUNIT_EXPECT_NE(test, 0, entry_val);228	KUNIT_EXPECT_EQ(test, 0, exit_val);229	KUNIT_EXPECT_EQ(test, 0, fp.nmissed);230	entry_return_value = 0;231 232	KUNIT_EXPECT_EQ(test, 0, unregister_fprobe(&fp));233}234 235static unsigned long get_ftrace_location(void *func)236{237	unsigned long size, addr = (unsigned long)func;238 239	if (!kallsyms_lookup_size_offset(addr, &size, NULL) || !size)240		return 0;241 242	return ftrace_location_range(addr, addr + size - 1);243}244 245static int fprobe_test_init(struct kunit *test)246{247	rand1 = get_random_u32_above(div_factor);248	target = fprobe_selftest_target;249	target2 = fprobe_selftest_target2;250	target_nest = fprobe_selftest_nest_target;251	target_ip = get_ftrace_location(target);252	target2_ip = get_ftrace_location(target2);253	target_nest_ip = get_ftrace_location(target_nest);254 255	return 0;256}257 258static struct kunit_case fprobe_testcases[] = {259	KUNIT_CASE(test_fprobe_entry),260	KUNIT_CASE(test_fprobe),261	KUNIT_CASE(test_fprobe_syms),262	KUNIT_CASE(test_fprobe_data),263	KUNIT_CASE(test_fprobe_nest),264	KUNIT_CASE(test_fprobe_skip),265	{}266};267 268static struct kunit_suite fprobe_test_suite = {269	.name = "fprobe_test",270	.init = fprobe_test_init,271	.test_cases = fprobe_testcases,272};273 274kunit_test_suites(&fprobe_test_suite);275 276