[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