Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
### Bugfixes

- Don't allow unsafe code in initialisation expression of `#[percore]` variables.
- Fixed undefined behaviour in `LinkedPerCore::get` due to pointer provenance.

## 0.2.5

Expand Down
17 changes: 11 additions & 6 deletions src/derive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@
pub mod aarch64;

use crate::lock::ExceptionLock;
use core::ptr::NonNull;
use core::ptr::with_exposed_provenance;
pub use percore_derive::percore;

#[allow(improper_ctypes)]
Expand Down Expand Up @@ -181,23 +181,28 @@ impl<T> LinkedPerCore<T> {
/// Returns a shared reference to the value for the current CPU core.
#[inline(always)]
pub fn get(&self) -> &T {
// SAFETY: PercoreLocalOffset guarantees a valid offset.
let percore_ptr = unsafe { NonNull::from_ref(&self.0).byte_offset(percore_local_offset()) };
// We need to construct a new pointer with exposed provenance rather than just using
// `byte_offset` on the pointer to `self.0` because the per-core copy is not part of the
// same allocation.
let percore_ptr = with_exposed_provenance::<T>(
((&raw const self.0).expose_provenance().cast_signed() + percore_local_offset())

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me know if I'm wrong, but according to the corresponding section in Rust docs this seems incorrect.

  • expose_provenance [...] adds the provenance of the pointer to a global list of 'exposed' provenances [...]
  • with_exposed_provenance can be used to construct a pointer with one of these previously
    'exposed' provenances

In this line we "save" the provenance of the primary core's pointer, and in the previous line create a new pointer by "retrieving" the saved provenance, but that still only describes the primary core's memory area.

I'm not sure if it's possible, but we should rather save the provenance of the pointer that was passed to percore_copy_secondary_data() and use that when constructing the secondary core's pointer here.

.cast_unsigned(),
);

debug_assert!(!percore_ptr.is_null());
debug_assert!(percore_ptr.is_aligned());

// SAFETY:
// * The percore region must be aligned to the maximum alignment of any percore variable,
// and `&self.0` must be aligned as it comes from a reference, so adding the offset to it
// must still be properly aligned. (In debug builds we also double-check with the
// debug_assert above.)
// * The pointer is non-null because it is constructed from NonNull and the offset produces
// a valid address.
// * The pointer is non-null because the offset is guaranteed to produce a valid address.
// * The PercoreLocalOffset implementation promises that the calculated pointer points into
// * the percore memory area which is initialized and it is dereferenceable for the T type.
// * Aliasing is prevented by each core having its own instance of the variable and by
// requiring `ExceptionLock` for `Sync` implementation.
unsafe { percore_ptr.as_ref() }
unsafe { percore_ptr.as_ref_unchecked() }
}
}

Expand Down
Loading