printf-format-fix.c revision 92cb1ef4f68f48c4b58d3949edb952158024af9d
89a126810703c666309310d0f3189e9834d70b5bTimo Sirainen/* Copyright (c) 2002-2017 Dovecot authors, see the included COPYING file */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic const char *
e8bdf1be00aec45d0c6dd72ad9c8be02a3dfc778Timo Sirainenfix_format_real(const char *fmt, const char *p, size_t *len_r)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* we'll assume that there's only one %m in the format string.
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen this simplifies the code and there's really no good reason to have
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen it multiple times. Callers can trap this case themselves. */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* @UNSAFE */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic const char *
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenprintf_format_fix_noalloc(const char *format, size_t *len_r)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* NOTE: This function is overly strict in what it accepts. Some
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen format strings that are valid (and safe) in C99 will cause a panic
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen here. This is because we don't really need to support the weirdest
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen special cases, and we're also being extra careful not to pass
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen anything to the underlying libc printf, which might treat the string
b6a7e0a7899e7f5d60c23cdaa50e025e4c67d05fTimo Sirainen differently than us and unexpectedly handling it as %n. For example
b6a7e0a7899e7f5d60c23cdaa50e025e4c67d05fTimo Sirainen "%**%n" with glibc. */
b6a7e0a7899e7f5d60c23cdaa50e025e4c67d05fTimo Sirainen /* Allow only the standard C99 flags. There are also <'> and <I> flags,
b6a7e0a7899e7f5d60c23cdaa50e025e4c67d05fTimo Sirainen but we don't really need them. And at worst if they're not supported
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen by the underlying printf, they could potentially be used to work
b6a7e0a7899e7f5d60c23cdaa50e025e4c67d05fTimo Sirainen around our restrictions. */
b6a7e0a7899e7f5d60c23cdaa50e025e4c67d05fTimo Sirainen /* As a tiny optimization keep the most commonly used conversion
b6a7e0a7899e7f5d60c23cdaa50e025e4c67d05fTimo Sirainen specifiers first, so strchr() stops early. */
b6a7e0a7899e7f5d60c23cdaa50e025e4c67d05fTimo Sirainen static const char *printf_specifiers = "sudcixXpoeEfFgGaA";
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (*p == '%') {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* we'll be strict and allow %% only when there are no
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen optinal flags or modifiers. */
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen /* 1) zero or more flags. We'll add a further restriction that
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen each flag can be used only once, since there's no need to
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen use them more than once, and some implementations might
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen add their own limits. */
b6a7e0a7899e7f5d60c23cdaa50e025e4c67d05fTimo Sirainen bool printf_flags_seen[N_ELEMENTS(printf_flags)] = { FALSE, };
e9d29ae46d435aee85514decfe6ee27399ebf794Timo Sirainen while (*p != '\0' &&
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen i_panic("Duplicate %% flag '%c' starting at #%u in '%s'",
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* 2) Optional minimum field width */
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen if (*p == '*') {
ccc895c0358108d2304239063e940b7d75f364abTimo Sirainen /* We don't bother supporting "*m$" - it's not used
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen anywhere and seems a bit dangerous. */
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen /* Limit to 4 digits - we'll never want more than that.
e9d29ae46d435aee85514decfe6ee27399ebf794Timo Sirainen Some implementations might not handle long digits
e9d29ae46d435aee85514decfe6ee27399ebf794Timo Sirainen correctly, or maybe even could be used for DoS due
e9d29ae46d435aee85514decfe6ee27399ebf794Timo Sirainen to using too much CPU. */
e9d29ae46d435aee85514decfe6ee27399ebf794Timo Sirainen unsigned int i = 0;
b6a7e0a7899e7f5d60c23cdaa50e025e4c67d05fTimo Sirainen if (++i > 4) {
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen i_panic("Too large minimum field width starting at #%u in '%s'",
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* 3) Optional precision */
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen if (*p == '.') {
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen /* We don't bother supporting anything but numbers
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen here. 9999 should be long enough precision. */
83bb013a99f0936995f9c7a1077822662d8fefdbTimo Sirainen unsigned int i = 0;
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen if (++i > 4) {
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen i_panic("Too large precision starting at #%u in '%s'",
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* 4) Optional length modifier */
ae1b268ffff743ad9927c304a1344c5cbd7f909dTimo Sirainen switch (*p) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (*++p == 'h')
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen if (*++p == 'l')
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen /* 5) conversion specifier */
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen if (*p == '\0' || strchr(printf_specifiers, *p) == NULL) {
90adcaa0a00eba29b7fbd50ca66be11c8d086d6aTimo Sirainen switch (*p) {
3ec2c1f31631bb5ff86f5fc93a563c33e5cae90dTimo Sirainen i_panic("Missing %% specifier starting at #%u in '%s'",
3ec2c1f31631bb5ff86f5fc93a563c33e5cae90dTimo Sirainen i_panic("Unsupported 0x%02x specifier starting at #%u in '%s'",
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainenconst char *printf_format_fix_get_len(const char *format, size_t *len_r)
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen const char *ret;
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen ret = printf_format_fix_noalloc(format, len_r);
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainenconst char *printf_format_fix(const char *format)
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen const char *ret;
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainen ret = printf_format_fix_noalloc(format, &len);
c36ec256c1bd1abe1c12e792cf64f0b7e3b3135aTimo Sirainenconst char *printf_format_fix_unsafe(const char *format)