On Thu, Nov 21, 2024 at 12:47:33PM +0100, Alice Ryhl wrote: > 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. Yeah this is really critical on this one for me. > > > > 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. Yes this is a good idea. > > > > Signed-off-by: Alice Ryhl <aliceryhl@xxxxxxxxxx> This stuff is sensitive (again, we really need to do something about this in the VMA code, TBD) so will want to see the v9 respin before ack. > > > --- > > > 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. Yeah, I mean _in practice_ byte-for-byte it will make no difference but it'd be nice just for the purposes of maintaining a similar abstraction. It might be worth wrapping in something that explicitly references that this is on init only but I guess _the whole type_ does this. > > > > + /// 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