aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEmil Fresk <emil.fresk@gmail.com>2024-03-13 20:51:31 +0100
committerGitHub <noreply@github.com>2024-03-13 19:51:31 +0000
commit82cf534f5db00a2b00565172800f84a434edcb37 (patch)
tree5112ebf05900bac62c8d4a35af4e8ac5f7d9c6df
parent0b365f03eb77302663b751305aac7641b2721eb3 (diff)
Fix thumbv6 source masking (#902)
We unconditionally enabled interrupts on exit of locks, now we only enable interrupts that were disabled by the mask.
-rw-r--r--rtic/CHANGELOG.md6
-rw-r--r--rtic/Cargo.toml2
-rw-r--r--rtic/ci/expected/racy-source-masking.run6
-rw-r--r--rtic/examples/racy-source-masking.rs74
-rw-r--r--rtic/src/export/cortex_source_mask.rs22
5 files changed, 106 insertions, 4 deletions
diff --git a/rtic/CHANGELOG.md b/rtic/CHANGELOG.md
index 22c5078..0f6e85d 100644
--- a/rtic/CHANGELOG.md
+++ b/rtic/CHANGELOG.md
@@ -7,6 +7,12 @@ For each category, *Added*, *Changed*, *Fixed* add new entries at the top!
## [Unreleased]
+## [v2.1.1] - 2024-03-13
+
+### Fixed
+
+- **Soundness fix:** `thumbv6` was subject to race in source mask.
+
## [v2.1.0] - 2024-02-27
### Added
diff --git a/rtic/Cargo.toml b/rtic/Cargo.toml
index d76f6e9..2fcda6d 100644
--- a/rtic/Cargo.toml
+++ b/rtic/Cargo.toml
@@ -22,7 +22,7 @@ name = "rtic"
readme = "../README.md"
repository = "https://github.com/rtic-rs/rtic"
-version = "2.1.0"
+version = "2.1.1"
[package.metadata.docs.rs]
features = ["rtic-macros/test-template"]
diff --git a/rtic/ci/expected/racy-source-masking.run b/rtic/ci/expected/racy-source-masking.run
new file mode 100644
index 0000000..69ebf61
--- /dev/null
+++ b/rtic/ci/expected/racy-source-masking.run
@@ -0,0 +1,6 @@
+foo accessing shared2 resource start
+pending bar
+bar pended
+foo accessing shared2 resource end
+bar running
+bar accesing shared2 resource
diff --git a/rtic/examples/racy-source-masking.rs b/rtic/examples/racy-source-masking.rs
new file mode 100644
index 0000000..d871d0f
--- /dev/null
+++ b/rtic/examples/racy-source-masking.rs
@@ -0,0 +1,74 @@
+//! Bug in sourcemasking test.
+
+#![no_main]
+#![no_std]
+#![deny(warnings)]
+#![deny(unsafe_code)]
+#![deny(missing_docs)]
+
+use panic_semihosting as _;
+
+#[rtic::app(device = lm3s6965, dispatchers = [GPIOA])]
+mod app {
+ use cortex_m_semihosting::{debug, hprintln};
+
+ #[shared]
+ struct Shared {
+ shared1: u32, //shared between foo and bar, masks GPIOA and GPIOB
+ shared2: u32, //same
+ }
+
+ #[local]
+ struct Local {}
+
+ #[init]
+ fn init(_: init::Context) -> (Shared, Local) {
+ foo::spawn().unwrap();
+
+ (
+ Shared {
+ shared1: 0,
+ shared2: 0,
+ },
+ Local {},
+ )
+ }
+
+ #[task(shared = [shared1, shared2])]
+ async fn foo(c: foo::Context) {
+ let mut shared1 = c.shared.shared1;
+ let mut shared2 = c.shared.shared2;
+
+ shared2.lock(|shared2| {
+ hprintln!("foo accessing shared2 resource start");
+ //GPIOA and GPIOB masked
+ *shared2 += 1; //OK access to shared 1
+ shared1.lock(|shared1| {
+ //GPIOA and GPIOB masked again
+ *shared1 += 1; //so far so ggood
+ });
+
+ hprintln!("pending bar");
+ rtic::pend(lm3s6965::Interrupt::GPIOB);
+ hprintln!("bar pended");
+
+ //GPIOA and GPIOB unmasked
+ //racy access to shared2!
+ *shared2 += 1;
+ hprintln!("foo accessing shared2 resource end");
+ });
+
+ //GPIOA and GPIOB unmasked again
+
+ debug::exit(debug::EXIT_SUCCESS); // Exit QEMU simulator
+ }
+
+ #[task(binds = GPIOB, priority = 2, shared = [shared1, shared2])]
+ fn bar(mut c: bar::Context) {
+ hprintln!("bar running");
+ c.shared.shared2.lock(|shared2| {
+ hprintln!("bar accesing shared2 resource");
+ *shared2 += 1; // this can race with access in foo!
+ });
+ }
+}
diff --git a/rtic/src/export/cortex_source_mask.rs b/rtic/src/export/cortex_source_mask.rs
index 22a9201..a2ebe8c 100644
--- a/rtic/src/export/cortex_source_mask.rs
+++ b/rtic/src/export/cortex_source_mask.rs
@@ -134,12 +134,13 @@ pub unsafe fn lock<T, R, const M: usize>(
} else {
// safe to manipulate outside critical section
let mask = compute_mask(0, ceiling, masks);
+ let old_mask = read_mask(mask);
clear_enable_mask(mask);
// execute closure under protection of raised system ceiling
let r = f(&mut *ptr);
- set_enable_mask(mask);
+ set_enable_mask(mask, old_mask);
// safe to manipulate outside critical section
r
@@ -178,11 +179,26 @@ pub const fn compute_mask<const M: usize>(
// enables interrupts
#[inline(always)]
-unsafe fn set_enable_mask<const M: usize>(mask: Mask<M>) {
+unsafe fn read_mask<const M: usize>(mask: Mask<M>) -> Mask<M> {
+ let mut out = Mask([0; M]);
+
+ for i in 0..M {
+ // This check should involve compile time constants and be optimized out.
+ if mask.0[i] != 0 {
+ out.0[i] = (*NVIC::PTR).iser[i].read();
+ }
+ }
+
+ out
+}
+
+// enables interrupts
+#[inline(always)]
+unsafe fn set_enable_mask<const M: usize>(mask: Mask<M>, old_mask: Mask<M>) {
for i in 0..M {
// This check should involve compile time constants and be optimized out.
if mask.0[i] != 0 {
- (*NVIC::PTR).iser[i].write(mask.0[i]);
+ (*NVIC::PTR).iser[i].write(old_mask.0[i]);
}
}
}