mirror of
https://sourceware.org/git/glibc.git
synced 2026-10-28 10:25:13 +08:00
_IO_new_file_init_internal linked the new stream into _IO_list_all
before setting fp->_fileno to -1. A concurrent thread that walks
_IO_list_all (for example via fflush (NULL)) could observe the stream
with an uninitialized _fileno before initialization completed.
Set _fileno = -1 before _IO_link_in so the stream is fully
initialized when it becomes visible in the global list.
This is the residual concurrency defect noted at the end of commit
b657f72fa3 ("libio: Fix deadlock between freopen, fflush (NULL) and
fclose (bug 24963)").
Add libio/tst-file-init-race exercising concurrent fopen/fclose and
fflush (NULL) to detect regressions.
Signed-off-by: Shamil Abdulaev <ashamil435@gmail.com>
Reviewed-by: Florian Weimer <fweimer@redhat.com>
70 lines
1.9 KiB
C
70 lines
1.9 KiB
C
/* Test for race during FILE initialization in _IO_new_file_init_internal.
|
|
Copyright The GNU Toolchain Authors.
|
|
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 <stdio.h>
|
|
#include <stdatomic.h>
|
|
#include <pthread.h>
|
|
#include <time.h>
|
|
|
|
#include <support/check.h>
|
|
#include <support/xthread.h>
|
|
|
|
static atomic_bool stop = ATOMIC_VAR_INIT (0);
|
|
|
|
static void *
|
|
opener_thread (__attribute__ ((unused)) void *arg)
|
|
{
|
|
while (!atomic_load_explicit (&stop, memory_order_acquire))
|
|
{
|
|
FILE *fp = fopen ("/dev/null", "r");
|
|
if (fp == NULL)
|
|
FAIL_EXIT1 ("fopen: %m");
|
|
if (fclose (fp) != 0)
|
|
FAIL_EXIT1 ("fclose: %m");
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static void *
|
|
flusher_thread (__attribute__ ((unused)) void *arg)
|
|
{
|
|
while (!atomic_load_explicit (&stop, memory_order_acquire))
|
|
fflush (NULL);
|
|
return NULL;
|
|
}
|
|
|
|
static int
|
|
do_test (void)
|
|
{
|
|
pthread_t t1 = xpthread_create (NULL, opener_thread, NULL);
|
|
pthread_t t2 = xpthread_create (NULL, flusher_thread, NULL);
|
|
|
|
struct timespec ts = { .tv_sec = 3, .tv_nsec = 0 };
|
|
nanosleep (&ts, NULL);
|
|
|
|
atomic_store_explicit (&stop, 1, memory_order_release);
|
|
|
|
xpthread_join (t1);
|
|
xpthread_join (t2);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#define TIMEOUT 30
|
|
#include <support/test-driver.c>
|