Problem with using PT_SYSCALL.

Артем Анисимов <[email protected]> Tue, 6 Jul 2010 17:55:38 +0400
Newsgroups gmane.os.netbsd.devel.general
Message-ID <[email protected]>
Hi!

  I've been experimenting with ptrace() and found out that simply following 
man (2) ptrace is not enough.

  The attached problem is supposed to run "/bin/ls /" and stop at every system 
call it makes. The problem is that ptrace(PT_SYSCALL,...) at the end of the 
main loop seems simply to resume the child and make it run until it exits, but 
not until it makes a system call.

  Attached program works as expected under FreeBSD and Linux, so I believe 
there is some detail that needs to be taken into account for NetBSD and that I 
have missed.

  Can you please give a hint how to correct this program?

-------------------------------

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ptrace.h>
#include <errno.h>

#if defined(BSD)
	#define	REQ_TRACE_ME	PT_TRACE_ME
	#define	REQ_SYSCALL		PT_SYSCALL
	#define	REQ_KILL		PT_KILL
	#define	CONT_ADDR		(caddr_t)1
#elif defined(LINUX)
	#define	REQ_TRACE_ME	PTRACE_TRACEME
	#define	REQ_SYSCALL		PTRACE_SYSCALL
	#define	REQ_KILL		PTRACE_KILL
	#define	CONT_ADDR		NULL
#else
	#error "Define BSD or LINUX."
#endif

static void die(const char *msg)
{
	printf("%s, errno=%d\n",msg,errno);
	_exit(1);
}

static void child()
{
const char	*args[]	=	{"/bin/ls","/",NULL};
const char	*env[]	=	{NULL};

	if(0>ptrace(REQ_TRACE_ME,0,NULL,0))
		die("failed to trace myself");
	
	execve(args[0],(char * const *)args,(char * const *)env);
	die("failed to start the child");
}

int main()
{
pid_t	p;
int		status;
int		sig;

	p=fork();
	if(p<0)
		die("failed to fork");
	
	if(!p)
		child();
	
	for(;;)
	{
		if(0>waitpid(p,&status,0))
			die("failed to wait for child");
		if(WIFSTOPPED(status))
		{
			sig=WSTOPSIG(status);
			printf("child stopped by signal %d (%s)\n",sig,strsignal(sig));
			if(sig!=SIGTRAP)
			{
				printf("signal is not SIGTRAP; killing child\n");
				if(0>ptrace(REQ_KILL,p,NULL,0))
					die("failed to kill the child");
				break;
			}
		}
		else if(WIFSIGNALED(status))
		{
			sig=WTERMSIG(status);
			printf("child was signaled by signal %d (%d)\n",
				sig,strsignal(sig));
			if(0>ptrace(REQ_KILL,p,NULL,0))
				die("failed to kill the child");
			break;
		}
		else if(WIFEXITED(status))
		{
			printf("child exited with status %d\n",WEXITSTATUS(status));
			break;
		}
		else
		{
			printf("neither stopped, nor signaled, nor killed?\n");
			if(0>ptrace(PT_KILL,p,NULL,0))
				die("failed to kill the child");
			break;
		}
		
		printf("resuming the child\n");
		if(0>ptrace(REQ_SYSCALL,p,CONT_ADDR,0))
			die("failed to resume the child");
	}
	
	return 0;
}