【问题标题】:libcurl: Segmentation fault on curl_easy_perform when WriteMemoryCallback is usedlibcurl:使用 WriteMemoryCallback 时 curl_easy_perform 的分段错误
【发布时间】:2014-12-23 22:58:45
【问题描述】:

我使用 C 和 libcurl 成功使用以下代码发出 json-rpc 请求

#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 示例(以便在变量而不是 stdout 中获取输出)时,会得到以下代码(我有 缩进我的编辑,基本上是添加上面的代码)

#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

此外,我应该提一下,上面示例中在标准输出上打印的标头并未在此处打印。唯一打印出来的是

$ ./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:我也尝试过使用类似 here 的代码的流,得到了完全相同的行为。

【问题讨论】:

  • 首先检查每个函数的返回值,然后尝试替换malloc/realloc调用。
  • curl.haxx.se/libcurl/c/postinmemory.html 示例可能更接近您想要的...
  • 好点。所有 libcurl 函数都返回 0 将 ''realloc'' 替换为 ''malloc'' 给出(在 gdb 中):程序收到信号 SIGSEGV,分段错误。 __memcpy_sse2_unaligned () at ../sysdeps/x86_64/multiarch/memcpy-sse2-unaligned.S:35 35 ../sysdeps/x86_64/multiarch/memcpy-sse2-unaligned.S: 没有这样的文件或目录。
  • @Daniel 是的,实际上这是我使用的基本代码
  • 如果有人仍然不知道为什么会导致段错误,我建议你用 gdb 调试你的程序,并将错误 backtrace 到知道哪一行导致了段错误,我的大多数情况下,确实是非常简单的错误导致了这些错误,here is how to debug and track the error

标签: c segmentation-fault libcurl


【解决方案1】:

愚蠢的错误,我已经用chunkstdout 覆盖了CURLOPT_WRITEDATA。 从而删除

curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, stdout);

做的工作...

PS:curl_easy_perform 中的分段错误似乎也会在身份验证数据为假时发生(例如:会话过期)

【讨论】:

    猜你喜欢
    • 2023-03-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-17
    • 2011-06-12
    • 1970-01-01
    • 2021-09-03
    相关资源
    最近更新 更多