SecureTransport proxy error cleanup frees borrowed stream before TLS close · Issue #7432 · libgit2/libgit2 · GitHub
Skip to content

SecureTransport proxy error cleanup frees borrowed stream before TLS close #7432

Open

Description

@vizanto

Reproduction steps

I observed a crash in a macOS arm64 application using libgit2 1.9.7 with the SecureTransport HTTPS backend while resolving refs on an HTTPS remote through an HTTP CONNECT proxy (GIT_PROXY_AUTO). A connection attempt failed and entered http_client_connect's on_error cleanup. I do not yet have a standalone reproducer, and the original connection error is unknown because cleanup crashes before it can propagate.

The relevant lifetime relationship is visible in the source:

on_error:
	if (error != GIT_RETRY)
		close_stream(&client->proxy);

	close_stream(&client->server);
	return error;

close_stream calls both git_stream_close and git_stream_free and sets the pointer to NULL. The subsequent TLS close calls SSLClose, which can call write_cb; that callback uses the borrowed st->io, which now refers to the freed proxy stream.

Expected behavior

Close/free the borrowing TLS child before its underlying proxy stream and return the original connection failure rather than crashing. The normal reconnect path already follows this order: httpclient.c:1085–1096 closes the server first, then the proxy.

Suggested minimal reorder (not locally built or verified yet):

 on_error:
+	close_stream(&client->server);
+
 	if (error != GIT_RETRY)
 		close_stream(&client->proxy);
 
-	close_stream(&client->server);
 	return error;

The GIT_RETRY guard stays on the proxy close. This moves the unconditional child cleanup; it is not a swap of the two stream names under the existing guard. The helper already handles NULL, frees the stream, and clears its pointer.

Actual behavior

SIGSEGV / EXC_BAD_ACCESS, instruction pointer 0, with this stack (top first):

0x0
write_cb + 72                         libgit2.1.9.7.dylib
SSLRecordServiceWriteQueueInternal   Security.framework
SSLClose                             Security.framework
stransport_close + 24                 libgit2.1.9.7.dylib
git_http_client_send_request + 648    libgit2.1.9.7.dylib
http_stream_read
git_smart__recv
git_smart__store_refs
git_smart__connect
git_remote_connect_ext
git_remote_connect

The crash report's library UUID (C737B73B-D177-3444-9997-890059719E64) matches the loaded libgit2 dylib. In one subsequent debugger diagnostic, stopped at stransport_close before SSLClose, the TLS stream was non-owning (owned = 0) and git_error_last()->klass was already 0. Thus no useful original error class remained at child cleanup. I did not observe the class before parent cleanup, so I cannot identify the original failure or attribute the class reset to a particular cleanup call. No conclusion about certificate validation or proxy authentication follows from this crash.

Version of libgit2 (release number or SHA1)

Observed binary: libgit2 1.9.7, native SecureTransport backend (not libcurl).

The same parent-before-child error cleanup is also present in current upstream main at 42a89d7cc9374a19e72fa57011e5fe3a99c7b3bf, httpclient.c:1118–1123. Current main has not been run locally.

Related issues #7396 and #6417 discuss SecureTransport deprecation and intermittent transport failures, respectively; neither describes this borrowed-stream cleanup order.

Operating system(s) tested

macOS, arm64 (Darwin 25.6.0).

Activity

  1. Tiancheng-Xu commented on Oct 17, 2026

    @Tiancheng-Xu

    May I take #7432 as a focused cleanup-order fix? I'd first add an offline stream regression that checks the TLS wrapper is closed and freed before its borrowed proxy, while preserving the GIT_RETRY proxy-retention behavior; I haven't reproduced the original SecureTransport crash locally yet.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions