bug-bash
[Top][All Lists]
Advanced

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

bash printf has undefined behavior if user specifies invalid flags


From: Paul Eggert
Subject: bash printf has undefined behavior if user specifies invalid flags
Date: Tue, 06 Jul 2004 22:34:42 -0700
User-agent: Gnus/5.1006 (Gnus v5.10.6) Emacs/21.3 (gnu/linux)

Configuration Information [Automatically generated, do not change]:
Machine: sparc
OS: solaris2.8
Compiler: gcc
Compilation CFLAGS:  -DPROGRAM='bash' -DCONF_HOSTTYPE='sparc' 
-DCONF_OSTYPE='solaris2.8' -DCONF_MACHTYPE='sparc-sun-solaris2.8' 
-DCONF_VENDOR='sun' -DSHELL  -DHAVE_CONFIG_H  -I.  -I. -I./include -I./lib  -g 
-O2 -Wall -W -Wno-sign-compare -Wpointer-arith -Wstrict-prototypes 
-Wmissing-prototypes -Wmissing-noreturn -Wmissing-format-attribute
uname output: SunOS sic.twinsun.com 5.8 Generic_117350-04 sun4u sparc 
SUNW,UltraSPARC-IIi-Engine
Machine Type: sparc-sun-solaris2.8

Bash Version: 2.05b
Patch Level: 0
Release Status: release

Description:
        In a few cases, Bash 2.05b's printf implementation relies on
        undefined behavior within printf(), and this could in theory
        lead to core dumps or worse.

        For the POSIX spec, please see:

        http://www.opengroup.org/onlinepubs/000095399/functions/printf.html

        For example, POSIX says that the use of the # flag character has
        undefined behavior when combined with the d conversion
        specifier.

Repeat-By:
        printf "%#d\n" 0
        printf "%0s\n" 0
        printf "%.9c\n" 0
        printf "%'s\n" 0

        In all the above cases, Bash invokes printf() with
        conversion specifications that POSIX says leads to
        undefined behavior.

Fix:

The following patch assumes the printf.def patch I submitted on
2003-09-30 in
<http://lists.gnu.org/archive/html/bug-bash/2003-09/msg00498.html>.

2004-07-06  Paul Eggert  <eggert@cs.ucla.edu>

        * builtins/printf.def (printf_builtin):
        Don't invoke printf in such a way that
        undefined behavior results.  Print the offending conversion
        specification in this case.
        * tests/printf.tests:
        Add test cases that would provoke undefined behavior in printf();
        printf should reject them.
        * tests/printf.right: Update to match latest behavior.

===================================================================
RCS file: builtins/printf.def,v
retrieving revision 2.5.2.2
retrieving revision 2.5.2.3
diff -pu -a -r2.5.2.2 -r2.5.2.3
--- builtins/printf.def 2003/09/30 17:35:06     2.5.2.2
+++ builtins/printf.def 2004/07/07 05:17:55     2.5.2.3
@@ -144,6 +144,7 @@ printf_builtin (list)
   int have_fieldwidth, have_precision;
   intmax_t tw;
   char convch, thisch, nextch, *format, *modstart, *fmt, *start;
+  char ok[UCHAR_MAX + 1];
 
   conversion_error = 0;
   retval = EXECUTION_SUCCESS;
@@ -212,8 +213,38 @@ printf_builtin (list)
            }
 
          /* found format specification, skip to field width */
-         for (; *fmt && strchr(SKIP1, *fmt); ++fmt)
-           ;
+
+         memset (ok, 0, sizeof ok);
+         ok['b'] = ok['n'] = ok['q'] =
+#ifdef HAVE_PRINTF_A_FORMAT
+           ok['a'] = ok['A'] =
+#endif
+           ok['c'] = ok['d'] = ok['e'] = ok['E'] =
+           ok['f'] = ok['F'] = ok['g'] = ok['G'] = ok['i'] = ok['o'] =
+           ok['s'] = ok['u'] = ok['x'] = ok['X'] = 1;
+
+         for (;; fmt++)
+           switch (*fmt)
+             {
+             case '\'':
+#ifdef HAVE_PRINTF_A_FORMAT
+               ok['a'] = ok['A'] =
+#endif
+                 ok['c'] = ok['e'] = ok['E'] =
+                 ok['o'] = ok['s'] = ok['x'] = ok['X'] = 0;
+               break;
+             case '-': case '+': case ' ':
+               break;
+             case '#':
+               ok['c'] = ok['d'] = ok['i'] = ok['s'] = ok['u'] = 0;
+               break;
+             case '0':
+               ok['c'] = ok['s'] = 0;
+               break;
+             default:
+               goto no_more_flag_characters;
+             }
+       no_more_flag_characters:;
 
          /* Skip optional field width. */
          if (*fmt == '*')
@@ -230,6 +261,7 @@ printf_builtin (list)
          if (*fmt == '.')
            {
              ++fmt;
+             ok['c'] = 0;
              if (*fmt == '*')
                {
                  fmt++;
@@ -245,14 +277,22 @@ printf_builtin (list)
          modstart = fmt;
          while (*fmt && strchr (LENMODS, *fmt))
            fmt++;
-           
-         if (*fmt == 0)
-           {
-             builtin_error ("`%s': missing format character", start);
-             PRETURN (EXECUTION_FAILURE);
-           }
 
          convch = *fmt;
+
+         /* Don't output unrecognized conversion specifications;
+            instead print an error message and return a failure exit
+            status.  */
+         {
+           unsigned char conversion = convch;
+           if (! ok[conversion])
+             {
+               builtin_error ("`%.*s': invalid conversion specification",
+                              (int) (fmt + 1 - start), start);
+               PRETURN (EXECUTION_FAILURE);
+             }
+         }
+
          thisch = modstart[0];
          nextch = modstart[1];
          modstart[0] = convch;
@@ -403,12 +443,6 @@ printf_builtin (list)
                PF (f, p);
                break;
              }
-
-           /* We don't output unrecognized format characters; we print an
-              error message and return a failure exit status. */
-           default:
-             builtin_error ("`%c': invalid format character", convch);
-             PRETURN (EXECUTION_FAILURE);
            }
 
          modstart[0] = thisch;
===================================================================
RCS file: tests/printf.tests,v
retrieving revision 2.5.2.0
retrieving revision 2.5.2.1
diff -pu -a -r2.5.2.0 -r2.5.2.1
--- tests/printf.tests  2001/10/04 19:35:26     2.5.2.0
+++ tests/printf.tests  2004/07/07 05:17:55     2.5.2.1
@@ -206,6 +206,12 @@ printf "ab%Mcd\n"
 # this caused an infinite loop in older versions of printf
 printf "%y" 0
 
+# these had undefined behavior throubh bash-2.05b
+printf "%#d\n" 0
+printf "%0s\n" 0
+printf "%.9c\n" 0
+printf "%'s\n" 0
+
 # these should print a warning and `0', according to POSIX.2
 printf "%d\n" GNU
 printf "%o\n" GNU
===================================================================
RCS file: tests/printf.right,v
retrieving revision 2.5.2.0
retrieving revision 2.5.2.1
diff -pu -a -r2.5.2.0 -r2.5.2.1
--- tests/printf.right  2002/03/25 14:49:29     2.5.2.0
+++ tests/printf.right  2004/07/07 05:20:38     2.5.2.1
@@ -92,12 +92,16 @@ A7
 26
 ./printf.tests: line 201: printf: -v: invalid option
 printf: usage: printf format [arguments]
-./printf.tests: line 203: printf: `%10': missing format character
-./printf.tests: line 204: printf: `M': invalid format character
-ab./printf.tests: line 207: printf: `y': invalid format character
-./printf.tests: line 210: printf: GNU: invalid number
+./printf.tests: line 203: printf: `%10': invalid conversion specification
+./printf.tests: line 204: printf: `%M': invalid conversion specification
+ab./printf.tests: line 207: printf: `%y': invalid conversion specification
+./printf.tests: line 210: printf: `%#d': invalid conversion specification
+./printf.tests: line 211: printf: `%0s': invalid conversion specification
+./printf.tests: line 212: printf: `%.9c': invalid conversion specification
+./printf.tests: line 213: printf: `%'s': invalid conversion specification
+./printf.tests: line 216: printf: GNU: invalid number
 0
-./printf.tests: line 211: printf: GNU: invalid number
+./printf.tests: line 217: printf: GNU: invalid number
 0
 -
 (foo )(bar )




reply via email to

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