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