[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20250527132351.2050820-2-pulehui@huaweicloud.com>
Date: Tue, 27 May 2025 13:23:50 +0000
From: Pu Lehui <pulehui@...weicloud.com>
To: mhiramat@...nel.org,
oleg@...hat.com,
peterz@...radead.org,
akpm@...ux-foundation.org,
Liam.Howlett@...cle.com,
lorenzo.stoakes@...cle.com,
vbabka@...e.cz,
jannh@...gle.com,
pfalcato@...e.de
Cc: linux-mm@...ck.org,
linux-kernel@...r.kernel.org,
pulehui@...wei.com
Subject: [RFC PATCH v2 1/2] mm/mremap: Fix uprobe anon page be overwritten when expanding vma during mremap
From: Pu Lehui <pulehui@...wei.com>
We encountered a BUG alert triggered by Syzkaller as follows:
BUG: Bad rss-counter state mm:00000000b4a60fca type:MM_ANONPAGES val:1
And we can reproduce it with the following steps:
1. register uprobe on file at zero offset
2. mmap the file at zero offset:
addr1 = mmap(NULL, 2 * 4096, PROT_NONE, MAP_PRIVATE, fd, 0);
3. mremap part of vma1 to new vma2:
addr2 = mremap(addr1, 4096, 2 * 4096, MREMAP_MAYMOVE);
4. mremap back to orig addr1:
mremap(addr2, 4096, 4096, MREMAP_MAYMOVE | MREMAP_FIXED, addr1);
In the step 3, the vma1 range [addr1, addr1 + 4096] will be remap to new
vma2 with range [addr2, addr2 + 8192], and remap uprobe anon page from
the vma1 to vma2, then unmap the vma1 range [addr1, addr1 + 4096].
In tht step 4, the vma2 range [addr2, addr2 + 4096] will be remap back
to the addr range [addr1, addr1 + 4096]. Since the addr range [addr1 +
4096, addr1 + 8192] still maps the file, it will take
vma_merge_new_range to merge these two addr ranges, and then do
uprobe_mmap in vma_complete. Since the merged vma pgoff is also zero
offset, it will install uprobe anon page to the merged vma. However, the
upcomming move_page_tables step, which use set_pte_at to remap the vma2
uprobe anon page to the merged vma, will over map the old uprobe anon
page in the merged vma, and lead the old uprobe anon page to be orphan.
Since the uprobe anon page will be remapped to the merged vma, we can
remove the unnecessary uprobe_mmap on merged vma, that is, do not
perform uprobe_mmap on expanded vma.
This problem was first find in linux-6.6.y and also exists in the
community syzkaller:
https://lore.kernel.org/all/000000000000ada39605a5e71711@google.com/T/
The complete Syzkaller C reproduction program is as follows:
#define _GNU_SOURCE
#include <fcntl.h>
#include <unistd.h>
#include <syscall.h>
#include <sys/mman.h>
#include <linux/perf_event.h>
int main(int argc, char *argv[])
{
int fd = open(FNAME, O_RDWR|O_CREAT, 0600);
struct perf_event_attr attr = {
.type = 9,
.uprobe_path = (long) FNAME,
.probe_offset = 0x0,
};
void *addr1, *addr2;
write(fd, "x", 1);
mmap(NULL, 4096, PROT_EXEC, MAP_PRIVATE, fd, 0);
syscall(__NR_perf_event_open, &attr, 0, 0, -1, 0);
addr1 = mmap(NULL, 2 * 4096, PROT_NONE, MAP_PRIVATE, fd, 0);
addr2 = mremap(addr1, 4096, 2 * 4096, MREMAP_MAYMOVE);
mremap(addr2, 4096, 4096, MREMAP_MAYMOVE | MREMAP_FIXED, addr1);
return 0;
}
Fixes: 78a320542e6c ("uprobes: Change valid_vma() to demand VM_MAYEXEC rather than VM_EXEC")
Signed-off-by: Pu Lehui <pulehui@...wei.com>
---
mm/vma.c | 7 ++++++-
mm/vma.h | 7 +++++++
2 files changed, 13 insertions(+), 1 deletion(-)
diff --git a/mm/vma.c b/mm/vma.c
index 1c6595f282e5..6445f515c7f2 100644
--- a/mm/vma.c
+++ b/mm/vma.c
@@ -358,7 +358,8 @@ static void vma_complete(struct vma_prepare *vp, struct vma_iterator *vmi,
if (vp->file) {
i_mmap_unlock_write(vp->mapping);
- uprobe_mmap(vp->vma);
+ if (!vp->skip_vma_uprobe)
+ uprobe_mmap(vp->vma);
if (vp->adj_next)
uprobe_mmap(vp->adj_next);
@@ -737,6 +738,7 @@ static int commit_merge(struct vma_merge_struct *vmg)
if (vma_iter_prealloc(vmg->vmi, vma))
return -ENOMEM;
+ vp.skip_vma_uprobe = vmg->skip_vma_uprobe;
vma_prepare(&vp);
/*
* THP pages may need to do additional splits if we increase
@@ -1151,6 +1153,9 @@ int vma_expand(struct vma_merge_struct *vmg)
if (remove_next)
vmg->__remove_next = true;
+ /* skip uprobe_mmap on expanded vma */
+ vmg->skip_vma_uprobe = true;
+
if (commit_merge(vmg))
goto nomem;
diff --git a/mm/vma.h b/mm/vma.h
index 9a8af9be29a8..56cc0364d239 100644
--- a/mm/vma.h
+++ b/mm/vma.h
@@ -19,6 +19,8 @@ struct vma_prepare {
struct vm_area_struct *insert;
struct vm_area_struct *remove;
struct vm_area_struct *remove2;
+ /* Whether to skip uprobe_mmap on vma */
+ bool skip_vma_uprobe;
};
struct unlink_vma_file_batch {
@@ -120,6 +122,11 @@ struct vma_merge_struct {
*/
bool give_up_on_oom :1;
+ /*
+ * Whether to skip uprobe_mmap on merged vma.
+ */
+ bool skip_vma_uprobe :1;
+
/* Internal flags set during merge process: */
/*
--
2.34.1
Powered by blists - more mailing lists