3 ms·
If uint8_t is a char type (and invariably it is), then it is fully defined. Similarly, it is perfectly safe to cast a correctly aligned uint8_t* to uint64_t*
by gpderetta 3y ago
If uint8_t is a char type (and invariably it is), then it is fully defined.
Similarly, it is perfectly safe to cast a correctly aligned uint8_t* to uint64_t* and dereference it as long as you originally stored an uint64_t into it (or the compiler can't prove you didn't). Remember aliasing UB is always on derefencing with the wrong dynamic type, not on casting pointers.
- Chabsff 3y agoYou are correct in principle, but the code in the original post definitely exhibits strict aliasing violations. Not only that, it didn't need to.
- gpderetta 3y agoThe original code computing the hash by casting uint8_t* to uint64_t* for wide leads is generally wrong, but the memcpy one is fine.
- tom_ 3y agoI'm surprised the gcc and clang people have never noticed that uint8_t is an integer type, and therefore does not need to be a character type.
- gpderetta 3y agoWell, if we were starting from scratch {u,}int8_t should definitely not be character type; instead we would have byte_t (plus an explicitly sized variant, octect_t?) as the general universally aliasable byte type, and all of these would be guaranteed to be distinct types. But today, uint8_t is in practice always a char type. You can static_assert if you want to be completely sure. Or just use std::byte.
- JonChesterfield 3y agoThere's an old gcc bug report about whether uint8_t should have the magic aliasing properties of char or not. I don't remember what they concluded. You can cast any pointer you like to any other pointer. When you dereference it, if the declared type is not the type of the underlying object, bad times for you. An interface which takes a uint8_t* and immediately invokes UB if you pass it anything other than a pointer to a uint64_t can indeed be written, but one should expect people to pass it things other than a cast uint64_t*. Further, the magic aliasing char doesn't work in both directions. If this code was changed to take a char instead of a uint8_t it would be exactly as undefined as it is currently. You can aim a char* into a uint64_t and deref the char, but you can't aim a uint64_t* into a char and deref the uint64_t.*
- quietbritishjim 3y agoThey ended up making uint8_t a typedef for unsigned char rather than a distinct type (which is permitted but not required by the standard) so the semantics are now clear. Edit: That's what I remember but I can't find evidence of it. The docs say this is ABI dependent so perhaps the above statement is true on some specific ABI (if I haven't misremembered it entirely). But I can't track down wheere a relevant ABI would be documented. https://gcc.gnu.org/onlinedocs/gcc-13.2.0/gcc/Architecture-implementation.html https://gcc.gnu.org/onlinedocs/gcc-13.2.0/gcc/Architecture-i... Edit 2: this header has a typedef of uint8_t to unsigned char, but it's part of glibc rather than gcc https://android.googlesource.com/platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6/+/refs/heads/tools_r20/sysroot/usr/include/stdint.h https://android.googlesource.com/platform/prebuilts/gcc/linu...
- kzrdude 3y agoI thought if uint8_t exists, it logically must be the same as unsigned char? You put some of the statements of the standards together and that's their combined consequence.
- quietbritishjim 3y agoNo it doesn't have to be, and it wasn't. That was the whole problem. They were both unsigned integer types with 8 bit widths but they were distinct and, in particular, uint8_t didn't have the aliasing exception that unsigned char does.
- leni536 3y agoNo, the relationship is directional. You can reinterpret a pointer to an integral type to a pointer to unsigned char and read the underlying bytes. The other way around is UB.
- gpderetta 3y agoWhat I mean is that this is perfectly valid code: uint64_t x=0; uint8_t * p = (uint8_t*)&x; uint64_t y = *(uint64_t*)p; It is not the casting that is the problem, it is the dereferencing. And as long as that matches the dynamic type, it is ok.