Skip to content

Commit 6b2a8a1

Browse files
committed
[BUG] Check what curl_slist_append and curl_multi_init return
Both are allocation points whose failure was treated as success, and both sit next to a check the same file already makes. curl_slist_append returns null without freeing the list it was given, and the docs say so: "To avoid overwriting an existing non-empty list on failure, the new list should be returned to a temporary variable which can be tested for NULL before updating the original list pointer." Assigning straight back over headers_chunk did the opposite. The pointer to everything appended so far went with it, so the two places that free the list saw null and never ran, and Setup() only sets CURLOPT_HTTPHEADER when headers_chunk is non-null, so the request went out with none of the caller's headers rather than not going out. For an OTLP export that means no Content-Type and a receiver that rejects a request the exporter believes it sent. It now appends into a temporary. On failure the partial list is released, the operation reports CreateFailed the same way it already does when curl_easy_init returns null fifteen lines earlier, and nothing half built is left behind. curl_multi_init returns null the same way, and its result went straight into multi_handle_ in both constructors and in resetMultiHandle. Measured against libcurl 8.14.1, curl_multi_add_handle on a null multi handle reports CURLM_BAD_HANDLE, and that return is discarded today, so such a client would accept every session, add none of them, and complete none of them. The reset path is the worse of the three: it runs while recovering from a multi error, so a failure there turns the client into a black hole for the rest of its life. All three now go through one helper that logs. Fixes #4404 Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com>
1 parent 04a8ddd commit 6b2a8a1

3 files changed

Lines changed: 36 additions & 5 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ Increment the:
3434
deprecated C headers (`stdint.h`, `stddef.h`, `stdlib.h`, `string.h`,
3535
`stdio.h`, `ctype.h`, `limits.h`, `assert.h`) with their C++ equivalents
3636
([#4349](https://github.com/open-telemetry/opentelemetry-cpp/pull/4349))
37+
* [BUG] Check what curl_slist_append and curl_multi_init return instead of
38+
treating a failed allocation as success
39+
[#4404](https://github.com/open-telemetry/opentelemetry-cpp/issues/4404)
3740

3841
* [CONFIGURATION] Add SDK component builder interfaces to the registry
3942
[#4358](https://github.com/open-telemetry/opentelemetry-cpp/issues/4358)

‎ext/src/http/client/curl/http_client_curl.cc‎

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "opentelemetry/ext/http/common/url_parser.h"
2424
#include "opentelemetry/nostd/shared_ptr.h"
2525
#include "opentelemetry/nostd/string_view.h"
26+
#include "opentelemetry/sdk/common/global_log_handler.h"
2627
#include "opentelemetry/sdk/common/thread_instrumentation.h"
2728
#include "opentelemetry/version.h"
2829

@@ -270,8 +271,20 @@ void Session::FinishOperation()
270271
}
271272
}
272273

274+
// A null multi handle makes every curl_multi_add_handle report CURLM_BAD_HANDLE, so a client
275+
// built on one accepts sessions and never sends any of them. Say so rather than fail quietly.
276+
static CURLM *initMultiHandle()
277+
{
278+
CURLM *handle = curl_multi_init();
279+
if (nullptr == handle)
280+
{
281+
OTEL_INTERNAL_LOG_ERROR("[HTTP Client Curl] curl_multi_init failed, this client cannot send");
282+
}
283+
return handle;
284+
}
285+
273286
HttpClient::HttpClient()
274-
: multi_handle_(curl_multi_init()),
287+
: multi_handle_(initMultiHandle()),
275288
next_session_id_{0},
276289
max_sessions_per_connection_{8},
277290
background_thread_instrumentation_(nullptr),
@@ -282,7 +295,7 @@ HttpClient::HttpClient()
282295

283296
HttpClient::HttpClient(
284297
const std::shared_ptr<sdk::common::ThreadInstrumentation> &thread_instrumentation)
285-
: multi_handle_(curl_multi_init()),
298+
: multi_handle_(initMultiHandle()),
286299
next_session_id_{0},
287300
max_sessions_per_connection_{8},
288301
background_thread_instrumentation_(thread_instrumentation),
@@ -909,7 +922,7 @@ void HttpClient::resetMultiHandle()
909922
curl_multi_cleanup(multi_handle_);
910923

911924
// Create a another multi handle to continue pending sessions
912-
multi_handle_ = curl_multi_init();
925+
multi_handle_ = initMultiHandle();
913926
}
914927

915928
} // namespace curl

‎ext/src/http/client/curl/http_operation_curl.cc‎

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -467,8 +467,23 @@ HttpOperation::HttpOperation(opentelemetry::ext::http::client::Method method,
467467
for (auto &kv : this->request_headers_)
468468
{
469469
const auto header = std::string(kv.first).append(": ").append(kv.second);
470-
curl_resource_.headers_chunk =
471-
curl_slist_append(curl_resource_.headers_chunk, header.c_str());
470+
471+
// Into a temporary first. curl_slist_append returns null without freeing the list it was
472+
// given, so assigning the result straight back would drop the only pointer to everything
473+
// appended so far, and Setup() would then send the request with none of these headers
474+
// rather than not send it.
475+
curl_slist *appended = curl_slist_append(curl_resource_.headers_chunk, header.c_str());
476+
if (nullptr == appended)
477+
{
478+
curl_slist_free_all(curl_resource_.headers_chunk);
479+
curl_resource_.headers_chunk = nullptr;
480+
last_curl_result_ = CURLE_OUT_OF_MEMORY;
481+
DispatchEvent(opentelemetry::ext::http::client::SessionState::CreateFailed,
482+
curl_easy_strerror(last_curl_result_));
483+
return;
484+
}
485+
486+
curl_resource_.headers_chunk = appended;
472487
}
473488
}
474489

0 commit comments

Comments
 (0)