Mysterious hang with openssl and asan on ubuntu 18.04

classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

Mysterious hang with openssl and asan on ubuntu 18.04

Dan Kegel-2
Hi folks.

The project I'm working on exhibits a hang in one test case when
dealing with openssl connections, usually on 8 core machines,
when built with address sanitizer enabled.  This is mature,
theoretically well-debugged production code.  With help from c-reduce,
I minimized the rather complex test case to 108 lines of C
linked to openssl.  The hang occurs only on Ubuntu 18.04, not
on ubuntu 19.10 or 20.04 beta.   Before I go trying to run c-reduce 
on openssl, I thought I'd run the so-far minimal reproducer by folks
here and see if anyone can think of a change between openssl 1.1.1 and 1.1.13
(well, to be exact, ubuntu's 1.1.1-1ubuntu2.1~18.04.5 and 1.1.1d-2ubuntu3)
that might account for this.

The following script reproduces the hang reliably for me in under a minute
(sometimes under a second):

#!/bin/sh
set -ex
gcc -g -O2 -fsanitize=address -pthread bug.i -lssl -o bug
export ASAN_OPTIONS=detect_stack_use_after_return=1
export LSAN_OPTIONS=verbosity=1
for iter in $(seq 1 1000)
do
  ./bug
done
echo "No hang found."

where bug.i contains:

-- snip --
static int readers = 40;
static int once_control = 0;
static int test_secs = 1;

typedef int pid_t;
struct timeval {
  long tv_sec;
  long tv_usec;
};
struct timespec {
  long tv_sec;
  long tv_nsec;
};
typedef unsigned long int pthread_t;
enum __itimer_which {
  ITIMER_REAL,
};
struct itimerval {
  struct timeval it_interval;
  struct timeval it_value;
};
typedef int sig_atomic_t;
typedef void(*__sighandler_t);
struct sigaction {
  struct {
    __sighandler_t sa_handler;
  } __sigaction_handler;
};

typedef struct ssl_ctx_st SSL_CTX;
typedef struct ssl_method_st SSL_METHOD;
static SSL_CTX *context;

static struct timespec start_time;
static struct timespec goal_end_time;
static volatile sig_atomic_t keep_on_chugging = 1;

void do_once() {
  const struct ssl_method_st *p = 0;
  SSL_CTX *context = SSL_CTX_new(p);
}

void thread_main(void *v) { }

void do_work() {
  pthread_t net_5;
  pthread_once(&once_control, do_once);
  pthread_create(&net_5, 0, thread_main, 0);
}

static _Bool is_time_to_quit(void) {
  struct timespec now_time;
  clock_gettime(0, &now_time);
  long long remaining_nsecs =
      (goal_end_time.tv_sec - now_time.tv_sec) * 1000000000ULL;
  remaining_nsecs += goal_end_time.tv_nsec - now_time.tv_nsec;
  if (remaining_nsecs < 0) return 1;
  return 0;
}

static void set_flag_for_exit(int signo) {
  if (is_time_to_quit()) keep_on_chugging = 0;
}

static void set_timer(void) {
  struct itimerval iv = {{0}, {0}};
  iv.it_value.tv_usec = 3000;
  iv.it_interval.tv_usec = 3000;
  if (setitimer(ITIMER_REAL, &iv, ((void *)0)) < 0)
    abort();
  if (is_time_to_quit()) exit(0);
}

static void create_children(void) {
  int i;
  struct sigaction act;
  memset(&act, 0, sizeof(act));
  act.__sigaction_handler.sa_handler = set_flag_for_exit;
  sigaction(14, &act, ((void *)0));
  act.__sigaction_handler.sa_handler = ((__sighandler_t)1);
  pid_t pid;
  for (i = 0; i < readers; i++) {
    if (i < readers) {
      if ((pid = fork()) < 0)
        abort();
      if (pid == 0) {
        set_timer();
        do_work();
        _exit(0);
      }
    }
  }
}

static int reap_children(void) {
  int i;
  int status;
  for (i = 0; i < readers; i++) wait(&status);
}

int main(int argc, char *argv[]) {
  clock_gettime(0, &start_time);
  goal_end_time = start_time;
  goal_end_time.tv_sec += test_secs;

  create_children();
  reap_children();
}

-- snip --