Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
229 changes: 229 additions & 0 deletions SPECS/opentelemetry-cpp/CVE-2026-44967.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
From 7faf10c8ae527b03ccd0916eb18067e64b2ec94c Mon Sep 17 00:00:00 2001
From: AllSpark <allspark@microsoft.com>
Date: Tue, 16 Jun 2026 11:33:39 +0000
Subject: [PATCH] Fix unbounded OTLP HTTP response reads

Signed-off-by: Azure Linux Security Servicing Account <azurelinux-security@microsoft.com>
Upstream-reference: AI Backport of https://patch-diff.githubusercontent.com/raw/open-telemetry/opentelemetry-cpp/pull/4078.patch
---
CHANGELOG.md | 15 +++
.../ext/http/client/curl/http_operation_curl.h | 16 +++
ext/src/http/client/curl/http_operation_curl.cc | 101 ++++++++++++++++++--
3 files changed, 123 insertions(+), 9 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index dbae8e3..3d5dffd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -101,6 +101,21 @@ Important changes:
[#2457](https://github.com/open-telemetry/opentelemetry-cpp/pull/2457)
* [EXPORTER] Rename populate_otel_scope to without_otel_scope
[#2479](https://github.com/open-telemetry/opentelemetry-cpp/pull/2479)
+
+* [EXPORTER] OTLP HTTP exporters read unbounded HTTP response
+ [#4078](https://github.com/open-telemetry/opentelemetry-cpp/pull/4078)
+
+Security fix:
+
+* [EXPORTER] OTLP HTTP exporters read unbounded HTTP response
+ [#4078](https://github.com/open-telemetry/opentelemetry-cpp/pull/4078)
+
+ * When exporting OTLP HTTP data to a misconfigured or malicious endpoint,
+ the exporter could allocate an arbitrary amount of memory when getting
+ the endpoint HTTP response back.
+ * The size of HTTP responses is now limited to 4MiB by default,
+ following the opentelemetry-proto recommendations.
+ * See CVE-2026-44967
* [EXPORTER SDK] Additional fixes after NOMINMAX removal on Windows
[#2475](https://github.com/open-telemetry/opentelemetry-cpp/pull/2475)
* [EXPORTER] Do not use regex in `CleanUpString` because some implementations of
diff --git a/ext/include/opentelemetry/ext/http/client/curl/http_operation_curl.h b/ext/include/opentelemetry/ext/http/client/curl/http_operation_curl.h
index 587e74a..c99e580 100644
--- a/ext/include/opentelemetry/ext/http/client/curl/http_operation_curl.h
+++ b/ext/include/opentelemetry/ext/http/client/curl/http_operation_curl.h
@@ -42,6 +42,14 @@ const std::chrono::milliseconds default_http_conn_timeout(5000); // ms
const std::string http_status_regexp = "HTTP\\/\\d\\.\\d (\\d+)\\ .*";
const std::string http_header_regexp = "(.*)\\: (.*)\\n*";

+/**
+ * Default max HTTP Response size.
+ * 4MiB
+ * @see
+ * https://github.com/open-telemetry/opentelemetry-proto/blob/main/docs/specification.md#otlphttp-response
+ */
+const size_t kDefaultMaxResponseSize = 4 * 1024 * 1024;
+
class HttpClient;
class Session;

@@ -226,6 +234,12 @@ class HttpOperation
*/
inline const std::vector<uint8_t> &GetRawResponse() const noexcept { return raw_response_; }

+ /**
+ * Limit memory consumption on HTTP response.
+ * Size is @c kDefaultMaxResponseSize by default.
+ */
+ void SetMaxResponseSize(size_t max_size) { max_response_size_ = max_size; }
+
/**
* Release memory allocated for response
*/
@@ -300,6 +314,8 @@ class HttpOperation
std::vector<uint8_t> response_headers_;
std::vector<uint8_t> response_body_;
std::vector<uint8_t> raw_response_;
+ /** Max HTTP response size. */
+ size_t max_response_size_{kDefaultMaxResponseSize};

struct AsyncData
{
diff --git a/ext/src/http/client/curl/http_operation_curl.cc b/ext/src/http/client/curl/http_operation_curl.cc
index f24d3fa..615136c 100644
--- a/ext/src/http/client/curl/http_operation_curl.cc
+++ b/ext/src/http/client/curl/http_operation_curl.cc
@@ -31,8 +31,43 @@ size_t HttpOperation::WriteMemoryCallback(void *contents, size_t size, size_t nm
return 0;
}

- self->raw_response_.insert(self->raw_response_.end(), static_cast<char *>(contents),
- static_cast<char *>(contents) + (size * nmemb));
+ const size_t data_size = size * nmemb;
+
+ // This code is defensive on purpose, to avoid allocation of unbound
+ // amounts of memory, controlled by the (remote) endpoint response.
+
+ // 1: Check data_size did not overflow
+ if (nmemb != 0)
+ {
+ if (data_size / nmemb != size)
+ {
+ // Should be impossible, really:
+ // CURL will report a huge response small chunks at a time,
+ // not in one block, which would be a DOS in CURL already.
+ return 0;
+ }
+ }
+
+ // 2: Check internal integrity
+ if (self->raw_response_.size() > self->max_response_size_)
+ {
+ // Should be impossible,
+ // this means the previous call did exceed the max size already.
+ return 0;
+ }
+
+ // 3: Protect against memory exhaustion caused by the remote endpoint
+ if (data_size > self->max_response_size_ - self->raw_response_.size())
+ {
+ // This one is possible and must be protected against (CVE-2026-44967).
+ // Checks 1 and 2 ensure the math is correct.
+ return 0;
+ }
+
+ const unsigned char *begin = static_cast<unsigned char *>(contents);
+ const unsigned char *end = begin + data_size;
+
+ self->raw_response_.insert(self->raw_response_.end(), begin, end);

if (self->WasAborted())
{
@@ -49,7 +84,7 @@ size_t HttpOperation::WriteMemoryCallback(void *contents, size_t size, size_t nm
self->DispatchEvent(opentelemetry::ext::http::client::SessionState::Sending);
}

- return size * nmemb;
+ return data_size;
}

size_t HttpOperation::WriteVectorHeaderCallback(void *ptr, size_t size, size_t nmemb, void *userp)
@@ -60,8 +95,32 @@ size_t HttpOperation::WriteVectorHeaderCallback(void *ptr, size_t size, size_t n
return 0;
}

- const unsigned char *begin = (unsigned char *)(ptr);
- const unsigned char *end = begin + size * nmemb;
+ const size_t data_size = size * nmemb;
+
+ // See comments in HttpOperation::WriteMemoryCallback().
+
+ if (nmemb != 0)
+ {
+ if (data_size / nmemb != size)
+ {
+ return 0;
+ }
+ }
+
+ // Common limit for header + body
+ if (self->response_headers_.size() + self->response_body_.size() > self->max_response_size_)
+ {
+ return 0;
+ }
+
+ if (data_size >
+ self->max_response_size_ - self->response_headers_.size() - self->response_body_.size())
+ {
+ return 0;
+ }
+
+ const unsigned char *begin = static_cast<unsigned char *>(ptr);
+ const unsigned char *end = begin + data_size;
self->response_headers_.insert(self->response_headers_.end(), begin, end);

if (self->WasAborted())
@@ -79,7 +138,7 @@ size_t HttpOperation::WriteVectorHeaderCallback(void *ptr, size_t size, size_t n
self->DispatchEvent(opentelemetry::ext::http::client::SessionState::Sending);
}

- return size * nmemb;
+ return data_size;
}

size_t HttpOperation::WriteVectorBodyCallback(void *ptr, size_t size, size_t nmemb, void *userp)
@@ -90,8 +149,32 @@ size_t HttpOperation::WriteVectorBodyCallback(void *ptr, size_t size, size_t nme
return 0;
}

- const unsigned char *begin = (unsigned char *)(ptr);
- const unsigned char *end = begin + size * nmemb;
+ const size_t data_size = size * nmemb;
+
+ // See comments in HttpOperation::WriteMemoryCallback().
+
+ if (nmemb != 0)
+ {
+ if (data_size / nmemb != size)
+ {
+ return 0;
+ }
+ }
+
+ // Common limit for header + body
+ if (self->response_headers_.size() + self->response_body_.size() > self->max_response_size_)
+ {
+ return 0;
+ }
+
+ if (data_size >
+ self->max_response_size_ - self->response_headers_.size() - self->response_body_.size())
+ {
+ return 0;
+ }
+
+ const unsigned char *begin = static_cast<unsigned char *>(ptr);
+ const unsigned char *end = begin + data_size;
self->response_body_.insert(self->response_body_.end(), begin, end);

if (self->WasAborted())
@@ -109,7 +192,7 @@ size_t HttpOperation::WriteVectorBodyCallback(void *ptr, size_t size, size_t nme
self->DispatchEvent(opentelemetry::ext::http::client::SessionState::Sending);
}

- return size * nmemb;
+ return data_size;
}

size_t HttpOperation::ReadMemoryCallback(char *buffer, size_t size, size_t nitems, void *userp)
--
2.45.4
6 changes: 5 additions & 1 deletion SPECS/opentelemetry-cpp/opentelemetry-cpp.spec
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@
Summary: The OpenTelemetry C++ Client
Name: opentelemetry-cpp
Version: 1.14.2
Release: 2%{?dist}
Release: 3%{?dist}
License: MIT
Vendor: Microsoft Corporation
Distribution: Azure Linux
URL: https://github.com/open-telemetry/opentelemetry-cpp
Source0: https://github.com/open-telemetry/opentelemetry-cpp/archive/refs/tags/v%{version}.tar.gz#/%{name}-%{version}.tar.gz
Source1: https://github.com/open-telemetry/%{proto_name}/archive/refs/tags/v%{proto_version}.tar.gz#/%{proto_name}-%{proto_version}.tar.gz
Patch0: CVE-2026-44967.patch

BuildRequires: c-ares-devel
BuildRequires: cmake
Expand Down Expand Up @@ -77,6 +78,9 @@ mkdir build && cd build
%{_libdir}/cmake/opentelemetry-cpp/*

%changelog
* Tue Jun 16 2026 Azure Linux Security Servicing Account <azurelinux-security@microsoft.com> - 1.14.2-3
- Patch for CVE-2026-44967

* Thu Jul 25 2024 Devin Anderson <danderson@microsoft.com> - 1.14.2-2
- Bump release to rebuild with latest 'abseil-cpp'.
- Provide explicit fetch for protobuf archive.
Expand Down
Loading