Bug 15020 - wordexp goes into busy loop after child process closes it's output file descriptor
Summary: wordexp goes into busy loop after child process closes it's output file descr...
Status: RESOLVED FIXED
Alias: None
Product: glibc
Classification: Unclassified
Component: libc (show other bugs)
Version: unspecified
: P2 normal
Target Milestone: 2.18
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2013-01-16 17:42 UTC by Aron Ujvari
Modified: 2014-06-13 19:03 UTC (History)
2 users (show)

See Also:
Host:
Target:
Build:
Last reconfirmed:
fweimer: security-


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Aron Ujvari 2013-01-16 17:42:07 UTC
When wordexp is used to execute shell command and run a program in that command the program may close it's output file descriptor before exits. The wordexp function reads the program output (from the read side of a pipe), but when the program closes it output file descriptor (the write side of a pipe), the wrodexp function goes into a busy loop.

An strace output when a program uses wordexp and goes into busy loop:

> 15:44:38.748956 pipe([5, 8])            = 0 <0.000016>
> 15:44:38.749027 clone(Process 20369 attached
> child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x42292b48) = 20369 <0.001831>
> [pid 30525] 15:44:38.750969 close(8 <unfinished ...>
> [pid 20369] 15:44:38.751074 dup2(8, 1 <unfinished ...>
> [pid 30525] 15:44:38.751098 <... close resumed> ) = 0 <0.000096>
> [pid 20369] 15:44:38.751127 <... dup2 resumed> ) = 1 <0.000035>
> [pid 30525] 15:44:38.751150 read(5,  <unfinished ...>
> [pid 20369] 15:44:38.751180 close(8)    = 0 <0.000008>
> [pid 20369] 15:44:38.751216 close(2)    = 0 <0.000035>
> [pid 20369] 15:44:38.751285 open("/dev/null", O_WRONLY) = 2 <0.000018>
> [pid 20369] 15:44:38.751347 fstat64(2, {st_mode=S_IFCHR|0666, st_rdev=makedev(1, 3), ...}) = 0 <0.000008>
> [pid 20369] 15:44:38.751437 close(5)    = 0 <0.000007>
> [pid 20369] 15:44:38.751477 execve("/bin/sh", ["/bin/sh", "-c", "/usr/bin/stat -c %s /tmp/med30525f000000"], [/* 122 vars */]) = 0 <0.001610>
> ...
> [pid 20369] 15:44:38.826332 write(1, "11486\n", 6 <unfinished ...>
> [pid 30525] 15:44:38.826473 <... read resumed> "11486\n", 128) = 6 <0.075257>
> [pid 20369] 15:44:38.826578 <... write resumed> ) = 6 <0.000173>
> [pid 30525] 15:44:38.826661 read(5,  <unfinished ...>
> [pid 20369] 15:44:38.826773 close(1 <unfinished ...>
> [pid 30525] 15:44:38.826881 <... read resumed> "", 128) = 0 <0.000121>
> [pid 20369] 15:44:38.826962 <... close resumed> ) = 0 <0.000129>
> [pid 30525] 15:44:38.827037 waitpid(20369, 0xbffecfc0, WNOHANG) = 0 <0.000008>
> [pid 30525] 15:44:38.827182 read(5, "", 128) = 0 <0.000007>
> [pid 30525] 15:44:38.827311 waitpid(20369, 0xbffecfc0, WNOHANG) = 0 <0.000008>
> [pid 30525] 15:44:38.827431 read(5, "", 128) = 0 <0.000008>
> ...
> [pid 30525] 15:44:38.997549 read(5, "", 128) = 0 <0.000007>
> [pid 30525] 15:44:38.997586 waitpid(20369, 0xbffecfc0, WNOHANG) = 0 <0.000007>
> [pid 30525] 15:44:38.997624 read(5, "", 128) = 0 <0.000007>
> [pid 30525] 15:44:38.997661 waitpid(20369, 0xbffecfc0, WNOHANG) = 0 <0.000007>
> [pid 30525] 15:44:38.997699 read(5, "", 128) = 0 <0.000007>
> [pid 30525] 15:44:38.997737 waitpid(20369,  <unfinished ...>
> [pid 20369] 15:44:38.997777 <... munmap resumed> ) = 0 <0.131820>
> [pid 30525] 15:44:38.997826 <... waitpid resumed> 0xbffecfc0, WNOHANG) = 0 <0.000072>
> [pid 20369] 15:44:38.997854 exit_group(0) = ?
Process 20369 detached
> 15:44:38.997986 read(5, "", 128)        = 0 <0.000007>
> 15:44:38.998026 waitpid(20369, 0xbffecfc0, WNOHANG) = -1 ECHILD (No child processes) <0.000007>
> 15:44:38.998070 read(5, "", 128)        = 0 <0.000006>
> 15:44:38.998105 close(5)                = 0 <0.000013>

The corresponding wordexp code:

>  951       while (1)
>  952         {
>  953           if ((buflen = TEMP_FAILURE_RETRY (__read (fildes[0], buffer,
>  954                                                     bufsize))) < 1)
>  955             {
>  956               if (TEMP_FAILURE_RETRY (__waitpid (pid, &status, WNOHANG)) == 0)
>  957                 continue;
>  958               if ((buflen = TEMP_FAILURE_RETRY (__read (fildes[0], buffer,
>  959                                                         bufsize))) < 1)
>  960                 break;
>  961             }
>  ...
>  968         }
(This code snippet is duplicated at lines 981-991.)

Wordexp code tries to read from the pipe when it's write side already closed. On the closed pipe the read function does not block, but immediately returns with 0 as the read file handle is already at EOF. Then waitpid is called while the child process has not exited yet, therefore it returns with 0. This loop goes on till the child process has really exited and waitpid returns its PID.
Comment 1 Rich Felker 2013-01-17 22:57:15 UTC
Could you post a test case? I'm having a hard time understanding how the file descriptor could be closed by the shell before the shell exits.
Comment 2 Andreas Schwab 2013-01-18 00:02:30 UTC
wordexp("$(exec >&-; sleep 10)", p, 0);
Comment 3 Andreas Schwab 2013-01-21 16:16:35 UTC
Fixed in ab087e0.