【问题标题】:curl_easy_perform() crashing on Ubuntu Server 14curl_easy_perform() 在 Ubuntu Server 14 上崩溃
【发布时间】:2014-05-05 22:08:47
【问题描述】:

这是我的上传功能。

目标是:根据偏移量和块大小仅上传文件的一个块

在这个函数中,如果p_offset不为零,我自己调用fseek(),然后让libcurl使用fread()读取文件的内容。

函数的调用者负责给出正确有效的块大小,确保p_offset + p_sizeOfChunk <= ACTUAL_SIZE_OF_FILE

来自服务器的答案应该是一个字符串。我通过我的回调writeToString()得到它

代码在 Windows 和 OS X 上运行良好。但 curl_easy_perform() 在 Ubuntu 14 上有时会崩溃。

我的代码中是否缺少任何可能导致此崩溃的内容?

void uploadFile( const string & p_filename, const string & p_url, size_t p_offset, size_t p_sizeOfChunk )
{
    FILE * file( fopen( p_filename.c_str(), "rb" ) );

    if ( !file )
    {
        throw Exception( __FILE__, __LINE__ ) << "Could not open file " << p_filename << " when posting to " << p_url;
    }

    if ( p_offset )
    {
        if ( fseek( file, (long)p_offset, SEEK_SET ) )
        {
            throw Exception( __FILE__, __LINE__ ) << "Could not seek in file " << p_filename << " when posting to " << p_url;
        }
    }

    CURL * curl( curl_easy_init() );

    if ( !curl )
    {
        throw Exception( __FILE__, __LINE__ ) << "Could not initialize cURL when posting " << p_filename << " to " << p_url;
    }

    // URL
    curl_easy_setopt( curl, CURLOPT_URL, p_url.c_str() );

    // PUT HTTP method
    string answer;
    curl_easy_setopt( curl, CURLOPT_UPLOAD, 1L );
    curl_easy_setopt( curl, CURLOPT_READFUNCTION, fread );
    curl_easy_setopt( curl, CURLOPT_READDATA, file );
    curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, writeToString );
    curl_easy_setopt( curl, CURLOPT_WRITEDATA, &answer );
    curl_easy_setopt( curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)p_sizeOfChunk );

    char errorBuffer[ CURL_ERROR_SIZE + 1 ];
    curl_easy_setopt( curl, CURLOPT_ERRORBUFFER, errorBuffer );

    // No signal handlers...
    curl_easy_setopt( curl, CURLOPT_NOSIGNAL, 1 );
    curl_easy_setopt( curl, CURLOPT_TIMEOUT_MS, 120000 );

    // HEADER
    char contentLength[ 512 ];
    snprintf( contentLength, sizeof( contentLength ), "Content-Length: %zu", p_sizeOfChunk );

    struct curl_slist * headers( nullptr );
    headers = curl_slist_append( headers, contentLength );
    curl_easy_setopt( curl, CURLOPT_HTTPHEADER, headers );

    // SSL
    curl_easy_setopt( curl, CURLOPT_CAINFO, "path/to/cacert.pem" );

    CURLcode res( curl_easy_perform( curl ) );

    fclose( file );

    if ( res != CURLE_OK && res != CURLE_SEND_ERROR )
    {
        curl_easy_cleanup( curl );
        throw Exception( __FILE__, __LINE__ ) << "cURL error when posting " << p_filename << " to " << p_url << ": " << errorBuffer;
    }

    long httpResponseCode( 0 );
    curl_easy_getinfo( curl, CURLINFO_RESPONSE_CODE, &httpResponseCode );
    curl_easy_cleanup( curl );

    if ( ( httpResponseCode / 100 ) != 2 )
    {
        cout << answer << endl;
        throw Exception( __FILE__, __LINE__ ) << "HTTP error " << httpResponseCode << " when posting " << p_filename;
    }
}

我得到答案并将其记录在std::stringwriteToString() 上。这肯定不是坠机的原因。我测试它只是返回size * count 并且崩溃仍然发生。

static size_t writeToString( const void * ptr, size_t size, size_t count, FILE * stream )
{
    string & retContent( *( reinterpret_cast< string * >( stream ) ) );

    if ( !retContent.length() )
    {
        int skipBOM( ( reinterpret_cast< const unsigned char * >( ptr )[ 0 ] == 0xEF && reinterpret_cast< const unsigned char * >( ptr )[ 1 ] == 0xBB && reinterpret_cast< const unsigned char * >( ptr )[ 2 ] == 0xBF ) ? 3 : 0 );
        retContent += string( static_cast< const char * >( ptr ) + skipBOM, static_cast< int >( size * count ) - skipBOM );
    }
    else
    {
        retContent += string( static_cast< const char * >( ptr ), size * count );
    }

    return size * count;
}

这是崩溃时的堆栈!好像和OpenSSL有关。

#0  0x00007ffff65ad35d in write () at ../sysdeps/unix/syscall-template.S:81
#1  0x00007ffff73187a6 in ?? () from /lib/x86_64-linux-gnu/libcrypto.so.1.0.0
#2  0x00007ffff731684b in BIO_write () from /lib/x86_64-linux-gnu/libcrypto.so.1.0.0
#3  0x00007ffff6ffcb72 in ?? () from /lib/x86_64-linux-gnu/libssl.so.1.0.0
#4  0x00007ffff6ffd273 in ?? () from /lib/x86_64-linux-gnu/libssl.so.1.0.0
#5  0x00007ffff76873e1 in ossl_send (conn=0x7ffef8013b28, sockindex=0, mem=0x7ffef8005379, len=16384, curlcode=0x7fff127fa5c0) at vtls/openssl.c:2720
#6  0x00007ffff762fe0f in Curl_write (conn=0x7ffef8013b28, sockfd=64, mem=0x7ffef8005379, len=16384, written=0x7fff127fa608) at sendf.c:233
#7  0x00007ffff764fb01 in readwrite_upload (data=0x7ffef8000a78, conn=0x7ffef8013b28, k=0x7ffef8000af0, didwhat=0x7fff127fa664) at transfer.c:954
#8  0x00007ffff764fdd9 in Curl_readwrite (conn=0x7ffef8013b28, done=0x7fff127fa6dc) at transfer.c:1059
#9  0x00007ffff765ced7 in multi_runsingle (multi=0x7ffef800a668, now=..., data=0x7ffef8000a78) at multi.c:1484
#10 0x00007ffff765d60c in curl_multi_perform (multi_handle=0x7ffef800a668, running_handles=0x7fff127fa870) at multi.c:1759
#11 0x00007ffff7652103 in easy_transfer (multi=0x7ffef800a668) at easy.c:705
#12 0x00007ffff7652311 in easy_perform (data=0x7ffef8000a78, events=false) at easy.c:793
#13 0x00007ffff7652364 in curl_easy_perform (easy=0x7ffef8000a78) at easy.c:812
#14 ...

【问题讨论】:

  • 哪里崩溃了?你试过在 gdb 下运行它吗?
  • 无关:每当您抛出异常时,您的 sn-p 就会像疯了一样泄漏(file 应该在每个 throw 站点上关闭)。对于这种常见的模式,我建议使用守卫:实例化一个小对象,无论函数的退出路径是什么,它的析构函数都会执行一个动作。然后,您可以使用此保护正确释放 filecurl 句柄。
  • @jsantander ...我已经尝试过GDB ...崩溃似乎与OpenSSL有关,一旦我通过HTTPS上传它。我将使用堆栈更新问题。
  • 也许您可以使用调试版本库并包括符号 (-g) 重新编译。或者,您可以尝试将您的程序提炼成sscce 吗?然后我们可以自己尝试...
  • 这有点奇怪,世界上没有人跟我一样。 libcurl 应该足够“流行”,以至于其他人之前都知道这个堆栈......但似乎真的没有人知道......我将重新编译 cURL(和 OpenSSL)并调试它。谢谢。

标签: c++ ubuntu curl openssl libcurl


【解决方案1】:

首先...用调试器运行它(或获取适当的核心转储)以找出真正失败的地方。

不确定这是否是问题...但是查看documentation,您传入CURLOPT_WRITEFUNCTION 的函数必须具有以下签名。

size_t function( char *ptr, size_t size, size_t nmemb, void *userdata);

但是在你的情况下:

size_t writeToString(const void * ptr, size_t size, size_t count, FILE * stream);

显然 CURLOPT_WRITEFUNCTION 的默认实现需要一个 FILE * 那里...但在 void * 形式

【讨论】:

  • 我已经试过了。但是 cURL 总是接受 fread/fwrite 函数作为回调。因此,这不应该是一个问题。这些签名在汇编级别是等效的......我将用堆栈更新我的问题......它似乎与OpenSSL有关
【解决方案2】:

问题在于我完全误解了一个细节。

curl_easy_setopt( curl, CURLOPT_NOSIGNAL, 1 );

CURLOPT_NOSIGNAL 阻止 libcurl 处理任何信号的效果!我最初的解释是完全相反的。发生错误是因为在 OS X 上它正在工作 [并且管道处理也存在],所以我认为“信号管理”是正确的。我不知道为什么在 OS X 上没有出现相同的错误。不管怎样,我删除了这条线,它工作得很好。

当然,另一种可能的解决方案是将 ir 显式设置为零:

curl_easy_setopt( curl, CURLOPT_NOSIGNAL, 0 );

【讨论】:

    猜你喜欢
    • 2022-07-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-30
    • 1970-01-01
    • 1970-01-01
    • 2021-03-23
    • 1970-01-01
    相关资源
    最近更新 更多