yhirose/cpp-httplib

SIGSEGV on https client with std::thread

toge opened this issue · 14 comments

toge commented

Probably the same problem as #1797.

SEGSEGV error on https client.
This only occurs with https clients running in std::thread.

Error code is following.

#include <iostream>
#include <thread>

#define CPPHTTPLIB_OPENSSL_SUPPORT
#include "httplib.h"

int main() {
  auto th = std::thread([&]() {
    auto client = httplib::Client("https://example.org");
    auto const&& response = client.Get("/");
    if (response->status != httplib::StatusCode::OK_200) { // SIGSEGV
      std::cout << "error : " << response->status << '\n';
      return;
    }
    std::cout << response->body << '\n';
  });

  th.join();
  return 0;
}

Both of the two codes that follow work fine.

  1. http client
#include <iostream>
#include <thread>

#include "httplib.h"

int main() {
  auto th = std::thread([&]() {
    auto client = httplib::Client("http://example.org");
    auto const&& response = client.Get("/");
    if (response->status != httplib::StatusCode::OK_200) {
      std::cout << "error : " << response->status << '\n';
      return;
    }
    std::cout << response->body << '\n';
  });

  th.join();
  return 0;
}
  1. https client with https instance on main thread
#include <iostream>
#include <thread>

#define CPPHTTPLIB_OPENSSL_SUPPORT
#include "httplib.h"

int main() {
  auto client = httplib::Client("https://example.org");
  auto th = std::thread([&]() {
    auto const&& response = client.Get("/");
    if (response->status != httplib::StatusCode::OK_200) {
      std::cout << "error : " << response->status << '\n';
      return;
    }
    std::cout << response->body << '\n';
  });

  th.join();
  return 0;
}

My environments is following:

item detail
OS Fedora Linux 40 (x64)
Compiler gcc 14.2.1
cpp-httplib 0.18.0
openssl 3.3.2

@toge thanks for the report. Which line in the error example code is causing SIGSEGV?

toge commented

@yhirose

SIGSEGV occurred at the line with comment // SIGSEGV.

The following is the output of gdb.

0x0000000000421663 in operator() (__closure=<optimized out>) at test05.cpp:11
11          if (response->status != httplib::StatusCode::OK_200) {
(gdb) bt
#0  0x0000000000421663 in operator() (__closure=<optimized out>) at test05.cpp:11
#1  0x00007ffff7ce7564 in std::execute_native_thread_routine (__p=0x985e10) at ../../../../../libstdc++-v3/src/c++11/thread.cc:104
#2  0x00007ffff7aa66d7 in start_thread (arg=<optimized out>) at pthread_create.c:447
#3  0x00007ffff7b2a60c in clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:78

httplib::Result.res_.get() is null.

(gdb) p response
$1 = (const httplib::Result &&) @0x7ffff79ffb80: {res_ = std::unique_ptr<httplib::Response> = {get() = 0x0}, err_ = httplib::Error::Success, request_headers_ = std::unordered_multimap with 0 elements}
(gdb) p response.res_.get()
$2 = (httplib::Response *) 0x0

@toge could you try the following code?
https://github.com/yhirose/cpp-httplib?tab=readme-ov-file#client-1

#include <iostream>
#include <thread>

#define CPPHTTPLIB_OPENSSL_SUPPORT
#include "httplib.h"

int main() {
  auto th = std::thread([&]() {
    auto client = httplib::Client("https://example.org");

    if (auto response = client.Get("/")) {
      if (response->status != httplib::StatusCode::OK_200) { // SIGSEGV
        std::cout << "error status: " << response->status << '\n';
      }
      std::cout << response->body << '\n';
    } else {
      auto err = response.error();
      std::cout << "error: " << httplib::to_string(err) << std::endl;
    }
  });

  th.join();
  return 0;
}
toge commented

@yhirose
It prints following without SIGSEGV.

error: Success (no error)

@yhirose thanks for the additional info. I tested it on macOS and Ubuntu 22.04, but I didn't get the problem. So it seems like the particular environment (Fedora Linux 40, g++ 14.2.1, OpenSSL 3.3.2) causes the problem. I'll look into it when I am available.

toge commented

@yhirose
Thank you for your investigation.
I will check this on other environments.

toge commented

@yhirose
I have made some progress in investigation and will share it.

It is not caused by cpp-httplib.
It seems to be caused by openssl.

SSL_CTX_new returns null pointer on multi thread environment.
https://github.com/yhirose/cpp-httplib/blob/v0.18.0/httplib.h#L9161

The following code causes same error.

#include <iostream>
#include <thread>
#include <openssl/ssl.h>

int main() {
  SSL_library_init();
  auto th = std::thread([&]() {
    auto ctx = SSL_CTX_new(TLS_client_method());
    if (ctx == nullptr) {
      std::cerr << "failed to create SSL_CTX" << std::endl;
    }
    SSL_CTX_free(ctx);
  });
  th.join();
}

Of course, following code works fine.

#include <iostream>
#include <thread>
#include <openssl/ssl.h>

int main() {
  SSL_library_init();
  auto ctx = SSL_CTX_new(TLS_client_method());
  if (ctx == nullptr) {
    std::cerr << "failed to create SSL_CTX" << std::endl;
  }
  SSL_CTX_free(ctx);
}

There is a workaround "calling SSL_load_error_strings()".
But I don't know the reason (yet).

#include <iostream>
#include <thread>
#include <openssl/ssl.h>

int main() {
  SSL_library_init();
  SSL_load_error_strings();
  auto th = std::thread([&]() {
    auto ctx = SSL_CTX_new(TLS_client_method());
    if (ctx == nullptr) {
      std::cerr << "failed to create SSL_CTX" << std::endl;
    }
    SSL_CTX_free(ctx);
  });
  th.join();
}