#642·proxygen

HTTPClient POST body helper can dereference a released unique_ptr

Author: mavamCreated Sep 17, 2026Updated Sep 17, 2026

Problem

proxygen::coro::HTTPClient::post() can dereference a null pointer while constructing a non-empty request body. The internal stringToIOBuf() helper evaluates accesses to a std::unique_ptr and release() in the same argument list:

cpp
auto strPtr = std::make_unique<std::string>(std::move(str).value());
strBuf = folly::IOBuf::takeOwnership(
    strPtr->data(),
    strPtr->size(),
    [](void*, void* data) { delete static_cast<std::string*>(data); },
    strPtr.release());

C++ does not specify the order of evaluation of these arguments. If the last argument is evaluated first, release() clears strPtr, and evaluating either of the first two arguments then dereferences null. This is not a coroutine lifetime issue: the problem is within this single call expression.

The code is still present on upstream main at 6bd0ee43ad0709df359f2bcdb9137cc63b0dc43a.

Trigger and observations

An HTTP/1.1 POST to a local server, with a non-empty body, enters this path:

cpp
co_await proxygen::coro::HTTPClient::post(
    evb, "http://127.0.0.1:8080/anything", "{\"a\":1}",
    std::chrono::seconds{5}, false,
    {{"content-type", "application/json"}});

We observed a SIGSEGV on x86-64 Linux with a dependency based on release v2026.06.29.00; the corresponding test passes on arm64 macOS. The crash's async stack goes through HTTPClient::post(). The innermost CI frames are not symbolized, so this report distinguishes the definite source-level defect from full confirmation of that particular crash location.

GET and empty-body POST requests skip the offending block. A caller can avoid it by constructing an HTTPFixedSource with an owned IOBuf and using the HTTPClient::request() overload that accepts an HTTPSourceHolder.

Suggested fix

Use folly::IOBuf::fromString(std::move(*str)), or evaluate strPtr->data() and strPtr->size() into locals before the takeOwnership() call. In either case, no argument should dereference the pointer that another argument releases.