@@ -38,6 +38,25 @@ pub(crate) fn make_non_pod(s: &mut ItemStruct, layout: Option<Layout>) {
3838 // (if we have layout information from bindgen we use that instead)
3939 // (2) We want to ensure the type is !Unpin
4040 // (3) We want to ensure it's not Send or Sync
41+ // In addition, we want to avoid UB:
42+ // (4) By marking the data as MaybeUninit we ensure there's no UB
43+ // by Rust assuming it's initialized
44+ // (5) By marking it as UnsafeCell we perhaps help reduce aliasing UB.
45+ // This is on the assumption that references to this type may pass
46+ // through C++ and get duplicated, so there may be multiple Rust
47+ // references to the same underlying data.
48+ // The correct solution to this is to put autocxx into the mode
49+ // where it uses CppRef<T> instead of Rust references, but otherwise,
50+ // using UnsafeCell here may help a bit. It definitely does not
51+ // eliminate the UB here for the following reasons:
52+ // a) The references floating around are to the outer type, not the
53+ // data stored within the UnsafeCell.
54+ // b) C++ may have multiple mutable references, or may have mutable
55+ // references coexisting with immutable references, and no amount
56+ // of UnsafeCell can make that safe.
57+ // Nevertheless the use of UnsafeCell here may (*may*) reduce the
58+ // opportunities for aliasing UB. Again, the only actual way to
59+ // eliminate UB is to use CppRef<T> everywhere instead of &T and &mut T.
4160 //
4261 // For opaque types, the Rusty opaque structure could in fact be generated
4362 // by three different things:
@@ -51,6 +70,13 @@ pub(crate) fn make_non_pod(s: &mut ItemStruct, layout: Option<Layout>) {
5170 // much more difficult.
5271 // We use (c) for abstract types. For everything else, we do it ourselves
5372 // for maximal control. See codegen_rs/mod.rs generate_type for more notes.
73+ //
74+ // It is worth noting that our constraints here are a bit more severe than
75+ // for cxx. In the case of cxx, C++ types are usually represented as
76+ // zero-sized types within Rust. Zero-sized types, by definition, can't
77+ // have overlapping references and thus can't have aliasing UB. We can't
78+ // do that because we want C++ types to be representable on the Rust stack,
79+ // and thus we need to tell Rust their real size and alignment.
5480 // First work out attributes.
5581 let doc_attr = s
5682 . attrs
@@ -98,7 +124,7 @@ pub(crate) fn make_non_pod(s: &mut ItemStruct, layout: Option<Layout>) {
98124 Some (
99125 syn:: Field :: parse_named
100126 . parse2 ( quote ! {
101- _data: [ u8 ; #size]
127+ _data: :: core :: cell :: UnsafeCell < :: core :: mem :: MaybeUninit < [ u8 ; #size] >>
102128 } )
103129 . unwrap ( ) ,
104130 )
0 commit comments