Describe your environment main at 3fb1d31, Ubuntu, gcc 14, libcurl 8.14.1, CMake, -fsanitize=address.
HttpOperation keeps a reference to four of its constructor arguments rather than a copy, in ext/include/opentelemetry/ext/http/client/curl/http_operation_curl.h:
const opentelemetry::ext::http::client::HttpSslOptions &ssl_options_; // :336
const Headers &request_headers_; // :338
const opentelemetry::ext::http::client::Body &request_body_; // :339
const opentelemetry::ext::http::client::Compression &compression_; // :344
Three of those four parameters have a default argument:
const opentelemetry::ext::http::client::Headers &request_headers =
opentelemetry::ext::http::client::Headers(),
const opentelemetry::ext::http::client::Body &request_body =
opentelemetry::ext::http::client::Body(),
const opentelemetry::ext::http::client::Compression &compression =
opentelemetry::ext::http::client::Compression::kNone,
A default argument is a temporary created in the caller and destroyed at the end of the full-expression that contains the call, so a member reference bound to one dangles as soon as the constructor returns. The same happens for any caller that passes a prvalue for one of those parameters rather than a named object.
The header is installed, so this is reachable from outside the repository with nothing unusual: four arguments and the defaults do it.
Steps to reproduce
TEST_F(BasicCurlHttpTests, DefaultArgumentsDangle)
{
http_client::HttpSslOptions no_ssl;
curl::HttpOperation operation(http_client::Method::Get, "http://127.0.0.1:19000/get/", no_ssl,
nullptr);
operation.Send();
}
Built with -fsanitize=address on main at 3fb1d31:
ERROR: AddressSanitizer: stack-use-after-scope on address 0x7f9071000830
SUMMARY: AddressSanitizer: stack-use-after-scope
ext/src/http/client/curl/http_operation_curl.cc:1182 in HttpOperation::Setup()
#0 HttpOperation::Setup() http_operation_curl.cc:1182
#1 HttpOperation::Send() http_operation_curl.cc:1402
:1182 is the read of compression_. The constructor itself is fine because it uses the parameters, which are alive for the length of the call. The dangling read is later, when Setup() reads the member.
What is the expected behavior? Constructing an HttpOperation and using it is defined, however the caller spells the arguments.
What is the actual behavior? The defaults, and any prvalue argument, leave the object holding references to storage that is already gone.
Additional context Nothing in this repository trips it. Session::SendRequest passes members of the Request it owns, and HttpClientSync::Get passes named locals, so all four references outlive the operation in both. The tests pass named locals too. So this is about what the installed header allows rather than about a failure anyone is seeing today.
Two ways out that I can see, and the choice is a design one rather than mine to make. Holding the four by value costs a copy of the headers and the body per request, which is the straightforward fix and matches what retry_policy_ already does. Deleting the three default arguments costs nothing at runtime and closes the shape that invites it, but leaves a prvalue argument dangling just the same. Happy to send either.
Found while writing a test that constructs an HttpOperation directly, on the way through the curl client work in #4391 and #4390.
Describe your environment
mainat 3fb1d31, Ubuntu, gcc 14, libcurl 8.14.1, CMake,-fsanitize=address.HttpOperationkeeps a reference to four of its constructor arguments rather than a copy, inext/include/opentelemetry/ext/http/client/curl/http_operation_curl.h:Three of those four parameters have a default argument:
A default argument is a temporary created in the caller and destroyed at the end of the full-expression that contains the call, so a member reference bound to one dangles as soon as the constructor returns. The same happens for any caller that passes a prvalue for one of those parameters rather than a named object.
The header is installed, so this is reachable from outside the repository with nothing unusual: four arguments and the defaults do it.
Steps to reproduce
Built with
-fsanitize=addressonmainat 3fb1d31::1182is the read ofcompression_. The constructor itself is fine because it uses the parameters, which are alive for the length of the call. The dangling read is later, whenSetup()reads the member.What is the expected behavior? Constructing an
HttpOperationand using it is defined, however the caller spells the arguments.What is the actual behavior? The defaults, and any prvalue argument, leave the object holding references to storage that is already gone.
Additional context Nothing in this repository trips it.
Session::SendRequestpasses members of theRequestit owns, andHttpClientSync::Getpasses named locals, so all four references outlive the operation in both. The tests pass named locals too. So this is about what the installed header allows rather than about a failure anyone is seeing today.Two ways out that I can see, and the choice is a design one rather than mine to make. Holding the four by value costs a copy of the headers and the body per request, which is the straightforward fix and matches what
retry_policy_already does. Deleting the three default arguments costs nothing at runtime and closes the shape that invites it, but leaves a prvalue argument dangling just the same. Happy to send either.Found while writing a test that constructs an
HttpOperationdirectly, on the way through the curl client work in #4391 and #4390.