Hi,
as reported in RHBZ#660033
( https://bugzilla.redhat.com/show_bug.cgi?id=660033 ), echo, printf and
stat allows 3 octal digits without limitation to 8-bit.
Documentation(manpages, info) refers to "byte with octal value" or
"8-bit octal value". Therefore 9-bit octal values should not be allowed.
Especially in echo, only unsigned char is used for storing this octal
number, so 9-bit values overflow.
I see two ways of fixing : a) change documentation (informing only 1-3
octal digits input, no "8-bit" or "byte" words)
                           b) accept only up to 8-bit octals

Because of the unsigned char overflow, I prefer the 8-bit limit - and I
did so in attached patch. As I don't expect this will be noticed by
anyone (probably most of users already limit these octals to 8-bit
independently), I didn't added NEWS entry. Test testing printf '\0610'
output is added. Previously it was interpreted as 392 and this was
passed to putchar(), after the patch it is interpreted as '\061' + '0'
=> 10 .
I have also added missing \NNN GNU extension to --help output of echo.

Greetings,
          Ondrej Vasik

>From 010b4fd670fa07e5e4e83ffd4076bea11516946e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ond=C5=99ej=20Va=C5=A1=C3=ADk?= <[email protected]>
Date: Mon, 6 Dec 2010 17:10:31 +0100
Subject: [PATCH] echo,printf,stat: Allow only 8-bit octal input for backslash-escaped chars

* src/echo.c: (usage) Document \NNN octal format in --help
              (main) Allow only 8-bit octal input for backslash-escaped chars
* src/printf.c: (print_esc) Likewise
* src/stat.c:   (print_it)  Likewise
* doc/coreutils.texi: Clarify printf documentation about byte octal input
* tests/misc/printf: Add test for "like 9-bit" octal value backslash-escaped
                     char
---
 doc/coreutils.texi |    6 +++---
 src/echo.c         |    7 ++++++-
 src/printf.c       |   10 +++++++---
 src/stat.c         |    4 +++-
 tests/misc/printf  |    3 +++
 5 files changed, 22 insertions(+), 8 deletions(-)

diff --git a/doc/coreutils.texi b/doc/coreutils.texi
index 34d9ff0..11a9d16 100644
--- a/doc/coreutils.texi
+++ b/doc/coreutils.texi
@@ -11116,9 +11116,9 @@ the command @samp{printf %g 3,14} is an error.
 @kindex \...@var{ooo}
 @kindex \...@var{hh}
 @command{printf} interprets @sam...@var{ooo}} in @var{format} as an octal number
-(if @var{ooo} is 1 to 3 octal digits) specifying a character to print,
-and @samp...@var{hh}} as a hexadecimal number (if @var{hh} is 1 to 2 hex
-digits) specifying a character to print.
+(if @var{ooo} is 1 to 3 octal digits byte value) specifying a character
+to print, and @samp...@var{hh}} as a hexadecimal number (if @var{hh} is
+1 to 2 hex digits) specifying a character to print.
 
 @kindex \uhhhh
 @kindex \Uhhhhhhhh
diff --git a/src/echo.c b/src/echo.c
index 6a1eeed..c7d6e77 100644
--- a/src/echo.c
+++ b/src/echo.c
@@ -78,6 +78,7 @@ If -e is in effect, the following sequences are recognized:\n\
 "), stdout);
       fputs (_("\
   \\0NNN   byte with octal value NNN (1 to 3 digits)\n\
+  \\NNN    byte with octal value NNN (1 to 3 digits)\n\
   \\xHH    byte with hexadecimal value HH (1 to 2 digits)\n\
 "), stdout);
       printf (USAGE_BUILTIN_WARNING, PROGRAM_NAME);
@@ -197,6 +198,7 @@ just_echo:
         {
           char const *s = argv[0];
           unsigned char c;
+          bool threeoctdigits = false;
 
           while ((c = *s++))
             {
@@ -235,12 +237,15 @@ just_echo:
                       c = *s++;
                       /* Fall through.  */
                     case '1': case '2': case '3':
+                      if ('0' <= c && c <= '3')
+                        threeoctdigits = true;
                     case '4': case '5': case '6': case '7':
                       c -= '0';
                       if ('0' <= *s && *s <= '7')
                         c = c * 8 + (*s++ - '0');
-                      if ('0' <= *s && *s <= '7')
+                      if (threeoctdigits && '0' <= *s && *s <= '7')
                         c = c * 8 + (*s++ - '0');
+                      threeoctdigits = false;
                       break;
                     case '\\': break;
 
diff --git a/src/printf.c b/src/printf.c
index 1f5451e..2530bef 100644
--- a/src/printf.c
+++ b/src/printf.c
@@ -236,6 +236,7 @@ print_esc (const char *escstart, bool octal_0)
   const char *p = escstart + 1;
   int esc_value = 0;		/* Value of \nnn escape. */
   int esc_length;		/* Length of \nnn escape. */
+  int max_octdigits = 3;        /* Maximum length of byte octal input. */
 
   if (*p == 'x')
     {
@@ -252,9 +253,12 @@ print_esc (const char *escstart, bool octal_0)
     {
       /* Parse \0ooo (if octal_0 && *p == '0') or \ooo (otherwise).
          Allow \ooo if octal_0 && *p != '0'; this is an undocumented
-         extension to POSIX that is compatible with Bash 2.05b.  */
-      for (esc_length = 0, p += octal_0 && *p == '0';
-           esc_length < 3 && isodigit (*p);
+         extension to POSIX that is compatible with Bash 2.05b.
+         If the octal character begins with number 4 or higher,
+         only 2 octal digits fit to byte */
+      for (esc_length = 0, p += octal_0 && *p == '0',
+           max_octdigits -= ('4' <= *p && *p <= '7');
+           esc_length < max_octdigits && isodigit (*p);
            ++esc_length, ++p)
         esc_value = esc_value * 8 + octtobin (*p);
       putchar (esc_value);
diff --git a/src/stat.c b/src/stat.c
index b419f19..69593be 100644
--- a/src/stat.c
+++ b/src/stat.c
@@ -1096,7 +1096,9 @@ print_it (char const *format, char const *filename,
             {
               int esc_value = octtobin (*b);
               int esc_length = 1;	/* number of octal digits */
-              for (++b; esc_length < 3 && isodigit (*b);
+              int max_octdigits = 3;    /* maximum length of byte octal input */
+              for (max_octdigits -= ('4' <= *b && *b <= '7'), ++b;
+                   esc_length < max_octdigits && isodigit (*b);
                    ++esc_length, ++b)
                 {
                   esc_value = esc_value * 8 + octtobin (*b);
diff --git a/tests/misc/printf b/tests/misc/printf
index b02352b..9c99853 100755
--- a/tests/misc/printf
+++ b/tests/misc/printf
@@ -72,6 +72,8 @@ POSIXLY_CORRECT=1 \
 
 "$prog" '11 %*c\n' 2 x >>out || fail=1
 
+"$prog" '12 \0610\n' >>out || fail=1
+
 "$prog" '%#d\n' 0 >>out 2> /dev/null && fail=1
 
 "$prog" '%0s\n' 0 >>out 2> /dev/null && fail=1
@@ -92,6 +94,7 @@ cat <<\EOF > exp
 9 0 x
 10 0x
 11  x
+12 10
 EOF
 
 compare out exp || fail=1
-- 
1.7.1

Reply via email to