diff --git a/doc/fixes34.4 b/doc/fixes34.4 index eea2e2d95..b259db9e3 100644 --- a/doc/fixes34.4 +++ b/doc/fixes34.4 @@ -410,6 +410,9 @@ unlit candelabrum would become unlightable if its candles had exactly 1 turn temporary loss of Dex from wounded legs will become permanent if it occurs while mounted and hero dismounts before steed's legs have healed jaberwocks don't have hands +character escape sequence handling during options processing was vulernable + to malformed escapes and could potentially be abused to clobber the + stack and launch a buffer overrun attack Platform- and/or Interface-Specific Fixes diff --git a/src/options.c b/src/options.c index e36f91ca7..c4a696aa0 100644 --- a/src/options.c +++ b/src/options.c @@ -747,45 +747,47 @@ nmcpy(dest, src, maxlen) } /* - * escapes: escape expansion for showsyms. C-style escapes understood include - * \n, \b, \t, \r, \xnnn (hex), \onnn (octal), \nnn (decimal). The ^-prefix - * for control characters is also understood, and \[mM] followed by any of the - * previous forms or by a character has the effect of 'meta'-ing the value (so - * that the alternate character set will be enabled). + * escapes(): escape expansion for showsyms. C-style escapes understood + * include \n, \b, \t, \r, \xnnn (hex), \onnn (octal), \nnn (decimal). + * The ^-prefix for control characters is also understood, and \[mM] + * has the effect of 'meta'-ing the value which follows (so that the + * alternate character set will be enabled). + * + * For 3.4.3 and earlier, ending with "\M", backslash, or caret prior + * to terminating '\0' would pull that '\0' into the output and then + * keep processing past it. Now, a trailing escape will be handled + * as if it was preceded with its own backslash and be kept as is, + * with end of string terminator always honored as end of input. */ STATIC_OVL void escapes(cp, tp) const char *cp; char *tp; { - while (*cp) - { + while (*cp) { int cval = 0, meta = 0; - if (*cp == '\\' && index("mM", cp[1])) { + if (*cp == '\\' && cp[1] && index("mM", cp[1]) && cp[2]) { meta = 1; cp += 2; } - if (*cp == '\\' && index("0123456789xXoO", cp[1])) - { - const char *dp, *hex = "00112233445566778899aAbBcCdDeEfF"; + if (*cp == '\\' && cp[1] && index("0123456789xXoO", cp[1]) && cp[2]) { + NEARDATA const char hex[] = "00112233445566778899aAbBcCdDeEfF"; + const char *dp; int dcount = 0; cp++; if (*cp == 'x' || *cp == 'X') for (++cp; *cp && (dp = index(hex, *cp)) && (dcount++ < 2); cp++) - cval = (int)((cval * 16) + (dp - hex) / 2); + cval = (cval * 16) + ((int)(dp - hex) / 2); else if (*cp == 'o' || *cp == 'O') for (++cp; *cp && (index("01234567",*cp)) && (dcount++ < 3); cp++) cval = (cval * 8) + (*cp - '0'); else for (; *cp && (index("0123456789",*cp)) && (dcount++ < 3); cp++) cval = (cval * 10) + (*cp - '0'); - } - else if (*cp == '\\') /* C-style character escapes */ - { - switch (*++cp) - { + } else if (*cp == '\\' && cp[1]) { /* C-style character escapes */ + switch (*++cp) { case '\\': cval = '\\'; break; case 'n': cval = '\n'; break; case 't': cval = '\t'; break; @@ -794,14 +796,12 @@ char *tp; default: cval = *cp; } cp++; - } - else if (*cp == '^') /* expand control-character syntax */ - { + } else if (*cp == '^' && cp[1]) { /* expand control-character syntax */ cval = (*++cp & 0x1f); cp++; - } - else + } else cval = *cp++; + if (meta) cval |= 0x80; *tp++ = cval;