8000 SSL_CTX_set_quiet_shutdown in SSL_CTX_FUNCTION callback Leads To Fault in macOS · Issue #17283 · curl/curl · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

SSL_CTX_set_quiet_shutdown in SSL_CTX_FUNCTION callback Leads To Fault in macOS #17283

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
dgreatwood opened this issue May 8, 2025 · 9 comments

Comments

@dgreatwood
Copy link

I did this

On macOS, when an SSL_CTX_new openSSL call is made prior to calling curl_easy_perform, and an CURLOPT_SSL_CTX_FUNCTION callback is provided , and the callback invokes SSL_CTX_set_quiet_shutdown, there is intermittently a Fault, for instance a Seg Fault, during curl_easy_perform or after the curl_easy_perform returns.

OS Version: Principally macOS 15.4.1 on M3 silicon.
Also fails intermittently on macOS 13 and macOS 14
Does NOT fail on Linux, nor on Windows (tested on multiple versions of each).
Libcurl version:

curl -V
curl 8.7.1 (x86_64-apple-darwin24.0) libcurl/8.7.1 (SecureTransport) LibreSSL/3.3.6 zlib/1.2.12 nghttp2/1.64.0
Release-Date: 2024-03-27
Protocols: dict file ftp ftps gopher gophers http https imap imaps ipfs ipns ldap ldaps mqtt pop3 pop3s rtsp smb smbs smtp smtps telnet tftp
Features: alt-svc AsynchDNS GSS-API HSTS HTTP2 HTTPS-proxy IPv6 Kerberos Largefile libz MultiSSL NTLM SPNEGO SSL threadsafe UnixSockets
openssl version
OpenSSL 3.4.0 22 Oct 2024 (Library: OpenSSL 3.4.0 22 Oct 2024)

Frequency of Fault: The frequency seems sensitive to small changes in the code, but typically occurs between 1% and 20% of the test runs.

URL/protocol. Protocol is HTTPS. The issue is seen whether the target is a localhost server or (as in example code below) google.com.

Possible incorrect sslctx pointer in callback
In the example test code provided below, SSL_CTX_set_quiet_shutdown sets the quiet-shutdown flag to 1. However, even if the SSL_CTX_set_quiet_shutdown writes 0 instead of 1, the Fault will still be seen from time to time. This is odd, since 0 is supposed to be the default value.
If SSL_CTX_get_quiet_shutdown is invoked in the callback, it sometimes returns an obviously invalid value, such as a large negative number (a value of 0 or 1 is expected).
Given these two data points, we may speculate that the sslctx pointer passed to the callback is invalid in some way, and so when we write to the object sslctx is pointing too we may corrupt the memory heap.

Other Observations
If we comment out the SSL_CTX_set_quiet_shutdown in the callback, there is no Fault. It is not the callback method that leads to the Fault, it is writing to *sslctx within the callback.

The Fault only happens if openSSL's SSL_CTX_new is invoked prior to the curl calls. Otherwise, there is no Fault.

Here is a Google Test that generates the issue for us.

static size_t write_cb(void* contents, size_t size, size_t nmemb, void* userp)
{
    (static_cast<std::string*>(userp))->append(static_cast<char*>(contents), size * nmemb);
    return size * nmemb;
}

TEST(https_server_test, basic_tls_requests_with_no_shutdown_from_peer)
{
    CURLcode res = curl_global_init(CURL_GLOBAL_DEFAULT);

    ASSERT_EQ(res, CURLE_OK);

    const auto sslctxCallback = +[](CURL* curl, void* sslctx, void* /* parm */) -> CURLcode {
        // Enable quiet shutdown so that we do not send any shutdown notification to server from peer.

        SSL_CTX_set_quiet_shutdown(reinterpret_cast<SSL_CTX*>(sslctx), 1L);
        return CURLE_OK;
    };

    SSL_load_error_strings();
    OpenSSL_add_ssl_algorithms();

    const SSL_METHOD* method = SSLv23_server_method();
    if (!method)
    {
        throw std::runtime_error("Cannot setup SSL context");
    }

    SSL_CTX * ssl_ctx_ptr = SSL_CTX_new(method);
    if (!ssl_ctx_ptr)
    {
        throw std::runtime_error("Cannot setup SSL context");
    }

    const std::string url("https://www.google.com:443");
    std::string buffer_no_shutdown;

    // Perform request with quiet shutdown and ensure it is handled.
    CURL* curl = curl_easy_init();
    ASSERT_NE(curl, nullptr);

    curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
    curl_easy_setopt(curl, CURLOPT_CAINFO, "./certs/rootCA.crt");
    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &write_cb);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buffer_no_shutdown);

    curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, sslctxCallback);

    res = curl_easy_perform(curl);

    EXPECT_EQ(res, CURLE_OK);

    curl_easy_cleanup(curl);

    buffer_no_shutdown.clear();

    curl_global_cleanup();
}

Here is a stack trace of the Fault itself. BTW, the location of the Fault seems to vary, but it usually is something to do with initialization, or memory free or cleanup or similar.

ENGINE_init (@ENGINE_init:15)
int_ctx_new (@int_ctx_new:35)
do_sigver_init (@do_sigver_init:25)
ASN1_item_verify (@ASN1_item_verify:64)
internal_verify (@internal_verify:75)
X509_verify_cert_orig (@X509_verify_cert_orig:207)
X509_verify_cert (@X509_verify_cert:27)
ssl_verify_cert_chain (@ssl_verify_cert_chain:79)
tls13_server_certificate_recv (@tls13_server_certificate_recv:107)
tls13_handshake_perform (@tls13_handshake_perform:114)
tls13_legacy_connect (@tls13_legacy_connect:17)
ossl_connect_common (@ossl_connect_common:66)
ssl_cf_connect (@ssl_cf_connect:85)
cf_setup_connect (@cf_setup_connect:30)
cf_hc_connect (@cf_hc_connect:174)
Curl_conn_connect (@Curl_conn_connect:23)
multi_runsingle (@multi_runsingle:137)
curl_multi_perform (@curl_multi_perform:54)
curl_easy_perform (@curl_easy_perform:71)
https_server_test_basic_tls_requests_with_no_shutdown_from_peer_Test::TestBody() 

Thanks in advance.

I expected the following

No Fault/crash

curl/libcurl version

curl 8.7.1 (x86_64-apple-darwin24.0) libcurl/8.7.1 (SecureTransport) LibreSSL/3.3.6 zlib/1.2.12 nghttp2/1.64.0
Release-Date: 2024-03-27

operating system

macOS 15

@bagder
Copy link
Member
bagder commented May 8, 2025

If it only crashes when you call that function and not otherwise, it would indicate that the ctx pointer is correct and that the function call is what triggers the problem - after all, the context that is passed to the callback is the exact same context that libcurl uses internally itself so if it would be wrong curl would totally fail (and crash) even without the callback. This, to me, would rather suggest this is a libressl problem.

The Fault only happens if openSSL's SSL_CTX_new is invoked prior to the curl calls. Otherwise, there is no Fault.

You mean invoked by something else outside of this libcurl transfer call itself? Because in curl SSL_CTX_new() has to be called before the callback, right? That's the function that creates the object to which the pointer is passed in the callback?

I think this is an additional hint that this a problem in the TLS library.

You could try to build curl to use another OpenSSL (fork) to see if the problem changes or goes away.

I also propose that you also build curl and the TLS library with full debug symbols present so that future stack traces get more info, as they might help shed light on the problem.

@icing
Copy link
Contributor
icing commented May 8, 2025

If this only crashes when you do your own SSL_CTX_new() before engaging curl, this looks very weird indeed.

@jay
Copy link
Member
jay commented May 9, 2025

OS Version: Principally macOS 15.4.1 on M3 silicon.
Also fails intermittently on macOS 13 and macOS 14
Does NOT fail on Linux, nor on Windows (tested on multiple versions of each).

Is your application on Mac using an SSL library different from the one libcurl is actually using?

@dgreatwood
Copy link
Author

@jay - yes, I think that is possible.

Per prior post, the curl -V output shows LibreSSL/3.3.6. I guess that libcurl uses the same SSL library (i.e. uses LibreSSL)?

Meanwhile, the Google test in the prior post - the one that generates the issue - is built using openssl. Specifically, it is built with meson, and adds dependency('openssl').

Would using different libraries be an issue?

@dgreatwood
Copy link
Author

If this only crashes when you do your own SSL_CTX_new() before engaging curl, this looks very weird indeed.

@icing - yes, that is exactly what happens.

@dgreatwood
Copy link
Author

If it only crashes when you call that function and not otherwise, it would indicate that the ctx pointer is correct and that the function call is what triggers the problem - after all, the context that is passed to the callback is the exact same context that libcurl uses internally itself so if it would be wrong curl would totally fail (and crash) even without the callback. This, to me, would rather suggest this is a libressl problem.
[DG] Fair enough - without knowing the root cause it is of course impossible to be certain if the issue is in the SSL library or in curl.
That said, note the point that if SSL_CTX_**get**_quiet_shutdown is called from the callback, an obviously incorrect value is sometimes returned. If SSL_CTX_**set**_quiet_shutdown is at fault, then SSL_CTX_**get**_quiet_shutdown would likely have the same issue - not impossible, but perhaps less likely than that SSL_CTX_**set**_quiet_shutdown alone has a problem.

The Fault only happens if openSSL's SSL_CTX_new is invoked prior to the curl calls. Otherwise, there is no Fault.

You mean invoked by something else outside of this libcurl transfer call itself? Because in curl SSL_CTX_new() has to be called before the callback, right? That's the function that creates the object to which the pointer is passed in the callback?
[DG] Yes, the call to SSL_CTX_new is made outside curl in application code - see the example Google test code included in the post.

I think this is an additional hint that this a problem in the TLS library.
[DG] Although as @jay pointed put it may actually be a different TLS library to the one curl is is using...

You could try to build curl to use another OpenSSL (fork) to see if the problem changes or goes away.

I also propose that you also build curl and the TLS library with full debug symbols present so that future stack traces get more info, as they might help shed light on the problem.
[DG] I could go down that path, but the stack trace seems different on every crash and seems a symptom of prior heap corruption rather than meaningful in itself.

@dgreatwood
Copy link
Author

@jay

OS Version: Principally macOS 15.4.1 on M3 silicon.
Also fails intermittently on macOS 13 and macOS 14
Does NOT fail on Linux, nor on Windows (tested on multiple versions of each).

Is your application on Mac using an SSL library different from the one libcurl is actually using?

Oh wait... I think I see your point. Maybe the issue is that CURLOPT_SSL_CTX_FUNCTION callback is invoking the openssl version of SSL_CTX_set_quiet_shutdown, but the sslctx point being passed to SSL_CTX_set_quiet_shutdown is a pointer to a LibreSSL SSL_CTX.

Is that it?

@jay
Copy link
Member
jay commented May 9, 2025

Maybe the issue is that CURLOPT_SSL_CTX_FUNCTION callback is invoking the openssl version of SSL_CTX_set_quiet_shutdown, but the sslctx point being passed to SSL_CTX_set_quiet_shutdown is a pointer to a LibreSSL SSL_CTX.

Yes that would cause a crash.

@dgreatwood
Copy link
Author

I was able to verify that using an OpenSSL version of libcurl, i.e. not a LibreSSL version, removed the crash.

For the record, to install the OpenSSL version of libcurl:
brew install curl
then before building my own test/application:
export PKG_CONFIG_PATH="$(brew --prefix)/opt/curl/lib/pkgconfig:$PKG_CONFIG_PATH"
(Note: My application builds with meson, which uses pkg-config to find the newly-installed libcurl).

Thanks so much for the help!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

No branches or pull requests

4 participants
0