memory: make function overrides work reliably in unity builds

Fixing:
- HTTPS-RR builds with c-ares and Linux MUSL.
- curl-for-win minimal builds with Linux MUSL.

It should fix all other kinds of entaglement between curl's redefintions
of system symbols and system (or 3rd-party) headers sensitive to that.

It also syncs memory override behavior between unity & non-unity builds,
thus reducing build variations.

The idea is to define and declare everything once in `curl_setup.h`,
without overriding any system symbols with curl ones yet. Then, like
before this patch, override them, if necessary, in each source file via
`curl_memory.h` and `memdebug.h`, after including system headers.
To ensure a clean slate with no overrides at the beginning of each
source file, reset all of them unconditionally at the end of
`curl_setup.h`, by including `curl_mem_undef.h`. (This assumes
`curl_setup.h` is always included first, which is already the case
throughout the codebase.)

`curl_mem_undef.h` can also be included explicitly wherever overrides
are causing problems. E.g. in tests which use unity-style builds and
a previously included `curl_memory.h`/`memdebug.h` can be spilling into
other source files.

The simplified role of the two override headers:
- `curl_memory.h`: overrides system memory allocator functions to
  libcurl ones, when memory tracing (aka `CURLDEBUG`) is disabled.
- `memdebug.h`: overrides system memory allocator and some other
  functions to curl debug functions, when memory tracing is enabled.

Changed made in this patch, step-by-step:
- curl_memory.h: move allocator typedefs and protos to `curl_setup.h`.
- memdebug.h: move `ALLOC_*` macros to `curl_setup.h`.
- memdebug.h: move allocator protos to `curl_setup.h`.
- memdebug.h: move `Curl_safefree()` macro to `curl_setup.h`.
  (it's a regular macro, with a one-time, global, definition.)
- curl_memory.h: move system symbol undefs to a new, separate header:
  `curl_mem_undef.h`.
- curl_setup.h: include `curl_mem_undef.h` at the end, unconditionally,
  to reset system symbol macros after each inclusion.
- handle `sclose()` and `fake_sclose()` in `curl_setup.h`. They are not
  system symbols, a one-time definition does the job.

Also:
- GHA/linux: enable unity mode for the HTTP-RR c-ares MUSL job.
  Follow-up to 17ab4d62e6 #16413

That said, I'd still find it better to avoid redefining system macros.
To communicate clearly the fact that they are not the original system
calls and they do behave differently. And, it would allow dropping the
undef/redef dance in each source file, and maintaining the logic with
it. The "last #include files should be in this order" comments in each
source would also become unnecessary. Also the trick of using
`(func)` (or interim macros) to call the non-overridden function where
required. This method works for printf and most everything else already.
For `_tcsdup`, socket and fopen functions this could work without
disturbing the codebase much.

Ref: #16428 (clean reboot of)

Closes #17827
This commit is contained in:
Viktor Szakats 2025-07-05 11:14:24 +02:00
parent 113f6aacd4
commit 3bb5e58c10
No known key found for this signature in database
GPG key ID: B5ABD165E2AEF201
7 changed files with 190 additions and 193 deletions

View file

@ -1,6 +1,5 @@
#ifndef HEADER_CURL_MEMDEBUG_H
#define HEADER_CURL_MEMDEBUG_H
#ifdef CURLDEBUG
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
@ -30,99 +29,7 @@
* as well as the library. Do not mix with library internals!
*/
#include <curl/curl.h>
#include "functypes.h"
#ifdef __clang__
# define ALLOC_FUNC __attribute__((__malloc__))
# if __clang_major__ >= 4
# define ALLOC_SIZE(s) __attribute__((__alloc_size__(s)))
# define ALLOC_SIZE2(n, s) __attribute__((__alloc_size__(n, s)))
# else
# define ALLOC_SIZE(s)
# define ALLOC_SIZE2(n, s)
# endif
#elif defined(__GNUC__) && __GNUC__ >= 3
# define ALLOC_FUNC __attribute__((__malloc__))
# define ALLOC_SIZE(s) __attribute__((__alloc_size__(s)))
# define ALLOC_SIZE2(n, s) __attribute__((__alloc_size__(n, s)))
#elif defined(_MSC_VER)
# define ALLOC_FUNC __declspec(restrict)
# define ALLOC_SIZE(s)
# define ALLOC_SIZE2(n, s)
#else
# define ALLOC_FUNC
# define ALLOC_SIZE(s)
# define ALLOC_SIZE2(n, s)
#endif
/* Avoid redundant redeclaration warnings with modern compilers, when including
this header multiple times. */
#ifndef HEADER_CURL_MEMDEBUG_H_EXTERNS
#define HEADER_CURL_MEMDEBUG_H_EXTERNS
extern FILE *curl_dbg_logfile;
/* memory functions */
CURL_EXTERN void curl_dbg_free(void *ptr, int line, const char *source);
CURL_EXTERN ALLOC_FUNC ALLOC_SIZE(1)
void *curl_dbg_malloc(size_t size, int line, const char *source);
CURL_EXTERN ALLOC_FUNC ALLOC_SIZE2(1, 2)
void *curl_dbg_calloc(size_t n, size_t size, int line, const char *source);
CURL_EXTERN ALLOC_SIZE(2)
void *curl_dbg_realloc(void *ptr, size_t size, int line, const char *source);
CURL_EXTERN ALLOC_FUNC
char *curl_dbg_strdup(const char *str, int line, const char *src);
#if defined(_WIN32) && defined(UNICODE)
CURL_EXTERN ALLOC_FUNC
wchar_t *curl_dbg_wcsdup(const wchar_t *str, int line, const char *source);
#endif
CURL_EXTERN void curl_dbg_memdebug(const char *logname);
CURL_EXTERN void curl_dbg_memlimit(long limit);
CURL_EXTERN void curl_dbg_log(const char *format, ...) CURL_PRINTF(1, 2);
/* file descriptor manipulators */
CURL_EXTERN curl_socket_t curl_dbg_socket(int domain, int type, int protocol,
int line, const char *source);
CURL_EXTERN void curl_dbg_mark_sclose(curl_socket_t sockfd,
int line, const char *source);
CURL_EXTERN int curl_dbg_sclose(curl_socket_t sockfd,
int line, const char *source);
CURL_EXTERN curl_socket_t curl_dbg_accept(curl_socket_t s, void *a, void *alen,
int line, const char *source);
#ifdef HAVE_ACCEPT4
CURL_EXTERN curl_socket_t curl_dbg_accept4(curl_socket_t s, void *saddr,
void *saddrlen, int flags,
int line, const char *source);
#endif
#ifdef HAVE_SOCKETPAIR
CURL_EXTERN int curl_dbg_socketpair(int domain, int type, int protocol,
curl_socket_t socket_vector[2],
int line, const char *source);
#endif
/* send/receive sockets */
CURL_EXTERN SEND_TYPE_RETV curl_dbg_send(SEND_TYPE_ARG1 sockfd,
SEND_QUAL_ARG2 SEND_TYPE_ARG2 buf,
SEND_TYPE_ARG3 len,
SEND_TYPE_ARG4 flags, int line,
const char *source);
CURL_EXTERN RECV_TYPE_RETV curl_dbg_recv(RECV_TYPE_ARG1 sockfd,
RECV_TYPE_ARG2 buf,
RECV_TYPE_ARG3 len,
RECV_TYPE_ARG4 flags, int line,
const char *source);
/* FILE functions */
CURL_EXTERN int curl_dbg_fclose(FILE *file, int line, const char *source);
CURL_EXTERN ALLOC_FUNC
FILE *curl_dbg_fopen(const char *file, const char *mode,
int line, const char *source);
CURL_EXTERN ALLOC_FUNC
FILE *curl_dbg_fdopen(int filedes, const char *mode,
int line, const char *source);
#endif /* HEADER_CURL_MEMDEBUG_H_EXTERNS */
#ifdef CURLDEBUG
/* Set this symbol on the command-line, recompile all lib-sources */
#undef strdup
@ -166,12 +73,6 @@ CURL_EXTERN ALLOC_FUNC
__LINE__, __FILE__)
#endif
/* sclose is probably already defined, redefine it! */
#undef sclose
#define sclose(sockfd) curl_dbg_sclose(sockfd,__LINE__,__FILE__)
#define fake_sclose(sockfd) curl_dbg_mark_sclose(sockfd,__LINE__,__FILE__)
#undef fopen
#define fopen(file,mode) curl_dbg_fopen(file,mode,__LINE__,__FILE__)
#undef fdopen
@ -180,22 +81,4 @@ CURL_EXTERN ALLOC_FUNC
#define fclose(file) curl_dbg_fclose(file,__LINE__,__FILE__)
#endif /* CURLDEBUG */
/*
** Following section applies even when CURLDEBUG is not defined.
*/
#ifndef fake_sclose
#define fake_sclose(x) Curl_nop_stmt
#endif
/*
* Curl_safefree defined as a macro to allow MemoryTracking feature
* to log free() calls at same location where Curl_safefree is used.
* This macro also assigns NULL to given pointer when free'd.
*/
#define Curl_safefree(ptr) \
do { free((ptr)); (ptr) = NULL;} while(0)
#endif /* HEADER_CURL_MEMDEBUG_H */