m4-patches
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

head: Re: branch-1_4 allow cross-compiles; expose buffer overrun


From: Eric Blake
Subject: head: Re: branch-1_4 allow cross-compiles; expose buffer overrun
Date: Mon, 17 Jul 2006 07:25:27 -0600
User-agent: Thunderbird 1.5.0.4 (Windows/20060516)

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

According to Eric Blake on 6/29/2006 9:36 PM:
> $ M4_cv_have_efgcvt=no configure
> $ make
> ...
> $ echo 'format(%300d,1)'|src/m4
> Segmentation fault (core dumped)
>
> 2006-06-29  Eric Blake  <address@hidden>
> 
>       Fix buffer overrun bug.
>       * m4/gnulib-cache.m4: Augment with gnulib-tool --import
>       xvasprintf.
...

forward ported to head (where the buffer overflow was at 4096, instead of
256, characters; revision 1.9 of modules/format.c, in 2001).  It looks
like m4-1.4o (now 6 years old, but the last released snapshot of head) was
still using ecvt when possible, so the buffer overflow wasn't on all
platforms until revision 1.6 of modules/format.c (Aug 2001).

There is still a lot of work I would like to do on format.c, such as
issuing decent warnings on bad input, or recognizing newer modifiers like
%Lg or %j, but my first course of business is forward-porting all the
fixes from the branch.

2006-07-17  Eric Blake  <address@hidden>

        * ltdl/m4/gnulib-cache.m4: Augment with `gnulib-tool --import
        xvasprintf'.
        * modules/format.c (includes): Use xvasprintf.h.
        (format): Make static.  Avoid buffer overflow, that can lead to
        arbitrary code execution exploit.  Only pass unsigned char to
        is*().  Support F, g, and G specifiers.
        * doc/m4.texinfo (Format): Expose buffer overrun bug.  Document
        new specifiers.

- --
Life is short - so eat dessert first!

Eric Blake             address@hidden
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.2.1 (Cygwin)
Comment: Public key at home.comcast.net/~ericblake/eblake.gpg
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iD8DBQFEu4/G84KuGfSFAYARAkVHAJ9pLs5V+T/EMv/K5lnFMuBSg/HrLgCgsQiK
4OiiJ/TCGVsgSRy1jZel0JU=
=xJGh
-----END PGP SIGNATURE-----
? ltdl/m4/eoverflow.m4
? ltdl/m4/intmax_t.m4
? ltdl/m4/stdarg.m4
? ltdl/m4/vasnprintf.m4
? ltdl/m4/vasprintf.m4
? ltdl/m4/xvasprintf.m4
Index: doc/m4.texinfo
===================================================================
RCS file: /sources/m4/m4/doc/m4.texinfo,v
retrieving revision 1.26
diff -u -p -r1.26 m4.texinfo
--- doc/m4.texinfo      15 Jul 2006 21:35:37 -0000      1.26
+++ doc/m4.texinfo      17 Jul 2006 13:18:46 -0000
@@ -3464,6 +3464,10 @@ define(`foo', `The brown fox jumped over
 @result{}
 format(`The string "%s" uses %d characters', foo, len(foo))
 @result{}The string "The brown fox jumped over the lazy dog" uses 38 characters
+format(`%.0f', `56789.9876')
address@hidden
+len(format(`%-*X', `5000', `1'))
address@hidden
 @end example
 
 Using the @code{forloop} macro defined in @xref{Loops}, this
@@ -3488,11 +3492,14 @@ forloop(`i', 1, 10, `format(`%6d squared
 The builtin @code{format} is modeled after the ANSI C @samp{printf}
 function, and supports the normal @samp{%} specifiers: @samp{c},
 @samp{s}, @samp{d}, @samp{o}, @samp{x}, @samp{X}, @samp{u}, @samp{e},
address@hidden and @samp{f}; it supports field widths and precisions, and the
address@hidden, @samp{f}, @samp{F}, @samp{g}, and @samp{G}; it supports
+field widths and precisions, and the
 modifiers @samp{+}, @samp{-}, @address@hidden }}, @samp{0}, @samp{#}, @samp{h} 
and
 @samp{l}.  For more details on the functioning of @code{printf}, see the
 C Library Manual.
 
address@hidden FIXME - format still needs some improvements.
+
 @node Arithmetic
 @chapter Macros for doing arithmetic
 
Index: ltdl/m4/gnulib-cache.m4
===================================================================
RCS file: /sources/m4/m4/ltdl/m4/gnulib-cache.m4,v
retrieving revision 1.4
diff -u -p -r1.4 gnulib-cache.m4
--- ltdl/m4/gnulib-cache.m4     15 Jul 2006 21:35:37 -0000      1.4
+++ ltdl/m4/gnulib-cache.m4     17 Jul 2006 13:18:46 -0000
@@ -15,10 +15,10 @@
 
 
 # Specification in the form of a command-line invocation:
-#   gnulib-tool --import --dir=. --lib=libgnu --source-base=gnu 
--m4-base=ltdl/m4 --doc-base=doc --aux-dir=ltdl/config --libtool 
--macro-prefix=M4 assert error exit fdl free gendocs gettext mkstemp obstack 
progname regex stdbool strtol xalloc xalloc-die xstrndup
+#   gnulib-tool --import --dir=. --lib=libgnu --source-base=gnu 
--m4-base=ltdl/m4 --doc-base=doc --aux-dir=ltdl/config --libtool 
--macro-prefix=M4 assert error exit fdl free gendocs gettext mkstemp obstack 
progname regex stdbool strtol xalloc xalloc-die xstrndup xvasprintf
 
 # Specification in the form of a few gnulib-tool.m4 macro invocations:
-gl_MODULES([assert error exit fdl free gendocs gettext mkstemp obstack 
progname regex stdbool strtol xalloc xalloc-die xstrndup])
+gl_MODULES([assert error exit fdl free gendocs gettext mkstemp obstack 
progname regex stdbool strtol xalloc xalloc-die xstrndup xvasprintf])
 gl_AVOID([])
 gl_SOURCE_BASE([gnu])
 gl_M4_BASE([ltdl/m4])
Index: modules/format.c
===================================================================
RCS file: /sources/m4/m4/modules/format.c,v
retrieving revision 1.17
diff -u -p -r1.17 format.c
--- modules/format.c    1 May 2005 11:10:05 -0000       1.17
+++ modules/format.c    17 Jul 2006 13:18:46 -0000
@@ -1,5 +1,5 @@
 /* GNU m4 -- A simple macro processor
-   Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 2001
+   Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 2001, 2006
    Free Software Foundation, Inc.
 
    This program is free software; you can redistribute it and/or modify
@@ -20,7 +20,13 @@
 
 /* printf like formatting for m4.  */
 
-/* Simple varargs substitute.  */
+#include "xvasprintf.h"
+
+/* Simple varargs substitute.
+   TODO - warn if we use these because too many % specifiers were used in
+   relation to number of arguments passed.
+   TODO - use xstrtoimax, not atoi, to catch overflow, non-numeric
+   arguments, etc.  */
 
 #define ARG_INT(argc, argv) \
        ((argc == 0) ? 0 : \
@@ -50,9 +56,8 @@
 /* The main formatting function.  Output is placed on the obstack OBS, the
    first argument in ARGV is the formatting string, and the rest is
    arguments for the string.  */
-void format (m4_obstack *obs, int argc, m4_symbol_value **argv);
 
-void
+static void
 format (m4_obstack *obs, int argc, m4_symbol_value **argv)
 {
   char *fmt;                   /* format control string */
@@ -69,7 +74,7 @@ format (m4_obstack *obs, int argc, m4_sy
   char hflag;                  /* short flag */
 
   /* Buffer and stuff.  */
-  char str[4096];              /* buffer for formatted text */
+  char *str;                   /* malloc'd buffer for formatted text */
   enum {INT, UINT, LONG, ULONG, DOUBLE, STR} datatype;
 
   fmt = ARG_STR (argc, argv);
@@ -77,7 +82,7 @@ format (m4_obstack *obs, int argc, m4_sy
     {
       while ((c = *fmt++) != '%')
        {
-         if (c == 0)
+         if (c == '\0')
            return;
          obstack_1grow (obs, c);
        }
@@ -92,6 +97,8 @@ format (m4_obstack *obs, int argc, m4_sy
        }
 
       /* Parse flags.  */
+      /* TODO - borrow ideas from coreutils printf(1) for argument validation,
+        and for supporting specifiers for larger types.  */
       flags = 1;
       do
        {
@@ -118,13 +125,13 @@ format (m4_obstack *obs, int argc, m4_sy
          width = ARG_INT (argc, argv);
          fmt++;
        }
-      else if (isdigit ((int) *fmt))
+      else if (isdigit ((unsigned char) *fmt))
        {
          do
            {
              fmt++;
            }
-         while (isdigit ((int) *fmt));
+         while (isdigit ((unsigned char) *fmt));
        }
 
       /* Maximum precision.  */
@@ -136,13 +143,13 @@ format (m4_obstack *obs, int argc, m4_sy
              prec = ARG_INT (argc, argv);
              ++fmt;
            }
-         else if (isdigit ((int) *fmt))
+         else if (isdigit ((unsigned char) *fmt))
            {
              do
                {
                  fmt++;
                }
-             while (isdigit ((int) *fmt));
+             while (isdigit ((unsigned char) *fmt));
            }
        }
 
@@ -156,6 +163,7 @@ format (m4_obstack *obs, int argc, m4_sy
        {
 
        case '\0':
+         /* TODO - warn about incomplete % specifier.  */
          return;
 
        case 'c':
@@ -195,6 +203,9 @@ format (m4_obstack *obs, int argc, m4_sy
        case 'e':
        case 'E':
        case 'f':
+       case 'F':
+       case 'g':
+       case 'G':
          datatype = DOUBLE;
          break;
 
@@ -209,73 +220,79 @@ format (m4_obstack *obs, int argc, m4_sy
        {
        case INT:
          if (width != -1 && prec != -1)
-           sprintf (str, fstart, width, prec, ARG_INT(argc, argv));
+           str = xasprintf (fstart, width, prec, ARG_INT(argc, argv));
          else if (width != -1)
-           sprintf (str, fstart, width, ARG_INT(argc, argv));
+           str = xasprintf (fstart, width, ARG_INT(argc, argv));
          else if (prec != -1)
-           sprintf (str, fstart, prec, ARG_INT(argc, argv));
+           str = xasprintf (fstart, prec, ARG_INT(argc, argv));
          else
-           sprintf (str, fstart, ARG_INT(argc, argv));
+           str = xasprintf (fstart, ARG_INT(argc, argv));
          break;
 
        case UINT:
          if (width != -1 && prec != -1)
-           sprintf (str, fstart, width, prec, ARG_UINT(argc, argv));
+           str = xasprintf (fstart, width, prec, ARG_UINT(argc, argv));
          else if (width != -1)
-           sprintf (str, fstart, width, ARG_UINT(argc, argv));
+           str = xasprintf (fstart, width, ARG_UINT(argc, argv));
          else if (prec != -1)
-           sprintf (str, fstart, prec, ARG_UINT(argc, argv));
+           str = xasprintf (fstart, prec, ARG_UINT(argc, argv));
          else
-           sprintf (str, fstart, ARG_UINT(argc, argv));
+           str = xasprintf (fstart, ARG_UINT(argc, argv));
          break;
 
        case LONG:
          if (width != -1 && prec != -1)
-           sprintf (str, fstart, width, prec, ARG_LONG(argc, argv));
+           str = xasprintf (fstart, width, prec, ARG_LONG(argc, argv));
          else if (width != -1)
-           sprintf (str, fstart, width, ARG_LONG(argc, argv));
+           str = xasprintf (fstart, width, ARG_LONG(argc, argv));
          else if (prec != -1)
-           sprintf (str, fstart, prec, ARG_LONG(argc, argv));
+           str = xasprintf (fstart, prec, ARG_LONG(argc, argv));
          else
-           sprintf (str, fstart, ARG_LONG(argc, argv));
+           str = xasprintf (fstart, ARG_LONG(argc, argv));
          break;
 
        case ULONG:
          if (width != -1 && prec != -1)
-           sprintf (str, fstart, width, prec, ARG_ULONG(argc, argv));
+           str = xasprintf (fstart, width, prec, ARG_ULONG(argc, argv));
          else if (width != -1)
-           sprintf (str, fstart, width, ARG_ULONG(argc, argv));
+           str = xasprintf (fstart, width, ARG_ULONG(argc, argv));
          else if (prec != -1)
-           sprintf (str, fstart, prec, ARG_ULONG(argc, argv));
+           str = xasprintf (fstart, prec, ARG_ULONG(argc, argv));
          else
-           sprintf (str, fstart, ARG_ULONG(argc, argv));
+           str = xasprintf (fstart, ARG_ULONG(argc, argv));
          break;
 
        case DOUBLE:
          if (width != -1 && prec != -1)
-           sprintf (str, fstart, width, prec, ARG_DOUBLE(argc, argv));
+           str = xasprintf (fstart, width, prec, ARG_DOUBLE(argc, argv));
          else if (width != -1)
-           sprintf (str, fstart, width, ARG_DOUBLE(argc, argv));
+           str = xasprintf (fstart, width, ARG_DOUBLE(argc, argv));
          else if (prec != -1)
-           sprintf (str, fstart, prec, ARG_DOUBLE(argc, argv));
+           str = xasprintf (fstart, prec, ARG_DOUBLE(argc, argv));
          else
-           sprintf (str, fstart, ARG_DOUBLE(argc, argv));
+           str = xasprintf (fstart, ARG_DOUBLE(argc, argv));
          break;
 
        case STR:
          if (width != -1 && prec != -1)
-           sprintf (str, fstart, width, prec, ARG_STR(argc, argv));
+           str = xasprintf (fstart, width, prec, ARG_STR(argc, argv));
          else if (width != -1)
-           sprintf (str, fstart, width, ARG_STR(argc, argv));
+           str = xasprintf (fstart, width, ARG_STR(argc, argv));
          else if (prec != -1)
-           sprintf (str, fstart, prec, ARG_STR(argc, argv));
+           str = xasprintf (fstart, prec, ARG_STR(argc, argv));
          else
-           sprintf (str, fstart, ARG_STR(argc, argv));
+           str = xasprintf (fstart, ARG_STR(argc, argv));
          break;
        }
 
       *fmt = c;
 
+      /* NULL was returned on failure, such as invalid format string.  For
+        now, just silently ignore that bad specifier.  */
+      if (str == NULL)
+       continue;
+
       obstack_grow (obs, str, strlen (str));
+      free (str);
     }
 }

reply via email to

[Prev in Thread] Current Thread [Next in Thread]