Re: [PATCH v8 6/7] mm: rust: add VmAreaNew

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Wed, Nov 20, 2024 at 9:02 PM Lorenzo Stoakes
<lorenzo.stoakes@xxxxxxxxxx> wrote:
>
> On Wed, Nov 20, 2024 at 02:50:00PM +0000, Alice Ryhl wrote:
> > When setting up a new vma in an mmap call, you have a bunch of extra
> > permissions that you normally don't have. This introduces a new
> > VmAreaNew type that is used for this case.
>
> Hm I'm confused by what you mean here. What permissions do you mean?

I just mean that there are additional things you can do, e.g. you can
set VM_MIXEDMAP.

> Is this to abstract a VMA as passed by f_op->mmap()? I think it would be
> better to explicitly say this if so.

Yeah, the VmAreaNew type is specifically for f_op->mmap(). I'll be
more explicit about that.

> > To avoid setting invalid flag values, the methods for clearing
> > VM_MAYWRITE and similar involve a check of VM_WRITE, and return an error
> > if VM_WRITE is set. Trying to use `try_clear_maywrite` without checking
> > the return value results in a compilation error because the `Result`
> > type is marked #[must_use].
>
> This is nice.
>
> Though note that, it is explicitly not permitted to permit writability for
> a VMA that previously had it disallowed, and we explicitly WARN_ON() this
> now. Concretely that means a VMA where !(vma->vm_flags & VM_MAYWRITE), you
> must not vma->vm_flags |= VM_MAYWRITE.

Got it. The API here doesn't allow that, but good to know we can't add
it in the future.

> > For now, there's only a method for VM_MIXEDMAP and not VM_PFNMAP. When
> > we add a VM_PFNMAP method, we will need some way to prevent you from
> > setting both VM_MIXEDMAP and VM_PFNMAP on the same vma.
>
> Yes this would be unwise.
>
> An aside here - really you should _only_ change flags in this hook (perhaps
> of course also initialising vma->vm_private_data state), trying to change
> anything _core_ here would be deeply dangerous.
>
> We are far too permissive with this right now, and it's something we want
> to try ideally to limit in the future.

The previous version just had a function called "set_flags" that could
be used to set any flags you want. Given Jann's feedback, I had
restricted it to only allow certain flag changes.

> > Signed-off-by: Alice Ryhl <aliceryhl@xxxxxxxxxx>
> > ---
> >  rust/kernel/mm/virt.rs | 169 ++++++++++++++++++++++++++++++++++++++++++++++++-
> >  1 file changed, 168 insertions(+), 1 deletion(-)
> >
> > diff --git a/rust/kernel/mm/virt.rs b/rust/kernel/mm/virt.rs
> > index de7f2338810a..22aff8e77854 100644
> > --- a/rust/kernel/mm/virt.rs
> > +++ b/rust/kernel/mm/virt.rs
> > @@ -6,7 +6,7 @@
> >
> >  use crate::{
> >      bindings,
> > -    error::{to_result, Result},
> > +    error::{code::EINVAL, to_result, Result},
> >      page::Page,
> >      types::Opaque,
> >  };
> > @@ -148,6 +148,173 @@ pub fn vm_insert_page(&self, address: usize, page: &Page) -> Result {
> >      }
> >  }
> >
> > +/// A builder for setting up a vma in an `mmap` call.
>
> Would be better to explicitly reference the struct file_operations->mmap()
> hook and to say that we should only be updating flags and vm_private_data
> here (though perhaps not worth mentioning _that_ if not explicitly exposed
> by your interface).

I guess also vm_ops?

> I'm guessing fields are, unless a setter/builder is established, immutable?

If you have a mutable reference, you can always modify fields. When
you don't want that, fields are made private.

> > +    /// Internal method for updating the vma flags.
> > +    ///
> > +    /// # Safety
> > +    ///
> > +    /// This must not be used to set the flags to an invalid value.
> > +    #[inline]
> > +    unsafe fn update_flags(&self, set: vm_flags_t, unset: vm_flags_t) {
> > +        let mut flags = self.flags();
> > +        flags |= set;
> > +        flags &= !unset;
> > +
> > +        // SAFETY: This is not a data race: the vma is undergoing initial setup, so it's not yet
> > +        // shared. Additionally, `VmAreaNew` is `!Sync`, so it cannot be used to write in parallel.
> > +        // The caller promises that this does not set the flags to an invalid value.
> > +        unsafe { (*self.as_ptr()).__bindgen_anon_2.vm_flags = flags };
>
> Hm not sure if this is correct. We explicitly maintain a union in struct vm_area_struct as:
>
>         union {
>                 const vm_flags_t vm_flags;
>                 vm_flags_t __private __vm_flags;
>         };
>
> Where vma->vm_flags is const, and then use helpers like vm_flags_init() to
> set them, which also do things like assert locks (though not in the init
> case, of course).
>
> So erally we should at least be updating __vm_flags here, though I'm not
> sure how bindgen treats it?

I don't think using vm_flags vs __vm_flags changes anything on the
Rust side. The modifications are happening unsafely through a raw
pointer to something inside Opaque, so Rust isn't going to care about
stuff like your const annotation; it's unsafe either way.

I'll update this to use __vm_flags instead.

> > +    /// Try to clear the `VM_MAYREAD` flag, failing if `VM_READ` is set.
> > +    ///
> > +    /// This flag indicates whether userspace is allowed to make this vma readable with
> > +    /// `mprotect()`.
> > +    #[inline]
> > +    pub fn try_clear_mayread(&self) -> Result {
> > +        if self.get_read() {
> > +            return Err(EINVAL);
> > +        }
>
> This is quite nice! Strong(er) typing for the win, again :>)

:)

Alice





[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Bugtraq]     [Linux OMAP]     [Linux MIPS]     [eCos]     [Asterisk Internet PBX]     [Linux API]

  Powered by Linux