diff options
| author | Oleksandr Babak <[email protected]> | 2024-09-11 10:42:46 +0200 |
|---|---|---|
| committer | Oleksandr Babak <[email protected]> | 2024-09-11 10:46:28 +0200 |
| commit | 29932c295c44cea1a2ab5ad94c5d0bd16c07243d (patch) | |
| tree | 6dc51dbcc45b88e0ed7e1d5e5188f3c718cdf584 | |
| parent | dc98d865ff5a25e33da58fe6090c786ca06500b0 (diff) | |
fix: `select_slice` is unsound. fixes #3320
| -rw-r--r-- | embassy-futures/src/select.rs | 35 |
1 files changed, 15 insertions, 20 deletions
diff --git a/embassy-futures/src/select.rs b/embassy-futures/src/select.rs index 97a81a86d..57f0cb41f 100644 --- a/embassy-futures/src/select.rs +++ b/embassy-futures/src/select.rs | |||
| @@ -237,7 +237,7 @@ impl<Fut: Future, const N: usize> Future for SelectArray<Fut, N> { | |||
| 237 | #[derive(Debug)] | 237 | #[derive(Debug)] |
| 238 | #[must_use = "futures do nothing unless you `.await` or poll them"] | 238 | #[must_use = "futures do nothing unless you `.await` or poll them"] |
| 239 | pub struct SelectSlice<'a, Fut> { | 239 | pub struct SelectSlice<'a, Fut> { |
| 240 | inner: &'a mut [Fut], | 240 | inner: Pin<&'a mut [Fut]>, |
| 241 | } | 241 | } |
| 242 | 242 | ||
| 243 | /// Creates a new future which will select over a slice of futures. | 243 | /// Creates a new future which will select over a slice of futures. |
| @@ -247,31 +247,26 @@ pub struct SelectSlice<'a, Fut> { | |||
| 247 | /// future that was ready. | 247 | /// future that was ready. |
| 248 | /// | 248 | /// |
| 249 | /// If the slice is empty, the resulting future will be Pending forever. | 249 | /// If the slice is empty, the resulting future will be Pending forever. |
| 250 | pub fn select_slice<'a, Fut: Future>(slice: &'a mut [Fut]) -> SelectSlice<'a, Fut> { | 250 | pub fn select_slice<'a, Fut: Future>(slice: Pin<&'a mut [Fut]>) -> SelectSlice<'a, Fut> { |
| 251 | SelectSlice { inner: slice } | 251 | SelectSlice { inner: slice } |
| 252 | } | 252 | } |
| 253 | 253 | ||
| 254 | impl<'a, Fut: Future> Future for SelectSlice<'a, Fut> { | 254 | impl<'a, Fut: Future> Future for SelectSlice<'a, Fut> { |
| 255 | type Output = (Fut::Output, usize); | 255 | type Output = (Fut::Output, usize); |
| 256 | 256 | ||
| 257 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { | 257 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { |
| 258 | // Safety: Since `self` is pinned, `inner` cannot move. Since `inner` cannot move, | 258 | // Safety: refer to |
| 259 | // its elements also cannot move. Therefore it is safe to access `inner` and pin | 259 | // https://users.rust-lang.org/t/working-with-pinned-slices-are-there-any-structurally-pinning-vec-like-collection-types/50634/2 |
| 260 | // references to the contained futures. | 260 | #[inline(always)] |
| 261 | let item = unsafe { | 261 | fn pin_iter<T>(slice: Pin<&mut [T]>) -> impl Iterator<Item = Pin<&mut T>> { |
| 262 | self.get_unchecked_mut() | 262 | unsafe { slice.get_unchecked_mut().iter_mut().map(|v| Pin::new_unchecked(v)) } |
| 263 | .inner | 263 | } |
| 264 | .iter_mut() | 264 | for (i, fut) in pin_iter(self.inner.as_mut()).enumerate() { |
| 265 | .enumerate() | 265 | if let Poll::Ready(res) = fut.poll(cx) { |
| 266 | .find_map(|(i, f)| match Pin::new_unchecked(f).poll(cx) { | 266 | return Poll::Ready((res, i)); |
| 267 | Poll::Pending => None, | 267 | } |
| 268 | Poll::Ready(e) => Some((i, e)), | ||
| 269 | }) | ||
| 270 | }; | ||
| 271 | |||
| 272 | match item { | ||
| 273 | Some((idx, res)) => Poll::Ready((res, idx)), | ||
| 274 | None => Poll::Pending, | ||
| 275 | } | 268 | } |
| 269 | |||
| 270 | Poll::Pending | ||
| 276 | } | 271 | } |
| 277 | } | 272 | } |
