diff --git a/docs/reference/glib/glib-sections.txt b/docs/reference/glib/glib-sections.txt index c771dd96e..015a64930 100644 --- a/docs/reference/glib/glib-sections.txt +++ b/docs/reference/glib/glib-sections.txt @@ -853,6 +853,12 @@ g_realloc g_try_malloc g_try_malloc0 g_try_realloc +g_malloc_n +g_malloc0_n +g_realloc_n +g_try_malloc_n +g_try_malloc0_n +g_try_realloc_n g_free diff --git a/docs/reference/glib/tmpl/memory.sgml b/docs/reference/glib/tmpl/memory.sgml index 90df4f882..c113e486f 100644 --- a/docs/reference/glib/tmpl/memory.sgml +++ b/docs/reference/glib/tmpl/memory.sgml @@ -39,6 +39,7 @@ g_mem_set_vtable(). Allocates @n_structs elements of type @struct_type. The returned pointer is cast to a pointer to the given type. If @n_structs is 0 it returns %NULL. +Care is taken to avoid overflow when calculating the size of the allocated block. Since the returned pointer is already casted to the right type, @@ -56,6 +57,7 @@ so might hide memory allocation errors. Allocates @n_structs elements of type @struct_type, initialized to 0's. The returned pointer is cast to a pointer to the given type. If @n_structs is 0 it returns %NULL. +Care is taken to avoid overflow when calculating the size of the allocated block. Since the returned pointer is already casted to the right type, @@ -73,6 +75,7 @@ so might hide memory allocation errors. Reallocates the memory pointed to by @mem, so that it now has space for @n_structs elements of type @struct_type. It returns the new address of the memory, which may have been moved. +Care is taken to avoid overflow when calculating the size of the allocated block. @struct_type: the type of the elements to allocate @@ -86,7 +89,7 @@ the memory, which may have been moved. Attempts to allocate @n_structs elements of type @struct_type, and returns %NULL on failure. Contrast with g_new(), which aborts the program on failure. The returned pointer is cast to a pointer to the given type. -If @n_structs is 0 it returns %NULL. +The function returns %NULL when @n_structs is 0 of if an overflow occurs. @struct_type: the type of the elements to allocate @@ -101,7 +104,7 @@ Attempts to allocate @n_structs elements of type @struct_type, initialized to 0's, and returns %NULL on failure. Contrast with g_new0(), which aborts the program on failure. The returned pointer is cast to a pointer to the given type. -The function returns %NULL when @n_structs is 0. +The function returns %NULL when @n_structs is 0 of if an overflow occurs. @struct_type: the type of the elements to allocate @@ -116,6 +119,7 @@ Attempts to reallocate the memory pointed to by @mem, so that it now has space for @n_structs elements of type @struct_type, and returns %NULL on failure. Contrast with g_renew(), which aborts the program on failure. It returns the new address of the memory, which may have been moved. +The function returns %NULL if an overflow occurs. @struct_type: the type of the elements to allocate @@ -192,6 +196,80 @@ on failure. If @mem is %NULL, behaves the same as g_try_malloc(). @Returns: the allocated memory, or %NULL. + + +This function is similar to g_malloc(), allocating (@n_blocks * @n_block_bytes) bytes, +but care is taken to detect possible overflow during multiplication. + + +@n_blocks: the number of blocks to allocate +@n_block_bytes: the size of each block in bytes +@Returns: a pointer to the allocated memory +@Since: 2.24 + + + + +This function is similar to g_malloc0(), allocating (@n_blocks * @n_block_bytes) bytes, +but care is taken to detect possible overflow during multiplication. + + +@n_blocks: the number of blocks to allocate +@n_block_bytes: the size of each block in bytes +@Returns: a pointer to the allocated memory +@Since: 2.24 + + + + +This function is similar to g_realloc(), allocating (@n_blocks * @n_block_bytes) bytes, +but care is taken to detect possible overflow during multiplication. + + +@mem: the memory to reallocate +@n_blocks: the number of blocks to allocate +@n_block_bytes: the size of each block in bytes +@Returns: the new address of the allocated memory +@Since: 2.24 + + + + +This function is similar to g_try_malloc(), allocating (@n_blocks * @n_block_bytes) bytes, +but care is taken to detect possible overflow during multiplication. + + +@n_blocks: the number of blocks to allocate +@n_block_bytes: the size of each block in bytes +@Returns: the allocated memory, or %NULL. +@Since: 2.24 + + + + +This function is similar to g_try_malloc0(), allocating (@n_blocks * @n_block_bytes) bytes, +but care is taken to detect possible overflow during multiplication. + + +@n_blocks: the number of blocks to allocate +@n_block_bytes: the size of each block in bytes +@Returns: the allocated memory, or %NULL +@Since: 2.24 + + + + +This function is similar to g_try_realloc(), allocating (@n_blocks * @n_block_bytes) bytes, +but care is taken to detect possible overflow during multiplication. + + +@mem: previously-allocated memory, or %NULL. +@n_blocks: the number of blocks to allocate +@n_block_bytes: the size of each block in bytes +@Returns: the allocated memory, or %NULL. +@Since: 2.24 + + Frees the memory pointed to by @mem. diff --git a/glib/glib.symbols b/glib/glib.symbols index 4baec9f8f..bc5d51475 100644 --- a/glib/glib.symbols +++ b/glib/glib.symbols @@ -720,13 +720,19 @@ g_markup_collect_attributes g_free g_malloc G_GNUC_MALLOC g_malloc0 G_GNUC_MALLOC +g_malloc_n G_GNUC_MALLOC +g_malloc0_n G_GNUC_MALLOC g_mem_is_system_malloc g_mem_profile g_mem_set_vtable g_realloc +g_realloc_n g_try_malloc G_GNUC_MALLOC g_try_malloc0 G_GNUC_MALLOC +g_try_malloc_n G_GNUC_MALLOC +g_try_malloc0_n G_GNUC_MALLOC g_try_realloc +g_try_realloc_n #ifndef G_DISABLE_DEPRECATED g_allocator_free g_allocator_new diff --git a/glib/gmem.c b/glib/gmem.c index 54d60082a..0f190fcd8 100644 --- a/glib/gmem.c +++ b/glib/gmem.c @@ -203,11 +203,11 @@ g_try_malloc (gsize n_bytes) gpointer g_try_malloc0 (gsize n_bytes) -{ +{ gpointer mem; mem = g_try_malloc (n_bytes); - + if (mem) memset (mem, 0, n_bytes); @@ -229,6 +229,97 @@ g_try_realloc (gpointer mem, return NULL; } + +#define SIZE_OVERFLOWS(a,b) (G_UNLIKELY ((a) > G_MAXSIZE / (b))) + +#undef g_malloc_n +gpointer +g_malloc_n (gsize n_blocks, + gsize n_block_bytes) +{ + if (SIZE_OVERFLOWS (n_blocks, n_block_bytes)) + { + if (G_UNLIKELY (!g_mem_initialized)) + g_mem_init_nomessage(); + + g_error ("%s: overflow allocating %"G_GSIZE_FORMAT"*%"G_GSIZE_FORMAT" bytes", + G_STRLOC, n_blocks, n_block_bytes); + } + + return g_malloc (n_blocks * n_block_bytes); +} + +#undef g_malloc0_n +gpointer +g_malloc0_n (gsize n_blocks, + gsize n_block_bytes) +{ + if (SIZE_OVERFLOWS (n_blocks, n_block_bytes)) + { + if (G_UNLIKELY (!g_mem_initialized)) + g_mem_init_nomessage(); + + g_error ("%s: overflow allocating %"G_GSIZE_FORMAT"*%"G_GSIZE_FORMAT" bytes", + G_STRLOC, n_blocks, n_block_bytes); + } + + return g_malloc0 (n_blocks * n_block_bytes); +} + +#undef g_realloc_n +gpointer +g_realloc_n (gpointer mem, + gsize n_blocks, + gsize n_block_bytes) +{ + if (SIZE_OVERFLOWS (n_blocks, n_block_bytes)) + { + if (G_UNLIKELY (!g_mem_initialized)) + g_mem_init_nomessage(); + + g_error ("%s: overflow allocating %"G_GSIZE_FORMAT"*%"G_GSIZE_FORMAT" bytes", + G_STRLOC, n_blocks, n_block_bytes); + } + + return g_realloc (mem, n_blocks * n_block_bytes); +} + +#undef g_try_malloc_n +gpointer +g_try_malloc_n (gsize n_blocks, + gsize n_block_bytes) +{ + if (SIZE_OVERFLOWS (n_blocks, n_block_bytes)) + return NULL; + + return g_try_malloc (n_blocks * n_block_bytes); +} + +#undef g_try_malloc0_n +gpointer +g_try_malloc0_n (gsize n_blocks, + gsize n_block_bytes) +{ + if (SIZE_OVERFLOWS (n_blocks, n_block_bytes)) + return NULL; + + return g_try_malloc0 (n_blocks * n_block_bytes); +} + +#undef g_try_realloc_n +gpointer +g_try_realloc_n (gpointer mem, + gsize n_blocks, + gsize n_block_bytes) +{ + if (SIZE_OVERFLOWS (n_blocks, n_block_bytes)) + return NULL; + + return g_try_realloc (mem, n_blocks * n_block_bytes); +} + + + static gpointer fallback_calloc (gsize n_blocks, gsize n_block_bytes) diff --git a/glib/gmem.h b/glib/gmem.h index 8cb050ee3..fc5d95dde 100644 --- a/glib/gmem.h +++ b/glib/gmem.h @@ -48,40 +48,102 @@ typedef struct _GMemVTable GMemVTable; /* Memory allocation functions */ + +void g_free (gpointer mem); + gpointer g_malloc (gsize n_bytes) G_GNUC_MALLOC G_GNUC_ALLOC_SIZE(1); gpointer g_malloc0 (gsize n_bytes) G_GNUC_MALLOC G_GNUC_ALLOC_SIZE(1); gpointer g_realloc (gpointer mem, gsize n_bytes) G_GNUC_WARN_UNUSED_RESULT; -void g_free (gpointer mem); gpointer g_try_malloc (gsize n_bytes) G_GNUC_MALLOC G_GNUC_ALLOC_SIZE(1); gpointer g_try_malloc0 (gsize n_bytes) G_GNUC_MALLOC G_GNUC_ALLOC_SIZE(1); gpointer g_try_realloc (gpointer mem, gsize n_bytes) G_GNUC_WARN_UNUSED_RESULT; +gpointer g_malloc_n (gsize n_blocks, + gsize n_block_bytes) G_GNUC_MALLOC G_GNUC_ALLOC_SIZE2(1,2); +gpointer g_malloc0_n (gsize n_blocks, + gsize n_block_bytes) G_GNUC_MALLOC G_GNUC_ALLOC_SIZE2(1,2); +gpointer g_realloc_n (gpointer mem, + gsize n_blocks, + gsize n_block_bytes) G_GNUC_WARN_UNUSED_RESULT; +gpointer g_try_malloc_n (gsize n_blocks, + gsize n_block_bytes) G_GNUC_MALLOC G_GNUC_ALLOC_SIZE2(1,2); +gpointer g_try_malloc0_n (gsize n_blocks, + gsize n_block_bytes) G_GNUC_MALLOC G_GNUC_ALLOC_SIZE2(1,2); +gpointer g_try_realloc_n (gpointer mem, + gsize n_blocks, + gsize n_block_bytes) G_GNUC_WARN_UNUSED_RESULT; + + +/* avoid the overflow check if we can determine at compile-time that no + * overflow happens. */ +#if defined (__GNUC__) && (__GNUC__ >= 2) && defined (__OPTIMIZE__) +# define _G_MALLOC_N(n_blocks, n_block_bytes, func_1, func_n) \ + (__extension__ ({ \ + gsize __a = (gsize) (n_blocks); \ + gsize __b = (gsize) (n_block_bytes); \ + gpointer __p; \ + if (__builtin_constant_p (__a) && __a == 1) \ + __p = func_1 (__b); \ + else if (__builtin_constant_p (__b) && __b == 1) \ + __p = func_1 (__a); \ + else if (__builtin_constant_p (__a) && \ + __builtin_constant_p (__b) && \ + __a <= G_MAXSIZE / __b) \ + __p = func_1 (__a * __b); \ + else \ + __p = func_n (__a, __b); \ + __p; \ + })) +# define _G_REALLOC_N(mem, n_blocks, n_block_bytes, func_1, func_n) \ + (__extension__ ({ \ + gsize __a = (gsize) (n_blocks); \ + gsize __b = (gsize) (n_block_bytes); \ + gpointer __p = (gpointer) (mem); \ + if (__builtin_constant_p (__a) && __a == 1) \ + __p = func_1 (__p, __b); \ + else if (__builtin_constant_p (__b) && __b == 1) \ + __p = func_1 (__p, __a); \ + else if (__builtin_constant_p (__a) && \ + __builtin_constant_p (__b) && \ + __a <= G_MAXSIZE / __b) \ + __p = func_1 (__p, __a * __b); \ + else \ + __p = func_n (__p, __a, __b); \ + __p; \ + })) + +# define g_malloc_n(n_blocks,n_block_bytes) _G_MALLOC_N (n_blocks, n_block_bytes, g_malloc, g_malloc_n) +# define g_malloc0_n(n_blocks,n_block_bytes) _G_MALLOC_N (n_blocks, n_block_bytes, g_malloc0, g_malloc0_n) +# define g_realloc_n(mem,n_blocks,n_block_bytes) _G_REALLOC_N (mem, n_blocks, n_block_bytes, g_realloc, g_realloc_n) +# define g_try_malloc_n(n_blocks,n_block_bytes) _G_MALLOC_N (n_blocks, n_block_bytes, g_try_malloc, g_try_malloc_n) +# define g_try_malloc0_n(n_blocks,n_block_bytes) _G_MALLOC_N (n_blocks, n_block_bytes, g_try_malloc0, g_try_malloc0_n) +# define g_try_realloc_n(mem,n_blocks,n_block_bytes) _G_REALLOC_N (mem, n_blocks, n_block_bytes, g_try_realloc, g_try_realloc_n) +#endif + /* Convenience memory allocators */ -#define g_new(struct_type, n_structs) \ - ((struct_type *) g_malloc (((gsize) sizeof (struct_type)) * ((gsize) (n_structs)))) -#define g_new0(struct_type, n_structs) \ - ((struct_type *) g_malloc0 (((gsize) sizeof (struct_type)) * ((gsize) (n_structs)))) -#define g_renew(struct_type, mem, n_structs) \ - ((struct_type *) g_realloc ((mem), ((gsize) sizeof (struct_type)) * ((gsize) (n_structs)))) -#define g_try_new(struct_type, n_structs) \ - ((struct_type *) g_try_malloc (((gsize) sizeof (struct_type)) * ((gsize) (n_structs)))) -#define g_try_new0(struct_type, n_structs) \ - ((struct_type *) g_try_malloc0 (((gsize) sizeof (struct_type)) * ((gsize) (n_structs)))) -#define g_try_renew(struct_type, mem, n_structs) \ - ((struct_type *) g_try_realloc ((mem), ((gsize) sizeof (struct_type)) * ((gsize) (n_structs)))) +#define _G_NEW(struct_type, n_structs, _g_malloc_n) \ + ((struct_type *) _g_malloc_n ((n_structs), sizeof (struct_type))) +#define _G_RENEW(struct_type, mem, n_structs, _g_realloc_n) \ + ((struct_type *) _g_realloc_n ((mem), (n_structs), sizeof (struct_type))) + +#define g_new(struct_type, n_structs) _G_NEW (struct_type, n_structs, g_malloc_n) +#define g_new0(struct_type, n_structs) _G_NEW (struct_type, n_structs, g_malloc0_n) +#define g_renew(struct_type, mem, n_structs) _G_RENEW (struct_type, mem, n_structs, g_realloc_n) +#define g_try_new(struct_type, n_structs) _G_NEW (struct_type, n_structs, g_try_malloc_n) +#define g_try_new0(struct_type, n_structs) _G_NEW (struct_type, n_structs, g_try_malloc0_n) +#define g_try_renew(struct_type, mem, n_structs) _G_RENEW (struct_type, mem, n_structs, g_try_realloc_n) /* Memory allocation virtualization for debugging purposes * g_mem_set_vtable() has to be the very first GLib function called * if being used */ -struct _GMemVTable -{ +struct _GMemVTable { gpointer (*malloc) (gsize n_bytes); gpointer (*realloc) (gpointer mem, gsize n_bytes); diff --git a/glib/tests/Makefile.am b/glib/tests/Makefile.am index c271891a3..1b0d940d3 100644 --- a/glib/tests/Makefile.am +++ b/glib/tests/Makefile.am @@ -50,6 +50,9 @@ hostutils_LDADD = $(progs_ldadd) TEST_PROGS += gvariant gvariant_LDADD = $(progs_ldadd) +TEST_PROGS += mem-overflow +mem_overflow_LDADD = $(progs_ldadd) + if OS_UNIX # some testing of gtester funcitonality diff --git a/glib/tests/mem-overflow.c b/glib/tests/mem-overflow.c new file mode 100644 index 000000000..f78c7b734 --- /dev/null +++ b/glib/tests/mem-overflow.c @@ -0,0 +1,108 @@ +/* Unit tests for g + * Copyright (C) 2010 Red Hat, Inc. + * + * This work is provided "as is"; redistribution and modification + * in whole or in part, in any medium, physical or electronic is + * permitted without restriction. + * + * This work is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * In no event shall the authors or contributors be liable for any + * direct, indirect, incidental, special, exemplary, or consequential + * damages (including, but not limited to, procurement of substitute + * goods or services; loss of use, data, or profits; or business + * interruption) however caused and on any theory of liability, whether + * in contract, strict liability, or tort (including negligence or + * otherwise) arising in any way out of the use of this software, even + * if advised of the possibility of such damage. + */ + +#include "glib.h" +#include + +static void +mem_overflow (void) +{ + gsize a = G_MAXSIZE / 10 + 10; + gsize b = 10; + gpointer p, q; + typedef char X[10]; + +#define CHECK_PASS(P) p = (P); g_assert (p == NULL); +#define CHECK_FAIL(P) p = (P); g_assert (p != NULL); + + CHECK_PASS (g_try_malloc_n (a, a)); + CHECK_PASS (g_try_malloc_n (a, b)); + CHECK_PASS (g_try_malloc_n (b, a)); + CHECK_FAIL (g_try_malloc_n (b, b)); + + CHECK_PASS (g_try_malloc0_n (a, a)); + CHECK_PASS (g_try_malloc0_n (a, b)); + CHECK_PASS (g_try_malloc0_n (b, a)); + CHECK_FAIL (g_try_malloc0_n (b, b)); + + q = g_malloc (1); + CHECK_PASS (g_try_realloc_n (q, a, a)); + CHECK_PASS (g_try_realloc_n (q, a, b)); + CHECK_PASS (g_try_realloc_n (q, b, a)); + CHECK_FAIL (g_try_realloc_n (q, b, b)); + free (p); + + CHECK_PASS (g_try_new (X, a)); + CHECK_FAIL (g_try_new (X, b)); + + CHECK_PASS (g_try_new0 (X, a)); + CHECK_FAIL (g_try_new0 (X, b)); + + q = g_try_malloc (1); + CHECK_PASS (g_try_renew (X, q, a)); + CHECK_FAIL (g_try_renew (X, q, b)); + free (p); + +#undef CHECK_EQ +#undef CHECK_NEQ + +#define CHECK_FAIL(P) if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDERR)) { p = (P); exit (0); } g_test_trap_assert_failed(); +#define CHECK_PASS(P) if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDERR)) { p = (P); exit (0); } g_test_trap_assert_passed(); + + CHECK_FAIL (g_malloc_n (a, a)); + CHECK_FAIL (g_malloc_n (a, b)); + CHECK_FAIL (g_malloc_n (b, a)); + CHECK_PASS (g_malloc_n (b, b)); + + CHECK_FAIL (g_malloc0_n (a, a)); + CHECK_FAIL (g_malloc0_n (a, b)); + CHECK_FAIL (g_malloc0_n (b, a)); + CHECK_PASS (g_malloc0_n (b, b)); + + q = g_malloc (1); + CHECK_FAIL (g_realloc_n (q, a, a)); + CHECK_FAIL (g_realloc_n (q, a, b)); + CHECK_FAIL (g_realloc_n (q, b, a)); + CHECK_PASS (g_realloc_n (q, b, b)); + free (q); + + CHECK_FAIL (g_new (X, a)); + CHECK_PASS (g_new (X, b)); + + CHECK_FAIL (g_new0 (X, a)); + CHECK_PASS (g_new0 (X, b)); + + q = g_malloc (1); + CHECK_FAIL (g_renew (X, q, a)); + CHECK_PASS (g_renew (X, q, b)); + free (q); +} + +int +main (int argc, + char *argv[]) +{ + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/mem/overflow", mem_overflow); + + return g_test_run(); +} diff --git a/glib/tests/printf.c b/glib/tests/printf.c index ff8eee275..a8ab0dc30 100644 --- a/glib/tests/printf.c +++ b/glib/tests/printf.c @@ -1,4 +1,4 @@ -/* Unit tests for gstring +/* Unit tests for gprintf * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald * * This work is provided "as is"; redistribution and modification diff --git a/glib/tests/rand.c b/glib/tests/rand.c index 0bddfbf4d..62b5f7597 100644 --- a/glib/tests/rand.c +++ b/glib/tests/rand.c @@ -1,4 +1,4 @@ -/* Unit tests for gstring +/* Unit tests for grand * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald * * This work is provided "as is"; redistribution and modification