From 59c11074cfca00cf071b7194af6e2e4f8743bb69 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 24 Mar 2026 16:25:03 +0100 Subject: [PATCH] src: add curl_global_init_mem testing Build with "configure --enable-init-mem-debug" to make the tool use curl_global_init_mem() and a set of private memory funtion callbacks for libcurl's memory management. Using this setup, memory mixups in tool code is more likely to cause crashes and thus get discovered while running tests. This curl_global_init_mem debug mode can only be done when building libcurl shared (not static) and without debugging enabled - since it needs to use the custom memory funtion callbacks. Closes #21099 --- CMakeLists.txt | 6 +++ configure.ac | 17 +++++++++ src/tool_cfgable.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 114 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 32cbeebde7..06a21b1ce5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -510,7 +510,13 @@ option(CURL_DISABLE_TYPECHECK "Disable curl_easy_setopt()/curl_easy_getinfo() ty mark_as_advanced(CURL_DISABLE_TYPECHECK) option(CURL_DISABLE_VERBOSE_STRINGS "Disable verbose strings" OFF) mark_as_advanced(CURL_DISABLE_VERBOSE_STRINGS) +option(CURL_DEBUG_GLOBAL_MEM "Debug curl_global_init_mem" OFF) +mark_as_advanced(CURL_DEBUG_GLOBAL_MEM) +if(CURL_DEBUG_GLOBAL_MEM) + # Set it via the command-line to make it apply to the entire directory. + set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS "CURL_DEBUG_GLOBAL_MEM") +endif() if(CURL_DISABLE_TYPECHECK) # Set it via the command-line to make it apply to examples also. set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS "CURL_DISABLE_TYPECHECK") diff --git a/configure.ac b/configure.ac index 99ecbce5bc..9ae0cfd068 100644 --- a/configure.ac +++ b/configure.ac @@ -1087,6 +1087,23 @@ AS_HELP_STRING([--disable-mqtt],[Disable MQTT support]), AC_MSG_RESULT(no) ) +AC_MSG_CHECKING([enable curl_global_init_mem debug build]) +AC_ARG_ENABLE(init-mem-debug, +AS_HELP_STRING([--enable-init-mem-debug],[curl_global_init_mem debug]) +AS_HELP_STRING([--disable-init-mem-debug],[]), +[ case "$enableval" in + yes) + AC_MSG_RESULT(yes) + AC_DEFINE(CURL_DEBUG_GLOBAL_MEM, 1, [curl_debug_global_mem debug build]) + SUPPORT_FEATURES="$SUPPORT_FEATURES global-mem-debug" + ;; + *) + AC_MSG_RESULT(no) + ;; + esac ], + AC_MSG_RESULT(no) +) + dnl ********************************************************************** dnl Check for built-in manual dnl ********************************************************************** diff --git a/src/tool_cfgable.c b/src/tool_cfgable.c index 0d151af02e..381ca874d9 100644 --- a/src/tool_cfgable.c +++ b/src/tool_cfgable.c @@ -21,6 +21,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ +#include #include "tool_setup.h" #include "tool_cfgable.h" @@ -206,6 +207,90 @@ void config_free(struct OperationConfig *config) } } +#ifdef CURL_DEBUG_GLOBAL_MEM + +#ifdef CURL_MEMDEBUG +#error "curl_global_init_mem() testing does not work with memdebug debugging" +#endif + +/* + * This is the custom memory functions handed to curl when we run special test + * round to verify them. + * + * The main point is to make sure that what is returned is different than what + * the regular memory functions return so that mixup will trigger problems. + */ + +struct extramem { + size_t extra; + union { + curl_off_t o; + double d; + void *p; + } mem[1]; +}; + +static void *custom_calloc(size_t wanted_nmemb, size_t wanted_size) +{ + struct extramem *m; + size_t sz = wanted_size * wanted_nmemb; + sz += sizeof(struct extramem); + m = curlx_calloc(1, sz); + if(m) + return m->mem; + return NULL; +} + +static void *custom_malloc(size_t wanted_size) +{ + struct extramem *m; + size_t sz = wanted_size + sizeof(struct extramem); + m = curlx_malloc(sz); + if(m) + return m->mem; + return NULL; +} + +static char *custom_strdup(const char *ptr) +{ + struct extramem *m; + size_t size = strlen(ptr) + 1; + size_t sz = size + sizeof(struct extramem); + m = curlx_malloc(sz); + if(m) { + char *p = (char *)m->mem; + /* since strcpy is banned, we do memcpy */ + memcpy(p, ptr, sz); + p[sz] = 0; + return (char *)m->mem; + } + return NULL; + +} + +static void *custom_realloc(void *ptr, size_t size) +{ + struct extramem *m = NULL; + size_t sz = size + sizeof(struct extramem); + if(ptr) + m = (void *)((char *)ptr - offsetof(struct extramem, mem)); + m = curlx_realloc(ptr, sz); + if(m) + return m->mem; + return NULL; +} + +static void custom_free(void *ptr) +{ + struct extramem *m = NULL; + if(ptr) { + m = (void *)((char *)ptr - offsetof(struct extramem, mem)); + curlx_free(m); + } +} + +#endif + /* * This is the main global constructor for the app. Call this before * _any_ libcurl usage. If this fails, *NO* libcurl functions may be @@ -230,7 +315,13 @@ CURLcode globalconf_init(void) global->first = global->last = config_alloc(); if(global->first) { /* Perform the libcurl initialization */ +#ifdef GLOBAL_MEM + result = curl_global_init_mem(CURL_GLOBAL_ALL, custom_malloc, custom_free, + custom_realloc, custom_strdup, + custom_calloc); +#else result = curl_global_init(CURL_GLOBAL_DEFAULT); +#endif if(!result) { /* Get information about libcurl */ result = get_libcurl_info();