You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@apr.apache.org by Jeff Trawick <tr...@gmail.com> on 2009/04/07 19:45:37 UTC

Re: OpenLDAP's libldap and libldap_r

On Fri, Jan 23, 2009 at 11:14 AM, Joe Orton <jo...@redhat.com> wrote:

> On Fri, Jan 23, 2009 at 07:10:36AM -0500, Jeff Trawick wrote:
> > On Fri, Jan 23, 2009 at 7:00 AM, Eric Covener <co...@gmail.com> wrote:
> > > On Fri, Jan 23, 2009 at 3:46 AM, Graham Leggett <mi...@sharp.fm>
> wrote:
> > > > This is the first time I have heard of ldap_r. Is there a reason why
> we
> > > > can't just try bind to ldap_r first, and if that fails fall back to
> ldap?
> > >
> > > I think that's the way to go.
> >
> > Yeah, sounds right.
> >
> > FWIW, /usr/sbin/httpd.worker on RHEL 5 U2 loads just plain libldap (and
> > libldap_r is a distinct, and larger, library).  A naive observer (okay,
> me)
> > would have expected that to be an explosive combination that would have
> been
> > fixed long ago.
>
> From reading the OpenLDAP source code the libldap_r build appears to
> compile in a bunch of mutex locking calls around many/most of the ldap_*
> interfaces.
>
> It's not obvious to me whether:
>
> a) this is because those calls are in fact manipulating process-global
> state in some thread-unsafe way (not obvious how, if so), or
>
> b) this is enabling an additional API guarantee, that concurrent use of
> a single LDAP * object from multiple threads is safe.
>

(I had a reason to look at this in a little more detail; here are my notes.)

AFAICT, both a) and b) are true

Besides the more obvious issue b (above), the reentrant build

i. uses thread-safe forms of C library functions where available
ii. uses mutexes around calls to thread-unsafe library functions that can't
be avoided

and, perhaps suprisingly, the non-reentrant build purposefully forgets about
thread-safe forms of C library functions.

Example from util-int.c, which encapsulates a number of library functions
with thread safety issues:

#ifndef LDAP_R_COMPILE
# undef HAVE_REENTRANT_FUNCTIONS
# undef HAVE_CTIME_R
# undef HAVE_GETHOSTBYNAME_R
# undef HAVE_GETHOSTBYADDR_R

So, apart from the unforgotten detail of which flavor of libldap is linked
to by the code you want to combine:

Applications with multiple threads sharing LDAP connections need to use
libldap_r, for proper sharing of the connection.

Slightly less obvious:

Applications with multiple threads calling OpenLDAP need to use libldap_r,
even if they don't share LDAP connections; otherwise, resolver and certain
other calls made for independent LDAP connections will not be thread-safe
and may clobber global data owned by the system library.

Even less obvious are the implications for applications which use OpenLDAP
only on a single thread but have other threads running arbitrary code:

Generally, all code in a multi-threaded application should use threadsafe
forms of all library functions (you're broken as soon as you get two callers
to gethostbyname(), for example).  libldap_r is the build that maximizes the
use of threadsafe forms of library functions, so it needs to be used in this
case.  The mutexes used by libldap_r for callers of library functions with
no threadsafe form (or no form on this platform) won't protect against
non-OpenLDAP callers of the function (since they don't share that mutex), so
it is by no means perfect, but hopefully the number of non-threadsafe
library functions that must be used on modern systems is extremely small.



>
> I would assume any user of this apr-util API, hahaha sorry, let me start
> again....  I would assume that httpd does not rely on the additional API
> guarantee in (b).
>
> Since libldap and libldap_r seem to implement the same set of symbols
> (and without symbol versioning) this kind of issue is a minefield for
> downstream distributors, if apr-util/httpd link against libldap_r and
> some perl LDAP foo links against libldap, everything goes boom.
>

Downstream distributors shouldn't have any problem figuring this stuff out,
and they have at least some control of what libldap vs. libldap_r mean
anyway.

httpd's LDAP support has to use libldap_r instead of libldap within a
threaded MPM*, and other code which can be combined with it needs to
accommodate :(

*obviously it may not be practical to restrict the use of libldap_r to this
minimal extent

--/--

To sanity check that libldap really disables the reentrant forms of library
functions, I found the call to plain gethostbyname() with no protection of
the hostent structure in util-int.c and confirmed that it is indeed used
when building libldap.

#else
        *buf = NULL;
        *result = gethostbyname( name );
#error FOO        <- angry compiler

        if (*result!=NULL) {
                return 0;
        }

        *herrno_ptr = h_errno;

        return -1;
#endif

This is Solaris, where gethostbyname() simply cannot be used from multiple
threads.