From fw@abraxis.ask.fh-furtwangen.de  Sat Dec 17 22:38:58 2005
Return-Path: <fw@abraxis.ask.fh-furtwangen.de>
Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125])
	by hub.freebsd.org (Postfix) with ESMTP id 1A8FC16A41F
	for <FreeBSD-gnats-submit@freebsd.org>; Sat, 17 Dec 2005 22:38:58 +0000 (GMT)
	(envelope-from fw@abraxis.ask.fh-furtwangen.de)
Received: from ohjemine.ask.rz-int.fh-furtwangen.de (Ohjemine.ASK.rz-int.FH-Furtwangen.DE [141.28.15.41])
	by mx1.FreeBSD.org (Postfix) with ESMTP id A28A543D58
	for <FreeBSD-gnats-submit@freebsd.org>; Sat, 17 Dec 2005 22:38:57 +0000 (GMT)
	(envelope-from fw@abraxis.ask.fh-furtwangen.de)
Received: from abraxis.ask.fh-furtwangen.de (abraxis.ASK.FH-Furtwangen.DE [141.28.225.143])
	(using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits))
	(No client certificate requested)
	by weltmacht.ask.fh-furtwangen.de (Postfix) with ESMTP id E52F578C96
	for <FreeBSD-gnats-submit@freebsd.org>; Sat, 17 Dec 2005 23:38:55 +0100 (CET)
Received: by abraxis.ask.fh-furtwangen.de (Postfix, from userid 1001)
	id B65E4242; Sat, 17 Dec 2005 23:42:27 +0100 (CET)
Message-Id: <20051217224227.B65E4242@abraxis.ask.fh-furtwangen.de>
Date: Sat, 17 Dec 2005 23:42:27 +0100 (CET)
From: Florian Westphal <westphal@foo.fh-furtwangen.de>
To: FreeBSD-gnats-submit@freebsd.org
Cc:
Subject: wordexp(3) fails to check for EINTR	
X-Send-Pr-Version: 3.113
X-GNATS-Notify:

>Number:         90580
>Category:       kern
>Synopsis:       [libc] wordexp(3) fails to check for EINTR
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    jilles
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Sat Dec 17 22:40:01 GMT 2005
>Closed-Date:    Thu Dec 02 23:26:06 UTC 2010
>Last-Modified:  Thu Dec 02 23:26:06 UTC 2010
>Originator:     Florian Westphal
>Release:        FreeBSD 5.4-RELEASE-p6 i386
>Organization:
>Environment:
System: FreeBSD abraxis.ask.fh-furtwangen.de 5.4-RELEASE-p6 FreeBSD 5.4-RELEASE-p6 #12: Wed Aug 17 09:26:21 CEST 2005 root@abraxis.ask.fh-furtwangen.de:/usr/obj/usr/src/sys/ABRAXIS i386

	AMD k6/233 mhz, 32meg ram
>Description:
	wordexp() fails if a syscall fails; no check for errno == EINTR is performed.
	Applications that install signal handlers for SIGCHLD will be in a race with
	wordexp. The program below often fails on my box.
>How-To-Repeat:
/* This sample program MAY work or not work, depending on timing */ 
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>	
#include <wordexp.h>

static void handler(int x) { (void)x; } 

int main(void)
{
	struct sigaction sa;
	const char * expand = "*";
	wordexp_t p;
	int ret;

        sa.sa_flags = 0;
        sigemptyset(&sa.sa_mask);
        sa.sa_handler = handler;
        if (sigaction(SIGCHLD,&sa, NULL) != 0)
		return 111;

	errno = 0;	
        ret = wordexp(expand, &p, WRDE_SHOWERR|WRDE_UNDEF);
        if (ret == 0) {
                unsigned int i;
                char **w = p.we_wordv;
                for (i=0; i<p.we_wordc; i++)
                        printf("got token: \"%s\"\n", w[i]);

                wordfree(&p);
        } else {
		printf("wordexp failed, ret %d, err %s\n:", ret, strerror(errno));
	}
	return 0;
}
>Fix:
	Have wordexp() check for errno == EINTR


>Release-Note:
>Audit-Trail:

From: dfilter@FreeBSD.ORG (dfilter service)
To: bug-followup@FreeBSD.org
Cc:  
Subject: Re: kern/90580: commit references a PR
Date: Fri, 23 Oct 2009 14:50:26 +0000 (UTC)

 Author: jilles
 Date: Fri Oct 23 14:50:11 2009
 New Revision: 198406
 URL: http://svn.freebsd.org/changeset/base/198406
 
 Log:
   wordexp(3): fix some bugs with signals and long outputs
   * retry various system calls on EINTR
   * retry the rest after a short read (common if there is more than about 1K
     of output)
   * block SIGCHLD like system(3) does (note that this does not and cannot
     work fully in threaded programs, they will need to be careful with wait
     functions)
   
   PR:		90580
   MFC after:	1 month
 
 Modified:
   head/lib/libc/gen/wordexp.c
   head/tools/regression/lib/libc/gen/test-wordexp.c
 
 Modified: head/lib/libc/gen/wordexp.c
 ==============================================================================
 --- head/lib/libc/gen/wordexp.c	Fri Oct 23 14:43:17 2009	(r198405)
 +++ head/lib/libc/gen/wordexp.c	Fri Oct 23 14:50:11 2009	(r198406)
 @@ -28,8 +28,10 @@
  #include <sys/cdefs.h>
  #include <sys/types.h>
  #include <sys/wait.h>
 +#include <errno.h>
  #include <fcntl.h>
  #include <paths.h>
 +#include <signal.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
 @@ -73,6 +75,24 @@ wordexp(const char * __restrict words, w
  	return (0);
  }
  
 +static size_t
 +we_read_fully(int fd, char *buffer, size_t len)
 +{
 +	size_t done;
 +	ssize_t nread;
 +
 +	done = 0;
 +	do {
 +		nread = _read(fd, buffer + done, len - done);
 +		if (nread == -1 && errno == EINTR)
 +			continue;
 +		if (nread <= 0)
 +			break;
 +		done += nread;
 +	} while (done != len);
 +	return done;
 +}
 +
  /*
   * we_askshell --
   *	Use the `wordexp' /bin/sh builtin function to do most of the work
 @@ -90,20 +110,31 @@ we_askshell(const char *words, wordexp_t
  	size_t sofs;			/* Offset into we->we_strings */
  	size_t vofs;			/* Offset into we->we_wordv */
  	pid_t pid;			/* Process ID of child */
 +	pid_t wpid;			/* waitpid return value */
  	int status;			/* Child exit status */
 +	int error;			/* Our return value */
 +	int serrno;			/* errno to return */
  	char *ifs;			/* IFS env. var. */
  	char *np, *p;			/* Handy pointers */
  	char *nstrings;			/* Temporary for realloc() */
  	char **nwv;			/* Temporary for realloc() */
 +	sigset_t newsigblock, oldsigblock;
  
 +	serrno = errno;
  	if ((ifs = getenv("IFS")) == NULL)
  		ifs = " \t\n";
  
  	if (pipe(pdes) < 0)
  		return (WRDE_NOSPACE);	/* XXX */
 +	(void)sigemptyset(&newsigblock);
 +	(void)sigaddset(&newsigblock, SIGCHLD);
 +	(void)_sigprocmask(SIG_BLOCK, &newsigblock, &oldsigblock);
  	if ((pid = fork()) < 0) {
 +		serrno = errno;
  		_close(pdes[0]);
  		_close(pdes[1]);
 +		(void)_sigprocmask(SIG_SETMASK, &oldsigblock, NULL);
 +		errno = serrno;
  		return (WRDE_NOSPACE);	/* XXX */
  	}
  	else if (pid == 0) {
 @@ -114,6 +145,7 @@ we_askshell(const char *words, wordexp_t
  		int devnull;
  		char *cmd;
  
 +		(void)_sigprocmask(SIG_SETMASK, &oldsigblock, NULL);
  		_close(pdes[0]);
  		if (_dup2(pdes[1], STDOUT_FILENO) < 0)
  			_exit(1);
 @@ -139,10 +171,11 @@ we_askshell(const char *words, wordexp_t
  	 * the expanded words separated by nulls.
  	 */
  	_close(pdes[1]);
 -	if (_read(pdes[0], wbuf, 8) != 8 || _read(pdes[0], bbuf, 8) != 8) {
 -		_close(pdes[0]);
 -		_waitpid(pid, &status, 0);
 -		return (flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX);
 +	if (we_read_fully(pdes[0], wbuf, 8) != 8 ||
 +			we_read_fully(pdes[0], bbuf, 8) != 8) {
 +		error = flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX;
 +		serrno = errno;
 +		goto cleanup;
  	}
  	wbuf[8] = bbuf[8] = '\0';
  	nwords = strtol(wbuf, NULL, 16);
 @@ -162,33 +195,38 @@ we_askshell(const char *words, wordexp_t
  	if ((nwv = realloc(we->we_wordv, (we->we_wordc + 1 +
  	    (flags & WRDE_DOOFFS ?  we->we_offs : 0)) *
  	    sizeof(char *))) == NULL) {
 -		_close(pdes[0]);
 -		_waitpid(pid, &status, 0);
 -		return (WRDE_NOSPACE);
 +		error = WRDE_NOSPACE;
 +		goto cleanup;
  	}
  	we->we_wordv = nwv;
  	if ((nstrings = realloc(we->we_strings, we->we_nbytes)) == NULL) {
 -		_close(pdes[0]);
 -		_waitpid(pid, &status, 0);
 -		return (WRDE_NOSPACE);
 +		error = WRDE_NOSPACE;
 +		goto cleanup;
  	}
  	for (i = 0; i < vofs; i++)
  		if (we->we_wordv[i] != NULL)
  			we->we_wordv[i] += nstrings - we->we_strings;
  	we->we_strings = nstrings;
  
 -	if (_read(pdes[0], we->we_strings + sofs, nbytes) != nbytes) {
 -		_close(pdes[0]);
 -		_waitpid(pid, &status, 0);
 -		return (flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX);
 +	if (we_read_fully(pdes[0], we->we_strings + sofs, nbytes) != nbytes) {
 +		error = flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX;
 +		serrno = errno;
 +		goto cleanup;
  	}
  
 -	if (_waitpid(pid, &status, 0) < 0 || !WIFEXITED(status) ||
 -	    WEXITSTATUS(status) != 0) {
 -		_close(pdes[0]);
 -		return (flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX);
 -	}
 +	error = 0;
 +cleanup:
  	_close(pdes[0]);
 +	do
 +		wpid = _waitpid(pid, &status, 0);
 +	while (wpid < 0 && errno == EINTR);
 +	(void)_sigprocmask(SIG_SETMASK, &oldsigblock, NULL);
 +	if (error != 0) {
 +		errno = serrno;
 +		return (error);
 +	}
 +	if (wpid < 0 || !WIFEXITED(status) || WEXITSTATUS(status) != 0)
 +		return (flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX);
  
  	/*
  	 * Break the null-terminated expanded word strings out into
 
 Modified: head/tools/regression/lib/libc/gen/test-wordexp.c
 ==============================================================================
 --- head/tools/regression/lib/libc/gen/test-wordexp.c	Fri Oct 23 14:43:17 2009	(r198405)
 +++ head/tools/regression/lib/libc/gen/test-wordexp.c	Fri Oct 23 14:50:11 2009	(r198406)
 @@ -32,17 +32,36 @@
  #include <sys/cdefs.h>
  __FBSDID("$FreeBSD$");
  
 +#include <sys/wait.h>
 +
  #include <assert.h>
 +#include <errno.h>
 +#include <signal.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  #include <wordexp.h>
  
 +static void
 +chld_handler(int x)
 +{
 +	int status, serrno;
 +
 +	(void)x;
 +	serrno = errno;
 +	while (waitpid(-1, &status, WNOHANG) > 0)
 +		;
 +	errno = serrno;
 +}
 +
  int
  main(int argc, char *argv[])
  {
 +	struct sigaction sa;
  	wordexp_t we;
  	int r;
 +	int i;
 +	char longdata[6 * 10000 + 1];
  
  	/* Test that the macros are there. */
  	(void)(WRDE_APPEND + WRDE_DOOFFS + WRDE_NOCMD + WRDE_REUSE +
 @@ -59,6 +78,15 @@ main(int argc, char *argv[])
  	assert(we.we_wordv[2] == NULL);
  	wordfree(&we);
  
 +	/* Long output. */
 +	for (i = 0; i < 10000; i++)
 +		snprintf(longdata + 6 * i, 7, "%05d ", i);
 +	r = wordexp(longdata, &we, 0);
 +	assert(r == 0);
 +	assert(we.we_wordc == 10000);
 +	assert(we.we_wordv[10000] == NULL);
 +	wordfree(&we);
 +
  	/* WRDE_DOOFFS */
  	we.we_offs = 3;
  	r = wordexp("hello world", &we, WRDE_DOOFFS);
 @@ -167,6 +195,20 @@ main(int argc, char *argv[])
  	r = wordexp("test } test", &we, 0);
  	assert(r == WRDE_BADCHAR);
  
 +	/* With a SIGCHLD handler that reaps all zombies. */
 +	sa.sa_flags = 0;
 +	sigemptyset(&sa.sa_mask);
 +	sa.sa_handler = chld_handler;
 +	r = sigaction(SIGCHLD, &sa, NULL);
 +	assert(r == 0);
 +	r = wordexp("hello world", &we, 0);
 +	assert(r == 0);
 +	assert(we.we_wordc == 2);
 +	assert(strcmp(we.we_wordv[0], "hello") == 0);
 +	assert(strcmp(we.we_wordv[1], "world") == 0);
 +	assert(we.we_wordv[2] == NULL);
 +	wordfree(&we);
 +
  	printf("PASS wordexp()\n");
  	printf("PASS wordfree()\n");
  
 _______________________________________________
 svn-src-all@freebsd.org mailing list
 http://lists.freebsd.org/mailman/listinfo/svn-src-all
 To unsubscribe, send any mail to "svn-src-all-unsubscribe@freebsd.org"
 
Responsible-Changed-From-To: freebsd-bugs->jilles 
Responsible-Changed-By: jilles 
Responsible-Changed-When: Fri Oct 23 15:06:18 UTC 2009 
Responsible-Changed-Why:  
I'll take it. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=90580 
State-Changed-From-To: open->patched 
State-Changed-By: jilles 
State-Changed-When: Fri Oct 23 15:07:15 UTC 2009 
State-Changed-Why:  
Fixed in -current. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=90580 

From: dfilter@FreeBSD.ORG (dfilter service)
To: bug-followup@FreeBSD.org
Cc:  
Subject: Re: kern/90580: commit references a PR
Date: Sun,  6 Dec 2009 22:15:11 +0000 (UTC)

 Author: jilles
 Date: Sun Dec  6 22:14:58 2009
 New Revision: 200189
 URL: http://svn.freebsd.org/changeset/base/200189
 
 Log:
   MFC r198406: wordexp(3): fix some bugs with signals and long outputs
   
   * retry various system calls on EINTR
   * retry the rest after a short read (common if there is more than about 1K
     of output)
   * block SIGCHLD like system(3) does (note that this does not and cannot
     work fully in threaded programs, they will need to be careful with wait
     functions)
   
   PR:		90580
 
 Modified:
   stable/8/lib/libc/gen/wordexp.c
   stable/8/tools/regression/lib/libc/gen/test-wordexp.c
 Directory Properties:
   stable/8/lib/libc/   (props changed)
   stable/8/lib/libc/stdtime/   (props changed)
   stable/8/tools/regression/lib/libc/   (props changed)
 
 Modified: stable/8/lib/libc/gen/wordexp.c
 ==============================================================================
 --- stable/8/lib/libc/gen/wordexp.c	Sun Dec  6 22:01:45 2009	(r200188)
 +++ stable/8/lib/libc/gen/wordexp.c	Sun Dec  6 22:14:58 2009	(r200189)
 @@ -28,8 +28,10 @@
  #include <sys/cdefs.h>
  #include <sys/types.h>
  #include <sys/wait.h>
 +#include <errno.h>
  #include <fcntl.h>
  #include <paths.h>
 +#include <signal.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
 @@ -73,6 +75,24 @@ wordexp(const char * __restrict words, w
  	return (0);
  }
  
 +static size_t
 +we_read_fully(int fd, char *buffer, size_t len)
 +{
 +	size_t done;
 +	ssize_t nread;
 +
 +	done = 0;
 +	do {
 +		nread = _read(fd, buffer + done, len - done);
 +		if (nread == -1 && errno == EINTR)
 +			continue;
 +		if (nread <= 0)
 +			break;
 +		done += nread;
 +	} while (done != len);
 +	return done;
 +}
 +
  /*
   * we_askshell --
   *	Use the `wordexp' /bin/sh builtin function to do most of the work
 @@ -90,20 +110,31 @@ we_askshell(const char *words, wordexp_t
  	size_t sofs;			/* Offset into we->we_strings */
  	size_t vofs;			/* Offset into we->we_wordv */
  	pid_t pid;			/* Process ID of child */
 +	pid_t wpid;			/* waitpid return value */
  	int status;			/* Child exit status */
 +	int error;			/* Our return value */
 +	int serrno;			/* errno to return */
  	char *ifs;			/* IFS env. var. */
  	char *np, *p;			/* Handy pointers */
  	char *nstrings;			/* Temporary for realloc() */
  	char **nwv;			/* Temporary for realloc() */
 +	sigset_t newsigblock, oldsigblock;
  
 +	serrno = errno;
  	if ((ifs = getenv("IFS")) == NULL)
  		ifs = " \t\n";
  
  	if (pipe(pdes) < 0)
  		return (WRDE_NOSPACE);	/* XXX */
 +	(void)sigemptyset(&newsigblock);
 +	(void)sigaddset(&newsigblock, SIGCHLD);
 +	(void)_sigprocmask(SIG_BLOCK, &newsigblock, &oldsigblock);
  	if ((pid = fork()) < 0) {
 +		serrno = errno;
  		_close(pdes[0]);
  		_close(pdes[1]);
 +		(void)_sigprocmask(SIG_SETMASK, &oldsigblock, NULL);
 +		errno = serrno;
  		return (WRDE_NOSPACE);	/* XXX */
  	}
  	else if (pid == 0) {
 @@ -114,6 +145,7 @@ we_askshell(const char *words, wordexp_t
  		int devnull;
  		char *cmd;
  
 +		(void)_sigprocmask(SIG_SETMASK, &oldsigblock, NULL);
  		_close(pdes[0]);
  		if (_dup2(pdes[1], STDOUT_FILENO) < 0)
  			_exit(1);
 @@ -139,10 +171,11 @@ we_askshell(const char *words, wordexp_t
  	 * the expanded words separated by nulls.
  	 */
  	_close(pdes[1]);
 -	if (_read(pdes[0], wbuf, 8) != 8 || _read(pdes[0], bbuf, 8) != 8) {
 -		_close(pdes[0]);
 -		_waitpid(pid, &status, 0);
 -		return (flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX);
 +	if (we_read_fully(pdes[0], wbuf, 8) != 8 ||
 +			we_read_fully(pdes[0], bbuf, 8) != 8) {
 +		error = flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX;
 +		serrno = errno;
 +		goto cleanup;
  	}
  	wbuf[8] = bbuf[8] = '\0';
  	nwords = strtol(wbuf, NULL, 16);
 @@ -162,33 +195,38 @@ we_askshell(const char *words, wordexp_t
  	if ((nwv = realloc(we->we_wordv, (we->we_wordc + 1 +
  	    (flags & WRDE_DOOFFS ?  we->we_offs : 0)) *
  	    sizeof(char *))) == NULL) {
 -		_close(pdes[0]);
 -		_waitpid(pid, &status, 0);
 -		return (WRDE_NOSPACE);
 +		error = WRDE_NOSPACE;
 +		goto cleanup;
  	}
  	we->we_wordv = nwv;
  	if ((nstrings = realloc(we->we_strings, we->we_nbytes)) == NULL) {
 -		_close(pdes[0]);
 -		_waitpid(pid, &status, 0);
 -		return (WRDE_NOSPACE);
 +		error = WRDE_NOSPACE;
 +		goto cleanup;
  	}
  	for (i = 0; i < vofs; i++)
  		if (we->we_wordv[i] != NULL)
  			we->we_wordv[i] += nstrings - we->we_strings;
  	we->we_strings = nstrings;
  
 -	if (_read(pdes[0], we->we_strings + sofs, nbytes) != nbytes) {
 -		_close(pdes[0]);
 -		_waitpid(pid, &status, 0);
 -		return (flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX);
 +	if (we_read_fully(pdes[0], we->we_strings + sofs, nbytes) != nbytes) {
 +		error = flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX;
 +		serrno = errno;
 +		goto cleanup;
  	}
  
 -	if (_waitpid(pid, &status, 0) < 0 || !WIFEXITED(status) ||
 -	    WEXITSTATUS(status) != 0) {
 -		_close(pdes[0]);
 -		return (flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX);
 -	}
 +	error = 0;
 +cleanup:
  	_close(pdes[0]);
 +	do
 +		wpid = _waitpid(pid, &status, 0);
 +	while (wpid < 0 && errno == EINTR);
 +	(void)_sigprocmask(SIG_SETMASK, &oldsigblock, NULL);
 +	if (error != 0) {
 +		errno = serrno;
 +		return (error);
 +	}
 +	if (wpid < 0 || !WIFEXITED(status) || WEXITSTATUS(status) != 0)
 +		return (flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX);
  
  	/*
  	 * Break the null-terminated expanded word strings out into
 
 Modified: stable/8/tools/regression/lib/libc/gen/test-wordexp.c
 ==============================================================================
 --- stable/8/tools/regression/lib/libc/gen/test-wordexp.c	Sun Dec  6 22:01:45 2009	(r200188)
 +++ stable/8/tools/regression/lib/libc/gen/test-wordexp.c	Sun Dec  6 22:14:58 2009	(r200189)
 @@ -32,17 +32,36 @@
  #include <sys/cdefs.h>
  __FBSDID("$FreeBSD$");
  
 +#include <sys/wait.h>
 +
  #include <assert.h>
 +#include <errno.h>
 +#include <signal.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  #include <wordexp.h>
  
 +static void
 +chld_handler(int x)
 +{
 +	int status, serrno;
 +
 +	(void)x;
 +	serrno = errno;
 +	while (waitpid(-1, &status, WNOHANG) > 0)
 +		;
 +	errno = serrno;
 +}
 +
  int
  main(int argc, char *argv[])
  {
 +	struct sigaction sa;
  	wordexp_t we;
  	int r;
 +	int i;
 +	char longdata[6 * 10000 + 1];
  
  	/* Test that the macros are there. */
  	(void)(WRDE_APPEND + WRDE_DOOFFS + WRDE_NOCMD + WRDE_REUSE +
 @@ -59,6 +78,15 @@ main(int argc, char *argv[])
  	assert(we.we_wordv[2] == NULL);
  	wordfree(&we);
  
 +	/* Long output. */
 +	for (i = 0; i < 10000; i++)
 +		snprintf(longdata + 6 * i, 7, "%05d ", i);
 +	r = wordexp(longdata, &we, 0);
 +	assert(r == 0);
 +	assert(we.we_wordc == 10000);
 +	assert(we.we_wordv[10000] == NULL);
 +	wordfree(&we);
 +
  	/* WRDE_DOOFFS */
  	we.we_offs = 3;
  	r = wordexp("hello world", &we, WRDE_DOOFFS);
 @@ -167,6 +195,20 @@ main(int argc, char *argv[])
  	r = wordexp("test } test", &we, 0);
  	assert(r == WRDE_BADCHAR);
  
 +	/* With a SIGCHLD handler that reaps all zombies. */
 +	sa.sa_flags = 0;
 +	sigemptyset(&sa.sa_mask);
 +	sa.sa_handler = chld_handler;
 +	r = sigaction(SIGCHLD, &sa, NULL);
 +	assert(r == 0);
 +	r = wordexp("hello world", &we, 0);
 +	assert(r == 0);
 +	assert(we.we_wordc == 2);
 +	assert(strcmp(we.we_wordv[0], "hello") == 0);
 +	assert(strcmp(we.we_wordv[1], "world") == 0);
 +	assert(we.we_wordv[2] == NULL);
 +	wordfree(&we);
 +
  	printf("PASS wordexp()\n");
  	printf("PASS wordfree()\n");
  
 _______________________________________________
 svn-src-all@freebsd.org mailing list
 http://lists.freebsd.org/mailman/listinfo/svn-src-all
 To unsubscribe, send any mail to "svn-src-all-unsubscribe@freebsd.org"
 

From: dfilter@FreeBSD.ORG (dfilter service)
To: bug-followup@FreeBSD.org
Cc:  
Subject: Re: kern/90580: commit references a PR
Date: Wed, 17 Nov 2010 21:45:17 +0000 (UTC)

 Author: jilles
 Date: Wed Nov 17 21:45:11 2010
 New Revision: 215442
 URL: http://svn.freebsd.org/changeset/base/215442
 
 Log:
   MFC r198406: wordexp(3): fix some bugs with signals and long outputs
   * retry various system calls on EINTR
   * retry the rest after a short read (common if there is more than about 1K
     of output)
   * block SIGCHLD like system(3) does (note that this does not and cannot
     work fully in threaded programs, they will need to be careful with wait
     functions)
   
   PR:		90580
 
 Modified:
   stable/7/lib/libc/gen/wordexp.c
   stable/7/tools/regression/lib/libc/gen/test-wordexp.c
 Directory Properties:
   stable/7/lib/libc/   (props changed)
   stable/7/lib/libc/stdtime/   (props changed)
   stable/7/tools/regression/lib/libc/   (props changed)
 
 Modified: stable/7/lib/libc/gen/wordexp.c
 ==============================================================================
 --- stable/7/lib/libc/gen/wordexp.c	Wed Nov 17 20:37:16 2010	(r215441)
 +++ stable/7/lib/libc/gen/wordexp.c	Wed Nov 17 21:45:11 2010	(r215442)
 @@ -28,8 +28,10 @@
  #include <sys/cdefs.h>
  #include <sys/types.h>
  #include <sys/wait.h>
 +#include <errno.h>
  #include <fcntl.h>
  #include <paths.h>
 +#include <signal.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
 @@ -73,6 +75,24 @@ wordexp(const char * __restrict words, w
  	return (0);
  }
  
 +static size_t
 +we_read_fully(int fd, char *buffer, size_t len)
 +{
 +	size_t done;
 +	ssize_t nread;
 +
 +	done = 0;
 +	do {
 +		nread = _read(fd, buffer + done, len - done);
 +		if (nread == -1 && errno == EINTR)
 +			continue;
 +		if (nread <= 0)
 +			break;
 +		done += nread;
 +	} while (done != len);
 +	return done;
 +}
 +
  /*
   * we_askshell --
   *	Use the `wordexp' /bin/sh builtin function to do most of the work
 @@ -90,20 +110,31 @@ we_askshell(const char *words, wordexp_t
  	size_t sofs;			/* Offset into we->we_strings */
  	size_t vofs;			/* Offset into we->we_wordv */
  	pid_t pid;			/* Process ID of child */
 +	pid_t wpid;			/* waitpid return value */
  	int status;			/* Child exit status */
 +	int error;			/* Our return value */
 +	int serrno;			/* errno to return */
  	char *ifs;			/* IFS env. var. */
  	char *np, *p;			/* Handy pointers */
  	char *nstrings;			/* Temporary for realloc() */
  	char **nwv;			/* Temporary for realloc() */
 +	sigset_t newsigblock, oldsigblock;
  
 +	serrno = errno;
  	if ((ifs = getenv("IFS")) == NULL)
  		ifs = " \t\n";
  
  	if (pipe(pdes) < 0)
  		return (WRDE_NOSPACE);	/* XXX */
 +	(void)sigemptyset(&newsigblock);
 +	(void)sigaddset(&newsigblock, SIGCHLD);
 +	(void)_sigprocmask(SIG_BLOCK, &newsigblock, &oldsigblock);
  	if ((pid = fork()) < 0) {
 +		serrno = errno;
  		_close(pdes[0]);
  		_close(pdes[1]);
 +		(void)_sigprocmask(SIG_SETMASK, &oldsigblock, NULL);
 +		errno = serrno;
  		return (WRDE_NOSPACE);	/* XXX */
  	}
  	else if (pid == 0) {
 @@ -114,6 +145,7 @@ we_askshell(const char *words, wordexp_t
  		int devnull;
  		char *cmd;
  
 +		(void)_sigprocmask(SIG_SETMASK, &oldsigblock, NULL);
  		_close(pdes[0]);
  		if (_dup2(pdes[1], STDOUT_FILENO) < 0)
  			_exit(1);
 @@ -139,10 +171,11 @@ we_askshell(const char *words, wordexp_t
  	 * the expanded words separated by nulls.
  	 */
  	_close(pdes[1]);
 -	if (_read(pdes[0], wbuf, 8) != 8 || _read(pdes[0], bbuf, 8) != 8) {
 -		_close(pdes[0]);
 -		_waitpid(pid, &status, 0);
 -		return (flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX);
 +	if (we_read_fully(pdes[0], wbuf, 8) != 8 ||
 +			we_read_fully(pdes[0], bbuf, 8) != 8) {
 +		error = flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX;
 +		serrno = errno;
 +		goto cleanup;
  	}
  	wbuf[8] = bbuf[8] = '\0';
  	nwords = strtol(wbuf, NULL, 16);
 @@ -162,33 +195,38 @@ we_askshell(const char *words, wordexp_t
  	if ((nwv = realloc(we->we_wordv, (we->we_wordc + 1 +
  	    (flags & WRDE_DOOFFS ?  we->we_offs : 0)) *
  	    sizeof(char *))) == NULL) {
 -		_close(pdes[0]);
 -		_waitpid(pid, &status, 0);
 -		return (WRDE_NOSPACE);
 +		error = WRDE_NOSPACE;
 +		goto cleanup;
  	}
  	we->we_wordv = nwv;
  	if ((nstrings = realloc(we->we_strings, we->we_nbytes)) == NULL) {
 -		_close(pdes[0]);
 -		_waitpid(pid, &status, 0);
 -		return (WRDE_NOSPACE);
 +		error = WRDE_NOSPACE;
 +		goto cleanup;
  	}
  	for (i = 0; i < vofs; i++)
  		if (we->we_wordv[i] != NULL)
  			we->we_wordv[i] += nstrings - we->we_strings;
  	we->we_strings = nstrings;
  
 -	if (_read(pdes[0], we->we_strings + sofs, nbytes) != nbytes) {
 -		_close(pdes[0]);
 -		_waitpid(pid, &status, 0);
 -		return (flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX);
 +	if (we_read_fully(pdes[0], we->we_strings + sofs, nbytes) != nbytes) {
 +		error = flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX;
 +		serrno = errno;
 +		goto cleanup;
  	}
  
 -	if (_waitpid(pid, &status, 0) < 0 || !WIFEXITED(status) ||
 -	    WEXITSTATUS(status) != 0) {
 -		_close(pdes[0]);
 -		return (flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX);
 -	}
 +	error = 0;
 +cleanup:
  	_close(pdes[0]);
 +	do
 +		wpid = _waitpid(pid, &status, 0);
 +	while (wpid < 0 && errno == EINTR);
 +	(void)_sigprocmask(SIG_SETMASK, &oldsigblock, NULL);
 +	if (error != 0) {
 +		errno = serrno;
 +		return (error);
 +	}
 +	if (wpid < 0 || !WIFEXITED(status) || WEXITSTATUS(status) != 0)
 +		return (flags & WRDE_UNDEF ? WRDE_BADVAL : WRDE_SYNTAX);
  
  	/*
  	 * Break the null-terminated expanded word strings out into
 
 Modified: stable/7/tools/regression/lib/libc/gen/test-wordexp.c
 ==============================================================================
 --- stable/7/tools/regression/lib/libc/gen/test-wordexp.c	Wed Nov 17 20:37:16 2010	(r215441)
 +++ stable/7/tools/regression/lib/libc/gen/test-wordexp.c	Wed Nov 17 21:45:11 2010	(r215442)
 @@ -32,17 +32,36 @@
  #include <sys/cdefs.h>
  __FBSDID("$FreeBSD$");
  
 +#include <sys/wait.h>
 +
  #include <assert.h>
 +#include <errno.h>
 +#include <signal.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  #include <wordexp.h>
  
 +static void
 +chld_handler(int x)
 +{
 +	int status, serrno;
 +
 +	(void)x;
 +	serrno = errno;
 +	while (waitpid(-1, &status, WNOHANG) > 0)
 +		;
 +	errno = serrno;
 +}
 +
  int
  main(int argc, char *argv[])
  {
 +	struct sigaction sa;
  	wordexp_t we;
  	int r;
 +	int i;
 +	char longdata[6 * 10000 + 1];
  
  	/* Test that the macros are there. */
  	(void)(WRDE_APPEND + WRDE_DOOFFS + WRDE_NOCMD + WRDE_REUSE +
 @@ -59,6 +78,15 @@ main(int argc, char *argv[])
  	assert(we.we_wordv[2] == NULL);
  	wordfree(&we);
  
 +	/* Long output. */
 +	for (i = 0; i < 10000; i++)
 +		snprintf(longdata + 6 * i, 7, "%05d ", i);
 +	r = wordexp(longdata, &we, 0);
 +	assert(r == 0);
 +	assert(we.we_wordc == 10000);
 +	assert(we.we_wordv[10000] == NULL);
 +	wordfree(&we);
 +
  	/* WRDE_DOOFFS */
  	we.we_offs = 3;
  	r = wordexp("hello world", &we, WRDE_DOOFFS);
 @@ -167,6 +195,20 @@ main(int argc, char *argv[])
  	r = wordexp("test } test", &we, 0);
  	assert(r == WRDE_BADCHAR);
  
 +	/* With a SIGCHLD handler that reaps all zombies. */
 +	sa.sa_flags = 0;
 +	sigemptyset(&sa.sa_mask);
 +	sa.sa_handler = chld_handler;
 +	r = sigaction(SIGCHLD, &sa, NULL);
 +	assert(r == 0);
 +	r = wordexp("hello world", &we, 0);
 +	assert(r == 0);
 +	assert(we.we_wordc == 2);
 +	assert(strcmp(we.we_wordv[0], "hello") == 0);
 +	assert(strcmp(we.we_wordv[1], "world") == 0);
 +	assert(we.we_wordv[2] == NULL);
 +	wordfree(&we);
 +
  	printf("PASS wordexp()\n");
  	printf("PASS wordfree()\n");
  
 _______________________________________________
 svn-src-all@freebsd.org mailing list
 http://lists.freebsd.org/mailman/listinfo/svn-src-all
 To unsubscribe, send any mail to "svn-src-all-unsubscribe@freebsd.org"
 
State-Changed-From-To: patched->closed 
State-Changed-By: jilles 
State-Changed-When: Thu Dec 2 23:26:04 UTC 2010 
State-Changed-Why:  
Fixed in 7.x/8.x/9.x. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=90580 
>Unformatted:
