Home | History | Annotate | Download | only in x86
      1 #define _GNU_SOURCE
      2 
      3 #include <sys/ptrace.h>
      4 #include <sys/types.h>
      5 #include <sys/wait.h>
      6 #include <sys/syscall.h>
      7 #include <sys/user.h>
      8 #include <unistd.h>
      9 #include <errno.h>
     10 #include <stddef.h>
     11 #include <stdio.h>
     12 #include <err.h>
     13 #include <string.h>
     14 #include <asm/ptrace-abi.h>
     15 #include <sys/auxv.h>
     16 
     17 /* Bitness-agnostic defines for user_regs_struct fields. */
     18 #ifdef __x86_64__
     19 # define user_syscall_nr	orig_rax
     20 # define user_arg0		rdi
     21 # define user_arg1		rsi
     22 # define user_arg2		rdx
     23 # define user_arg3		r10
     24 # define user_arg4		r8
     25 # define user_arg5		r9
     26 # define user_ip		rip
     27 # define user_ax		rax
     28 #else
     29 # define user_syscall_nr	orig_eax
     30 # define user_arg0		ebx
     31 # define user_arg1		ecx
     32 # define user_arg2		edx
     33 # define user_arg3		esi
     34 # define user_arg4		edi
     35 # define user_arg5		ebp
     36 # define user_ip		eip
     37 # define user_ax		eax
     38 #endif
     39 
     40 static int nerrs = 0;
     41 
     42 struct syscall_args32 {
     43 	uint32_t nr, arg0, arg1, arg2, arg3, arg4, arg5;
     44 };
     45 
     46 #ifdef __i386__
     47 extern void sys32_helper(struct syscall_args32 *, void *);
     48 extern void int80_and_ret(void);
     49 #endif
     50 
     51 /*
     52  * Helper to invoke int80 with controlled regs and capture the final regs.
     53  */
     54 static void do_full_int80(struct syscall_args32 *args)
     55 {
     56 #ifdef __x86_64__
     57 	register unsigned long bp asm("bp") = args->arg5;
     58 	asm volatile ("int $0x80"
     59 		      : "+a" (args->nr),
     60 			"+b" (args->arg0), "+c" (args->arg1), "+d" (args->arg2),
     61 			"+S" (args->arg3), "+D" (args->arg4), "+r" (bp));
     62 	args->arg5 = bp;
     63 #else
     64 	sys32_helper(args, int80_and_ret);
     65 #endif
     66 }
     67 
     68 #ifdef __i386__
     69 static void (*vsyscall32)(void);
     70 
     71 /*
     72  * Nasty helper to invoke AT_SYSINFO (i.e. __kernel_vsyscall) with
     73  * controlled regs and capture the final regs.  This is so nasty that it
     74  * crashes my copy of gdb :)
     75  */
     76 static void do_full_vsyscall32(struct syscall_args32 *args)
     77 {
     78 	sys32_helper(args, vsyscall32);
     79 }
     80 #endif
     81 
     82 static siginfo_t wait_trap(pid_t chld)
     83 {
     84 	siginfo_t si;
     85 	if (waitid(P_PID, chld, &si, WEXITED|WSTOPPED) != 0)
     86 		err(1, "waitid");
     87 	if (si.si_pid != chld)
     88 		errx(1, "got unexpected pid in event\n");
     89 	if (si.si_code != CLD_TRAPPED)
     90 		errx(1, "got unexpected event type %d\n", si.si_code);
     91 	return si;
     92 }
     93 
     94 static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
     95 		       int flags)
     96 {
     97 	struct sigaction sa;
     98 	memset(&sa, 0, sizeof(sa));
     99 	sa.sa_sigaction = handler;
    100 	sa.sa_flags = SA_SIGINFO | flags;
    101 	sigemptyset(&sa.sa_mask);
    102 	if (sigaction(sig, &sa, 0))
    103 		err(1, "sigaction");
    104 }
    105 
    106 static void setsigign(int sig, int flags)
    107 {
    108 	struct sigaction sa;
    109 	memset(&sa, 0, sizeof(sa));
    110 	sa.sa_sigaction = (void *)SIG_IGN;
    111 	sa.sa_flags = flags;
    112 	sigemptyset(&sa.sa_mask);
    113 	if (sigaction(sig, &sa, 0))
    114 		err(1, "sigaction");
    115 }
    116 
    117 static void clearhandler(int sig)
    118 {
    119 	struct sigaction sa;
    120 	memset(&sa, 0, sizeof(sa));
    121 	sa.sa_handler = SIG_DFL;
    122 	sigemptyset(&sa.sa_mask);
    123 	if (sigaction(sig, &sa, 0))
    124 		err(1, "sigaction");
    125 }
    126 
    127 #ifdef __x86_64__
    128 # define REG_BP REG_RBP
    129 #else
    130 # define REG_BP REG_EBP
    131 #endif
    132 
    133 static void empty_handler(int sig, siginfo_t *si, void *ctx_void)
    134 {
    135 }
    136 
    137 static void test_sys32_regs(void (*do_syscall)(struct syscall_args32 *))
    138 {
    139 	struct syscall_args32 args = {
    140 		.nr = 224,	/* gettid */
    141 		.arg0 = 10, .arg1 = 11, .arg2 = 12,
    142 		.arg3 = 13, .arg4 = 14, .arg5 = 15,
    143 	};
    144 
    145 	do_syscall(&args);
    146 
    147 	if (args.nr != getpid() ||
    148 	    args.arg0 != 10 || args.arg1 != 11 || args.arg2 != 12 ||
    149 	    args.arg3 != 13 || args.arg4 != 14 || args.arg5 != 15) {
    150 		printf("[FAIL]\tgetpid() failed to preseve regs\n");
    151 		nerrs++;
    152 	} else {
    153 		printf("[OK]\tgetpid() preserves regs\n");
    154 	}
    155 
    156 	sethandler(SIGUSR1, empty_handler, 0);
    157 
    158 	args.nr = 37;	/* kill */
    159 	args.arg0 = getpid();
    160 	args.arg1 = SIGUSR1;
    161 	do_syscall(&args);
    162 	if (args.nr != 0 ||
    163 	    args.arg0 != getpid() || args.arg1 != SIGUSR1 || args.arg2 != 12 ||
    164 	    args.arg3 != 13 || args.arg4 != 14 || args.arg5 != 15) {
    165 		printf("[FAIL]\tkill(getpid(), SIGUSR1) failed to preseve regs\n");
    166 		nerrs++;
    167 	} else {
    168 		printf("[OK]\tkill(getpid(), SIGUSR1) preserves regs\n");
    169 	}
    170 	clearhandler(SIGUSR1);
    171 }
    172 
    173 static void test_ptrace_syscall_restart(void)
    174 {
    175 	printf("[RUN]\tptrace-induced syscall restart\n");
    176 	pid_t chld = fork();
    177 	if (chld < 0)
    178 		err(1, "fork");
    179 
    180 	if (chld == 0) {
    181 		if (ptrace(PTRACE_TRACEME, 0, 0, 0) != 0)
    182 			err(1, "PTRACE_TRACEME");
    183 
    184 		printf("\tChild will make one syscall\n");
    185 		raise(SIGSTOP);
    186 
    187 		syscall(SYS_gettid, 10, 11, 12, 13, 14, 15);
    188 		_exit(0);
    189 	}
    190 
    191 	int status;
    192 
    193 	/* Wait for SIGSTOP. */
    194 	if (waitpid(chld, &status, 0) != chld || !WIFSTOPPED(status))
    195 		err(1, "waitpid");
    196 
    197 	struct user_regs_struct regs;
    198 
    199 	printf("[RUN]\tSYSEMU\n");
    200 	if (ptrace(PTRACE_SYSEMU, chld, 0, 0) != 0)
    201 		err(1, "PTRACE_SYSEMU");
    202 	wait_trap(chld);
    203 
    204 	if (ptrace(PTRACE_GETREGS, chld, 0, &regs) != 0)
    205 		err(1, "PTRACE_GETREGS");
    206 
    207 	if (regs.user_syscall_nr != SYS_gettid ||
    208 	    regs.user_arg0 != 10 || regs.user_arg1 != 11 ||
    209 	    regs.user_arg2 != 12 || regs.user_arg3 != 13 ||
    210 	    regs.user_arg4 != 14 || regs.user_arg5 != 15) {
    211 		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);
    212 		nerrs++;
    213 	} else {
    214 		printf("[OK]\tInitial nr and args are correct\n");
    215 	}
    216 
    217 	printf("[RUN]\tRestart the syscall (ip = 0x%lx)\n",
    218 	       (unsigned long)regs.user_ip);
    219 
    220 	/*
    221 	 * This does exactly what it appears to do if syscall is int80 or
    222 	 * SYSCALL64.  For SYSCALL32 or SYSENTER, though, this is highly
    223 	 * magical.  It needs to work so that ptrace and syscall restart
    224 	 * work as expected.
    225 	 */
    226 	regs.user_ax = regs.user_syscall_nr;
    227 	regs.user_ip -= 2;
    228 	if (ptrace(PTRACE_SETREGS, chld, 0, &regs) != 0)
    229 		err(1, "PTRACE_SETREGS");
    230 
    231 	if (ptrace(PTRACE_SYSEMU, chld, 0, 0) != 0)
    232 		err(1, "PTRACE_SYSEMU");
    233 	wait_trap(chld);
    234 
    235 	if (ptrace(PTRACE_GETREGS, chld, 0, &regs) != 0)
    236 		err(1, "PTRACE_GETREGS");
    237 
    238 	if (regs.user_syscall_nr != SYS_gettid ||
    239 	    regs.user_arg0 != 10 || regs.user_arg1 != 11 ||
    240 	    regs.user_arg2 != 12 || regs.user_arg3 != 13 ||
    241 	    regs.user_arg4 != 14 || regs.user_arg5 != 15) {
    242 		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);
    243 		nerrs++;
    244 	} else {
    245 		printf("[OK]\tRestarted nr and args are correct\n");
    246 	}
    247 
    248 	printf("[RUN]\tChange nr and args and restart the syscall (ip = 0x%lx)\n",
    249 	       (unsigned long)regs.user_ip);
    250 
    251 	regs.user_ax = SYS_getpid;
    252 	regs.user_arg0 = 20;
    253 	regs.user_arg1 = 21;
    254 	regs.user_arg2 = 22;
    255 	regs.user_arg3 = 23;
    256 	regs.user_arg4 = 24;
    257 	regs.user_arg5 = 25;
    258 	regs.user_ip -= 2;
    259 
    260 	if (ptrace(PTRACE_SETREGS, chld, 0, &regs) != 0)
    261 		err(1, "PTRACE_SETREGS");
    262 
    263 	if (ptrace(PTRACE_SYSEMU, chld, 0, 0) != 0)
    264 		err(1, "PTRACE_SYSEMU");
    265 	wait_trap(chld);
    266 
    267 	if (ptrace(PTRACE_GETREGS, chld, 0, &regs) != 0)
    268 		err(1, "PTRACE_GETREGS");
    269 
    270 	if (regs.user_syscall_nr != SYS_getpid ||
    271 	    regs.user_arg0 != 20 || regs.user_arg1 != 21 || regs.user_arg2 != 22 ||
    272 	    regs.user_arg3 != 23 || regs.user_arg4 != 24 || regs.user_arg5 != 25) {
    273 		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);
    274 		nerrs++;
    275 	} else {
    276 		printf("[OK]\tReplacement nr and args are correct\n");
    277 	}
    278 
    279 	if (ptrace(PTRACE_CONT, chld, 0, 0) != 0)
    280 		err(1, "PTRACE_CONT");
    281 	if (waitpid(chld, &status, 0) != chld)
    282 		err(1, "waitpid");
    283 	if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
    284 		printf("[FAIL]\tChild failed\n");
    285 		nerrs++;
    286 	} else {
    287 		printf("[OK]\tChild exited cleanly\n");
    288 	}
    289 }
    290 
    291 static void test_restart_under_ptrace(void)
    292 {
    293 	printf("[RUN]\tkernel syscall restart under ptrace\n");
    294 	pid_t chld = fork();
    295 	if (chld < 0)
    296 		err(1, "fork");
    297 
    298 	if (chld == 0) {
    299 		if (ptrace(PTRACE_TRACEME, 0, 0, 0) != 0)
    300 			err(1, "PTRACE_TRACEME");
    301 
    302 		printf("\tChild will take a nap until signaled\n");
    303 		setsigign(SIGUSR1, SA_RESTART);
    304 		raise(SIGSTOP);
    305 
    306 		syscall(SYS_pause, 0, 0, 0, 0, 0, 0);
    307 		_exit(0);
    308 	}
    309 
    310 	int status;
    311 
    312 	/* Wait for SIGSTOP. */
    313 	if (waitpid(chld, &status, 0) != chld || !WIFSTOPPED(status))
    314 		err(1, "waitpid");
    315 
    316 	struct user_regs_struct regs;
    317 
    318 	printf("[RUN]\tSYSCALL\n");
    319 	if (ptrace(PTRACE_SYSCALL, chld, 0, 0) != 0)
    320 		err(1, "PTRACE_SYSCALL");
    321 	wait_trap(chld);
    322 
    323 	/* We should be stopped at pause(2) entry. */
    324 
    325 	if (ptrace(PTRACE_GETREGS, chld, 0, &regs) != 0)
    326 		err(1, "PTRACE_GETREGS");
    327 
    328 	if (regs.user_syscall_nr != SYS_pause ||
    329 	    regs.user_arg0 != 0 || regs.user_arg1 != 0 ||
    330 	    regs.user_arg2 != 0 || regs.user_arg3 != 0 ||
    331 	    regs.user_arg4 != 0 || regs.user_arg5 != 0) {
    332 		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);
    333 		nerrs++;
    334 	} else {
    335 		printf("[OK]\tInitial nr and args are correct\n");
    336 	}
    337 
    338 	/* Interrupt it. */
    339 	kill(chld, SIGUSR1);
    340 
    341 	/* Advance.  We should be stopped at exit. */
    342 	printf("[RUN]\tSYSCALL\n");
    343 	if (ptrace(PTRACE_SYSCALL, chld, 0, 0) != 0)
    344 		err(1, "PTRACE_SYSCALL");
    345 	wait_trap(chld);
    346 
    347 	if (ptrace(PTRACE_GETREGS, chld, 0, &regs) != 0)
    348 		err(1, "PTRACE_GETREGS");
    349 
    350 	if (regs.user_syscall_nr != SYS_pause ||
    351 	    regs.user_arg0 != 0 || regs.user_arg1 != 0 ||
    352 	    regs.user_arg2 != 0 || regs.user_arg3 != 0 ||
    353 	    regs.user_arg4 != 0 || regs.user_arg5 != 0) {
    354 		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);
    355 		nerrs++;
    356 	} else {
    357 		printf("[OK]\tArgs after SIGUSR1 are correct (ax = %ld)\n",
    358 		       (long)regs.user_ax);
    359 	}
    360 
    361 	/* Poke the regs back in.  This must not break anything. */
    362 	if (ptrace(PTRACE_SETREGS, chld, 0, &regs) != 0)
    363 		err(1, "PTRACE_SETREGS");
    364 
    365 	/* Catch the (ignored) SIGUSR1. */
    366 	if (ptrace(PTRACE_CONT, chld, 0, 0) != 0)
    367 		err(1, "PTRACE_CONT");
    368 	if (waitpid(chld, &status, 0) != chld)
    369 		err(1, "waitpid");
    370 	if (!WIFSTOPPED(status)) {
    371 		printf("[FAIL]\tChild was stopped for SIGUSR1 (status = 0x%x)\n", status);
    372 		nerrs++;
    373 	} else {
    374 		printf("[OK]\tChild got SIGUSR1\n");
    375 	}
    376 
    377 	/* The next event should be pause(2) again. */
    378 	printf("[RUN]\tStep again\n");
    379 	if (ptrace(PTRACE_SYSCALL, chld, 0, 0) != 0)
    380 		err(1, "PTRACE_SYSCALL");
    381 	wait_trap(chld);
    382 
    383 	/* We should be stopped at pause(2) entry. */
    384 
    385 	if (ptrace(PTRACE_GETREGS, chld, 0, &regs) != 0)
    386 		err(1, "PTRACE_GETREGS");
    387 
    388 	if (regs.user_syscall_nr != SYS_pause ||
    389 	    regs.user_arg0 != 0 || regs.user_arg1 != 0 ||
    390 	    regs.user_arg2 != 0 || regs.user_arg3 != 0 ||
    391 	    regs.user_arg4 != 0 || regs.user_arg5 != 0) {
    392 		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);
    393 		nerrs++;
    394 	} else {
    395 		printf("[OK]\tpause(2) restarted correctly\n");
    396 	}
    397 
    398 	/* Kill it. */
    399 	kill(chld, SIGKILL);
    400 	if (waitpid(chld, &status, 0) != chld)
    401 		err(1, "waitpid");
    402 }
    403 
    404 int main()
    405 {
    406 	printf("[RUN]\tCheck int80 return regs\n");
    407 	test_sys32_regs(do_full_int80);
    408 
    409 #if defined(__i386__) && (!defined(__GLIBC__) || __GLIBC__ > 2 || __GLIBC_MINOR__ >= 16)
    410 	vsyscall32 = (void *)getauxval(AT_SYSINFO);
    411 	printf("[RUN]\tCheck AT_SYSINFO return regs\n");
    412 	test_sys32_regs(do_full_vsyscall32);
    413 #endif
    414 
    415 	test_ptrace_syscall_restart();
    416 
    417 	test_restart_under_ptrace();
    418 
    419 	return 0;
    420 }
    421