Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

mm/vma: do not try to unmap a VMA if mmap_prepare() invoked from mmap()

The mmap_prepare hook functionality includes the ability to invoke
mmap_prepare() from the mmap() hook of existing 'stacked' drivers, that is
ones which are capable of calling the mmap hooks of other drivers/file
systems (e.g. overlayfs, shm).

As part of the mmap_prepare action functionality, we deal with errors by
unmapping the VMA should one arise. This works in the usual mmap_prepare
case, as we invoke this action at the last moment, when the VMA is
established in the maple tree.

However, the mmap() hook passes a not-fully-established VMA pointer to the
caller (which is the motivation behind the mmap_prepare() work), which is
detached.

So attempting to unmap a VMA in this state will be problematic, with the
most obvious symptom being a warning in vma_mark_detached(), because the
VMA is already detached.

It's also unncessary - the mmap() handler will clean up the VMA on error.

So to fix this issue, this patch propagates whether or not an mmap action
is being completed via the compatibility layer or directly.

If the former, then we do not attempt VMA cleanup, if the latter, then we
do.

This patch also updates the userland VMA tests to reflect the change.

Link: https://lore.kernel.org/20260421102150.189982-1-ljs@kernel.org
Fixes: ac0a3fc9c07d ("mm: add ability to take further action in vm_area_desc")
Signed-off-by: Lorenzo Stoakes <ljs@kernel.org>
Reported-by: syzbot+db390288d141a1dccf96@syzkaller.appspotmail.com
Closes: https://lore.kernel.org/all/69e69734.050a0220.24bfd3.0027.GAE@google.com/
Cc: David Hildenbrand <david@kernel.org>
Cc: Jann Horn <jannh@google.com>
Cc: Liam Howlett <liam.howlett@oracle.com>
Cc: Michal Hocko <mhocko@suse.com>
Cc: Mike Rapoport <rppt@kernel.org>
Cc: Pedro Falcato <pfalcato@suse.de>
Cc: Suren Baghdasaryan <surenb@google.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>

authored by

Lorenzo Stoakes and committed by
Andrew Morton
619eab23 04379068

+23 -13
+1 -1
include/linux/mm.h
··· 4391 4391 4392 4392 int mmap_action_prepare(struct vm_area_desc *desc); 4393 4393 int mmap_action_complete(struct vm_area_struct *vma, 4394 - struct mmap_action *action); 4394 + struct mmap_action *action, bool is_compat); 4395 4395 4396 4396 /* Look up the first VMA which exactly match the interval vm_start ... vm_end */ 4397 4397 static inline struct vm_area_struct *find_exact_vma(struct mm_struct *mm,
+17 -9
mm/util.c
··· 1232 1232 /* Update the VMA from the descriptor. */ 1233 1233 compat_set_vma_from_desc(vma, desc); 1234 1234 /* Complete any specified mmap actions. */ 1235 - return mmap_action_complete(vma, &desc->action); 1235 + return mmap_action_complete(vma, &desc->action, /*is_compat=*/true); 1236 1236 } 1237 1237 EXPORT_SYMBOL(__compat_vma_mmap); 1238 1238 ··· 1389 1389 } 1390 1390 1391 1391 static int mmap_action_finish(struct vm_area_struct *vma, 1392 - struct mmap_action *action, int err) 1392 + struct mmap_action *action, int err, 1393 + bool is_compat) 1393 1394 { 1394 1395 size_t len; 1395 1396 ··· 1401 1400 1402 1401 /* do_munmap() might take rmap lock, so release if held. */ 1403 1402 maybe_rmap_unlock_action(vma, action); 1404 - if (!err) 1405 - return 0; 1403 + /* 1404 + * If this is invoked from the compatibility layer, post-mmap() hook 1405 + * logic will handle cleanup for us. 1406 + */ 1407 + if (!err || is_compat) 1408 + return err; 1406 1409 1407 1410 /* 1408 1411 * If an error occurs, unmap the VMA altogether and return an error. We ··· 1456 1451 * mmap_action_complete - Execute VMA descriptor action. 1457 1452 * @vma: The VMA to perform the action upon. 1458 1453 * @action: The action to perform. 1454 + * @is_compat: Is this being invoked from the compatibility layer? 1459 1455 * 1460 1456 * Similar to mmap_action_prepare(). 1461 1457 * 1462 - * Return: 0 on success, or error, at which point the VMA will be unmapped. 1458 + * Return: 0 on success, or error, at which point the VMA will be unmapped if 1459 + * !@is_compat. 1463 1460 */ 1464 1461 int mmap_action_complete(struct vm_area_struct *vma, 1465 - struct mmap_action *action) 1462 + struct mmap_action *action, bool is_compat) 1466 1463 { 1467 1464 int err = 0; 1468 1465 ··· 1485 1478 break; 1486 1479 } 1487 1480 1488 - return mmap_action_finish(vma, action, err); 1481 + return mmap_action_finish(vma, action, err, is_compat); 1489 1482 } 1490 1483 EXPORT_SYMBOL(mmap_action_complete); 1491 1484 #else ··· 1507 1500 EXPORT_SYMBOL(mmap_action_prepare); 1508 1501 1509 1502 int mmap_action_complete(struct vm_area_struct *vma, 1510 - struct mmap_action *action) 1503 + struct mmap_action *action, 1504 + bool is_compat) 1511 1505 { 1512 1506 int err = 0; 1513 1507 ··· 1525 1517 break; 1526 1518 } 1527 1519 1528 - return mmap_action_finish(vma, action, err); 1520 + return mmap_action_finish(vma, action, err, is_compat); 1529 1521 } 1530 1522 EXPORT_SYMBOL(mmap_action_complete); 1531 1523 #endif
+2 -1
mm/vma.c
··· 2780 2780 __mmap_complete(&map, vma); 2781 2781 2782 2782 if (have_mmap_prepare && allocated_new) { 2783 - error = mmap_action_complete(vma, &desc.action); 2783 + error = mmap_action_complete(vma, &desc.action, 2784 + /*is_compat=*/false); 2784 2785 if (error) 2785 2786 return error; 2786 2787 }
+1 -1
tools/testing/vma/include/dup.h
··· 1330 1330 /* Update the VMA from the descriptor. */ 1331 1331 compat_set_vma_from_desc(vma, desc); 1332 1332 /* Complete any specified mmap actions. */ 1333 - return mmap_action_complete(vma, &desc->action); 1333 + return mmap_action_complete(vma, &desc->action, /*is_compat=*/true); 1334 1334 } 1335 1335 1336 1336 static inline int compat_vma_mmap(struct file *file, struct vm_area_struct *vma)
+2 -1
tools/testing/vma/include/stubs.h
··· 87 87 } 88 88 89 89 static inline int mmap_action_complete(struct vm_area_struct *vma, 90 - struct mmap_action *action) 90 + struct mmap_action *action, 91 + bool is_compat) 91 92 { 92 93 return 0; 93 94 }