[Top][All Lists]
[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 <address@hidden>
* 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 )
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- bash printf has undefined behavior if user specifies invalid flags,
Paul Eggert <=