view src/lib/printf-format-fix.c @ 21339:8d49b6ed7bab

lib: Fix %n detection in printf_format_fix_noalloc() It's undefined how flags, precision or length modifiers are handled with %n, so make sure we catch all of them to detect an unwanted %n.
author Timo Sirainen <timo.sirainen@dovecot.fi>
date Tue, 29 Nov 2016 23:29:04 +0200
parents bc57c62167fc
children 4efaa627264d
line wrap: on
line source

/* Copyright (c) 2002-2016 Dovecot authors, see the included COPYING file */

#include "lib.h"
#include "printf-format-fix.h"

static const char *
fix_format_real(const char *fmt, const char *p, size_t *len_r)
{
	const char *errstr;
	char *buf;
	size_t len1, len2, len3;

	i_assert((size_t)(p - fmt) < INT_MAX);
	i_assert(p[0] == '%' && p[1] == 'm');

	errstr = strerror(errno);

	/* we'll assume that there's only one %m in the format string.
	   this simplifies the code and there's really no good reason to have
	   it multiple times. Callers can trap this case themselves. */
	len1 = p - fmt;
	len2 = strlen(errstr);
	len3 = strlen(p + 2);

	/* @UNSAFE */
	buf = t_buffer_get(len1 + len2 + len3 + 1);
	memcpy(buf, fmt, len1);
	memcpy(buf + len1, errstr, len2);
	memcpy(buf + len1 + len2, p + 2, len3 + 1);

	*len_r = len1 + len2 + len3;
	return buf;
}

static const char *
printf_format_fix_noalloc(const char *format, size_t *len_r)
{
	static const char *printf_skip_chars = "# -+'I.*0123456789hlLjzt";
	const char *ret, *p, *p2;

	p = ret = format;
	while ((p2 = strchr(p, '%')) != NULL) {
		p = p2+1;
		while (*p != '\0' && strchr(printf_skip_chars, *p) != NULL)
			p++;
		switch (*p) {
		case 'n':
			i_panic("%%n modifier used");
		case 'm':
			if (ret != format)
				i_panic("%%m used twice");
			ret = fix_format_real(format, p-1, len_r);
			break;
		case '\0':
			i_panic("%% modifier missing in '%s'", format);
		}
		p++;
	}

	if (ret == format)
		*len_r = p - format + strlen(p);
	return ret;
}

const char *printf_format_fix_get_len(const char *format, size_t *len_r)
{
	const char *ret;

	ret = printf_format_fix_noalloc(format, len_r);
	if (ret != format)
		t_buffer_alloc(*len_r + 1);
	return ret;
}

const char *printf_format_fix(const char *format)
{
	const char *ret;
	size_t len;

	ret = printf_format_fix_noalloc(format, &len);
	if (ret != format)
		t_buffer_alloc(len + 1);
	return ret;
}

const char *printf_format_fix_unsafe(const char *format)
{
	size_t len;

	return printf_format_fix_noalloc(format, &len);
}