mirror of
https://sourceware.org/git/glibc.git
synced 2024-11-23 01:33:36 +08:00
9c0d6f7a10
As reported in bug 32140, freopen leaks the FILE object when it returns NULL: there is no valid use of the FILE * pointer (including passing to freopen again or to fclose) after such an error return, so the underlying object should be freed. Add code to free it. Note 1: while I think it's clear from the relevant standards that the object should be freed and the FILE * can't be used after the call in this case (the stream is closed, which ends the lifetime of the FILE), it's entirely possible that some existing code does in fact try to use the existing FILE * in some way and could be broken by this change. (Though the most common case for freopen may be stdin / stdout / stderr, which _IO_deallocate_file explicitly checks for and does not deallocate.) Note 2: the deallocation is only done in the _IO_IS_FILEBUF case. Other kinds of streams bypass all the freopen logic handling closing the file, meaning a call to _IO_deallocate_file would neither be safe (the FILE might still be linked into the list of all open FILEs) nor sufficient (other internal memory allocations associated with the file would not have been freed). I think the validity of freopen for any other kind of stream will need clarifying with the Austin Group, but if it is valid in any such case (where "valid" means "not undefined behavior so required to close the stream" rather than "required to successfully associate the stream with the new file in cases where fopen would work"), more significant changes would be needed to ensure the stream gets fully closed. Tested for x86_64.
93 lines
2.5 KiB
C
93 lines
2.5 KiB
C
/* Test freopen failure.
|
|
Copyright (C) 2024 Free Software Foundation, Inc.
|
|
This file is part of the GNU C Library.
|
|
|
|
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
|
|
<https://www.gnu.org/licenses/>. */
|
|
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <mcheck.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <support/check.h>
|
|
#include <support/descriptors.h>
|
|
#include <support/file_contents.h>
|
|
#include <support/support.h>
|
|
#include <support/temp_file.h>
|
|
#include <support/test-driver.h>
|
|
#include <support/xstdio.h>
|
|
|
|
#define START_TEST(DESC) \
|
|
do \
|
|
{ \
|
|
fds = support_descriptors_list (); \
|
|
verbose_printf (DESC); \
|
|
} \
|
|
while (0)
|
|
|
|
#define END_TEST \
|
|
do \
|
|
{ \
|
|
support_descriptors_check (fds); \
|
|
support_descriptors_free (fds); \
|
|
} \
|
|
while (0)
|
|
|
|
int
|
|
do_test (void)
|
|
{
|
|
mtrace ();
|
|
struct support_descriptors *fds;
|
|
char *temp_dir = support_create_temp_directory ("tst-freopen3");
|
|
char *file1 = xasprintf ("%s/file1", temp_dir);
|
|
support_write_file_string (file1, "file1");
|
|
add_temp_file (file1);
|
|
char *file2 = xasprintf ("%s/file2", temp_dir);
|
|
support_write_file_string (file2, "file2");
|
|
add_temp_file (file2);
|
|
char *file_nodir = xasprintf ("%s/nodir/file", temp_dir);
|
|
FILE *fp;
|
|
int ret;
|
|
int fd;
|
|
|
|
START_TEST ("Testing w -> wx (file exists)\n");
|
|
fp = xfopen (file1, "w");
|
|
fp = FREOPEN (file2, "wx", fp);
|
|
TEST_VERIFY (fp == NULL);
|
|
END_TEST;
|
|
|
|
/* Test old file is closed even when opening the new file fails. */
|
|
|
|
START_TEST ("testing r -> r (opening new file fails)\n");
|
|
fp = xfopen (file1, "r");
|
|
fd = fileno (fp);
|
|
fp = FREOPEN (file_nodir, "r", fp);
|
|
TEST_VERIFY (fp == NULL);
|
|
errno = 0;
|
|
ret = fcntl (fd, F_GETFL);
|
|
TEST_COMPARE (ret, -1);
|
|
TEST_COMPARE (errno, EBADF);
|
|
END_TEST;
|
|
|
|
free (temp_dir);
|
|
free (file1);
|
|
free (file2);
|
|
free (file_nodir);
|
|
return 0;
|
|
}
|
|
|
|
#include <support/test-driver.c>
|