This is one of those bugs that can cost entire days of work. The symptom of this bug is that git -c http.extraheader push ... will fail frequently in Git for Windows v2.24.0, but not always. When it fails, though, it will cause a segmentation fault in git remote-https while calling http_cleanup() and leave no visual indication of that problem, the only indication of a problem will be the exit code of the caller (in this instance, git push will fail with exit code 1). In my tests during the pre-release period, I pushed many a time, it probably failed a lot, in the way indicated above, and due to the absence of any error message, I failed to realize that there was a problem in the first place. Fun side note: this bug haunted me for the best part of this past Monday, when I tried to get Git for Windows v2.24.0 out the door. Large parts of Git for Windows' release management are scripted, and that script failed, claiming to have been unsuccessful in pushing the tag v2.24.0.windows.1, just after printing a message to the extent that the tag is already up to date (except in the first attempt, when it reported to have been successful in pushing the tag). My attempts to fix the release script were doomed to fail because the root cause was not a bug in the script, but the bug fixed in this patch. Changes since v1: * The patch was completely redone: instead of moving the call to curl_global_init() (which would have broken support for http.sslbackend), this iteration instead replaces the usage of cURL's slist in the config handling by using Git's own string_list. Johannes Schindelin (1): remote-curl: unbreak http.extraHeader with custom allocators http.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) base-commit: 93b980e58f5624ee4e3b2dc0d0babaa97ef66d19 Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-453%2Fdscho%2Ffix-curl-xmalloc-regression-v2 Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-453/dscho/fix-curl-xmalloc-regression-v2 Pull-Request: https://github.com/gitgitgadget/git/pull/453 Range-diff vs v1: 1: d47a2aa594 ! 1: 3168ba2c9e remote-curl: unbreak http.extraHeader with custom allocators @@ -15,8 +15,18 @@ presence of custom allocators, cURL would attempt to use the wrong allocator to release the memory. - Let's fix this by moving the initialization _before_ the - `http_options()` function is called. + A naïve attempt at fixing this would move the call to + `curl_global_init()` _before_ the config is parsed (i.e. before that + call to `slist_append()`). + + However, that does work, as we _also_ parse the config setting + `http.sslbackend` and if found, call `curl_global_sslset()` which *must* + be called before `curl_global_init()`, for details see: + https://curl.haxx.se/libcurl/c/curl_global_sslset.html + + So let's instead make the config parsing entirely independent from + cURL's data structures. Incidentally, this deletes two more lines than + it introduces, which is nice. Signed-off-by: Johannes Schindelin <johannes.schindelin@xxxxxx> @@ -24,22 +34,50 @@ --- a/http.c +++ b/http.c @@ - char *normalized_url; - struct urlmatch_config config = { STRING_LIST_INIT_DUP }; - -+ if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) -+ die("curl_global_init failed"); -+ - config.section = "http"; - config.key = NULL; - config.collect_fn = http_options; + + static struct curl_slist *pragma_header; + static struct curl_slist *no_pragma_header; +-static struct curl_slist *extra_http_headers; ++static struct string_list extra_http_headers = STRING_LIST_INIT_DUP; + + static struct active_request_slot *active_queue_head; + @@ + if (!value) { + return config_error_nonbool(var); + } else if (!*value) { +- curl_slist_free_all(extra_http_headers); +- extra_http_headers = NULL; ++ string_list_clear(&extra_http_headers, 0); + } else { +- extra_http_headers = +- curl_slist_append(extra_http_headers, value); ++ string_list_append(&extra_http_headers, value); + } + return 0; } +@@ #endif + curl_global_cleanup(); + +- curl_slist_free_all(extra_http_headers); +- extra_http_headers = NULL; ++ string_list_clear(&extra_http_headers, 0); + + curl_slist_free_all(pragma_header); + pragma_header = NULL; +@@ + + struct curl_slist *http_copy_default_headers(void) + { +- struct curl_slist *headers = NULL, *h; ++ struct curl_slist *headers = NULL; ++ const struct string_list_item *item; -- if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) -- die("curl_global_init failed"); -- - http_proactive_auth = proactive_auth; +- for (h = extra_http_headers; h; h = h->next) +- headers = curl_slist_append(headers, h->data); ++ for_each_string_list_item(item, &extra_http_headers) ++ headers = curl_slist_append(headers, item->string); - if (remote && remote->http_proxy) + return headers; + } -- gitgitgadget