548: Fixed aliasing issue due to RacyCell implementation r=perlindgren a=korken89



Co-authored-by: Emil Fresk <emil.fresk@gmail.com>
Co-authored-by: Per Lindgren <per.lindgren@ltu.se>
This commit is contained in:
bors[bot] 2021-11-03 08:45:53 +00:00 committed by GitHub
commit 7155b55ac8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 68 additions and 51 deletions

View file

@ -15,7 +15,6 @@ mod app {
#[shared] #[shared]
struct Shared { struct Shared {
#[cfg(debug_assertions)] // <- `true` when using the `dev` profile
count: u32, count: u32,
#[cfg(never)] #[cfg(never)]
unused: u32, unused: u32,
@ -31,7 +30,6 @@ mod app {
( (
Shared { Shared {
#[cfg(debug_assertions)]
count: 0, count: 0,
#[cfg(never)] #[cfg(never)]
unused: 1, unused: 1,

View file

@ -135,7 +135,7 @@ pub fn app(app: &App, analysis: &Analysis, extra: &Extra) -> TokenStream2 {
rtic::export::interrupt::free(|_| { rtic::export::interrupt::free(|_| {
use rtic::Monotonic as _; use rtic::Monotonic as _;
use rtic::time::Clock as _; use rtic::time::Clock as _;
if let Some(m) = unsafe{ super::super::#ident.get_mut_unchecked() } { if let Some(m) = unsafe{ &mut *super::super::#ident.get_mut() } {
if let Ok(v) = m.try_now() { if let Ok(v) = m.try_now() {
v v
} else { } else {

View file

@ -78,12 +78,12 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec<TokenStrea
#(#cfgs)* #(#cfgs)*
#t::#name => { #t::#name => {
let #tupled = let #tupled =
#inputs (&*#inputs
.get_unchecked() .get())
.get_unchecked(usize::from(index)) .get_unchecked(usize::from(index))
.as_ptr() .as_ptr()
.read(); .read();
#fq.get_mut_unchecked().split().0.enqueue_unchecked(index); (&mut *#fq.get_mut()).split().0.enqueue_unchecked(index);
let priority = &rtic::export::Priority::new(PRIORITY); let priority = &rtic::export::Priority::new(PRIORITY);
#name( #name(
#name::Context::new(priority) #name::Context::new(priority)
@ -95,7 +95,7 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec<TokenStrea
.collect::<Vec<_>>(); .collect::<Vec<_>>();
stmts.push(quote!( stmts.push(quote!(
while let Some((task, index)) = #rq.get_mut_unchecked().split().1.dequeue() { while let Some((task, index)) = (&mut *#rq.get_mut()).split().1.dequeue() {
match task { match task {
#(#arms)* #(#arms)*
} }

View file

@ -57,9 +57,9 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2,
let expr = if is_declared { let expr = if is_declared {
// If the local resources is already initialized, we only need to access its value and // If the local resources is already initialized, we only need to access its value and
// not go through an `MaybeUninit` // not go through an `MaybeUninit`
quote!(#mangled_name.get_mut_unchecked()) quote!(&mut *#mangled_name.get_mut())
} else { } else {
quote!(&mut *#mangled_name.get_mut_unchecked().as_mut_ptr()) quote!(&mut *(&mut *#mangled_name.get_mut()).as_mut_ptr())
}; };
values.push(quote!( values.push(quote!(

View file

@ -232,15 +232,15 @@ pub fn codegen(
let input = #tupled; let input = #tupled;
unsafe { unsafe {
if let Some(index) = rtic::export::interrupt::free(|_| #fq.get_mut_unchecked().dequeue()) { if let Some(index) = rtic::export::interrupt::free(|_| (&mut *#fq.get_mut()).dequeue()) {
#inputs (&mut *#inputs
.get_mut_unchecked() .get_mut())
.get_unchecked_mut(usize::from(index)) .get_unchecked_mut(usize::from(index))
.as_mut_ptr() .as_mut_ptr()
.write(input); .write(input);
rtic::export::interrupt::free(|_| { rtic::export::interrupt::free(|_| {
#rq.get_mut_unchecked().enqueue_unchecked((#t::#name, index)); (&mut *#rq.get_mut()).enqueue_unchecked((#t::#name, index));
}); });
rtic::pend(#device::#enum_::#interrupt); rtic::pend(#device::#enum_::#interrupt);
@ -330,16 +330,16 @@ pub fn codegen(
impl #internal_spawn_handle_ident { impl #internal_spawn_handle_ident {
pub fn cancel(self) -> Result<#ty, ()> { pub fn cancel(self) -> Result<#ty, ()> {
rtic::export::interrupt::free(|_| unsafe { rtic::export::interrupt::free(|_| unsafe {
let tq = #tq.get_mut_unchecked(); let tq = &mut *#tq.get_mut();
if let Some((_task, index)) = tq.cancel_marker(self.marker) { if let Some((_task, index)) = tq.cancel_marker(self.marker) {
// Get the message // Get the message
let msg = #inputs let msg = (&*#inputs
.get_unchecked() .get())
.get_unchecked(usize::from(index)) .get_unchecked(usize::from(index))
.as_ptr() .as_ptr()
.read(); .read();
// Return the index to the free queue // Return the index to the free queue
#fq.get_mut_unchecked().split().0.enqueue_unchecked(index); (&mut *#fq.get_mut()).split().0.enqueue_unchecked(index);
Ok(msg) Ok(msg)
} else { } else {
@ -359,10 +359,10 @@ pub fn codegen(
pub fn reschedule_at(self, instant: rtic::time::Instant<#mono_type>) -> Result<Self, ()> pub fn reschedule_at(self, instant: rtic::time::Instant<#mono_type>) -> Result<Self, ()>
{ {
rtic::export::interrupt::free(|_| unsafe { rtic::export::interrupt::free(|_| unsafe {
let marker = *#tq_marker.get_mut_unchecked(); let marker = #tq_marker.get().read();
*#tq_marker.get_mut_unchecked() = #tq_marker.get_mut_unchecked().wrapping_add(1); #tq_marker.get_mut().write(marker.wrapping_add(1));
let tq = #tq.get_mut_unchecked(); let tq = (&mut *#tq.get_mut());
tq.update_marker(self.marker, marker, instant, || #pend).map(|_| #name::#m::SpawnHandle { marker }) tq.update_marker(self.marker, marker, instant, || #pend).map(|_| #name::#m::SpawnHandle { marker })
}) })
@ -383,7 +383,7 @@ pub fn codegen(
D::T: Into<<#mono_type as rtic::time::Clock>::T>, D::T: Into<<#mono_type as rtic::time::Clock>::T>,
{ {
let instant = if rtic::export::interrupt::free(|_| unsafe { #m_ident.get_mut_unchecked().is_none() }) { let instant = if rtic::export::interrupt::free(|_| unsafe { (&*#m_ident.get()).is_none() }) {
rtic::time::Instant::new(0) rtic::time::Instant::new(0)
} else { } else {
monotonics::#m::now() monotonics::#m::now()
@ -401,21 +401,21 @@ pub fn codegen(
) -> Result<#name::#m::SpawnHandle, #ty> { ) -> Result<#name::#m::SpawnHandle, #ty> {
unsafe { unsafe {
let input = #tupled; let input = #tupled;
if let Some(index) = rtic::export::interrupt::free(|_| #fq.get_mut_unchecked().dequeue()) { if let Some(index) = rtic::export::interrupt::free(|_| (&mut *#fq.get_mut()).dequeue()) {
#inputs (&mut *#inputs
.get_mut_unchecked() .get_mut())
.get_unchecked_mut(usize::from(index)) .get_unchecked_mut(usize::from(index))
.as_mut_ptr() .as_mut_ptr()
.write(input); .write(input);
#instants (&mut *#instants
.get_mut_unchecked() .get_mut())
.get_unchecked_mut(usize::from(index)) .get_unchecked_mut(usize::from(index))
.as_mut_ptr() .as_mut_ptr()
.write(instant); .write(instant);
rtic::export::interrupt::free(|_| { rtic::export::interrupt::free(|_| {
let marker = *#tq_marker.get_mut_unchecked(); let marker = #tq_marker.get().read();
let nr = rtic::export::NotReady { let nr = rtic::export::NotReady {
instant, instant,
index, index,
@ -423,15 +423,15 @@ pub fn codegen(
marker, marker,
}; };
*#tq_marker.get_mut_unchecked() = #tq_marker.get_mut_unchecked().wrapping_add(1); #tq_marker.get_mut().write(#tq_marker.get().read().wrapping_add(1));
let tq = #tq.get_mut_unchecked(); let tq = &mut *#tq.get_mut();
tq.enqueue_unchecked( tq.enqueue_unchecked(
nr, nr,
|| #enable_interrupt, || #enable_interrupt,
|| #pend, || #pend,
#m_ident.get_mut_unchecked().as_mut()); (&mut *#m_ident.get_mut()).as_mut());
Ok(#name::#m::SpawnHandle { marker }) Ok(#name::#m::SpawnHandle { marker })
}) })

View file

@ -19,10 +19,9 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec<TokenStream2> {
// We include the cfgs // We include the cfgs
#(#cfgs)* #(#cfgs)*
// Resource is a RacyCell<MaybeUninit<T>> // Resource is a RacyCell<MaybeUninit<T>>
// - `get_mut_unchecked` to obtain `MaybeUninit<T>` // - `get_mut` to obtain a raw pointer to `MaybeUninit<T>`
// - `as_mut_ptr` to obtain a raw pointer to `MaybeUninit<T>`
// - `write` the defined value for the late resource T // - `write` the defined value for the late resource T
#mangled_name.get_mut_unchecked().as_mut_ptr().write(shared_resources.#name); #mangled_name.get_mut().write(core::mem::MaybeUninit::new(shared_resources.#name));
)); ));
} }
} }
@ -37,10 +36,9 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec<TokenStream2> {
// We include the cfgs // We include the cfgs
#(#cfgs)* #(#cfgs)*
// Resource is a RacyCell<MaybeUninit<T>> // Resource is a RacyCell<MaybeUninit<T>>
// - `get_mut_unchecked` to obtain `MaybeUninit<T>` // - `get_mut` to obtain a raw pointer to `MaybeUninit<T>`
// - `as_mut_ptr` to obtain a raw pointer to `MaybeUninit<T>`
// - `write` the defined value for the late resource T // - `write` the defined value for the late resource T
#mangled_name.get_mut_unchecked().as_mut_ptr().write(local_resources.#name); #mangled_name.get_mut().write(core::mem::MaybeUninit::new(local_resources.#name));
)); ));
} }
} }
@ -58,7 +56,7 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec<TokenStream2> {
// Store the monotonic // Store the monotonic
let name = util::monotonic_ident(&monotonic.to_string()); let name = util::monotonic_ident(&monotonic.to_string());
stmts.push(quote!(*#name.get_mut_unchecked() = Some(monotonics.#idx);)); stmts.push(quote!(#name.get_mut().write(Some(monotonics.#idx));));
} }
// Enable the interrupts -- this completes the `init`-ialization phase // Enable the interrupts -- this completes the `init`-ialization phase

View file

@ -19,7 +19,7 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream
let fq_ident = util::fq_ident(name); let fq_ident = util::fq_ident(name);
stmts.push(quote!( stmts.push(quote!(
(0..#cap).for_each(|i| #fq_ident.get_mut_unchecked().enqueue_unchecked(i)); (0..#cap).for_each(|i| (&mut *#fq_ident.get_mut()).enqueue_unchecked(i));
)); ));
} }

View file

@ -69,7 +69,7 @@ pub fn codegen(
let ptr = quote!( let ptr = quote!(
#(#cfgs)* #(#cfgs)*
#mangled_name.get_mut_unchecked().as_mut_ptr() #mangled_name.get_mut() as *mut _
); );
let ceiling = match analysis.ownerships.get(name) { let ceiling = match analysis.ownerships.get(name) {

View file

@ -75,9 +75,9 @@ pub fn codegen(ctxt: Context, needs_lt: &mut bool, app: &App) -> (TokenStream2,
} }
let expr = if access.is_exclusive() { let expr = if access.is_exclusive() {
quote!(&mut *#mangled_name.get_mut_unchecked().as_mut_ptr()) quote!(&mut *(&mut *#mangled_name.get_mut()).as_mut_ptr())
} else { } else {
quote!(&*#mangled_name.get_unchecked().as_ptr()) quote!(&*(&*#mangled_name.get()).as_ptr())
}; };
values.push(quote!( values.push(quote!(

View file

@ -117,7 +117,7 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec<TokenStrea
quote!( quote!(
#(#cfgs)* #(#cfgs)*
#t::#name => { #t::#name => {
rtic::export::interrupt::free(|_| #rq.get_mut_unchecked().split().0.enqueue_unchecked((#rqt::#name, index))); rtic::export::interrupt::free(|_| (&mut *#rq.get_mut()).split().0.enqueue_unchecked((#rqt::#name, index)));
#pend #pend
} }
@ -137,8 +137,8 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec<TokenStrea
#[allow(non_snake_case)] #[allow(non_snake_case)]
unsafe fn #bound_interrupt() { unsafe fn #bound_interrupt() {
while let Some((task, index)) = rtic::export::interrupt::free(|_| while let Some((task, index)) = rtic::export::interrupt::free(|_|
if let Some(mono) = #m_ident.get_mut_unchecked().as_mut() { if let Some(mono) = (&mut *#m_ident.get_mut()).as_mut() {
#tq.get_mut_unchecked().dequeue(|| #disable_isr, mono) (&mut *#tq.get_mut()).dequeue(|| #disable_isr, mono)
} else { } else {
// We can only use the timer queue if `init` has returned, and it // We can only use the timer queue if `init` has returned, and it
// writes the `Some(monotonic)` we are accessing here. // writes the `Some(monotonic)` we are accessing here.
@ -150,7 +150,7 @@ pub fn codegen(app: &App, analysis: &Analysis, _extra: &Extra) -> Vec<TokenStrea
} }
} }
rtic::export::interrupt::free(|_| if let Some(mono) = #m_ident.get_mut_unchecked().as_mut() { rtic::export::interrupt::free(|_| if let Some(mono) = (&mut *#m_ident.get_mut()).as_mut() {
mono.on_interrupt(); mono.on_interrupt();
}); });
} }

View file

@ -59,6 +59,27 @@ where
use core::cell::UnsafeCell; use core::cell::UnsafeCell;
/// Internal replacement for `static mut T` /// Internal replacement for `static mut T`
///
/// Used to represent RTIC Resources
///
/// Soundness:
/// 1) Unsafe API for internal use only
/// 2) get_mut(&self) -> *mut T
/// returns a raw mutable pointer to the inner T
/// casting to &mut T is under control of RTIC
/// RTIC ensures &mut T to be unique under Rust aliasing rules.
///
/// Implementation uses the underlying UnsafeCell<T>
/// self.0.get() -> *mut T
///
/// 3) get(&self) -> *const T
/// returns a raw immutable (const) pointer to the inner T
/// casting to &T is under control of RTIC
/// RTIC ensures &T to be shared under Rust aliasing rules.
///
/// Implementation uses the underlying UnsafeCell<T>
/// self.0.get() -> *mut T, demoted to *const T
///
#[repr(transparent)] #[repr(transparent)]
pub struct RacyCell<T>(UnsafeCell<T>); pub struct RacyCell<T>(UnsafeCell<T>);
@ -69,16 +90,16 @@ impl<T> RacyCell<T> {
RacyCell(UnsafeCell::new(value)) RacyCell(UnsafeCell::new(value))
} }
/// Get `&mut T` /// Get `*mut T`
#[inline(always)] #[inline(always)]
pub unsafe fn get_mut_unchecked(&self) -> &mut T { pub unsafe fn get_mut(&self) -> *mut T {
&mut *self.0.get() self.0.get()
} }
/// Get `&T` /// Get `*const T`
#[inline(always)] #[inline(always)]
pub unsafe fn get_unchecked(&self) -> &T { pub unsafe fn get(&self) -> *const T {
&*self.0.get() self.0.get()
} }
} }