changeset 9029:3b5cd6b6e134

Avoid address wraparound inside system functions.
author Bruno Haible <bruno@clisp.org>
date Mon, 02 Jul 2007 00:56:18 +0000
parents 41c4b53b7797
children 870029929d86
files ChangeLog lib/sprintf.c lib/vsprintf.c modules/sprintf-posix modules/vsprintf-posix
diffstat 5 files changed, 33 insertions(+), 5 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2007-07-01  Bruno Haible  <bruno@clisp.org>
+
+	* lib/sprintf.c (sprintf): Limit the available length estimation,
+	to avoid address wraparound.
+	* lib/vsprintf.c (vsprintf): Likewise.
+	* modules/sprintf-posix (Dependencies): Add stdint.
+	* modules/vsprintf-posix (Dependencies): Likewise.
+
 2007-07-01  Bruno Haible <bruno@clisp.org>
 
 	* gnulib-tool (self_abspathname): Determine PATH_SEPARATOR and handle
--- a/lib/sprintf.c
+++ b/lib/sprintf.c
@@ -25,6 +25,7 @@
 #include <errno.h>
 #include <limits.h>
 #include <stdarg.h>
+#include <stdint.h>
 #include <stdlib.h>
 
 #include "vasnprintf.h"
@@ -46,10 +47,18 @@
 {
   char *output;
   size_t len;
+  size_t lenbuf;
+  va_list args;
+
   /* vasnprintf fails with EOVERFLOW when the buffer size argument is larger
-     than INT_MAX (if that fits into a 'size_t' at all).  */
-  size_t lenbuf = (SIZE_MAX < INT_MAX ? SIZE_MAX : INT_MAX);
-  va_list args;
+     than INT_MAX (if that fits into a 'size_t' at all).
+     Also note that glibc's iconv fails with E2BIG when we pass a length that
+     is so large that str + lenbuf wraps around, i.e.
+     (uintptr_t) (str + lenbuf) < (uintptr_t) str.
+     Therefore set lenbuf = min (SIZE_MAX, INT_MAX, - (uintptr_t) str - 1).  */
+  lenbuf = (SIZE_MAX < INT_MAX ? SIZE_MAX : INT_MAX);
+  if (lenbuf > ~ (uintptr_t) str)
+    lenbuf = ~ (uintptr_t) str;
 
   va_start (args, format);
   output = vasnprintf (str, &lenbuf, format, args);
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -25,6 +25,7 @@
 #include <errno.h>
 #include <limits.h>
 #include <stdarg.h>
+#include <stdint.h>
 #include <stdlib.h>
 
 #include "vasnprintf.h"
@@ -46,9 +47,17 @@
 {
   char *output;
   size_t len;
+  size_t lenbuf;
+
   /* vasnprintf fails with EOVERFLOW when the buffer size argument is larger
-     than INT_MAX (if that fits into a 'size_t' at all).  */
-  size_t lenbuf = (SIZE_MAX < INT_MAX ? SIZE_MAX : INT_MAX);
+     than INT_MAX (if that fits into a 'size_t' at all).
+     Also note that glibc's iconv fails with E2BIG when we pass a length that
+     is so large that str + lenbuf wraps around, i.e.
+     (uintptr_t) (str + lenbuf) < (uintptr_t) str.
+     Therefore set lenbuf = min (SIZE_MAX, INT_MAX, - (uintptr_t) str - 1).  */
+  lenbuf = (SIZE_MAX < INT_MAX ? SIZE_MAX : INT_MAX);
+  if (lenbuf > ~ (uintptr_t) str)
+    lenbuf = ~ (uintptr_t) str;
 
   output = vasnprintf (str, &lenbuf, format, args);
   len = lenbuf;
--- a/modules/sprintf-posix
+++ b/modules/sprintf-posix
@@ -17,6 +17,7 @@
 signbit
 fpucw
 printf-safe
+stdint
 
 configure.ac:
 gl_FUNC_SPRINTF_POSIX
--- a/modules/vsprintf-posix
+++ b/modules/vsprintf-posix
@@ -17,6 +17,7 @@
 signbit
 fpucw
 printf-safe
+stdint
 
 configure.ac:
 gl_FUNC_VSPRINTF_POSIX