Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright (C) 2011 Red Hat, Inc., Frederic Weisbecker <fweisbec@redhat.com>
0004  *
0005  * Selftests for breakpoints (and more generally the do_debug() path) in x86.
0006  */
0007 
0008 
0009 #include <sys/ptrace.h>
0010 #include <unistd.h>
0011 #include <stddef.h>
0012 #include <sys/user.h>
0013 #include <stdio.h>
0014 #include <stdlib.h>
0015 #include <signal.h>
0016 #include <sys/types.h>
0017 #include <sys/wait.h>
0018 #include <errno.h>
0019 #include <string.h>
0020 
0021 #include "../kselftest.h"
0022 
0023 #define COUNT_ISN_BPS   4
0024 #define COUNT_WPS   4
0025 
0026 /* Breakpoint access modes */
0027 enum {
0028     BP_X = 1,
0029     BP_RW = 2,
0030     BP_W = 4,
0031 };
0032 
0033 static pid_t child_pid;
0034 
0035 /*
0036  * Ensures the child and parent are always "talking" about
0037  * the same test sequence. (ie: that we haven't forgotten
0038  * to call check_trapped() somewhere).
0039  */
0040 static int nr_tests;
0041 
0042 static void set_breakpoint_addr(void *addr, int n)
0043 {
0044     int ret;
0045 
0046     ret = ptrace(PTRACE_POKEUSER, child_pid,
0047              offsetof(struct user, u_debugreg[n]), addr);
0048     if (ret)
0049         ksft_exit_fail_msg("Can't set breakpoint addr: %s\n",
0050             strerror(errno));
0051 }
0052 
0053 static void toggle_breakpoint(int n, int type, int len,
0054                   int local, int global, int set)
0055 {
0056     int ret;
0057 
0058     int xtype, xlen;
0059     unsigned long vdr7, dr7;
0060 
0061     switch (type) {
0062     case BP_X:
0063         xtype = 0;
0064         break;
0065     case BP_W:
0066         xtype = 1;
0067         break;
0068     case BP_RW:
0069         xtype = 3;
0070         break;
0071     }
0072 
0073     switch (len) {
0074     case 1:
0075         xlen = 0;
0076         break;
0077     case 2:
0078         xlen = 4;
0079         break;
0080     case 4:
0081         xlen = 0xc;
0082         break;
0083     case 8:
0084         xlen = 8;
0085         break;
0086     }
0087 
0088     dr7 = ptrace(PTRACE_PEEKUSER, child_pid,
0089              offsetof(struct user, u_debugreg[7]), 0);
0090 
0091     vdr7 = (xlen | xtype) << 16;
0092     vdr7 <<= 4 * n;
0093 
0094     if (local) {
0095         vdr7 |= 1 << (2 * n);
0096         vdr7 |= 1 << 8;
0097     }
0098     if (global) {
0099         vdr7 |= 2 << (2 * n);
0100         vdr7 |= 1 << 9;
0101     }
0102 
0103     if (set)
0104         dr7 |= vdr7;
0105     else
0106         dr7 &= ~vdr7;
0107 
0108     ret = ptrace(PTRACE_POKEUSER, child_pid,
0109              offsetof(struct user, u_debugreg[7]), dr7);
0110     if (ret) {
0111         ksft_print_msg("Can't set dr7: %s\n", strerror(errno));
0112         exit(-1);
0113     }
0114 }
0115 
0116 /* Dummy variables to test read/write accesses */
0117 static unsigned long long dummy_var[4];
0118 
0119 /* Dummy functions to test execution accesses */
0120 static void dummy_func(void) { }
0121 static void dummy_func1(void) { }
0122 static void dummy_func2(void) { }
0123 static void dummy_func3(void) { }
0124 
0125 static void (*dummy_funcs[])(void) = {
0126     dummy_func,
0127     dummy_func1,
0128     dummy_func2,
0129     dummy_func3,
0130 };
0131 
0132 static int trapped;
0133 
0134 static void check_trapped(void)
0135 {
0136     /*
0137      * If we haven't trapped, wake up the parent
0138      * so that it notices the failure.
0139      */
0140     if (!trapped)
0141         kill(getpid(), SIGUSR1);
0142     trapped = 0;
0143 
0144     nr_tests++;
0145 }
0146 
0147 static void write_var(int len)
0148 {
0149     char *pcval; short *psval; int *pival; long long *plval;
0150     int i;
0151 
0152     for (i = 0; i < 4; i++) {
0153         switch (len) {
0154         case 1:
0155             pcval = (char *)&dummy_var[i];
0156             *pcval = 0xff;
0157             break;
0158         case 2:
0159             psval = (short *)&dummy_var[i];
0160             *psval = 0xffff;
0161             break;
0162         case 4:
0163             pival = (int *)&dummy_var[i];
0164             *pival = 0xffffffff;
0165             break;
0166         case 8:
0167             plval = (long long *)&dummy_var[i];
0168             *plval = 0xffffffffffffffffLL;
0169             break;
0170         }
0171         check_trapped();
0172     }
0173 }
0174 
0175 static void read_var(int len)
0176 {
0177     char cval; short sval; int ival; long long lval;
0178     int i;
0179 
0180     for (i = 0; i < 4; i++) {
0181         switch (len) {
0182         case 1:
0183             cval = *(char *)&dummy_var[i];
0184             break;
0185         case 2:
0186             sval = *(short *)&dummy_var[i];
0187             break;
0188         case 4:
0189             ival = *(int *)&dummy_var[i];
0190             break;
0191         case 8:
0192             lval = *(long long *)&dummy_var[i];
0193             break;
0194         }
0195         check_trapped();
0196     }
0197 }
0198 
0199 /*
0200  * Do the r/w/x accesses to trigger the breakpoints. And run
0201  * the usual traps.
0202  */
0203 static void trigger_tests(void)
0204 {
0205     int len, local, global, i;
0206     char val;
0207     int ret;
0208 
0209     ret = ptrace(PTRACE_TRACEME, 0, NULL, 0);
0210     if (ret) {
0211         ksft_print_msg("Can't be traced? %s\n", strerror(errno));
0212         return;
0213     }
0214 
0215     /* Wake up father so that it sets up the first test */
0216     kill(getpid(), SIGUSR1);
0217 
0218     /* Test instruction breakpoints */
0219     for (local = 0; local < 2; local++) {
0220         for (global = 0; global < 2; global++) {
0221             if (!local && !global)
0222                 continue;
0223 
0224             for (i = 0; i < COUNT_ISN_BPS; i++) {
0225                 dummy_funcs[i]();
0226                 check_trapped();
0227             }
0228         }
0229     }
0230 
0231     /* Test write watchpoints */
0232     for (len = 1; len <= sizeof(long); len <<= 1) {
0233         for (local = 0; local < 2; local++) {
0234             for (global = 0; global < 2; global++) {
0235                 if (!local && !global)
0236                     continue;
0237                 write_var(len);
0238             }
0239         }
0240     }
0241 
0242     /* Test read/write watchpoints (on read accesses) */
0243     for (len = 1; len <= sizeof(long); len <<= 1) {
0244         for (local = 0; local < 2; local++) {
0245             for (global = 0; global < 2; global++) {
0246                 if (!local && !global)
0247                     continue;
0248                 read_var(len);
0249             }
0250         }
0251     }
0252 
0253     /* Icebp trap */
0254     asm(".byte 0xf1\n");
0255     check_trapped();
0256 
0257     /* Int 3 trap */
0258     asm("int $3\n");
0259     check_trapped();
0260 
0261     kill(getpid(), SIGUSR1);
0262 }
0263 
0264 static void check_success(const char *msg)
0265 {
0266     int child_nr_tests;
0267     int status;
0268     int ret;
0269 
0270     /* Wait for the child to SIGTRAP */
0271     wait(&status);
0272 
0273     ret = 0;
0274 
0275     if (WSTOPSIG(status) == SIGTRAP) {
0276         child_nr_tests = ptrace(PTRACE_PEEKDATA, child_pid,
0277                     &nr_tests, 0);
0278         if (child_nr_tests == nr_tests)
0279             ret = 1;
0280         if (ptrace(PTRACE_POKEDATA, child_pid, &trapped, 1))
0281             ksft_exit_fail_msg("Can't poke: %s\n", strerror(errno));
0282     }
0283 
0284     nr_tests++;
0285 
0286     if (ret)
0287         ksft_test_result_pass(msg);
0288     else
0289         ksft_test_result_fail(msg);
0290 }
0291 
0292 static void launch_instruction_breakpoints(char *buf, int local, int global)
0293 {
0294     int i;
0295 
0296     for (i = 0; i < COUNT_ISN_BPS; i++) {
0297         set_breakpoint_addr(dummy_funcs[i], i);
0298         toggle_breakpoint(i, BP_X, 1, local, global, 1);
0299         ptrace(PTRACE_CONT, child_pid, NULL, 0);
0300         sprintf(buf, "Test breakpoint %d with local: %d global: %d\n",
0301             i, local, global);
0302         check_success(buf);
0303         toggle_breakpoint(i, BP_X, 1, local, global, 0);
0304     }
0305 }
0306 
0307 static void launch_watchpoints(char *buf, int mode, int len,
0308                    int local, int global)
0309 {
0310     const char *mode_str;
0311     int i;
0312 
0313     if (mode == BP_W)
0314         mode_str = "write";
0315     else
0316         mode_str = "read";
0317 
0318     for (i = 0; i < COUNT_WPS; i++) {
0319         set_breakpoint_addr(&dummy_var[i], i);
0320         toggle_breakpoint(i, mode, len, local, global, 1);
0321         ptrace(PTRACE_CONT, child_pid, NULL, 0);
0322         sprintf(buf,
0323             "Test %s watchpoint %d with len: %d local: %d global: %d\n",
0324             mode_str, i, len, local, global);
0325         check_success(buf);
0326         toggle_breakpoint(i, mode, len, local, global, 0);
0327     }
0328 }
0329 
0330 /* Set the breakpoints and check the child successfully trigger them */
0331 static void launch_tests(void)
0332 {
0333     char buf[1024];
0334     unsigned int tests = 0;
0335     int len, local, global, i;
0336 
0337     tests += 3 * COUNT_ISN_BPS;
0338     tests += sizeof(long) / 2 * 3 * COUNT_WPS;
0339     tests += sizeof(long) / 2 * 3 * COUNT_WPS;
0340     tests += 2;
0341     ksft_set_plan(tests);
0342 
0343     /* Instruction breakpoints */
0344     for (local = 0; local < 2; local++) {
0345         for (global = 0; global < 2; global++) {
0346             if (!local && !global)
0347                 continue;
0348             launch_instruction_breakpoints(buf, local, global);
0349         }
0350     }
0351 
0352     /* Write watchpoint */
0353     for (len = 1; len <= sizeof(long); len <<= 1) {
0354         for (local = 0; local < 2; local++) {
0355             for (global = 0; global < 2; global++) {
0356                 if (!local && !global)
0357                     continue;
0358                 launch_watchpoints(buf, BP_W, len,
0359                            local, global);
0360             }
0361         }
0362     }
0363 
0364     /* Read-Write watchpoint */
0365     for (len = 1; len <= sizeof(long); len <<= 1) {
0366         for (local = 0; local < 2; local++) {
0367             for (global = 0; global < 2; global++) {
0368                 if (!local && !global)
0369                     continue;
0370                 launch_watchpoints(buf, BP_RW, len,
0371                            local, global);
0372             }
0373         }
0374     }
0375 
0376     /* Icebp traps */
0377     ptrace(PTRACE_CONT, child_pid, NULL, 0);
0378     check_success("Test icebp\n");
0379 
0380     /* Int 3 traps */
0381     ptrace(PTRACE_CONT, child_pid, NULL, 0);
0382     check_success("Test int 3 trap\n");
0383 
0384     ptrace(PTRACE_CONT, child_pid, NULL, 0);
0385 }
0386 
0387 int main(int argc, char **argv)
0388 {
0389     pid_t pid;
0390     int ret;
0391 
0392     ksft_print_header();
0393 
0394     pid = fork();
0395     if (!pid) {
0396         trigger_tests();
0397         exit(0);
0398     }
0399 
0400     child_pid = pid;
0401 
0402     wait(NULL);
0403 
0404     launch_tests();
0405 
0406     wait(NULL);
0407 
0408     ksft_exit_pass();
0409 }