commit b683490c9408a4c1a8e3e0cbf48214f60a7de867 from: Sergey Bronnikov date: Tue Dec 19 14:17:45 2023 UTC 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 commit - 345401063a6720e61f93f38a24732478f6628c2c commit + b683490c9408a4c1a8e3e0cbf48214f60a7de867 blob - /dev/null blob + d35973abc4c5b17df58d8dad4813b6e42c0fcd6a (mode 644) --- /dev/null +++ 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). blob - 8f2f0fb49166348a6bd1fc3c4fd8fc36b1e4c928 blob + da8e6b9fbde8fe9b146ffb782c2468d5b8a8c688 --- src/curl.c +++ 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); }