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