~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/tools/testing/selftests/powerpc/ptrace/ptrace-perf-hwbreak.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0+
  2 
  3 #include <asm/unistd.h>
  4 #include <linux/hw_breakpoint.h>
  5 #include <linux/ptrace.h>
  6 #include <memory.h>
  7 #include <stdlib.h>
  8 #include <sys/wait.h>
  9 
 10 #include "utils.h"
 11 
 12 /*
 13  * Child subroutine that performs a load on the address, then traps
 14  */
 15 void same_watch_addr_child(unsigned long *addr);
 16 
 17 /* Address of the ld instruction in same_watch_addr_child() */
 18 extern char same_watch_addr_load[];
 19 
 20 /* Address of the end trap instruction in same_watch_addr_child() */
 21 extern char same_watch_addr_trap[];
 22 
 23 /*
 24  * Child subroutine that performs a load on the first address, then a load on
 25  * the second address (with no instructions separating this from the first
 26  * load), then traps.
 27  */
 28 void perf_then_ptrace_child(unsigned long *first_addr, unsigned long *second_addr);
 29 
 30 /* Address of the first ld instruction in perf_then_ptrace_child() */
 31 extern char perf_then_ptrace_load1[];
 32 
 33 /* Address of the second ld instruction in perf_then_ptrace_child() */
 34 extern char perf_then_ptrace_load2[];
 35 
 36 /* Address of the end trap instruction in perf_then_ptrace_child() */
 37 extern char perf_then_ptrace_trap[];
 38 
 39 static inline long sys_ptrace(long request, pid_t pid, unsigned long addr, unsigned long data)
 40 {
 41         return syscall(__NR_ptrace, request, pid, addr, data);
 42 }
 43 
 44 static long ptrace_traceme(void)
 45 {
 46         return sys_ptrace(PTRACE_TRACEME, 0, 0, 0);
 47 }
 48 
 49 static long ptrace_getregs(pid_t pid, struct pt_regs *result)
 50 {
 51         return sys_ptrace(PTRACE_GETREGS, pid, 0, (unsigned long)result);
 52 }
 53 
 54 static long ptrace_setregs(pid_t pid, struct pt_regs *result)
 55 {
 56         return sys_ptrace(PTRACE_SETREGS, pid, 0, (unsigned long)result);
 57 }
 58 
 59 static long ptrace_cont(pid_t pid, long signal)
 60 {
 61         return sys_ptrace(PTRACE_CONT, pid, 0, signal);
 62 }
 63 
 64 static long ptrace_singlestep(pid_t pid, long signal)
 65 {
 66         return sys_ptrace(PTRACE_SINGLESTEP, pid, 0, signal);
 67 }
 68 
 69 static long ppc_ptrace_gethwdbginfo(pid_t pid, struct ppc_debug_info *dbginfo)
 70 {
 71         return sys_ptrace(PPC_PTRACE_GETHWDBGINFO, pid, 0, (unsigned long)dbginfo);
 72 }
 73 
 74 static long ppc_ptrace_sethwdbg(pid_t pid, struct ppc_hw_breakpoint *bp_info)
 75 {
 76         return sys_ptrace(PPC_PTRACE_SETHWDEBUG, pid, 0, (unsigned long)bp_info);
 77 }
 78 
 79 static long ppc_ptrace_delhwdbg(pid_t pid, int bp_id)
 80 {
 81         return sys_ptrace(PPC_PTRACE_DELHWDEBUG, pid, 0L, bp_id);
 82 }
 83 
 84 static long ptrace_getreg_pc(pid_t pid, void **pc)
 85 {
 86         struct pt_regs regs;
 87         long err;
 88 
 89         err = ptrace_getregs(pid, &regs);
 90         if (err)
 91                 return err;
 92 
 93         *pc = (void *)regs.nip;
 94 
 95         return 0;
 96 }
 97 
 98 static long ptrace_setreg_pc(pid_t pid, void *pc)
 99 {
100         struct pt_regs regs;
101         long err;
102 
103         err = ptrace_getregs(pid, &regs);
104         if (err)
105                 return err;
106 
107         regs.nip = (unsigned long)pc;
108 
109         err = ptrace_setregs(pid, &regs);
110         if (err)
111                 return err;
112 
113         return 0;
114 }
115 
116 static int perf_event_open(struct perf_event_attr *attr, pid_t pid, int cpu,
117                            int group_fd, unsigned long flags)
118 {
119         return syscall(__NR_perf_event_open, attr, pid, cpu, group_fd, flags);
120 }
121 
122 static void perf_user_event_attr_set(struct perf_event_attr *attr, void *addr, u64 len)
123 {
124         memset(attr, 0, sizeof(struct perf_event_attr));
125 
126         attr->type              = PERF_TYPE_BREAKPOINT;
127         attr->size              = sizeof(struct perf_event_attr);
128         attr->bp_type           = HW_BREAKPOINT_R;
129         attr->bp_addr           = (u64)addr;
130         attr->bp_len            = len;
131         attr->exclude_kernel    = 1;
132         attr->exclude_hv        = 1;
133 }
134 
135 static int perf_watchpoint_open(pid_t child_pid, void *addr, u64 len)
136 {
137         struct perf_event_attr attr;
138 
139         perf_user_event_attr_set(&attr, addr, len);
140         return perf_event_open(&attr, child_pid, -1, -1, 0);
141 }
142 
143 static int perf_read_counter(int perf_fd, u64 *count)
144 {
145         /*
146          * A perf counter is retrieved by the read() syscall. It contains
147          * the current count as 8 bytes that are interpreted as a u64
148          */
149         ssize_t len = read(perf_fd, count, sizeof(*count));
150 
151         if (len != sizeof(*count))
152                 return -1;
153 
154         return 0;
155 }
156 
157 static void ppc_ptrace_init_breakpoint(struct ppc_hw_breakpoint *info,
158                                        int type, void *addr, int len)
159 {
160         info->version = 1;
161         info->trigger_type = type;
162         info->condition_mode = PPC_BREAKPOINT_CONDITION_NONE;
163         info->addr = (u64)addr;
164         info->addr2 = (u64)addr + len;
165         info->condition_value = 0;
166         if (!len)
167                 info->addr_mode = PPC_BREAKPOINT_MODE_EXACT;
168         else
169                 info->addr_mode = PPC_BREAKPOINT_MODE_RANGE_INCLUSIVE;
170 }
171 
172 /*
173  * Checks if we can place at least 2 watchpoints on the child process
174  */
175 static int check_watchpoints(pid_t pid)
176 {
177         struct ppc_debug_info dbginfo;
178 
179         FAIL_IF_MSG(ppc_ptrace_gethwdbginfo(pid, &dbginfo), "PPC_PTRACE_GETHWDBGINFO failed");
180         SKIP_IF_MSG(dbginfo.num_data_bps <= 1, "Not enough data watchpoints (need at least 2)");
181 
182         return 0;
183 }
184 
185 /*
186  * Wrapper around a plain fork() call that sets up the child for
187  * ptrace-ing. Both the parent and child return from this, though
188  * the child is stopped until ptrace_cont(pid) is run by the parent.
189  */
190 static int ptrace_fork_child(pid_t *pid)
191 {
192         int status;
193 
194         *pid = fork();
195 
196         if (*pid < 0)
197                 FAIL_IF_MSG(1, "Failed to fork child");
198 
199         if (!*pid) {
200                 FAIL_IF_EXIT_MSG(ptrace_traceme(), "PTRACE_TRACEME failed");
201                 FAIL_IF_EXIT_MSG(raise(SIGSTOP), "Child failed to raise SIGSTOP");
202         } else {
203                 /* Synchronise on child SIGSTOP */
204                 FAIL_IF_MSG(waitpid(*pid, &status, 0) == -1, "Failed to wait for child");
205                 FAIL_IF_MSG(!WIFSTOPPED(status), "Child is not stopped");
206         }
207 
208         return 0;
209 }
210 
211 /*
212  * Tests the interaction between ptrace and perf watching the same data.
213  *
214  * We expect ptrace to take 'priority', as it is has before-execute
215  * semantics.
216  *
217  * The perf counter should not be incremented yet because perf has after-execute
218  * semantics. E.g., if ptrace changes the child PC, we don't even execute the
219  * instruction at all.
220  *
221  * When the child is stopped for ptrace, we test both continue and single step.
222  * Both should increment the perf counter. We also test changing the PC somewhere
223  * different and stepping, which should not increment the perf counter.
224  */
225 int same_watch_addr_test(void)
226 {
227         struct ppc_hw_breakpoint bp_info;       /* ptrace breakpoint info */
228         int bp_id;      /* Breakpoint handle of ptrace watchpoint */
229         int perf_fd;    /* File descriptor of perf performance counter */
230         u64 perf_count; /* Most recently fetched perf performance counter value */
231         pid_t pid;      /* PID of child process */
232         void *pc;       /* Most recently fetched child PC value */
233         int status;     /* Stop status of child after waitpid */
234         unsigned long value;    /* Dummy value to be read/written to by child */
235         int err;
236 
237         err = ptrace_fork_child(&pid);
238         if (err)
239                 return err;
240 
241         if (!pid) {
242                 same_watch_addr_child(&value);
243                 exit(1);
244         }
245 
246         err = check_watchpoints(pid);
247         if (err)
248                 return err;
249 
250         /* Place a perf watchpoint counter on value */
251         perf_fd = perf_watchpoint_open(pid, &value, sizeof(value));
252         FAIL_IF_MSG(perf_fd < 0, "Failed to open perf performance counter");
253 
254         /* Place a ptrace watchpoint on value */
255         ppc_ptrace_init_breakpoint(&bp_info, PPC_BREAKPOINT_TRIGGER_READ, &value, sizeof(value));
256         bp_id = ppc_ptrace_sethwdbg(pid, &bp_info);
257         FAIL_IF_MSG(bp_id < 0, "Failed to set ptrace watchpoint");
258 
259         /* Let the child run. It should stop on the ptrace watchpoint */
260         FAIL_IF_MSG(ptrace_cont(pid, 0), "Failed to continue child");
261 
262         FAIL_IF_MSG(waitpid(pid, &status, 0) == -1, "Failed to wait for child");
263         FAIL_IF_MSG(!WIFSTOPPED(status), "Child is not stopped");
264         FAIL_IF_MSG(ptrace_getreg_pc(pid, &pc), "Failed to get child PC");
265         FAIL_IF_MSG(pc != same_watch_addr_load, "Child did not stop on load instruction");
266 
267         /*
268          * We stopped before executing the load, so perf should not have
269          * recorded any events yet
270          */
271         FAIL_IF_MSG(perf_read_counter(perf_fd, &perf_count), "Failed to read perf counter");
272         FAIL_IF_MSG(perf_count != 0, "perf recorded unexpected event");
273 
274         /* Single stepping over the load should increment the perf counter */
275         FAIL_IF_MSG(ptrace_singlestep(pid, 0), "Failed to single step child");
276 
277         FAIL_IF_MSG(waitpid(pid, &status, 0) == -1, "Failed to wait for child");
278         FAIL_IF_MSG(!WIFSTOPPED(status), "Child is not stopped");
279         FAIL_IF_MSG(ptrace_getreg_pc(pid, &pc), "Failed to get child PC");
280         FAIL_IF_MSG(pc != same_watch_addr_load + 4, "Failed to single step load instruction");
281         FAIL_IF_MSG(perf_read_counter(perf_fd, &perf_count), "Failed to read perf counter");
282         FAIL_IF_MSG(perf_count != 1, "perf counter did not increment");
283 
284         /*
285          * Set up a ptrace watchpoint on the value again and trigger it.
286          * The perf counter should not have incremented because we do not
287          * execute the load yet.
288          */
289         FAIL_IF_MSG(ppc_ptrace_delhwdbg(pid, bp_id), "Failed to remove old ptrace watchpoint");
290         bp_id = ppc_ptrace_sethwdbg(pid, &bp_info);
291         FAIL_IF_MSG(bp_id < 0, "Failed to set ptrace watchpoint");
292         FAIL_IF_MSG(ptrace_setreg_pc(pid, same_watch_addr_load), "Failed to set child PC");
293         FAIL_IF_MSG(ptrace_cont(pid, 0), "Failed to continue child");
294 
295         FAIL_IF_MSG(waitpid(pid, &status, 0) == -1, "Failed to wait for child");
296         FAIL_IF_MSG(!WIFSTOPPED(status), "Child is not stopped");
297         FAIL_IF_MSG(ptrace_getreg_pc(pid, &pc), "Failed to get child PC");
298         FAIL_IF_MSG(pc != same_watch_addr_load, "Child did not stop on load trap");
299         FAIL_IF_MSG(perf_read_counter(perf_fd, &perf_count), "Failed to read perf counter");
300         FAIL_IF_MSG(perf_count != 1, "perf counter should not have changed");
301 
302         /* Continuing over the load should increment the perf counter */
303         FAIL_IF_MSG(ptrace_cont(pid, 0), "Failed to continue child");
304 
305         FAIL_IF_MSG(waitpid(pid, &status, 0) == -1, "Failed to wait for child");
306         FAIL_IF_MSG(!WIFSTOPPED(status), "Child is not stopped");
307         FAIL_IF_MSG(ptrace_getreg_pc(pid, &pc), "Failed to get child PC");
308         FAIL_IF_MSG(pc != same_watch_addr_trap, "Child did not stop on end trap");
309         FAIL_IF_MSG(perf_read_counter(perf_fd, &perf_count), "Failed to read perf counter");
310         FAIL_IF_MSG(perf_count != 2, "perf counter did not increment");
311 
312         /*
313          * If we set the child PC back to the load instruction, then continue,
314          * we should reach the end trap (because ptrace is one-shot) and have
315          * another perf event.
316          */
317         FAIL_IF_MSG(ptrace_setreg_pc(pid, same_watch_addr_load), "Failed to set child PC");
318         FAIL_IF_MSG(ptrace_cont(pid, 0), "Failed to continue child");
319 
320         FAIL_IF_MSG(waitpid(pid, &status, 0) == -1, "Failed to wait for child");
321         FAIL_IF_MSG(!WIFSTOPPED(status), "Child is not stopped");
322         FAIL_IF_MSG(ptrace_getreg_pc(pid, &pc), "Failed to get child PC");
323         FAIL_IF_MSG(pc != same_watch_addr_trap, "Child did not stop on end trap");
324         FAIL_IF_MSG(perf_read_counter(perf_fd, &perf_count), "Failed to read perf counter");
325         FAIL_IF_MSG(perf_count != 3, "perf counter did not increment");
326 
327         /*
328          * If we set the child PC back to the load instruction, set a ptrace
329          * watchpoint on the load, then continue, we should immediately get
330          * the ptrace trap without incrementing the perf counter
331          */
332         FAIL_IF_MSG(ppc_ptrace_delhwdbg(pid, bp_id), "Failed to remove old ptrace watchpoint");
333         bp_id = ppc_ptrace_sethwdbg(pid, &bp_info);
334         FAIL_IF_MSG(bp_id < 0, "Failed to set ptrace watchpoint");
335         FAIL_IF_MSG(ptrace_setreg_pc(pid, same_watch_addr_load), "Failed to set child PC");
336         FAIL_IF_MSG(ptrace_cont(pid, 0), "Failed to continue child");
337 
338         FAIL_IF_MSG(waitpid(pid, &status, 0) == -1, "Failed to wait for child");
339         FAIL_IF_MSG(!WIFSTOPPED(status), "Child is not stopped");
340         FAIL_IF_MSG(ptrace_getreg_pc(pid, &pc), "Failed to get child PC");
341         FAIL_IF_MSG(pc != same_watch_addr_load, "Child did not stop on load instruction");
342         FAIL_IF_MSG(perf_read_counter(perf_fd, &perf_count), "Failed to read perf counter");
343         FAIL_IF_MSG(perf_count != 3, "perf counter should not have changed");
344 
345         /*
346          * If we change the PC while stopped on the load instruction, we should
347          * not increment the perf counter (because ptrace is before-execute,
348          * perf is after-execute).
349          */
350         FAIL_IF_MSG(ptrace_setreg_pc(pid, same_watch_addr_load + 4), "Failed to set child PC");
351         FAIL_IF_MSG(ptrace_cont(pid, 0), "Failed to continue child");
352 
353         FAIL_IF_MSG(waitpid(pid, &status, 0) == -1, "Failed to wait for child");
354         FAIL_IF_MSG(!WIFSTOPPED(status), "Child is not stopped");
355         FAIL_IF_MSG(ptrace_getreg_pc(pid, &pc), "Failed to get child PC");
356         FAIL_IF_MSG(pc != same_watch_addr_trap, "Child did not stop on end trap");
357         FAIL_IF_MSG(perf_read_counter(perf_fd, &perf_count), "Failed to read perf counter");
358         FAIL_IF_MSG(perf_count != 3, "perf counter should not have changed");
359 
360         /* Clean up child */
361         FAIL_IF_MSG(kill(pid, SIGKILL) != 0, "Failed to kill child");
362 
363         return 0;
364 }
365 
366 /*
367  * Tests the interaction between ptrace and perf when:
368  * 1. perf watches a value
369  * 2. ptrace watches a different value
370  * 3. The perf value is read, then the ptrace value is read immediately after
371  *
372  * A breakpoint implementation may accidentally misattribute/skip one of
373  * the ptrace or perf handlers, as interrupt based work is done after perf
374  * and before ptrace.
375  *
376  * We expect the perf counter to increment before the ptrace watchpoint
377  * triggers.
378  */
379 int perf_then_ptrace_test(void)
380 {
381         struct ppc_hw_breakpoint bp_info;       /* ptrace breakpoint info */
382         int bp_id;      /* Breakpoint handle of ptrace watchpoint */
383         int perf_fd;    /* File descriptor of perf performance counter */
384         u64 perf_count; /* Most recently fetched perf performance counter value */
385         pid_t pid;      /* PID of child process */
386         void *pc;       /* Most recently fetched child PC value */
387         int status;     /* Stop status of child after waitpid */
388         unsigned long perf_value;       /* Dummy value to be watched by perf */
389         unsigned long ptrace_value;     /* Dummy value to be watched by ptrace */
390         int err;
391 
392         err = ptrace_fork_child(&pid);
393         if (err)
394                 return err;
395 
396         /*
397          * If we are the child, run a subroutine that reads the perf value,
398          * then reads the ptrace value with consecutive load instructions
399          */
400         if (!pid) {
401                 perf_then_ptrace_child(&perf_value, &ptrace_value);
402                 exit(0);
403         }
404 
405         err = check_watchpoints(pid);
406         if (err)
407                 return err;
408 
409         /* Place a perf watchpoint counter */
410         perf_fd = perf_watchpoint_open(pid, &perf_value, sizeof(perf_value));
411         FAIL_IF_MSG(perf_fd < 0, "Failed to open perf performance counter");
412 
413         /* Place a ptrace watchpoint */
414         ppc_ptrace_init_breakpoint(&bp_info, PPC_BREAKPOINT_TRIGGER_READ,
415                                    &ptrace_value, sizeof(ptrace_value));
416         bp_id = ppc_ptrace_sethwdbg(pid, &bp_info);
417         FAIL_IF_MSG(bp_id < 0, "Failed to set ptrace watchpoint");
418 
419         /* Let the child run. It should stop on the ptrace watchpoint */
420         FAIL_IF_MSG(ptrace_cont(pid, 0), "Failed to continue child");
421 
422         FAIL_IF_MSG(waitpid(pid, &status, 0) == -1, "Failed to wait for child");
423         FAIL_IF_MSG(!WIFSTOPPED(status), "Child is not stopped");
424         FAIL_IF_MSG(ptrace_getreg_pc(pid, &pc), "Failed to get child PC");
425         FAIL_IF_MSG(pc != perf_then_ptrace_load2, "Child did not stop on ptrace load");
426 
427         /* perf should have recorded the first load */
428         FAIL_IF_MSG(perf_read_counter(perf_fd, &perf_count), "Failed to read perf counter");
429         FAIL_IF_MSG(perf_count != 1, "perf counter did not increment");
430 
431         /* Clean up child */
432         FAIL_IF_MSG(kill(pid, SIGKILL) != 0, "Failed to kill child");
433 
434         return 0;
435 }
436 
437 int main(int argc, char *argv[])
438 {
439         int err = 0;
440 
441         err |= test_harness(same_watch_addr_test, "same_watch_addr");
442         err |= test_harness(perf_then_ptrace_test, "perf_then_ptrace");
443 
444         return err;
445 }
446 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php