From patchwork Sun Sep 22 16:51:54 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jonathan Wakely X-Patchwork-Id: 1988346 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=QUMD6/FA; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=8.43.85.97; helo=server2.sourceware.org; envelope-from=gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=patchwork.ozlabs.org) Received: from server2.sourceware.org (server2.sourceware.org [8.43.85.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4XBXHV3gV0z1xsM for ; Mon, 23 Sep 2024 02:52:54 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id A38333858C50 for ; Sun, 22 Sep 2024 16:52:52 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTP id F39033858C3A for ; Sun, 22 Sep 2024 16:52:23 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org F39033858C3A Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org F39033858C3A Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1727023945; cv=none; b=JBnqow8G60o9DarThglbodui30I/N8lUqELlk122/0Di1KsIM4fYcKN04ola5gPT90KClr4V2d+wRqIA6rWK6yQkWJkzhaWpc1e+eavE9ZJ9LOs0kzbYgSlhH/LdsJutYPzkGDAjr7n4KAkn91t1xR5HzRaVo168IWQoPsI2C+Q= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1727023945; c=relaxed/simple; bh=Xc3sKZFiu02el906/UYPZUConHYKTJujscNWwVuQTvg=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=TxvRD2sLO/rH4flLaVXZN5gXMyZIw6EUvy5MBWO/OPH6XumT3oFmzMJFy/rx25W/FfDedw9f4wl2utYkGw1N0OokWVkiTnC6C4/ZaFhpjdRUuUafl7TQ7tWovtbARpKEbaXRqBovuntrM17Jh9DIgEABUuGbBdDD4CxBtS8PzMM= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1727023942; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=EshjC9AnLpfBa0H6BfhqjBPUepMH6oRzpl0AevrTzl8=; b=QUMD6/FAK4mCOuqvvHoIYAZl5HNiCtNWnza9/jGNyboUTSzt9phE7b8Bo7NcnafsltEPJ8 LvXdw0XIZ9aqMxwUk7a2/YlZl2xv8ZYp1Ypa+keIKOFWuoZdwnWeBJL4ecMkuPqD5e2v8J OMOrDqMhdB4fx4yRKWJ/mV4SRzvDV1k= Received: from mx-prod-mc-02.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-412-W4CAUWfsPhCDelk0KtpioA-1; Sun, 22 Sep 2024 12:52:17 -0400 X-MC-Unique: W4CAUWfsPhCDelk0KtpioA-1 Received: from mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (unknown [10.30.177.4]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-02.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 625BF1934BEE; Sun, 22 Sep 2024 16:52:15 +0000 (UTC) Received: from localhost (unknown [10.42.28.136]) by mx-prod-int-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id C011C30001A1; Sun, 22 Sep 2024 16:52:14 +0000 (UTC) From: Jonathan Wakely To: gcc-patches@gcc.gnu.org, libstdc++@gcc.gnu.org Subject: [PATCH] libstdc++: Fix race condition in std::ios_base::Init construction [PR111676] Date: Sun, 22 Sep 2024 17:51:54 +0100 Message-ID: <20240922165213.1435833-1-jwakely@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.30.177.4 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-12.0 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org Tested x86_64-linux. -- >8 -- The current implementation of std::ios_base::Init::Init() uses an atomic reference count to ensure that the global iostreams are only initialized once. However, it doesn't ensure that other threads will wait for that initialization to finish before trying to use the streams. The reference count also doesn't ensure that only one thread flushes the streams on process exit. libstdc++-v3/ChangeLog: PR libstdc++/111676 * src/c++98/ios_init.cc (init_once): New function. (ios_base::Init::Init()): Move initialization logic to init_once and use local static initialization to call it. --- libstdc++-v3/src/c++98/ios_init.cc | 84 +++++++++++++++++------------- 1 file changed, 49 insertions(+), 35 deletions(-) diff --git a/libstdc++-v3/src/c++98/ios_init.cc b/libstdc++-v3/src/c++98/ios_init.cc index 6e2e5014cf0..14f8bb678b0 100644 --- a/libstdc++-v3/src/c++98/ios_init.cc +++ b/libstdc++-v3/src/c++98/ios_init.cc @@ -75,49 +75,63 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION extern wostream wclog; #endif - ios_base::Init::Init() +namespace +{ + bool init_once(bool* synced_with_stdio, _Atomic_word* refcount) { - if (__gnu_cxx::__exchange_and_add_dispatch(&_S_refcount, 1) == 0) - { - // Standard streams default to synced with "C" operations. - _S_synced_with_stdio = true; + // Standard streams default to synced with "C" operations. + *synced_with_stdio = true; - new (&buf_cout_sync) stdio_sync_filebuf(stdout); - new (&buf_cin_sync) stdio_sync_filebuf(stdin); - new (&buf_cerr_sync) stdio_sync_filebuf(stderr); + new (&buf_cout_sync) stdio_sync_filebuf(stdout); + new (&buf_cin_sync) stdio_sync_filebuf(stdin); + new (&buf_cerr_sync) stdio_sync_filebuf(stderr); - // The standard streams are constructed once only and never - // destroyed. - new (&cout) ostream(&buf_cout_sync); - new (&cin) istream(&buf_cin_sync); - new (&cerr) ostream(&buf_cerr_sync); - new (&clog) ostream(&buf_cerr_sync); - cin.tie(&cout); - cerr.setf(ios_base::unitbuf); - // _GLIBCXX_RESOLVE_LIB_DEFECTS - // 455. cerr::tie() and wcerr::tie() are overspecified. - cerr.tie(&cout); + // The standard streams are constructed once only and never + // destroyed. + new (&cout) ostream(&buf_cout_sync); + new (&cin) istream(&buf_cin_sync); + new (&cerr) ostream(&buf_cerr_sync); + new (&clog) ostream(&buf_cerr_sync); + cin.tie(&cout); + cerr.setf(ios_base::unitbuf); + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 455. cerr::tie() and wcerr::tie() are overspecified. + cerr.tie(&cout); #ifdef _GLIBCXX_USE_WCHAR_T - new (&buf_wcout_sync) stdio_sync_filebuf(stdout); - new (&buf_wcin_sync) stdio_sync_filebuf(stdin); - new (&buf_wcerr_sync) stdio_sync_filebuf(stderr); + new (&buf_wcout_sync) stdio_sync_filebuf(stdout); + new (&buf_wcin_sync) stdio_sync_filebuf(stdin); + new (&buf_wcerr_sync) stdio_sync_filebuf(stderr); - new (&wcout) wostream(&buf_wcout_sync); - new (&wcin) wistream(&buf_wcin_sync); - new (&wcerr) wostream(&buf_wcerr_sync); - new (&wclog) wostream(&buf_wcerr_sync); - wcin.tie(&wcout); - wcerr.setf(ios_base::unitbuf); - wcerr.tie(&wcout); + new (&wcout) wostream(&buf_wcout_sync); + new (&wcin) wistream(&buf_wcin_sync); + new (&wcerr) wostream(&buf_wcerr_sync); + new (&wclog) wostream(&buf_wcerr_sync); + wcin.tie(&wcout); + wcerr.setf(ios_base::unitbuf); + wcerr.tie(&wcout); #endif - // NB: Have to set refcount above one, so that standard - // streams are not re-initialized with uses of ios_base::Init - // besides static object, ie just using with - // ios_base::Init objects. - __gnu_cxx::__atomic_add_dispatch(&_S_refcount, 1); - } + *refcount = 1; + + return true; + } +} // namespace + + ios_base::Init::Init() + { +#if defined(__GTHREADS) && !defined(__cpp_threadsafe_static_init) +# error "Initialization of global iostreams requires thread-safe static init" +#endif + + [[__maybe_unused__]] + static const bool init = init_once(&_S_synced_with_stdio, &_S_refcount); + + // NB: Have to set refcount above one, so that standard + // streams are not re-initialized with uses of ios_base::Init + // besides static object, ie just using with + // ios_base::Init objects. + __gnu_cxx::__atomic_add_dispatch(&_S_refcount, 1); } ios_base::Init::~Init()