From: Mark Wielaard Date: Fri, 12 Feb 2021 22:29:34 +0000 (+0100) Subject: PR217695 malloc/calloc/realloc/memalign failure doesn't set errno to ENOMEM X-Git-Tag: VALGRIND_3_17_0~57 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1c9a0bf58a47e855e6e5bf78a30bcee0af835804;p=thirdparty%2Fvalgrind.git 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 --- diff --git a/NEWS b/NEWS index eafe0cb009..69856917f6 100644 --- a/NEWS +++ b/NEWS @@ -53,6 +53,7 @@ where XXXXXX is the bug number as listed below. 140178 open("/proc/self/exe", ...); doesn't quite work 140939 --track-fds reports leakage of stdout/in/err and doesn't respect -q +217695 malloc/calloc/realloc/memalign failure doesn't set errno to ENOMEM 345077 linux syscall execveat support (linux 3.19) 369029 handle linux syscalls sched_getattr and sched_setattr n-i-bz helgrind: If hg_cli__realloc fails, return NULL. diff --git a/coregrind/m_replacemalloc/vg_replace_malloc.c b/coregrind/m_replacemalloc/vg_replace_malloc.c index a0e3aa398f..c3be5c6a81 100644 --- a/coregrind/m_replacemalloc/vg_replace_malloc.c +++ b/coregrind/m_replacemalloc/vg_replace_malloc.c @@ -192,6 +192,17 @@ static void init(void); if (info.clo_trace_malloc) { \ VALGRIND_INTERNAL_PRINTF(format, ## args ); } +/* Tries to set ERRNO to ENOMEM if possible. + Only implemented for glibc at the moment. +*/ +#if defined(VGO_linux) +extern int *__errno_location (void) __attribute__((weak)); +#define SET_ERRNO_ENOMEM if (__errno_location) \ + (*__errno_location ()) = VKI_ENOMEM; +#else +#define SET_ERRNO_ENOMEM {} +#endif + /* Below are new versions of malloc, __builtin_new, free, __builtin_delete, calloc, realloc, memalign, and friends. @@ -246,6 +257,7 @@ static void init(void); \ v = (void*)VALGRIND_NON_SIMD_CALL1( info.tl_##vg_replacement, n ); \ MALLOC_TRACE(" = %p\n", v ); \ + if (!v) SET_ERRNO_ENOMEM; \ return v; \ } @@ -752,6 +764,7 @@ static void init(void); if (umulHW(size, nmemb) != 0) return NULL; \ v = (void*)VALGRIND_NON_SIMD_CALL2( info.tl_calloc, nmemb, size ); \ MALLOC_TRACE(" = %p\n", v ); \ + if (!v) SET_ERRNO_ENOMEM; \ return v; \ } @@ -826,6 +839,7 @@ static void init(void); } \ v = (void*)VALGRIND_NON_SIMD_CALL2( info.tl_realloc, ptrV, new_size ); \ MALLOC_TRACE(" = %p\n", v ); \ + if (!v) SET_ERRNO_ENOMEM; \ return v; \ } @@ -899,6 +913,7 @@ static void init(void); \ v = (void*)VALGRIND_NON_SIMD_CALL2( info.tl_memalign, alignment, n ); \ MALLOC_TRACE(" = %p\n", v ); \ + if (!v) SET_ERRNO_ENOMEM; \ return v; \ } diff --git a/memcheck/tests/linux/Makefile.am b/memcheck/tests/linux/Makefile.am index 3111f631b4..1c53ffbaf8 100644 --- a/memcheck/tests/linux/Makefile.am +++ b/memcheck/tests/linux/Makefile.am @@ -29,7 +29,8 @@ EXTRA_DIST = \ getregset.stderr.exp getregset.stdout.exp \ sys-preadv_pwritev.vgtest sys-preadv_pwritev.stderr.exp \ sys-preadv2_pwritev2.vgtest sys-preadv2_pwritev2.stderr.exp \ - sys-execveat.vgtest sys-execveat.stderr.exp sys-execveat.stdout.exp + sys-execveat.vgtest sys-execveat.stderr.exp sys-execveat.stdout.exp \ + enomem.vgtest enomem.stderr.exp enomem.stdout.exp check_PROGRAMS = \ brk \ @@ -50,7 +51,8 @@ check_PROGRAMS = \ timerfd-syscall \ proc-auxv \ sys-execveat \ - check_execveat + check_execveat \ + enomem if HAVE_AT_FDCWD check_PROGRAMS += sys-openat diff --git a/memcheck/tests/linux/enomem.c b/memcheck/tests/linux/enomem.c new file mode 100644 index 0000000000..05cc08cd52 --- /dev/null +++ b/memcheck/tests/linux/enomem.c @@ -0,0 +1,34 @@ + +/* Test malloc, calloc, realloc and memalign set errno to ENOMEM */ + +#include +#include +#include +#include +#include + +int main ( void ) +{ + char* small = malloc (16); + char* p; + + errno = 0; + p = malloc(SSIZE_MAX); + if (!p && errno == ENOMEM) puts("malloc: Cannot allocate memory"); + + errno = 0; + p = calloc(1, SSIZE_MAX); + if (!p && errno == ENOMEM) puts("calloc: Cannot allocate memory"); + + errno = 0; + p = realloc(small, SSIZE_MAX); + if (!p && errno == ENOMEM) puts("realloc: Cannot allocate memory"); + + errno = 0; + p = memalign(64, SSIZE_MAX); + if (!p && errno == ENOMEM) puts("memalign: Cannot allocate memory"); + + free(small); + + return 0; +} diff --git a/memcheck/tests/linux/enomem.stderr.exp b/memcheck/tests/linux/enomem.stderr.exp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/memcheck/tests/linux/enomem.stdout.exp b/memcheck/tests/linux/enomem.stdout.exp new file mode 100644 index 0000000000..0c43a85a42 --- /dev/null +++ b/memcheck/tests/linux/enomem.stdout.exp @@ -0,0 +1,4 @@ +malloc: Cannot allocate memory +calloc: Cannot allocate memory +realloc: Cannot allocate memory +memalign: Cannot allocate memory diff --git a/memcheck/tests/linux/enomem.vgtest b/memcheck/tests/linux/enomem.vgtest new file mode 100644 index 0000000000..9a6b949279 --- /dev/null +++ b/memcheck/tests/linux/enomem.vgtest @@ -0,0 +1,2 @@ +prog: enomem +vgopts: -q