[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <202405131047.A3861EC13@keescook>
Date: Mon, 13 May 2024 11:34:20 -0700
From: Kees Cook <keescook@...omium.org>
To: Linus Torvalds <torvalds@...ux-foundation.org>
Cc: Martin Uecker <uecker@...raz.at>, Justin Stitt <justinstitt@...gle.com>,
Peter Zijlstra <peterz@...radead.org>,
Mark Rutland <mark.rutland@....com>,
linux-hardening@...r.kernel.org, linux-kernel@...r.kernel.org,
llvm@...ts.linux.dev
Subject: Re: [RFC] Mitigating unexpected arithmetic overflow
On Sun, May 12, 2024 at 09:09:08AM -0700, Linus Torvalds wrote:
> unsigned char *p;
> u32 val;
>
> p[0] = val;
> p[1] = val >> 8;
> p[2] = val >> 16;
> p[3] = val >> 24;
>
> kind of code is both traditional and correct, but obviously drops bits
> very much intentionally on each of those assignments.
The good news here is that the integer implicit truncation sanitizers
are already split between "signed" and "unsigned". So the 2 cases of
exploitable flaws mentioned earlier:
u8 num_elems;
...
num_elems++; /* int promotion stored back to u8 */
and
int size;
u16 read_size;
...
read_size = size; /* large int stored to u16 */
are both confusions across signed/unsigned types, which the signed
sanitizer would catch. The signed sanitizer would entirely ignore
the quoted example at the top: everything is unsigned and no int
promotion is happening.
So, I think we can start with just the "signed integer implicit
truncation" sanitizer. The compiler will still need to deal with the
issues I outlined in [1], where I think we need some consideration
specifically on how to handle things like this (that have a
smaller-than-int size and trip the sanitizer due to int promotion):
u8 checksum(const u8 *buf)
{
u8 sum = 0;
for (int i = 0; i < 4; i++)
sum += buf[i]; /* int promotion */
return sum;
}
We want "sum" to wrap. We could avoid the "implicit" truncation by
explicitly truncating with something eye-bleedingly horrible like:
sum = (u8)(sum + buf[i]);
Adding a wrapper for the calculation could work but repeats "sum", and
needs to be explicitly typed, making it just as unfriendly:
sum = truncate(u8, sum + buf[i]);
Part of the work I'd done in preparation for all this was making the
verbosely named wrapping_assign_add() helper which handles all the
types by examining the arguments and avoids repeating the destination
argument. So this would become:
wrapping_assign_add(sum, buf[i]);
Still not as clean as "+=", but at least more readable than the
alternatives and leaves no question about wrapping intent.
-Kees
[1] https://lore.kernel.org/lkml/202405081949.0565810E46@keescook/
--
Kees Cook
Powered by blists - more mailing lists