From c6e6dd939b6175b17880b794293b7376668b4ac6 Mon Sep 17 00:00:00 2001
From: Sergey Bronnikov <sergeyb@tarantool.org>
Date: Mon, 20 Nov 2023 10:06:32 +0300
Subject: [PATCH] httpc: fix a crash triggered by gc

Bump curl version to 8.4.0 triggers a crash in Tarantool due to commit
"h2: testcase and fix for pausing h2 streams" [1]. The original
reproducer involves etcd and an etcd-client Lua module, running
etcd-client tests as a part of Tarantool integration testing is planned
to do in scope of [1].

However, the problem could be reproduced with a Lua code below:

```
local url = 'https://google.com/'

local c = require('http.client').new()

r1 = c:get(url, {chunked = true})
r1:read(1)
r2 = c:get(url, {chunked = true})
r2:read(1)
r3 = c:get(url, {chunked = true})
r3:read(1)
r4 = c:get(url, {chunked = true})
r4:read(1)

c = nil
collectgarbage()
collectgarbage()

r1:read(1)
r2:read(1)
r3:read(1)
r4:read(1)

collectgarbage()
collectgarbage()
```

According to Curl documentation, `curl_multi_cleanup` [1] must be called
before any easy handles are cleaned up. The patch adds a cleanup of easy
handles on running `curl_env_destroy`, right before calling
`curl_multi_cleanup`. The patch uses a function 'curl_multi_get_handles'
that returns all added easy handles introduced in Curl 8.4.0. Therefore
bump to 8.4.0 is required.

1. https://github.com/curl/curl/commit/6b9a591bf7d82031f463373706d7de1cba0adee6
2. https://curl.se/libcurl/c/curl_multi_cleanup.html

Fixes #9283

1. https://github.com/tarantool/tarantool/issues/9093

NO_DOC=bugfix
NO_TEST=no simple reproducer, covered by tests in etcd-client
---
 .../unreleased/gh-9283-fix-crash-on-gc-collect.md  |  3 +++
 src/curl.c                                         | 14 +++++++++++++-
 2 files changed, 16 insertions(+), 1 deletion(-)
 create mode 100644 changelogs/unreleased/gh-9283-fix-crash-on-gc-collect.md

diff --git a/changelogs/unreleased/gh-9283-fix-crash-on-gc-collect.md b/changelogs/unreleased/gh-9283-fix-crash-on-gc-collect.md
new file mode 100644
index 0000000000..d35973abc4
--- /dev/null
+++ b/changelogs/unreleased/gh-9283-fix-crash-on-gc-collect.md
@@ -0,0 +1,3 @@
+## bugfix/http
+
+* Fixed a crash on garbage collection of httpc objects (gh-9283).
diff --git a/src/curl.c b/src/curl.c
index 8f2f0fb491..da8e6b9fbd 100644
--- a/src/curl.c
+++ b/src/curl.c
@@ -262,8 +262,20 @@ void
 curl_env_destroy(struct curl_env *env)
 {
 	assert(env);
-	if (env->multi != NULL)
+	if (env->multi != NULL) {
+		CURL **list = curl_multi_get_handles(env->multi);
+		if (list) {
+			for (int i = 0; list[i]; i++) {
+				/* We are not interested in mcode and
+				 * setting curl_diag_set_merror, because
+				 * it is a destructor.
+				 */
+				curl_multi_remove_handle(env->multi, list[i]);
+			}
+			curl_free(list);
+		}
 		curl_multi_cleanup(env->multi);
+	}
 
 	mempool_destroy(&env->sock_pool);
 }
-- 
GitLab