[Pkg-shadow-devel] Bug#942680: Bug#942680: passwd: vipw does not resume properly when suspended

Serge E. Hallyn serge at hallyn.com
Sat Oct 26 13:49:33 BST 2019


Thanks,

second option sounds nicer but sure is a lot more code.  So I'm
leaning towards the first.  Do you  mind creating a github issue
at github.com/shadow-maint/shadow for this, or would you prefer that
I do it?

-serge

On Sat, Oct 19, 2019 at 04:20:11PM -0600, Todd C. Miller wrote:
> Package: passwd
> Version: 1:4.5-1.1
> Severity: normal
> Tags: patch
> 
> Dear Maintainer,
> 
>    * What led up to the situation?
> 
>     If vipw is suspended (e.g. via control-Z) and then resumed, it
>     often gets immediately suspended.  This is easier to reproduce
>     on a multi-core system.
> 
>    * What exactly did you do (or not do) that was effective (or
>      ineffective)?
>  
>     root at buster:~# /usr/sbin/vipw
> 
>     [1]+  Stopped                 /usr/sbin/vipw
>     root at buster:~# fg
>     /usr/sbin/vipw
> 
>     [1]+  Stopped                 /usr/sbin/vipw
> 
>     root at buster:~# fg
>     [vipw resumes on the second fg]
> 
>     The problem is that vipw forks a child process and calls waitpid()
>     with the WUNTRACED flag.  When the child process (running the editor)
>     is suspended, the parent sends itself SIGSTOP to suspend the main vipw
>     process.  However, because the main vipw is in the same process group
>     as the editor which received the ^Z, the kernel already sent the
>     main vipw SIGTSTP.
> 
>     If the main vipw receives SIGTSTP before the child, it will be
>     suspended and then, once resumed, will proceed to suspend itself
>     again.
> 
>     There are two main ways to fix this.  I've included patches for
>     each approach.
> 
>     1) Don't pass the WUNTRACED flag to waitpid() in vipw.c and
>        just assume the main vipw will receive a stop signal from
>        the kernel.  This is the simplest fix and works fine for
>        stop signals generated due to ^Z.  However, if someone sends
>        SIGSTOP to the vipw child process via the kill command, the
>        parent vipw will not notice.
> 
> --- vipw.c.orig	2019-10-18 16:19:15.673956247 -0600
> +++ vipw.c.simple_fix	2019-10-18 16:43:04.265583507 -0600
> @@ -325,16 +325,9 @@
>  	}
>  
>  	for (;;) {
> -		pid = waitpid (pid, &status, WUNTRACED);
> -		if ((pid != -1) && (WIFSTOPPED (status) != 0)) {
> -			/* The child (editor) was suspended.
> -			 * Suspend vipw. */
> -			kill (getpid (), SIGSTOP);
> -			/* wake child when resumed */
> -			kill (pid, SIGCONT);
> -		} else {
> +		pid = waitpid (pid, &status, 0);
> +		if (pid != -1 || errno != EINTR)
>  			break;
> -		}
>  	}
>  
>  	if (-1 == pid) {
> 
>     2) The other option is to run the child process in its own process
>        group as the foregroup process group.  That way, control-Z will
>        only affect the child process and the parent can use the existing
>        logic to suspend the parent.
> 
> 
> --- vipw.c.orig	2019-10-18 16:19:15.673956247 -0600
> +++ vipw.c.pgrp_fix	2019-10-19 12:55:50.526591299 -0600
> @@ -206,6 +206,8 @@
>  	struct stat st1, st2;
>  	int status;
>  	FILE *f;
> +	pid_t orig_pgrp, editor_pgrp = -1;
> +	sigset_t mask, omask;
>  	/* FIXME: the following should have variable sizes */
>  	char filebackup[1024], fileedit[1024];
>  	char *to_rename;
> @@ -293,6 +295,8 @@
>  		editor = DEFAULT_EDITOR;
>  	}
>  
> +	orig_pgrp = tcgetpgrp(STDIN_FILENO);
> +
>  	pid = fork ();
>  	if (-1 == pid) {
>  		vipwexit ("fork", 1, 1);
> @@ -302,6 +306,14 @@
>  		char *buf;
>  		int status;
>  
> +		/* Wait for parent to make us the foreground pgrp. */
> +		if (orig_pgrp != -1) {
> +			pid = getpid();
> +			setpgid(0, 0);
> +			while (tcgetpgrp(STDIN_FILENO) != pid)
> +				continue;
> +		}
> +
>  		buf = (char *) malloc (strlen (editor) + strlen (fileedit) + 2);
>  		snprintf (buf, strlen (editor) + strlen (fileedit) + 2,
>  		          "%s %s", editor, fileedit);
> @@ -324,19 +336,50 @@
>  		}
>  	}
>  
> +	/* Run child in a new pgrp and make it the foreground pgrp. */
> +	if (orig_pgrp != -1) {
> +		setpgid(pid, pid);
> +		tcsetpgrp(STDIN_FILENO, pid);
> +
> +		/* Avoid SIGTTOU when changing foreground pgrp below. */
> +		sigemptyset(&mask);
> +		sigaddset(&mask, SIGTTOU);
> +		sigprocmask(SIG_BLOCK, &mask, &omask);
> +	}
> +
>  	for (;;) {
>  		pid = waitpid (pid, &status, WUNTRACED);
>  		if ((pid != -1) && (WIFSTOPPED (status) != 0)) {
>  			/* The child (editor) was suspended.
> -			 * Suspend vipw. */
> +			 * Restore terminal pgrp and suspend vipw. */
> +			if (orig_pgrp != -1) {
> +				editor_pgrp = tcgetpgrp(STDIN_FILENO);
> +				if (editor_pgrp == -1) {
> +					fprintf (stderr, "%s: %s: %s", Prog,
> +						 "tcgetpgrp", strerror (errno));
> +				}
> +				if (tcsetpgrp(STDIN_FILENO, orig_pgrp) == -1) {
> +					fprintf (stderr, "%s: %s: %s", Prog,
> +						 "tcsetpgrp", strerror (errno));
> +				}
> +			}
>  			kill (getpid (), SIGSTOP);
>  			/* wake child when resumed */
> -			kill (pid, SIGCONT);
> +			if (editor_pgrp != -1) {
> +				if (tcsetpgrp(STDIN_FILENO, editor_pgrp) == -1) {
> +					fprintf (stderr, "%s: %s: %s", Prog,
> +						 "tcsetpgrp", strerror (errno));
> +				}
> +			}
> +			killpg (pid, SIGCONT);
>  		} else {
>  			break;
>  		}
>  	}
>  
> +	if (orig_pgrp != -1)
> +		sigprocmask(SIG_SETMASK, &omask, NULL);
> +
>  	if (-1 == pid) {
>  		vipwexit (editor, 1, 1);
>  	} else if (   WIFEXITED (status)
> 
> -- System Information:
> Debian Release: 10.1
>   APT prefers stable-updates
>   APT policy: (500, 'stable-updates'), (500, 'stable')
> Architecture: amd64 (x86_64)
> 
> Kernel: Linux 4.19.0-6-amd64 (SMP w/2 CPU cores)
> Locale: LANG=en_US.UTF-8, LC_CTYPE=en_US.UTF-8 (charmap=UTF-8), LANGUAGE=en_US.UTF-8 (charmap=UTF-8)
> Shell: /bin/sh linked to /usr/bin/dash
> Init: systemd (via /run/systemd/system)
> LSM: AppArmor: enabled
> 
> Versions of packages passwd depends on:
> ii  libaudit1       1:2.8.4-3
> ii  libc6           2.28-10
> ii  libpam-modules  1.3.1-5
> ii  libpam0g        1.3.1-5
> ii  libselinux1     2.8-1+b1
> ii  libsemanage1    2.8-2
> 
> passwd recommends no packages.
> 
> passwd suggests no packages.
> 
> -- no debconf information
> 
> _______________________________________________
> Pkg-shadow-devel mailing list
> Pkg-shadow-devel at alioth-lists.debian.net
> https://alioth-lists.debian.net/cgi-bin/mailman/listinfo/pkg-shadow-devel



More information about the Pkg-shadow-devel mailing list