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) {

Reply via email to