C 2018 6.5 7 says:
An object shall have its stored value accessed only by an lvalue expression that has one of the following types:
— a type compatible with the effective type of the object,
— a qualified version of a type compatible with the effective type of the object,
— a type that is the signed or unsigned type corresponding to the effective type of the object,
— a type that is the signed or unsigned type corresponding to a qualified version of the effective type of the object,
— an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union), or
— a character type.
The type of the buf member is an array of unsigned char, and its elements have type unsigned char.
p[0] accesses memory with an lvalue of type uint32_t (because p was as uint32_t *p). Considering the list:
— uint32_t is not compatible with array of unsigned char or unsigned char.
— uint32_t is not a qualified version of a type compatible with array of unsigned char or unsigned char.
— uint32_t is not a signed or unsigned type corresponding to array of unsigned char or unsigned char.
— uint32_t is not a signed or unsigned type corresponding to a qualified version of array of unsigned char or unsigned char.
— uint32_t not an aggregate or union type that includes one of the aforementioned.
— uint32_t is not a character type.
Therefore, p[0] violates C 2018 6.5 7. Then, per C 2018 4 2:
If a "shall" or "shall not" requirement that appears outside of a constraint or runtime-constraint is violated, the behavior is undefined…