Fix UB in the access of Priority for asyc executors

The `Priority` was generated on the stack in the dispatcher
which caused it to be dropped after usage. This is now fixed
by having the `Priority` being a static variable for executors
This commit is contained in:
Emil Fresk 2022-08-05 11:28:02 +02:00
parent b48a95e879
commit 46a3f2befd
5 changed files with 42 additions and 15 deletions

View file

@ -16,7 +16,10 @@ mod app {
use systick_monotonic::*; use systick_monotonic::*;
#[shared] #[shared]
struct Shared {} struct Shared {
a: u32,
b: u32,
}
#[local] #[local]
struct Local {} struct Local {}
@ -34,7 +37,7 @@ mod app {
async_task2::spawn().ok(); async_task2::spawn().ok();
( (
Shared {}, Shared { a: 0, b: 0 },
Local {}, Local {},
init::Monotonics(Systick::new(cx.core.SYST, 12_000_000)), init::Monotonics(Systick::new(cx.core.SYST, 12_000_000)),
) )
@ -49,24 +52,24 @@ mod app {
} }
} }
#[task(priority = 1)] #[task(priority = 1, shared = [a, b])]
fn normal_task(_cx: normal_task::Context) { fn normal_task(_cx: normal_task::Context) {
hprintln!("hello from normal 1").ok(); hprintln!("hello from normal 1").ok();
} }
#[task(priority = 1)] #[task(priority = 1, shared = [a, b])]
async fn async_task(_cx: async_task::Context) { async fn async_task(_cx: async_task::Context) {
hprintln!("hello from async 1").ok(); hprintln!("hello from async 1").ok();
debug::exit(debug::EXIT_SUCCESS); debug::exit(debug::EXIT_SUCCESS);
} }
#[task(priority = 2)] #[task(priority = 2, shared = [a, b])]
fn normal_task2(_cx: normal_task2::Context) { fn normal_task2(_cx: normal_task2::Context) {
hprintln!("hello from normal 2").ok(); hprintln!("hello from normal 2").ok();
} }
#[task(priority = 2)] #[task(priority = 2, shared = [a, b])]
async fn async_task2(_cx: async_task2::Context) { async fn async_task2(_cx: async_task2::Context) {
hprintln!("hello from async 2").ok(); hprintln!("hello from async 2").ok();
} }

View file

@ -10,17 +10,23 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream
let interrupts = &analysis.interrupts; let interrupts = &analysis.interrupts;
// Generate executor definition in global scope // Generate executor definition and priority in global scope
for (name, task) in app.software_tasks.iter() { for (name, task) in app.software_tasks.iter() {
if task.is_async { if task.is_async {
let type_name = util::internal_task_ident(name, "F"); let type_name = util::internal_task_ident(name, "F");
let exec_name = util::internal_task_ident(name, "EXEC"); let exec_name = util::internal_task_ident(name, "EXEC");
let prio_name = util::internal_task_ident(name, "PRIORITY");
items.push(quote!( items.push(quote!(
type #type_name = impl core::future::Future + 'static; type #type_name = impl core::future::Future + 'static;
static #exec_name: static #exec_name:
rtic::RacyCell<rtic::export::executor::AsyncTaskExecutor<#type_name>> = rtic::RacyCell<rtic::export::executor::AsyncTaskExecutor<#type_name>> =
rtic::RacyCell::new(rtic::export::executor::AsyncTaskExecutor::new()); rtic::RacyCell::new(rtic::export::executor::AsyncTaskExecutor::new());
// The executors priority, this can be any value - we will overwrite it when we
// start a task
static #prio_name: rtic::RacyCell<rtic::export::Priority> =
unsafe { rtic::RacyCell::new(rtic::export::Priority::new(0)) };
)); ));
} }
} }
@ -92,6 +98,7 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream
let inputs = util::inputs_ident(name); let inputs = util::inputs_ident(name);
let (_, tupled, pats, _) = util::regroup_inputs(&task.inputs); let (_, tupled, pats, _) = util::regroup_inputs(&task.inputs);
let exec_name = util::internal_task_ident(name, "EXEC"); let exec_name = util::internal_task_ident(name, "EXEC");
let prio_name = util::internal_task_ident(name, "PRIORITY");
if task.is_async { if task.is_async {
let executor_run_ident = util::executor_run_ident(name); let executor_run_ident = util::executor_run_ident(name);
@ -108,7 +115,10 @@ pub fn codegen(app: &App, analysis: &Analysis, extra: &Extra) -> Vec<TokenStream
.read(); .read();
(&mut *#fq.get_mut()).split().0.enqueue_unchecked(index); (&mut *#fq.get_mut()).split().0.enqueue_unchecked(index);
let priority = &rtic::export::Priority::new(PRIORITY); // The async executor needs a static priority
#prio_name.get_mut().write(rtic::export::Priority::new(PRIORITY));
let priority: &'static _ = &*#prio_name.get();
(&mut *#exec_name.get_mut()).spawn(#name(#name::Context::new(priority) #(,#pats)*)); (&mut *#exec_name.get_mut()).spawn(#name(#name::Context::new(priority) #(,#pats)*));
#executor_run_ident.store(true, core::sync::atomic::Ordering::Relaxed); #executor_run_ident.store(true, core::sync::atomic::Ordering::Relaxed);
} else { } else {

View file

@ -139,17 +139,24 @@ pub fn codegen(
let attrs = &task.attrs; let attrs = &task.attrs;
let cfgs = &task.cfgs; let cfgs = &task.cfgs;
let stmts = &task.stmts; let stmts = &task.stmts;
let async_marker = if task.is_async { let (async_marker, context_lifetime) = if task.is_async {
quote!(async) (
quote!(async),
if shared_needs_lt || local_needs_lt {
quote!(<'static>)
} else {
quote!()
},
)
} else { } else {
quote!() (quote!(), quote!())
}; };
user_tasks.push(quote!( user_tasks.push(quote!(
#(#attrs)* #(#attrs)*
#(#cfgs)* #(#cfgs)*
#[allow(non_snake_case)] #[allow(non_snake_case)]
#async_marker fn #name(#context: #name::Context #(,#inputs)*) { #async_marker fn #name(#context: #name::Context #context_lifetime #(,#inputs)*) {
use rtic::Mutex as _; use rtic::Mutex as _;
use rtic::mutex::prelude::*; use rtic::mutex::prelude::*;

View file

@ -242,7 +242,7 @@ impl Priority {
/// ///
/// Will overwrite the current Priority /// Will overwrite the current Priority
#[inline(always)] #[inline(always)]
pub unsafe fn new(value: u8) -> Self { pub const unsafe fn new(value: u8) -> Self {
Priority { Priority {
inner: Cell::new(value), inner: Cell::new(value),
} }

View file

@ -46,7 +46,14 @@ impl<'a> CargoCommand<'a> {
features, features,
mode, mode,
} => { } => {
let mut args = vec![self.name(), "--example", example, "--target", target]; let mut args = vec![
"+nightly",
self.name(),
"--example",
example,
"--target",
target,
];
if let Some(feature_name) = features { if let Some(feature_name) = features {
args.extend_from_slice(&["--features", feature_name]); args.extend_from_slice(&["--features", feature_name]);
@ -61,7 +68,7 @@ impl<'a> CargoCommand<'a> {
features, features,
mode, mode,
} => { } => {
let mut args = vec![self.name(), "--examples", "--target", target]; let mut args = vec!["+nightly", self.name(), "--examples", "--target", target];
if let Some(feature_name) = features { if let Some(feature_name) = features {
args.extend_from_slice(&["--features", feature_name]); args.extend_from_slice(&["--features", feature_name]);