Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 #define _GNU_SOURCE
0003 
0004 #include <sys/ptrace.h>
0005 #include <sys/types.h>
0006 #include <sys/wait.h>
0007 #include <sys/syscall.h>
0008 #include <sys/user.h>
0009 #include <unistd.h>
0010 #include <errno.h>
0011 #include <stddef.h>
0012 #include <stdio.h>
0013 #include <err.h>
0014 #include <string.h>
0015 #include <asm/ptrace-abi.h>
0016 #include <sys/auxv.h>
0017 
0018 /* Bitness-agnostic defines for user_regs_struct fields. */
0019 #ifdef __x86_64__
0020 # define user_syscall_nr    orig_rax
0021 # define user_arg0      rdi
0022 # define user_arg1      rsi
0023 # define user_arg2      rdx
0024 # define user_arg3      r10
0025 # define user_arg4      r8
0026 # define user_arg5      r9
0027 # define user_ip        rip
0028 # define user_ax        rax
0029 #else
0030 # define user_syscall_nr    orig_eax
0031 # define user_arg0      ebx
0032 # define user_arg1      ecx
0033 # define user_arg2      edx
0034 # define user_arg3      esi
0035 # define user_arg4      edi
0036 # define user_arg5      ebp
0037 # define user_ip        eip
0038 # define user_ax        eax
0039 #endif
0040 
0041 static int nerrs = 0;
0042 
0043 struct syscall_args32 {
0044     uint32_t nr, arg0, arg1, arg2, arg3, arg4, arg5;
0045 };
0046 
0047 #ifdef __i386__
0048 extern void sys32_helper(struct syscall_args32 *, void *);
0049 extern void int80_and_ret(void);
0050 #endif
0051 
0052 /*
0053  * Helper to invoke int80 with controlled regs and capture the final regs.
0054  */
0055 static void do_full_int80(struct syscall_args32 *args)
0056 {
0057 #ifdef __x86_64__
0058     register unsigned long bp asm("bp") = args->arg5;
0059     asm volatile ("int $0x80"
0060               : "+a" (args->nr),
0061             "+b" (args->arg0), "+c" (args->arg1), "+d" (args->arg2),
0062             "+S" (args->arg3), "+D" (args->arg4), "+r" (bp)
0063             : : "r8", "r9", "r10", "r11");
0064     args->arg5 = bp;
0065 #else
0066     sys32_helper(args, int80_and_ret);
0067 #endif
0068 }
0069 
0070 #ifdef __i386__
0071 static void (*vsyscall32)(void);
0072 
0073 /*
0074  * Nasty helper to invoke AT_SYSINFO (i.e. __kernel_vsyscall) with
0075  * controlled regs and capture the final regs.  This is so nasty that it
0076  * crashes my copy of gdb :)
0077  */
0078 static void do_full_vsyscall32(struct syscall_args32 *args)
0079 {
0080     sys32_helper(args, vsyscall32);
0081 }
0082 #endif
0083 
0084 static siginfo_t wait_trap(pid_t chld)
0085 {
0086     siginfo_t si;
0087     if (waitid(P_PID, chld, &si, WEXITED|WSTOPPED) != 0)
0088         err(1, "waitid");
0089     if (si.si_pid != chld)
0090         errx(1, "got unexpected pid in event\n");
0091     if (si.si_code != CLD_TRAPPED)
0092         errx(1, "got unexpected event type %d\n", si.si_code);
0093     return si;
0094 }
0095 
0096 static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
0097                int flags)
0098 {
0099     struct sigaction sa;
0100     memset(&sa, 0, sizeof(sa));
0101     sa.sa_sigaction = handler;
0102     sa.sa_flags = SA_SIGINFO | flags;
0103     sigemptyset(&sa.sa_mask);
0104     if (sigaction(sig, &sa, 0))
0105         err(1, "sigaction");
0106 }
0107 
0108 static void setsigign(int sig, int flags)
0109 {
0110     struct sigaction sa;
0111     memset(&sa, 0, sizeof(sa));
0112     sa.sa_sigaction = (void *)SIG_IGN;
0113     sa.sa_flags = flags;
0114     sigemptyset(&sa.sa_mask);
0115     if (sigaction(sig, &sa, 0))
0116         err(1, "sigaction");
0117 }
0118 
0119 static void clearhandler(int sig)
0120 {
0121     struct sigaction sa;
0122     memset(&sa, 0, sizeof(sa));
0123     sa.sa_handler = SIG_DFL;
0124     sigemptyset(&sa.sa_mask);
0125     if (sigaction(sig, &sa, 0))
0126         err(1, "sigaction");
0127 }
0128 
0129 #ifdef __x86_64__
0130 # define REG_BP REG_RBP
0131 #else
0132 # define REG_BP REG_EBP
0133 #endif
0134 
0135 static void empty_handler(int sig, siginfo_t *si, void *ctx_void)
0136 {
0137 }
0138 
0139 static void test_sys32_regs(void (*do_syscall)(struct syscall_args32 *))
0140 {
0141     struct syscall_args32 args = {
0142         .nr = 224,  /* gettid */
0143         .arg0 = 10, .arg1 = 11, .arg2 = 12,
0144         .arg3 = 13, .arg4 = 14, .arg5 = 15,
0145     };
0146 
0147     do_syscall(&args);
0148 
0149     if (args.nr != getpid() ||
0150         args.arg0 != 10 || args.arg1 != 11 || args.arg2 != 12 ||
0151         args.arg3 != 13 || args.arg4 != 14 || args.arg5 != 15) {
0152         printf("[FAIL]\tgetpid() failed to preserve regs\n");
0153         nerrs++;
0154     } else {
0155         printf("[OK]\tgetpid() preserves regs\n");
0156     }
0157 
0158     sethandler(SIGUSR1, empty_handler, 0);
0159 
0160     args.nr = 37;   /* kill */
0161     args.arg0 = getpid();
0162     args.arg1 = SIGUSR1;
0163     do_syscall(&args);
0164     if (args.nr != 0 ||
0165         args.arg0 != getpid() || args.arg1 != SIGUSR1 || args.arg2 != 12 ||
0166         args.arg3 != 13 || args.arg4 != 14 || args.arg5 != 15) {
0167         printf("[FAIL]\tkill(getpid(), SIGUSR1) failed to preserve regs\n");
0168         nerrs++;
0169     } else {
0170         printf("[OK]\tkill(getpid(), SIGUSR1) preserves regs\n");
0171     }
0172     clearhandler(SIGUSR1);
0173 }
0174 
0175 static void test_ptrace_syscall_restart(void)
0176 {
0177     printf("[RUN]\tptrace-induced syscall restart\n");
0178     pid_t chld = fork();
0179     if (chld < 0)
0180         err(1, "fork");
0181 
0182     if (chld == 0) {
0183         if (ptrace(PTRACE_TRACEME, 0, 0, 0) != 0)
0184             err(1, "PTRACE_TRACEME");
0185 
0186         pid_t pid = getpid(), tid = syscall(SYS_gettid);
0187 
0188         printf("\tChild will make one syscall\n");
0189         syscall(SYS_tgkill, pid, tid, SIGSTOP);
0190 
0191         syscall(SYS_gettid, 10, 11, 12, 13, 14, 15);
0192         _exit(0);
0193     }
0194 
0195     int status;
0196 
0197     /* Wait for SIGSTOP. */
0198     if (waitpid(chld, &status, 0) != chld || !WIFSTOPPED(status))
0199         err(1, "waitpid");
0200 
0201     struct user_regs_struct regs;
0202 
0203     printf("[RUN]\tSYSEMU\n");
0204     if (ptrace(PTRACE_SYSEMU, chld, 0, 0) != 0)
0205         err(1, "PTRACE_SYSEMU");
0206     wait_trap(chld);
0207 
0208     if (ptrace(PTRACE_GETREGS, chld, 0, &regs) != 0)
0209         err(1, "PTRACE_GETREGS");
0210 
0211     if (regs.user_syscall_nr != SYS_gettid ||
0212         regs.user_arg0 != 10 || regs.user_arg1 != 11 ||
0213         regs.user_arg2 != 12 || regs.user_arg3 != 13 ||
0214         regs.user_arg4 != 14 || regs.user_arg5 != 15) {
0215         printf("[FAIL]\tInitial args are wrong (nr=%lu, args=%lu %lu %lu %lu %lu %lu)\n", (unsigned long)regs.user_syscall_nr, (unsigned long)regs.user_arg0, (unsigned long)regs.user_arg1, (unsigned long)regs.user_arg2, (unsigned long)regs.user_arg3, (unsigned long)regs.user_arg4, (unsigned long)regs.user_arg5);
0216         nerrs++;
0217     } else {
0218         printf("[OK]\tInitial nr and args are correct\n");
0219     }
0220 
0221     printf("[RUN]\tRestart the syscall (ip = 0x%lx)\n",
0222            (unsigned long)regs.user_ip);
0223 
0224     /*
0225      * This does exactly what it appears to do if syscall is int80 or
0226      * SYSCALL64.  For SYSCALL32 or SYSENTER, though, this is highly
0227      * magical.  It needs to work so that ptrace and syscall restart
0228      * work as expected.
0229      */
0230     regs.user_ax = regs.user_syscall_nr;
0231     regs.user_ip -= 2;
0232     if (ptrace(PTRACE_SETREGS, chld, 0, &regs) != 0)
0233         err(1, "PTRACE_SETREGS");
0234 
0235     if (ptrace(PTRACE_SYSEMU, chld, 0, 0) != 0)
0236         err(1, "PTRACE_SYSEMU");
0237     wait_trap(chld);
0238 
0239     if (ptrace(PTRACE_GETREGS, chld, 0, &regs) != 0)
0240         err(1, "PTRACE_GETREGS");
0241 
0242     if (regs.user_syscall_nr != SYS_gettid ||
0243         regs.user_arg0 != 10 || regs.user_arg1 != 11 ||
0244         regs.user_arg2 != 12 || regs.user_arg3 != 13 ||
0245         regs.user_arg4 != 14 || regs.user_arg5 != 15) {
0246         printf("[FAIL]\tRestart nr or args are wrong (nr=%lu, args=%lu %lu %lu %lu %lu %lu)\n", (unsigned long)regs.user_syscall_nr, (unsigned long)regs.user_arg0, (unsigned long)regs.user_arg1, (unsigned long)regs.user_arg2, (unsigned long)regs.user_arg3, (unsigned long)regs.user_arg4, (unsigned long)regs.user_arg5);
0247         nerrs++;
0248     } else {
0249         printf("[OK]\tRestarted nr and args are correct\n");
0250     }
0251 
0252     printf("[RUN]\tChange nr and args and restart the syscall (ip = 0x%lx)\n",
0253            (unsigned long)regs.user_ip);
0254 
0255     regs.user_ax = SYS_getpid;
0256     regs.user_arg0 = 20;
0257     regs.user_arg1 = 21;
0258     regs.user_arg2 = 22;
0259     regs.user_arg3 = 23;
0260     regs.user_arg4 = 24;
0261     regs.user_arg5 = 25;
0262     regs.user_ip -= 2;
0263 
0264     if (ptrace(PTRACE_SETREGS, chld, 0, &regs) != 0)
0265         err(1, "PTRACE_SETREGS");
0266 
0267     if (ptrace(PTRACE_SYSEMU, chld, 0, 0) != 0)
0268         err(1, "PTRACE_SYSEMU");
0269     wait_trap(chld);
0270 
0271     if (ptrace(PTRACE_GETREGS, chld, 0, &regs) != 0)
0272         err(1, "PTRACE_GETREGS");
0273 
0274     if (regs.user_syscall_nr != SYS_getpid ||
0275         regs.user_arg0 != 20 || regs.user_arg1 != 21 || regs.user_arg2 != 22 ||
0276         regs.user_arg3 != 23 || regs.user_arg4 != 24 || regs.user_arg5 != 25) {
0277         printf("[FAIL]\tRestart nr or args are wrong (nr=%lu, args=%lu %lu %lu %lu %lu %lu)\n", (unsigned long)regs.user_syscall_nr, (unsigned long)regs.user_arg0, (unsigned long)regs.user_arg1, (unsigned long)regs.user_arg2, (unsigned long)regs.user_arg3, (unsigned long)regs.user_arg4, (unsigned long)regs.user_arg5);
0278         nerrs++;
0279     } else {
0280         printf("[OK]\tReplacement nr and args are correct\n");
0281     }
0282 
0283     if (ptrace(PTRACE_CONT, chld, 0, 0) != 0)
0284         err(1, "PTRACE_CONT");
0285     if (waitpid(chld, &status, 0) != chld)
0286         err(1, "waitpid");
0287     if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
0288         printf("[FAIL]\tChild failed\n");
0289         nerrs++;
0290     } else {
0291         printf("[OK]\tChild exited cleanly\n");
0292     }
0293 }
0294 
0295 static void test_restart_under_ptrace(void)
0296 {
0297     printf("[RUN]\tkernel syscall restart under ptrace\n");
0298     pid_t chld = fork();
0299     if (chld < 0)
0300         err(1, "fork");
0301 
0302     if (chld == 0) {
0303         if (ptrace(PTRACE_TRACEME, 0, 0, 0) != 0)
0304             err(1, "PTRACE_TRACEME");
0305 
0306         pid_t pid = getpid(), tid = syscall(SYS_gettid);
0307 
0308         printf("\tChild will take a nap until signaled\n");
0309         setsigign(SIGUSR1, SA_RESTART);
0310         syscall(SYS_tgkill, pid, tid, SIGSTOP);
0311 
0312         syscall(SYS_pause, 0, 0, 0, 0, 0, 0);
0313         _exit(0);
0314     }
0315 
0316     int status;
0317 
0318     /* Wait for SIGSTOP. */
0319     if (waitpid(chld, &status, 0) != chld || !WIFSTOPPED(status))
0320         err(1, "waitpid");
0321 
0322     struct user_regs_struct regs;
0323 
0324     printf("[RUN]\tSYSCALL\n");
0325     if (ptrace(PTRACE_SYSCALL, chld, 0, 0) != 0)
0326         err(1, "PTRACE_SYSCALL");
0327     wait_trap(chld);
0328 
0329     /* We should be stopped at pause(2) entry. */
0330 
0331     if (ptrace(PTRACE_GETREGS, chld, 0, &regs) != 0)
0332         err(1, "PTRACE_GETREGS");
0333 
0334     if (regs.user_syscall_nr != SYS_pause ||
0335         regs.user_arg0 != 0 || regs.user_arg1 != 0 ||
0336         regs.user_arg2 != 0 || regs.user_arg3 != 0 ||
0337         regs.user_arg4 != 0 || regs.user_arg5 != 0) {
0338         printf("[FAIL]\tInitial args are wrong (nr=%lu, args=%lu %lu %lu %lu %lu %lu)\n", (unsigned long)regs.user_syscall_nr, (unsigned long)regs.user_arg0, (unsigned long)regs.user_arg1, (unsigned long)regs.user_arg2, (unsigned long)regs.user_arg3, (unsigned long)regs.user_arg4, (unsigned long)regs.user_arg5);
0339         nerrs++;
0340     } else {
0341         printf("[OK]\tInitial nr and args are correct\n");
0342     }
0343 
0344     /* Interrupt it. */
0345     kill(chld, SIGUSR1);
0346 
0347     /* Advance.  We should be stopped at exit. */
0348     printf("[RUN]\tSYSCALL\n");
0349     if (ptrace(PTRACE_SYSCALL, chld, 0, 0) != 0)
0350         err(1, "PTRACE_SYSCALL");
0351     wait_trap(chld);
0352 
0353     if (ptrace(PTRACE_GETREGS, chld, 0, &regs) != 0)
0354         err(1, "PTRACE_GETREGS");
0355 
0356     if (regs.user_syscall_nr != SYS_pause ||
0357         regs.user_arg0 != 0 || regs.user_arg1 != 0 ||
0358         regs.user_arg2 != 0 || regs.user_arg3 != 0 ||
0359         regs.user_arg4 != 0 || regs.user_arg5 != 0) {
0360         printf("[FAIL]\tArgs after SIGUSR1 are wrong (nr=%lu, args=%lu %lu %lu %lu %lu %lu)\n", (unsigned long)regs.user_syscall_nr, (unsigned long)regs.user_arg0, (unsigned long)regs.user_arg1, (unsigned long)regs.user_arg2, (unsigned long)regs.user_arg3, (unsigned long)regs.user_arg4, (unsigned long)regs.user_arg5);
0361         nerrs++;
0362     } else {
0363         printf("[OK]\tArgs after SIGUSR1 are correct (ax = %ld)\n",
0364                (long)regs.user_ax);
0365     }
0366 
0367     /* Poke the regs back in.  This must not break anything. */
0368     if (ptrace(PTRACE_SETREGS, chld, 0, &regs) != 0)
0369         err(1, "PTRACE_SETREGS");
0370 
0371     /* Catch the (ignored) SIGUSR1. */
0372     if (ptrace(PTRACE_CONT, chld, 0, 0) != 0)
0373         err(1, "PTRACE_CONT");
0374     if (waitpid(chld, &status, 0) != chld)
0375         err(1, "waitpid");
0376     if (!WIFSTOPPED(status)) {
0377         printf("[FAIL]\tChild was stopped for SIGUSR1 (status = 0x%x)\n", status);
0378         nerrs++;
0379     } else {
0380         printf("[OK]\tChild got SIGUSR1\n");
0381     }
0382 
0383     /* The next event should be pause(2) again. */
0384     printf("[RUN]\tStep again\n");
0385     if (ptrace(PTRACE_SYSCALL, chld, 0, 0) != 0)
0386         err(1, "PTRACE_SYSCALL");
0387     wait_trap(chld);
0388 
0389     /* We should be stopped at pause(2) entry. */
0390 
0391     if (ptrace(PTRACE_GETREGS, chld, 0, &regs) != 0)
0392         err(1, "PTRACE_GETREGS");
0393 
0394     if (regs.user_syscall_nr != SYS_pause ||
0395         regs.user_arg0 != 0 || regs.user_arg1 != 0 ||
0396         regs.user_arg2 != 0 || regs.user_arg3 != 0 ||
0397         regs.user_arg4 != 0 || regs.user_arg5 != 0) {
0398         printf("[FAIL]\tpause did not restart (nr=%lu, args=%lu %lu %lu %lu %lu %lu)\n", (unsigned long)regs.user_syscall_nr, (unsigned long)regs.user_arg0, (unsigned long)regs.user_arg1, (unsigned long)regs.user_arg2, (unsigned long)regs.user_arg3, (unsigned long)regs.user_arg4, (unsigned long)regs.user_arg5);
0399         nerrs++;
0400     } else {
0401         printf("[OK]\tpause(2) restarted correctly\n");
0402     }
0403 
0404     /* Kill it. */
0405     kill(chld, SIGKILL);
0406     if (waitpid(chld, &status, 0) != chld)
0407         err(1, "waitpid");
0408 }
0409 
0410 int main()
0411 {
0412     printf("[RUN]\tCheck int80 return regs\n");
0413     test_sys32_regs(do_full_int80);
0414 
0415 #if defined(__i386__) && (!defined(__GLIBC__) || __GLIBC__ > 2 || __GLIBC_MINOR__ >= 16)
0416     vsyscall32 = (void *)getauxval(AT_SYSINFO);
0417     if (vsyscall32) {
0418         printf("[RUN]\tCheck AT_SYSINFO return regs\n");
0419         test_sys32_regs(do_full_vsyscall32);
0420     } else {
0421         printf("[SKIP]\tAT_SYSINFO is not available\n");
0422     }
0423 #endif
0424 
0425     test_ptrace_syscall_restart();
0426 
0427     test_restart_under_ptrace();
0428 
0429     return 0;
0430 }