Kernel: Adjustments to unsafe code - #2600
Conversation
| [core::mem::MaybeUninit<crate::task::Task>; HUBRIS_TASK_COUNT] = | ||
| [const { core::mem::MaybeUninit::uninit() }; HUBRIS_TASK_COUNT]; |
There was a problem hiding this comment.
You certainly know more about this area than I do but is this a functional change? I don't think so but this is also accessed via humility so is there any chance this could be disruptive there?
There was a problem hiding this comment.
To the best of my knowledge an "executable" perspective, this is not a functional change: MaybeUninit is repr(transparent), therefore:
MaybeUninit<T>is the same size + align asTMaybeUninit<[T; N]>is the same size + align as[T; N]
If this wasn't the case, this AsRef impl which is stable and safe, wouldn't be sound.
It might cause problems for humility, depending on how we access this symbol. The name and size are the same, but it might be a noticeable type change. I'll test this before going to merge it.
There was a problem hiding this comment.
I agree that this is probably not a functional change from Hubris' perspective and shouldn't actually impact how this is initialized. But, I think we should definitely check if it changes the DWARF in a way that makes Humility sad. If it does, it should be okay to change Humility to handle either type, just a bit annoying.
There was a problem hiding this comment.
Hilariously, I think we're good on this. Running humility tasks and humility dump, both over SWD and net-hiffy, works to totally fine, and I think it's because for legacy compat reasons, we return a base pointer and task count here:
We then read back Task sized objects from the read blobs, which means it doesn't care about this shape change!
| // this frame. | ||
| task.save_mut().psp = frame as *const _ as u32; | ||
| let frame: *const ExtendedExceptionFrame = frame; | ||
| task.save_mut().psp = frame as u32; |
There was a problem hiding this comment.
i wonder if it would be more pedantically correct to use expose_provenance here? i do not think it actually matters, since from strict provenance's perspective, we are never actually turning the u32 back into a pointer in the kernel (while this does happen, it happens in the task when we return from the syscall and not in a way that's visible to Rust)... but it seems like this could technically be treated as a provenance exposing cast?
There was a problem hiding this comment.
I'm somewhat of the opinion that "that's another program's data", and it isn't necessarily relevant to us, the kernel.
The hardware itself swaps from psp back to msp before re-entering the kernel in an interrupt, so I don't think it's necessary. That explanation is all a little handwavey though.
There was a problem hiding this comment.
for the record, i also don't think it's necessary, i was thinking more from a position of "is this something we should generally be in the habit of doing?"
There was a problem hiding this comment.
The expose_provenance docs specifically state: "This is equivalent to self as usize ... Furthermore, this (like the as cast) has the [...] effect of marking the provenance of 'exposed'" so there doesn't seem to be absolute any difference between these to.
What would be interesting if you could use addr(): probably not if it's the hardware using this data.
aapoalas
left a comment
There was a problem hiding this comment.
Please excuse my unrequested comments; the topic called out to me.
| [[config.i2c.devices]] | ||
| controller = 1 | ||
| mux = 1 | ||
| segment = 1 |
There was a problem hiding this comment.
thought: Meh, I wish there was toml formatting. (Mostly because my editor formats it automatically, and has caused annoyance in PRs :) )
| // this frame. | ||
| task.save_mut().psp = frame as *const _ as u32; | ||
| let frame: *const ExtendedExceptionFrame = frame; | ||
| task.save_mut().psp = frame as u32; |
There was a problem hiding this comment.
The expose_provenance docs specifically state: "This is equivalent to self as usize ... Furthermore, this (like the as cast) has the [...] effect of marking the provenance of 'exposed'" so there doesn't seem to be absolute any difference between these to.
What would be interesting if you could use addr(): probably not if it's the hardware using this data.
also add a contrasting comment vs static-cell Similar to #2600.

The following is a number of tweaks to the kernel code that in my opinion, make some of the unsafe code a bit more direct to follow. In particular, these changes:
switch_toas a single method onTask, rather than one version insyscalls.rs, and a handful of semantically-equal copy/pastesascasts as is reasonable, e.g.x as *const _ as *mut _, replacing them with discrete steps that note the implicit casts (references to pointer), and explicit casts, usingptr::cast()unsafeusage a bit instartupwhen initializing each task slot&mut Taskinstead of&Task, as these are used for settingCURRENT_TASK_PTR, which semantically allows us to write-through to that address (often in assembly) when returning to the kernel, and that feels a little sketch to me doing from a pointer with shared provenanceMaybeUninit<[Task; N]>to[MaybeUninit<Task>; N], as this makes startup a little less awkwardIn my opinion, none of these are strictly necessary for soundness, so if we don't want to touch it, I could definitely understand that!
I think 1, 2, and 3 are pretty clear readability/clarity wins.
I think 4 is maybe a little nitpicky, and is a bit more semantically correct, but also unlikely to cause miscompilations in practice.
I think 5 is maybe a mixed bag (we can still make the changes for 3 without 5), and I need to check it isn't going to make
humilityupset as the types have changed (even though the before/after are guaranteed to have the same layout and semantics).Happy to revert any chunks, or rework these into separate commits if that makes reviewing easier.
This is extracted from #2592, and most of these were noticed while I was hacking around on that.