libcurl: ошибка сегментации curl_easy_perform при использовании WriteMemoryCallback

Я делаю запрос json-rpc, используя C и libcurl, успешно используя следующий код

#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;
}

Однако, когда я пытаюсь интегрировать getinmemory.c пример libcurl (чтобы получить результат в переменной вместо стандартного вывода), в результате получился следующий код (где я сделал отступ для моих правок, которые в основном добавляют вышеуказанный код)

#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;
}

Я получаю ошибку сегментации в

res = curl_easy_perform(curl_handle);

что происходит при вызове realloc внутри writeMemoryCallback.

Кроме того, я должен упомянуть, что заголовки, напечатанные на stdout из приведенного выше примера, здесь не печатаются. Единственное, что напечатано, это

$ ./dafuq
THIS IS PRINTED
Segmentation fault

Это дамп gdb

(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:

$ 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)

Есть предположения?

PS: Я также пробовал использовать поток, такой как код sth, из здесь, придумать точно такое же поведение.


person stelios    schedule 28.10.2014    source источник
comment
Сначала проверьте возвращаемое значение каждой функции, затем попробуйте заменить вызовы malloc / realloc.   -  person 2501    schedule 28.10.2014
comment
Пример curl.haxx.se/libcurl/c/postinmemory.html может будь еще ближе к тому, чего хочешь ...   -  person Daniel Stenberg    schedule 28.10.2014
comment
Хорошая точка зрения. Все функции libcurl возвращают 0. Замена realloc на malloc дает (на gdb): Программа получила сигнал SIGSEGV, ошибка сегментации. __memcpy_sse2_unaligned () в ../sysdeps/x86_64/multiarch/memcpy-sse2-unaligned.S:35 35 ../sysdeps/x86_64/multiarch/memcpy-sse2-unaligned.S: Нет такого файла или каталога.   -  person stelios    schedule 28.10.2014
comment
@Daniel Да, на самом деле это базовый код, который я использовал   -  person stelios    schedule 28.10.2014
comment
Если кто-то все еще не может знать, почему вызван segfault, я предлагаю вам отладить свою программу с помощью gdb и backtrace ошибки, чтобы узнать, какая строка вызывает segfault, в большинстве моих случаев эти ошибки были действительно очень простыми, вот как отлаживать и отслеживать ошибку   -  person Accountant م    schedule 21.07.2019


Ответы (1)


Глупая ошибка, я переопределил CURLOPT_WRITEDATA как chunk, так и stdout. Таким образом удаляя

curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, stdout);

делает свою работу ...

PS: Ошибка сегментации в curl_easy_perform также, похоже, происходит, когда данные аутентификации неверны (например, истек срок действия сеанса)

person stelios    schedule 03.11.2014