On Fri, Sep 15, 2023 at 08:18:28AM -0700, Andrew Pinski wrote:
> On Fri, Sep 15, 2023 at 8:12 AM Qing Zhao <qing.z...@oracle.com> wrote:
> >
> >
> >
> > > On Sep 15, 2023, at 3:43 AM, Xi Ruoyao <xry...@xry111.site> wrote:
> > >
> > > On Thu, 2023-09-14 at 21:41 +0000, Qing Zhao wrote:
> > >>>> CLANG already provided -fsanitize=unsigned-integer-overflow. GCC
> > >>>> might need to do the same.
> > >>>
> > >>> NO. There is no such thing as unsigned integer overflow. That option
> > >>> is badly designed and the GCC community has rejected a few times now
> > >>> having that sanitizer before. It is bad form to have a sanitizer for
> > >>> well defined code.
> > >>
> > >> Even though unsigned integer overflow is well defined, it might be
> > >> unintentional, shall we warn user about this?
> > >
> > > *Everything* could be unintentional and should be warned then.  GCC is a
> > > compiler, not an advanced AI educating the programmers.
> >
> > Well, you are right in some sense. -:)
> >
> > However, overflow is one important source for security flaws, it’s 
> > important  for compilers to detect
> > overflows in the programs in general.
> 
> Except it is NOT an overflow. Rather it is wrapping. That is a big
> point here. unsigned wraps and does NOT overflow. Yes there is a major
> difference.

Right, yes. I will try to pick my language very carefully. :)

The practical problem I am trying to solve in the 30 million lines of
Linux kernel code is that of catching arithmetic wrap-around. The
problem is one of evolving the code -- I can't just drop -fwrapv and
-fwrapv-pointer because it's not possible to fix all the cases at once.
(And we really don't want to reintroduce undefined behavior.)

So, for signed, pointer, and unsigned types, we need:

a) No arithmetic UB -- everything needs to have deterministic behavior.
   The current solution here is "-fno-strict-overflow", which eliminates
   the UB and makes sure everything wraps.

b) A way to run-time warn/trap on overflow/underflow/wrap-around. This
   would work with -fsanitize=[signed-integer|pointer]-overflow except
   due to "a)" we always wrap. And there isn't currently coverage like
   this for unsigned (in GCC).

Our problem is that the kernel is filled with a mix of places where there
is intended wrap-around and unintended wrap-around. We can chip away at
fixing the intended wrap-around that we can find with static analyzers,
etc, but at the end of the day there is a long tail of finding the places
where intended wrap-around is hiding. But when the refactoring is
sufficiently completely, we can move the wrap-around warning to a trap,
and the kernel will not longer have this class of security flaw.

As a real-world example, here is a bug where a u8 wraps around causing
an under-allocation that allowed for a heap overwrite:

https://git.kernel.org/linus/6311071a0562
https://elixir.bootlin.com/linux/v6.5/source/net/wireless/nl80211.c#L5422

If there were more than 255 elements in a linked list, the allocation
would be too small, and the second loop would write past the end of the
allocation. This is a pretty classic allocation underflow and linear
heap write overflow security flaw. (And it would be trivially stopped by
trapping on the u8 wrap around.)

So, I want to be able to catch that at run-time. But we also have code
doing things like "if (ulong + offset < ulong) { ... }":

https://elixir.bootlin.com/linux/v6.5/source/drivers/crypto/axis/artpec6_crypto.c#L1187

This is easy for a static analyzer to find and we can replace it with a
non-wrapping test (e.g. __builtin_add_overflow()), but we'll not find
them all immediately, especially for the signed and pointer cases.

So, I need to retain the "everything wraps" behavior while still being
able to detect when it happens.

-- 
Kees Cook

Reply via email to