You're fixing a theoretical problem (mismatch between CPU and compiler memory models, the CPU is perfectly fine doing these reads and writes, it's only the compiler declaring them "UB") by throwing away a shitton of performance, forcing everything into bytewise accesses. Considering this is Rust, I would at minimum expect this be written to be generic over access size to allow using 64-bit reads/writes.
I'm also missing any acquire/release barrier annotations in your code snippets. If you're using sequentially consistent accesses you might as well just single thread your code, performance wise.
Lastly, in almost all cases it's way more efficient and appropriate to shuffle things on the whole-object level, posting and retrieving pointers, and not poke around inside objects (especially on the byte level). Check how rare the use of seqlocks in the Linux kernel is, compared to other RCU primitives. (and regarding "appropriate", cf. top-level comment by danbruc https://news.ycombinator.com/item?id=49168283 )
The compiler declares them UB because very useful transformations would change the semantics of races. Races are ok with specially marked variables!