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