Skip to content

kernel/process_{standard,loading}: don't create &mut ref to app memory#4714

Merged
alevy merged 4 commits intomasterfrom
dev/process-standard-create-app-memory-raw-slice-ptr
Feb 11, 2026
Merged

kernel/process_{standard,loading}: don't create &mut ref to app memory#4714
alevy merged 4 commits intomasterfrom
dev/process-standard-create-app-memory-raw-slice-ptr

Conversation

@lschuermann
Copy link
Copy Markdown
Member

@lschuermann lschuermann commented Jan 8, 2026

Pull Request Overview

This changes ProcessStandard::create and its users in process loading to operate on app memory referenced by a raw slice pointer, instead of a unique (mutable) Rust reference.

Currently, during process loading, Tock creates and stores Rust multiple shared and unique Rust references pointing into process memory. This is dangerous, for multiple reasons: first, the pointee may not be properly initialized memory, violating a basic Rust invariant [1]. Furthermore, the memory may be concurrently accessed and modified by the process, either directly (in the case of write-acessible RAM), or indirectly through system calls to a flash controller.

We should avoid creating Rust references into process memory, except for rare, carefully reasoned about cases. This includes the process buffer infrastructure, to give capsules ephemeral access to select regions in process memory, and the kernel's grant region.

Even if we would only use Rust references during process loading, and only on memory that was previously initialized, this is still hard to reason about. For instance, we would have to ensure that no process runs after all processes have been loaded, and all Rust references to process memory have gone out of scope. Additions like dynamic process loading make establishing and reasoning about these guarantees difficult. Instead, using raw slice pointers (*mut [u8]) is a more obviously safer choice, onto which Rust places much fewer safety requirements.

Testing Strategy

Compiling and careful review?

TODO or Help Wanted

N/A

Documentation Updated

  • Updated the relevant files in /docs, or no updates are required.

Formatting

  • Ran make prepush.

@github-actions github-actions Bot added the kernel label Jan 8, 2026
@lschuermann lschuermann force-pushed the dev/process-standard-create-app-memory-raw-slice-ptr branch from 8a32455 to 15c17a9 Compare January 8, 2026 15:26
Comment thread kernel/src/process_loading.rs
Comment thread kernel/src/process_standard.rs Outdated
Comment thread kernel/src/process_standard.rs Outdated
jrvanwhy added a commit to jrvanwhy/tock that referenced this pull request Jan 9, 2026
This changes PR tock#4714 to avoid `as` casting pointers. Instead, this uses methods to perform the casts (mostly `cast` and `addr`), which IMO makes it easier to understand what the code is doing.

This also changes `ProcessStandard::create` to derive the provenance of pointers from `remaining_memory` rather than picking it up from an exposed provenance. I *think* this is a bugfix.
lschuermann added a commit that referenced this pull request Jan 12, 2026
jrvanwhy
jrvanwhy previously approved these changes Jan 12, 2026
Comment on lines 709 to 718
let load_result = load_process(
self.kernel,
self.chip,
process_binary,
self.app_memory.take(),
self.app_memory.get(),
short_app_id,
index,
self.fault_policy,
self.storage_policy,
);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this change? Semantically, we are passing ownership of the remaining app memory to the load process function and then getting back the updated memory region when the function returns.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because *mut [u8] cannot not implement Default (it's missing pointer metadata) whereas &'static mut [u8] does (where the default value is an empty slice).

app_memory is a Cell<>, not an OptionalCell<> or TakeCell<>. I agree that what we're doing conceptually is taking and then replacing, but what the previous code did with take() is immediately replace app_memory with the empty slice. Which is probably actually not what we wanted to do.

I can change this to an OptionalCell if you prefer, and then restore the .take() semantics?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do think it's worth changing, but shouldn't it be a TakeCell? I know that isn't what we normally use, but typically things which are memory are typically not copyable.

Copy link
Copy Markdown
Member Author

@lschuermann lschuermann Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So TakeCell must hold a reference with a given lifetime, but this is now a raw pointer. So it wouldn't work. OptionalCell would, which I can change this to.

Which raises an orthogonal question I've had for a long time: why do we have TakeCell<'a, T> when you can use OptionalCell<&'a T>MapCell<&'a T>? It seems like ~~OptionalCell~~MapCellsupports a strict superset of the types thatTakeCelldoes, with almost the same APIs. Shouldn'tTakeCell` be removed?

Copy link
Copy Markdown
Contributor

@jrvanwhy jrvanwhy Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is totally outside the scope of this PR, but IMO OptionalCell should be deprecated. Nowadays Cell<Option<T>> has almost all the functionality we want. If we still want the remaining utilities (is_some, is_none, contains, etc) then an extension trait seems like a better place for them.

Copy link
Copy Markdown
Member Author

@lschuermann lschuermann Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, and I actually meant to say MapCell above, which works well for objects that aren't Copy such as unique references.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm confused. Can we use MapCell then? Something with take() semantics. It seems too easy to lose track of who owns the pointer when not using slices.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I'll change it to use MapCell.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now changed to use MapCell.

Comment thread kernel/src/process_standard.rs
lschuermann and others added 3 commits January 31, 2026 15:21
This changes `ProcessStandard::create` and its users in process
loading to operate on app memory referenced by a raw slice pointer,
instead of a unique (mutable) Rust reference.

Currently, during process loading, Tock creates and stores Rust
multiple shared and unique Rust references pointing into process
memory. This is dangerous, for multiple reasons: first, the pointee
may not be properly initialized memory, violating a basic Rust
invariant [1]. Furthermore, the memory may be concurrently accessed
and modified by the process, either directly (in the case of
write-acessible RAM), or indirectly through system calls to a flash
controller.

We should avoid creating Rust references into process memory, except
for rare, carefully reasoned about cases. This includes the process
buffer infrastructure, to give capsules ephemeral access to select
regions in process memory, and the kernel's grant region.

Even if we would only use Rust reference _during_ process loading, and
only on memory that was previously initialized, this is still hard to
reason about. For instance, we would have to ensure that no process
runs after all processes have been loaded, and all Rust references to
process memory have gone out of scope. Additions like dynamic process
loading make establishing and reasoning about these guarantees
difficult. Instead, using raw slice pointers (`*mut [u8]`) is a more
obviously safer choice, onto which Rust places much fewer safety
requirements.
This changes PR #4714 to avoid `as` casting pointers. Instead, this uses methods to perform the casts (mostly `cast` and `addr`), which IMO makes it easier to understand what the code is doing.

This also changes `ProcessStandard::create` to derive the provenance of pointers from `remaining_memory` rather than picking it up from an exposed provenance. I *think* this is a bugfix.
@lschuermann lschuermann force-pushed the dev/process-standard-create-app-memory-raw-slice-ptr branch from 430801e to 5c584cb Compare January 31, 2026 20:21
jrvanwhy
jrvanwhy previously approved these changes Jan 31, 2026
@lschuermann lschuermann force-pushed the dev/process-standard-create-app-memory-raw-slice-ptr branch from d3eaf1d to 8d5b9ec Compare February 4, 2026 16:55
@alevy alevy added this pull request to the merge queue Feb 11, 2026
Merged via the queue into master with commit 95bf605 Feb 11, 2026
21 checks passed
@alevy alevy deleted the dev/process-standard-create-app-memory-raw-slice-ptr branch February 11, 2026 17:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants