Curl_formfree не работает должным образом, возвращает ошибку: SegFault

Я пишу код для загрузки файла на файловый сервер вместе с двумя другими строковыми переменными как часть почтового запроса HTTP.

Идея использования мультиинтерфейса здесь заключается в том, чтобы в будущем загружать несколько файлов.

Здесь используется версия Libcurl: 7.44.

Вот моя программа:

#include <iostream>
#include <string>
#include <curl/curl.h>

const auto TimeoutInMS = 1000;
const auto FileDescriptorZero = 0;

bool HTTPPostSuccessful(long httpResponseCode)
{
    bool httpRequestSuccessful = false;
    if (httpResponseCode == 200)
    {
        httpRequestSuccessful = true;
    }
    return httpRequestSuccessful;
}

size_t WriteCallback(void * buffer, size_t size, size_t count, void * userp)
{
    size_t numBytes = size * count;
    static_cast<std::string*>(userp)->append(static_cast<char*>(buffer), numBytes);
    return numBytes;
}

void HTTPPost(const std::string& value1, const std::string& value2, const std::string& filePath)
{
    struct curl_slist *pHTTPRequestHeaders = nullptr;
    struct curl_httppost* pFormpost = nullptr;
    struct curl_httppost* pLastptr = nullptr;
    uint16_t httpResponseCode = 0;
    int stillSendingFile = 0;
    CURL* pCurlEasyHandle = curl_easy_init();
    CURLM *pCurlMultiHandle = curl_multi_init();
    std::string responseData{};

    
    if (pCurlEasyHandle && pCurlMultiHandle)
    {
        pHTTPRequestHeaders = curl_slist_append(pHTTPRequestHeaders, "Content-Type: multipart/form-data");
        curl_easy_setopt(pCurlEasyHandle, CURLOPT_HTTPHEADER, pHTTPRequestHeaders);
        curl_easy_setopt(pCurlEasyHandle, CURLOPT_URL, "https://http_.org/logs/readers");
        curl_easy_setopt(pCurlEasyHandle, CURLOPT_VERBOSE, 1L);
        
        curl_formadd(&pFormpost, &pLastptr,
                     CURLFORM_COPYNAME, "fileName",
                     CURLFORM_FILE, filePath.c_str(),
                     CURLFORM_CONTENTTYPE, "text/csv",
                     CURLFORM_END);
        
        curl_formadd(&pFormpost, &pLastptr,
                     CURLFORM_COPYNAME, "Value1",
                     CURLFORM_COPYCONTENTS, value1.c_str(),
                     CURLFORM_END);
        
        curl_formadd(&pFormpost, &pLastptr,
                     CURLFORM_COPYNAME, "Value2",
                     CURLFORM_COPYCONTENTS, value2.c_str(),
                     CURLFORM_END);
        
        curl_easy_setopt(pCurlEasyHandle, CURLOPT_HTTPPOST, pFormpost);
        curl_easy_setopt(pCurlEasyHandle, CURLOPT_WRITEFUNCTION, WriteCallback);
        curl_easy_setopt(pCurlEasyHandle, CURLOPT_WRITEDATA, &responseData);
        
        curl_multi_add_handle(pCurlMultiHandle, pCurlEasyHandle);
        
        do
        {
            curl_multi_perform(pCurlMultiHandle, &stillSendingFile);
            if (stillSendingFile)
            {
                curl_multi_wait(pCurlMultiHandle, nullptr, FileDescriptorZero, TimeoutInMS, nullptr);
            }
        }
        while(stillSendingFile);
        
        CURLcode res = curl_easy_getinfo(pCurlEasyHandle, CURLINFO_RESPONSE_CODE, &httpResponseCode);
        
        if (HTTPPostSuccessful(httpResponseCode) && res == CURLE_OK)
        {
            
            std::cout << "File sent Successfully, HTTP response code: " << httpResponseCode << ", ResponseData: "<< responseData<< std::endl;
        }
        else
        {
            std::cerr << "Error during request: " << curl_easy_strerror(res) << ", Failure HTTP response code: " << httpResponseCode << std::endl;
        }
        
        if (pCurlMultiHandle && pCurlEasyHandle)
        {
            std::cout << "Clean up for curl_multi_remove_handle " << std::endl;
            curl_multi_remove_handle(pCurlMultiHandle, pCurlEasyHandle);
        }
        
        if (pCurlEasyHandle)
        {
            std::cout << "Clean up for pCurlEasyHandle " << std::endl;
            curl_easy_cleanup(pCurlEasyHandle);
            pCurlEasyHandle = nullptr;
        }
        
        if (pCurlMultiHandle)
        {
            std::cout << "Clean up for pCurlMultiHandle " << std::endl;
            curl_multi_cleanup(pCurlMultiHandle);
            pCurlMultiHandle = nullptr;
        }
        
        if (pHTTPRequestHeaders)
        {
            std::cout << "Clean up pHTTPRequestHeaders " << std::endl;
            curl_slist_free_all(pHTTPRequestHeaders);
            pHTTPRequestHeaders = nullptr;
        }
        
        if (pFormpost)
        {
            std::cout << "Clean up pFormpost " << std::endl;
            curl_formfree(pFormpost);
            pFormpost = nullptr;
            pLastptr = nullptr;
        }
    }


}


bool UploadFile(const std::string& value1, const std::string& value2, const std::string& filePath)
{
    curl_global_init(CURL_GLOBAL_ALL);
    HTTPPost(value1, value2, filePath);
    curl_global_cleanup();

    return true;
}

int main ()
{
    UploadFile("1", "1", "/tmp/UploadFIle/testDoc.txt");
}

приведенный выше код отлично работает на моей машине, когда я фиксирую его на сервере сборки Jenkins, я получаю следующую ошибку:

7: HTTPClient Initialization is successful.
7: * Could not resolve host: http_.org
7: * Closing connection 0
7: Error during request: No error, Failure HTTP response code: 0
7: Clean up for curl_multi_remove_handle 
7: Clean up for pCurlEasyHandle 
7: Clean up for pCurlMultiHandle 
7: Clean up pHTTPRequestHeaders 
7: Clean up pFormpost 
 7/30 Test  #7: **** ....................................***Exception: SegFault  0.29 sec

В некоторых случаях также наблюдается следующая ошибка:

8: HTTPClient Initialization is successful.
8: * Could not resolve host: http_.org
8: * Closing connection 0
8: Error during request: No error, Failure HTTP response code: 0
8: Clean up for curl_multi_remove_handle 
8: Clean up for pCurlEasyHandle 
8: Clean up for pCurlMultiHandle 
8: Clean up pHTTPRequestHeaders 
8: Clean up pFormpost 
8: ==11267== Invalid read of size 8
8: ==11267==    at 0x4E48018: curl_formfree (in /usr/lib/x86_64-linux-gnu/libcurl.so.4.4.0)

Может кто-нибудь, пожалуйста, сообщите мне, что здесь не так, новичок в Curl.

Почему вы используете устаревший API?

273K 03.06.2023 07:22

Отмечен Valgrind, но нет вывода Valgrind?

Paul Floyd 03.06.2023 07:24

Привет @ 273K, Мы должны использовать версию curl 7.44, поэтому, пожалуйста, дайте мне знать, есть ли какой-либо другой способ в этой версии libcurl.

user2094814 03.06.2023 07:53

Вероятно, это не связано с вашей проблемой, но в целом неплохо уничтожать вещи в порядке, обратном порядку их создания.

Alan Birtles 03.06.2023 08:23

Привет @ 273K, Обновил код, как вы предложили, теперь вместо недействительного бесплатного я вижу ошибку сегмента.

user2094814 03.06.2023 08:27

@273K, да в одной строке с curl_formfree. В настоящее время наш сервер не готов, поэтому можно тестировать только отрицательные случаи на сервере jenkins, эта ошибка, которую я получаю в случае, когда URI сервера недействителен, а файл пуст, я также протестировал тот же код с URI: «httpbin.org /post" на моей машине с пустым файлом, который отлично работает. Я получаю сообщение об ошибке только на сервере сборки jenkins, когда я фиксирую свой код.

user2094814 03.06.2023 08:54
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
6
50
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Первая проблема, приводящая к UB, находится в WriteCallback:

static_cast<std::string*>(userp)->append(static_cast<char*>(buffer), 0, numBytes);

Вы выбрали перегруженную функцию-член

basic_string& append( const basic_string& str,
                      size_type pos, size_type count )

который создает std::string из данных, не заканчивающихся нулем, в buffer.

Вы должны использовать другую перегруженную функцию-член

basic_string& append( const CharT* s, size_type count )

поэтому правильный вызов

static_cast<std::string*>(userp)->append(static_cast<char*>(buffer), numBytes);

Вторая проблема — это uint16_t httpResponseCode, в то время как CURLINFO_RESPONSE_CODE требует указатель на длинное значение, вы передаете &httpResponseCode указатель на короткое значение, это еще один UB. В частности портит данные в локальных переменных, записывает нули в 2 или 6 байт вне httpResponseCode хранилища, возможно в pFormpost байтах. Должен быть

long httpResponseCode = 0;

Спасибо @ 273K, я проверю это и сообщу вам результат.

user2094814 03.06.2023 08:00

Другие вопросы по теме