NetBSD-Bugs archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: bin/56254: script(1) abuses non-async-signal-safe functions in signal handlers



The following reply was made to PR bin/56254; it has been noted by GNATS.

From: RVP <rvp%SDF.ORG@localhost>
To: gnats-bugs%netbsd.org@localhost
Cc: 
Subject: Re: bin/56254: script(1) abuses non-async-signal-safe functions in
 signal handlers
Date: Thu, 17 Jun 2021 09:50:36 +0000 (UTC)

 On Thu, 17 Jun 2021, RVP wrote:
 
 > Since script(1) a) isn't using atexit() and b) is flushing the script
 > file, I think we can just use _exit() instead of exit() here:
 >
 
 On reflection, probably not: since printf & cohorts are being called.
 
 Here's one I made earlier:
 
 --- START PATCH ---
 diff -urN a/src/usr.bin/script/script.c b/src/usr.bin/script/script.c
 --- a/src/usr.bin/script/script.c	2020-08-31 15:32:15.000000000 +0000
 +++ b/src/usr.bin/script/script.c	2021-06-17 00:38:33.578014997 +0000
 @@ -81,19 +81,21 @@
   static int	quiet, flush;
   static const char *fname;
 
 +static volatile sig_atomic_t doexit;
   static int	isterm;
   static struct	termios tt;
 
 -__dead static void	done(void);
 +static void	start(void);
 +static void	done(void);
   __dead static void	dooutput(void);
   __dead static void	doshell(const char *);
 -__dead static void	fail(void);
   static void	finish(int);
   static void	scriptflush(int);
   static void	record(FILE *, char *, size_t, int);
   static void	consume(FILE *, off_t, char *, int);
 -static void	childwait(void);
   __dead static void	playback(FILE *);
 +static sig_t	xsignal(int, sig_t);
 +static void	killchild(int);
 
   int
   main(int argc, char *argv[])
 @@ -179,18 +181,18 @@
   		(void)tcsetattr(STDIN_FILENO, TCSAFLUSH, &rtt);
   	}
 
 -	(void)signal(SIGCHLD, finish);
 +	(void)xsignal(SIGCHLD, finish);
 +	(void)xsignal(SIGHUP, killchild);
 +	(void)xsignal(SIGINT, killchild);
 +	(void)xsignal(SIGQUIT, killchild);
 +	(void)xsignal(SIGTERM, killchild);
   	child = fork();
 -	if (child == -1) {
 -		warn("fork");
 -		fail();
 -	}
 +	if (child == -1)
 +		err(EXIT_FAILURE, "fork");
   	if (child == 0) {
   		subchild = child = fork();
 -		if (child == -1) {
 -			warn("fork");
 -			fail();
 -		}
 +		if (child == -1)
 +			err(EXIT_FAILURE, "fork");
   		if (child)
   			dooutput();
   		else
 @@ -199,37 +201,50 @@
 
   	if (!rawout)
   		(void)fclose(fscript);
 -	while ((scc = read(STDIN_FILENO, ibuf, BUFSIZ)) > 0) {
 +	while (!doexit && (scc = read(STDIN_FILENO, ibuf, BUFSIZ)) > 0) {
   		cc = (size_t)scc;
   		if (rawout)
   			record(fscript, ibuf, cc, 'i');
   		(void)write(master, ibuf, cc);
   	}
 -	childwait();
 +	done();
   	return EXIT_SUCCESS;
   }
 
 -static void
 -childwait(void)
 +/**
 + * wrapper around sigaction() because we want POSIX semantics:
 + * no auto-restarting of interrupted slow syscalls.
 + */
 +static sig_t
 +xsignal(int signo, sig_t handler)
   {
 -	sigset_t set;
 +	struct sigaction sa, osa;
 
 -	sigemptyset(&set);
 -	sigsuspend(&set);
 +	sa.sa_handler = handler;
 +	sa.sa_flags = 0;
 +	sigemptyset(&sa.sa_mask);
 +	if (sigaction(signo, &sa, &osa) < 0)
 +		return SIG_ERR;
 +	return osa.sa_handler;
   }
 
   static void
 -finish(int signo)
 +killchild(int signo)
   {
 -	int die, pid, status;
 +	if (child > 1)
 +		(void)kill(child, signo);
 +}
 
 -	die = 0;
 -	while ((pid = wait3(&status, WNOHANG, 0)) > 0)
 -		if (pid == child)
 -			die = 1;
 +static void
 +finish(int signo)
 +{
 +	int pid, status;
 
 -	if (die)
 -		done();
 +	while ((pid = wait3(&status, WNOHANG, NULL)) > 0)
 +		if (pid == child) {
 +			doexit = 1;
 +			break;
 +		}
   }
 
   static void
 @@ -238,22 +253,15 @@
   	struct itimerval value;
   	ssize_t scc;
   	size_t cc;
 -	time_t tvec;
   	char obuf[BUFSIZ];
 
 -	(void)close(STDIN_FILENO);
 -	tvec = time(NULL);
 -	if (rawout)
 -		record(fscript, NULL, 0, 's');
 -	else if (!quiet)
 -		(void)fprintf(fscript, "Script started on %s", ctime(&tvec));
 -
 +	start();
   	(void)signal(SIGALRM, scriptflush);
   	value.it_interval.tv_sec = SECSPERMIN / 2;
   	value.it_interval.tv_usec = 0;
   	value.it_value = value.it_interval;
   	(void)setitimer(ITIMER_REAL, &value, NULL);
 -	for (;;) {
 +	while (!doexit) {
   		scc = read(master, obuf, sizeof(obuf));
   		if (scc <= 0)
   			break;
 @@ -267,7 +275,7 @@
   		if (flush)
   			(void)fflush(fscript);
   	}
 -	childwait();
 +	done();
   	exit(EXIT_SUCCESS);
   }
 
 @@ -293,30 +301,31 @@
   		if (shell == NULL)
   			shell = _PATH_BSHELL;
   		execl(shell, shell, "-i", NULL);
 -		warn("execl `%s'", shell);
 +		err(EXIT_FAILURE, "execl `%s'", shell);
   	} else {
   		if (system(command) == -1)
 -			warn("system `%s'", command);
 +			err(EXIT_FAILURE, "system `%s'", command);
   	}
 -
 -	fail();
   }
 
   static void
 -fail(void)
 +start(void)
   {
 +	time_t tvec;
 
 -	(void)kill(0, SIGTERM);
 -	done();
 +	(void)close(STDIN_FILENO);
 +	tvec = time(NULL);
 +	if (rawout)
 +		record(fscript, NULL, 0, 's');
 +	else if (!quiet)
 +		(void)fprintf(fscript, "Script started on %s", ctime(&tvec));
   }
 
   static void
   done(void)
   {
 -	time_t tvec;
 -
   	if (subchild) {
 -		tvec = time(NULL);
 +		time_t tvec = time(NULL);
   		if (rawout)
   			record(fscript, NULL, 0, 'e');
   		else if (!quiet)
 @@ -330,7 +339,6 @@
   		if (!quiet)
   			(void)printf("Script done, output file is %s\n", fname);
   	}
 -	exit(EXIT_SUCCESS);
   }
 
   static void
 --- END PATCH ---
 
 -RVP
 


Home | Main Index | Thread Index | Old Index