0

When running my code (pertinent sections pasted below), I periodically get the following error:

program(34010,0x70000e58b000) malloc: *** error for object 0x7fc43d93fcf0: pointer being freed was not allocated set a breakpoint in malloc_error_break to debug Signal: SIGABRT (signal SIGABRT)

I am running multi-threaded C++ code on a Macbook (OS-10.13) wherein different threads make use of the code in question simultaneously. To my knowledge, libcurl is indeed thread safe as long as I do not utilize the same "curl handle" (which I understand to be an instance of "CURL" aka "CURL *curl = curl_easy_init();") in two different threads at the same time. In my case, since each thread calls the function separately and initializes a new instance of a CURL object, I should be "safe", right? Hopefully there is something obvious that I'm missing that is causing me (or lib curl in this case) to attempt to free memory that has already been freed. If there is any more information I should have included (below) please don't hesitate to let me know.

The function that seg faults is

string http_lib::make_get_request(string url)

on the line that reads

curl_easy_cleanup(curl);

and sometimes (less often) on the line that reads

res = curl_easy_perform(curl);

Below is what I think would be the pertinent sections of my code:

size_t http_lib::CurlWrite_CallbackFunc_StdString(void *contents, size_t size, size_t nmemb, std::string *s)
{
    size_t newLength = size*nmemb;
    size_t oldLength = s->size();
    try
    {
        s->resize(oldLength + newLength);
    }
    catch(std::bad_alloc &e)
    {
        //handle memory problem
        return 0;
    }

    std::copy((char*)contents,(char*)contents+newLength,s->begin()+oldLength);
    return size*nmemb;
}

string http_lib::make_post_request(string url, vector<string> headers, string post_params) {
    CURL *curl;
    CURLcode res;

    curl = curl_easy_init();
    string s;
    if(curl)
    {
        struct curl_slist *chunk = NULL;

        for(int i=0; i<headers.size(); i++){
            /* Add a custom header */
            chunk = curl_slist_append(chunk, headers[i].c_str());
        }

        /* set our custom set of headers */
        res = curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk);
        curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_params.c_str());
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); //only for https
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); //only for https
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWrite_CallbackFunc_StdString);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s);
        if(networking_debug){
            curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); //verbose output
        }

        /* Perform the request, res will get the return code */
        res = curl_easy_perform(curl);

        /* Check for errors */
        if(res != CURLE_OK)
        {
            fprintf(stderr, "curl_easy_perform() failed: %s\n",
                    curl_easy_strerror(res));
        }

        /* always cleanup */
        curl_easy_cleanup(curl);
    }

    // Debug output
    if (networking_debug){
        cout<<"Response: " << s <<endl;
    }

    return s;
}

string http_lib::make_get_request(string url) {
    //SslCurlWrapper sslObject;
    CURL *curl;
    CURLcode res;

    curl = curl_easy_init();
    string s;
    if (curl) {
        curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
        //tell libcurl to follow redirection
        curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); //only for https
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); //only for https
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWrite_CallbackFunc_StdString);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s);
        if(networking_debug){
            curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); //verbose output
        }

        /* Perform the request, res will get the return code */
        res = curl_easy_perform(curl);
        /* Check for errors */
        if (res != CURLE_OK)
            fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));

        /* always cleanup */
        curl_easy_cleanup(curl);
    }

    if (networking_debug){
        cout << "Response: " << s << endl;
    }

    return s;
}

In main() I have

int main(int argc, char *argv[]){
    // Initialize http_lib (curl)
    curl_global_init(CURL_GLOBAL_DEFAULT);

    ... spin up 10 or so threads that make get/post requests to https site (some requests utilize the make_post_request() function and others utilize make_get_requet() function). 
}
Xandrix
  • 494
  • 5
  • 15
  • 1
    Instead of the explicit resize and copy for the string in the callback, why not [*append*](http://en.cppreference.com/w/cpp/string/basic_string/append) the received string? – Some programmer dude Nov 29 '17 at 03:24
  • And what is `http_lib`? Is it a namespace? Is it a class? If it's a class, is `CurlWrite_CallbackFunc_StdString` a static member function? – Some programmer dude Nov 29 '17 at 03:25
  • http_lib is just the name I gave the hpp/cpp file that the functions are in. It is not a class. Just a container/namespace for functions related to network calls in my program. As for why I didn't append, I don't have a good reason. – Xandrix Nov 29 '17 at 03:29
  • It's just a *feeling*, but if you try to replace that code with just `s->append((char *) contents, nmemb);` does it make thing better? – Some programmer dude Nov 29 '17 at 03:36
  • Your callback function has the wrong parameter types. – Galik Nov 29 '17 at 03:45
  • Some programmer dude, I changed the contents to be "s->append((char *) contents, nmemb);return size*nmemb;" Same issue, unfortunately. Galik, OK, wrong parameters! Could you please show me what it/they should be (or a link that would show that)!? Thank you so much for your help folks. Also, here (https://imgur.com/a/FweBu) is a link to an image that shows the call stack on fail. – Xandrix Nov 29 '17 at 03:53
  • I think that @Galik refers to the last argument, which should be an `void*`. See https://curl.haxx.se/libcurl/c/CURLOPT_WRITEFUNCTION.html. Also, if the crash is not consistently in the same place then it might be a multi-threading issue? – Some programmer dude Nov 29 '17 at 04:01
  • This post (https://stackoverflow.com/questions/2329571/c-libcurl-get-output-into-a-string) explains what I was/am doing. If you use curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s);, you can specify the final var and use it. I don't believe my issue lies there. Though willing to be wrong, as always. :-) – Xandrix Nov 29 '17 at 04:10

1 Answers1

0

CMAKE doesn't/didn't seem to want to use anything other than CURL_ROOT_DIR of "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.13.sdk/usr/include" for libcurl (aka curl).

Thus it was using the curl lib that mac (and/or Xcode) ships with. I haven't figured out what version that is, but I can say that not using it and instead using CURL version 7.57 is what fixed my issue.

I used "brew" package manager to

brew install curl

Doing so created /usr/local/Cellar/curl/7.57.0 directory and put all libs/includes in there.

Then I added

 -I/usr/local/Cellar/curl/7.57.0/include -L/usr/local/Cellar/curl/7.57.0/lib

to my CMAKE CMAKE_CXX_FLAGS.

TLDR; Solution was to ensure I was using the newest version of the curl lib. Now that I am, no problem.

Xandrix
  • 494
  • 5
  • 15