aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorJohn Baldwin <jhb@FreeBSD.org>2010-12-09 20:28:30 +0000
committerJohn Baldwin <jhb@FreeBSD.org>2010-12-09 20:28:30 +0000
commitcc3d85727dff868e59352aef7cff20b2aaa1a063 (patch)
treec7298cc52d28696c6859135fd5289360e37cf1f7 /lib
parentd1cf854b5df45ec607652c7fb1ac1e51a3218610 (diff)
downloadsrc-cc3d85727dff868e59352aef7cff20b2aaa1a063.tar.gz
src-cc3d85727dff868e59352aef7cff20b2aaa1a063.zip
When reopening a stream backed by an open file descriptor, do not close
the existing file descriptor. Instead, let dup2() atomically close the old file descriptor when assigning the newly opened file to the same descriptor. This closes a race in a multithreaded application where a concurrent open() could allocate the existing file descriptor in between the calls to close() and dup2(). PR: threads/79887 Submitted by: Dmitrij Tejblum tejblum of yandex-team.ru Reviewed by: davidxu MFC after: 1 week
Notes
Notes: svn path=/head/; revision=216334
Diffstat (limited to 'lib')
-rw-r--r--lib/libc/stdio/freopen.c21
1 files changed, 9 insertions, 12 deletions
diff --git a/lib/libc/stdio/freopen.c b/lib/libc/stdio/freopen.c
index d7184969a649..be7bc8a2f8d0 100644
--- a/lib/libc/stdio/freopen.c
+++ b/lib/libc/stdio/freopen.c
@@ -150,14 +150,6 @@ freopen(file, mode, fp)
/* Get a new descriptor to refer to the new file. */
f = _open(file, oflags, DEFFILEMODE);
- if (f < 0 && isopen) {
- /* If out of fd's close the old one and try again. */
- if (errno == ENFILE || errno == EMFILE) {
- (void) (*fp->_close)(fp->_cookie);
- isopen = 0;
- f = _open(file, oflags, DEFFILEMODE);
- }
- }
sverrno = errno;
finish:
@@ -165,9 +157,11 @@ finish:
* Finish closing fp. Even if the open succeeded above, we cannot
* keep fp->_base: it may be the wrong size. This loses the effect
* of any setbuffer calls, but stdio has always done this before.
+ *
+ * Leave the existing file descriptor open until dup2() is called
+ * below to avoid races where a concurrent open() in another thread
+ * could claim the existing descriptor.
*/
- if (isopen)
- (void) (*fp->_close)(fp->_cookie);
if (fp->_flags & __SMBF)
free((char *)fp->_bf._base);
fp->_w = 0;
@@ -186,6 +180,8 @@ finish:
memset(&fp->_mbstate, 0, sizeof(mbstate_t));
if (f < 0) { /* did not get it after all */
+ if (isopen)
+ (void) (*fp->_close)(fp->_cookie);
fp->_flags = 0; /* set it free */
FUNLOCKFILE(fp);
errno = sverrno; /* restore in case _close clobbered */
@@ -197,11 +193,12 @@ finish:
* to maintain the descriptor. Various C library routines (perror)
* assume stderr is always fd STDERR_FILENO, even if being freopen'd.
*/
- if (wantfd >= 0 && f != wantfd) {
+ if (wantfd >= 0) {
if (_dup2(f, wantfd) >= 0) {
(void)_close(f);
f = wantfd;
- }
+ } else
+ (void)_close(fp->_file);
}
/*