All of lore.kernel.org
 help / color / mirror / Atom feed
* [uml-devel] Testtools
@ 2004-10-29 14:43 Bodo Stroesser
  0 siblings, 0 replies; only message in thread
From: Bodo Stroesser @ 2004-10-29 14:43 UTC (permalink / raw)
  To: Jeff Dike; +Cc: BlaisorBlade, user-mode-linux-devel

[-- Attachment #1: Type: text/plain, Size: 1017 bytes --]

As promised, here are the next test tools.

interrupted_syscall.c checks, whether syscall restarting is correctly
done for
- nanosleep(), which returns -ERESTART_RESTARTBLOCK on interruption
- read(), which returns -ERESTARTSYS on interruption.
- sigsuspend(), which does do_signal() while being in the syscall and/or
   on return to user.
The test sets SIG_IGN or a signal handler (nanosleep, read) and it tests
with and without SA_RESTART (read). And it does the test twice, once the
test running normal, and one with the test being PTRACE_SYSCALL'ed.
At least for SIG_IGN, this makes a difference.

kernel_restorer.c uses a directly coded call to sys_rt_sigaction(). By this
way, it can force the kernel to use it's own restorer-stub (For UML at the
moment, this is the code on the stack). It does this for both possible
stack-layouts / restorers.
A further test done by this tool is the exploit for sigreturn() doing a
wrong systemcall restart handling. This case is done for sigreturn() and
rt_sigreturn()

Bodo

[-- Attachment #2: interrupted_syscall.c --]
[-- Type: text/plain, Size: 6980 bytes --]

/*
 *  interrupted_syscall.c
 *
 *  Testtool for UML
 *  This tool tests several cases of interrupted syscalls and
 *  checks for the syscall restarting being correctly done.
 *
 *  Copyright (C) 2004 Fujitsu Siemens Computers GmbH
 *  Author: Bodo Stroesser (bodo.stroesser@fujitsu-siemens.com)
 *
 *  Licensed under the GPL
 */

#include <stdio.h>
#include <time.h>
#include <signal.h>
#include <errno.h>
#include <sys/ptrace.h>
#include <asm/ptrace.h>
#include <sys/wait.h>
#include <sys/mman.h>

int child, tester;
int traced;
int pipe_fd[2];

volatile int * counterp;

char outbuf[512], *p;

void sighdlr( int sig)
{
	if ( ! traced ) {
		printf("Signal %d caught\n", sig);
	}
}

void alarm_writer( int parent, int count)
{
	while ( count-- ) {
		sleep( 1);
		if ( kill( parent, SIGALRM) ) {
			perror("kill( parent, SIGALRM)");
			exit(1);
		}
	}
	if ( pipe_fd[0] == -1 )
		goto out;

	sleep( 1);
	close( pipe_fd[0]);
	if ( write( pipe_fd[1], "X", 1) != 1 ) {
		perror("write( pipe_fd[1], \"X\", 1)");
		exit(1);
	}
    out:
	sleep(60);
	exit(0);
}

void
prepare( int mkpipe, int count, void * hdlr, int flags)
{
	struct sigaction sa;
	int ret;
	int pid = getpid();

	if ( mkpipe ) {
		if ( pipe( pipe_fd) ) {
			perror("pipe( pipe_fd)");
			exit(1);
		}
	} else 
		pipe_fd[0] = pipe_fd[1] = -1;

	memset( &sa, 0, sizeof( sa));
	sa.sa_handler = hdlr;
	sa.sa_flags = flags;
	if ( sigaction( SIGALRM, &sa, NULL) ) {
		perror("sigaction( SIGALRM, &sa)");
		exit(1);
	}
	if ( (child = fork()) < 0 ) {
		perror("fork()");
		exit(1);
	}
	else if ( !child )
		alarm_writer( pid, count); /* Child will never return */

	if ( mkpipe ) {
		close( pipe_fd[1]);
		pipe_fd[1] = -1;
	}
	counterp[0] = 0;
	counterp[1] = 0;
	if ( hdlr == SIG_IGN )
		counterp[2] = 0;
	else
		counterp[2] = 1;

	p = outbuf;
}

void
cleanup( int ok, int syscalls, int signals)
{
	int status;

	int syscall_count = counterp[0];
	int signal_count = counterp[1];

	printf( outbuf);

	if ( pipe_fd[0] != -1 )
		close( pipe_fd[0]);
	if ( pipe_fd[1] != -1 )
		close( pipe_fd[1]);

	kill( child, SIGKILL);

	waitpid( child, &status, 0);

	if ( traced ) {
		printf("Syscalls: %d, Signals: %d ==> ", syscall_count, signal_count);
		if ( syscall_count != syscalls || signal_count != signals ) {
			ok = 0;
			printf(" ERROR!\nExpected: Syscalls: %d Signals: %d\n", syscalls, signals);
		}
		else
			printf("OK.\n");
	}

	if ( ! ok ) {
		printf("Test failed!\n");
		exit(1);
	}
	printf("Test succeeded\n\n");
}


int
tester_fn( void)
{
	struct timespec ts, ts2;
	sigset_t set;
	char buf[10];
	int ret, errno_sav;

	sigprocmask( SIG_BLOCK, NULL, &set);


	printf("\n=====> Test case 1: nanosleep() 3.5 seconds, SIGALRM after 1 and 2 seconds, SIG_IGN\n");

	prepare( 0, 2, SIG_IGN, 0);

	ts = (struct timespec){ 3, 500000000};
	ret = nanosleep( &ts, &ts2);
	if ( ret ) {
		p+=sprintf(p, "ERROR: nanosleep(): %s", strerror( errno));
		p+=sprintf(p, ";  remaining time=%ld/%ld\n", ts2.tv_sec, ts2.tv_nsec);
	}
	else
		p+=sprintf(p, "OK: nanosleep() returns OK.\n");

	cleanup( ret == 0 , 6, 2);


	printf("\n=====> Test case 2: nanosleep() 3.5 seconds, SIGALRM after 1 and 2 seconds, signal handler\n");

	prepare( 0, 2, sighdlr, 0);

	ts = (struct timespec){ 3, 500000000};
	ret = nanosleep( &ts, &ts2);
	if ( ret ) {
		p+=sprintf(p, "OK: nanosleep(): %s", strerror( errno));
		p+=sprintf(p, ";  remaining time=%ld/%ld\n", ts2.tv_sec, ts2.tv_nsec);
	}
	else
		p+=sprintf(p, "ERROR: nanosleep() returns OK.\n");

	cleanup( ret && ts2.tv_sec == 2 , 4, 1);


	printf("\n=====> Test case 3: read(), SIGALRM after 1 and 2 seconds, 1 Byte after 3rd second, SIG_IGN\n");

	prepare( 1, 2, SIG_IGN, 0);

	ret = read( pipe_fd[0], buf, sizeof( buf));
	if ( ret != 1 )
		p+=sprintf(p, "ERROR: read( pipe_fd[0], buf, sizeof(buf)): %s\n", strerror( errno));
	else 
		p+=sprintf(p, "OK: read received %d bytes\n", ret);

	cleanup( ret == 1 , 6, 2);


	printf("\n=====> Test case 4: read(), SIGALRM after 1 and 2 seconds, 1 Byte after 3rd second, signal handler, SA_RESTART\n");

	prepare( 1, 2, sighdlr, SA_RESTART);

	ret = read( pipe_fd[0], buf, sizeof( buf));
	if ( ret != 1 )
		p+=sprintf(p, "ERROR: read( pipe_fd[0], buf, sizeof(buf)): %s\n", strerror( errno));
	else 
		p+=sprintf(p, "OK: read received %d bytes\n", ret);

	cleanup( ret == 1 , 10, 2);


	printf("\n=====> Test case 5: read(), SIGALRM after 1 second, 1 Byte after 2nd second, signal handler, ! SA_RESTART\n");

	prepare( 1, 1, sighdlr, 0);

	ret = read( pipe_fd[0], buf, sizeof( buf));
	errno_sav = errno;
	if ( ret != 1 )
		p+=sprintf(p, "OK: read( pipe_fd[0], buf, sizeof(buf)): %s\n", strerror( errno));
	else 
		p+=sprintf(p, "ERROR: read received %d bytes\n", ret);

	cleanup( ret == -1 && errno_sav == EINTR , 4, 1);


	printf("\n=====> Test case 6: sigsuspend(), SIGALRM after 1 second, signal handler, SA_RESTART\n");

	prepare( 0, 1, sighdlr, SA_RESTART);

	ret = sigsuspend( &set);
	errno_sav = errno;
	if ( ret < 0 )
		p+=sprintf(p, "OK: sigsuspend(): %s\n", strerror( errno));
	else 
		p+=sprintf(p, "ERROR: sigsuspend returns OK.\n");

	cleanup( ret == -1 && errno_sav == EINTR , 4, 1);
}


int
main( int argc, char **argv)
{
	int status, ret, sig, debug;
	sigset_t set;

	sigprocmask( SIG_BLOCK, NULL, &set);
	sigdelset( &set, SIGCHLD);
	sigprocmask( SIG_SETMASK, NULL, &set);

	counterp = mmap( NULL, 4096, PROT_WRITE|PROT_READ, MAP_SHARED|MAP_ANONYMOUS, 0, 0);
	if ( counterp == MAP_FAILED ) {
		perror("mmap");
		exit(1);
	}

	debug = argc > 1 && !strcmp("debug",argv[1]);

	tester = fork();
	if ( tester < 0 ) {
		perror("fork()");
		exit(1);
	}
	if ( ! tester ) {
		printf("\n\nStarting Test: UNTRACED\n\n");
		traced = 0;
		tester_fn();
		if ( ptrace( PTRACE_TRACEME, 0, (void *)0, (void *)0) ) {
			perror("ptrace( PTRACE_TRACEME, 0, 0, 0)");
			exit(1);
		}
		printf("\nStarting Test: TRACED\n");
		traced = 1;
		kill( getpid(), SIGTRAP);
		tester_fn();
		exit(0);
	}

	while (1) {
		ret = waitpid( tester, &status, 0);
		if ( ret != tester ) {
			fprintf( stderr, "Tracer: ");
			perror("waitpid");
			exit(1);
		}
		if ( WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP ) {
			sig = 0;
			counterp[0]++;
			if ( debug )
				printf("TRAP: syscall-# = %d, count = %d\n",
				       ptrace( PTRACE_PEEKUSER, tester, (void *)(ORIG_EAX*4), NULL),
				       counterp[0]);
		}
		else if ( WIFSTOPPED(status) && WSTOPSIG(status) == SIGALRM ) {
			if ( debug )
				printf("SIGCHLD !!!!!\n");
			sig = SIGALRM;
			if ( counterp[2] )
				printf("Signal %d caught\n", sig);
			else
				printf("Signal %d ignored\n", sig);
			counterp[1]++;
		}
		else if ( WIFSTOPPED(status) && WSTOPSIG(status) == SIGCHLD ) {
			if ( debug )
				printf("SIGCHLD !!!!!\n");
			sig = SIGCHLD;
		}
		else {
			printf("\nTracer: Tester's status is %x: exiting\n", status);
			return (status != 0);
		}
		if ( ptrace( PTRACE_SYSCALL, tester, (void *)0, sig) < 0 ) {
			fprintf( stderr, "Parent: ");
			perror("ptrace( PTRACE_SYSCALL, tester, 0, sig)");
			exit(1);
		}
	}
}

[-- Attachment #3: kernel_restorer.c --]
[-- Type: text/plain, Size: 2954 bytes --]

/*
 *  kernel_restorer.c
 *
 *  Testtool for UML
 *  This tool tests the kernels restorer code stubs.
 *  Also it checks, whether syscall restarting is skipped on
 *  return from sys_sigreturn() and sys_rt_sigreturn()
 *
 *  Copyright (C) 2004 Fujitsu Siemens Computers GmbH
 *  Author: Bodo Stroesser (bodo.stroesser@fujitsu-siemens.com)
 *
 *  Licensed under the GPL
 */

#define __KERNEL__
#include <asm/signal.h>
#include <asm/siginfo.h>
#include <asm/sigcontext.h>
#include <asm/ucontext.h>
#include <unistd.h>
#include <errno.h>
#include <asm/unistd.h>

int counter;
int mod_eip;

void sighdlr( int sig)
{
	struct sigcontext * sc = (struct sigcontext *)(&sig + 1);

	printf("sighdlr(): signal %d caught\n", sig);
	if ( --counter )
		alarm(1);
	else if ( mod_eip )
		sc->eip -= 2;
}


void rt_sighdlr( int sig, siginfo_t * info, struct ucontext * uc)
{
	printf("rt_sighdlr(): signal %d caught\n", sig);
	if ( --counter )
		alarm(1);
	else if ( mod_eip )
		uc->uc_mcontext.eip -= 2;
}

_syscall4( int, rt_sigaction, int, signr, struct k_sigaction *, new, struct k_sigaction *, old, int, size)


void
prepare( struct k_sigaction *sa, void * hdlr, int count)
{
	int ret;

	counter = count;
	sa->sa.sa_handler = hdlr;
	sa->sa.sa_flags = SA_RESTART;
	if ( hdlr == rt_sighdlr )
		sa->sa.sa_flags |= SA_SIGINFO;
	ret = rt_sigaction( SIGALRM, sa, NULL, sizeof( sigset_t));
	if ( ret ) {
		perror("rt_sigaction()");
		exit(1);
	}
	
	alarm(1);
}


void
test_suspend( void)
{
	int ret, errno_sav;
	sigset_t set;

	memset( &set, 0, sizeof( sigset_t));

	ret = sigsuspend( &set);
	if ( ret ) {
		errno_sav=errno;
		perror("sigsuspend()");
		if ( errno_sav != EINTR ) {
			printf("ERROR: sigsuspend() didn't return -EINTR\n");
			exit(1);
		}
	} else {
		printf("ERROR: sigsuspend() returns O.K. --> this should never happen!\n");
		exit(1);
	}

	if ( counter ) {
		printf("ERROR: signal counter not counted down to 0, is %d\n", counter);
		exit(1);
	}
	printf("==> OK.\n");
}


void
test_loop( void)
{
	__asm__ volatile ("	call 1f	\n"
			"	jmp  2f	\n"
			"	ret	\n"
			"	nop	\n"
			"1:	jmp  1b	\n"
			"2:		\n"
				: : "a" (-ERESTARTSYS));

	alarm(0);

	if ( counter ) {
		printf("ERROR: signal counter not counted down to 0, is %d\n", counter);
		exit(1);
	}
	printf("==> OK.\n");
}


int
main( void)
{
	struct k_sigaction sa;

	sa.sa.sa_restorer = NULL;
	memset( sa.sa.sa_mask.sig, 0xff, sizeof( sigset_t));

	mod_eip = 0;

	printf("\nTesting kernels restorer for sigreturn(): this simply shouldn't crash\n\n");

	prepare( &sa, sighdlr, 1);

	test_suspend();

	printf("\nTesting kernels restorer for rt_sigreturn(): this simply shouldn't crash\n\n");

	prepare( &sa, rt_sighdlr, 1);

	test_suspend();

	mod_eip = 1;

	printf("\nTesting correct returncode-handling for sigreturn()\n");

	prepare( &sa, sighdlr, 3);

	test_loop();

	printf("\nTesting correct returncode-handling for rt_sigreturn()\n");

	prepare( &sa, rt_sighdlr, 3);

	test_loop();

	return 0;
}

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2004-10-29 14:44 UTC | newest]

Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2004-10-29 14:43 [uml-devel] Testtools Bodo Stroesser

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.