/* * breakout.c * * Testtool for UML * This tool tries to breakout from UML or to crash UML * Hopefully it has no success! * * Copyright (C) 2004 Fujitsu Siemens Computers GmbH * Author: Bodo Stroesser (bodo.stroesser@fujitsu-siemens.com) * * Licensed under the GPL */ #define VERSION_STRING "breakout V1.0" #include #include #include #include #include #include #include #include #include #include #include volatile int * stepme; int parent_fn( pid_t child) { int ret, status; unsigned long code, eip; static int count = 0; printf("Parent: childs PID is %d\n", child); while (1) { ret = waitpid( child, &status, 0); if ( ret != child ) { fprintf( stderr, "Parent: "); perror("waitpid"); exit(1); } if ( WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP ) { errno = 0; eip = ptrace( PTRACE_PEEKUSER, child, (void *)(EIP*4), (void *)0); if ( errno ) { fprintf( stderr, "Parent: "); perror("ptrace( PTRACE_PEEKUSER, child, EIP, 0)"); exit(1); } printf("Parent: eip = 0x%08x, ", eip); code = ptrace( PTRACE_PEEKTEXT, child, eip, (void *)0); if ( errno ) printf("ptrace( PTRACE_PEEKTEXT, child, eip, 0): %s", strerror( errno)); printf("code = %02x %02x %02x %02x, ", code&0xff, (code>>8)&0xff, (code>>16)&0xff, (code>>24)&0xff); if ( (code & 0xffff) == 0x80cd || (code & 0xffff) == 0x340f ) printf("SYSCALL!, "); else printf(" "); if ( *stepme == 2 ) { if ( ++count == 3 ) { errno = 0; ptrace( PTRACE_POKEUSER, child, (void *)(EIP*4), (void *)(eip-2)); if ( errno ) { fprintf( stderr, "Parent: "); perror("ptrace( PTRACE_POKEUSER, child, EIP, eip-2)"); exit(1); } ptrace( PTRACE_POKEUSER, child, (void *)(EAX*4), (void *)__NR_getpid); if ( errno ) { fprintf( stderr, "Parent: "); perror("ptrace( PTRACE_POKEUSER, child, EAX, __NR_getpid)"); exit(1); } *stepme = 1; } else { printf("Doing SYSCALL_TRACE ...\n"); fflush( stdout); if ( ptrace( PTRACE_SYSCALL, child, (void *)0, (void *)0) < 0 ) { fprintf( stderr, "Parent: "); perror("ptrace( PTRACE_SYSCALL, child, 0, 0)"); exit(1); } } } if ( *stepme == 1 ) { printf("Doing SINGLESTEP ...\n"); fflush( stdout); if ( ptrace( PTRACE_SINGLESTEP, child, (void *)0, (void *)0) < 0 ) { fprintf( stderr, "Parent: "); perror("ptrace( PTRACE_SINGLESTEP, child, 0, 0)"); exit(1); } } else if ( *stepme == 0 ) { printf("Doing CONTINUE ...\n"); fflush( stdout); if ( ptrace( PTRACE_CONT, child, (void *)0, (void *)0) < 0 ) { fprintf( stderr, "Parent: "); perror("ptrace( PTRACE_CONT, child, 0, 0)"); exit(1); } count = 0; } } else { printf("\nParent: Childs status is %x: exiting\n", status); return (status != 0); } } } int getpid_sysenter(void) { long res; __asm__ volatile (" call 1f\n\t" " jmp 2f\n\t" "1: push %%ecx\n\t" " push %%edx\n\t" " push %%ebp\n\t" " movl %%esp,%%ebp\n\t" " sysenter\n\t" /* Note: the following code is for information only! * It is never executed, since the kernel jumps to the * vsyscall-page on return. This is hard-coded, because * sysenter dosn't save a return address. */ " pop %%ebp\n\t" " pop %%edx\n\t" " pop %%ecx\n\t" " ret\n\t" "2: " : "=a" (res) : "0" (__NR_getpid)); return res; } int getpid_int0x80(void) { long res; __asm__ volatile (" int $0x80\n\t" : "=a" (res) : "0" (__NR_getpid)); } int getpid_vsyscall(void) { long res; __asm__ volatile (" call 0xffffe400\n\t" : "=a" (res) : "0" (__NR_getpid)); return res; } int child_fn( void) { pid_t pid, mypid = getpid(); printf(" Child: untraced: my PID is %d\n", mypid); fflush( stdout); if ( ptrace( PTRACE_TRACEME, 0, (void *)0, (void *)0) ) { fprintf( stderr, " Child: "); perror("ptrace( PTRACE_TRACEME, 0, 0, 0)"); exit(1); } kill( mypid, SIGTRAP); printf("\n========== Test Case 1: singlestep sycall via sysenter ==========\n\n"); fflush( stdout); *stepme = 1; kill( mypid, SIGTRAP); pid = getpid_sysenter(); *stepme = 0; printf(" Child: traced: my PID is %d\n", pid); fflush( stdout); if ( pid != mypid ) return 1; printf("\n========== Test Case 2: singlestep sycall via int $0x80 ==========\n\n"); fflush( stdout); *stepme = 1; kill( mypid, SIGTRAP); pid = getpid_int0x80(); *stepme = 0; printf(" Child: traced: my PID is %d\n", pid); fflush( stdout); if ( pid != mypid ) return 1; printf("\n========== Test Case 3: singlestep sycall via vsyscall-page ==========\n\n"); fflush( stdout); *stepme = 1; kill( mypid, SIGTRAP); pid = getpid_vsyscall(); *stepme = 0; printf(" Child: traced: my PID is %d\n", pid); fflush( stdout); if ( pid != mypid ) return 1; printf("\n========== Test Case 4: trace sycall via int $0x80 (twice) ==========\n\n"); fflush( stdout); *stepme = 2; kill( mypid, SIGTRAP); pid = getpid_int0x80(); *stepme = 0; printf(" Child: traced: my PID is %d\n", pid); fflush( stdout); if ( pid != mypid ) return 1; return 0; } int main( void) { int res; pid_t child; printf(VERSION_STRING "\n"); stepme = mmap( NULL, 4096, PROT_WRITE|PROT_READ, MAP_SHARED|MAP_ANONYMOUS, 0, 0); if ( stepme == MAP_FAILED ) { perror("mmap"); exit(1); } printf("Parent: my PID is %d\n", getpid()); fflush( stdout); child = fork(); if ( child < 0 ) { perror("fork"); exit(1); } else if ( child ) { res = parent_fn( child); if ( res ) return res; printf("\n========== Test Case 5: syscall(-1) ==========\n\n"); __asm__ volatile (" int $0x80\n\t" : "=a" (res) : "0" (-1)); printf("Syscall( -1) returns %d\n", res); if ( res != -ENOSYS ) { printf("Syscall( -1): wrong result!!!!!\n"); return 1; } printf("\n========== Test ended normally ==========\n"); return 0; } else { res = child_fn(); if ( res ) printf(" Child: pid != traced pid --> BREAKOUT!!!\n"); return res; } }