printf-format-fix.c revision ebe00087d3c7f9706d4acb9017eaad912404516c
183bea41fa640dc8117f3eb45ff935cd81377a84Timo Sirainen/* Copyright (c) 2002-2016 Dovecot authors, see the included COPYING file */
847aeef259d42e2f14cf126699e28291e6e1fb53Timo Sirainen
847aeef259d42e2f14cf126699e28291e6e1fb53Timo Sirainen#include "lib.h"
847aeef259d42e2f14cf126699e28291e6e1fb53Timo Sirainen#include "printf-format-fix.h"
847aeef259d42e2f14cf126699e28291e6e1fb53Timo Sirainen
65988f5a8abed57e9894fec77105941e046d3490Timo Sirainenstatic const char *
72388282bf6718c39af34cfcf51438910f9d62daTimo Sirainenfix_format_real(const char *fmt, const char *p, size_t *len_r)
847aeef259d42e2f14cf126699e28291e6e1fb53Timo Sirainen{
847aeef259d42e2f14cf126699e28291e6e1fb53Timo Sirainen const char *errstr;
847aeef259d42e2f14cf126699e28291e6e1fb53Timo Sirainen char *buf;
812ac1e2570c600a086c09b24d250224a822a97dTimo Sirainen size_t len1, len2, len3;
812ac1e2570c600a086c09b24d250224a822a97dTimo Sirainen
812ac1e2570c600a086c09b24d250224a822a97dTimo Sirainen i_assert((size_t)(p - fmt) < INT_MAX);
847aeef259d42e2f14cf126699e28291e6e1fb53Timo Sirainen i_assert(p[0] == '%' && p[1] == 'm');
812ac1e2570c600a086c09b24d250224a822a97dTimo Sirainen
812ac1e2570c600a086c09b24d250224a822a97dTimo Sirainen errstr = strerror(errno);
812ac1e2570c600a086c09b24d250224a822a97dTimo Sirainen
812ac1e2570c600a086c09b24d250224a822a97dTimo Sirainen /* we'll assume that there's only one %m in the format string.
812ac1e2570c600a086c09b24d250224a822a97dTimo Sirainen this simplifies the code and there's really no good reason to have
812ac1e2570c600a086c09b24d250224a822a97dTimo Sirainen it multiple times. Callers can trap this case themselves. */
812ac1e2570c600a086c09b24d250224a822a97dTimo Sirainen len1 = p - fmt;
812ac1e2570c600a086c09b24d250224a822a97dTimo Sirainen len2 = strlen(errstr);
812ac1e2570c600a086c09b24d250224a822a97dTimo Sirainen len3 = strlen(p + 2);
812ac1e2570c600a086c09b24d250224a822a97dTimo Sirainen
812ac1e2570c600a086c09b24d250224a822a97dTimo Sirainen /* @UNSAFE */
812ac1e2570c600a086c09b24d250224a822a97dTimo Sirainen buf = t_buffer_get(len1 + len2 + len3 + 1);
812ac1e2570c600a086c09b24d250224a822a97dTimo Sirainen memcpy(buf, fmt, len1);
812ac1e2570c600a086c09b24d250224a822a97dTimo Sirainen memcpy(buf + len1, errstr, len2);
812ac1e2570c600a086c09b24d250224a822a97dTimo Sirainen memcpy(buf + len1 + len2, p + 2, len3 + 1);
847aeef259d42e2f14cf126699e28291e6e1fb53Timo Sirainen
847aeef259d42e2f14cf126699e28291e6e1fb53Timo Sirainen *len_r = len1 + len2 + len3;
812ac1e2570c600a086c09b24d250224a822a97dTimo Sirainen return buf;
812ac1e2570c600a086c09b24d250224a822a97dTimo Sirainen}
812ac1e2570c600a086c09b24d250224a822a97dTimo Sirainen
812ac1e2570c600a086c09b24d250224a822a97dTimo Sirainenstatic const char *
812ac1e2570c600a086c09b24d250224a822a97dTimo Sirainenprintf_format_fix_noalloc(const char *format, size_t *len_r)
812ac1e2570c600a086c09b24d250224a822a97dTimo Sirainen{
812ac1e2570c600a086c09b24d250224a822a97dTimo Sirainen static const char *printf_skip_chars = "# -+'I.*0123456789hlLjzt";
1a1fcdbe27a8cee9a4c453a6b2f625a5be572a32Timo Sirainen /* as a tiny optimization keep the most commonly used conversion
1a1fcdbe27a8cee9a4c453a6b2f625a5be572a32Timo Sirainen modifiers first, so strchr() stops early. */
1a1fcdbe27a8cee9a4c453a6b2f625a5be572a32Timo Sirainen static const char *printf_allowed_conversions = "sudcioxXp%eEfFgGaA";
812ac1e2570c600a086c09b24d250224a822a97dTimo Sirainen const char *ret, *p, *p2;
812ac1e2570c600a086c09b24d250224a822a97dTimo Sirainen
812ac1e2570c600a086c09b24d250224a822a97dTimo Sirainen p = ret = format;
812ac1e2570c600a086c09b24d250224a822a97dTimo Sirainen while ((p2 = strchr(p, '%')) != NULL) {
812ac1e2570c600a086c09b24d250224a822a97dTimo Sirainen p = p2+1;
812ac1e2570c600a086c09b24d250224a822a97dTimo Sirainen while (*p != '\0' && strchr(printf_skip_chars, *p) != NULL)
812ac1e2570c600a086c09b24d250224a822a97dTimo Sirainen p++;
847aeef259d42e2f14cf126699e28291e6e1fb53Timo Sirainen
812ac1e2570c600a086c09b24d250224a822a97dTimo Sirainen if (*p == '\0') {
812ac1e2570c600a086c09b24d250224a822a97dTimo Sirainen i_panic("%% modifier missing in '%s'", format);
812ac1e2570c600a086c09b24d250224a822a97dTimo Sirainen } else if (strchr(printf_allowed_conversions, *p) != NULL) {
812ac1e2570c600a086c09b24d250224a822a97dTimo Sirainen /* allow & ignore */
847aeef259d42e2f14cf126699e28291e6e1fb53Timo Sirainen } else {
847aeef259d42e2f14cf126699e28291e6e1fb53Timo Sirainen switch (*p) {
812ac1e2570c600a086c09b24d250224a822a97dTimo Sirainen case 'n':
847aeef259d42e2f14cf126699e28291e6e1fb53Timo Sirainen i_panic("%%n modifier used");
847aeef259d42e2f14cf126699e28291e6e1fb53Timo Sirainen case 'm':
b014857be9961acf2d37ef7b76d941b20cc8c2d1Timo Sirainen if (ret != format)
b014857be9961acf2d37ef7b76d941b20cc8c2d1Timo Sirainen i_panic("%%m used twice");
b014857be9961acf2d37ef7b76d941b20cc8c2d1Timo Sirainen ret = fix_format_real(format, p-1, len_r);
b014857be9961acf2d37ef7b76d941b20cc8c2d1Timo Sirainen break;
b014857be9961acf2d37ef7b76d941b20cc8c2d1Timo Sirainen default:
b014857be9961acf2d37ef7b76d941b20cc8c2d1Timo Sirainen i_panic("Unsupported %%%c modifier", *p);
b014857be9961acf2d37ef7b76d941b20cc8c2d1Timo Sirainen }
b014857be9961acf2d37ef7b76d941b20cc8c2d1Timo Sirainen }
b014857be9961acf2d37ef7b76d941b20cc8c2d1Timo Sirainen p++;
b014857be9961acf2d37ef7b76d941b20cc8c2d1Timo Sirainen }
b014857be9961acf2d37ef7b76d941b20cc8c2d1Timo Sirainen
b014857be9961acf2d37ef7b76d941b20cc8c2d1Timo Sirainen if (ret == format)
847aeef259d42e2f14cf126699e28291e6e1fb53Timo Sirainen *len_r = p - format + strlen(p);
7c424aa51c956c628e3512055841aa2f9eef4833Timo Sirainen return ret;
f923659c0e5298263d80622c99f4dc4132b4675bTimo Sirainen}
847aeef259d42e2f14cf126699e28291e6e1fb53Timo Sirainen
812ac1e2570c600a086c09b24d250224a822a97dTimo Sirainenconst char *printf_format_fix_get_len(const char *format, size_t *len_r)
812ac1e2570c600a086c09b24d250224a822a97dTimo Sirainen{
2a3fc652e13a574ca14ff2405b5c29a59232db49Timo Sirainen const char *ret;
847aeef259d42e2f14cf126699e28291e6e1fb53Timo Sirainen
812ac1e2570c600a086c09b24d250224a822a97dTimo Sirainen ret = printf_format_fix_noalloc(format, len_r);
8bb360f9e5de1c25e4f875205bb06e8bf15dae14Timo Sirainen if (ret != format)
812ac1e2570c600a086c09b24d250224a822a97dTimo Sirainen t_buffer_alloc(*len_r + 1);
812ac1e2570c600a086c09b24d250224a822a97dTimo Sirainen return ret;
812ac1e2570c600a086c09b24d250224a822a97dTimo Sirainen}
812ac1e2570c600a086c09b24d250224a822a97dTimo Sirainen
812ac1e2570c600a086c09b24d250224a822a97dTimo Sirainenconst char *printf_format_fix(const char *format)
847aeef259d42e2f14cf126699e28291e6e1fb53Timo Sirainen{
812ac1e2570c600a086c09b24d250224a822a97dTimo Sirainen const char *ret;
b014857be9961acf2d37ef7b76d941b20cc8c2d1Timo Sirainen size_t len;
b014857be9961acf2d37ef7b76d941b20cc8c2d1Timo Sirainen
812ac1e2570c600a086c09b24d250224a822a97dTimo Sirainen ret = printf_format_fix_noalloc(format, &len);
812ac1e2570c600a086c09b24d250224a822a97dTimo Sirainen if (ret != format)
812ac1e2570c600a086c09b24d250224a822a97dTimo Sirainen t_buffer_alloc(len + 1);
812ac1e2570c600a086c09b24d250224a822a97dTimo Sirainen return ret;
812ac1e2570c600a086c09b24d250224a822a97dTimo Sirainen}
812ac1e2570c600a086c09b24d250224a822a97dTimo Sirainen
812ac1e2570c600a086c09b24d250224a822a97dTimo Sirainenconst char *printf_format_fix_unsafe(const char *format)
847aeef259d42e2f14cf126699e28291e6e1fb53Timo Sirainen{
812ac1e2570c600a086c09b24d250224a822a97dTimo Sirainen size_t len;
fab050cbfdf3da692441d2e2fb4b2a4c6ac9e0daTimo Sirainen
fab050cbfdf3da692441d2e2fb4b2a4c6ac9e0daTimo Sirainen return printf_format_fix_noalloc(format, &len);
847aeef259d42e2f14cf126699e28291e6e1fb53Timo Sirainen}
812ac1e2570c600a086c09b24d250224a822a97dTimo Sirainen