tech-userlevel archive

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

printf(1) and incomplete escape sequence



Hello folks,
I have discovered a possible edge-case in printf(1) usage when
providing as an argument just a single backslash (`\'). In that
case printf(1) try to print through the end of *argv[] leading to
print out the first environment variable, e.g.:

 | % /usr/bin/printf '\'
 | printf: unknown escape sequence `\'
 | ACRONYMDB=...

This happen because in printf.c:173 (-r1.37) conv_escape() is called
with a fmt that is just a `\0':

   169			/* find next format specification */
   170			for (fmt = format; (ch = *fmt++) != '\0';) {
   171				if (ch == '\\') {
   172					char c_ch;
   173					fmt = conv_escape(fmt, &c_ch);
   174					putchar(c_ch);
   175					continue;
   176				}

...and then in conv_escape() we do:

   426	static char *
   427	conv_escape(char *str, char *conv_ch)
   428	{
   429		char value;
   430		char ch;
   431		char num_buf[4], *num_end;
   432	
   433		ch = *str++;
   434	
   435		switch (ch) {
   ...		...
   472		default:
   473			warnx("unknown escape sequence `\\%c'", ch);
   474			rval = 1;
   475			value = ch;
   476			break;
   477		}
   478	
   479		*conv_ch = value;
   480		return str;
   481	}

So ch is actually `\0' and due the *str++ we "jump" over the `\0', so
when the execution come back to printf.c:174 no `\0' will be found and
we iterate pass through *argv[].


What should we do if we receive just a `printf '\''?

IIUC according POSIX this is unspecified behaviour and for consistency
I have treated it similarly to the `default' case for "unknown
escape sequence" (possible patch with a candidate commit message
attached in this email).


What do you think we should do? Any feedbacks/comments about it?


Thank you!
Fix possible out-of-bounds read for empty escape sequence (i.e. a `\' alone).

The single `\' is treated as `\\' and a warning is printed and the exit status
raised (like other unknown escape sequences).

Index: printf.c
===================================================================
RCS file: /cvsroot/src/usr.bin/printf/printf.c,v
retrieving revision 1.37
diff -u -p -r1.37 printf.c
--- printf.c	16 Jun 2015 22:54:10 -0000	1.37
+++ printf.c	2 Jul 2018 14:35:45 -0000
@@ -430,6 +430,14 @@ conv_escape(char *str, char *conv_ch)
 	char ch;
 	char num_buf[4], *num_end;
 
+	if (*str == '\0') {
+		warnx("incomplete escape sequence");
+		rval = 1;
+		value = '\\';
+		*conv_ch = value;
+		return str;
+	}
+
 	ch = *str++;
 
 	switch (ch) {


Home | Main Index | Thread Index | Old Index