[PATCH v2.1] Use saturated arithmetic for overflow detection.
Ondřej Bílka
neleai@seznam.cz
Tue Dec 3 11:16:00 GMT 2013
On Sat, Nov 30, 2013 at 03:46:46AM -0500, Mike Frysinger wrote:
> On Friday 01 November 2013 09:31:26 OndÅej BÃlka wrote:
> > --- a/malloc/malloc.c
> > +++ b/malloc/malloc.c
> >
> > + if (__builtin_expect (bytes == SIZE_MAX, 0)) {
>
> stick with glibc_likely & friends
> -mike
Here is new version, For now I omitted a system specific version for now
and files that will be touched by nmalloc patch.
* sysdeps/generic/saturated.h: New file.
* malloc/malloc.c (__libc_calloc): Use saturated arithmetic for
overflow detection.
* stdio-common/vfprintf.c (vfprintf): Likewise.
* string/strcoll_l.c (STRCOLL): Likewise.
* sysdeps/posix/getaddrinfo.c: Likewise.
---
malloc/malloc.c | 14 ++++------
stdio-common/vfprintf.c | 9 +++---
string/strcoll_l.c | 14 ++++------
sysdeps/generic/saturated.h | 64 +++++++++++++++++++++++++++++++++++++++++++
sysdeps/posix/getaddrinfo.c | 25 ++++++++---------
5 files changed, 90 insertions(+), 36 deletions(-)
create mode 100644 sysdeps/generic/saturated.h
diff --git a/malloc/malloc.c b/malloc/malloc.c
index 8977687..7f304dd 100644
--- a/malloc/malloc.c
+++ b/malloc/malloc.c
@@ -241,6 +241,7 @@
/* For MIN, MAX, powerof2. */
#include <sys/param.h>
+#include <saturated.h>
/*
Debugging:
@@ -3114,15 +3115,10 @@ __libc_calloc(size_t n, size_t elem_size)
unsigned long nclears;
INTERNAL_SIZE_T* d;
- /* size_t is unsigned so the behavior on overflow is defined. */
- 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 0;
- }
+ bytes = mul_s (n, elem_size);
+ if (__glibc_unlikely (bytes == SIZE_MAX)) {
+ __set_errno (ENOMEM);
+ return 0;
}
void *(*hook) (size_t, const void *) =
diff --git a/stdio-common/vfprintf.c b/stdio-common/vfprintf.c
index 8cd7a85..99ea5c8 100644
--- a/stdio-common/vfprintf.c
+++ b/stdio-common/vfprintf.c
@@ -25,6 +25,7 @@
#include <errno.h>
#include <wchar.h>
#include <bits/libc-lock.h>
+#include <saturated.h>
#include <sys/param.h>
#include <_itoa.h>
#include <locale/localeinfo.h>
@@ -1067,10 +1068,10 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap)
/* Allocate dynamically an array which definitely is long \
enough for the wide character version. Each byte in the \
multi-byte string can produce at most one wide character. */ \
- if (__libc_use_alloca (len * sizeof (wchar_t))) \
- string = (CHAR_T *) alloca (len * sizeof (wchar_t)); \
- else if ((string = (CHAR_T *) malloc (len * sizeof (wchar_t))) \
- == NULL) \
+ size_t needed = mul_s (len, sizeof (wchar_t)); \
+ if (__libc_use_alloca (needed)) \
+ string = (CHAR_T *) alloca (needed); \
+ else if ((string = (CHAR_T *) malloc (needed)) == NULL) \
{ \
done = -1; \
goto all_done; \
diff --git a/string/strcoll_l.c b/string/strcoll_l.c
index 5095e98..d448ece 100644
--- a/string/strcoll_l.c
+++ b/string/strcoll_l.c
@@ -25,6 +25,7 @@
#include <stdlib.h>
#include <string.h>
#include <sys/param.h>
+#include <saturated.h>
#ifndef STRING_TYPE
# define STRING_TYPE char
@@ -525,17 +526,12 @@ STRCOLL (const STRING_TYPE *s1, const STRING_TYPE *s2, __locale_t l)
memset (&seq1, 0, sizeof (seq1));
seq2 = seq1;
- size_t size_max = SIZE_MAX / (sizeof (int32_t) + 1);
+ size_t size_needed = mul_s (add_s (s1len, s2len), sizeof (int32_t) + 1);
- if (MIN (s1len, s2len) > size_max
- || MAX (s1len, s2len) > size_max - MIN (s1len, s2len))
- {
- /* If the strings are long enough to cause overflow in the size request,
- then skip the allocation and proceed with the non-cached routines. */
- }
- else if (! __libc_use_alloca ((s1len + s2len) * (sizeof (int32_t) + 1)))
+
+ if (!__libc_use_alloca (size_needed))
{
- seq1.idxarr = (int32_t *) malloc ((s1len + s2len) * (sizeof (int32_t) + 1));
+ seq1.idxarr = (int32_t *) malloc (size_needed);
/* If we failed to allocate memory, we leave everything as NULL so that
we use the nocache version of traversal and comparison functions. */
diff --git a/sysdeps/generic/saturated.h b/sysdeps/generic/saturated.h
new file mode 100644
index 0000000..74fef05
--- /dev/null
+++ b/sysdeps/generic/saturated.h
@@ -0,0 +1,64 @@
+/* Saturated unsigned arithmetic for size calculations - generic version.
+ Copyright (C) 2013 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library 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. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
+
+#ifndef _SATURATED_H
+#define _SATURATED_H
+
+static inline __attribute__((always_inline, unused)) size_t
+add_s (size_t x, size_t y)
+{
+ /* This check is recognized by gcc */
+ if (__glibc_unlikely (x + y < x))
+ return SIZE_MAX;
+ return x + y;
+}
+
+static inline __attribute__((always_inline, unused)) size_t
+mul_s (size_t x, size_t y)
+{
+ if (__builtin_constant_p (x))
+ {
+ size_t tmp = x;
+ x = y;
+ y = tmp;
+ }
+ if (__builtin_constant_p (y))
+ {
+ if (y == 0)
+ return 0;
+ if (__glibc_unlikely (x > SIZE_MAX / y))
+ return SIZE_MAX;
+ return x * y;
+ }
+ if (__glibc_unlikely (((x | y) >> (8 * sizeof (size_t) / 2)))
+ && y > SIZE_MAX / (x == 0 ? 1 : x))
+ return SIZE_MAX;
+ else
+ return x * y;
+}
+
+static inline __attribute__((always_inline, unused)) size_t
+div_s (size_t x, size_t y)
+{
+ if (__glibc_unlikely (x == SIZE_MAX))
+ return SIZE_MAX;
+ else
+ return x / y;
+}
+
+#endif
diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
index 8218237..9a6da94 100644
--- a/sysdeps/posix/getaddrinfo.c
+++ b/sysdeps/posix/getaddrinfo.c
@@ -63,6 +63,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <nscd/nscd-client.h>
#include <nscd/nscd_proto.h>
#include <resolv/res_hconf.h>
+#include <saturated.h>
#ifdef HAVE_LIBIDN
extern int __idna_to_ascii_lz (const char *input, char **output, int flags);
@@ -619,14 +620,12 @@ gaih_inet (const char *name, const struct gaih_service *service,
if (i > 0 && *pat != NULL)
--i;
- if (__libc_use_alloca (alloca_used
- + i * sizeof (struct gaih_addrtuple)))
- addrmem = alloca_account (i * sizeof (struct gaih_addrtuple),
- alloca_used);
+ size_t needed = mul_s (i, sizeof (struct gaih_addrtuple));
+ if (__libc_use_alloca (alloca_used + needed))
+ addrmem = alloca_account (needed, alloca_used);
else
{
- addrmem = malloc (i
- * sizeof (struct gaih_addrtuple));
+ addrmem = malloc (needed);
if (addrmem == NULL)
{
result = -EAI_MEMORY;
@@ -690,15 +689,13 @@ gaih_inet (const char *name, const struct gaih_service *service,
bool added_canon = (req->ai_flags & AI_CANONNAME) == 0;
char *addrs = air->addrs;
- if (__libc_use_alloca (alloca_used
- + air->naddrs * sizeof (struct gaih_addrtuple)))
- addrmem = alloca_account (air->naddrs
- * sizeof (struct gaih_addrtuple),
- alloca_used);
+ size_t needed = mul_s (air->naddrs, sizeof (struct gaih_addrtuple));
+
+ if (__libc_use_alloca (alloca_used + needed))
+ addrmem = alloca_account (needed, alloca_used);
else
{
- addrmem = malloc (air->naddrs
- * sizeof (struct gaih_addrtuple));
+ addrmem = malloc (needed);
if (addrmem == NULL)
{
result = -EAI_MEMORY;
@@ -2436,7 +2433,7 @@ getaddrinfo (const char *name, const char *service,
struct addrinfo *last = NULL;
char *canonname = NULL;
bool malloc_results;
- size_t alloc_size = nresults * (sizeof (*results) + sizeof (size_t));
+ size_t alloc_size = mul_s (nresults, (sizeof (*results) + sizeof (size_t)));
malloc_results
= !__libc_use_alloca (alloc_size);
--
1.7.10.4
More information about the Libc-alpha
mailing list