CTSRD-CHERI / cheri-c-programming

CHERI C/C++ Programming Guide
28 stars 3 forks source link

vaddr_t rules #22

Open ltratt opened 3 years ago

ltratt commented 3 years ago

We (mostly @Jacobbramley and I) are unsure about the precise semantics of vaddr_t. The C/C++ programming guide says:

vaddr_t This is a new integer type introduced by CHERI C and should be used to hold virtual addresses. vaddr_t should not be directly cast to a pointer type for dereference; instead, it must be combined with an existing valid capability to the address space to generate a dereferenceable pointer. Typically, this is done using the cheri_address_set(c, x) function.

From this we infer that the expectation is that it maps to a uintX_t type? Does it map to the same type in both hybrid and purecap compilation? [One possibility is that vaddr_t could map to void * in hybrid mode but that wouldn't work in purecap mode where presumably uintX_t is the only practical C type to map to.]

We're also not sure what to do with "vaddr_t should not be directly cast to a pointer type for dereference". Does that mean that (say) char *x = cheri_address_get(cap) causes UB? Are there implications for pointer provenance (and are those the same in hybrid/purecap)? We're also not sure if there are things that we should think are different about CHERI C/C++ vs. "standard" C/C++.

I'm sure there are other questions I should ask. I know enough about C's semantics to know that I don't know everything :)

arichardson commented 3 years ago

ptraddr_t (the new name for vaddr_t) is a non-provenance-carrying integer type in both purecap and hybrid. In hybrid you can obviously still cast it to an integer pointer and dereference it (relative to DDC), but in purecap it's always a plain integer than can't be dereferenced. The compiler now provides __PTRADDR_TYPE__ to define ptraddr_t (it usually expands to long unsigned int)

jrtc27 commented 3 years ago

On RV32 it should be just unsigned int, and on 64-bit Windows it would be unsigned long long as their long is 32-bit.

As with all types in C though there’s no upper bound on its size, it’s just large enough to be able to hold the integer addresses of all valid pointers.

ltratt commented 3 years ago

ptraddr_t (the new name for vaddr_t) is a non-provenance-carrying integer type in both purecap and hybrid. In hybrid you can obviously still cast it to an integer pointer and dereference it (relative to DDC)

In hybrid mode, if it's non-provenance carrying, can I safely cast it to a pointer type and dereference it? [I ask this slightly naively: I've not poked and prodded all the dark corners of pointer provenance.]

jrtc27 commented 3 years ago

It's non-provenance-carrying in the CHERI sense. In hybrid C it maintains all the same semantics as any other integral type being cast to a pointer, namely that it works provided the integer is big enough (which, by the definition of ptraddr_t, it will be), so in hybrid C you can consider it to just be another name for uintptr_t (though they could differ in representation).

ltratt commented 3 years ago

It's non-provenance-carrying in the CHERI sense

Just to check: if I'm in hybrid mode, and I cast a capability to (say) void *, I've lost CHERI provenance (since I no longer have a capability) and I'm back in normal C provenance rules?

jrtc27 commented 3 years ago

It's non-provenance-carrying in the CHERI sense

Just to check: if I'm in hybrid mode, and I cast a capability to (say) void *, I've lost CHERI provenance (since I no longer have a capability) and I'm back in normal C provenance rules?

Yes (with the only subtle difference being that, if you've mucked with DDC, that pointer had better be in DDC's bounds if you want to use it).

ltratt commented 3 years ago

with the only subtle difference being that, if you've mucked with DDC, that pointer had better be in DDC's bounds if you want to use it

Indeed :)

OK, I think that @jacobbramley and I feel better informed now. I can't promise we won't have future questions, but I am grateful for the answers to our current head-scratchers!

mirabilos commented 1 year ago

are there corresponding limits for ptraddr_t (and why’s the guide not updated for the new name)?

ok I guess one could use ((ptraddr_t)~(ptraddr_t)0) as limit as with all other unsigned types

… but CheriOS uses vaddr_t!

mirabilos commented 1 year ago

and are there cpp constants to check whether to use vaddr_t or ptraddr_t (with CHERI) or uintptr_t with a fallback to size_t (on nōn-CHERI UNIX)?

jrtc27 commented 1 year ago

https://github.com/CTSRD-CHERI/cheri-c-programming/commit/b6062ced9312840bd0f32dac473873800dd9d26d, just the published report predates that. CheriOS is old abandonware, most of which was written years before ptraddr_t was adopted.

__PTRADDR_MAX__ exists like __SIZE_MAX__ for GNU C, but it's true we're missing a PTRADDR_MAX in CheriBSD like the standard non-GNU-specific SIZE_MAX.

jrtc27 commented 1 year ago

and are there cpp constants to check whether to use vaddr_t or ptraddr_t (with CHERI) or uintptr_t with a fallback to size_t (on nōn-CHERI UNIX)?

__PTRADDR_TYPE__ exists like __SIZE_TYPE__ and __(U)INTPTR_TYPE__, though it's GNU-specific if that matters to you (which likely doesn't, unless Windows+CHERI becomes a thing?).

mirabilos commented 1 year ago

Thanks! Is there also something for Cheri* (generally, not limited to BSD, and yes including a hypothetical Windows) so that I could use…

#ifdef __CHERI__    /* ← this? */
#define myPointerBaseAddressType ptraddr_t
#elif defined(UINTPTR_MAX)
#define myPointerBaseAddressType uintptr_t
#else
#define myPointerBaseAddressType size_t /* fallback */
#endif

… and in which header(s) would ptraddr_t be? (And, I can completely disregard vaddr_t now?)

jrtc27 commented 1 year ago

Eh, __CHERI__ does exist, and currently means "hybrid or pure-capability". Some of us want to repurpose it to mean "pure-capability" given we call that CHERI C/C++ these days. Having said that, for you it doesn't matter, since in the hybrid case ptraddr_t and uintptr_t will both exist and be the same type. There's also __has_feature(capabilities) for "hybrid or pure-capability" and __CHERI_PURE_CAPABILITY__ for pure-capability specifically. Take your pick.

ptraddr_t lives in stdint.h like (u)intptr_t. You can ignore vaddr_t; using it will even give you a warning in CheriBSD as of late, so that we can finally purge it.