@@ -38,6 +38,25 @@ pub(crate) fn make_non_pod(s: &mut ItemStruct, layout: Option<Layout>) {
38
38
// (if we have layout information from bindgen we use that instead)
39
39
// (2) We want to ensure the type is !Unpin
40
40
// (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.
41
60
//
42
61
// For opaque types, the Rusty opaque structure could in fact be generated
43
62
// by three different things:
@@ -51,6 +70,13 @@ pub(crate) fn make_non_pod(s: &mut ItemStruct, layout: Option<Layout>) {
51
70
// much more difficult.
52
71
// We use (c) for abstract types. For everything else, we do it ourselves
53
72
// 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.
54
80
// First work out attributes.
55
81
let doc_attr = s
56
82
. attrs
@@ -98,7 +124,7 @@ pub(crate) fn make_non_pod(s: &mut ItemStruct, layout: Option<Layout>) {
98
124
Some (
99
125
syn:: Field :: parse_named
100
126
. parse2 ( quote ! {
101
- _data: [ u8 ; #size]
127
+ _data: :: core :: cell :: UnsafeCell < :: core :: mem :: MaybeUninit < [ u8 ; #size] >>
102
128
} )
103
129
. unwrap ( ) ,
104
130
)
0 commit comments