Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 13 Aug 2002 11:14:32 +0300
From:      Maxim Sobolev <sobomax@FreeBSD.org>
To:        Terry Lambert <tlambert2@mindspring.com>
Cc:        hackers@FreeBSD.ORG, audit@FreeBSD.ORG, Alexander Litvin <archer@whichever.org>, Andriy Gapon <agapon@excite.com>
Subject:   Re: Thread-safe resolver [patches for review]
Message-ID:  <3D58BFE8.9281433@FreeBSD.org>
References:  <3D578A99.F0821712@FreeBSD.org> <3D5792CD.497C80F0@mindspring.com> <3D57A9D4.DAA043EF@FreeBSD.org> <3D57CF6D.2982CE8@mindspring.com>

next in thread | previous in thread | raw e-mail | index | archive | help
Terry Lambert wrote:
> 
> Maxim Sobolev wrote:
> > > You may also want to consider the use of a .init and .fini
> > > section for the code, to permit the creation of an initial
> > > lookup context chunk; this is kind of a tradeoff, but it will
> > > mean that a server will not have to do the recheck each time.
> > > The .fini section would aloow auto-cleanup.  This may be a
> > > necessity for a long running program that uses a shared object
> > > to perform the thread creation and lookup (you could leak
> > > memory, otherwise).
> >
> > Could you please elaborate how exactly memory could be leaked in this
> > case, if the program does correctly shut down all its threads?
> 
> Create PIC object foo.so.
> Link PIC object foo.so against libc.so.
> Call dlopen to load module foo.so into program "bob".
> Call function in foo.so from program "bob".
> Function in foo.so creates two threads, one for IPv4 lookup,
>         another for IPv6 lookup to cause lookups to proceed
>         concurrently.
> Lookup completes.
> Unload module foo.so.
>         -> leak memory in libc.so image

This scenario doesn't look as a legitimate way to do things for me.
Let's inspect what will happen when you are unloading a PIC module,
which has one or more threads running. There are two possibilities:
either thread scheduler (libc_r) was linked with the program itself
and therefore loaded with it, or it was linked with PIC module and
loaded along with that module. In the first case, after you have
dlclose'd the PIC module, dynamic linker will unmap module's code from
memory, but the thread scheduler will remain running and on the next
attempt to pass control to the thread in your PIC module will probably
get SIGBUS due to the fact that code is no longer mapped. In the
second case, you'll unload module along with thread scheduler, but
thread-scheduling signals setup will remain in place, so that shortly
you will get the same SIGBUS, when the kernel will be trying to
delivery signal to no longer mapper region.

In either case, you will get the problem much more serious than memory
leak.

> The assumption (which is potentially wrong) is that the program
> will correctly shut down all its threads, when in fact it was a
> module not under the programs control that created and used the
> threads.

I do not quite agree. In such case, the module should probably have
destructor function, either placed into the fini section, or to be
explicitly called by the program before dlclose().

> The leak depends on:
> 
> 1)      A pool of worker threads being created and left around
>         or the purpose of simultaneous resolution
> 
> 2)      The parent shutting down the module without explicitly
>         dealing with the threads (basically, code which would
>         need to live in ".fini" of the foo.so, and could not be
>         automatically triggered on unload of foo.so any other way).
> 
> I think that parallel IPv6/IPv4 resolution presented as a single
> serial interface is a high probability implementation with the
> support for threaded access to the resolver, particularly with
> the Mozilla code acting the way it does.
> 
> > I also would like to hear from you whether or not you think that we
> > need all those gethostbyXXX_r(3) functions.
> 
> No.  I don't think any of the _r functions are needed, so long
> as the results are not cached by pointer instead of a copy,
> before passing them from one thread to another.  It's a risk on
> the clobber case of a call with a cached reference outstanding
> but not processed by another thread which is not an issue with
> the _r functions, which require that you pass the storage down.
> 
> Of course, if you pass down per thread storage, you could have
> the same problem if you didn't copy rather than reference the
> results before passing to another thread by address.
> 
> Given that, per thread allocations ("thread local storage")
> makes more sense than allocate/free fights between threads
> based on who's responsible for owning the memory after an
> inter-thread call.  8-).

Thank you for the explanation!

-Maxim

To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-audit" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?3D58BFE8.9281433>