首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >为什么curl_easy_perform SEGFAULT在我的统一测试中,而不是在常规程序中?

为什么curl_easy_perform SEGFAULT在我的统一测试中,而不是在常规程序中?
EN

Stack Overflow用户
提问于 2022-10-04 15:57:46
回答 1查看 104关注 0票数 1

我正在制作一个小型库,使从web中获取数据对于我的程序的其余部分来说很简单。

图书馆request.c有6个功能,5个“公共”和1个“私有”。

  1. WriteMemoryCallback (私有,从[医]利伯尔博士复制)
  2. request_init
  3. request_set_headers
  4. request_set_url
  5. request_fetch
  6. request_cleanup

我使用统一测试框架按照这个顺序测试所有“公共”函数。这会导致test_request_fetch上的分段错误。

但是,如果我制作了一个小程序,以相同的顺序调用相同的函数,它就会工作得很好。

所以我的测试有什么问题?我刚开始使用C。我试过使用gdb并瞄准val差生,但即使有了这些工具,我也不知道如何调试它。

编辑:在request.c文件中,request_set_headers函数、注释掉curl_easy_setopt和没有标题设置的测试将删除SEGFAULT,并且100%的测试通过。我不知道这是为什么。

请让我知道,如果我应该添加任何信息或澄清的东西。

编辑:我试着按照Andreas的建议做一个最小的可重复示例,但是有很多文件。我做了一个git来简化代码的运行:

项目文件夹设置

代码语言:javascript
运行
复制
requestlib/
    src/
        request.c
        request.h
    tests/
        includes/ (unity v2.5.2 is here)
        test_request.c
    try/
        simpletest.c
        compile-simpletest.sh
    ex_bin/
        compile.sh
        test.sh
    CMakeLists.txt

request.c

代码语言:javascript
运行
复制
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <curl/curl.h>

#include "request.h"


MemoryStruct chunk = {
    .memory = NULL,
    .size = 0
};
CURL *curl_handle = NULL;



static size_t
WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp)
{
  size_t realsize = size * nmemb;
  MemoryStruct *mem = (MemoryStruct *)userp;
 
  char *ptr = realloc(mem->memory, mem->size + realsize + 1);
  if(!ptr) {
    /* out of memory! */
    printf("not enough memory (realloc returned NULL)\n");
    return 0;
  }

  mem->memory = ptr;
  memcpy(&(mem->memory[mem->size]), contents, realsize);
  mem->size += realsize;
  mem->memory[mem->size] = 0;
 
  return realsize;
}


int request_fetch(MemoryStruct *putdatahere) {
    if (!curl_handle) {
        return 100;
    }
    int rc = 0;
    putdatahere = &chunk;

    if (chunk.memory != NULL) {
        free(chunk.memory);
    }
    chunk.memory = malloc(1);
    chunk.size = 0;

    
    rc += curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&chunk);
    rc += curl_easy_perform(curl_handle);
    return rc;
}


int request_init(void) {
    static int function_runs = 0;
    if (function_runs != 0) {
        return 100;
    }
    function_runs += 1;

    int rc = 0;

    rc += curl_global_init(CURL_GLOBAL_ALL);
    curl_handle = curl_easy_init();

    rc += curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L);
    rc += curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
    // rc += curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&chunk);
    rc += curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");

    return rc;
}


int request_set_headers(char **headers, int num) {
    if (!curl_handle) {
        return 100;
    }

    struct curl_slist *list = NULL;
    int rc = 0;
    char *s;

    for (int a = 0; a < num; a = a + 1) {
        s = headers[a];
        // printf("header set: %s\n", s);
        list = curl_slist_append(list, s);
    }
    
    /// REMOVING THIS LINE TAKES AWAY SEGFAULT ///
    rc = curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, list);
    curl_slist_free_all(list);

    return rc;
}


int request_set_url(char *url) {
    if (!curl_handle) {
        return 100;
    }

    return curl_easy_setopt(curl_handle, CURLOPT_URL, url);
}


int request_cleanup(void) {
    static int function_runs = 0;
    if (function_runs != 0) {
        return 100;
    }
    function_runs += 1;

    if (chunk.memory != NULL) {
        free(chunk.memory);
        chunk.memory = NULL;
        chunk.size = 0;
    }

    curl_easy_cleanup(curl_handle);
    curl_handle = NULL;
    curl_global_cleanup();

    return 0;
}

request.h

代码语言:javascript
运行
复制
#ifndef REQUEST_H
#define REQUEST_H

#include <stdlib.h>
#include <curl/curl.h>

typedef struct MemoryStruct {
    char *memory;
    size_t size;
} MemoryStruct;

extern MemoryStruct chunk;
extern CURL *curl_handle;
extern CURLcode res;

// static size_t
// WriteMemoryCallback(void *, size_t, size_t, void *);
int request_init(void);
int request_fetch(struct MemoryStruct *putdatahere);
int request_set_headers(char **, int);
int request_set_url(char *);
int request_cleanup(void);

#endif  // REQUEST_H

test_request.c

代码语言:javascript
运行
复制
#include <string.h>
#include <stdlib.h>
#include <curl/curl.h>

#include "../tests/includes/ex_unity/src/unity.h"
#include "../src/request.h"

void setUp(void) {
    // we don't need any setup
}

void tearDown(void) {
    // don't need any teardown
}


void test_request_init(void) {
    char *failmsg = "request_init SHOULD return 0 AND create a curl_handle that is NOT NULL, but it didn't.";
    int cond;

    int rc = request_init();
    cond = (rc == 0 && curl_handle != NULL);
    TEST_ASSERT_MESSAGE(cond, failmsg);
    

    failmsg = "On second call, request_init SHOULD return 100, but it didn't.";
    rc = request_init();
    cond = (rc == 100);
    TEST_ASSERT_MESSAGE(cond, failmsg);
}


void test_request_set_headers(void) {
    char *failmsg = "request_setopt SHOULD return 0, but did not.";
    int cond;

    char *headers[] = {"Content-Type: multipart/mixed"};
    int rc = request_set_headers(headers, 1);
    cond = (rc == 0);
    TEST_ASSERT_MESSAGE(cond, failmsg);


    // failmsg = "request_set_headers SHOULD return 100 when curl_handle is NULL, but did not.";
    // CURL *tmpbak = curl_handle;
    // curl_handle = NULL;

    // rc = request_set_headers(headers, 1);
    // curl_handle = tmpbak;

    // cond = (rc == 100);
    // TEST_ASSERT_MESSAGE(cond, failmsg);
}


void test_request_set_url(void) {
    char *failmsg = "request_set_url SHOULD return 0, but did not.";
    int cond;
    char *url = "https://raw.githubusercontent.com/undoingtech/Sticky-Steps/main/README.md";
    
    int rc = request_set_url(url);
    cond = (rc == 0);
    TEST_ASSERT_MESSAGE(cond, failmsg);


    failmsg = "request_set_url SHOULD return 100 when curl_handle is NULL, but did not.";
    CURL *tmpbak = curl_handle;
    curl_handle = NULL;

    rc = request_set_url(url);
    curl_handle = tmpbak;

    cond = (rc == 100);
    TEST_ASSERT_MESSAGE(cond, failmsg);
}


void test_request_fetch(void) {
    char *failmsg = "request_fetch SHOULD return 100 when curl_handle is NULL, but did not.";
    CURL *tmpbak = curl_handle;
    curl_handle = NULL;

    MemoryStruct *putdatahere = NULL;
    int rc = request_fetch(putdatahere);
    curl_handle = tmpbak;

    int cond = (rc == 100);  
    TEST_ASSERT_MESSAGE(cond, failmsg);


    /// THIS TEST CAUSES THE SEGFAULT ///
    failmsg = "request_fetch SHOULD return 0 AND chunk size SHOULD be greater than 0, but did not.";
    rc = request_fetch(putdatahere);
    cond = (rc == 0); 
    TEST_ASSERT_MESSAGE(cond, failmsg);
}


void test_request_cleanup(void) {
    char *failmsg = "request_cleanup SHOULD return 0 AND set curl_handle to NULL, but it didn't.";
    int cond;

    int rc = request_cleanup();
    cond = (rc == 0 && curl_handle == NULL);
    TEST_ASSERT_MESSAGE(cond, failmsg);


    failmsg = "On second call, request_cleanup SHOULD return 100, but it didn't.";
    rc = request_cleanup();
    cond = (rc == 100);
    TEST_ASSERT_MESSAGE(cond, failmsg);
}

// expect 2 "still reachable" leaks due to libcurl 
// it could be libcurl just leaks
// or it could be I set it up wrong
int main(void) {
    UNITY_BEGIN();
    RUN_TEST(test_request_init);
    RUN_TEST(test_request_set_headers);
    RUN_TEST(test_request_set_url);
    RUN_TEST(test_request_fetch);
    RUN_TEST(test_request_cleanup);
    return UNITY_END();
}

CMakeLists.txt

代码语言:javascript
运行
复制
cmake_minimum_required(VERSION 3.10)
project(requestlib)

add_library(request STATIC src/request.c)
target_link_libraries(request curl)

include(CTest)

find_program( MEMORYCHECK_COMMAND valgrind )
set( MEMORYCHECK_COMMAND_OPTIONS "--trace-children=yes --leak-check=full --dsymutil=yes" )
set( MEMORYCHECK_SUPPRESSIONS_FILE "${PROJECT_SOURCE_DIR}/valgrind_suppress.txt" )

add_executable(test_request tests/test_request.c)

add_library(unity STATIC tests/includes/ex_unity/src/unity.c)
target_include_directories(test_request PUBLIC "tests/includes/ex_unity/src")
target_link_libraries(test_request unity)
target_link_libraries(test_request curl)
target_link_libraries(test_request request)


add_test(
    NAME test_request
    COMMAND $<TARGET_FILE:test_request>
)

if ( CMAKE_COMPILER_IS_GNUCC )
    target_compile_options(test_request PRIVATE -Wall -Wextra)
endif()
if ( MSVC )
    target_compile_options(test_request PRIVATE /W4)
endif()

compile.sh

代码语言:javascript
运行
复制
#!/bin/bash

# get script's directory
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
project_path="$(dirname "$SCRIPT_DIR")"
build_path="$project_path/build-linux"
mkdir -p $build_path

cmake -S $project_path -B $build_path
cmake --build $build_path --config "Debug"

test.sh

代码语言:javascript
运行
复制
#!/bin/bash

# get script's directory
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
project_path="$(dirname "$SCRIPT_DIR")"
build_path="$project_path/build-linux"
mkdir -p $build_path


(cd $build_path; ctest -D ExperimentalMemCheck -C "Debug" -V)

单机运行时的偏值输出

代码语言:javascript
运行
复制
1   ==7559== Invalid read of size 8
2  ==7559==    at 0x48C21C8: ??? (in /usr/lib/x86_64-linux-gnu/libcurl-gnutls.so.4.7.0)
3  ==7559==    by 0x4892496: ??? (in /usr/lib/x86_64-linux-gnu/libcurl-gnutls.so.4.7.0)
4  ==7559==    by 0x48A75FD: ??? (in /usr/lib/x86_64-linux-gnu/libcurl-gnutls.so.4.7.0)
5  ==7559==    by 0x48A8465: curl_multi_perform (in /usr/lib/x86_64-linux-gnu/libcurl-gnutls.so.4.7.0)
6  ==7559==    by 0x487F03A: curl_easy_perform (in /usr/lib/x86_64-linux-gnu/libcurl-gnutls.so.4.7.0)
7  ==7559==    by 0x10C56D: request_fetch (in /media/sf_portal/mono/requestlib/so/build-linux/test_request)
8  ==7559==    by 0x109523: test_request_fetch (in /media/sf_portal/mono/requestlib/so/build-linux/test_request)
9  ==7559==    by 0x10C1BB: UnityDefaultTestRun (in /media/sf_portal/mono/requestlib/so/build-linux/test_request)
10  ==7559==    by 0x10964A: main (in /media/sf_portal/mono/requestlib/so/build-linux/test_request)
11  ==7559==  Address 0x55752c0 is 0 bytes inside an unallocated block of size 16 in arena "client"
12  ==7559== 
13  ==7559== Invalid read of size 1
14  ==7559==    at 0x48BD6E3: ??? (in /usr/lib/x86_64-linux-gnu/libcurl-gnutls.so.4.7.0)
15  ==7559==    by 0x48C21D5: ??? (in /usr/lib/x86_64-linux-gnu/libcurl-gnutls.so.4.7.0)
16  ==7559==    by 0x4892496: ??? (in /usr/lib/x86_64-linux-gnu/libcurl-gnutls.so.4.7.0)
17  ==7559==    by 0x48A75FD: ??? (in /usr/lib/x86_64-linux-gnu/libcurl-gnutls.so.4.7.0)
18  ==7559==    by 0x48A8465: curl_multi_perform (in /usr/lib/x86_64-linux-gnu/libcurl-gnutls.so.4.7.0)
19  ==7559==    by 0x487F03A: curl_easy_perform (in /usr/lib/x86_64-linux-gnu/libcurl-gnutls.so.4.7.0)
20  ==7559==    by 0x10C56D: request_fetch (in /media/sf_portal/mono/requestlib/so/build-linux/test_request)
21  ==7559==    by 0x109523: test_request_fetch (in /media/sf_portal/mono/requestlib/so/build-linux/test_request)
22  ==7559==    by 0x10C1BB: UnityDefaultTestRun (in /media/sf_portal/mono/requestlib/so/build-linux/test_request)
23  ==7559==    by 0x10964A: main (in /media/sf_portal/mono/requestlib/so/build-linux/test_request)
24  ==7559==  Address 0x0 is not stack'd, malloc'd or (recently) free'd
25  ==7559== 
26  ==7559== 
27  ==7559== Process terminating with default action of signal 11 (SIGSEGV)
28  ==7559==  Access not within mapped region at address 0x0
29  ==7559==    at 0x48BD6E3: ??? (in /usr/lib/x86_64-linux-gnu/libcurl-gnutls.so.4.7.0)
30  ==7559==    by 0x48C21D5: ??? (in /usr/lib/x86_64-linux-gnu/libcurl-gnutls.so.4.7.0)
31  ==7559==    by 0x4892496: ??? (in /usr/lib/x86_64-linux-gnu/libcurl-gnutls.so.4.7.0)
32  ==7559==    by 0x48A75FD: ??? (in /usr/lib/x86_64-linux-gnu/libcurl-gnutls.so.4.7.0)
33  ==7559==    by 0x48A8465: curl_multi_perform (in /usr/lib/x86_64-linux-gnu/libcurl-gnutls.so.4.7.0)
34  ==7559==    by 0x487F03A: curl_easy_perform (in /usr/lib/x86_64-linux-gnu/libcurl-gnutls.so.4.7.0)
35  ==7559==    by 0x10C56D: request_fetch (in /media/sf_portal/mono/requestlib/so/build-linux/test_request)
36  ==7559==    by 0x109523: test_request_fetch (in /media/sf_portal/mono/requestlib/so/build-linux/test_request)
37  ==7559==    by 0x10C1BB: UnityDefaultTestRun (in /media/sf_portal/mono/requestlib/so/build-linux/test_request)
38  ==7559==    by 0x10964A: main (in /media/sf_portal/mono/requestlib/so/build-linux/test_request)
39  ==7559==  If you believe this happened as a result of a stack
40  ==7559==  overflow in your program's main thread (unlikely but
41  ==7559==  possible), you can try to increase the size of the
42  ==7559==  main thread stack using the --main-stacksize= flag.
43  ==7559==  The main thread stack size used in this run was 8388608.

simpletest.c

代码语言:javascript
运行
复制
#include <string.h>
#include <stdlib.h>
#include <curl/curl.h>

#include "../src/request.h"

void step1(void) {
    request_init();
}

void step2(void) {
    char *headers[] = {"Content-Type: multipart/mixed"};
    request_set_headers(headers, 1);
}

void step3(void) {
    request_set_url("https://raw.githubusercontent.com/undoingtech/Sticky-Steps/main/README.md");
}

void step4(void) {
    MemoryStruct *putdatahere = NULL;
    request_fetch(putdatahere);
}

void step5(void) {
    request_cleanup();
}

int main(void) {
    step1();
    step2();
    step3();
    step4();
    step5();
    return 0;
}

编译-simpletest.sh(在try文件夹中运行)

代码语言:javascript
运行
复制
#!/bin/bash

gcc -Wall -Wextra -g -o simpletest simpletest.c ../src/request.c -lcurl
EN

Stack Overflow用户

发布于 2022-10-04 22:17:01

罪魁祸首在request.c in request_set_headers()

代码语言:javascript
运行
复制
/// REMOVING THIS LINE TAKES AWAY SEGFAULT ///
rc = curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, list);
curl_slist_free_all(list);  // <-- curl_easy_perform SHOULD BE BEFORE THIS LINE!!
  1. 我用curl_easy_setopt和保存头数据的curl_slist设置头。
  2. 立即释放用于使用curl_slist_free_all设置标头的数据。
  3. 稍后在request_fetch()中调用curl_easy_perform

根据这个libcurl例子的命令应该是:

  1. 设置标题。
  2. 打给curl_easy_perform
  3. 自由头数据与curl_slist_free_all
票数 1
EN
查看全部 1 条回答
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/73950546

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档