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