Re: [PATCH v8 14/21] mm/mmap: Avoid zeroing vma tree in mmap_region()

Lorenzo Stoakes
2024-10-01T11:56:22Z

On Tue, Oct 01, 2024 at 04:34:00AM GMT, Bert Karwatzki wrote:
> I just noticed (via a bisect between v6.11 and v6.12-rc1) that this patch
> (commit f8d112a4e657 in linux-next tree) leads to a severe memory corruption
> error under these (rather rare) circumstances:
> 1. Start a 32bit windows game via steam (which uses proton, steam's version of wine)
> 2. When starting the game you the proton version used has to be updated
>
> The effect is the following: The updating process of proton hangs and the game does
> not start and even after an exit from steam two processes remain, one of them at
> 100% CPU:
> $ ps aux | grep rundll
> bert 222638 1.7 0.1 2054868 87492 ? Ss 23:14 0:01 C:\windows\syswow64\rundll32.exe setupapi,InstallHinfSection Wow64Install 128 \\?\Z:\mnt\data\.steam\debian-installation\steamapps\common\Proton - Experimental\files\share\wine\wine.inf
> bert 222639 99.8 0.0 2054868 2380 ? R 23:14 1:01 C:\windows\syswow64\rundll32.exe setupapi,InstallHinfSection Wow64Install 128 \\?\Z:\mnt\data\.steam\debian-installation\steamapps\common\Proton - Experimental\files\share\wine\wine.inf

[snip]

Replying to head of thread so we don't get too lost in the mail thread.

Could you try this patch on latest next or mm-unstable and see if it fixes
the issue?

The theory is that perhaps an error is arising and being masked by this
incorrect error handling.

Regardless I'll upstream this fix, but be good if it also resolved the bug
you've found!

Thanks, Lorenzo

----8<----
From 39e7dd7d6d548adb36c928e1bf6e367fb38beab1 Mon Sep 17 00:00:00 2001
From: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
Date: Tue, 1 Oct 2024 12:44:50 +0100
Subject: [PATCH] mm: correct error handling in mmap_region()

Commit f8d112a4e657 ("mm/mmap: avoid zeroing vma tree in mmap_region()")
changed how error handling is performed in mmap_region(), defaulting to
-ENOMEM but then potentially clearing this state when invoking
vms_gather_munmap_vmas().

Later calls which have abort cases, such as the check against
may_expand_vm() and vm_area_alloc() do not update error, meaning that we
may report the operation is successful when in fact it failed.

Correct this and avoid future confusion by strictly setting error on each
and every occasion we jump to the error handling logic, and set the error
code immediately prior to doing so.

This way we can see at a glance that the error code is always correct

Thanks to Vegard Nossum who spotted this issue in discussion around this
problem.

Reported-by: Bert Karwatzki <spasswolf@web.de>
Suggested-by: Vegard Nossum <vegard.nossum@oracle.com>
Fixes: f8d112a4e657 ("mm/mmap: avoid zeroing vma tree in mmap_region()")
Signed-off-by: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
---
mm/mmap.c | 32 +++++++++++++++++++++-----------
1 file changed, 21 insertions(+), 11 deletions(-)

diff --git a/mm/mmap.c b/mm/mmap.c
index dd4b35a25aeb..9c0fb43064b5 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -1371,7 +1371,7 @@ unsigned long mmap_region(struct file *file, unsigned long addr,
struct maple_tree mt_detach;
unsigned long end = addr + len;
bool writable_file_mapping = false;
- int error = -ENOMEM;
+ int error;
VMA_ITERATOR(vmi, mm, addr);
VMG_STATE(vmg, mm, &vmi, addr, end, vm_flags, pgoff);

@@ -1396,8 +1396,10 @@ unsigned long mmap_region(struct file *file, unsigned long addr,
}

/* Check against address space limit. */
- if (!may_expand_vm(mm, vm_flags, pglen - vms.nr_pages))
+ if (!may_expand_vm(mm, vm_flags, pglen - vms.nr_pages)) {
+ error = -ENOMEM;
goto abort_munmap;
+ }

/*
* Private writable mapping: check memory availability
@@ -1405,8 +1407,11 @@ unsigned long mmap_region(struct file *file, unsigned long addr,
if (accountable_mapping(file, vm_flags)) {
charged = pglen;
charged -= vms.nr_accounted;
- if (charged && security_vm_enough_memory_mm(mm, charged))
- goto abort_munmap;
+ if (charged) {
+ error = security_vm_enough_memory_mm(mm, charged);
+ if (error)
+ goto abort_munmap;
+ }

vms.nr_accounted = 0;
vm_flags |= VM_ACCOUNT;
@@ -1422,8 +1427,10 @@ unsigned long mmap_region(struct file *file, unsigned long addr,
* not unmapped, but the maps are removed from the list.
*/
vma = vm_area_alloc(mm);
- if (!vma)
+ if (!vma) {
+ error = -ENOMEM;
goto unacct_error;
+ }

vma_iter_config(&vmi, addr, end);
vma_set_range(vma, addr, end, pgoff);
@@ -1453,9 +1460,10 @@ unsigned long mmap_region(struct file *file, unsigned long addr,
* Expansion is handled above, merging is handled below.
* Drivers should not alter the address of the VMA.
*/
- error = -EINVAL;
- if (WARN_ON((addr != vma->vm_start)))
+ if (WARN_ON((addr != vma->vm_start))) {
+ error = -EINVAL;
goto close_and_free_vma;
+ }

vma_iter_config(&vmi, addr, end);
/*
@@ -1500,13 +1508,15 @@ unsigned long mmap_region(struct file *file, unsigned long addr,
}

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

- error = -ENOMEM;
- if (vma_iter_prealloc(&vmi, vma))
+ if (vma_iter_prealloc(&vmi, vma)) {
+ error = -ENOMEM;
goto close_and_free_vma;
+ }

/* Lock the VMA since it is modified after insertion into VMA tree */
vma_start_write(vma);
--
2.46.2