On Thu Jun 19, 2025 at 2:20 PM CEST, Andreas Hindborg wrote:
> "Benno Lossin" <los...@kernel.org> writes:
>> On Thu Jun 12, 2025 at 3:40 PM CEST, Andreas Hindborg wrote:
>>> +
>>> +// SAFETY: C kernel handles serializing access to this type. We never 
>>> access it
>>> +// from Rust module.
>>> +unsafe impl Sync for RacyKernelParam {}
>>> +
>>> +/// Types that can be used for module parameters.
>>> +pub trait ModuleParam: Sized + Copy {
>>
>> Why the `Copy` bound?
>
> Because of potential unsoundness due to drop [1]. I should document
> this. It is noted in the change log for the series under the obscure
> entry "Assign through pointer rather than using `core::ptr::replace`."
>
> [1] https://lore.kernel.org/all/878qnbxtyi....@kernel.org

Ah thanks for the pointer, yeah please mention this in a comment
somewhere.

>>> +    ///
>>> +    /// Parameters passed at boot time will be set before [`kmalloc`] is
>>> +    /// available (even if the module is loaded at a later time). However, 
>>> in
>>
>> I think we should make a section out of this like `# No allocations` (or
>> something better). Let's also mention it on the trait itself, since
>> that's where implementers will most likely look.
>
> Since this series only support `Copy` types that are passed by value, I
> think we can remove this comment for now. I will also restrict the
> lifetime of the string to he duration of the call. Putting static here
> would be lying.
>
>>
>>> +    /// this case, the argument buffer will be valid for the entire 
>>> lifetime of
>>> +    /// the kernel. So implementations of this method which need to 
>>> allocate
>>> +    /// should first check that the allocator is available (with
>>> +    /// [`crate::bindings::slab_is_available`]) and when it is not 
>>> available
>>
>> We probably shouldn't recommend directly using `bindings`.
>>
>>> +    /// provide an alternative implementation which doesn't allocate. In 
>>> cases
>>> +    /// where the allocator is not available it is safe to save references 
>>> to
>>> +    /// `arg` in `Self`, but in other cases a copy should be made.
>>
>> I don't understand this convention, but it also doesn't seem to
>> relevant (so feel free to leave it as is, but it would be nice if you
>> could explain it).
>
> It has become irrelevant as the series evolved. When we supported
> `!Copy` types we would use the reference if we knew it would be valid
> for the lifetime of the kernel, otherwise we would allocate [1].
>
> However, when the reference is passed at module load time, it is still
> guaranteed to be live for the lifetime of the module, and hence it can
> still be considered `'static`. But, if the reference were to find it's
> way across the module boundary, it can cause UAF issues as the reference
> is not truely `'static`, it is actually `'module`. This ties into the
> difficulty we have around safety of unloading modules. Module unload
> should be marked unsafe.

Ah so the argument should rather be an enum that is either
`Static(&'static str)` or `WithAlloc(&'short str)` with the (non-safety)
guarantee that `WithAlloc` is only passed when the allocator is
available.

> At any rate, I will remove the `'static` lifetime from the reference and
> we are all good for now.

Sounds simplest for now.

>>> +    crate::error::from_result(|| {
>>> +        let new_value = T::try_from_param_arg(arg)?;
>>> +
>>> +        // SAFETY: By function safety requirements `param` is be valid for 
>>> reads.
>>> +        let old_value = unsafe { (*param).__bindgen_anon_1.arg as *mut T };
>>> +
>>> +        // SAFETY: By function safety requirements, the target of 
>>> `old_value` is valid for writes
>>> +        // and is initialized.
>>> +        unsafe { *old_value = new_value };
>>
>> So if we keep the `ModuleParam: Copy` bound from above, then we don't
>> need to drop the type here (as `Copy` implies `!Drop`). So we could also
>> remove the requirement for initialized memory and use `ptr::write` here
>> instead. Thoughts?
>
> Yes, that is the rationale for the `Copy` bound. What would be the
> benefit of using `ptr::write`? They should be equivalent for `Copy`
> types, right.

They should be equivalent, but if we drop the requirement that the value
is initialized to begin with, then removing `Copy` will result in UB
here.

> I was using `ptr::replace`, but Alice suggested the pace expression
> assignment instead, since I was not using the old value.

That makes sense, but if we also remove the initialized requirement,
then I would prefer `ptr::write`.

---
Cheers,
Benno

Reply via email to