Libcurl: segmentation fault in curl_easy_perform when using WriteMemoryCallback
I am making a json-rpc request using C and libcurl using the following code successfully
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <curl/curl.h>
void post_rpc(CURL *curl_handle)
{
CURLcode res;
char JSONRPC_BASE_URL[] = "https://api.betfair.com/exchange/betting/json-rpc/v1";
char rpc_request[]="{\"jsonrpc\": \"2.0\", \"method\": \"SportsAPING/v1.0/listEventTypes\", \"params\": {\"filter\":{ }}, \"id\": 1}";
char session_token[]= "MY_ACTIVE_SESSION_STRING";
char session_header[100+sizeof(session_token)];
strcpy(session_header, "X-Authentication:");
strcat(session_header, session_token);
struct curl_slist * headers = NULL;
headers = curl_slist_append(headers, "X-Application: MY_API_KEY");
headers = curl_slist_append(headers, session_header);
headers = curl_slist_append(headers, "content-type : application/json");
/* init the curl session */
curl_handle = curl_easy_init();
/* HEADERS */
curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, headers);
/* POST FIELD : Json-rpc request */
curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDS, rpc_request);
curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDSIZE, (long)strlen(rpc_request));
/* stdout the header sent */
curl_easy_setopt(curl_handle, CURLOPT_WRITEHEADER, stdout);
curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, stdout);
/* specify URL to get */
curl_easy_setopt(curl_handle, CURLOPT_URL, JSONRPC_BASE_URL);
/* get it! */
res = curl_easy_perform(curl_handle);
/* check for errors */
if(res != CURLE_OK) {
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));
}
}
int main(){
int res;
CURL *curl_handle;
curl_global_init(CURL_GLOBAL_DEFAULT);
post_rpc(curl_handle);
curl_global_cleanup();
return 0;
}
However, when I try to integrate the getinmemory.c example libcurl (to get the result into a variable instead of stdout), thus the following code appears (where I have indented my changes that basically add the above code)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <curl/curl.h>
struct MemoryStruct {
char *memory;
size_t size;
};
static size_t
WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp)
{
size_t realsize = size * nmemb;
struct MemoryStruct *mem = (struct MemoryStruct *)userp;
mem->memory = realloc(mem->memory, mem->size + realsize + 1);
if(mem->memory == NULL) {
/* out of memory! */
printf("not enough memory (realloc returned NULL)\n");
return 0;
}
memcpy(&(mem->memory[mem->size]), contents, realsize);
mem->size += realsize;
mem->memory[mem->size] = 0;
return realsize;
}
int main(void)
{
CURL *curl_handle;
CURLcode res;
struct MemoryStruct chunk;
chunk.memory = malloc(1); /* will be grown as needed by the realloc above */
chunk.size = 0; /* no data at this point */
curl_global_init(CURL_GLOBAL_ALL);
/* init the curl session */
curl_handle = curl_easy_init();
char JSONRPC_BASE_URL[] = "https://api.betfair.com/exchange/betting/json-rpc/v1";
char rpc_request[]="{\"jsonrpc\": \"2.0\", \"method\": \"SportsAPING/v1.0/listEventTypes\", \"params\": {\"filter\":{ }}, \"id\": 1}";
char session_token[]= "MY_ACTIVE_SESSION_STRING";
char session_header[100+sizeof(session_token)];
strcpy(session_header, "X-Authentication:");
strcat(session_header, session_token);
struct curl_slist * headers = NULL;
headers = curl_slist_append(headers, "X-Application: MY_API_KEY");
headers = curl_slist_append(headers, session_header);
headers = curl_slist_append(headers, "content-type : application/json");
/* HEADERS */
curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, headers);
/* POST FIELD : Json-rpc request */
curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDS, rpc_request);
curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDSIZE, (long)strlen(rpc_request));
/* stdout the header sent */
curl_easy_setopt(curl_handle, CURLOPT_WRITEHEADER, stdout);
curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, stdout);
/* specify URL to get */
curl_easy_setopt(curl_handle, CURLOPT_URL, JSONRPC_BASE_URL);
/* send all data to this function */
curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
/* we pass our 'chunk' struct to the callback function */
curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&chunk);
/* some servers don't like requests that are made without a user-agent
field, so we provide one */
curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
printf("THIS IS PRINTED\n");
fflush(stdout);
/* get it! */
res = curl_easy_perform(curl_handle); //LINE 114: SEGFAULT
printf("THIS IS NOT...\n");
fflush(stdout);
/* check for errors */
if(res != CURLE_OK) {
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));
}
else {
/*
* Now, our chunk.memory points to a memory block that is chunk.size
* bytes big and contains the remote file.
*
* Do something nice with it!
*/
printf("%lu bytes retrieved\n", (long)chunk.size);
printf("Chunk:%s\n", chunk.memory);
}
/* cleanup curl stuff */
curl_easy_cleanup(curl_handle);
if(chunk.memory)
free(chunk.memory);
/* we're done with libcurl, so clean it up */
curl_global_cleanup();
return 0;
}
I am getting segmentation fault in
res = curl_easy_perform(curl_handle);
which happens in the realloc
call inside writeMemoryCallback
.
Also, I should mention that headers printed to stdout from the example above are not printed here. The only thing printed
$ ./dafuq
THIS IS PRINTED
Segmentation fault
This is a gdb dump
(gdb) break 114
Breakpoint 1 at 0x40116d: file getinmemory.c, line 114.
(gdb) break 115
Breakpoint 2 at 0x40117c: file getinmemory.c, line 115.
(gdb) run
Starting program: dafuq
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
THIS IS PRINTED
Breakpoint 1, main () at getinmemory.c:114
114 res = curl_easy_perform(curl_handle);
(gdb) step
[New Thread 0x7ffff341e700 (LWP 6774)]
[Thread 0x7ffff341e700 (LWP 6774) exited]
Program received signal SIGSEGV, Segmentation fault.
__GI___libc_realloc (oldmem=0xfbad2a84, bytes=140737354092562) at malloc.c:2977
2977 malloc.c: No such file or directory.
(gdb)
Valgrind dump:
$ valgrind ./dafuq --tool memcheck
==7002== Memcheck, a memory error detector
==7002== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==7002== Using Valgrind-3.10.0 and LibVEX; rerun with -h for copyright info
==7002== Command: ./dafuq --tool memcheck
==7002==
THIS IS PRINTED
==7002== Invalid free() / delete / delete[] / realloc()
==7002== at 0x4C2AF2E: realloc (vg_replace_malloc.c:692)
==7002== by 0x400D27: WriteMemoryCallback (getinmemory.c:44)
==7002== by 0x4E4BD89: ??? (in /usr/lib/x86_64-linux-gnu/libcurl.so.4.3.0)
==7002== by 0x4E4A353: ??? (in /usr/lib/x86_64-linux-gnu/libcurl.so.4.3.0)
==7002== by 0x4E605AF: ??? (in /usr/lib/x86_64-linux-gnu/libcurl.so.4.3.0)
==7002== by 0x4E6ACE8: ??? (in /usr/lib/x86_64-linux-gnu/libcurl.so.4.3.0)
==7002== by 0x4E6B560: curl_multi_perform (in /usr/lib/x86_64-linux-gnu/libcurl.so.4.3.0)
==7002== by 0x4E6215A: curl_easy_perform (in /usr/lib/x86_64-linux-gnu/libcurl.so.4.3.0)
==7002== by 0x401178: main (getinmemory.c:114)
==7002== Address 0xfbad2a84 is not stack'd, malloc'd or (recently) free'd
==7002==
not enough memory (realloc returned NULL)
THIS IS NOT...
curl_easy_perform() failed: Failed writing received data to disk/application
==7002==
==7002== HEAP SUMMARY:
==7002== in use at exit: 238 bytes in 8 blocks
==7002== total heap usage: 7,171 allocs, 7,163 frees, 67,842,654 bytes allocated
==7002==
==7002== LEAK SUMMARY:
==7002== definitely lost: 16 bytes in 1 blocks
==7002== indirectly lost: 158 bytes in 5 blocks
==7002== possibly lost: 0 bytes in 0 blocks
==7002== still reachable: 64 bytes in 2 blocks
==7002== suppressed: 0 bytes in 0 blocks
==7002== Rerun with --leak-check=full to see details of leaked memory
==7002==
==7002== For counts of detected and suppressed errors, rerun with: -v
==7002== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
Any thoughts?
PS: I've also tried using a stream like the sth code from here , arriving at the same behavior.
source to share
Silly mistake, I am overriding CURLOPT_WRITEDATA
both with chunk
and with stdout
. So removing
curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, stdout);
doing the job ...
PS: Segmentation fault in curl_easy_perform
also occurs when authentication data is false (ex: expired session)
source to share