Pre-RFC: `usize` semantics

The fundamental point of CHERI is that (from) expose(d) addr doesn't work. CHERI compatible code must not roundtrip pointers through integers except uintptr_t. Rust FFI to code using uintptr_t would, in a world where usize === size_t, would use a type like uptr. (uptr would need to change its implementation for CHERI.) Due to CHERI uintptr_t maintaining the bonus hidden data, this is a more accurate capture of semantics for Rust than presenting as an integer.

3 Likes

Not that I'm aware of.

My guess is that some or all of the following need to happen before this will go much further:

  • stabilisation of strict provenance
  • more widely available CHERI hardware
  • agreement about how to gate CHERI support to avoid breaking crates

The latest development is probably the Crater run from December. We ran an experiment to get an idea of how many crates are likely to be CHERI compatible by looking for usize to pointer casts. The results were promising, ~99% of 380,000 crates seemed to be ok. Details here: Experiment: run Crater with strict provenance lints ยท Issue #117752 ยท rust-lang/rust ยท GitHub

It's possibly worth noting that there are already lints for most of the interesting situations available in nightly compilers, thanks to strict provenance. I think the only easy (probably?) to lint case they don't cover is transmute, though on CHERI targets with size_of::<usize>() != size_of::<*const T>() that will be picked up by the usual size check.

For our part, me and the team at the University of Kent are still working on our experimental Rust port to Morello (CHERI AArch64). We have builds and source code available for anyone who wants to play with it: Rust for Morello

We're also interested in doing what we can to keep this discussion moving along.

6 Likes

That is but one half of the incompatibility risk. The other half is code assuming that usize and pointers are the same size and doing manual layout computations based on this assumption.

2 Likes

My thinking generally at this point is that we want to have a notion of "experimental target" so that it is possible to do nightly-only experiments with CHERI support upstream (similar to how we handle a bunch of other experimental features).

8 Likes

Can you actually get hold of any CHERI hardware or is it still purely academical? I haven't heard of any generally available dev board let alone any Cherry Pis (that would be a great name by the way).

Until there are at least dev boards it seems kind of pointless to merge support upstream, we don't know if it will ever become a real thing. How is it being handled by other relevant software? (Clang, LLVM, GCC, the Linux kernel, ...)?

1 Like

How do new platforms usually get bootstraped? I've certainly heard of the Linux kernel merging driver support for "upcoming" CPU generations. So it seems entirely sensible that compilers would do something similar with new CPU architectures. If you go through the tier 3 target list there's a lot of stuff in there many will never have heard of (I had to search for both "C-SKY" and "Hexagon").

For CHERI, software emulation via QEMU is freely available. I don't see anything in our Tier 3 target policy that says that the hardware must be commercially available.

CHERI is interesting enough that just today a researcher asked me whether they could write the CHERI kernel they are going to write in Rust and I had to disappoint them. (Okay to be fair this isn't representative of my average day, but it does happen.)

CHERI is an experiment, true. It could fail, in which case support can be removed from Rust again -- that's why I suggested a (tier 3) nightly-only target. But meanwhile the goals of CHERI are very aligned with the memory safety goals of Rust, so it seems like a shame if Rust decided to miss out on partaking in that experiment.

18 Likes

Some hardware exists, but you can't really go out and buy it yet. ARM have made a run of experimental Morello (CHERI AArch64) boards that have been available to industry and academic institutions. I'm told the GCC compile farm have one that they make available for open source contributors to use.

Looking at the uptr source code I don't think any change would be needed there since it's already just a wrapper around a pointer. The implementation already uses map_addr, so that would retain the metadata for CHERI. In CHERI LLVM, uintptr_t is represented the same way as a pointer (ptr addrspace(200) in LLVM IR and arithmetic is mapped to appropriate GEP instructions or a special intrinsics for set_addr.

Regarding the point that using size_of::<usize>() for pointers is a footgun for CHERI, I'd argue the same problem exists for 32-bit only code that might use something like 5 * size_of::<u32>() for a struct containing pointers and u32 members to avoid having to spell out more types in that expression. I agree that rust should not break guarantees for existing architectures, but ensuring that code written for platform A will run unmodified on platform B seems almost impossible to me?

That's not really comparable. Rust had 32bit and 64bit targets from the start so it seems extremely unlikely that someone would write such code and expect it to be portable.

In contrast, for the size of usize vs *const (), the compiler itself assumes them to be the same when it accesses the fields of a vtable. If this assumption is used in rustc we can assume that it also occurs throughout the ecosystem.

Of course one could say those are just things that need to be fixed to make the code work on the new CHERI targets. The actual question is whether we communicate an official expectation that Rust code should be portable to CHERI. Currently it is generally considered a bug to write code that assumes a given pointer size or endianess. The problem we have to solve is a social one, not a technical one: how do we add CHERI support to rustc while making it clear that it is completely okay for a crate to not support CHERI? That's why I think introducing a notion of "experimental/unstable targets" would help.

4 Likes

TBH, I don't know that such a (general) expectation exists. There are quite a few targets that many crates won't work OOB on - any target without std support trivially won't work on any crate that uses std, neither will wasm32-unknown-unknown (this is even more subtle, because that code will compile, but not function properly). I don't really think there's even been a practically true expectation that any crate will support any target rustc supports - I don't think CHERI would be any different here.

3 Likes

I think this is worse than wasm32-unknown-unknown. There, things generally panic in a clean way when they are not supported. Here, they will just silently misbehave. I am not aware of code that is unsound due to wasm specifics, but this would be the kind of issue you'd expect on CHERI.

There's also not much a crate can do to be compatible with wasm, I think? If it needs some API then that's just that. So there's not much risk of crate authors being asked to do extra work here. For CHERI, this is quite different -- "please make the crate work with CHERI" is a request that can actually be satisfied, but it can be a bunch of work. (Presumably Miri would support CHERI targets so at least there's a way to test this without owning CHERI hardware -- provided the code runs in Miri, anyway.)

I think a good next step here would be an RFC proposing CHERI as an experimental target.

5 Likes

Isn't the whole point of CHERI that is should incorrect usage of memory and terminate the program? So I would expect additional segfaults, not UB. That is, these additional failures to run should simply terminate the program, no nasal demons to be seen. Otherwise the idea of CHERI doesn't seem useful at all.

It may a bit offtopic, but I haven't seen discussions about how hypothetical CHERI support would change ABI of slice-like types. Right now, slices are pairs of raw pointer and usize length. Naively translating it to CHERI would work fine, but it would be inefficient. After all, on such targets we could use just "pointer"! Granted, under the hood length of slices would be passed in bytes, not in T, but I think it should be manageable.

CHERI doesn't remove UB. That would require (a) a lot more checks and (b) doing no UB-based optimizations.

All CHERI does is contain the damage that UB can cause. But soundness still remains a very relevant question.

1 Like

That is not what confused me about your previous statement. It sounded like CHERI introduced additional UB. Rather than just making (some) existing UB terminate the program. My understanding was the latter. If CHERI adds additional UB (especially UB of the nasal demon variety) that would be very problematic.

The entire reason this thread is controversial is because of the new soundness issues caused by CHERI. I understand it's a lot of text to read, but it's also not great to jump into a thread without any context of the prior discussion.

The upshot is: code out there assumes that usize and *const T (for sized T) have the same size. CHERI makes that not be true. That can lead to pointer arithmetic going wrong and thus cause soundness issues.

One position in the controversy is: usize and *const T (for sized T) must continue to have the same size for CHERI, inefficient though that may be.

3 Likes

I don't think that can easily work unfortunately. On Cheri the bounds stored in the pointer are compressed and not precise above a certain allocation size.

It works for Cheri, because they can be conservative with the bounds check, but for Rust slices we really need to know precisely if it was 64355 or 64354 bytes.

1 Like

Also, using pointers, it's possible to have a slice that is smaller (or even larger) than the actual bounds the pointer is allowed to access.

I had been involved in this discussion before, and at that point I read the whole thing. My mistake was in thinking that I correctly remembered all the details of a several months old thread, which I apparently did not.

In my memory, doing a cast via usize that would result in loosing provenance would simply result in the program crashing (since a pointer with no provenance would not be allowed to access anything).

There is a big difference between saying "CHERI makes X behaviour invalid, terminating the program when running on CHERI (behaviour still allowed on other platforms)" and "CHERI makes X behaviour UB, causing nasal demons". I think the former is a lot easier to accept for many people. Worst of all would be if the behaviour would cause UB on other platforms too.