printf-format-fix.c revision ebe00087d3c7f9706d4acb9017eaad912404516c
2454dfa32c93c20a8522c6ed42fe057baaac9f9aStephan Bosch/* Copyright (c) 2002-2016 Dovecot authors, see the included COPYING file */
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen#include "lib.h"
0536ccb51d41e3078c3a9fa33e509fb4b2420f95Timo Sirainen#include "printf-format-fix.h"
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainenstatic const char *
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainenfix_format_real(const char *fmt, const char *p, size_t *len_r)
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen{
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen const char *errstr;
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen char *buf;
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen size_t len1, len2, len3;
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen i_assert((size_t)(p - fmt) < INT_MAX);
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen i_assert(p[0] == '%' && p[1] == 'm');
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen errstr = strerror(errno);
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen /* we'll assume that there's only one %m in the format string.
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen this simplifies the code and there's really no good reason to have
3785910c303507db5f629684e6dde2cc7f83668eTimo Sirainen it multiple times. Callers can trap this case themselves. */
42dbeeb3462895b03e7633dbc59e8e191199734bTimo Sirainen len1 = p - fmt;
1de2b5a16a455e018d8cbf72ee114d4b5d557a48Timo Sirainen len2 = strlen(errstr);
e815af0640b38444b31eadfaa1673bcb422e1573Timo Sirainen len3 = strlen(p + 2);
e815af0640b38444b31eadfaa1673bcb422e1573Timo Sirainen
1de2b5a16a455e018d8cbf72ee114d4b5d557a48Timo Sirainen /* @UNSAFE */
1de2b5a16a455e018d8cbf72ee114d4b5d557a48Timo Sirainen buf = t_buffer_get(len1 + len2 + len3 + 1);
e815af0640b38444b31eadfaa1673bcb422e1573Timo Sirainen memcpy(buf, fmt, len1);
e815af0640b38444b31eadfaa1673bcb422e1573Timo Sirainen memcpy(buf + len1, errstr, len2);
3785910c303507db5f629684e6dde2cc7f83668eTimo Sirainen memcpy(buf + len1 + len2, p + 2, len3 + 1);
1a266561b099269bef75eee1a3742e61130ef780Timo Sirainen
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen *len_r = len1 + len2 + len3;
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen return buf;
dd2df6a67f10792ce31a3666197c0b6885893a3aTimo Sirainen}
14175321ddb88619015866978c05a27786ca4814Timo Sirainen
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainenstatic const char *
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainenprintf_format_fix_noalloc(const char *format, size_t *len_r)
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen{
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen static const char *printf_skip_chars = "# -+'I.*0123456789hlLjzt";
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen /* as a tiny optimization keep the most commonly used conversion
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen modifiers first, so strchr() stops early. */
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen static const char *printf_allowed_conversions = "sudcioxXp%eEfFgGaA";
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen const char *ret, *p, *p2;
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen p = ret = format;
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen while ((p2 = strchr(p, '%')) != NULL) {
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen p = p2+1;
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen while (*p != '\0' && strchr(printf_skip_chars, *p) != NULL)
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen p++;
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen if (*p == '\0') {
1de2b5a16a455e018d8cbf72ee114d4b5d557a48Timo Sirainen i_panic("%% modifier missing in '%s'", format);
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen } else if (strchr(printf_allowed_conversions, *p) != NULL) {
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen /* allow & ignore */
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen } else {
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen switch (*p) {
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen case 'n':
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen i_panic("%%n modifier used");
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen case 'm':
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen if (ret != format)
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen i_panic("%%m used twice");
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen ret = fix_format_real(format, p-1, len_r);
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen break;
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen default:
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen i_panic("Unsupported %%%c modifier", *p);
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen }
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen }
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen p++;
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen }
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen if (ret == format)
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen *len_r = p - format + strlen(p);
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen return ret;
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen}
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainenconst char *printf_format_fix_get_len(const char *format, size_t *len_r)
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen{
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen const char *ret;
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen ret = printf_format_fix_noalloc(format, len_r);
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen if (ret != format)
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen t_buffer_alloc(*len_r + 1);
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen return ret;
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen}
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainenconst char *printf_format_fix(const char *format)
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen{
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen const char *ret;
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen size_t len;
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen ret = printf_format_fix_noalloc(format, &len);
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen if (ret != format)
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen t_buffer_alloc(len + 1);
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen return ret;
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen}
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen
d4fe93a9c242d745e0cf2e6cc58d5caf265de2a0Timo Sirainenconst char *printf_format_fix_unsafe(const char *format)
d4fe93a9c242d745e0cf2e6cc58d5caf265de2a0Timo Sirainen{
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen size_t len;
d4fe93a9c242d745e0cf2e6cc58d5caf265de2a0Timo Sirainen
d4fe93a9c242d745e0cf2e6cc58d5caf265de2a0Timo Sirainen return printf_format_fix_noalloc(format, &len);
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen}
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen