C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Deliberate memory leak

From: Frederick Virchanza Gotham <cauldwell.thomas_at_[hidden]>
Date: Thu, 20 Aug 2026 14:34:28 +0100
I'm top-posting because the below post is from two years ago, and I
want to revive this topic.

When I'm trying to find a memory leak, I use:
    - Address Sanitizer (very commonly)
    - Valgrind (very rarely)

I very rarely use Valgrind anymore because Address Sanitizer catches everything.

I think it would be good to have "std::leak" which can then be used by
both Valgrind and Address Sanitizer (or any other leak finder) to
avoid false positives, like this:

    int *p1 = (int*)malloc(1024);
    leak(p1);

    int *p2 = new int[64];
    leak(p2);

    int *p3 = new int;
    leak(p3);

My first idea was that 'leak' would use 'atexit' to deallocate memory
before the process dies. I looked into implementing the 'leak'
function, but if you look at 'p2' and 'p3', there's no way of
determining whether to use "delete" or "delete []" (and this can be
important when dealing with types that have a nontrivial destructor).
The following could have been an alternative:

    leak_malloc(p1);
    leak_new_array(p2);
    leak_new(p3);

But after looking into how Valgrind and Address Sanitizer work, I saw
that they won't report a leak if the address is kept in a global
variable. So I decided that the 'leak' function should just save the
addresses in a global array. Here's what I've got:

#define LEAK_MAX_COUNT 128u

#ifdef __cplusplus
 extern "C" {
#endif
 __attribute__((__weak__, __used__)) void const volatile *volatile
leak_addresses[LEAK_MAX_COUNT];
 __attribute__((__weak__ )) unsigned leak_counter;
#ifdef __cplusplus
 }
#endif

__attribute__((__unused__)) static inline void leak(void const
volatile *const p)
{
 unsigned const i = __atomic_fetch_add(&leak_counter, 1u, __ATOMIC_RELAXED);

 if ( i >= LEAK_MAX_COUNT )
 {
    fprintf(stdout, "leak(): more than %u deliberate leaks, you must
increase LEAK_MAX_COUNT. Killing the process.\n", LEAK_MAX_COUNT);
    fprintf(stderr, "leak(): more than %u deliberate leaks, you must
increase LEAK_MAX_COUNT. Killing the process.\n", LEAK_MAX_COUNT);
    _Exit(EXIT_FAILURE);
 }

 leak_addresses[i] = p;
}

This works for malloc, new and new[]. So maybe the Standard Library
should have "std::leak" that works something along these lines?





On Fri, Oct 11, 2024 at 12:11 PM Frederick Virchanza Gotham wrote:
>
> There are times when we allocate memory which we never intend to
> deallocate. For example, I'm writing a program at the moment that
> loads in plugins, and the plugins stay loaded until the end of the
> program.
>
> The only problem with this is that when you run a debugging tool to
> detect memory leaks, you get a load of false positives. I had a
> program before that linked with libpango, and I spent ages trying to
> find a memory leak, wondering if I was misusing the library or if the
> library internally had a bug, and I went the whole hog and joined the
> libpango mailing list and shared all my debugging output. In the end
> it turned out to be a benign memory leak -- i.e. the allocation
> happened once in the program and was intended to last the entire
> program.
>
> But what if we could do the following:
>
> void *p = malloc( 1024ul * 1024ul * 64ul );
> std::deliberate_leak(p);
>
> Or:
>
> long unsigned *p = new long unsigned[2048u];
> std::deliberate_leak(p);
>
> It could work something like this: https://godbolt.org/z/Gx6Pc6ne5
>
> And then when you compile in Release Mode, you don't bother deallocating.
>
> And here it is copy-pasted:
>
> #include <cstdlib> // abort, atexit, free
> #include <mutex> // lock_guard, mutex
>
> #define PRINT_TO_SCREEN // ------- You can comment this line out -------
>
> #ifdef PRINT_TO_SCREEN
> # include <cstdint> // uintptr_t
> # include <iomanip> // hex
> # include <iostream> // cout, endl
> #endif
>
> void DeliberateLeak(void const volatile *const arg) noexcept
> {
> constexpr unsigned capacity = 64u;
> static void const volatile *leaks[capacity] = {}; // all nullptr's
>
> static std::mutex m;
> std::lock_guard mylock(m);
>
> if ( nullptr == leaks[0u] )
> {
> std::atexit(
> [](void) noexcept -> void
> {
> for ( unsigned i = 0u; i < capacity; ++i )
> {
> if ( nullptr == leaks[i] ) return;
> std::free( const_cast<void*>(leaks[i]) );
> #ifdef PRINT_TO_SCREEN
> std::cout << "Freeing deliberately leaked memory
> at address 0x"
> << std::hex
> << reinterpret_cast<std::uintptr_t>(leaks[i])
> << std::endl;
> #endif
> }
> });
> }
>
> static unsigned index = 0u;
> if ( index >= capacity ) std::abort(); // no more free slots available
> leaks[index++] = arg;
> }
>
> int main(void)
> {
> void *p1 = std::malloc(1024ul * 1024ul * 64ul);
> DeliberateLeak(p1);
> void *p2 = std::malloc(1024ul * 1024ul * 64ul);
> DeliberateLeak(p2);
> }

Received on 2026-08-20 13:34:46