0

I am trying to wrap libcurl as a helper QObject class. Unfortunately I am getting a mysterious segfault which does not happen when the exact same code is placed outside a class.

Sample code:

classless working code

//main.cpp
size_t writeCallback(char* ptr, size_t size, size_t nmemb, void* up)
{
    size_t data_size = size * nmemb;
    QByteArray *data = static_cast<QByteArray*>(up);
    data->append(ptr, data_size);
    return data_size;
}
 
int main(int argc, char *argv[]) {
 
    QCoreApplication a(argc, argv);  
 
    QByteArray buffer;  
    CURL *curl;
    CURLcode res;
 
    curl = curl_easy_init();
    if (curl) {
        curl_easy_setopt(curl, CURLOPT_URL, "http://cnn.com");
        /* example.com is redirected, so we tell libcurl to follow redirection */
        curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &writeCallback);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buffer);
 
        /* 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);
    }
    qDebug() << buffer;
    return a.exec();
}

QObject wrapper which segfaults

//Http.h 
class Http : public QObject
{
    Q_OBJECT
public:
    Http();
    void download();
signals:
    void finished(const QByteArray &buffer);
private:
    size_t writeCallback(char* ptr, size_t size, size_t nmemb, void* up);
};
 
//Http.cpp
Http::Http()
{
 
}
 
void Http::download()
{
    QByteArray buffer;
 
    CURL *curl;
    CURLcode res;
 
    curl = curl_easy_init();
    if (curl) {
        curl_easy_setopt(curl, CURLOPT_URL, "http://cnn.com");
        /* example.com is redirected, so we tell libcurl to follow redirection */
        curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &Http::writeCallback);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buffer);
 
        /* 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);
    }
 
    qDebug() << buffer;
    emit finished(buffer);
}
 
size_t Http::writeCallback(char* ptr, size_t size, size_t nmemb, void* up)
{
    size_t data_size = size * nmemb;
 
    QByteArray *data = static_cast<QByteArray*>(up);
    data->append(ptr, data_size); //<--SEGFAULTS
 
    return data_size;
}
 
//main.cpp
int main(int argc, char *argv[]) {
 
    QCoreApplication a(argc, argv);  
    Http http;
    http.download();
    return a.exec();
}

The line that segfaults is present in Http::writeCallback(char* ptr, size_t size, size_t nmemb, void* up)

valgrind output

    ==11246== Invalid read of size 1
    ==11246==    at 0x4C2D7A2: strlen (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
    ==11246==    by 0x511EA14: QByteArray::append(char const*, int) (in /usr/lib/x86_64-linux-gnu/libQt5Core.so.5.0.1)
    ==11246==    by 0x401B77: Http::writeCallback(char*, unsigned long, unsigned long, void*) (http.cpp:45)
    ==11246==    by 0x4E48717: ??? (in /usr/lib/x86_64-linux-gnu/libcurl.so.4.3.0)
    ==11246==    by 0x4E64A1B: ??? (in /usr/lib/x86_64-linux-gnu/libcurl.so.4.3.0)
    ==11246==    by 0x4E5F8B1: ??? (in /usr/lib/x86_64-linux-gnu/libcurl.so.4.3.0)
    ==11246==    by 0x4E68739: ??? (in /usr/lib/x86_64-linux-gnu/libcurl.so.4.3.0)
    ==11246==    by 0x4E693D4: curl_multi_perform (in /usr/lib/x86_64-linux-gnu/libcurl.so.4.3.0)
    ==11246==    by 0x4E60FDC: curl_easy_perform (in /usr/lib/x86_64-linux-gnu/libcurl.so.4.3.0)

==11246==    by 0x401A45: Http::download() (http.cpp:26)
==11246==    by 0x401838: main (main.cpp:57)
==11246==  Address 0x1 is not stack'd, malloc'd or (recently) free'd
==11246== 
==11246== 
==11246== Process terminating with default action of signal 11 (SIGSEGV)
==11246==  Access not within mapped region at address 0x1
==11246==    at 0x4C2D7A2: strlen (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==11246==    by 0x511EA14: QByteArray::append(char const*, int) (in /usr/lib/x86_64-linux-gnu/libQt5Core.so.5.0.1)
==11246==    by 0x401B77: Http::writeCallback(char*, unsigned long, unsigned long, void*) (http.cpp:45)
==11246==    by 0x4E48717: ??? (in /usr/lib/x86_64-linux-gnu/libcurl.so.4.3.0)
==11246==    by 0x4E64A1B: ??? (in /usr/lib/x86_64-linux-gnu/libcurl.so.4.3.0)
==11246==    by 0x4E5F8B1: ??? (in /usr/lib/x86_64-linux-gnu/libcurl.so.4.3.0)
==11246==    by 0x4E68739: ??? (in /usr/lib/x86_64-linux-gnu/libcurl.so.4.3.0)
==11246==    by 0x4E693D4: curl_multi_perform (in /usr/lib/x86_64-linux-gnu/libcurl.so.4.3.0)
==11246==    by 0x4E60FDC: curl_easy_perform (in /usr/lib/x86_64-linux-gnu/libcurl.so.4.3.0)
==11246==    by 0x401A45: Http::download() (http.cpp:26)

I am completely lost why the code fails.

PS. I am aware this sample code is blocking and I know there's QNetworkAccessManager. I plan to move it to a QThread later on. The reason why I use libcurl is that a webservice I need to hit doesn't play nice with default headers sent by QNAM.

1 Answer 1

2

You can't pass C++ member methods as C callbacks. Make the callback a static method of the class, or a toplevel free function.

Sign up to request clarification or add additional context in comments.

1 Comment

Doh, alright, that explains it. Thanks I wasn't aware of it. I thought that perhaps it had something to do exclusively with Qt.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.