[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <861bca05-1964-4539-ac69-f3afc82bfe99@nvidia.com>
Date: Tue, 25 Nov 2025 16:49:15 -0800
From: John Hubbard <jhubbard@...dia.com>
To: Matthew Wilcox <willy@...radead.org>,
Linus Torvalds <torvalds@...ux-foundation.org>
Cc: Kees Cook <kees@...nel.org>, Vlastimil Babka <vbabka@...e.cz>,
Christoph Lameter <cl@...ux.com>, Pekka Enberg <penberg@...nel.org>,
David Rientjes <rientjes@...gle.com>, Joonsoo Kim <iamjoonsoo.kim@....com>,
Andrew Morton <akpm@...ux-foundation.org>,
Roman Gushchin <roman.gushchin@...ux.dev>,
Hyeonggon Yoo <42.hyeyoo@...il.com>,
"Gustavo A . R . Silva" <gustavoars@...nel.org>,
Bill Wendling <morbo@...gle.com>, Justin Stitt <justinstitt@...gle.com>,
Jann Horn <jannh@...gle.com>, Przemek Kitszel
<przemyslaw.kitszel@...el.com>, Marco Elver <elver@...gle.com>,
Greg Kroah-Hartman <gregkh@...uxfoundation.org>,
Sasha Levin <sashal@...nel.org>, linux-mm@...ck.org,
Randy Dunlap <rdunlap@...radead.org>, Miguel Ojeda <ojeda@...nel.org>,
Vegard Nossum <vegard.nossum@...cle.com>, Harry Yoo <harry.yoo@...cle.com>,
Nathan Chancellor <nathan@...nel.org>, Peter Zijlstra
<peterz@...radead.org>, Nick Desaulniers <nick.desaulniers+lkml@...il.com>,
Jonathan Corbet <corbet@....net>, Jakub Kicinski <kuba@...nel.org>,
Yafang Shao <laoar.shao@...il.com>, Tony Ambardar <tony.ambardar@...il.com>,
Alexander Lobakin <aleksander.lobakin@...el.com>,
Jan Hendrik Farr <kernel@...rr.cc>, Alexander Potapenko <glider@...gle.com>,
linux-kernel@...r.kernel.org, linux-hardening@...r.kernel.org,
linux-doc@...r.kernel.org, llvm@...ts.linux.dev
Subject: Re: [PATCH v5 2/4] slab: Introduce kmalloc_obj() and family
On 11/24/25 5:09 PM, Matthew Wilcox wrote:
> On Mon, Nov 24, 2025 at 03:30:19PM -0800, Linus Torvalds wrote:
...
>> Imagine how many other places you add integers to 'unsigned long'.
>> EVERY SINGLE ONE of those places involves sign-extending the integer
>> and then doing arithmetic in unsigned.
>
> I have bad news. Rust requires it.
Well, yes.
But the pain is front-loaded, so it's not like we have to go back and
retrofit anything.
And I also want to tweak the characterization of that, below, because
I suspect this is unevenly understood, so far:
>
> fn add(base: u64, off: i32) -> u64 {
> base + off
> }
>
> error[E0308]: mismatched types
> --> add.rs:2:12
> |
> 2 | base + off
> | ^^^ expected `u64`, found `i32`
>
> error[E0277]: cannot add `i32` to `u64`
> --> add.rs:2:10
> |
> 2 | base + off
> | ^ no implementation for `u64 + i32`
> |
> = help: the trait `Add<i32>` is not implemented for `u64`
> = help: the following other types implement trait `Add<Rhs>`:
> <u64 as Add>
> <u64 as Add<&u64>>
> <&'a u64 as Add<u64>>
> <&u64 as Add<&u64>>
>
> so the Rust language people have clearly decided that this is too
> complicated for your average programmer to figure out, and you need
> explicit casts to make it work.
A couple of key points, though:
a) There are many other, better options than casts (Rust's "as"
keyword).
In fact, Rust doesn't actually require casts here, but it does require
something to explicitly declare the programmer's intentions.
b) Rust doesn't do this because programmers are too dumb to figure it
out. On the contrary, programmers are required to specify exactly what
they want, in the first place.
Some examples, using Matthew's example as a starting point:
// u64 add operations:
// + - panics on overflow (debug), wraps (release)
// wrapping_add(u64) - always wraps
// wrapping_add_signed(i64) - always wraps, accepts signed offset
// checked_add(u64) - returns Option, None on overflow
// checked_add_signed(i64) - returns Option, None on overflow
// saturating_add(u64) - clamps to u64::MAX
// saturating_add_signed(i64) - clamps to 0 or u64::MAX
// overflowing_add(u64) - returns (result, bool)
// overflowing_add_signed(i64)- returns (result, bool)
// strict_add(u64) - always panics on overflow (even release)
// unchecked_add(u64) - UB on overflow (unsafe, for optimization)
// carrying_add(u64, bool) - multi-word arithmetic, returns (result, carry)
// 1. Wrapping: overflow/underflow wraps around (address arithmetic)
fn add_wrapping(base: u64, off: i32) -> u64 {
base.wrapping_add_signed(i64::from(off))
}
// 2. Checked: returns None on overflow/underflow
fn add_checked(base: u64, off: i32) -> Option<u64> {
base.checked_add_signed(i64::from(off))
}
// 3. Saturating: clamps to 0 or u64::MAX
fn add_saturating(base: u64, off: i32) -> u64 {
base.saturating_add_signed(i64::from(off))
}
// 4. Overflowing: returns (result, did_overflow)
fn add_overflowing(base: u64, off: i32) -> (u64, bool) {
base.overflowing_add_signed(i64::from(off))
}
fn main() {
let base: u64 = 0x100;
let off: i32 = -0x200;
println!("base=0x{:x}, off={} (underflow case)", base, off);
println!("wrapping: 0x{:x}", add_wrapping(base, off));
println!("checked: {:?}", add_checked(base, off));
println!("saturating: 0x{:x}", add_saturating(base, off));
println!("overflowing: {:?}", add_overflowing(base, off));
}
// Output:
base=0x100, off=-512 (underflow case)
wrapping: 0xffffffffffffff00
checked: None
saturating: 0x0
overflowing: (18446744073709551360, true)
thanks,
--
John Hubbard
Powered by blists - more mailing lists