Skip to content

Commit d70cec8

Browse files
MiaoheLintorvalds
authored andcommitted
mm: mmap: merge vma after call_mmap() if possible
The vm_flags may be changed after call_mmap() because drivers may set some flags for their own purpose. As a result, we failed to merge the adjacent vma due to the different vm_flags as userspace can't pass in the same one. Try to merge vma after call_mmap() to fix this issue. Signed-off-by: Hongxiang Lou <[email protected]> Signed-off-by: Miaohe Lin <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Reviewed-by: Andrew Morton <[email protected]> Link: http://lkml.kernel.org/r/[email protected] Signed-off-by: Linus Torvalds <[email protected]>
1 parent eee0793 commit d70cec8

File tree

1 file changed

+21
-1
lines changed

1 file changed

+21
-1
lines changed

mm/mmap.c

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1690,7 +1690,7 @@ unsigned long mmap_region(struct file *file, unsigned long addr,
16901690
struct list_head *uf)
16911691
{
16921692
struct mm_struct *mm = current->mm;
1693-
struct vm_area_struct *vma, *prev;
1693+
struct vm_area_struct *vma, *prev, *merge;
16941694
int error;
16951695
struct rb_node **rb_link, *rb_parent;
16961696
unsigned long charged = 0;
@@ -1774,6 +1774,25 @@ unsigned long mmap_region(struct file *file, unsigned long addr,
17741774
if (error)
17751775
goto unmap_and_free_vma;
17761776

1777+
/* If vm_flags changed after call_mmap(), we should try merge vma again
1778+
* as we may succeed this time.
1779+
*/
1780+
if (unlikely(vm_flags != vma->vm_flags && prev)) {
1781+
merge = vma_merge(mm, prev, vma->vm_start, vma->vm_end, vma->vm_flags,
1782+
NULL, vma->vm_file, vma->vm_pgoff, NULL, NULL_VM_UFFD_CTX);
1783+
if (merge) {
1784+
fput(file);
1785+
vm_area_free(vma);
1786+
vma = merge;
1787+
/* Update vm_flags and possible addr to pick up the change. We don't
1788+
* warn here if addr changed as the vma is not linked by vma_link().
1789+
*/
1790+
addr = vma->vm_start;
1791+
vm_flags = vma->vm_flags;
1792+
goto unmap_writable;
1793+
}
1794+
}
1795+
17771796
/* Can addr have changed??
17781797
*
17791798
* Answer: Yes, several device drivers can do it in their
@@ -1796,6 +1815,7 @@ unsigned long mmap_region(struct file *file, unsigned long addr,
17961815
vma_link(mm, vma, prev, rb_link, rb_parent);
17971816
/* Once vma denies write, undo our temporary denial count */
17981817
if (file) {
1818+
unmap_writable:
17991819
if (vm_flags & VM_SHARED)
18001820
mapping_unmap_writable(file->f_mapping);
18011821
if (vm_flags & VM_DENYWRITE)

0 commit comments

Comments
 (0)