From patchwork Wed Apr 29 14:29:18 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Shamil Abdulaev X-Patchwork-Id: 134157 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from vm01.sourceware.org (localhost [127.0.0.1]) by sourceware.org (Postfix) with ESMTP id 871514BB5890 for ; Wed, 29 Apr 2026 14:29:58 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 871514BB5890 Authentication-Results: sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20251104 header.b=CdoinNEe X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from mail-lj1-x233.google.com (mail-lj1-x233.google.com [IPv6:2a00:1450:4864:20::233]) by sourceware.org (Postfix) with ESMTPS id 476484BB1C09 for ; Wed, 29 Apr 2026 14:29:27 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 476484BB1C09 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=gmail.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 476484BB1C09 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2a00:1450:4864:20::233 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1777472967; cv=none; b=jI0gX8ycH+Y0v467mlaoOXmI+to+h/3VMBQ8z295Sl6uYLMD0wP/h0aVr2VTILOTngoDkMpWauyx9DW0XWA99QL5O+kkDxvW5RTbkAHZkfVDIP8kd2ZGcsvLydxmvfU4ThhtbEpr5+q5BHuZwONmEXTgZE5rqpNgyUwB+U8YAZs= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1777472967; c=relaxed/simple; bh=eKD2or/hqSJ71mBCcGBh6U80JHaWDkurNUmmrXFujrc=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=Ht4rvQVbuDmKS7b4fabMGEpeMJYJ41Kn5S66n/EQVVymeCrir0ih1uhtj/PNN2aVVQxz3af+HpCp+QxF97MJ/crW7uGE2iW/p8cveBY9LRRTZS+ctMq6QfMhwmKjpKfWJ0pNkrflizPhZDf4tLsENoRLaOb//A4v4RfJK+fYVqY= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 476484BB1C09 Received: by mail-lj1-x233.google.com with SMTP id 38308e7fff4ca-386b553c70eso103935381fa.0 for ; Wed, 29 Apr 2026 07:29:27 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1777472965; x=1778077765; darn=sourceware.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:from:to:cc:subject:date:message-id :reply-to; bh=WyfCfznExAQmIpwsK5TL0KsccLPYb1X7aeo/94rgxyE=; b=CdoinNEeUKj8WB6RhZpXkb0zCCqDqOqHLI1sHzpbaeNf/oyUyIlEVFjWqMW060MPuF FVJrDeo+ODu8L890V+Q8ebfToitc+nf9rbRcgTtgY/iZbpHq8kM0o5GYPjkniQ083jdQ 3+lpott6xgiohZXE0i76gycxzv5pi8ydrgnFhk02exbqXrQx8q7pgbTyUlGDvPp0qwGE AxR7nFaEwxc3Geg/siqin/ccxo/z7FUeQHLdicZ7Slf//Qxtb0Hmx+pX00Kg33jOGyGX K0OJd2Xi4Q8UattVfWTune+GXFroQm2u4RJGn6KXJUKLXo2lZ2Ty8T3yqMGZ2XT+/pBS 8x4A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777472965; x=1778077765; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=WyfCfznExAQmIpwsK5TL0KsccLPYb1X7aeo/94rgxyE=; b=d+X7RVhp+aOAxFMlcsYPwTknQqRMWyzv+X+nYyU8Fw/KbUVVfd0Pt9RErqT8JAheNG PmKJ0cGayB4ZZB9adDNEPkrwFQXGSsjUeb46TroUuLgBHGHCMBfgopk7V1vct9HQw7cT a6EOPp4L87o65gggos6FPn9b4Fddh+gt2AIMiR0qD8k9YOurcrDQL1wjDEIirCvglaf3 gpoNJdvb0PrTeeQqucR9gQFbEF+iwon53LuYVr8K+lPHe+smbHWiZhUdIRir1LpgrjXI B9Fm4CHHnnHDAXzA/3RqoC2KgvS9uPWMf/Ql2sqDYhvvkMLSNw4YSwxxHx3B3HUEOgUX 8Xtg== X-Gm-Message-State: AOJu0YxfIvke48yXt5C9jGcOOYt09zdujXqKycdAGIaCpAE0Ccj9OBWI pBrxg55+uX0eIJGK/yc45iIoCS167WVqiX66CRd80NsfK6pVJPYwD0C1YbmQS1XB X-Gm-Gg: AeBDievgL3r9wYzhTbbz89ygPH5Vyilwfge5XeZcPlf2Wkyb7HEYSTl3uYItDJSMS/7 zT82YMPCM05nwKpXqzmk1Yl1Ch2xYk8A7nmuH6pvGo37hs3UM/wQgbMprRbml/+zq2WO4i4G4X4 dqZISgchdOCBHWjmttEJ7VgfKGXnNDXxX6q50fyM0jXuA3rSmzYSVh7EIyom3bNTzM4zztyATV3 +UfzX27J0KpNsNXJT0xj9qrQxQ2RyKhBJgZ8Pck6drL+rjY6MC3aqInpJ7lnYXSiYu2Z4+n5Xrh hEtkzFpXwz5sD3r55FPZVl/bcjogpJKtrxByvInlGfeRDKKlaJa78VV2iGtHJfOWZt9PMbTkG2U vGXbxHjYs3VdzHHzzQ8SKKgoHSjPmlL3FFTvxX+ot2T/Z+86decz9bUg1YZR6vwdwBemTHxx4AV C+no/0QWMo6cInAlVgzwbxNQStT9DcGE/5lcIuYdgL+rBYhiZtmPvuHdbPwAl3VoHGlMac2F+9m efFuZMCcQw445NhNFlQImeS X-Received: by 2002:a05:651c:41d6:b0:38b:f0f0:e3f7 with SMTP id 38308e7fff4ca-39240cac934mr28749361fa.7.1777472965192; Wed, 29 Apr 2026 07:29:25 -0700 (PDT) Received: from oops-lab ([62.152.34.188]) by smtp.gmail.com with ESMTPSA id 38308e7fff4ca-3924fac6675sm5820881fa.38.2026.04.29.07.29.24 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 29 Apr 2026 07:29:24 -0700 (PDT) From: Shamil Abdulaev To: libc-alpha@sourceware.org Subject: [PATCH v2] libio: Fix race in _IO_new_file_init_internal initialization order [BZ #33785] Date: Wed, 29 Apr 2026 17:29:18 +0300 Message-ID: <4024b75e5db1853b62dcd5147151a8f3433d46b8.1777472828.git.ashamil435@gmail.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <94933ea56cf84f067c5e7bb7a67eb39f9d9384c0.1777424201.git.ashamil435@gmail.com> References: <94933ea56cf84f067c5e7bb7a67eb39f9d9384c0.1777424201.git.ashamil435@gmail.com> MIME-Version: 1.0 X-Spam-Status: No, score=-10.0 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_ENVFROM_END_DIGIT, FREEMAIL_FROM, GB_FREEMAIL_NUM, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on sourceware.org X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libc-alpha-bounces~patchwork=sourceware.org@sourceware.org _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 Reviewed-by: Florian Weimer --- Changes since v1: - Apply the same _fileno/_IO_link_in reordering to libio/oldfileops.c (suggested by Florian Weimer). - tst-file-init-race: treat fopen/fclose failures as test failures via FAIL_EXIT1 instead of silently ignoring them (suggested by Florian Weimer). libio/Makefile | 3 ++ libio/fileops.c | 2 +- libio/oldfileops.c | 2 +- libio/tst-file-init-race.c | 69 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 74 insertions(+), 2 deletions(-) create mode 100644 libio/tst-file-init-race.c diff --git a/libio/Makefile b/libio/Makefile index 93656466df..7e448295e3 100644 --- a/libio/Makefile +++ b/libio/Makefile @@ -107,6 +107,7 @@ tests = \ tst-fgetc-after-eof \ tst-fgetwc \ tst-fgetws \ + tst-file-init-race \ tst-fopenloc2 \ tst-fputws \ tst-freopen \ @@ -160,6 +161,8 @@ tests-static += \ $(objpfx)tst-popen-fork: $(shared-thread-library) +$(objpfx)tst-file-init-race: $(shared-thread-library) + tests-internal = tst-vtables tst-vtables-interposed ifeq (yes,$(build-shared)) diff --git a/libio/fileops.c b/libio/fileops.c index 8067c0a9cf..9348d7c3a1 100644 --- a/libio/fileops.c +++ b/libio/fileops.c @@ -111,8 +111,8 @@ _IO_new_file_init_internal (struct _IO_FILE_plus *fp) fp->file._offset = _IO_pos_BAD; fp->file._flags |= CLOSED_FILEBUF_FLAGS; - _IO_link_in (fp); fp->file._fileno = -1; + _IO_link_in (fp); } /* External version of _IO_new_file_init_internal which switches off diff --git a/libio/oldfileops.c b/libio/oldfileops.c index 41b15491cb..0b92afbdb1 100644 --- a/libio/oldfileops.c +++ b/libio/oldfileops.c @@ -108,8 +108,8 @@ _IO_old_file_init_internal (struct _IO_FILE_plus *fp) _IO_vtable_offset is used to detect the old binaries. */ fp->file._vtable_offset = ((int) sizeof (struct _IO_FILE) - (int) sizeof (struct _IO_FILE_complete)); - _IO_link_in (fp); fp->file._fileno = -1; + _IO_link_in (fp); if (&_IO_stdin_used != NULL || !_IO_legacy_file ((FILE *) fp)) /* The object is dynamically allocated and large enough. Initialize diff --git a/libio/tst-file-init-race.c b/libio/tst-file-init-race.c new file mode 100644 index 0000000000..7adaba272c --- /dev/null +++ b/libio/tst-file-init-race.c @@ -0,0 +1,69 @@ +/* Test for race during FILE initialization in _IO_new_file_init_internal. + Copyright (C) 2026 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 + . */ + +#include +#include +#include +#include + +#include +#include + +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