Skip to content

Commit a0e560d

Browse files
nbdd0121gregkh
authored andcommitted
rust: pin-init: fix incorrect accessor reference lifetime
commit 68bf102 upstream When a field has been initialized, `init!`/`pin_init!` create a reference or pinned reference to the field so it can be accessed later during the initialization of other fields. However, the reference it created is incorrectly `&'static` rather than just the scope of the initializer. This means that you can do init!(Foo { a: 1, _: { let b: &'static u32 = a; } }) which is unsound. This is caused by `&mut (*$slot).$ident`, which actually allows arbitrary lifetime, so this is effectively `'static`. Fix it by adding `let_binding` method on `DropGuard` to shorten lifetime. This results in exactly what we want for these accessors. The safety and invariant comments of `DropGuard` have been reworked; instead of reasoning about what caller can do with the guard, express it in a way that the ownership is transferred to the guard and `forget` takes it back, so the unsafe operations within the `DropGuard` can be more easily justified. Assisted-by: Claude:claude-3-opus Signed-off-by: Gary Guo <gary@garyguo.net> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 4af2e62 commit a0e560d

2 files changed

Lines changed: 73 additions & 46 deletions

File tree

rust/pin-init/src/__internal.rs

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -218,32 +218,42 @@ fn stack_init_reuse() {
218218
/// When a value of this type is dropped, it drops a `T`.
219219
///
220220
/// Can be forgotten to prevent the drop.
221+
///
222+
/// # Invariants
223+
///
224+
/// - `ptr` is valid and properly aligned.
225+
/// - `*ptr` is initialized and owned by this guard.
221226
pub struct DropGuard<T: ?Sized> {
222227
ptr: *mut T,
223228
}
224229

225230
impl<T: ?Sized> DropGuard<T> {
226-
/// Creates a new [`DropGuard<T>`]. It will [`ptr::drop_in_place`] `ptr` when it gets dropped.
231+
/// Creates a drop guard and transfer the ownership of the pointer content.
227232
///
228-
/// # Safety
233+
/// The ownership is only relinquished if the guard is forgotten via [`core::mem::forget`].
229234
///
230-
/// `ptr` must be a valid pointer.
235+
/// # Safety
231236
///
232-
/// It is the callers responsibility that `self` will only get dropped if the pointee of `ptr`:
233-
/// - has not been dropped,
234-
/// - is not accessible by any other means,
235-
/// - will not be dropped by any other means.
237+
/// - `ptr` is valid and properly aligned.
238+
/// - `*ptr` is initialized, and the ownership is transferred to this guard.
236239
#[inline]
237240
pub unsafe fn new(ptr: *mut T) -> Self {
241+
// INVARIANT: By safety requirement.
238242
Self { ptr }
239243
}
244+
245+
/// Create a let binding for accessor use.
246+
#[inline]
247+
pub fn let_binding(&mut self) -> &mut T {
248+
// SAFETY: Per type invariant.
249+
unsafe { &mut *self.ptr }
250+
}
240251
}
241252

242253
impl<T: ?Sized> Drop for DropGuard<T> {
243254
#[inline]
244255
fn drop(&mut self) {
245-
// SAFETY: A `DropGuard` can only be constructed using the unsafe `new` function
246-
// ensuring that this operation is safe.
256+
// SAFETY: `self.ptr` is valid, properly aligned and `*self.ptr` is owned by this guard.
247257
unsafe { ptr::drop_in_place(self.ptr) }
248258
}
249259
}

rust/pin-init/src/macros.rs

Lines changed: 54 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1310,27 +1310,33 @@ macro_rules! __init_internal {
13101310
// return when an error/panic occurs.
13111311
// We also use the `data` to require the correct trait (`Init` or `PinInit`) for `$field`.
13121312
unsafe { $data.$field(::core::ptr::addr_of_mut!((*$slot).$field), init)? };
1313-
// NOTE: the field accessor ensures that the initialized field is properly aligned.
1313+
// NOTE: this ensures that the initialized field is properly aligned.
13141314
// Unaligned fields will cause the compiler to emit E0793. We do not support
13151315
// unaligned fields since `Init::__init` requires an aligned pointer; the call to
13161316
// `ptr::write` below has the same requirement.
1317-
// SAFETY:
1318-
// - the project function does the correct field projection,
1319-
// - the field has been initialized,
1320-
// - the reference is only valid until the end of the initializer.
1321-
#[allow(unused_variables)]
1322-
let $field = $crate::macros::paste!(unsafe { $data.[< __project_ $field >](&mut (*$slot).$field) });
1317+
// SAFETY: the field has been initialized.
1318+
let _ = unsafe { &mut (*$slot).$field };
13231319

13241320
// Create the drop guard:
13251321
//
13261322
// We rely on macro hygiene to make it impossible for users to access this local variable.
13271323
// We use `paste!` to create new hygiene for `$field`.
13281324
$crate::macros::paste! {
1329-
// SAFETY: We forget the guard later when initialization has succeeded.
1330-
let [< __ $field _guard >] = unsafe {
1325+
// SAFETY:
1326+
// - `addr_of_mut!((*$slot).$field)` is valid.
1327+
// - `(*$slot).$field` has been initialized above.
1328+
// - We only need the ownership to the pointee back when initialization has
1329+
// succeeded, where we `forget` the guard.
1330+
let mut [< __ $field _guard >] = unsafe {
13311331
$crate::__internal::DropGuard::new(::core::ptr::addr_of_mut!((*$slot).$field))
13321332
};
13331333

1334+
// NOTE: The reference is derived from the guard so that it only lives as long as
1335+
// the guard does and cannot escape the scope.
1336+
#[allow(unused_variables)]
1337+
// SAFETY: the project function does the correct field projection.
1338+
let $field = unsafe { $data.[< __project_ $field >]([< __ $field _guard >].let_binding()) };
1339+
13341340
$crate::__init_internal!(init_slot($use_data):
13351341
@data($data),
13361342
@slot($slot),
@@ -1353,27 +1359,30 @@ macro_rules! __init_internal {
13531359
// return when an error/panic occurs.
13541360
unsafe { $crate::Init::__init(init, ::core::ptr::addr_of_mut!((*$slot).$field))? };
13551361

1356-
// NOTE: the field accessor ensures that the initialized field is properly aligned.
1362+
// NOTE: this ensures that the initialized field is properly aligned.
13571363
// Unaligned fields will cause the compiler to emit E0793. We do not support
13581364
// unaligned fields since `Init::__init` requires an aligned pointer; the call to
13591365
// `ptr::write` below has the same requirement.
1360-
// SAFETY:
1361-
// - the field is not structurally pinned, since the line above must compile,
1362-
// - the field has been initialized,
1363-
// - the reference is only valid until the end of the initializer.
1364-
#[allow(unused_variables)]
1365-
let $field = unsafe { &mut (*$slot).$field };
1366+
// SAFETY: the field has been initialized.
1367+
let _ = unsafe { &mut (*$slot).$field };
13661368

13671369
// Create the drop guard:
13681370
//
13691371
// We rely on macro hygiene to make it impossible for users to access this local variable.
13701372
// We use `paste!` to create new hygiene for `$field`.
13711373
$crate::macros::paste! {
1372-
// SAFETY: We forget the guard later when initialization has succeeded.
1373-
let [< __ $field _guard >] = unsafe {
1374+
// SAFETY:
1375+
// - `addr_of_mut!((*$slot).$field)` is valid.
1376+
// - `(*$slot).$field` has been initialized above.
1377+
// - We only need the ownership to the pointee back when initialization has
1378+
// succeeded, where we `forget` the guard.
1379+
let mut [< __ $field _guard >] = unsafe {
13741380
$crate::__internal::DropGuard::new(::core::ptr::addr_of_mut!((*$slot).$field))
13751381
};
13761382

1383+
#[allow(unused_variables)]
1384+
let $field = [< __ $field _guard >].let_binding();
1385+
13771386
$crate::__init_internal!(init_slot():
13781387
@data($data),
13791388
@slot($slot),
@@ -1397,28 +1406,30 @@ macro_rules! __init_internal {
13971406
unsafe { ::core::ptr::write(::core::ptr::addr_of_mut!((*$slot).$field), $field) };
13981407
}
13991408

1400-
// NOTE: the field accessor ensures that the initialized field is properly aligned.
1409+
// NOTE: this ensures that the initialized field is properly aligned.
14011410
// Unaligned fields will cause the compiler to emit E0793. We do not support
14021411
// unaligned fields since `Init::__init` requires an aligned pointer; the call to
14031412
// `ptr::write` below has the same requirement.
1404-
#[allow(unused_variables)]
1405-
// SAFETY:
1406-
// - the field is not structurally pinned, since no `use_data` was required to create this
1407-
// initializer,
1408-
// - the field has been initialized,
1409-
// - the reference is only valid until the end of the initializer.
1410-
let $field = unsafe { &mut (*$slot).$field };
1413+
// SAFETY: the field has been initialized.
1414+
let _ = unsafe { &mut (*$slot).$field };
14111415

14121416
// Create the drop guard:
14131417
//
14141418
// We rely on macro hygiene to make it impossible for users to access this local variable.
14151419
// We use `paste!` to create new hygiene for `$field`.
14161420
$crate::macros::paste! {
1417-
// SAFETY: We forget the guard later when initialization has succeeded.
1418-
let [< __ $field _guard >] = unsafe {
1421+
// SAFETY:
1422+
// - `addr_of_mut!((*$slot).$field)` is valid.
1423+
// - `(*$slot).$field` has been initialized above.
1424+
// - We only need the ownership to the pointee back when initialization has
1425+
// succeeded, where we `forget` the guard.
1426+
let mut [< __ $field _guard >] = unsafe {
14191427
$crate::__internal::DropGuard::new(::core::ptr::addr_of_mut!((*$slot).$field))
14201428
};
14211429

1430+
#[allow(unused_variables)]
1431+
let $field = [< __ $field _guard >].let_binding();
1432+
14221433
$crate::__init_internal!(init_slot():
14231434
@data($data),
14241435
@slot($slot),
@@ -1441,27 +1452,33 @@ macro_rules! __init_internal {
14411452
// SAFETY: The memory at `slot` is uninitialized.
14421453
unsafe { ::core::ptr::write(::core::ptr::addr_of_mut!((*$slot).$field), $field) };
14431454
}
1444-
// NOTE: the field accessor ensures that the initialized field is properly aligned.
1455+
// NOTE: this ensures that the initialized field is properly aligned.
14451456
// Unaligned fields will cause the compiler to emit E0793. We do not support
14461457
// unaligned fields since `Init::__init` requires an aligned pointer; the call to
14471458
// `ptr::write` below has the same requirement.
1448-
// SAFETY:
1449-
// - the project function does the correct field projection,
1450-
// - the field has been initialized,
1451-
// - the reference is only valid until the end of the initializer.
1452-
#[allow(unused_variables)]
1453-
let $field = $crate::macros::paste!(unsafe { $data.[< __project_ $field >](&mut (*$slot).$field) });
1459+
// SAFETY: the field has been initialized.
1460+
let _ = unsafe { &mut (*$slot).$field };
14541461

14551462
// Create the drop guard:
14561463
//
14571464
// We rely on macro hygiene to make it impossible for users to access this local variable.
14581465
// We use `paste!` to create new hygiene for `$field`.
14591466
$crate::macros::paste! {
1460-
// SAFETY: We forget the guard later when initialization has succeeded.
1461-
let [< __ $field _guard >] = unsafe {
1467+
// SAFETY:
1468+
// - `addr_of_mut!((*$slot).$field)` is valid.
1469+
// - `(*$slot).$field` has been initialized above.
1470+
// - We only need the ownership to the pointee back when initialization has
1471+
// succeeded, where we `forget` the guard.
1472+
let mut [< __ $field _guard >] = unsafe {
14621473
$crate::__internal::DropGuard::new(::core::ptr::addr_of_mut!((*$slot).$field))
14631474
};
14641475

1476+
// NOTE: The reference is derived from the guard so that it only lives as long as
1477+
// the guard does and cannot escape the scope.
1478+
#[allow(unused_variables)]
1479+
// SAFETY: the project function does the correct field projection.
1480+
let $field = unsafe { $data.[< __project_ $field >]([< __ $field _guard >].let_binding()) };
1481+
14651482
$crate::__init_internal!(init_slot($use_data):
14661483
@data($data),
14671484
@slot($slot),

0 commit comments

Comments
 (0)