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