Open ltratt opened 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
)
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.
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.]
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).
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?
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).
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!
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
!
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)?
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
.
and are there cpp constants to check whether to use
vaddr_t
orptraddr_t
(with CHERI) oruintptr_t
with a fallback tosize_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?).
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?)
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.
We (mostly @Jacobbramley and I) are unsure about the precise semantics of
vaddr_t
. The C/C++ programming guide says: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 thatvaddr_t
could map tovoid *
in hybrid mode but that wouldn't work in purecap mode where presumablyuintX_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 :)