curl_slist_free_all() causes segfault on GKE w/ Debian 8.7

6/3/2017

I've wrapped libcurl for a C++ daemon I've deployed on Google Container Engine. Everything works splendid except for one small problem. It segfaults whenever I call curl_slist_free_all(). It doesn't happen on the Ubuntu 14s or 16s nor macOS. It only occurs in the GKE Docker environment with Debian 8.7. This is literally my only bug and it's been bothering me for weeks.

I've wrapped resource handles with RAII style containers for exception safety (yeah, yeah... I use exceptions) and leak protection. The easy_init and easy_cleanup are in the CurlSession constructors and destructors. The global_init & cleanup are in the HTTP constructors and destructors.

I validated that there are no double-free situations, spelunked the libcurl code, and still cannot fathom why this is happening only on this OS env. I managed to attach a debugger and isolated it to the single slist cleanup call.

The only way I can get my code to work is to leak in every other env, which isn't a deal breaker, I'd just rather my memory profiler gave me a clean bill of health.

Any insight or shared pain appreciated.

My header slist wrapper:

HTTP::Headers::Headers() : slist{nullptr} {}

HTTP::Headers::Headers(const HeaderKeyValues &headers)
    : slist{nullptr}
{
    for (const auto& header : headers) add(header.first, header.second);
}

HTTP::Headers::~Headers() {
    curl_slist_free_all(slist); // <- seems to crash on Google's Debian image
    slist = nullptr;
};

void HTTP::Headers::add(const std::string& key, const std::string& value) 
{
    std::ostringstream os;
    os << key << ": " << value;

    slist = curl_slist_append(slist, os.str().c_str());
    if (!slist) {
        LOG(fatal) << "Failed appending to header list";
        throw std::runtime_error{"Failed appending to header list"};
    }
}

Subset of the dispatcher:

HTTP::Response HTTP::dispatch(const Request& req) const {
    CurlSession session;
    const auto handle = session.handle;

    Headers headerList{req.headers};
    if (req.chunked)
        headerList.add("Transfer-Encoding", "chunked");

    // more ... //

    if (headerList.notEmpty())
        curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headerList.slist);

    // perform the actual request
    CURLcode result = curl_easy_perform(handle);
-- pestilence669
c++
docker
kubernetes
libcurl
segmentation-fault

2 Answers

12/25/2017

I suspect this was some sort of subtle incompatibility between the Docker build image and the Docker deploy image that only manifested when running on GKE.

-- pestilence669
Source: StackOverflow

4/14/2019

In my case it was like this

if ( strcmp(req->headers, ""){
    curl_slist_free_all(list);// segfault
}

if ( strcmp(req->headers, ""){
   // no segfault
}

and req->headers was NULL, so whenever I remove the curl_slist_free_all line, the compiler does not produce binary code for this IF statement at all as an optimization step, so the strcmp is not called which was what actually causing the segfault not curl_slist_free_all(list);

-- Accountant م
Source: StackOverflow