[patch] Break by Ctrl-C before first syscall SIGSTOPs the traced process

Jan Kratochvil jan.kratochvil at redhat.com
Mon Jun 11 12:08:12 UTC 2007


On Sat, 02 Jun 2007 02:45:35 +0200, Roland McGrath wrote:
> > TCB_STARTUP && TCB_BPTSET can IMO happen only happen for the newly
> > forked/cloned child and its first signal should be always SIGSTOP.
> > I hope I did not miss any other case.
> 
> It's true that TCB_BPTSET|TCB_STARTUP happens only for a new child when the
> parent was traced with internal_{clone,fork}.  It is not true that the
> child's first signal will always be SIGSTOP.  In this case as any other,
> some other signal could have come along and be seen first.

I believe the current code should handle it although I did not try to test it,
thanks for the analysis.

...
> I'm not entirely following what you are saying here.  But the situation
> this is.  Signals like SIGSTOP do not queue, only one SIGSTOP is in the
> pending set at a time.  So, when a SIGSTOP is already pending, another kill
> does not do anything more.  But, you never know when the thread might
> actually wake up and deliver the SIGSTOP from the pending set.  If it had
> already pulled out the pending SIGSTOP, but not finished stopping, or your
> wait4 call hadn't finished waking up and returning yet, when you did the
> kill, then the second SIGSTOP will go into the pending set and be delivered
> later (like after you detach).

Thanks for the explanation.

...
> I didn't check that the broken-out functions' text didn't change from what
> was in main before.  But otherwise it looks fine modulo nits.

Attached WDIFF + DIFF of these moved functions just FYI.


> Your log entry has some format typos (missing :s).  Don't put my name in
> the log entry header, I didn't write the code.  Mention advice if you want to.
> Is there a Fedora bz# that should be mentioned in this log entry?

Processed, I hope everything is acceptable now.


Thanks,
Jan
-------------- next part --------------
2007-06-11  Jan Kratochvil  <jan.kratochvil at redhat.com>

	Never interrupt when the attached traced process would be left stopped.
	* strace.c (main): `-p' attaching moved to ...
	(startup_attach): ... a new function, renamed a variable C to TCBI.
	Block interrupting signals since the first tracee has been attached.
	New comment about INTERRUPTED in the nonthreaded case.
	[LINUX] (startup_attach): Check INTERRUPTED after each attached thread.
	(main): Command spawning moved to ...
	(startup_child): ... a new function, replaced RETURN with EXIT.
	[LINUX] (detach): New variable CATCH_SIGSTOP, do not signal
	new SIGSTOP for processes still in TCB_STARTUP.
	(main): Move signals and BLOCKED_SET init before the tracees attaching,
	[SUNOS4] (trace): Removed fixvfork () call as a dead code, SIGSTOP must
	have been already caught before clearing TCB_STARTUP.
	(trace): Removed the `!WIFSTOPPED(status)' dead code.
	Clear TCB_STARTUP only in the case the received signal was SIGSTOP.
	New comment when `TCB_BPTSET && TCB_STARTUP' combination can be set.
	Code advisory: Roland McGrath
	Fixes RH#240986.

Index: strace.c
===================================================================
RCS file: /cvsroot/strace/strace/strace.c,v
retrieving revision 1.76
diff -u -p -r1.76 strace.c
--- strace.c	2 Jun 2007 00:07:33 -0000	1.76
+++ strace.c	11 Jun 2007 11:44:52 -0000
@@ -334,6 +334,280 @@ newoutf(struct tcb *tcp)
 	return 0;
 }
 
+static void
+startup_attach(void)
+{
+	int tcbi;
+	struct tcb *tcp;
+
+	/*
+	 * Block user interruptions as we would leave the traced
+	 * process stopped (process state T) if we would terminate in
+	 * between PTRACE_ATTACH and wait4 () on SIGSTOP.
+	 * We rely on cleanup () from this point on.
+	 */
+	if (interactive)
+		sigprocmask(SIG_BLOCK, &blocked_set, NULL);
+
+	for (tcbi = 0; tcbi < tcbtabsize; tcbi++) {
+		tcp = tcbtab[tcbi];
+		if (!(tcp->flags & TCB_INUSE) || !(tcp->flags & TCB_ATTACHED))
+			continue;
+#ifdef LINUX
+		if (tcp->flags & TCB_CLONE_THREAD)
+			continue;
+#endif
+		/* Reinitialize the output since it may have changed. */
+		tcp->outf = outf;
+		if (newoutf(tcp) < 0)
+			exit(1);
+
+#ifdef USE_PROCFS
+		if (proc_open(tcp, 1) < 0) {
+			fprintf(stderr, "trouble opening proc file\n");
+			droptcb(tcp);
+			continue;
+		}
+#else /* !USE_PROCFS */
+# ifdef LINUX
+		if (followfork) {
+			char procdir[MAXPATHLEN];
+			DIR *dir;
+
+			sprintf(procdir, "/proc/%d/task", tcp->pid);
+			dir = opendir(procdir);
+			if (dir != NULL) {
+				unsigned int ntid = 0, nerr = 0;
+				struct dirent *de;
+				int tid;
+				while ((de = readdir(dir)) != NULL) {
+					if (de->d_fileno == 0 ||
+					    de->d_name[0] == '.')
+						continue;
+					tid = atoi(de->d_name);
+					if (tid <= 0)
+						continue;
+					++ntid;
+					if (ptrace(PTRACE_ATTACH, tid,
+						   (char *) 1, 0) < 0)
+						++nerr;
+					else if (tid != tcbtab[tcbi]->pid) {
+						if (nprocs == tcbtabsize &&
+						    expand_tcbtab())
+							tcp = NULL;
+						else
+							tcp = alloctcb(tid);
+						if (tcp == NULL)
+							exit(1);
+						tcp->flags |= TCB_ATTACHED|TCB_CLONE_THREAD|TCB_CLONE_DETACHED|TCB_FOLLOWFORK;
+						tcbtab[tcbi]->nchildren++;
+						tcbtab[tcbi]->nclone_threads++;
+						tcbtab[tcbi]->nclone_detached++;
+						tcp->parent = tcbtab[tcbi];
+					}
+					if (interactive) {
+						sigprocmask(SIG_SETMASK, &empty_set, NULL);
+						if (interrupted)
+							return;
+						sigprocmask(SIG_BLOCK, &blocked_set, NULL);
+					}
+				}
+				closedir(dir);
+				if (nerr == ntid) {
+					perror("attach: ptrace(PTRACE_ATTACH, ...)");
+					droptcb(tcp);
+					continue;
+				}
+				if (!qflag) {
+					ntid -= nerr;
+					if (ntid > 1)
+						fprintf(stderr, "\
+Process %u attached with %u threads - interrupt to quit\n",
+							tcp->pid, ntid);
+					else
+						fprintf(stderr, "\
+Process %u attached - interrupt to quit\n",
+							tcp->pid);
+				}
+				continue;
+			}
+		}
+# endif
+		if (ptrace(PTRACE_ATTACH, tcp->pid, (char *) 1, 0) < 0) {
+			perror("attach: ptrace(PTRACE_ATTACH, ...)");
+			droptcb(tcp);
+			continue;
+		}
+		/* INTERRUPTED is going to be checked at the top of TRACE.  */
+#endif /* !USE_PROCFS */
+		if (!qflag)
+			fprintf(stderr,
+				"Process %u attached - interrupt to quit\n",
+				tcp->pid);
+	}
+
+	if (interactive)
+		sigprocmask(SIG_SETMASK, &empty_set, NULL);
+}
+
+static void
+startup_child (char **argv)
+{
+	struct stat statbuf;
+	const char *filename;
+	char pathname[MAXPATHLEN];
+	int pid = 0;
+	struct tcb *tcp;
+
+	filename = argv[0];
+	if (strchr(filename, '/')) {
+		if (strlen(filename) > sizeof pathname - 1) {
+			errno = ENAMETOOLONG;
+			perror("strace: exec");
+			exit(1);
+		}
+		strcpy(pathname, filename);
+	}
+#ifdef USE_DEBUGGING_EXEC
+	/*
+	 * Debuggers customarily check the current directory
+	 * first regardless of the path but doing that gives
+	 * security geeks a panic attack.
+	 */
+	else if (stat(filename, &statbuf) == 0)
+		strcpy(pathname, filename);
+#endif /* USE_DEBUGGING_EXEC */
+	else {
+		char *path;
+		int m, n, len;
+
+		for (path = getenv("PATH"); path && *path; path += m) {
+			if (strchr(path, ':')) {
+				n = strchr(path, ':') - path;
+				m = n + 1;
+			}
+			else
+				m = n = strlen(path);
+			if (n == 0) {
+				if (!getcwd(pathname, MAXPATHLEN))
+					continue;
+				len = strlen(pathname);
+			}
+			else if (n > sizeof pathname - 1)
+				continue;
+			else {
+				strncpy(pathname, path, n);
+				len = n;
+			}
+			if (len && pathname[len - 1] != '/')
+				pathname[len++] = '/';
+			strcpy(pathname + len, filename);
+			if (stat(pathname, &statbuf) == 0 &&
+			    /* Accept only regular files
+			       with some execute bits set.
+			       XXX not perfect, might still fail */
+			    S_ISREG(statbuf.st_mode) &&
+			    (statbuf.st_mode & 0111))
+				break;
+		}
+	}
+	if (stat(pathname, &statbuf) < 0) {
+		fprintf(stderr, "%s: %s: command not found\n",
+			progname, filename);
+		exit(1);
+	}
+	switch (pid = fork()) {
+	case -1:
+		perror("strace: fork");
+		cleanup();
+		exit(1);
+		break;
+	case 0: {
+#ifdef USE_PROCFS
+		if (outf != stderr) close (fileno (outf));
+#ifdef MIPS
+		/* Kludge for SGI, see proc_open for details. */
+		sa.sa_handler = foobar;
+		sa.sa_flags = 0;
+		sigemptyset(&sa.sa_mask);
+		sigaction(SIGINT, &sa, NULL);
+#endif /* MIPS */
+#ifndef FREEBSD
+		pause();
+#else /* FREEBSD */
+		kill(getpid(), SIGSTOP); /* stop HERE */
+#endif /* FREEBSD */
+#else /* !USE_PROCFS */
+		if (outf!=stderr)
+			close(fileno (outf));
+
+		if (ptrace(PTRACE_TRACEME, 0, (char *) 1, 0) < 0) {
+			perror("strace: ptrace(PTRACE_TRACEME, ...)");
+			exit(1);
+		}
+		if (debug)
+			kill(getpid(), SIGSTOP);
+
+		if (username != NULL || geteuid() == 0) {
+			uid_t run_euid = run_uid;
+			gid_t run_egid = run_gid;
+
+			if (statbuf.st_mode & S_ISUID)
+				run_euid = statbuf.st_uid;
+			if (statbuf.st_mode & S_ISGID)
+				run_egid = statbuf.st_gid;
+
+			/*
+			 * It is important to set groups before we
+			 * lose privileges on setuid.
+			 */
+			if (username != NULL) {
+				if (initgroups(username, run_gid) < 0) {
+					perror("initgroups");
+					exit(1);
+				}
+				if (setregid(run_gid, run_egid) < 0) {
+					perror("setregid");
+					exit(1);
+				}
+				if (setreuid(run_uid, run_euid) < 0) {
+					perror("setreuid");
+					exit(1);
+				}
+			}
+		}
+		else
+			setreuid(run_uid, run_uid);
+
+		/*
+		 * Induce an immediate stop so that the parent
+		 * will resume us with PTRACE_SYSCALL and display
+		 * this execve call normally.
+		 */
+		kill(getpid(), SIGSTOP);
+#endif /* !USE_PROCFS */
+
+		execv(pathname, argv);
+		perror("strace: exec");
+		_exit(1);
+		break;
+	}
+	default:
+		if ((tcp = alloctcb(pid)) == NULL) {
+			cleanup();
+			exit(1);
+		}
+#ifdef USE_PROCFS
+		if (proc_open(tcp, 0) < 0) {
+			fprintf(stderr, "trouble opening proc file\n");
+			cleanup();
+			exit(1);
+		}
+#endif /* USE_PROCFS */
+		break;
+	}
+}
+
 int
 main(argc, argv)
 int argc;
@@ -532,250 +806,6 @@ char *argv[];
 		qflag = 1;
 	}
 
-	for (c = 0; c < tcbtabsize; c++) {
-		tcp = tcbtab[c];
-		if (!(tcp->flags & TCB_INUSE) || !(tcp->flags & TCB_ATTACHED))
-			continue;
-#ifdef LINUX
-		if (tcp->flags & TCB_CLONE_THREAD)
-			continue;
-#endif
-		/* Reinitialize the output since it may have changed. */
-		tcp->outf = outf;
-		if (newoutf(tcp) < 0)
-			exit(1);
-
-#ifdef USE_PROCFS
-		if (proc_open(tcp, 1) < 0) {
-			fprintf(stderr, "trouble opening proc file\n");
-			droptcb(tcp);
-			continue;
-		}
-#else /* !USE_PROCFS */
-# ifdef LINUX
-		if (followfork) {
-			char procdir[MAXPATHLEN];
-			DIR *dir;
-
-			sprintf(procdir, "/proc/%d/task", tcp->pid);
-			dir = opendir(procdir);
-			if (dir != NULL) {
-				unsigned int ntid = 0, nerr = 0;
-				struct dirent *de;
-				int tid;
-				while ((de = readdir(dir)) != NULL) {
-					if (de->d_fileno == 0 ||
-					    de->d_name[0] == '.')
-						continue;
-					tid = atoi(de->d_name);
-					if (tid <= 0)
-						continue;
-					++ntid;
-					if (ptrace(PTRACE_ATTACH, tid,
-						   (char *) 1, 0) < 0)
-						++nerr;
-					else if (tid != tcbtab[c]->pid) {
-						if (nprocs == tcbtabsize &&
-						    expand_tcbtab())
-							tcp = NULL;
-						else
-							tcp = alloctcb(tid);
-						if (tcp == NULL)
-							exit(1);
-						tcp->flags |= TCB_ATTACHED|TCB_CLONE_THREAD|TCB_CLONE_DETACHED|TCB_FOLLOWFORK;
-						tcbtab[c]->nchildren++;
-						tcbtab[c]->nclone_threads++;
-						tcbtab[c]->nclone_detached++;
-						tcp->parent = tcbtab[c];
-					}
-				}
-				closedir(dir);
-				if (nerr == ntid) {
-					perror("attach: ptrace(PTRACE_ATTACH, ...)");
-					droptcb(tcp);
-					continue;
-				}
-				if (!qflag) {
-					ntid -= nerr;
-					if (ntid > 1)
-						fprintf(stderr, "\
-Process %u attached with %u threads - interrupt to quit\n",
-							tcp->pid, ntid);
-					else
-						fprintf(stderr, "\
-Process %u attached - interrupt to quit\n",
-							tcp->pid);
-				}
-				continue;
-			}
-		}
-# endif
-		if (ptrace(PTRACE_ATTACH, tcp->pid, (char *) 1, 0) < 0) {
-			perror("attach: ptrace(PTRACE_ATTACH, ...)");
-			droptcb(tcp);
-			continue;
-		}
-#endif /* !USE_PROCFS */
-		if (!qflag)
-			fprintf(stderr,
-				"Process %u attached - interrupt to quit\n",
-				tcp->pid);
-	}
-
-	if (!pflag_seen) {
-		struct stat statbuf;
-		char *filename;
-		char pathname[MAXPATHLEN];
-
-		filename = argv[optind];
-		if (strchr(filename, '/')) {
-			if (strlen(filename) > sizeof pathname - 1) {
-				errno = ENAMETOOLONG;
-				perror("strace: exec");
-				exit(1);
-			}
-			strcpy(pathname, filename);
-		}
-#ifdef USE_DEBUGGING_EXEC
-		/*
-		 * Debuggers customarily check the current directory
-		 * first regardless of the path but doing that gives
-		 * security geeks a panic attack.
-		 */
-		else if (stat(filename, &statbuf) == 0)
-			strcpy(pathname, filename);
-#endif /* USE_DEBUGGING_EXEC */
-		else {
-			char *path;
-			int m, n, len;
-
-			for (path = getenv("PATH"); path && *path; path += m) {
-				if (strchr(path, ':')) {
-					n = strchr(path, ':') - path;
-					m = n + 1;
-				}
-				else
-					m = n = strlen(path);
-				if (n == 0) {
-					if (!getcwd(pathname, MAXPATHLEN))
-						continue;
-					len = strlen(pathname);
-				}
-				else if (n > sizeof pathname - 1)
-					continue;
-				else {
-					strncpy(pathname, path, n);
-					len = n;
-				}
-				if (len && pathname[len - 1] != '/')
-					pathname[len++] = '/';
-				strcpy(pathname + len, filename);
-				if (stat(pathname, &statbuf) == 0 &&
-				    /* Accept only regular files
-				       with some execute bits set.
-				       XXX not perfect, might still fail */
-				    S_ISREG(statbuf.st_mode) &&
-				    (statbuf.st_mode & 0111))
-					break;
-			}
-		}
-		if (stat(pathname, &statbuf) < 0) {
-			fprintf(stderr, "%s: %s: command not found\n",
-				progname, filename);
-			exit(1);
-		}
-		switch (pid = fork()) {
-		case -1:
-			perror("strace: fork");
-			cleanup();
-			exit(1);
-			break;
-		case 0: {
-#ifdef USE_PROCFS
-		        if (outf != stderr) close (fileno (outf));
-#ifdef MIPS
-			/* Kludge for SGI, see proc_open for details. */
-			sa.sa_handler = foobar;
-			sa.sa_flags = 0;
-			sigemptyset(&sa.sa_mask);
-			sigaction(SIGINT, &sa, NULL);
-#endif /* MIPS */
-#ifndef FREEBSD
-			pause();
-#else /* FREEBSD */
-			kill(getpid(), SIGSTOP); /* stop HERE */
-#endif /* FREEBSD */
-#else /* !USE_PROCFS */
-			if (outf!=stderr)
-				close(fileno (outf));
-
-			if (ptrace(PTRACE_TRACEME, 0, (char *) 1, 0) < 0) {
-				perror("strace: ptrace(PTRACE_TRACEME, ...)");
-				return -1;
-			}
-			if (debug)
-				kill(getpid(), SIGSTOP);
-
-			if (username != NULL || geteuid() == 0) {
-				uid_t run_euid = run_uid;
-				gid_t run_egid = run_gid;
-
-				if (statbuf.st_mode & S_ISUID)
-					run_euid = statbuf.st_uid;
-				if (statbuf.st_mode & S_ISGID)
-					run_egid = statbuf.st_gid;
-
-				/*
-				 * It is important to set groups before we
-				 * lose privileges on setuid.
-				 */
-				if (username != NULL) {
-					if (initgroups(username, run_gid) < 0) {
-						perror("initgroups");
-						exit(1);
-					}
-					if (setregid(run_gid, run_egid) < 0) {
-						perror("setregid");
-						exit(1);
-					}
-					if (setreuid(run_uid, run_euid) < 0) {
-						perror("setreuid");
-						exit(1);
-					}
-				}
-			}
-			else
-				setreuid(run_uid, run_uid);
-
-			/*
-			 * Induce an immediate stop so that the parent
-			 * will resume us with PTRACE_SYSCALL and display
-			 * this execve call normally.
-			 */
-			kill(getpid(), SIGSTOP);
-#endif /* !USE_PROCFS */
-
-			execv(pathname, &argv[optind]);
-			perror("strace: exec");
-			_exit(1);
-			break;
-		}
-		default:
-			if ((tcp = alloctcb(pid)) == NULL) {
-				cleanup();
-				exit(1);
-			}
-#ifdef USE_PROCFS
-			if (proc_open(tcp, 0) < 0) {
-				fprintf(stderr, "trouble opening proc file\n");
-				cleanup();
-				exit(1);
-			}
-#endif /* USE_PROCFS */
-			break;
-		}
-	}
-
 	sigemptyset(&empty_set);
 	sigemptyset(&blocked_set);
 	sa.sa_handler = SIG_IGN;
@@ -813,6 +843,11 @@ Process %u attached - interrupt to quit\
 	sigaction(SIGCHLD, &sa, NULL);
 #endif /* USE_PROCFS */
 
+	if (pflag_seen)
+		startup_attach();
+	else
+		startup_child(&argv[optind]);
+
 	if (trace() < 0)
 		exit(1);
 	cleanup();
@@ -1306,7 +1341,7 @@ int sig;
 {
 	int error = 0;
 #ifdef LINUX
-	int status, resumed;
+	int status, resumed, catch_sigstop;
 	struct tcb *zombie = NULL;
 
 	/* If the group leader is lingering only because of this other
@@ -1330,6 +1365,12 @@ int sig;
 #undef PTRACE_DETACH
 #define PTRACE_DETACH PTRACE_SUNDETACH
 #endif
+	/*
+	 * On TCB_STARTUP we did PTRACE_ATTACH but still did not get the
+	 * expected SIGSTOP.  We must catch exactly one as otherwise the
+	 * detached process would be left stopped (process state T).
+	 */
+	catch_sigstop = (tcp->flags & TCB_STARTUP);
 	if ((error = ptrace(PTRACE_DETACH, tcp->pid, (char *) 1, sig)) == 0) {
 		/* On a clear day, you can see forever. */
 	}
@@ -1343,13 +1384,15 @@ int sig;
 		if (errno != ESRCH)
 			perror("detach: checking sanity");
 	}
-	else if (my_tgkill((tcp->flags & TCB_CLONE_THREAD ? tcp->parent->pid
-							  : tcp->pid),
-			   tcp->pid, SIGSTOP) < 0) {
+	else if (!catch_sigstop && my_tgkill((tcp->flags & TCB_CLONE_THREAD
+					      ? tcp->parent->pid : tcp->pid),
+					     tcp->pid, SIGSTOP) < 0) {
 		if (errno != ESRCH)
 			perror("detach: stopping child");
 	}
-	else {
+	else
+		catch_sigstop = 1;
+	if (catch_sigstop)
 		for (;;) {
 #ifdef __WALL
 			if (wait4(tcp->pid, &status, __WALL, NULL) < 0) {
@@ -1400,7 +1443,6 @@ int sig;
 				break;
 			}
 		}
-	}
 #endif /* LINUX */
 
 #if defined(SUNOS4)
@@ -2121,6 +2163,8 @@ trace()
 #endif /* LINUX */
 
 	while (nprocs != 0) {
+		if (interrupted)
+			return 0;
 		if (interactive)
 			sigprocmask(SIG_SETMASK, &empty_set, NULL);
 #ifdef LINUX
@@ -2153,9 +2197,6 @@ trace()
 		if (interactive)
 			sigprocmask(SIG_BLOCK, &blocked_set, NULL);
 
-		if (interrupted)
-			return 0;
-
 		if (pid == -1) {
 			switch (wait_errno) {
 			case EINTR:
@@ -2301,35 +2342,24 @@ Process %d attached (waiting for parent)
 			fprintf(stderr, "pid %u stopped, [%s]\n",
 				pid, signame(WSTOPSIG(status)));
 
-		if (tcp->flags & TCB_STARTUP) {
+		/*
+		 * Interestingly, the process may stop
+		 * with STOPSIG equal to some other signal
+		 * than SIGSTOP if we happend to attach
+		 * just before the process takes a signal.
+		 */
+		if ((tcp->flags & TCB_STARTUP) && WSTOPSIG(status) == SIGSTOP) {
 			/*
 			 * This flag is there to keep us in sync.
 			 * Next time this process stops it should
 			 * really be entering a system call.
 			 */
 			tcp->flags &= ~TCB_STARTUP;
-			if (tcp->flags & TCB_ATTACHED) {
+			if (tcp->flags & TCB_BPTSET) {
 				/*
-				 * Interestingly, the process may stop
-				 * with STOPSIG equal to some other signal
-				 * than SIGSTOP if we happend to attach
-				 * just before the process takes a signal.
+				 * One example is a breakpoint inherited from
+				 * parent through fork ().
 				 */
-				if (!WIFSTOPPED(status)) {
-					fprintf(stderr,
-						"pid %u not stopped\n", pid);
-					detach(tcp, WSTOPSIG(status));
-					continue;
-				}
-			}
-			else {
-#ifdef SUNOS4
-				/* A child of us stopped at exec */
-				if (WSTOPSIG(status) == SIGTRAP && followvfork)
-					fixvfork(tcp);
-#endif /* SUNOS4 */
-			}
-			if (tcp->flags & TCB_BPTSET) {
 				if (clearbpt(tcp) < 0) /* Pretty fatal */ {
 					droptcb(tcp);
 					cleanup();
@@ -2404,6 +2434,9 @@ Process %d attached (waiting for parent)
 			tcp->flags &= ~TCB_SUSPENDED;
 			continue;
 		}
+		/* we handled the STATUS, we are permitted to interrupt now. */
+		if (interrupted)
+			return 0;
 		if (trace_syscall(tcp) < 0) {
 			if (tcp->flags & TCB_ATTACHED)
 				detach(tcp, 0);
-------------- next part --------------
{+static void
startup_attach(void)
{
	int tcbi;
	struct tcb *tcp;

	/*
	 * Block user interruptions as we would leave the traced
	 * process stopped (process state T) if we would terminate in
	 * between PTRACE_ATTACH and wait4 () on SIGSTOP.
	 * We rely on cleanup () from this point on.
	 */
	if (interactive)
		sigprocmask(SIG_BLOCK, &blocked_set, NULL);+}

	for [-(c-] {+(tcbi+} = 0; [-c-] {+tcbi+} < tcbtabsize; [-c++)-] {+tcbi++)+} {
		tcp = [-tcbtab[c];-] {+tcbtab[tcbi];+}
		if (!(tcp->flags & TCB_INUSE) || !(tcp->flags & TCB_ATTACHED))
			continue;
#ifdef LINUX
		if (tcp->flags & TCB_CLONE_THREAD)
			continue;
#endif
		/* Reinitialize the output since it may have changed. */
		tcp->outf = outf;
		if (newoutf(tcp) < 0)
			exit(1);

#ifdef USE_PROCFS
		if (proc_open(tcp, 1) < 0) {
			fprintf(stderr, "trouble opening proc file\n");
			droptcb(tcp);
			continue;
		}
#else /* !USE_PROCFS */
# ifdef LINUX
		if (followfork) {
			char procdir[MAXPATHLEN];
			DIR *dir;

			sprintf(procdir, "/proc/%d/task", tcp->pid);
			dir = opendir(procdir);
			if (dir != NULL) {
				unsigned int ntid = 0, nerr = 0;
				struct dirent *de;
				int tid;
				while ((de = readdir(dir)) != NULL) {
					if (de->d_fileno == 0 ||
					    de->d_name[0] == '.')
						continue;
					tid = atoi(de->d_name);
					if (tid <= 0)
						continue;
					++ntid;
					if (ptrace(PTRACE_ATTACH, tid,
						   (char *) 1, 0) < 0)
						++nerr;
					else if (tid != [-tcbtab[c]->pid)-] {+tcbtab[tcbi]->pid)+} {
						if (nprocs == tcbtabsize &&
						    expand_tcbtab())
							tcp = NULL;
						else
							tcp = alloctcb(tid);
						if (tcp == NULL)
							exit(1);
						tcp->flags |= TCB_ATTACHED|TCB_CLONE_THREAD|TCB_CLONE_DETACHED|TCB_FOLLOWFORK;
						[-tcbtab[c]->nchildren++;
						tcbtab[c]->nclone_threads++;
						tcbtab[c]->nclone_detached++;-]
						{+tcbtab[tcbi]->nchildren++;
						tcbtab[tcbi]->nclone_threads++;
						tcbtab[tcbi]->nclone_detached++;+}
						tcp->parent = [-tcbtab[c];-] {+tcbtab[tcbi];
					}
					if (interactive) {
						sigprocmask(SIG_SETMASK, &empty_set, NULL);
						if (interrupted)
							return;
						sigprocmask(SIG_BLOCK, &blocked_set, NULL);+}
					}
				}
				closedir(dir);
				if (nerr == ntid) {
					perror("attach: ptrace(PTRACE_ATTACH, ...)");
					droptcb(tcp);
					continue;
				}
				if (!qflag) {
					ntid -= nerr;
					if (ntid > 1)
						fprintf(stderr, "\
Process %u attached with %u threads - interrupt to quit\n",
							tcp->pid, ntid);
					else
						fprintf(stderr, "\
Process %u attached - interrupt to quit\n",
							tcp->pid);
				}
				continue;
			}
		}
# endif
		if (ptrace(PTRACE_ATTACH, tcp->pid, (char *) 1, 0) < 0) {
			perror("attach: ptrace(PTRACE_ATTACH, ...)");
			droptcb(tcp);
			continue;
		}
		{+/* INTERRUPTED is going to be checked at the top of TRACE.  */+}
#endif /* !USE_PROCFS */
		if (!qflag)
			fprintf(stderr,
				"Process %u attached - interrupt to quit\n",
				tcp->pid);
	}

	if [-(!pflag_seen)-] {+(interactive)
		sigprocmask(SIG_SETMASK, &empty_set, NULL);
}

static void
startup_child (char **argv)+}
{
	struct stat statbuf;
	{+const+} char *filename;
	char pathname[MAXPATHLEN];
	{+int pid = 0;
	struct tcb *tcp;+}

	filename = [-argv[optind];-] {+argv[0];+}
	if (strchr(filename, '/')) {
		if (strlen(filename) > sizeof pathname - 1) {
			errno = ENAMETOOLONG;
			perror("strace: exec");
			exit(1);
		}
		strcpy(pathname, filename);
	}
#ifdef USE_DEBUGGING_EXEC
	/*
	 * Debuggers customarily check the current directory
	 * first regardless of the path but doing that gives
	 * security geeks a panic attack.
	 */
	else if (stat(filename, &statbuf) == 0)
		strcpy(pathname, filename);
#endif /* USE_DEBUGGING_EXEC */
	else {
		char *path;
		int m, n, len;

		for (path = getenv("PATH"); path && *path; path += m) {
			if (strchr(path, ':')) {
				n = strchr(path, ':') - path;
				m = n + 1;
			}
			else
				m = n = strlen(path);
			if (n == 0) {
				if (!getcwd(pathname, MAXPATHLEN))
					continue;
				len = strlen(pathname);
			}
			else if (n > sizeof pathname - 1)
				continue;
			else {
				strncpy(pathname, path, n);
				len = n;
			}
			if (len && pathname[len - 1] != '/')
				pathname[len++] = '/';
			strcpy(pathname + len, filename);
			if (stat(pathname, &statbuf) == 0 &&
			    /* Accept only regular files
			       with some execute bits set.
			       XXX not perfect, might still fail */
			    S_ISREG(statbuf.st_mode) &&
			    (statbuf.st_mode & 0111))
				break;
		}
	}
	if (stat(pathname, &statbuf) < 0) {
		fprintf(stderr, "%s: %s: command not found\n",
			progname, filename);
		exit(1);
	}
	switch (pid = fork()) {
	case -1:
		perror("strace: fork");
		cleanup();
		exit(1);
		break;
	case 0: {
#ifdef USE_PROCFS
		if (outf != stderr) close (fileno (outf));
#ifdef MIPS
		/* Kludge for SGI, see proc_open for details. */
		sa.sa_handler = foobar;
		sa.sa_flags = 0;
		sigemptyset(&sa.sa_mask);
		sigaction(SIGINT, &sa, NULL);
#endif /* MIPS */
#ifndef FREEBSD
		pause();
#else /* FREEBSD */
		kill(getpid(), SIGSTOP); /* stop HERE */
#endif /* FREEBSD */
#else /* !USE_PROCFS */
		if (outf!=stderr)
			close(fileno (outf));

		if (ptrace(PTRACE_TRACEME, 0, (char *) 1, 0) < 0) {
			perror("strace: ptrace(PTRACE_TRACEME, ...)");
				[-return -1;-]
			{+exit(1);+}
		}
		if (debug)
			kill(getpid(), SIGSTOP);

		if (username != NULL || geteuid() == 0) {
			uid_t run_euid = run_uid;
			gid_t run_egid = run_gid;

			if (statbuf.st_mode & S_ISUID)
				run_euid = statbuf.st_uid;
			if (statbuf.st_mode & S_ISGID)
				run_egid = statbuf.st_gid;

			/*
			 * It is important to set groups before we
			 * lose privileges on setuid.
			 */
			if (username != NULL) {
				if (initgroups(username, run_gid) < 0) {
					perror("initgroups");
					exit(1);
				}
				if (setregid(run_gid, run_egid) < 0) {
					perror("setregid");
					exit(1);
				}
				if (setreuid(run_uid, run_euid) < 0) {
					perror("setreuid");
					exit(1);
				}
			}
		}
		else
			setreuid(run_uid, run_uid);

		/*
		 * Induce an immediate stop so that the parent
		 * will resume us with PTRACE_SYSCALL and display
		 * this execve call normally.
		 */
		kill(getpid(), SIGSTOP);
#endif /* !USE_PROCFS */

		execv(pathname, [-&argv[optind]);-] {+argv);+}
		perror("strace: exec");
		_exit(1);
		break;
	}
	default:
		if ((tcp = alloctcb(pid)) == NULL) {
			cleanup();
			exit(1);
		}
#ifdef USE_PROCFS
		if (proc_open(tcp, 0) < 0) {
			fprintf(stderr, "trouble opening proc file\n");
			cleanup();
			exit(1);
		}
#endif /* USE_PROCFS */
		break;
	}
}
-------------- next part --------------
--- strace.c-old	2007-06-11 13:49:36.000000000 +0200
+++ strace.c-new	2007-06-11 13:48:48.000000000 +0200
@@ -1,5 +1,20 @@
-	for (c = 0; c < tcbtabsize; c++) {
-		tcp = tcbtab[c];
+static void
+startup_attach(void)
+{
+	int tcbi;
+	struct tcb *tcp;
+
+	/*
+	 * Block user interruptions as we would leave the traced
+	 * process stopped (process state T) if we would terminate in
+	 * between PTRACE_ATTACH and wait4 () on SIGSTOP.
+	 * We rely on cleanup () from this point on.
+	 */
+	if (interactive)
+		sigprocmask(SIG_BLOCK, &blocked_set, NULL);
+
+	for (tcbi = 0; tcbi < tcbtabsize; tcbi++) {
+		tcp = tcbtab[tcbi];
 		if (!(tcp->flags & TCB_INUSE) || !(tcp->flags & TCB_ATTACHED))
 			continue;
 #ifdef LINUX
@@ -40,7 +55,7 @@
 					if (ptrace(PTRACE_ATTACH, tid,
 						   (char *) 1, 0) < 0)
 						++nerr;
-					else if (tid != tcbtab[c]->pid) {
+					else if (tid != tcbtab[tcbi]->pid) {
 						if (nprocs == tcbtabsize &&
 						    expand_tcbtab())
 							tcp = NULL;
@@ -49,10 +64,16 @@
 						if (tcp == NULL)
 							exit(1);
 						tcp->flags |= TCB_ATTACHED|TCB_CLONE_THREAD|TCB_CLONE_DETACHED|TCB_FOLLOWFORK;
-						tcbtab[c]->nchildren++;
-						tcbtab[c]->nclone_threads++;
-						tcbtab[c]->nclone_detached++;
-						tcp->parent = tcbtab[c];
+						tcbtab[tcbi]->nchildren++;
+						tcbtab[tcbi]->nclone_threads++;
+						tcbtab[tcbi]->nclone_detached++;
+						tcp->parent = tcbtab[tcbi];
+					}
+					if (interactive) {
+						sigprocmask(SIG_SETMASK, &empty_set, NULL);
+						if (interrupted)
+							return;
+						sigprocmask(SIG_BLOCK, &blocked_set, NULL);
 					}
 				}
 				closedir(dir);
@@ -81,6 +102,7 @@
 			droptcb(tcp);
 			continue;
 		}
+		/* INTERRUPTED is going to be checked at the top of TRACE.  */
 #endif /* !USE_PROCFS */
 		if (!qflag)
 			fprintf(stderr,
@@ -88,12 +110,20 @@
 				tcp->pid);
 	}
 
-	if (!pflag_seen) {
+	if (interactive)
+		sigprocmask(SIG_SETMASK, &empty_set, NULL);
+}
+
+static void
+startup_child (char **argv)
+{
 		struct stat statbuf;
-		char *filename;
+	const char *filename;
 		char pathname[MAXPATHLEN];
+	int pid = 0;
+	struct tcb *tcp;
 
-		filename = argv[optind];
+	filename = argv[0];
 		if (strchr(filename, '/')) {
 			if (strlen(filename) > sizeof pathname - 1) {
 				errno = ENAMETOOLONG;
@@ -177,7 +207,7 @@
 
 			if (ptrace(PTRACE_TRACEME, 0, (char *) 1, 0) < 0) {
 				perror("strace: ptrace(PTRACE_TRACEME, ...)");
-				return -1;
+			exit(1);
 			}
 			if (debug)
 				kill(getpid(), SIGSTOP);
@@ -221,7 +251,7 @@
 			kill(getpid(), SIGSTOP);
 #endif /* !USE_PROCFS */
 
-			execv(pathname, &argv[optind]);
+		execv(pathname, argv);
 			perror("strace: exec");
 			_exit(1);
 			break;


More information about the Strace-devel mailing list