On Tue, 10 Jul 2018 16:48:20 +0300 "Kirill A. Shutemov" <kirill.shutemov@xxxxxxxxxxxxxxx> wrote: > vma_is_anonymous() relies on ->vm_ops being NULL to detect anonymous > VMA. This is unreliable as ->mmap may not set ->vm_ops. > > False-positive vma_is_anonymous() may lead to crashes: > > ... > > This can be fixed by assigning anonymous VMAs own vm_ops and not relying > on it being NULL. > > If ->mmap() failed to set ->vm_ops, mmap_region() will set it to > dummy_vm_ops. This way we will have non-NULL ->vm_ops for all VMAs. Is there a smaller, simpler fix which we can use for backporting purposes and save the larger rework for development kernels? > > ... > > --- a/mm/mmap.c > +++ b/mm/mmap.c > @@ -71,6 +71,9 @@ int mmap_rnd_compat_bits __read_mostly = CONFIG_ARCH_MMAP_RND_COMPAT_BITS; > static bool ignore_rlimit_data; > core_param(ignore_rlimit_data, ignore_rlimit_data, bool, 0644); > > +const struct vm_operations_struct anon_vm_ops = {}; > +const struct vm_operations_struct dummy_vm_ops = {}; Some nice comments here would be useful. Especially for dummy_vm_ops. Why does it exist, what is its role, etc. > static void unmap_region(struct mm_struct *mm, > struct vm_area_struct *vma, struct vm_area_struct *prev, > unsigned long start, unsigned long end); > @@ -561,6 +564,8 @@ static unsigned long count_vma_pages_range(struct mm_struct *mm, > void __vma_link_rb(struct mm_struct *mm, struct vm_area_struct *vma, > struct rb_node **rb_link, struct rb_node *rb_parent) > { > + WARN_ONCE(!vma->vm_ops, "missing vma->vm_ops"); > + > /* Update tracking information for the gap following the new vma. */ > if (vma->vm_next) > vma_gap_update(vma->vm_next); > @@ -1774,12 +1779,19 @@ unsigned long mmap_region(struct file *file, unsigned long addr, > */ > WARN_ON_ONCE(addr != vma->vm_start); > > + /* All mappings must have ->vm_ops set */ > + if (!vma->vm_ops) > + vma->vm_ops = &dummy_vm_ops; Can this happen? Can we make it a rule that file_operations.mmap(vma) must initialize vma->vm_ops? Should we have a WARN here to detect when the fs implementation failed to do that? > addr = vma->vm_start; > vm_flags = vma->vm_flags; > } else if (vm_flags & VM_SHARED) { > error = shmem_zero_setup(vma); > if (error) > goto free_vma; > + } else { > + /* vma_is_anonymous() relies on this. */ > + vma->vm_ops = &anon_vm_ops; > } > > vma_link(mm, vma, prev, rb_link, rb_parent); > ... >