Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4d72ba01 authored by Rasmus Villemoes's avatar Rasmus Villemoes Committed by Linus Torvalds
Browse files

lib/vsprintf.c: warn about too large precisions and field widths



The field width is overloaded to pass some extra information for some %p
extensions (e.g.  #bits for %pb).  But we might silently truncate the
passed value when we stash it in struct printf_spec (see e.g.
"lib/vsprintf.c: expand field_width to 24 bits").  Hopefully 23 value
bits should now be enough for everybody, but if not, let's make some
noise.

Do the same for the precision.  In both cases, clamping seems more
sensible than truncating.  While, according to POSIX, "A negative
precision is taken as if the precision were omitted.", the kernel's
printf has always treated that case as if the precision was 0, so we use
that as lower bound.  For the field width, the smallest representable
value is actually -(1<<23), but a negative field width means 'set the
LEFT flag and use the absolute value', so we want the absolute value to
fit.

Signed-off-by: default avatarRasmus Villemoes <linux@rasmusvillemoes.dk>
Cc: Al Viro <viro@ZenIV.linux.org.uk>
Cc: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Joe Perches <joe@perches.com>
Cc: Kees Cook <keescook@chromium.org>
Cc: Maurizio Lombardi <mlombard@redhat.com>
Cc: Tejun Heo <tj@kernel.org>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 1c7a8e62
Loading
Loading
Loading
Loading
+24 −4
Original line number Diff line number Diff line
@@ -389,6 +389,8 @@ struct printf_spec {
	unsigned int	base:8;		/* number base, 8, 10 or 16 only */
	signed int	precision:16;	/* # of digits/chars */
} __packed;
#define FIELD_WIDTH_MAX ((1 << 23) - 1)
#define PRECISION_MAX ((1 << 15) - 1)

static noinline_for_stack
char *number(char *buf, char *end, unsigned long long num,
@@ -1845,6 +1847,24 @@ int format_decode(const char *fmt, struct printf_spec *spec)
	return ++fmt - start;
}

static void
set_field_width(struct printf_spec *spec, int width)
{
	spec->field_width = width;
	if (WARN_ONCE(spec->field_width != width, "field width %d too large", width)) {
		spec->field_width = clamp(width, -FIELD_WIDTH_MAX, FIELD_WIDTH_MAX);
	}
}

static void
set_precision(struct printf_spec *spec, int prec)
{
	spec->precision = prec;
	if (WARN_ONCE(spec->precision != prec, "precision %d too large", prec)) {
		spec->precision = clamp(prec, 0, PRECISION_MAX);
	}
}

/**
 * vsnprintf - Format a string and place it in a buffer
 * @buf: The buffer to place the result into
@@ -1912,11 +1932,11 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
		}

		case FORMAT_TYPE_WIDTH:
			spec.field_width = va_arg(args, int);
			set_field_width(&spec, va_arg(args, int));
			break;

		case FORMAT_TYPE_PRECISION:
			spec.precision = va_arg(args, int);
			set_precision(&spec, va_arg(args, int));
			break;

		case FORMAT_TYPE_CHAR: {
@@ -2356,11 +2376,11 @@ int bstr_printf(char *buf, size_t size, const char *fmt, const u32 *bin_buf)
		}

		case FORMAT_TYPE_WIDTH:
			spec.field_width = get_arg(int);
			set_field_width(&spec, get_arg(int));
			break;

		case FORMAT_TYPE_PRECISION:
			spec.precision = get_arg(int);
			set_precision(&spec, get_arg(int));
			break;

		case FORMAT_TYPE_CHAR: {