diff options
| author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2022-08-01 12:33:58 +0000 |
|---|---|---|
| committer | GitHub <[email protected]> | 2022-08-01 12:33:58 +0000 |
| commit | 2b0786129aacb0c5089e74415f45617bbf07a3eb (patch) | |
| tree | 66a63364b921a9e0c72158d33656d3e15999186d | |
| parent | bd6bab1625d90a2dc2a4b57b40dcfaa9516bf791 (diff) | |
| parent | 8d24cba72d6a36533d6858da0e9e2ab9406a420f (diff) | |
Merge #887
887: executor: miri fixes r=Dirbaio a=Dirbaio
Fixes a few MIRI errors due to loosely mixing `&TaskStorage<F>` and `&TaskHeader`. References "downgrade" the provenance. `TaskHeader` is smaller, so once you have a `&TaskHeader` you can't use pointer casts to access the whole `TaskStorage<F>`. This fixes it by always keeping the raw pointer around, which doesn't downgrade provenance.
The error was:
```
[dirbaio@mars std]$ MIRIFLAGS=-Zmiri-disable-isolation cargo miri run --bin tick
Finished dev [unoptimized + debuginfo] target(s) in 0.05s
Running `/home/dirbaio/.rustup/toolchains/nightly-2022-07-13-x86_64-unknown-linux-gnu/bin/cargo-miri target/miri/x86_64-unknown-linux-gnu/debug/tick`
error: Undefined Behavior: trying to reborrow <12349> for SharedReadWrite permission at alloc2[0x30], but that tag does not exist in the borrow stack for this location
--> /home/dirbaio/embassy/embassy/embassy-executor/src/executor/raw/mod.rs:162:20
|
162 | let this = &*(p.as_ptr() as *const TaskStorage<F>);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
| trying to reborrow <12349> for SharedReadWrite permission at alloc2[0x30], but that tag does not exist in the borrow stack for this location
| this error occurs as part of a reborrow at alloc2[0x30..0x40]
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
= help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
help: <12349> was created by a retag at offsets [0x0..0x30]
--> src/bin/tick.rs:15:1
|
15 | #[embassy_executor::main]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
= note: backtrace:
= note: inside `embassy_executor::executor::raw::TaskStorage::<std::future::from_generator::GenFuture<[static generator@src/bin/tick.rs:15:1: 15:26]>>::poll` at /home/dirbaio/embassy/embassy/embassy-executor/src/executor/raw/mod.rs:162:20
= note: inside closure at /home/dirbaio/embassy/embassy/embassy-executor/src/executor/raw/mod.rs:390:13
= note: inside `embassy_executor::executor::raw::run_queue::RunQueue::dequeue_all::<[closure@embassy_executor::executor::raw::Executor::poll::{closure#1}]>` at /home/dirbaio/embassy/embassy/embassy-executor/src/executor/raw/run_queue.rs:69:13
= note: inside `embassy_executor::executor::raw::Executor::poll` at /home/dirbaio/embassy/embassy/embassy-executor/src/executor/raw/mod.rs:373:9
= note: inside `embassy_executor::executor::Executor::run::<[closure@src/bin/tick.rs:15:1: 15:26]>` at /home/dirbaio/embassy/embassy/embassy-executor/src/executor/arch/std.rs:52:22
```
Co-authored-by: Dario Nieuwenhuis <[email protected]>
| -rw-r--r-- | embassy-executor/src/executor/raw/mod.rs | 48 | ||||
| -rw-r--r-- | embassy-executor/src/executor/raw/run_queue.rs | 6 | ||||
| -rw-r--r-- | embassy-executor/src/executor/raw/waker.rs | 4 |
3 files changed, 26 insertions, 32 deletions
diff --git a/embassy-executor/src/executor/raw/mod.rs b/embassy-executor/src/executor/raw/mod.rs index 87317bc02..fb4cc6288 100644 --- a/embassy-executor/src/executor/raw/mod.rs +++ b/embassy-executor/src/executor/raw/mod.rs | |||
| @@ -70,24 +70,6 @@ impl TaskHeader { | |||
| 70 | timer_queue_item: timer_queue::TimerQueueItem::new(), | 70 | timer_queue_item: timer_queue::TimerQueueItem::new(), |
| 71 | } | 71 | } |
| 72 | } | 72 | } |
| 73 | |||
| 74 | pub(crate) unsafe fn enqueue(&self) { | ||
| 75 | critical_section::with(|cs| { | ||
| 76 | let state = self.state.load(Ordering::Relaxed); | ||
| 77 | |||
| 78 | // If already scheduled, or if not started, | ||
| 79 | if (state & STATE_RUN_QUEUED != 0) || (state & STATE_SPAWNED == 0) { | ||
| 80 | return; | ||
| 81 | } | ||
| 82 | |||
| 83 | // Mark it as scheduled | ||
| 84 | self.state.store(state | STATE_RUN_QUEUED, Ordering::Relaxed); | ||
| 85 | |||
| 86 | // We have just marked the task as scheduled, so enqueue it. | ||
| 87 | let executor = &*self.executor.get(); | ||
| 88 | executor.enqueue(cs, self as *const TaskHeader as *mut TaskHeader); | ||
| 89 | }) | ||
| 90 | } | ||
| 91 | } | 73 | } |
| 92 | 74 | ||
| 93 | /// Raw storage in which a task can be spawned. | 75 | /// Raw storage in which a task can be spawned. |
| @@ -155,7 +137,7 @@ impl<F: Future + 'static> TaskStorage<F> { | |||
| 155 | // Initialize the task | 137 | // Initialize the task |
| 156 | self.raw.poll_fn.write(Self::poll); | 138 | self.raw.poll_fn.write(Self::poll); |
| 157 | self.future.write(future()); | 139 | self.future.write(future()); |
| 158 | NonNull::new_unchecked(&self.raw as *const TaskHeader as *mut TaskHeader) | 140 | NonNull::new_unchecked(self as *const TaskStorage<F> as *const TaskHeader as *mut TaskHeader) |
| 159 | } | 141 | } |
| 160 | 142 | ||
| 161 | unsafe fn poll(p: NonNull<TaskHeader>) { | 143 | unsafe fn poll(p: NonNull<TaskHeader>) { |
| @@ -323,7 +305,7 @@ impl Executor { | |||
| 323 | /// - `task` must be set up to run in this executor. | 305 | /// - `task` must be set up to run in this executor. |
| 324 | /// - `task` must NOT be already enqueued (in this executor or another one). | 306 | /// - `task` must NOT be already enqueued (in this executor or another one). |
| 325 | #[inline(always)] | 307 | #[inline(always)] |
| 326 | unsafe fn enqueue(&self, cs: CriticalSection, task: *mut TaskHeader) { | 308 | unsafe fn enqueue(&self, cs: CriticalSection, task: NonNull<TaskHeader>) { |
| 327 | if self.run_queue.enqueue(cs, task) { | 309 | if self.run_queue.enqueue(cs, task) { |
| 328 | (self.signal_fn)(self.signal_ctx) | 310 | (self.signal_fn)(self.signal_ctx) |
| 329 | } | 311 | } |
| @@ -339,11 +321,10 @@ impl Executor { | |||
| 339 | /// In this case, the task's Future must be Send. This is because this is effectively | 321 | /// In this case, the task's Future must be Send. This is because this is effectively |
| 340 | /// sending the task to the executor thread. | 322 | /// sending the task to the executor thread. |
| 341 | pub(super) unsafe fn spawn(&'static self, task: NonNull<TaskHeader>) { | 323 | pub(super) unsafe fn spawn(&'static self, task: NonNull<TaskHeader>) { |
| 342 | let task = task.as_ref(); | 324 | task.as_ref().executor.set(self); |
| 343 | task.executor.set(self); | ||
| 344 | 325 | ||
| 345 | critical_section::with(|cs| { | 326 | critical_section::with(|cs| { |
| 346 | self.enqueue(cs, task as *const _ as _); | 327 | self.enqueue(cs, task); |
| 347 | }) | 328 | }) |
| 348 | } | 329 | } |
| 349 | 330 | ||
| @@ -366,9 +347,7 @@ impl Executor { | |||
| 366 | /// no `poll()` already running. | 347 | /// no `poll()` already running. |
| 367 | pub unsafe fn poll(&'static self) { | 348 | pub unsafe fn poll(&'static self) { |
| 368 | #[cfg(feature = "time")] | 349 | #[cfg(feature = "time")] |
| 369 | self.timer_queue.dequeue_expired(Instant::now(), |p| { | 350 | self.timer_queue.dequeue_expired(Instant::now(), |task| wake_task(task)); |
| 370 | p.as_ref().enqueue(); | ||
| 371 | }); | ||
| 372 | 351 | ||
| 373 | self.run_queue.dequeue_all(|p| { | 352 | self.run_queue.dequeue_all(|p| { |
| 374 | let task = p.as_ref(); | 353 | let task = p.as_ref(); |
| @@ -421,7 +400,22 @@ impl Executor { | |||
| 421 | /// | 400 | /// |
| 422 | /// `task` must be a valid task pointer obtained from [`task_from_waker`]. | 401 | /// `task` must be a valid task pointer obtained from [`task_from_waker`]. |
| 423 | pub unsafe fn wake_task(task: NonNull<TaskHeader>) { | 402 | pub unsafe fn wake_task(task: NonNull<TaskHeader>) { |
| 424 | task.as_ref().enqueue(); | 403 | critical_section::with(|cs| { |
| 404 | let header = task.as_ref(); | ||
| 405 | let state = header.state.load(Ordering::Relaxed); | ||
| 406 | |||
| 407 | // If already scheduled, or if not started, | ||
| 408 | if (state & STATE_RUN_QUEUED != 0) || (state & STATE_SPAWNED == 0) { | ||
| 409 | return; | ||
| 410 | } | ||
| 411 | |||
| 412 | // Mark it as scheduled | ||
| 413 | header.state.store(state | STATE_RUN_QUEUED, Ordering::Relaxed); | ||
| 414 | |||
| 415 | // We have just marked the task as scheduled, so enqueue it. | ||
| 416 | let executor = &*header.executor.get(); | ||
| 417 | executor.enqueue(cs, task); | ||
| 418 | }) | ||
| 425 | } | 419 | } |
| 426 | 420 | ||
| 427 | #[cfg(feature = "time")] | 421 | #[cfg(feature = "time")] |
diff --git a/embassy-executor/src/executor/raw/run_queue.rs b/embassy-executor/src/executor/raw/run_queue.rs index 31615da7e..ed8c82a5c 100644 --- a/embassy-executor/src/executor/raw/run_queue.rs +++ b/embassy-executor/src/executor/raw/run_queue.rs | |||
| @@ -46,10 +46,10 @@ impl RunQueue { | |||
| 46 | /// | 46 | /// |
| 47 | /// `item` must NOT be already enqueued in any queue. | 47 | /// `item` must NOT be already enqueued in any queue. |
| 48 | #[inline(always)] | 48 | #[inline(always)] |
| 49 | pub(crate) unsafe fn enqueue(&self, _cs: CriticalSection, task: *mut TaskHeader) -> bool { | 49 | pub(crate) unsafe fn enqueue(&self, _cs: CriticalSection, task: NonNull<TaskHeader>) -> bool { |
| 50 | let prev = self.head.load(Ordering::Relaxed); | 50 | let prev = self.head.load(Ordering::Relaxed); |
| 51 | (*task).run_queue_item.next.store(prev, Ordering::Relaxed); | 51 | task.as_ref().run_queue_item.next.store(prev, Ordering::Relaxed); |
| 52 | self.head.store(task, Ordering::Relaxed); | 52 | self.head.store(task.as_ptr(), Ordering::Relaxed); |
| 53 | prev.is_null() | 53 | prev.is_null() |
| 54 | } | 54 | } |
| 55 | 55 | ||
diff --git a/embassy-executor/src/executor/raw/waker.rs b/embassy-executor/src/executor/raw/waker.rs index f6ae332fa..6b9c03a62 100644 --- a/embassy-executor/src/executor/raw/waker.rs +++ b/embassy-executor/src/executor/raw/waker.rs | |||
| @@ -2,7 +2,7 @@ use core::mem; | |||
| 2 | use core::ptr::NonNull; | 2 | use core::ptr::NonNull; |
| 3 | use core::task::{RawWaker, RawWakerVTable, Waker}; | 3 | use core::task::{RawWaker, RawWakerVTable, Waker}; |
| 4 | 4 | ||
| 5 | use super::TaskHeader; | 5 | use super::{wake_task, TaskHeader}; |
| 6 | 6 | ||
| 7 | const VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake, drop); | 7 | const VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake, drop); |
| 8 | 8 | ||
| @@ -11,7 +11,7 @@ unsafe fn clone(p: *const ()) -> RawWaker { | |||
| 11 | } | 11 | } |
| 12 | 12 | ||
| 13 | unsafe fn wake(p: *const ()) { | 13 | unsafe fn wake(p: *const ()) { |
| 14 | (*(p as *mut TaskHeader)).enqueue() | 14 | wake_task(NonNull::new_unchecked(p as *mut TaskHeader)) |
| 15 | } | 15 | } |
| 16 | 16 | ||
| 17 | unsafe fn drop(_: *const ()) { | 17 | unsafe fn drop(_: *const ()) { |
