Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Strict aliasing in Rust?

My understanding is that the following code has undefined behaviour in C++ due to something called "strict aliasing rule".

#include <cstdint>

enum Foo : int16_t {};

void test(Foo& foo) {
    reinterpret_cast<int16_t&>(foo) = 42;
}

In particular, a C++ compiler may omit the assignment altogether because it is allowed to assume that the int16_t& returned by the reinterpret_cast doesn't point to the same memory as foo (of type Foo&) because their types are different.

My question is, does Rust have something akin to the C++ "strict aliasing rule"? In other words, does the following, equivalent Rust code have undefined behaviour?

#[repr(i16)]
enum Foo { Dummy }

unsafe fn test(foo: &mut Foo) {
    *std::mem::transmute::<&mut Foo, &mut i16>(foo) = 42;
}

EDIT:
There's an unrelated issue with the above Rust example code in that test creates a non-existing enum variant. So, here's the same code with a quick fix:

#[repr(i16)]
enum Foo { Dummy, Yummy = 42 }

unsafe fn test(foo: &mut Foo) {
    *std::mem::transmute::<&mut Foo, &mut i16>(foo) = 42;
}
like image 374
kmky Avatar asked Oct 02 '14 14:10

kmky


1 Answers

My understanding is that general Rust code (as verified by the compiler) of course contains no undefined behavior (barring compiler bugs), and that Rust-the-language does not define any undefined behavior, unlike C.

Naturally, then, the only place undefined behavior could occur is within unsafe blocks or functions. unsafe blocks are meant to encapsulate any potentially dangerous behavior as being safe as a whole. A post from July 2014 mentions that Rust's compiler requires that certain invariants are met, and that it is generally dangerous to break those invariants, even in unsafe blocks (in fact, it is only possible to break then within unsafe blocks).

One of these dangerous behaviors is pointer aliasing (which seems to be defined in LLVM itself). Interestingly, however, the LLVM docs say (emphasis mine):

Consequently, type-based alias analysis, aka TBAA, aka -fstrict-aliasing, is not applicable to general unadorned LLVM IR

Thus, it seems that generally, as long as none of the other unsafe behaviors are triggered as a result of the unsafe block, strict aliasing isn't an issue in Rust.


That being said, your specific example is possibly dangerous, because it seems to match one of the unsafe behaviors from the reference:

Invalid values in primitive types, even in private fields/locals:

  • A discriminant in an enum not included in the type definition

I wrote a small extension to your example that shows the binary representations of the enum variants, before and after the unsafe operation: http://is.gd/x0K9kN

As you can see, assigning 42 to the enum value does not match any of the defined discriminator values. If, however, you had assigned either 0 or 1 to the enum value (or instead had defined explicit discriminators for the enum variants), then the operation should theoretically be fine.

like image 118
voithos Avatar answered Sep 19 '22 17:09

voithos