Re: [PATCH v2] mm/vma: keep the unlinked VMA off the file across unmap on mmap hook failure
From: Ihor Solodrai
Date: Thu Oct 08 2026 - 17:49:09 EST
On 10/7/26 12:47 PM, Oleg Keri wrote:
> Since commit 2ceb21171dd9 ("mm: consistently validate VMA state after
> mmap[_prepare] hooks"), the mmap error path clears vma->vm_file only
> after unmap_region(), so free_pgtables() unlinks the never-linked VMA
> and drops i_mmap_writable. Once it goes negative, every later shared
> writable mmap of that file fails with -EPERM until reboot.
>
> Clear vm_file across unmap_region() only, so that free_pgtables() does
> not unlink a VMA that was never linked to the file, and restore it for
> vma_close(): when the hook succeeded and the validation failed, the
> driver's close() still runs and may use vma->vm_file.
>
> Fixes: 2ceb21171dd9 ("mm: consistently validate VMA state after mmap[_prepare] hooks")
> Signed-off-by: Oleg Keri <okerixx@xxxxxxxxx>
Tested-by: Ihor Solodrai <ihor.solodrai@xxxxxxxxx>
BPF CI caught this bug on linux-next as well:
https://github.com/kernel-patches/bpf/actions/runs/37736351874
The patch fixes it:
https://github.com/kernel-patches/bpf/actions/runs/37829434215
> ---
> v2: keep vm_file for vma_close(): with the hook succeeded and the
> validation failed the driver's close() still runs. Reported by Sashiko
> via Andrew.
>
> Seen on next-20261006: one refused PCM mmap probe from alsa-lib left the
> device unmappable, so PipeWire could not play anything.
>
> mm/vma.c | 3 +++
> 1 file changed, 3 insertions(+)
>
> diff --git a/mm/vma.c b/mm/vma.c
> index dfa45c64222c..531e4f53fdd7 100644
> --- a/mm/vma.c
> +++ b/mm/vma.c
> @@ -2616,12 +2616,15 @@ static int __mmap_new_file_vma(struct mmap_state *map,
> map->vm_file = vma->vm_file;
>
> if (error) {
> + struct file *file = vma->vm_file;
> UNMAP_STATE(unmap, vmi, vma, vma->vm_start, vma->vm_end,
> map->prev, map->next);
>
> + vma->vm_file = NULL;
> vma_iter_set(vmi, vma->vm_end);
> /* Undo any partial mapping done by a device driver. */
> unmap_region(&unmap);
> + vma->vm_file = file;
> /* Only safe once unmapped. */
> vma_close(vma);
>