cURL / Mailing Lists / curl-library / Single Mail

curl-library

Corrupted heap after several hours

From: <chrisenpingouin_at_free.fr>
Date: Wed, 11 Jun 2008 12:01:09 +0200

Hello All,

I'm using a fedora core 6 linux PC in which I replaced the openssl library with
version 0.9.8g and the curl library with 7.18.1, compiled with c-ares 1.5.2
support.

Then, my program is made of several threads which regularly perform HTTP or
HTTPS GET requests until I decide to end it.

However, if I don't end the program before awhile, it ends up with a
"Segmentation fault" error message.

I ran it into GDB and noticed that this error was due to a corrupted list of
pthread_mutex_t* objects used by the OpenSSL lock_callback() operation. (Code is
the same as the one in "curl-7.18.1/docs/examples/threaded-ssl.c".

I first suspected my program to write in some unallocated memory location and,
therefore, ran it into valgrind and I noticed that a function called
"socket_write()" from libcurl.so.4 attempted to write to an unitialized
variable.

Then, since "curl-7.18.1/docs/examples/threaded-ssl.c" is the closest
illustration of what I'm actually trying to do, I just modified this code in
order for it to properly initialize the OpenSSL library at start, and properly
free resources at end.

Here is the code I have:
----- Beginning of code -----
/*****************************************************************************
 * _ _ ____ _
 * Project ___| | | | _ \| |
 * / __| | | | |_) | |
 * | (__| |_| | _ <| |___
 * \___|\___/|_| \_\_____|
 *
 * $Id: threaded-ssl.c,v 1.2 2008-02-23 23:00:24 bagder Exp $
 *
 * A multi-threaded example that uses pthreads and fetches 4 remote files at
 * once over HTTPS. The lock callbacks and stuff assume OpenSSL or GnuTLS
 * (libgcrypt) so far.
 *
 * OpenSSL docs for this:
 * http://www.openssl.org/docs/crypto/threads.html
 * gcrypt docs for this:
 * http://gnupg.org/documentation/manuals/gcrypt/Multi_002dThreading.html
 */

#define USE_OPENSSL /* or USE_GNUTLS accordingly */

#include <stdio.h>
#include <pthread.h>
#include <curl/curl.h>

/* we have this global to let the callback get easy access to it */
static pthread_mutex_t *lockarray;

#ifdef USE_OPENSSL
#include <openssl/crypto.h>
static void lock_callback(int mode, int type, char *file, int line)
{
  (void)file;
  (void)line;
  if (mode & CRYPTO_LOCK) {
    pthread_mutex_lock(&(lockarray[type]));
  }
  else {
    pthread_mutex_unlock(&(lockarray[type]));
  }
}

static unsigned long thread_id(void)
{
  unsigned long ret;

  ret=(unsigned long)pthread_self();
  return(ret);
}

static void init_locks(void)
{
  int i;

  lockarray=(pthread_mutex_t *)OPENSSL_malloc(CRYPTO_num_locks() *
                                            sizeof(pthread_mutex_t));
  for (i=0; i<CRYPTO_num_locks(); i++) {
    pthread_mutex_init(&(lockarray[i]),NULL);
  }

  CRYPTO_set_id_callback((unsigned long (*)())thread_id);
  CRYPTO_set_locking_callback((void (*)())lock_callback);
}

static void kill_locks(void)
{
  int i;

  CRYPTO_set_locking_callback(NULL);
  for (i=0; i<CRYPTO_num_locks(); i++)
    pthread_mutex_destroy(&(lockarray[i]));

  OPENSSL_free(lockarray);
}
#endif

#ifdef USE_GNUTLS
#include <gcrypt.h>
#include <errno.h>

GCRY_THREAD_OPTION_PTHREAD_IMPL;

void init_locks(void)
{
  gcry_control(GCRYCTL_SET_THREAD_CBS);
}

#define kill_locks()
#endif

/* List of URLs to fetch.*/
const char *urls[]= {
  "https://www.sf.net/",
  "https://www.openssl.org/",
  "https://www.sf.net/",
  "https://www.openssl.org/",
};

static void *pull_one_url(void *url)
{
  CURL *curl;

  curl = curl_easy_init();
  curl_easy_setopt(curl, CURLOPT_URL, url);
  /* this example doesn't verify the server's certificate, which means we
     might be downloading stuff from an impostor */
  curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
  curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);
  curl_easy_perform(curl); /* ignores error */
  curl_easy_cleanup(curl);

  return NULL;
}

int main(int argc, char **argv)
{
  pthread_t tid[4];
  int i;
  int error;
  (void)argc; /* we don't use any arguments in this example */
  (void)argv;

        curl_global_init(CURL_GLOBAL_ALL);

  init_locks();

  for(i=0; i< 4; i++) {
    error = pthread_create(&tid[i],
                           NULL, /* default attributes please */
                           pull_one_url,
                           (void *)urls[i]);
    if(0 != error)
      fprintf(stderr, "Couldn't run thread number %d, errno %d\n", i, error);
    else
      fprintf(stderr, "Thread %d, gets %s\n", i, urls[i]);
  }

  /* now wait for all threads to terminate */
  for(i=0; i< 4; i++) {
    error = pthread_join(tid[i], NULL);
    fprintf(stderr, "Thread %d terminated\n", i);
  }

  kill_locks();

        curl_global_cleanup();
  return 0;
}
----- End of code -----

I compiled this code with the following command:

gcc -g -lcurl -lssl -lcrypto -lpthread -o threaded-ssl threaded-ssl.c

Then, I ran it with the following command:

valgrind --leak-check=full -v -v --log-file=threaded-ssl_vglog ./threaded-ssl

The program seemed to run without errors. On stdout, I even could read the
retrieved HTML pages but the valgrind log file was full or errors like this:

==5961== Conditional jump or move depends on uninitialised value(s)
==5961== at 0x40EFCCE: BN_mod_inverse (in /usr/local/lib/libcurl.so.4.0.1)

Here is the beginning of the valgrind log file:
----- valgrind exerpt begin -----
==5961== Memcheck, a memory error detector.
==5961== Copyright (C) 2002-2006, and GNU GPL'd, by Julian Seward et al.
==5961== Using LibVEX rev 1658, a library for dynamic binary translation.
==5961== Copyright (C) 2004-2006, and GNU GPL'd, by OpenWorks LLP.
==5961== Using valgrind-3.2.1, a dynamic binary instrumentation framework.
==5961== Copyright (C) 2000-2006, and GNU GPL'd, by Julian Seward et al.
==5961==
==5961== My PID = 5961, parent PID = 29105. Prog and args are:
==5961== ./threaded-ssl
==5961==
--5961--
--5961-- Command line
--5961-- ./threaded-ssl
--5961-- Startup, with flags:
--5961-- --leak-check=full
--5961-- -v
--5961-- -v
--5961-- --log-file=threaded-ssl_vglog
--5961-- Contents of /proc/version:
--5961-- Linux version 2.6.9-67.0.15.ELsmp
(brewbuilder_at_hs20-bc1-7.build.redhat.com) (gcc version 3.4.6 20060404 (Red Hat
3.4.6-9)) #1 SMP Tue Apr 22 13:50:33 EDT 2008
--5961-- Arch and hwcaps: X86, x86-sse1-sse2
--5961-- Valgrind library directory: /usr/local/lib/valgrind
--5961-- TT/TC: VG_(init_tt_tc) (startup of code management)
--5961-- TT/TC: cache: 8 sectors of 20127744 bytes each = 161021952 total
--5961-- TT/TC: table: 524168 total entries, max occupancy 419328 (80%)
--5961-- Reading syms from /lib/ld-2.3.4.so (0x78C000)
--5961-- Reading syms from
/SavePeer/LinuxPeer/external/Peer/curl-7.18.1/docs/examples/threaded-ssl
(0x8048000)
--5961-- Reading syms from /usr/local/lib/valgrind/x86-linux/memcheck
(0x38000000)
--5961-- object doesn't have a dynamic symbol table
--5961-- Reading suppressions file: /usr/local/lib/valgrind/default.supp
--5961-- TT/TC: initialise sector 0
--5961-- REDIR: 0x79E5B0 (index) redirected to 0x38021F77
(vgPlain_x86_linux_REDIR_FOR_index)
--5961-- Reading syms from /usr/local/lib/valgrind/x86-linux/vgpreload_core.so
(0x4000000)
--5961-- Reading syms from
/usr/local/lib/valgrind/x86-linux/vgpreload_memcheck.so (0x4003000)
==5961== WARNING: new redirection conflicts with existing -- ignoring it
--5961-- new: 0x0079E5B0 (index ) R-> 0x04005F04 index
--5961-- REDIR: 0x79E750 (strlen) redirected to 0x4006148 (strlen)
--5961-- Reading syms from /usr/local/lib/libcurl.so.4.0.1 (0x4023000)
--5961-- Reading syms from /usr/local/ssl/lib/libssl.so.0.9.8 (0x4154000)
--5961-- Reading syms from /lib/tls/libpthread-2.3.4.so (0xA02000)
--5961-- Reading syms from /lib/tls/libc-2.3.4.so (0x7A6000)
--5961-- Reading syms from /usr/lib/libidn.so.11.4.6 (0xC9A000)
--5961-- object doesn't have a symbol table
--5961-- Reading syms from /usr/lib/libldap-2.2.so.7.0.6 (0x2F1000)
--5961-- object doesn't have a symbol table
--5961-- Reading syms from /usr/lib/libgssapi_krb5.so.2.2 (0x484000)
--5961-- object doesn't have a symbol table
--5961-- Reading syms from /usr/lib/libkrb5.so.3.2 (0x4FE5000)
--5961-- object doesn't have a symbol table
--5961-- Reading syms from /lib/libcom_err.so.2.1 (0xDFB000)
--5961-- object doesn't have a symbol table
--5961-- Reading syms from /usr/lib/libk5crypto.so.3.0 (0x72B000)
--5961-- object doesn't have a symbol table
--5961-- Reading syms from /lib/libresolv-2.3.4.so (0xCD8000)
--5961-- Reading syms from /lib/libdl-2.3.4.so (0x8F9000)
--5961-- Reading syms from /usr/lib/libz.so.1.2.1.2 (0x9F0000)
--5961-- object doesn't have a symbol table
--5961-- Reading syms from /usr/local/ssl/lib/libcrypto.so.0.9.8 (0x4191000)
--5961-- Reading syms from /usr/lib/liblber-2.2.so.7.0.6 (0x8FF000)
--5961-- object doesn't have a symbol table
--5961-- Reading syms from /usr/lib/libsasl2.so.2.0.19 (0xBA1000)
--5961-- object doesn't have a symbol table
--5961-- Reading syms from /usr/lib/libssl.so.0.9.8 (0x277000)
--5961-- Reading syms from /usr/lib/libcrypto.so.0.9.8 (0x4CB000)
--5961-- Reading syms from /lib/libcrypt-2.3.4.so (0x466D000)
--5961-- REDIR: 0x810BB0 (memset) redirected to 0x4006A1C (memset)
--5961-- REDIR: 0x8110D0 (memcpy) redirected to 0x40064B4 (memcpy)
--5961-- REDIR: 0x80FD50 (rindex) redirected to 0x4005E34 (rindex)
--5961-- REDIR: 0x80F970 (strlen) redirected to 0x400612C (strlen)
--5961-- REDIR: 0x80A9A0 (malloc) redirected to 0x40044A0 (malloc)
--5961-- REDIR: 0x80F410 (strcmp) redirected to 0x40063E8 (strcmp)
--5961-- REDIR: 0x80AFE0 (realloc) redirected to 0x400595E (realloc)
--5961-- REDIR: 0x80FB70 (strncmp) redirected to 0x4006398 (strncmp)
--5961-- REDIR: 0x810DA0 (stpcpy) redirected to 0x40067AC (stpcpy)
--5961-- REDIR: 0x80FC90 (strncpy) redirected to 0x400624C (strncpy)
--5961-- REDIR: 0x8088B0 (free) redirected to 0x4005039 (free)
--5961-- REDIR: 0x80A6C0 (calloc) redirected to 0x40058A9 (calloc)
--5961-- REDIR: 0x8106B0 (memchr) redirected to 0x4006490 (memchr)
--5961-- REDIR: 0x811930 (rawmemchr) redirected to 0x4006AAC (rawmemchr)
--5961-- REDIR: 0x80F2A0 (index) redirected to 0x4005EE4 (index)
--5961-- REDIR: 0x80F480 (strcpy) redirected to 0x4006180 (strcpy)
==5961== Thread 5:
==5961== Syscall param write(buf) points to uninitialised byte(s)
==5961== at 0xA0C61B: (within /lib/tls/libpthread-2.3.4.so)
==5961== by 0x40A6CA1: sock_write (in /usr/local/lib/libcurl.so.4.0.1)
==5961== Address 0x476D887 is 15 bytes inside a block of size 21,848 alloc'd
==5961== at 0x4004522: malloc (vg_replace_malloc.c:149)
==5961== by 0x408E69A: default_malloc_ex (in /usr/local/lib/libcurl.so.4.0.1)
--5961-- REDIR: 0x810B40 (memmove) redirected to 0x4006A3C (memmove)
==5961==
==5961== Conditional jump or move depends on uninitialised value(s)
==5961== at 0x40EFCCE: BN_mod_inverse (in /usr/local/lib/libcurl.so.4.0.1)
----- valgrind exerpt end -----

I probably made a mistake while trying to use libcurl but what did I do wrong
please?

Many thanks in advance. Have a nice day. ChD
Received on 2008-06-11