Unverified Commit 3800e9b6 authored by openeuler-ci-bot's avatar openeuler-ci-bot Committed by Gitee
Browse files

!15119 mm: resolve faulty mmap_region() error path behaviour

parents 6af1e57f 4b5e7082
Loading
Loading
Loading
Loading
+73 −55
Original line number Diff line number Diff line
@@ -1254,7 +1254,7 @@ static inline bool file_mmap_ok(struct file *file, struct inode *inode,
	return true;
}

static unsigned long __mmap_region(struct mm_struct *mm,
static unsigned long __mmap_region_ext(struct mm_struct *mm,
				   struct file *file, unsigned long addr,
				   unsigned long len, vm_flags_t vm_flags,
				   unsigned long pgoff, struct list_head *uf);
@@ -1431,7 +1431,7 @@ unsigned long __do_mmap_mm(struct mm_struct *mm, struct file *file, unsigned lon
			vm_flags |= VM_NORESERVE;
	}

	addr = __mmap_region(mm, file, addr, len, vm_flags, pgoff, uf);
	addr = __mmap_region_ext(mm, file, addr, len, vm_flags, pgoff, uf);
	if (!IS_ERR_VALUE(addr) &&
	    ((vm_flags & VM_LOCKED) ||
	     (flags & (MAP_POPULATE | MAP_NONBLOCK)) == MAP_POPULATE))
@@ -2739,14 +2739,14 @@ int do_munmap(struct mm_struct *mm, unsigned long start, size_t len,
	return do_vmi_munmap(&vmi, mm, start, len, uf, false);
}

static unsigned long __mmap_region(struct mm_struct *mm,
				   struct file *file, unsigned long addr,
				   unsigned long len, vm_flags_t vm_flags,
				   unsigned long pgoff, struct list_head *uf)
static unsigned long __mmap_region(struct mm_struct *mm, struct file *file,
				   unsigned long addr, unsigned long len,
				   vm_flags_t vm_flags, unsigned long pgoff,
				   struct list_head *uf)
{
	struct vm_area_struct *vma = NULL;
	struct vm_area_struct *next, *prev, *merge;
	pgoff_t pglen = len >> PAGE_SHIFT;
	pgoff_t pglen = PHYS_PFN(len);
	unsigned long charged = 0;
	unsigned long end = addr + len;
	unsigned long merge_start = addr, merge_end = end;
@@ -2843,25 +2843,26 @@ static unsigned long __mmap_region(struct mm_struct *mm,
	vma->vm_page_prot = vm_get_page_prot(vm_flags);
	vma->vm_pgoff = pgoff;

	if (file) {
		if (vm_flags & VM_SHARED) {
			error = mapping_map_writable(file->f_mapping);
			if (error)
	if (vma_iter_prealloc(&vmi, vma)) {
		error = -ENOMEM;
		goto free_vma;
	}

	if (file) {
		vma->vm_file = get_file(file);
		error = call_mmap(file, vma);
		if (error)
			goto unmap_and_free_vma;
			goto unmap_and_free_file_vma;

		/* Drivers cannot alter the address of the VMA. */
		WARN_ON_ONCE(addr != vma->vm_start);
		/*
		 * Expansion is handled above, merging is handled below.
		 * Drivers should not alter the address of the VMA.
		 * Drivers should not permit writability when previously it was
		 * disallowed.
		 */
		error = -EINVAL;
		if (WARN_ON((addr != vma->vm_start)))
			goto close_and_free_vma;
		VM_WARN_ON_ONCE(vm_flags != vma->vm_flags &&
				!(vm_flags & VM_MAYWRITE) &&
				(vma->vm_flags & VM_MAYWRITE));

		vma_iter_config(&vmi, addr, end);
		/*
@@ -2873,6 +2874,7 @@ static unsigned long __mmap_region(struct mm_struct *mm,
				    vma->vm_end, vma->vm_flags, NULL,
				    vma->vm_file, vma->vm_pgoff, NULL,
				    NULL_VM_UFFD_CTX, NULL);

			if (merge) {
				/*
				 * ->mmap() can change vma->vm_file and fput
@@ -2886,7 +2888,7 @@ static unsigned long __mmap_region(struct mm_struct *mm,
				vma = merge;
				/* Update vm_flags to pick up the change. */
				vm_flags = vma->vm_flags;
				goto unmap_writable;
				goto file_expanded;
			}
		}

@@ -2894,24 +2896,15 @@ static unsigned long __mmap_region(struct mm_struct *mm,
	} else if (vm_flags & VM_SHARED) {
		error = shmem_zero_setup(vma);
		if (error)
			goto free_vma;
			goto free_iter_vma;
	} else {
		vma_set_anonymous(vma);
	}

	if (map_deny_write_exec(vma->vm_flags, vma->vm_flags)) {
		error = -EACCES;
		goto close_and_free_vma;
	}

	/* Allow architectures to sanity-check the vm_flags */
	error = -EINVAL;
	if (!arch_validate_flags(vma->vm_flags))
		goto close_and_free_vma;

	error = -ENOMEM;
	if (vma_iter_prealloc(&vmi, vma))
		goto close_and_free_vma;
#ifdef CONFIG_SPARC64
	/* TODO: Fix SPARC ADI! */
	WARN_ON_ONCE(!arch_validate_flags(vm_flags));
#endif

	/* Lock the VMA since it is modified after insertion into VMA tree */
	vma_start_write(vma);
@@ -2925,10 +2918,7 @@ static unsigned long __mmap_region(struct mm_struct *mm,
	 */
	khugepaged_enter_vma(vma, vma->vm_flags);

	/* Once vma denies write, undo our temporary denial count */
unmap_writable:
	if (file && vm_flags & VM_SHARED)
		mapping_unmap_writable(file->f_mapping);
file_expanded:
	file = vma->vm_file;
	ksm_add_vma(vma);
expanded:
@@ -2958,14 +2948,9 @@ static unsigned long __mmap_region(struct mm_struct *mm,

	vma_set_page_prot(vma);

	validate_mm(mm);
	return addr;

close_and_free_vma:
	vma_close(vma);

	if (file || vma->vm_file) {
unmap_and_free_vma:
unmap_and_free_file_vma:
	fput(vma->vm_file);
	vma->vm_file = NULL;

@@ -2973,23 +2958,56 @@ static unsigned long __mmap_region(struct mm_struct *mm,
	/* Undo any partial mapping done by a device driver. */
	unmap_region(mm, &vmi.mas, vma, prev, next, vma->vm_start,
		     vma->vm_end, vma->vm_end, true);
	}
	if (file && (vm_flags & VM_SHARED))
		mapping_unmap_writable(file->f_mapping);
free_iter_vma:
	vma_iter_free(&vmi);
free_vma:
	vm_area_free(vma);
unacct_error:
	if (charged)
		vm_unacct_memory(charged);
	validate_mm(mm);
	return error;
}

static unsigned long __mmap_region_ext(struct mm_struct *mm, struct file *file,
				       unsigned long addr, unsigned long len,
				       vm_flags_t vm_flags, unsigned long pgoff,
				       struct list_head *uf)
{
	unsigned long ret;
	bool writable_file_mapping = false;

	/* Check to see if MDWE is applicable. */
	if (map_deny_write_exec(vm_flags, vm_flags))
		return -EACCES;

	/* Allow architectures to sanity-check the vm_flags. */
	if (!arch_validate_flags(vm_flags))
		return -EINVAL;

	/* Map writable and ensure this isn't a sealed memfd. */
	if (file && (vm_flags & VM_SHARED)) {
		int error = mapping_map_writable(file->f_mapping);

		if (error)
			return error;
		writable_file_mapping = true;
	}

	ret = __mmap_region(mm, file, addr, len, vm_flags, pgoff, uf);

	/* Clear our write mapping regardless of error. */
	if (writable_file_mapping)
		mapping_unmap_writable(file->f_mapping);

	validate_mm(current->mm);
	return ret;
}

unsigned long mmap_region(struct file *file, unsigned long addr,
		unsigned long len, vm_flags_t vm_flags, unsigned long pgoff,
		struct list_head *uf)
{
	return __mmap_region(current->mm, file, addr, len, vm_flags, pgoff, uf);
	return __mmap_region_ext(current->mm, file, addr, len, vm_flags, pgoff, uf);
}

static int __vm_munmap(unsigned long start, size_t len, bool unlock)