mirror of
https://sourceware.org/git/glibc.git
synced 2024-12-18 14:03:28 +08:00
f1a67a2c78
As described in BZ#21398 (close as dup of 21393) report current freopen implementation fails when one tries to freopen STDIN_FILENO, STDOUT_FILENO, or STDERR_FILENO. Although on bug report the discussion leads to argue if a close followed by a freopen on the standard file is a valid operation, the underlying issue is not really the check for dup3 returned value, but rather calling it if the returned file descriptor is equal as the input one. So for a quality of implementation this patch avoid calling dup3 for the aforementioned case. It also adds a dup3 error case check for the two possible failures, with one being Linux only: EINTR and EBUSY. The EBUSY issue is better explained on this stackoverflow thread [1], but in a short it is due the internal Linux implementation which allows a race condition window for dup2 due the logic dissociation of file descriptor allocation and actual VFS 'install' operation. For both outliers failures all allocated memory is freed and a NULL FILE* is returned. With this patch the example on BZ#21398 is now actually possible (I used as the testcase for the bug report). Checked on x86_64-linux-gnu. [BZ #21393] * libio/freopen.c (freopen): Avoid dup already opened file descriptor and add a check for dup3 failure. * libio/freopen64.c (freopen64): Likewise. * libio/tst-freopen.c (do_test): Rename to do_test_basic and use libsupport. (do_test_bz21398): New test. * manual/stdio.texi (freopen): Add documentation of EBUSY failure. [1] http://stackoverflow.com/questions/23440216/race-condition-when-using-dup2
113 lines
2.7 KiB
C
113 lines
2.7 KiB
C
/* Test freopen with mmap stdio.
|
|
Copyright (C) 2002-2017 Free Software Foundation, Inc.
|
|
This file is part of the GNU C Library.
|
|
Contributed by Jakub Jelinek <jakub@redhat.com>, 2002.
|
|
|
|
The GNU C Library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Lesser General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2.1 of the License, or (at your option) any later version.
|
|
|
|
The GNU C Library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
License along with the GNU C Library; if not, see
|
|
<http://www.gnu.org/licenses/>. */
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include <support/check.h>
|
|
#include <support/temp_file.h>
|
|
|
|
static int fd;
|
|
static char *name;
|
|
|
|
static void
|
|
do_prepare (int argc, char *argv[])
|
|
{
|
|
fd = create_temp_file ("tst-freopen.", &name);
|
|
TEST_VERIFY_EXIT (fd != -1);
|
|
}
|
|
|
|
#define PREPARE do_prepare
|
|
|
|
/* Basic tests for freopen. */
|
|
static void
|
|
do_test_basic (void)
|
|
{
|
|
const char * const test = "Let's test freopen.\n";
|
|
char temp[strlen (test) + 1];
|
|
|
|
FILE *f = fdopen (fd, "w");
|
|
if (f == NULL)
|
|
FAIL_EXIT1 ("fdopen: %m");
|
|
|
|
fputs (test, f);
|
|
fclose (f);
|
|
|
|
f = fopen (name, "r");
|
|
if (f == NULL)
|
|
FAIL_EXIT1 ("fopen: %m");
|
|
|
|
if (fread (temp, 1, strlen (test), f) != strlen (test))
|
|
FAIL_EXIT1 ("fread: %m");
|
|
temp [strlen (test)] = '\0';
|
|
|
|
if (strcmp (test, temp))
|
|
FAIL_EXIT1 ("read different string than was written: (%s, %s)",
|
|
test, temp);
|
|
|
|
f = freopen (name, "r+", f);
|
|
if (f == NULL)
|
|
FAIL_EXIT1 ("freopen: %m");
|
|
|
|
if (fseek (f, 0, SEEK_SET) != 0)
|
|
FAIL_EXIT1 ("fseek: %m");
|
|
|
|
if (fread (temp, 1, strlen (test), f) != strlen (test))
|
|
FAIL_EXIT1 ("fread: %m");
|
|
temp [strlen (test)] = '\0';
|
|
|
|
if (strcmp (test, temp))
|
|
FAIL_EXIT1 ("read different string than was written: (%s, %s)",
|
|
test, temp);
|
|
|
|
fclose (f);
|
|
}
|
|
|
|
/* Test for BZ#21398, where it tries to freopen stdio after the close
|
|
of its file descriptor. */
|
|
static void
|
|
do_test_bz21398 (void)
|
|
{
|
|
(void) close (STDIN_FILENO);
|
|
|
|
FILE *f = freopen (name, "r", stdin);
|
|
if (f == NULL)
|
|
FAIL_EXIT1 ("freopen: %m");
|
|
|
|
TEST_VERIFY_EXIT (ferror (f) == 0);
|
|
|
|
char buf[128];
|
|
char *ret = fgets (buf, sizeof (buf), stdin);
|
|
TEST_VERIFY_EXIT (ret != NULL);
|
|
TEST_VERIFY_EXIT (ferror (f) == 0);
|
|
}
|
|
|
|
static int
|
|
do_test (void)
|
|
{
|
|
do_test_basic ();
|
|
do_test_bz21398 ();
|
|
|
|
return 0;
|
|
}
|
|
|
|
#include <support/test-driver.c>
|