DMARC-Filter: OpenDMARC Filter v1.4.2 delorie.com 53A96JVu3180803
Authentication-Results: delorie.com; dmarc=pass (p=none dis=none) header.from=cygwin.com
Authentication-Results: delorie.com; spf=pass smtp.mailfrom=cygwin.com
DKIM-Filter: OpenDKIM Filter v2.11.0 delorie.com 53A96JVu3180803
Authentication-Results: delorie.com;
	dkim=pass (1024-bit key, unprotected) header.d=cygwin.com header.i=@cygwin.com header.a=rsa-sha256 header.s=default header.b=EjkanyEy
X-Recipient: archive-cygwin@delorie.com
DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org B3A013839821
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=cygwin.com;
	s=default; t=1744275978;
	bh=HRKAAlbzbOnBtEkIV1V66mfyNayFMlCAGwNwjQ6OcjU=;
	h=Date:To:Subject:References:In-Reply-To:List-Id:List-Unsubscribe:
	 List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc:
	 From;
	b=EjkanyEygvSp54lXCuoFwNNeGpSyeXr30UUlsagOcKVeeezu18uqRhkshPJCyr1ER
	 JxOY0yw/Zj4K+UFx+sUQxq1Px+8MNJb94UQ9h+kS/mZPTwG/uYkTRvpRPscGpRBZYs
	 u0xbXFWVhCKNSy0PJZEb0CTPJRAd0/5FQoj2/8fM=
X-Original-To: cygwin@cygwin.com
Delivered-To: cygwin@cygwin.com
DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org D4B2C3839C55
Date: Thu, 10 Apr 2025 11:04:40 +0200
To: cygwin@cygwin.com
Subject: Re: pthread_atfork vs dlopen/dlclose
Message-ID: <Z_eJqDJw-xbhktwC@calimero.vinschen.de>
Mail-Followup-To: cygwin@cygwin.com
References: <07665688-8199-2e80-cdfe-abeaa6f56ba7@jdrake.com>
 <2021152719.99592.1744265074916@mail.yahoo.com>
 <b150c1e4-b3cd-4f90-8548-0c5f287948d4@jdrake.com>
 <94af5480-167a-fcc6-5f4c-89afa5ce8637@jdrake.com>
MIME-Version: 1.0
Content-Disposition: inline
In-Reply-To: <94af5480-167a-fcc6-5f4c-89afa5ce8637@jdrake.com>
X-BeenThere: cygwin@cygwin.com
X-Mailman-Version: 2.1.30
Precedence: list
List-Id: General Cygwin discussions and problem reports <cygwin.cygwin.com>
List-Unsubscribe: <https://cygwin.com/mailman/options/cygwin>,
 <mailto:cygwin-request@cygwin.com?subject=unsubscribe>
List-Archive: <https://cygwin.com/pipermail/cygwin/>
List-Post: <mailto:cygwin@cygwin.com>
List-Help: <mailto:cygwin-request@cygwin.com?subject=help>
List-Subscribe: <https://cygwin.com/mailman/listinfo/cygwin>,
 <mailto:cygwin-request@cygwin.com?subject=subscribe>
From: Corinna Vinschen via Cygwin <cygwin@cygwin.com>
Reply-To: cygwin@cygwin.com
Cc: Corinna Vinschen <corinna-cygwin@cygwin.com>
Content-Type: text/plain; charset="utf-8"
Errors-To: cygwin-bounces~archive-cygwin=delorie.com@cygwin.com
Sender: "Cygwin" <cygwin-bounces~archive-cygwin=delorie.com@cygwin.com>
Content-Transfer-Encoding: 8bit
X-MIME-Autoconverted: from base64 to 8bit by delorie.com id 53A96JVu3180803

On Apr  9 23:32, Jeremy Drake via Cygwin wrote:
> On Wed, 9 Apr 2025, Jeremy Drake via Cygwin wrote:
> 
> > On Thu, 10 Apr 2025, Kevin Schnitzius via Cygwin wrote:
> >
> > > On Wednesday, April 9, 2025 at 06:54:34 PM EDT, Jeremy Drake via Cygwin <cygwin@cygwin.com> wrote:
> > >
> > > > The recent issue with pthread_atfork handlers reminded me of a scenario
> > > > that I know glibc handles, but it seems that Cygwin does not.  Test case:
> > >
> > > <... code that loads a shared lib, registers some functions in shared lib with pthread_atfork(), unloads the shared lib, and crashes on fork...>
> > >
> > > Calling functions in an unloaded library should result in undefined behavior.
> > >
> > > However, further investigation reveals that the Linux pthread_atfork() registered functions are not being called and POSIX does not proved a mechanism for un-registering these functions.   Note: pthread_atfork() is not bumping the ref count on the shared lib--those functions are definitely unavailable after the dlclose()
> > >
> > > In the Cygwin version, calling the functions in the unloaded library when the fork happens causes the crash.
> > >
> > > This seems to be a bug with fork(), if it is a bug at all.
> >
> > I did a quick search, and found a write-up of *my* bug report :D
> >
> > https://developers.redhat.com/articles/2022/12/14/how-we-addressed-unforeseen-use-case-pthreadatfork
> >
> > "Now, dlclose()'ing a module means that any fork handlers registered by it
> > should not be executed after the dlclose and should therefore implicitly
> > be deregistered."
> 
> It seems glibc takes care of this implicit deregistration in
> __cxa_finalize, after calling __cxa_atexit functions, it unregisters any
> at_quick_exit or pthread_atfork callbacks from the DSO being unloaded.
> 
> https://sourceware.org/git/?p=glibc.git;a=blob;f=stdlib/cxa_finalize.c;h=2bb35602edc6bf842e5d2c93ad03454d7b57ee65;hb=HEAD
> 
> It doesn't look like newlib deals with either at_quick_exit or
> pthread_atfork handlers in its __cxa_finalize implementation.

Cygwin is doing this stuff mostly in its own code, see thread.cc.
It keeps lists of the callbacks in a global structure which is
called MT_INTERFACE throughout thread.cc.

The functions pthread::atforkprepare(), pthread::atforkparent() and
pthread::atforkchild () are called from different spots during fork().

Here's what we're missing:

- Either keep track of the DLL a callback function is coming from by
  calling dlls.find (handle, true) and store the struct dll pointer.
  In dlclose(), if the DLL is of type DLL_LOAD or DLL_NATIVE, and
  the refcounter indicates that this is the last FreeLibrary, check the
  three callback lists in MT_INTERFACE and remove all functions
  with the same struct dll pointer.

- Or, instead of keeping track, tweak the aformentioned three functions
  to call GetModuleHandleEx() just as dladdr() does, and if it returns
  NULL, remove the callback from the list on the fly.


Corinna

-- 
Problem reports:      https://cygwin.com/problems.html
FAQ:                  https://cygwin.com/faq/
Documentation:        https://cygwin.com/docs.html
Unsubscribe info:     https://cygwin.com/ml/#unsubscribe-simple

