Skip to content

Random memory leak while calling curl_multi_cleanup #7683

@slydder101

Description

@slydder101

Random memory leak is detected when calling curl_multi_cleanup before the associated easy handles are closed.

Steps to reproduce.

Build the following sample multi_app application:

#include <stdio.h>
#include <string.h>

/* somewhat unix-specific */
#include <sys/time.h>
#include <unistd.h>

/* curl stuff */
#include <curl/curl.h>

#define HANDLECOUNT 2   /* Number of simultaneous transfers */

/* The maximum number of curl_multi_perform iterations to perform before
 * calling curl_multi_cleanup.
 */
#define MAX_ITERATIONS 14

int main(void)
{
    CURL *handles[HANDLECOUNT];
    CURLM *multi_handle;

    int still_running = 1; /* keep number of running handles */
    int i;
    int iteration = 0;

    CURLMsg *msg; /* for picking up messages with the transfer status */
    int msgs_left; /* how many messages are left */

    /* Allocate one CURL handle per transfer */
    for (i = 0; i < HANDLECOUNT; i++) {
        handles[i] = curl_easy_init();
        curl_easy_setopt(handles[i], CURLOPT_URL, "https://example.com");
    }

    /* init a multi stack */
    multi_handle = curl_multi_init();

    /* add the individual transfers */
    for(i = 0; i < HANDLECOUNT; i++)
        curl_multi_add_handle(multi_handle, handles[i]);

    while (still_running) {
        CURLMcode mc = curl_multi_perform(multi_handle, &still_running);

        if (iteration++ >= MAX_ITERATIONS)
            goto out;

        if (still_running)
            /* wait for activity, timeout or "nothing" */
            mc = curl_multi_poll(multi_handle, NULL, 0, 1000, NULL);

        if (mc)
            break;
    }

    /* Free the CURL handles */
    for(i = 0; i < HANDLECOUNT; i++) {
        curl_multi_remove_handle(multi_handle, handles[i]);
        curl_easy_cleanup(handles[i]);
    }

    curl_multi_cleanup(multi_handle);

    return 0;
}

Run the application with valgrind using the following shell script:

#!/bin/sh

while valgrind -q --error-exitcode=1 --leak-check=full ./multi_app > /dev/null; do
    echo "Retrying..."
done

Depending on your computer's speed the leak will appear after a few tries:

Retrying...
==146245== 20,162 (1,280 direct, 18,882 indirect) bytes in 2 blocks are definitely lost in loss record 81 of 81
==146245==    at 0x483E7C5: malloc (vg_replace_malloc.c:380)
==146245==    by 0x495877A: ssl_session_dup (ssl_sess.c:110)
==146245==    by 0x496ABD3: tls_process_new_session_ticket (statem_clnt.c:2620)
==146245==    by 0x4965D8C: read_state_machine (statem.c:636)
==146245==    by 0x4965D8C: state_machine.part.0 (statem.c:434)
==146245==    by 0x493C729: ssl3_read_bytes (rec_layer_s3.c:1658)
==146245==    by 0x4943765: ssl3_read_internal (s3_lib.c:4465)
==146245==    by 0x494EE17: SSL_read (ssl_lib.c:1783)
==146245==    by 0x48E5D5B: ossl_closeone.isra.0 (openssl.c:1407)
==146245==    by 0x48E5DE5: ossl_close (openssl.c:1427)
==146245==    by 0x48ED11E: Curl_ssl_close (vtls.c:676)
==146245==    by 0x48DA583: conn_shutdown (url.c:752)
==146245==    by 0x48DA583: Curl_disconnect (url.c:870)
==146245==    by 0x4889D92: Curl_conncache_close_all_connections (conncache.c:555)

Environment

  • curl 7.78
  • openSSL 1.1.1.k and openSSL 1.1.1.l

Notes

The memory leak appeared starting from curl 7.78 with the following commit
b249592d29ae0a2b3e8e07fdbc01f33b5a5b8420.
For know I have managed to reproduce it only with openSSL backend and not with
mbedTLS.

To reproduce the issue there needs to be at least two simultaneous connections
and the cleanup operations need to be close in time to exit.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions