Skip to content

Commit deb0f65

Browse files
Carlos Llamasakpm00
authored andcommitted
mm/mmap: undo ->mmap() when arch_validate_flags() fails
Commit c462ac2 ("mm: Introduce arch_validate_flags()") added a late check in mmap_region() to let architectures validate vm_flags. The check needs to happen after calling ->mmap() as the flags can potentially be modified during this callback. If arch_validate_flags() check fails we unmap and free the vma. However, the error path fails to undo the ->mmap() call that previously succeeded and depending on the specific ->mmap() implementation this translates to reference increments, memory allocations and other operations what will not be cleaned up. There are several places (mainly device drivers) where this is an issue. However, one specific example is bpf_map_mmap() which keeps count of the mappings in map->writecnt. The count is incremented on ->mmap() and then decremented on vm_ops->close(). When arch_validate_flags() fails this count is off since bpf_map_mmap_close() is never called. One can reproduce this issue in arm64 devices with MTE support. Here the vm_flags are checked to only allow VM_MTE if VM_MTE_ALLOWED has been set previously. From userspace then is enough to pass the PROT_MTE flag to mmap() syscall to trigger the arch_validate_flags() failure. The following program reproduces this issue: #include <stdio.h> #include <unistd.h> #include <linux/unistd.h> #include <linux/bpf.h> #include <sys/mman.h> int main(void) { union bpf_attr attr = { .map_type = BPF_MAP_TYPE_ARRAY, .key_size = sizeof(int), .value_size = sizeof(long long), .max_entries = 256, .map_flags = BPF_F_MMAPABLE, }; int fd; fd = syscall(__NR_bpf, BPF_MAP_CREATE, &attr, sizeof(attr)); mmap(NULL, 4096, PROT_WRITE | PROT_MTE, MAP_SHARED, fd, 0); return 0; } By manually adding some log statements to the vm_ops callbacks we can confirm that when passing PROT_MTE to mmap() the map->writecnt is off upon ->release(): With PROT_MTE flag: root@debian:~# ./bpf-test [ 111.263874] bpf_map_write_active_inc: map=9 writecnt=1 [ 111.288763] bpf_map_release: map=9 writecnt=1 Without PROT_MTE flag: root@debian:~# ./bpf-test [ 157.816912] bpf_map_write_active_inc: map=10 writecnt=1 [ 157.830442] bpf_map_write_active_dec: map=10 writecnt=0 [ 157.832396] bpf_map_release: map=10 writecnt=0 This patch fixes the above issue by calling vm_ops->close() when the arch_validate_flags() check fails, after this we can proceed to unmap and free the vma on the error path. Link: https://lkml.kernel.org/r/[email protected] Fixes: c462ac2 ("mm: Introduce arch_validate_flags()") Signed-off-by: Carlos Llamas <[email protected]> Reviewed-by: Catalin Marinas <[email protected]> Acked-by: Andrii Nakryiko <[email protected]> Reviewed-by: Liam Howlett <[email protected]> Cc: Christian Brauner (Microsoft) <[email protected]> Cc: Michal Hocko <[email protected]> Cc: Suren Baghdasaryan <[email protected]> Cc: <[email protected]> [5.10+] Signed-off-by: Andrew Morton <[email protected]>
1 parent 515778e commit deb0f65

File tree

1 file changed

+4
-1
lines changed

1 file changed

+4
-1
lines changed

mm/mmap.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2673,7 +2673,7 @@ unsigned long mmap_region(struct file *file, unsigned long addr,
26732673
if (!arch_validate_flags(vma->vm_flags)) {
26742674
error = -EINVAL;
26752675
if (file)
2676-
goto unmap_and_free_vma;
2676+
goto close_and_free_vma;
26772677
else
26782678
goto free_vma;
26792679
}
@@ -2742,6 +2742,9 @@ unsigned long mmap_region(struct file *file, unsigned long addr,
27422742
validate_mm(mm);
27432743
return addr;
27442744

2745+
close_and_free_vma:
2746+
if (vma->vm_ops && vma->vm_ops->close)
2747+
vma->vm_ops->close(vma);
27452748
unmap_and_free_vma:
27462749
fput(vma->vm_file);
27472750
vma->vm_file = NULL;

0 commit comments

Comments
 (0)