I have a non-thread-safe closed-source C library which basically works
ok in AOLserver, but over time shows heap corruption under Purify,
will eventually segfault, etc.

Turns out, there's supposed to be a pretty good way for me to fix
this!  I can find all the unsafe functions in the library with "nm",
then set the LD_PRELOAD environment variable to my OWN special library
which makes the C library functions thread-safe by using Thread Local
Storage.

Some links that explain the LD_PRELOAD stuff at greater length:

  http://www.tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html
  http://www-106.ibm.com/developerworks/linux/library/l-glibc.html
  http://www.uberhip.com/people/godber/interception/index.html

Anyway, I went ahead and did that.  Naturally, I decided to use
AOLserver's nice TLS API for all my thread local storage code.  In
fact, in many cases, I didn't have to do any work at all because
AOLserver already includes a TLS-threasafe version of standard library
calls.  E.g., "Ns_Localtime".

Here's the catch: I tested this under Purify, and I get LOTS of
errors!  All "IPW Invalid pointer" write and "IPR Invalid pointer
read" though (see below), which I've never seen before.

Interestingly, I seem to get these when I call Ns_Strtok and
Ns_Localtime, but NOT when I call my own gethostbyname, which I
implemented with Ns_TlsAlloc, etc.  (See functions below.)  But from
the ns_log statements I know my gethostbyname function is being called
twice during AOLserver startup, it just doesn't seem to generate any
Purify errors.

Any ideas what's going on here, or what I should do about it??  Is
there some reason I should e.g. implement my own TLS strtok using
Ns_TlsAlloc, rather than simply calling Ns_Strtok?

Thanks!


IPW: Invalid pointer write:
  * This is occurring while in thread 10:
        ns_strtok      [reentrant.c:324]
        strtok         [mt-safe.c:205]

IPR: Invalid pointer read:
  * This is occurring while in thread 10:
        ns_strtok      [reentrant.c:330]
        strtok         [mt-safe.c:205]

IPW: Invalid pointer write:
  * This is occurring while in thread 9:
        ns_localtime   [reentrant.c:134]
        localtime      [mt-safe.c:110]


^L
/*
 * ----------------------------------------------------------------------
 *
 * strtok --
 *
 *   Reentrant version using thread local storage.
 *
 * Notes:
 *
 *   Simply calls AOLserver's Ns_Strtok.
 *
 *   Again, this is only going to work properly when Ns_Strtok is
 *   implemented with strtok_r.  For AOLserver 3.x, this is the case
 *   on all platforms except for MS-Windows.  [EMAIL PROTECTED],
 *   2003/02/07 14:58 EST
 *
 * ----------------------------------------------------------------------
 */

char *
strtok(char *s1, const char *s2) {
   return Ns_Strtok(s1, s2);
}


struct tm *
localtime(const time_t *clock) {
   static const char func_name[] = "localtime";
   return Ns_Localtime(clock);
}


#define GETHOSTBYNAME_BUF_LEN 16384

static Ns_Tls   gethostbyname_tls = NULL;
static Ns_Mutex tls_lock;

typedef struct {
   struct hostent result;
   char buf[GETHOSTBYNAME_BUF_LEN];
} gethostbyname_t;

static void
Mt_TlsCleanup_gethostbyname(void * ptr) {
   /*
    * It's this simple only because there are no points to other
    * dynamically allocated storage inside the gethostbyname_t
    struct.
    */
   Ns_Free(ptr);
}

struct hostent *
gethostbyname(const char * name) {
   static const char func_name[] = "gethostbyname";
   int rc_err = 0;
   gethostbyname_t * state_ptr = NULL;
   /* It makes little or no sense for gethostbyname_r to return a
      hostent* rather than just an int, but that's the way it is:
      [EMAIL PROTECTED], 2003/02/09 08:05 EST */
   struct hostent * rc = NULL;

   Ns_Log(Notice, "%s: %s: Reimplementing library function:", MODULE_NAME, func_name);

   if (gethostbyname_tls == NULL) {
      Ns_MutexLock(&tls_lock);
      if (gethostbyname_tls == NULL) {
         state_ptr = (gethostbyname_t *) Ns_Calloc(sizeof(gethostbyname_t), 1);
         Ns_TlsAlloc(&gethostbyname_tls, Mt_TlsCleanup_gethostbyname);
         Ns_TlsSet(&gethostbyname_tls, state_ptr);
      }
      Ns_MutexUnlock(&tls_lock);
   }

   if (state_ptr == NULL) {
      state_ptr = Ns_TlsGet(&gethostbyname_tls);
   }

   rc = gethostbyname_r(name, &(state_ptr->result),
                        state_ptr->buf, GETHOSTBYNAME_BUF_LEN, &rc_err);

   if (rc == NULL) {
      if (rc_err == ERANGE) {
         Ns_Log(Error, "%s: %s: Failed with error code ERANGE, buffer too small.",
                MODULE_NAME, func_name);
      } else {
         Ns_Log(Error, "%s: %s: Failed with error code '%i'.",
                MODULE_NAME, func_name, rc_err);
      }
   } else {
      /* rc == &(state_ptr->result) */
   }

   return rc;
}

--
Andrew Piskorski <[EMAIL PROTECTED]>
http://www.piskorski.com

Reply via email to