1 // SPDX-License-Identifier: GPL-2.0 2 /* Copyright (c) 2023 Hengqi Chen */ 3 4 #include <test_progs.h> 5 #include <asm/ptrace.h> 6 #include "test_uprobe.skel.h" 7 8 static FILE *urand_spawn(int *pid) 9 { 10 FILE *f; 11 12 /* urandom_read's stdout is wired into f */ 13 f = popen("./urandom_read 1 report-pid", "r"); 14 if (!f) 15 return NULL; 16 17 if (fscanf(f, "%d", pid) != 1) { 18 pclose(f); 19 errno = EINVAL; 20 return NULL; 21 } 22 23 return f; 24 } 25 26 static int urand_trigger(FILE **urand_pipe) 27 { 28 int exit_code; 29 30 /* pclose() waits for child process to exit and returns their exit code */ 31 exit_code = pclose(*urand_pipe); 32 *urand_pipe = NULL; 33 34 return exit_code; 35 } 36 37 static void test_uprobe_attach(void) 38 { 39 LIBBPF_OPTS(bpf_uprobe_opts, uprobe_opts); 40 struct test_uprobe *skel; 41 FILE *urand_pipe = NULL; 42 int urand_pid = 0, err; 43 44 skel = test_uprobe__open_and_load(); 45 if (!ASSERT_OK_PTR(skel, "skel_open")) 46 return; 47 48 urand_pipe = urand_spawn(&urand_pid); 49 if (!ASSERT_OK_PTR(urand_pipe, "urand_spawn")) 50 goto cleanup; 51 52 skel->bss->my_pid = urand_pid; 53 54 /* Manual attach uprobe to urandlib_api 55 * There are two `urandlib_api` symbols in .dynsym section: 56 * - urandlib_api@LIBURANDOM_READ_1.0.0 57 * - urandlib_api@@LIBURANDOM_READ_2.0.0 58 * Both are global bind and would cause a conflict if user 59 * specify the symbol name without a version suffix 60 */ 61 uprobe_opts.func_name = "urandlib_api"; 62 skel->links.test4 = bpf_program__attach_uprobe_opts(skel->progs.test4, 63 urand_pid, 64 "./liburandom_read.so", 65 0 /* offset */, 66 &uprobe_opts); 67 if (!ASSERT_ERR_PTR(skel->links.test4, "urandlib_api_attach_conflict")) 68 goto cleanup; 69 70 uprobe_opts.func_name = "urandlib_api@LIBURANDOM_READ_1.0.0"; 71 skel->links.test4 = bpf_program__attach_uprobe_opts(skel->progs.test4, 72 urand_pid, 73 "./liburandom_read.so", 74 0 /* offset */, 75 &uprobe_opts); 76 if (!ASSERT_OK_PTR(skel->links.test4, "urandlib_api_attach_ok")) 77 goto cleanup; 78 79 /* Auto attach 3 u[ret]probes to urandlib_api_sameoffset */ 80 err = test_uprobe__attach(skel); 81 if (!ASSERT_OK(err, "skel_attach")) 82 goto cleanup; 83 84 /* trigger urandom_read */ 85 ASSERT_OK(urand_trigger(&urand_pipe), "urand_exit_code"); 86 87 ASSERT_EQ(skel->bss->test1_result, 1, "urandlib_api_sameoffset"); 88 ASSERT_EQ(skel->bss->test2_result, 1, "urandlib_api_sameoffset@v1"); 89 ASSERT_EQ(skel->bss->test3_result, 3, "urandlib_api_sameoffset@@v2"); 90 ASSERT_EQ(skel->bss->test4_result, 1, "urandlib_api"); 91 92 cleanup: 93 if (urand_pipe) 94 pclose(urand_pipe); 95 test_uprobe__destroy(skel); 96 } 97 98 #ifdef __x86_64__ 99 __naked __maybe_unused unsigned long uprobe_regs_change_trigger(void) 100 { 101 asm volatile ( 102 "ret\n" 103 ); 104 } 105 106 static __naked void uprobe_regs_change(struct pt_regs *before, struct pt_regs *after) 107 { 108 asm volatile ( 109 "movq %r11, 48(%rdi)\n" 110 "movq %r10, 56(%rdi)\n" 111 "movq %r9, 64(%rdi)\n" 112 "movq %r8, 72(%rdi)\n" 113 "movq %rax, 80(%rdi)\n" 114 "movq %rcx, 88(%rdi)\n" 115 "movq %rdx, 96(%rdi)\n" 116 "movq %rsi, 104(%rdi)\n" 117 "movq %rdi, 112(%rdi)\n" 118 119 /* save 2nd argument */ 120 "pushq %rsi\n" 121 "call uprobe_regs_change_trigger\n" 122 123 /* save return value and load 2nd argument pointer to rax */ 124 "pushq %rax\n" 125 "movq 8(%rsp), %rax\n" 126 127 "movq %r11, 48(%rax)\n" 128 "movq %r10, 56(%rax)\n" 129 "movq %r9, 64(%rax)\n" 130 "movq %r8, 72(%rax)\n" 131 "movq %rcx, 88(%rax)\n" 132 "movq %rdx, 96(%rax)\n" 133 "movq %rsi, 104(%rax)\n" 134 "movq %rdi, 112(%rax)\n" 135 136 /* restore return value and 2nd argument */ 137 "pop %rax\n" 138 "pop %rsi\n" 139 140 "movq %rax, 80(%rsi)\n" 141 "ret\n" 142 ); 143 } 144 145 static void regs_common(void) 146 { 147 struct pt_regs before = {}, after = {}, expected = { 148 .rax = 0xc0ffe, 149 .rcx = 0xbad, 150 .rdx = 0xdead, 151 .r8 = 0x8, 152 .r9 = 0x9, 153 .r10 = 0x10, 154 .r11 = 0x11, 155 .rdi = 0x12, 156 .rsi = 0x13, 157 }; 158 LIBBPF_OPTS(bpf_uprobe_opts, uprobe_opts); 159 struct test_uprobe *skel; 160 161 skel = test_uprobe__open_and_load(); 162 if (!ASSERT_OK_PTR(skel, "skel_open")) 163 return; 164 165 skel->bss->my_pid = getpid(); 166 skel->bss->regs = expected; 167 168 uprobe_opts.func_name = "uprobe_regs_change_trigger"; 169 skel->links.test_regs_change = bpf_program__attach_uprobe_opts(skel->progs.test_regs_change, 170 -1, 171 "/proc/self/exe", 172 0 /* offset */, 173 &uprobe_opts); 174 if (!ASSERT_OK_PTR(skel->links.test_regs_change, "bpf_program__attach_uprobe_opts")) 175 goto cleanup; 176 177 uprobe_regs_change(&before, &after); 178 179 ASSERT_EQ(after.rax, expected.rax, "ax"); 180 ASSERT_EQ(after.rcx, expected.rcx, "cx"); 181 ASSERT_EQ(after.rdx, expected.rdx, "dx"); 182 ASSERT_EQ(after.r8, expected.r8, "r8"); 183 ASSERT_EQ(after.r9, expected.r9, "r9"); 184 ASSERT_EQ(after.r10, expected.r10, "r10"); 185 ASSERT_EQ(after.r11, expected.r11, "r11"); 186 ASSERT_EQ(after.rdi, expected.rdi, "rdi"); 187 ASSERT_EQ(after.rsi, expected.rsi, "rsi"); 188 189 cleanup: 190 test_uprobe__destroy(skel); 191 } 192 193 static noinline unsigned long uprobe_regs_change_ip_1(void) 194 { 195 return 0xc0ffee; 196 } 197 198 static noinline unsigned long uprobe_regs_change_ip_2(void) 199 { 200 return 0xdeadbeef; 201 } 202 203 static void regs_ip(void) 204 { 205 LIBBPF_OPTS(bpf_uprobe_opts, uprobe_opts); 206 struct test_uprobe *skel; 207 unsigned long ret; 208 209 skel = test_uprobe__open_and_load(); 210 if (!ASSERT_OK_PTR(skel, "skel_open")) 211 return; 212 213 skel->bss->my_pid = getpid(); 214 skel->bss->ip = (unsigned long) uprobe_regs_change_ip_2; 215 216 uprobe_opts.func_name = "uprobe_regs_change_ip_1"; 217 skel->links.test_regs_change_ip = bpf_program__attach_uprobe_opts( 218 skel->progs.test_regs_change_ip, 219 -1, 220 "/proc/self/exe", 221 0 /* offset */, 222 &uprobe_opts); 223 if (!ASSERT_OK_PTR(skel->links.test_regs_change_ip, "bpf_program__attach_uprobe_opts")) 224 goto cleanup; 225 226 ret = uprobe_regs_change_ip_1(); 227 ASSERT_EQ(ret, 0xdeadbeef, "ret"); 228 229 cleanup: 230 test_uprobe__destroy(skel); 231 } 232 233 static void test_uprobe_regs_change(void) 234 { 235 if (test__start_subtest("regs_change_common")) 236 regs_common(); 237 if (test__start_subtest("regs_change_ip")) 238 regs_ip(); 239 } 240 #else 241 static void test_uprobe_regs_change(void) { } 242 #endif 243 244 void test_uprobe(void) 245 { 246 if (test__start_subtest("attach")) 247 test_uprobe_attach(); 248 test_uprobe_regs_change(); 249 } 250