Re: [PATCH v2 3/4] rust: uaccess: add typed accessors for userspace pointers

From: Alice Ryhl
Date: Fri Feb 09 2024 - 05:40:45 EST


On Thu, Feb 8, 2024 at 11:57 PM Valentin Obst <kernel@xxxxxxxxxxxxxxx> wrote:
>
> > +/// If a struct implements this trait, then it is okay to copy it byte-for-byte
> > +/// to userspace. This means that it should not have any padding, as padding
> > +/// bytes are uninitialized. Reading uninitialized memory is not just undefined
> > +/// behavior, it may even lead to leaking sensitive information on the stack to
> > +/// userspace.
>
> This feels a bit too restrictive to me. Isn't it okay to copy types with
> padding if it is ensured that the padding is always initialized?
>
> I recall that in C one occasionally does a `memset` for structs that are
> copied to user space. I imagine that one could have a Rust
> abstraction/macro that makes it easy to define custom types that can
> always guarantee that all padding bytes are initialized. Such types
> would then qualify for being copied to user space if all field do so as
> well.
>
> This could be a significant quality-of-life improvement for drivers
> as it can be tedious to define struct without padding.

I don't think we should go that route. For example:

let struct_1 = ..;
memset(&mut struct_1, 0);
let struct_2 = struct_1;

Even though struct_1 has its padding zeroed here, that is not the case
for struct_2. When Rust performs a typed copy/move, the padding is not
copied.

Anyway, there is a work-around. Define your struct with MaybeUninit:

// INVARIANT: All bytes always initialized.
struct MyWrapper(MaybeUninit<bindings::c_struct>);

impl Default for MyWrapper {
fn default() -> Self {
MyWrapper(MaybeUninit::zeroed())
}
}

Unlike the bare struct, things wrapped in MaybeUninit always have
their padding preserved. Then, you can implement the trait for this
wrapper, since its padding is always initialized even if that is not
true for the wrapped struct.

Alice