AppleDash
AppleDash

Reputation: 1714

Why is libcurl segfaulting on this call to curl_easy_perform()?

I'm getting a segfault when using libCURL in my HTTP flooder that I wrote for load-testing my site.

Here is the relevant code: https://gist.github.com/AppleDash/a26e0ce0b138cd9eacd2 (A bit large to paste here.)

Here's a link to the line it is segfaulting on: https://gist.github.com/AppleDash/a26e0ce0b138cd9eacd2#file-httpflood-improved-c-L57

And here is a backtrace of the segfault:

#0  0x00007ffff760d65b in fwrite () from /usr/lib/libc.so.6
#1  0x00007ffff79656d8 in ?? () from /usr/lib/libcurl.so.4
#2  0x00007ffff797a76b in ?? () from /usr/lib/libcurl.so.4
#3  0x00007ffff7984349 in ?? () from /usr/lib/libcurl.so.4
#4  0x00007ffff7984b11 in curl_multi_perform () from /usr/lib/libcurl.so.4
#5  0x00007ffff797b977 in curl_easy_perform () from /usr/lib/libcurl.so.4
#6  0x0000000000400f42 in flood (structPointer=0x7fffffffe060) at httpflood.c:57
#7  0x00007ffff7bc5124 in start_thread () from /usr/lib/libpthread.so.0
#8  0x00007ffff768b4bd in clone () from /usr/lib/libc.so.6

I don't see why this call would cause a segfault. Any ideas?

I know you're meant to only provide a small sample of relevant code, but here I am providing the whole thing due to the fact that I feel like context is needed here. (The fact it is being run from many threads and such.)

Upvotes: 0

Views: 2722

Answers (1)

Adam Rosenfield
Adam Rosenfield

Reputation: 400274

This is your problem:

for (i = 0; i < threadnum; i++) {
    struct flood_data ddosData;
    memset(&ddosData, 0, sizeof(struct flood_data));
    ddosData.url = url;
    ddosData.proxy = getProxy();
    pthread_create(&threads[i], NULL, flood, (void *)&ddosData);
}

You're allocating a single struct flood_data instance on the stack and passing that to all of the new threads simultaneously. Each time you iterate through the loop, you overwrite the same instance at the same time that threads spawned from earlier iterations might be trying to read from it. Major undefined behavior.

The proper way to do this is to dynamically allocate a separate instance for each thread:

for (i = 0; i < threadnum; i++) {
    struct flood_data *ddosData = calloc(1, sizeof(*ddosData));
    ddosData->url = url;
    ddosData->proxy = getProxy();
    pthread_create(&threads[i], NULL, flood, ddosData);
}

...

void *flood(void *structPointer) {
    struct flood_data *data = structPointer;
    char *bootable = data->url;
    char *proxy = data->proxy;
    free(data);
    ...
}

As pointed out in the comments, you also need to check your system calls for failure. You should validate that all of your calls to fopen() are succeeding, as you could very well be hitting the maximum number of file descriptors open in your process. Rather than opening up a file to /dev/null, why don't you just set a no-op write function with the CURLOPT_WRITEFUNCTION option?

static size_t noop_write_callback(char *ptr, size_t size, size_t nmemb, void *userdata)
{
    // Do nothing
    return size * nmemb;
}

...

curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &noop_write_callback);
// No need to call fopen("/dev/null") or set CURLOPT_WRITEDATA now

Upvotes: 5

Related Questions