malloc: make malloc fail with requests larger than PTRDIFF_MAX (BZ#23741)

As discussed previously on libc-alpha [1], this patch follows up the idea
and add both the __attribute_alloc_size__ on malloc functions (malloc,
calloc, realloc, reallocarray, valloc, pvalloc, and memalign) and limit
maximum requested allocation size to up PTRDIFF_MAX (taking into
consideration internal padding and alignment).

This aligns glibc with gcc expected size defined by default warning
-Walloc-size-larger-than value which warns for allocation larger than
PTRDIFF_MAX.  It also aligns with gcc expectation regarding libc and
expected size, such as described in PR#67999 [2] and previously discussed
ISO C11 issues [3] on libc-alpha.

From the RFC thread [4] and previous discussion, it seems that consensus
is only to limit such requested size for malloc functions, not the system
allocation one (mmap, sbrk, etc.).

The implementation changes checked_request2size to check for both overflow
and maximum object size up to PTRDIFF_MAX. No additional checks are done
on sysmalloc, so it can still issue mmap with values larger than
PTRDIFF_T depending on the requested size.

The __attribute_alloc_size__ is for functions that return a pointer only,
which means it cannot be applied to posix_memalign (see remarks in GCC
PR#87683 [5]). The runtimes checks to limit maximum requested allocation
size does applies to posix_memalign.

Checked on x86_64-linux-gnu and i686-linux-gnu.

[1] https://sourceware.org/ml/libc-alpha/2018-11/msg00223.html
[2] https://gcc.gnu.org/bugzilla//show_bug.cgi?id=67999
[3] https://sourceware.org/ml/libc-alpha/2011-12/msg00066.html
[4] https://sourceware.org/ml/libc-alpha/2018-11/msg00224.html
[5] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87683

	[BZ #23741]
	* malloc/hooks.c (malloc_check, realloc_check): Use
	__builtin_add_overflow on overflow check and adapt to
	checked_request2size change.
	* malloc/malloc.c (__libc_malloc, __libc_realloc, _mid_memalign,
	__libc_pvalloc, __libc_calloc, _int_memalign): Limit maximum
	allocation size to PTRDIFF_MAX.
	(REQUEST_OUT_OF_RANGE): Remove macro.
	(checked_request2size): Change to inline function and limit maximum
	requested size to PTRDIFF_MAX.
	(__libc_malloc, __libc_realloc, _int_malloc, _int_memalign): Limit
	maximum allocation size to PTRDIFF_MAX.
	(_mid_memalign): Use _int_memalign call for overflow check.
	(__libc_pvalloc): Use __builtin_add_overflow on overflow check.
	(__libc_calloc): Use __builtin_mul_overflow for overflow check and
	limit maximum requested size to PTRDIFF_MAX.
	* malloc/malloc.h (malloc, calloc, realloc, reallocarray, memalign,
	valloc, pvalloc): Add __attribute_alloc_size__.
	* stdlib/stdlib.h (malloc, realloc, reallocarray, valloc): Likewise.
	* malloc/tst-malloc-too-large.c (do_test): Add check for allocation
	larger than PTRDIFF_MAX.
	* malloc/tst-memalign.c (do_test): Disable -Walloc-size-larger-than=
	around tests of malloc with negative sizes.
	* malloc/tst-posix_memalign.c (do_test): Likewise.
	* malloc/tst-pvalloc.c (do_test): Likewise.
	* malloc/tst-valloc.c (do_test): Likewise.
	* malloc/tst-reallocarray.c (do_test): Replace call to reallocarray
	with resulting size allocation larger than PTRDIFF_MAX with
	reallocarray_nowarn.
	(reallocarray_nowarn): New function.
	* NEWS: Mention the malloc function semantic change.
This commit is contained in:
Adhemerval Zanella 2018-12-18 16:30:56 -02:00
parent 52faba65f8
commit 9bf8e29ca1
12 changed files with 227 additions and 88 deletions

View File

@ -1,3 +1,37 @@
2019-04-18 Adhemerval Zanella <adhemerval.zanella@linaro.org>
[BZ #23741]
* malloc/hooks.c (malloc_check, realloc_check): Use
__builtin_add_overflow on overflow check and adapt to
checked_request2size change.
* malloc/malloc.c (__libc_malloc, __libc_realloc, _mid_memalign,
__libc_pvalloc, __libc_calloc, _int_memalign): Limit maximum
allocation size to PTRDIFF_MAX.
(REQUEST_OUT_OF_RANGE): Remove macro.
(checked_request2size): Change to inline function and limit maximum
requested size to PTRDIFF_MAX.
(__libc_malloc, __libc_realloc, _int_malloc, _int_memalign): Limit
maximum allocation size to PTRDIFF_MAX.
(_mid_memalign): Use _int_memalign call for overflow check.
(__libc_pvalloc): Use __builtin_add_overflow on overflow check.
(__libc_calloc): Use __builtin_mul_overflow for overflow check and
limit maximum requested size to PTRDIFF_MAX.
* malloc/malloc.h (malloc, calloc, realloc, reallocarray, memalign,
valloc, pvalloc): Add __attribute_alloc_size__.
* stdlib/stdlib.h (malloc, realloc, reallocarray, valloc): Likewise.
* malloc/tst-malloc-too-large.c (do_test): Add check for allocation
larger than PTRDIFF_MAX.
* malloc/tst-memalign.c (do_test): Disable -Walloc-size-larger-than=
around tests of malloc with negative sizes.
* malloc/tst-posix_memalign.c (do_test): Likewise.
* malloc/tst-pvalloc.c (do_test): Likewise.
* malloc/tst-valloc.c (do_test): Likewise.
* malloc/tst-reallocarray.c (do_test): Replace call to reallocarray
with resulting size allocation larger than PTRDIFF_MAX with
reallocarray_nowarn.
(reallocarray_nowarn): New function.
* NEWS: Mention the malloc function semantic change.
2019-04-17 Adhemerval Zanella <adhemerval.zanella@linaro.org> 2019-04-17 Adhemerval Zanella <adhemerval.zanella@linaro.org>
* sysdeps/powerpc/fpu/s_fma.c: Fix format. * sysdeps/powerpc/fpu/s_fma.c: Fix format.

6
NEWS
View File

@ -160,6 +160,12 @@ Deprecated and removed features, and other changes affecting compatibility:
as all functions that call vscanf, vfscanf, or vsscanf are annotated with as all functions that call vscanf, vfscanf, or vsscanf are annotated with
__attribute__ ((format (scanf, ...))). __attribute__ ((format (scanf, ...))).
* Memory allocation functions malloc, calloc, realloc, reallocarray, valloc,
pvalloc, memalign, and posix_memalign fail now with total object size
larger than PTRDIFF_MAX. This is to avoid potential undefined behavior with
pointer subtraction within the allocated object, where results might
overflow the ptrdiff_t type.
Changes to build and runtime requirements: Changes to build and runtime requirements:
* Python 3.4 or later is required to build the GNU C Library. * Python 3.4 or later is required to build the GNU C Library.

View File

@ -226,8 +226,9 @@ static void *
malloc_check (size_t sz, const void *caller) malloc_check (size_t sz, const void *caller)
{ {
void *victim; void *victim;
size_t nb;
if (sz + 1 == 0) if (__builtin_add_overflow (sz, 1, &nb))
{ {
__set_errno (ENOMEM); __set_errno (ENOMEM);
return NULL; return NULL;
@ -235,7 +236,7 @@ malloc_check (size_t sz, const void *caller)
__libc_lock_lock (main_arena.mutex); __libc_lock_lock (main_arena.mutex);
top_check (); top_check ();
victim = _int_malloc (&main_arena, sz + 1); victim = _int_malloc (&main_arena, nb);
__libc_lock_unlock (main_arena.mutex); __libc_lock_unlock (main_arena.mutex);
return mem2mem_check (victim, sz); return mem2mem_check (victim, sz);
} }
@ -268,8 +269,9 @@ realloc_check (void *oldmem, size_t bytes, const void *caller)
INTERNAL_SIZE_T nb; INTERNAL_SIZE_T nb;
void *newmem = 0; void *newmem = 0;
unsigned char *magic_p; unsigned char *magic_p;
size_t rb;
if (bytes + 1 == 0) if (__builtin_add_overflow (bytes, 1, &rb))
{ {
__set_errno (ENOMEM); __set_errno (ENOMEM);
return NULL; return NULL;
@ -289,7 +291,9 @@ realloc_check (void *oldmem, size_t bytes, const void *caller)
malloc_printerr ("realloc(): invalid pointer"); malloc_printerr ("realloc(): invalid pointer");
const INTERNAL_SIZE_T oldsize = chunksize (oldp); const INTERNAL_SIZE_T oldsize = chunksize (oldp);
checked_request2size (bytes + 1, nb); if (!checked_request2size (rb, &nb))
goto invert;
__libc_lock_lock (main_arena.mutex); __libc_lock_lock (main_arena.mutex);
if (chunk_is_mmapped (oldp)) if (chunk_is_mmapped (oldp))
@ -308,7 +312,7 @@ realloc_check (void *oldmem, size_t bytes, const void *caller)
{ {
/* Must alloc, copy, free. */ /* Must alloc, copy, free. */
top_check (); top_check ();
newmem = _int_malloc (&main_arena, bytes + 1); newmem = _int_malloc (&main_arena, rb);
if (newmem) if (newmem)
{ {
memcpy (newmem, oldmem, oldsize - 2 * SIZE_SZ); memcpy (newmem, oldmem, oldsize - 2 * SIZE_SZ);
@ -320,8 +324,6 @@ realloc_check (void *oldmem, size_t bytes, const void *caller)
else else
{ {
top_check (); top_check ();
INTERNAL_SIZE_T nb;
checked_request2size (bytes + 1, nb);
newmem = _int_realloc (&main_arena, oldp, oldsize, nb); newmem = _int_realloc (&main_arena, oldp, oldsize, nb);
} }
@ -334,6 +336,7 @@ realloc_check (void *oldmem, size_t bytes, const void *caller)
/* mem2chunk_check changed the magic byte in the old chunk. /* mem2chunk_check changed the magic byte in the old chunk.
If newmem is NULL, then the old chunk will still be used though, If newmem is NULL, then the old chunk will still be used though,
so we need to invert that change here. */ so we need to invert that change here. */
invert:
if (newmem == NULL) if (newmem == NULL)
*magic_p ^= 0xFF; *magic_p ^= 0xFF;
DIAG_POP_NEEDS_COMMENT; DIAG_POP_NEEDS_COMMENT;

View File

@ -1187,17 +1187,6 @@ nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
((uintptr_t)(MALLOC_ALIGNMENT == 2 * SIZE_SZ ? (p) : chunk2mem (p)) \ ((uintptr_t)(MALLOC_ALIGNMENT == 2 * SIZE_SZ ? (p) : chunk2mem (p)) \
& MALLOC_ALIGN_MASK) & MALLOC_ALIGN_MASK)
/*
Check if a request is so large that it would wrap around zero when
padded and aligned. To simplify some other code, the bound is made
low enough so that adding MINSIZE will also not wrap around zero.
*/
#define REQUEST_OUT_OF_RANGE(req) \
((unsigned long) (req) >= \
(unsigned long) (INTERNAL_SIZE_T) (-2 * MINSIZE))
/* pad request bytes into a usable size -- internal version */ /* pad request bytes into a usable size -- internal version */
#define request2size(req) \ #define request2size(req) \
@ -1205,21 +1194,18 @@ nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
MINSIZE : \ MINSIZE : \
((req) + SIZE_SZ + MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK) ((req) + SIZE_SZ + MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK)
/* Same, except also perform an argument and result check. First, we check /* Check if REQ overflows when padded and aligned and if the resulting value
that the padding done by request2size didn't result in an integer is less than PTRDIFF_T. Returns TRUE and the requested size or MINSIZE in
overflow. Then we check (using REQUEST_OUT_OF_RANGE) that the resulting case the value is less than MINSIZE on SZ or false if any of the previous
size isn't so large that a later alignment would lead to another integer check fail. */
overflow. */ static inline bool
#define checked_request2size(req, sz) \ checked_request2size (size_t req, size_t *sz) __nonnull (1)
({ \ {
(sz) = request2size (req); \ if (__glibc_unlikely (req > PTRDIFF_MAX))
if (((sz) < (req)) \ return false;
|| REQUEST_OUT_OF_RANGE (sz)) \ *sz = request2size (req);
{ \ return true;
__set_errno (ENOMEM); \ }
return 0; \
} \
})
/* /*
--------------- Physical chunk operations --------------- --------------- Physical chunk operations ---------------
@ -3037,6 +3023,9 @@ __libc_malloc (size_t bytes)
mstate ar_ptr; mstate ar_ptr;
void *victim; void *victim;
_Static_assert (PTRDIFF_MAX <= SIZE_MAX / 2,
"PTRDIFF_MAX is not more than half of SIZE_MAX");
void *(*hook) (size_t, const void *) void *(*hook) (size_t, const void *)
= atomic_forced_read (__malloc_hook); = atomic_forced_read (__malloc_hook);
if (__builtin_expect (hook != NULL, 0)) if (__builtin_expect (hook != NULL, 0))
@ -3044,7 +3033,11 @@ __libc_malloc (size_t bytes)
#if USE_TCACHE #if USE_TCACHE
/* int_free also calls request2size, be careful to not pad twice. */ /* int_free also calls request2size, be careful to not pad twice. */
size_t tbytes; size_t tbytes;
checked_request2size (bytes, tbytes); if (!checked_request2size (bytes, &tbytes))
{
__set_errno (ENOMEM);
return NULL;
}
size_t tc_idx = csize2tidx (tbytes); size_t tc_idx = csize2tidx (tbytes);
MAYBE_INIT_TCACHE (); MAYBE_INIT_TCACHE ();
@ -3181,7 +3174,11 @@ __libc_realloc (void *oldmem, size_t bytes)
&& !DUMPED_MAIN_ARENA_CHUNK (oldp)) && !DUMPED_MAIN_ARENA_CHUNK (oldp))
malloc_printerr ("realloc(): invalid pointer"); malloc_printerr ("realloc(): invalid pointer");
checked_request2size (bytes, nb); if (!checked_request2size (bytes, &nb))
{
__set_errno (ENOMEM);
return NULL;
}
if (chunk_is_mmapped (oldp)) if (chunk_is_mmapped (oldp))
{ {
@ -3291,13 +3288,6 @@ _mid_memalign (size_t alignment, size_t bytes, void *address)
return 0; return 0;
} }
/* Check for overflow. */
if (bytes > SIZE_MAX - alignment - MINSIZE)
{
__set_errno (ENOMEM);
return 0;
}
/* Make sure alignment is power of 2. */ /* Make sure alignment is power of 2. */
if (!powerof2 (alignment)) if (!powerof2 (alignment))
@ -3357,14 +3347,16 @@ __libc_pvalloc (size_t bytes)
void *address = RETURN_ADDRESS (0); void *address = RETURN_ADDRESS (0);
size_t pagesize = GLRO (dl_pagesize); size_t pagesize = GLRO (dl_pagesize);
size_t rounded_bytes = ALIGN_UP (bytes, pagesize); size_t rounded_bytes;
/* ALIGN_UP with overflow check. */
/* Check for overflow. */ if (__glibc_unlikely (__builtin_add_overflow (bytes,
if (bytes > SIZE_MAX - 2 * pagesize - MINSIZE) pagesize - 1,
&rounded_bytes)))
{ {
__set_errno (ENOMEM); __set_errno (ENOMEM);
return 0; return 0;
} }
rounded_bytes = rounded_bytes & -(pagesize - 1);
return _mid_memalign (pagesize, rounded_bytes, address); return _mid_memalign (pagesize, rounded_bytes, address);
} }
@ -3374,30 +3366,24 @@ __libc_calloc (size_t n, size_t elem_size)
{ {
mstate av; mstate av;
mchunkptr oldtop, p; mchunkptr oldtop, p;
INTERNAL_SIZE_T bytes, sz, csz, oldtopsize; INTERNAL_SIZE_T sz, csz, oldtopsize;
void *mem; void *mem;
unsigned long clearsize; unsigned long clearsize;
unsigned long nclears; unsigned long nclears;
INTERNAL_SIZE_T *d; INTERNAL_SIZE_T *d;
ptrdiff_t bytes;
/* size_t is unsigned so the behavior on overflow is defined. */ if (__glibc_unlikely (__builtin_mul_overflow (n, elem_size, &bytes)))
bytes = n * elem_size;
#define HALF_INTERNAL_SIZE_T \
(((INTERNAL_SIZE_T) 1) << (8 * sizeof (INTERNAL_SIZE_T) / 2))
if (__builtin_expect ((n | elem_size) >= HALF_INTERNAL_SIZE_T, 0))
{ {
if (elem_size != 0 && bytes / elem_size != n) __set_errno (ENOMEM);
{ return NULL;
__set_errno (ENOMEM);
return 0;
}
} }
sz = bytes;
void *(*hook) (size_t, const void *) = void *(*hook) (size_t, const void *) =
atomic_forced_read (__malloc_hook); atomic_forced_read (__malloc_hook);
if (__builtin_expect (hook != NULL, 0)) if (__builtin_expect (hook != NULL, 0))
{ {
sz = bytes;
mem = (*hook)(sz, RETURN_ADDRESS (0)); mem = (*hook)(sz, RETURN_ADDRESS (0));
if (mem == 0) if (mem == 0)
return 0; return 0;
@ -3405,8 +3391,6 @@ __libc_calloc (size_t n, size_t elem_size)
return memset (mem, 0, sz); return memset (mem, 0, sz);
} }
sz = bytes;
MAYBE_INIT_TCACHE (); MAYBE_INIT_TCACHE ();
if (SINGLE_THREAD_P) if (SINGLE_THREAD_P)
@ -3553,12 +3537,16 @@ _int_malloc (mstate av, size_t bytes)
Convert request size to internal form by adding SIZE_SZ bytes Convert request size to internal form by adding SIZE_SZ bytes
overhead plus possibly more to obtain necessary alignment and/or overhead plus possibly more to obtain necessary alignment and/or
to obtain a size of at least MINSIZE, the smallest allocatable to obtain a size of at least MINSIZE, the smallest allocatable
size. Also, checked_request2size traps (returning 0) request sizes size. Also, checked_request2size returns false for request sizes
that are so large that they wrap around zero when padded and that are so large that they wrap around zero when padded and
aligned. aligned.
*/ */
checked_request2size (bytes, nb); if (!checked_request2size (bytes, &nb))
{
__set_errno (ENOMEM);
return NULL;
}
/* There are no usable arenas. Fall back to sysmalloc to get a chunk from /* There are no usable arenas. Fall back to sysmalloc to get a chunk from
mmap. */ mmap. */
@ -4680,21 +4668,17 @@ _int_memalign (mstate av, size_t alignment, size_t bytes)
checked_request2size (bytes, nb); if (!checked_request2size (bytes, &nb))
{
__set_errno (ENOMEM);
return NULL;
}
/* /*
Strategy: find a spot within that chunk that meets the alignment Strategy: find a spot within that chunk that meets the alignment
request, and then possibly free the leading and trailing space. request, and then possibly free the leading and trailing space.
*/ */
/* Check for overflow. */
if (nb > SIZE_MAX - alignment - MINSIZE)
{
__set_errno (ENOMEM);
return 0;
}
/* Call malloc with worst case padding to hit alignment. */ /* Call malloc with worst case padding to hit alignment. */
m = (char *) (_int_malloc (av, nb + alignment + MINSIZE)); m = (char *) (_int_malloc (av, nb + alignment + MINSIZE));

View File

@ -35,11 +35,12 @@
__BEGIN_DECLS __BEGIN_DECLS
/* Allocate SIZE bytes of memory. */ /* Allocate SIZE bytes of memory. */
extern void *malloc (size_t __size) __THROW __attribute_malloc__ __wur; extern void *malloc (size_t __size) __THROW __attribute_malloc__
__attribute_alloc_size__ ((1)) __wur;
/* Allocate NMEMB elements of SIZE bytes each, all initialized to 0. */ /* Allocate NMEMB elements of SIZE bytes each, all initialized to 0. */
extern void *calloc (size_t __nmemb, size_t __size) extern void *calloc (size_t __nmemb, size_t __size)
__THROW __attribute_malloc__ __wur; __THROW __attribute_malloc__ __attribute_alloc_size__ ((1, 2)) __wur;
/* Re-allocate the previously allocated block in __ptr, making the new /* Re-allocate the previously allocated block in __ptr, making the new
block SIZE bytes long. */ block SIZE bytes long. */
@ -47,7 +48,7 @@ __THROW __attribute_malloc__ __wur;
the same pointer that was passed to it, aliasing needs to be allowed the same pointer that was passed to it, aliasing needs to be allowed
between objects pointed by the old and new pointers. */ between objects pointed by the old and new pointers. */
extern void *realloc (void *__ptr, size_t __size) extern void *realloc (void *__ptr, size_t __size)
__THROW __attribute_warn_unused_result__; __THROW __attribute_warn_unused_result__ __attribute_alloc_size__ ((2));
/* Re-allocate the previously allocated block in PTR, making the new /* Re-allocate the previously allocated block in PTR, making the new
block large enough for NMEMB elements of SIZE bytes each. */ block large enough for NMEMB elements of SIZE bytes each. */
@ -55,21 +56,23 @@ __THROW __attribute_warn_unused_result__;
the same pointer that was passed to it, aliasing needs to be allowed the same pointer that was passed to it, aliasing needs to be allowed
between objects pointed by the old and new pointers. */ between objects pointed by the old and new pointers. */
extern void *reallocarray (void *__ptr, size_t __nmemb, size_t __size) extern void *reallocarray (void *__ptr, size_t __nmemb, size_t __size)
__THROW __attribute_warn_unused_result__; __THROW __attribute_warn_unused_result__ __attribute_alloc_size__ ((2, 3));
/* Free a block allocated by `malloc', `realloc' or `calloc'. */ /* Free a block allocated by `malloc', `realloc' or `calloc'. */
extern void free (void *__ptr) __THROW; extern void free (void *__ptr) __THROW;
/* Allocate SIZE bytes allocated to ALIGNMENT bytes. */ /* Allocate SIZE bytes allocated to ALIGNMENT bytes. */
extern void *memalign (size_t __alignment, size_t __size) extern void *memalign (size_t __alignment, size_t __size)
__THROW __attribute_malloc__ __wur; __THROW __attribute_malloc__ __attribute_alloc_size__ ((2)) __wur;
/* Allocate SIZE bytes on a page boundary. */ /* Allocate SIZE bytes on a page boundary. */
extern void *valloc (size_t __size) __THROW __attribute_malloc__ __wur; extern void *valloc (size_t __size) __THROW __attribute_malloc__
__attribute_alloc_size__ ((1)) __wur;
/* Equivalent to valloc(minimum-page-that-holds(n)), that is, round up /* Equivalent to valloc(minimum-page-that-holds(n)), that is, round up
__size to nearest pagesize. */ __size to nearest pagesize. */
extern void *pvalloc (size_t __size) __THROW __attribute_malloc__ __wur; extern void *pvalloc (size_t __size) __THROW __attribute_malloc__
__attribute_alloc_size__ ((1)) __wur;
/* Underlying allocation function; successive calls should return /* Underlying allocation function; successive calls should return
contiguous pieces of memory. */ contiguous pieces of memory. */

View File

@ -72,13 +72,28 @@ test_large_allocations (size_t size)
void * ptr_to_realloc; void * ptr_to_realloc;
test_setup (); test_setup ();
DIAG_PUSH_NEEDS_COMMENT;
#if __GNUC_PREREQ (7, 0)
/* GCC 7 warns about too-large allocations; here we want to test
that they fail. */
DIAG_IGNORE_NEEDS_COMMENT (7, "-Walloc-size-larger-than=");
#endif
TEST_VERIFY (malloc (size) == NULL); TEST_VERIFY (malloc (size) == NULL);
#if __GNUC_PREREQ (7, 0)
DIAG_POP_NEEDS_COMMENT;
#endif
TEST_VERIFY (errno == ENOMEM); TEST_VERIFY (errno == ENOMEM);
ptr_to_realloc = malloc (16); ptr_to_realloc = malloc (16);
TEST_VERIFY_EXIT (ptr_to_realloc != NULL); TEST_VERIFY_EXIT (ptr_to_realloc != NULL);
test_setup (); test_setup ();
#if __GNUC_PREREQ (7, 0)
DIAG_IGNORE_NEEDS_COMMENT (7, "-Walloc-size-larger-than=");
#endif
TEST_VERIFY (realloc (ptr_to_realloc, size) == NULL); TEST_VERIFY (realloc (ptr_to_realloc, size) == NULL);
#if __GNUC_PREREQ (7, 0)
DIAG_POP_NEEDS_COMMENT;
#endif
TEST_VERIFY (errno == ENOMEM); TEST_VERIFY (errno == ENOMEM);
free (ptr_to_realloc); free (ptr_to_realloc);
@ -135,7 +150,13 @@ test_large_aligned_allocations (size_t size)
for (align = 1; align <= pagesize; align *= 2) for (align = 1; align <= pagesize; align *= 2)
{ {
test_setup (); test_setup ();
#if __GNUC_PREREQ (7, 0)
DIAG_IGNORE_NEEDS_COMMENT (7, "-Walloc-size-larger-than=");
#endif
TEST_VERIFY (memalign (align, size) == NULL); TEST_VERIFY (memalign (align, size) == NULL);
#if __GNUC_PREREQ (7, 0)
DIAG_POP_NEEDS_COMMENT;
#endif
TEST_VERIFY (errno == ENOMEM); TEST_VERIFY (errno == ENOMEM);
/* posix_memalign expects an alignment that is a power of 2 *and* a /* posix_memalign expects an alignment that is a power of 2 *and* a
@ -151,7 +172,13 @@ test_large_aligned_allocations (size_t size)
if ((size % align) == 0) if ((size % align) == 0)
{ {
test_setup (); test_setup ();
#if __GNUC_PREREQ (7, 0)
DIAG_IGNORE_NEEDS_COMMENT (7, "-Walloc-size-larger-than=");
#endif
TEST_VERIFY (aligned_alloc (align, size) == NULL); TEST_VERIFY (aligned_alloc (align, size) == NULL);
#if __GNUC_PREREQ (7, 0)
DIAG_POP_NEEDS_COMMENT;
#endif
TEST_VERIFY (errno == ENOMEM); TEST_VERIFY (errno == ENOMEM);
} }
} }
@ -159,11 +186,23 @@ test_large_aligned_allocations (size_t size)
/* Both valloc and pvalloc return page-aligned memory. */ /* Both valloc and pvalloc return page-aligned memory. */
test_setup (); test_setup ();
#if __GNUC_PREREQ (7, 0)
DIAG_IGNORE_NEEDS_COMMENT (7, "-Walloc-size-larger-than=");
#endif
TEST_VERIFY (valloc (size) == NULL); TEST_VERIFY (valloc (size) == NULL);
#if __GNUC_PREREQ (7, 0)
DIAG_POP_NEEDS_COMMENT;
#endif
TEST_VERIFY (errno == ENOMEM); TEST_VERIFY (errno == ENOMEM);
test_setup (); test_setup ();
#if __GNUC_PREREQ (7, 0)
DIAG_IGNORE_NEEDS_COMMENT (7, "-Walloc-size-larger-than=");
#endif
TEST_VERIFY (pvalloc (size) == NULL); TEST_VERIFY (pvalloc (size) == NULL);
#if __GNUC_PREREQ (7, 0)
DIAG_POP_NEEDS_COMMENT;
#endif
TEST_VERIFY (errno == ENOMEM); TEST_VERIFY (errno == ENOMEM);
} }
@ -226,6 +265,16 @@ do_test (void)
test_large_aligned_allocations (SIZE_MAX - i); test_large_aligned_allocations (SIZE_MAX - i);
} }
/* Allocation larger than PTRDIFF_MAX does play well with C standard,
since pointer subtraction within the object might overflow ptrdiff_t
resulting in undefined behavior. To prevent it malloc function fail
for such allocations. */
for (size_t i = 1; i <= FOURTEEN_ON_BITS; i++)
{
test_large_allocations (PTRDIFF_MAX + i);
test_large_aligned_allocations (PTRDIFF_MAX + i);
}
#if __WORDSIZE >= 64 #if __WORDSIZE >= 64
/* On 64-bit targets, we need to test a much wider range of too-large /* On 64-bit targets, we need to test a much wider range of too-large
sizes, so we test at intervals of (1 << 50) that allocation sizes sizes, so we test at intervals of (1 << 50) that allocation sizes

View File

@ -21,6 +21,7 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include <libc-diag.h>
static int errors = 0; static int errors = 0;
@ -41,9 +42,18 @@ do_test (void)
errno = 0; errno = 0;
DIAG_PUSH_NEEDS_COMMENT;
#if __GNUC_PREREQ (7, 0)
/* GCC 7 warns about too-large allocations; here we want to test
that they fail. */
DIAG_IGNORE_NEEDS_COMMENT (7, "-Walloc-size-larger-than=");
#endif
/* An attempt to allocate a huge value should return NULL and set /* An attempt to allocate a huge value should return NULL and set
errno to ENOMEM. */ errno to ENOMEM. */
p = memalign (sizeof (void *), -1); p = memalign (sizeof (void *), -1);
#if __GNUC_PREREQ (7, 0)
DIAG_POP_NEEDS_COMMENT;
#endif
save = errno; save = errno;

View File

@ -21,6 +21,7 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include <libc-diag.h>
static int errors = 0; static int errors = 0;
@ -41,9 +42,18 @@ do_test (void)
p = NULL; p = NULL;
DIAG_PUSH_NEEDS_COMMENT;
#if __GNUC_PREREQ (7, 0)
/* GCC 7 warns about too-large allocations; here we want to test
that they fail. */
DIAG_IGNORE_NEEDS_COMMENT (7, "-Walloc-size-larger-than=");
#endif
/* An attempt to allocate a huge value should return ENOMEM and /* An attempt to allocate a huge value should return ENOMEM and
p should remain NULL. */ p should remain NULL. */
ret = posix_memalign (&p, sizeof (void *), -1); ret = posix_memalign (&p, sizeof (void *), -1);
#if __GNUC_PREREQ (7, 0)
DIAG_POP_NEEDS_COMMENT;
#endif
if (ret != ENOMEM) if (ret != ENOMEM)
merror ("posix_memalign (&p, sizeof (void *), -1) succeeded."); merror ("posix_memalign (&p, sizeof (void *), -1) succeeded.");

View File

@ -21,6 +21,7 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include <libc-diag.h>
static int errors = 0; static int errors = 0;
@ -41,9 +42,18 @@ do_test (void)
errno = 0; errno = 0;
DIAG_PUSH_NEEDS_COMMENT;
#if __GNUC_PREREQ (7, 0)
/* GCC 7 warns about too-large allocations; here we want to test
that they fail. */
DIAG_IGNORE_NEEDS_COMMENT (7, "-Walloc-size-larger-than=");
#endif
/* An attempt to allocate a huge value should return NULL and set /* An attempt to allocate a huge value should return NULL and set
errno to ENOMEM. */ errno to ENOMEM. */
p = pvalloc (-1); p = pvalloc (-1);
#if __GNUC_PREREQ (7, 0)
DIAG_POP_NEEDS_COMMENT;
#endif
save = errno; save = errno;

View File

@ -20,6 +20,23 @@
#include <malloc.h> #include <malloc.h>
#include <string.h> #include <string.h>
#include <support/check.h> #include <support/check.h>
#include <libc-diag.h>
static void *
reallocarray_nowarn (void *ptr, size_t nmemb, size_t size)
{
#if __GNUC_PREREQ (7, 0)
/* GCC 7 warns about too-large allocations; here we want to test
that they fail. */
DIAG_IGNORE_NEEDS_COMMENT (7, "-Walloc-size-larger-than=");
#endif
void *ret = reallocarray (ptr, nmemb, size);
#if __GNUC_PREREQ (7, 0)
DIAG_POP_NEEDS_COMMENT;
#endif
return ret;
}
static int static int
do_test (void) do_test (void)
@ -34,24 +51,24 @@ do_test (void)
/* Test overflow detection. */ /* Test overflow detection. */
errno = 0; errno = 0;
ptr = reallocarray (NULL, max, 2); ptr = reallocarray_nowarn (NULL, max, 2);
TEST_VERIFY (!ptr); TEST_VERIFY (!ptr);
TEST_VERIFY (errno == ENOMEM); TEST_VERIFY (errno == ENOMEM);
errno = 0; errno = 0;
ptr = reallocarray (NULL, 2, max); ptr = reallocarray_nowarn (NULL, 2, max);
TEST_VERIFY (!ptr); TEST_VERIFY (!ptr);
TEST_VERIFY (errno == ENOMEM); TEST_VERIFY (errno == ENOMEM);
a = 65537; a = 65537;
b = max/65537 + 1; b = max/65537 + 1;
errno = 0; errno = 0;
ptr = reallocarray (NULL, a, b); ptr = reallocarray_nowarn (NULL, a, b);
TEST_VERIFY (!ptr); TEST_VERIFY (!ptr);
TEST_VERIFY (errno == ENOMEM); TEST_VERIFY (errno == ENOMEM);
errno = 0; errno = 0;
ptr = reallocarray (NULL, b, a); ptr = reallocarray_nowarn (NULL, b, a);
TEST_VERIFY (!ptr); TEST_VERIFY (!ptr);
TEST_VERIFY (errno == ENOMEM); TEST_VERIFY (errno == ENOMEM);
@ -97,7 +114,7 @@ do_test (void)
/* Overflow should leave buffer untouched. */ /* Overflow should leave buffer untouched. */
errno = 0; errno = 0;
ptr2 = reallocarray (ptr, 2, ~(size_t)0); ptr2 = reallocarray_nowarn (ptr, 2, ~(size_t)0);
TEST_VERIFY (!ptr2); TEST_VERIFY (!ptr2);
TEST_VERIFY (errno == ENOMEM); TEST_VERIFY (errno == ENOMEM);

View File

@ -21,6 +21,7 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include <libc-diag.h>
static int errors = 0; static int errors = 0;
@ -41,9 +42,18 @@ do_test (void)
errno = 0; errno = 0;
DIAG_PUSH_NEEDS_COMMENT;
#if __GNUC_PREREQ (7, 0)
/* GCC 7 warns about too-large allocations; here we want to test
that they fail. */
DIAG_IGNORE_NEEDS_COMMENT (7, "-Walloc-size-larger-than=");
#endif
/* An attempt to allocate a huge value should return NULL and set /* An attempt to allocate a huge value should return NULL and set
errno to ENOMEM. */ errno to ENOMEM. */
p = valloc (-1); p = valloc (-1);
#if __GNUC_PREREQ (7, 0)
DIAG_POP_NEEDS_COMMENT;
#endif
save = errno; save = errno;

View File

@ -536,10 +536,11 @@ extern int lcong48_r (unsigned short int __param[7],
#endif /* Use misc or X/Open. */ #endif /* Use misc or X/Open. */
/* Allocate SIZE bytes of memory. */ /* Allocate SIZE bytes of memory. */
extern void *malloc (size_t __size) __THROW __attribute_malloc__ __wur; extern void *malloc (size_t __size) __THROW __attribute_malloc__
__attribute_alloc_size__ ((1)) __wur;
/* Allocate NMEMB elements of SIZE bytes each, all initialized to 0. */ /* Allocate NMEMB elements of SIZE bytes each, all initialized to 0. */
extern void *calloc (size_t __nmemb, size_t __size) extern void *calloc (size_t __nmemb, size_t __size)
__THROW __attribute_malloc__ __wur; __THROW __attribute_malloc__ __attribute_alloc_size__ ((1, 2)) __wur;
/* Re-allocate the previously allocated block /* Re-allocate the previously allocated block
in PTR, making the new block SIZE bytes long. */ in PTR, making the new block SIZE bytes long. */
@ -547,7 +548,7 @@ extern void *calloc (size_t __nmemb, size_t __size)
the same pointer that was passed to it, aliasing needs to be allowed the same pointer that was passed to it, aliasing needs to be allowed
between objects pointed by the old and new pointers. */ between objects pointed by the old and new pointers. */
extern void *realloc (void *__ptr, size_t __size) extern void *realloc (void *__ptr, size_t __size)
__THROW __attribute_warn_unused_result__; __THROW __attribute_warn_unused_result__ __attribute_alloc_size__ ((2));
#ifdef __USE_MISC #ifdef __USE_MISC
/* Re-allocate the previously allocated block in PTR, making the new /* Re-allocate the previously allocated block in PTR, making the new
@ -556,7 +557,8 @@ extern void *realloc (void *__ptr, size_t __size)
the same pointer that was passed to it, aliasing needs to be allowed the same pointer that was passed to it, aliasing needs to be allowed
between objects pointed by the old and new pointers. */ between objects pointed by the old and new pointers. */
extern void *reallocarray (void *__ptr, size_t __nmemb, size_t __size) extern void *reallocarray (void *__ptr, size_t __nmemb, size_t __size)
__THROW __attribute_warn_unused_result__; __THROW __attribute_warn_unused_result__
__attribute_alloc_size__ ((2, 3));
#endif #endif
/* Free a block allocated by `malloc', `realloc' or `calloc'. */ /* Free a block allocated by `malloc', `realloc' or `calloc'. */
@ -569,7 +571,8 @@ extern void free (void *__ptr) __THROW;
#if (defined __USE_XOPEN_EXTENDED && !defined __USE_XOPEN2K) \ #if (defined __USE_XOPEN_EXTENDED && !defined __USE_XOPEN2K) \
|| defined __USE_MISC || defined __USE_MISC
/* Allocate SIZE bytes on a page boundary. The storage cannot be freed. */ /* Allocate SIZE bytes on a page boundary. The storage cannot be freed. */
extern void *valloc (size_t __size) __THROW __attribute_malloc__ __wur; extern void *valloc (size_t __size) __THROW __attribute_malloc__
__attribute_alloc_size__ ((1)) __wur;
#endif #endif
#ifdef __USE_XOPEN2K #ifdef __USE_XOPEN2K