diff mbox series

[RFC,1/1] vsprintf: Warn on integer scanning overflows

Message ID 20230607223755.1610-2-richard@nod.at (mailing list archive)
State New, archived
Headers show
Series Integer overflows while scanning for integers | expand

Commit Message

Richard Weinberger June 7, 2023, 10:37 p.m. UTC
The scanf function family has no way to indicate overflows
while scanning. As consequence users of these function have to make
sure their input cannot cause an overflow.
Since this is not always the case add WARN_ON_ONCE() guards to
trigger a warning upon an overflow.

Signed-off-by: Richard Weinberger <richard@nod.at>
---
 lib/vsprintf.c | 33 +++++++++++++++++++++++++--------
 1 file changed, 25 insertions(+), 8 deletions(-)

Comments

Andy Shevchenko June 8, 2023, 2:27 p.m. UTC | #1
On Thu, Jun 08, 2023 at 12:37:55AM +0200, Richard Weinberger wrote:
> The scanf function family has no way to indicate overflows
> while scanning. As consequence users of these function have to make
> sure their input cannot cause an overflow.
> Since this is not always the case add WARN_ON_ONCE() guards to
> trigger a warning upon an overflow.

...

>  	if (prefix_chars < max_chars) {
>  		rv = _parse_integer_limit(cp, base, &result, max_chars - prefix_chars);
> +		WARN_ON_ONCE(rv & KSTRTOX_OVERFLOW);

This seems incorrect. simple_strto*() are okay to overflow. It's by design.

>  		/* FIXME */

...and that's why this one is here.

>  		cp += (rv & ~KSTRTOX_OVERFLOW);
>  	} else {
Richard Weinberger June 8, 2023, 4:14 p.m. UTC | #2
----- Ursprüngliche Mail -----
> Von: "Andy Shevchenko" <andriy.shevchenko@linux.intel.com>
>>  	if (prefix_chars < max_chars) {
>>  		rv = _parse_integer_limit(cp, base, &result, max_chars - prefix_chars);
>> +		WARN_ON_ONCE(rv & KSTRTOX_OVERFLOW);
> 
> This seems incorrect. simple_strto*() are okay to overflow. It's by design.

Is this design decision also known to all users of scanf functions in the kernel?

Thanks,
//richard
Andy Shevchenko June 8, 2023, 4:23 p.m. UTC | #3
On Thu, Jun 08, 2023 at 06:14:33PM +0200, Richard Weinberger wrote:
> ----- Ursprüngliche Mail -----
> > Von: "Andy Shevchenko" <andriy.shevchenko@linux.intel.com>
> >>  	if (prefix_chars < max_chars) {
> >>  		rv = _parse_integer_limit(cp, base, &result, max_chars - prefix_chars);
> >> +		WARN_ON_ONCE(rv & KSTRTOX_OVERFLOW);
> > 
> > This seems incorrect. simple_strto*() are okay to overflow. It's by design.
> 
> Is this design decision also known to all users of scanf functions in the kernel?

We have test_scanf.c. Does it miss any test cases? Please add them!
diff mbox series

Patch

diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index 40f560959b169..3d8d751306cdc 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -70,6 +70,7 @@  static noinline unsigned long long simple_strntoull(const char *startp, size_t m
 	prefix_chars = cp - startp;
 	if (prefix_chars < max_chars) {
 		rv = _parse_integer_limit(cp, base, &result, max_chars - prefix_chars);
+		WARN_ON_ONCE(rv & KSTRTOX_OVERFLOW);
 		/* FIXME */
 		cp += (rv & ~KSTRTOX_OVERFLOW);
 	} else {
@@ -3657,22 +3658,34 @@  int vsscanf(const char *buf, const char *fmt, va_list args)
 
 		switch (qualifier) {
 		case 'H':	/* that's 'hh' in format */
-			if (is_sign)
+			if (is_sign) {
+				WARN_ON_ONCE(val.s > 127);
+				WARN_ON_ONCE(val.s < -128);
 				*va_arg(args, signed char *) = val.s;
-			else
+			} else {
+				WARN_ON_ONCE(val.u > 255);
 				*va_arg(args, unsigned char *) = val.u;
+			}
 			break;
 		case 'h':
-			if (is_sign)
+			if (is_sign) {
+				WARN_ON_ONCE(val.s > SHRT_MAX);
+				WARN_ON_ONCE(val.s < SHRT_MIN);
 				*va_arg(args, short *) = val.s;
-			else
+			} else {
+				WARN_ON_ONCE(val.u > USHRT_MAX);
 				*va_arg(args, unsigned short *) = val.u;
+			}
 			break;
 		case 'l':
-			if (is_sign)
+			if (is_sign) {
+				WARN_ON_ONCE(val.s > LONG_MAX);
+				WARN_ON_ONCE(val.s < LONG_MIN);
 				*va_arg(args, long *) = val.s;
-			else
+			} else {
+				WARN_ON_ONCE(val.u > ULONG_MAX);
 				*va_arg(args, unsigned long *) = val.u;
+			}
 			break;
 		case 'L':
 			if (is_sign)
@@ -3684,10 +3697,14 @@  int vsscanf(const char *buf, const char *fmt, va_list args)
 			*va_arg(args, size_t *) = val.u;
 			break;
 		default:
-			if (is_sign)
+			if (is_sign) {
+				WARN_ON_ONCE(val.s > INT_MAX);
+				WARN_ON_ONCE(val.s < INT_MIN);
 				*va_arg(args, int *) = val.s;
-			else
+			} else {
+				WARN_ON_ONCE(val.u > UINT_MAX);
 				*va_arg(args, unsigned int *) = val.u;
+			}
 			break;
 		}
 		num++;