Re: [PATCH 5/5] types: Add standard __ob_trap and __ob_wrap scalar types

From: Linus Torvalds

Date: Tue Mar 31 2026 - 16:07:15 EST


On Tue, 31 Mar 2026 at 11:59, Kees Cook <kees@xxxxxxxxxx> wrote:
>
> The syntax problem (as made clear by many other people, and even you
> here in the first half of this email) is that no one will use function
> based math primitives.

I don't think that's true.

It's just that they have to be made simple enough to use, and have a
good *reason* to use them without the end result becoming horrendous.

Ok, so I just spent fifteen minutes trying it out, trying to aim for a
really simple syntax.

Look at this contrieved example where there are two different overflow
things with two different exception handlers. I decided to add a
"default" label that is just called "overflow", so you can write code
like this:

static int testme(int a, int b, int c, int d)
{
return addo(addmulo(a,b,c,addmul_overflow),d);
overflow:
return -1;
addmul_overflow:
return -2;
}

which obviously isn't pretty, but it's still at least somewhat
readable. It does a "addmul" and an "add", both with overflow
handling, and returns the end result.

If the final add overflows (which doesn't have an explicit overflow
label name), it goes to the default "overflow:" label.

And if the addmul overflows, it goes to addmul_overflow. It's all kind
of obvous, and not syntactically all that onerous.

And it does work:

#define TEST(x) printf(#x "=%d\n", x)

#define MAX_INT 2147483647

int main(int argc, char **argv)
{
TEST(testme(1,2,3,4));
TEST(testme(MAX_INT,2,3,4));
TEST(testme(1,2,3,MAX_INT));
return 0;
}

results in:

$ gcc -O2 ov.c && ./a.out
testme(1,2,3,4)=11
testme(MAX_INT,2,3,4)=-2
testme(1,2,3,MAX_INT)=-1

and in this case gcc actually did everything at compile-time, so code
generation is actually good too: the compiler will optimize this to
hell and back.

But even *without* constant arguments, the compiler can actually
generate good code too:

.globl testme
.type testme, @function
testme:
.LFB11:
.cfi_startproc
imull %edx, %esi
jo .L6
addl %edi, %esi
jo .L6
addl %ecx, %esi
jo .L15
movl %esi, %eax
ret
.L6:
movl $-2, %eax
ret
.L15:
movl $-1, %eax
ret

that really isn't bad.

So the code is legible, the code generation is fine, and it's pretty
flexible. And are the macros complicated? No. This is literally the
code that did all this:

#define __default_exception(a,b,...) b
#define default_exception(...)
__default_exception(,##__VA_ARGS__,overflow)
#define overflow_op(op,a,b,c) __builtin_##op##_overflow(a,b,c)

#define __overflow(op,a,b,...) ({ \
__typeof__(a) __res; \
if (overflow_op(op,a,b,&__res)) \
goto default_exception(__VA_ARGS__); \
__res; })

#define addo(a,b,...) __overflow(add,a,b,##__VA_ARGS__)
#define mulo(a,b,...) __overflow(mul,a,b,##__VA_ARGS__)
#define addmulo(a,b,c,...) addo(a,mulo(b,c,##__VA_ARGS__),##__VA_ARGS__)

Now will people ENJOY using "addo()" and things like that? No. Clearly
it's still *easier* and even clearer to just write

return a + b*c + d;

and yes, that is more legible.

But no, I really *really* don't want people to be able to just
randomly say "I'm just going to kill the kernel if this overflows".

Linus