Bug 217695 - realloc failure doesn't set errno to ENOMEM
Summary: realloc failure doesn't set errno to ENOMEM
Status: RESOLVED FIXED
Alias: None
Product: valgrind
Classification: Developer tools
Component: general (show other bugs)
Version: 3.15 SVN
Platform: unspecified Linux
: NOR normal
Target Milestone: ---
Assignee: Mark Wielaard
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2009-12-07 11:28 UTC by Nicolas Sitbon
Modified: 2021-02-17 12:16 UTC (History)
2 users (show)

See Also:
Latest Commit:
Version Fixed In:
Sentry Crash Report:


Attachments
reproduce the problem (409 bytes, text/plain)
2009-12-07 11:28 UTC, Nicolas Sitbon
Details
Set (glibc) errno to ENOMEM when malloc/calloc/realloc/memalign fail (6.38 KB, text/plain)
2021-02-12 23:00 UTC, Mark Wielaard
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Nicolas Sitbon 2009-12-07 11:28:27 UTC
Created attachment 38894 [details]
reproduce the problem

All is in the title, when realloc fails because of memory error, valgrind doesn't set errno to ENOMEM (http://www.opengroup.org/onlinepubs/009695399/functions/realloc.html). Moreover, counter for bytes allocated becomes wrong when memory error happens.

I compile the program with those flags:
"gcc -Wall -Wextra -pedantic -std=c99 -D_XOPEN_SOURCE=600 -O0 -ggdb3 -o test_realloc test_realloc.c"

here is output of the program:

==26773== Memcheck, a memory error detector.
==26773== Copyright (C) 2002-2008, and GNU GPL'd, by Julian Seward et al.
==26773== Using LibVEX rev 1884, a library for dynamic binary translation.
==26773== Copyright (C) 2004-2008, and GNU GPL'd, by OpenWorks LLP.
==26773== Using valgrind-3.4.1-Debian, a dynamic binary instrumentation framework.
==26773== Copyright (C) 2000-2008, and GNU GPL'd, by Julian Seward et al.
==26773== For more details, rerun with: -v
==26773==
bad
==26773==
==26773== FILE DESCRIPTORS: 3 open at exit.
==26773== Open file descriptor 2: /dev/pts/0
==26773==    <inherited from parent>
==26773==
==26773== Open file descriptor 1: /dev/pts/0
==26773==    <inherited from parent>
==26773==
==26773== Open file descriptor 0: /dev/pts/0
==26773==    <inherited from parent>
==26773==
==26773==
==26773== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 7 from 1)
==26773== malloc/free: in use at exit: 0 bytes in 0 blocks.
==26773== malloc/free: 2 allocs, 2 frees, 9,223,372,036,854,775,827 bytes allocated.
==26773== For counts of detected errors, rerun with: -v
==26773== All heap blocks were freed -- no leaks are possible.

running valgrind with those flags :
"valgrind --leak-check=full --show-reachable=yes --track-origins=yes --track-fds=yes"

memory counter says that there is 2 allocs and 2 free and 9,223,372,036,854,775,827 bytes allocated, in fact, there is only 1 alloc/1 free and just 20 bytes allocated.

same problems with valgrind-3.5-debian.
Comment 1 David Timber 2020-03-31 10:40:35 UTC
Can be reproducible by calling `calloc()` with values that would result in overflow. All the dynamic memory allocation system calls(malloc, calloc, realloc) set `errno` to ENOMEM when they return NULL. This is POSIX compliant.

Granted, no one checks errno. We all assume that allocation failed when the functions return NULL. But there are use cases where errno is used for diagnostic purposes. That's how I found this bug anyway.

This bug is 11 years old. Would someone please at least give this bug a verdict? I'd take WONTFIX as an answer.
Comment 2 Mark Wielaard 2021-02-10 16:41:13 UTC
(In reply to Ashe David Goulding from comment #1)
> Can be reproducible by calling `calloc()` with values that would result in
> overflow. All the dynamic memory allocation system calls(malloc, calloc,
> realloc) set `errno` to ENOMEM when they return NULL. This is POSIX
> compliant.
> 
> Granted, no one checks errno. We all assume that allocation failed when the
> functions return NULL. But there are use cases where errno is used for
> diagnostic purposes. That's how I found this bug anyway.
> 
> This bug is 11 years old. Would someone please at least give this bug a
> verdict? I'd take WONTFIX as an answer.

In theory this (setting errno to ENOMEM on an allocation function returning NULL) could be supported. But it is not that easy to do correct without knowing how the libc the program is running against handles errno. In practice this is always glibc, so maybe we can try.

To show why this is non-trivial from errno(3):

       errno is defined by the ISO C standard to be a modifiable lvalue
       of type int, and must not be explicitly declared; errno may be a
       macro.

And glibc defines it as:

extern int *__errno_location (void) __THROW __attribute__ ((__const__));
#define errno (*__errno_location ())

Now the malloc intercepts actually run on the simulated cpu (see coregrind/m_replacemalloc/vg_replace_malloc.c) and could in theory use the above definition to set errno to ENOMEM in ALLOC_or_NULL (when NULL).

But that would make that vg_replace_malloc rely on a libc that has the magic __errno_location function.
Comment 3 Mark Wielaard 2021-02-12 23:00:17 UTC
Created attachment 135649 [details]
Set (glibc) errno to ENOMEM when malloc/calloc/realloc/memalign fail

This turned out to be less hard than I assumed. We already have a trick for calling functions which might only be defined in glibc. The idea is to define the symbol as weak in out preload library. It will then be NULL unless some other library defines it for real. Then we can simply call it when it is non-NULL (and only when one of the allocation functions returns NULL).
Comment 4 David Timber 2021-02-15 08:29:21 UTC
12 years! Thank you.
Comment 5 Mark Wielaard 2021-02-17 12:16:32 UTC
commit 1c9a0bf58a47e855e6e5bf78a30bcee0af835804
Author: Mark Wielaard <mark@klomp.org>
Date:   Fri Feb 12 23:29:34 2021 +0100

    PR217695 malloc/calloc/realloc/memalign failure doesn't set errno to ENOMEM
    
    When one of the allocation functions in vg_replace_malloc failed
    they return NULL, but didn't set errno. This is slightly tricky since
    errno is implementation defined and might be a macro. In the case of
    glibc ernno is defined as:
    
      extern int *__errno_location (void) __THROW __attribute__ ((__const__));
      #define errno (*__errno_location ())
    
    We can use the same trick as we use for __libc_freeres in
    coregrind/vg_preloaded.c. Define the function as "weak". This means
    it will only be defined if another library (glibc in this case)
    actually provides a definition. Otherwise it will be NULL.
    So we will only call it if it is defined and one of the allocation
    functions failed, returned NULL.
    
    Include a new linux only memcheck testcase, enomem.vgtest.
    
    https://bugs.kde.org/show_bug.cgi?id=217695