Bug 248281
| Summary: | popen() inadvertently closes the child's stdout if the parent's fd 1 is another popen'd stream | ||
|---|---|---|---|
| Product: | [Fedora] Fedora | Reporter: | r6144 <rainy6144> |
| Component: | glibc | Assignee: | Jakub Jelinek <jakub> |
| Status: | CLOSED UPSTREAM | QA Contact: | Fedora Extras Quality Assurance <extras-qa> |
| Severity: | low | Docs Contact: | |
| Priority: | low | ||
| Version: | 6 | ||
| Target Milestone: | --- | ||
| Target Release: | --- | ||
| Hardware: | All | ||
| OS: | Linux | ||
| Whiteboard: | |||
| Fixed In Version: | Doc Type: | Bug Fix | |
| Doc Text: | Story Points: | --- | |
| Clone Of: | Environment: | ||
| Last Closed: | 2007-07-20 07:53:31 UTC | Type: | --- |
| Regression: | --- | Mount Type: | --- |
| Documentation: | --- | CRM: | |
| Verified Versions: | Category: | --- | |
| oVirt Team: | --- | RHEL 7.3 requirements from Atomic Host: | |
| Cloudforms Team: | --- | Target Upstream Version: | |
| Embargoed: | |||
Fixed upstream, will eventually make it into F7 updates and if there will be still FC6 glibc updates, there as well. glibc has been updated in both F7 and FC6 very recently, so it won't be updated again in the very near future. |
Description of problem: The program below gives the error message "sh: line 0: echo: write error: Bad file descriptor". #include <assert.h> #include <stdio.h> int main(void) { FILE *p1, *p2; int result1, result2; fclose(stdout); p1 = popen("echo a", "r"); assert(p1 != NULL); p2 = popen("echo b", "r"); assert(p2 != NULL); result1 = pclose(p1); result2 = pclose(p2); fprintf(stderr, "result1 = %d, result2 = %d\n", result1, result2); return 0; } I dug into this problem with "strace -f", and found that the stdout fd (fd 1) of the child process p2 (pid 5072) got closed before the execve(). pipe([1, 4]) = 0 clone(Process 5071 attached child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0xb7f55708) = 5071 [pid 5070] close(4) = 0 [pid 5070] pipe([4, 5]) = 0 [pid 5070] clone(Process 5072 attached child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0xb7f55708) = 5072 [pid 5070] close(5) = 0 [pid 5070] close(1) = 0 [pid 5070] waitpid(5071, Process 5070 suspended <unfinished ...> [pid 5071] close(1 <unfinished ...> [pid 5072] close(4 <unfinished ...> [pid 5071] <... close resumed> ) = 0 [pid 5072] <... close resumed> ) = 0 [pid 5071] dup2(4, 1 <unfinished ...> [pid 5072] dup2(5, 1 <unfinished ...> [pid 5071] <... dup2 resumed> ) = 1 [pid 5072] <... dup2 resumed> ) = 1 [pid 5071] close(4 <unfinished ...> [pid 5072] close(5 <unfinished ...> [pid 5071] <... close resumed> ) = 0 [pid 5072] <... close resumed> ) = 0 [pid 5071] execve("/bin/sh", ["sh", "-c", "echo a"], [/* 51 vars */] <unfinished ...> [pid 5072] close(1 <unfinished ...> [pid 5071] <... execve resumed> ) = 0 [pid 5072] <... close resumed> ) = 0 [pid 5072] execve("/bin/sh", ["sh", "-c", "echo b"], [/* 51 vars */] <unfinished ...> This seems to be caused by the following code in libc/libio/iopopen.c: _IO_close (parent_end); if (child_end != child_std_end) { _IO_dup2 (child_end, child_std_end); _IO_close (child_end); } /* POSIX.2: "popen() shall ensure that any streams from previous popen() calls that remain open in the parent process are closed in the new child process." */ for (p = proc_file_chain; p; p = p->next) _IO_close (_IO_fileno ((_IO_FILE *) p)); In our case, when doing the second popen(), the first popen()'s fd is 1 and it has just been overwritten by the dup2() call, so it is wrong to close it. Version-Release number of selected component (if applicable): glibc-2.5-10.fc6 How reproducible: Always Steps to Reproduce: 1. Compile the program above and run it. Actual results: sh: line 0: echo: write error: Bad file descriptor result1 = 0, result2 = 256 (result1 might also be 13 (SIGPIPE) if the parent pclose()'d before the child writes its output.) Expected results: result1 = 0, result2 = 0 (Either or both results might also be 13.)