3
0
Fork 0
forked from mirrors/linux

- The 4 patch series "Fix uprobe pte be overwritten when expanding vma"

fixes a longstanding and quite obscure bug related to the vma merging of
   the uprobe mmap page.
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYKAB0WIQTTMBEPP41GrTpTJgfdBJ7gKXxAjgUCaEN1LAAKCRDdBJ7gKXxA
 jsfLAQCC+C8397X6lNKPI3bHGLGEHubP2uzb6bOFMAU6fIRobQEAqUnoUhfP+xsu
 tDbcQEBZ+vfyeT9Zr9vA+uBDcA3OGw0=
 =9oaG
 -----END PGP SIGNATURE-----

Merge tag 'mm-stable-2025-06-06-16-09' of git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm

Pull more MM updates from Andrew Morton:
 "The series 'Fix uprobe pte be overwritten when expanding vma' fixes a
  longstanding and quite obscure bug related to the vma merging of the
  uprobe mmap page"

* tag 'mm-stable-2025-06-06-16-09' of git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm:
  selftests/mm: add test about uprobe pte be orphan during vma merge
  selftests/mm: extract read_sysfs and write_sysfs into vm_util
  mm: expose abnormal new_pte during move_ptes
  mm: fix uprobe pte be overwritten when expanding vma
  mm/damon: s/primitives/code/ on comments
This commit is contained in:
Linus Torvalds 2025-06-06 22:06:57 -07:00
commit bdc7f8c5ad
16 changed files with 122 additions and 44 deletions

View file

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Common Primitives for DAMON Modules
* Common Code for DAMON Modules
*
* Author: SeongJae Park <sj@kernel.org>
*/

View file

@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Common Primitives for DAMON Modules
* Common Code for DAMON Modules
*
* Author: SeongJae Park <sj@kernel.org>
*/

View file

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Common Primitives for Data Access Monitoring
* Common Code for Data Access Monitoring
*
* Author: SeongJae Park <sj@kernel.org>
*/

View file

@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Common Primitives for Data Access Monitoring
* Common Code for Data Access Monitoring
*
* Author: SeongJae Park <sj@kernel.org>
*/

View file

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
* DAMON Primitives for The Physical Address Space
* DAMON Code for The Physical Address Space
*
* Author: SeongJae Park <sj@kernel.org>
*/

View file

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Common Primitives for DAMON Sysfs Interface
* Common Code for DAMON Sysfs Interface
*
* Author: SeongJae Park <sj@kernel.org>
*/

View file

@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Common Primitives for DAMON Sysfs Interface
* Common Code for DAMON Sysfs Interface
*
* Author: SeongJae Park <sj@kernel.org>
*/

View file

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
* DAMON Primitives for Virtual Address Spaces
* DAMON Code for Virtual Address Spaces
*
* Author: SeongJae Park <sj@kernel.org>
*/

View file

@ -237,6 +237,8 @@ static int move_ptes(struct pagetable_move_control *pmc,
for (; old_addr < old_end; old_pte++, old_addr += PAGE_SIZE,
new_pte++, new_addr += PAGE_SIZE) {
VM_WARN_ON_ONCE(!pte_none(*new_pte));
if (pte_none(ptep_get(old_pte)))
continue;

View file

@ -169,6 +169,9 @@ static void init_multi_vma_prep(struct vma_prepare *vp,
vp->file = vma->vm_file;
if (vp->file)
vp->mapping = vma->vm_file->f_mapping;
if (vmg && vmg->skip_vma_uprobe)
vp->skip_vma_uprobe = true;
}
/*
@ -358,11 +361,14 @@ static void vma_complete(struct vma_prepare *vp, struct vma_iterator *vmi,
if (vp->file) {
i_mmap_unlock_write(vp->mapping);
if (!vp->skip_vma_uprobe) {
uprobe_mmap(vp->vma);
if (vp->adj_next)
uprobe_mmap(vp->adj_next);
}
}
if (vp->remove) {
again:
@ -1830,6 +1836,14 @@ struct vm_area_struct *copy_vma(struct vm_area_struct **vmap,
faulted_in_anon_vma = false;
}
/*
* If the VMA we are copying might contain a uprobe PTE, ensure
* that we do not establish one upon merge. Otherwise, when mremap()
* moves page tables, it will orphan the newly created PTE.
*/
if (vma->vm_file)
vmg.skip_vma_uprobe = true;
new_vma = find_vma_prev(mm, addr, &vmg.prev);
if (new_vma && new_vma->vm_start < addr + len)
return NULL; /* should never get here */

View file

@ -19,6 +19,8 @@ struct vma_prepare {
struct vm_area_struct *insert;
struct vm_area_struct *remove;
struct vm_area_struct *remove2;
bool skip_vma_uprobe :1;
};
struct unlink_vma_file_batch {
@ -120,6 +122,11 @@ struct vma_merge_struct {
*/
bool give_up_on_oom :1;
/*
* If set, skip uprobe_mmap upon merged vma.
*/
bool skip_vma_uprobe :1;
/* Internal flags set during merge process: */
/*

View file

@ -58,40 +58,12 @@ int debug;
static int ksm_write_sysfs(const char *file_path, unsigned long val)
{
FILE *f = fopen(file_path, "w");
if (!f) {
fprintf(stderr, "f %s\n", file_path);
perror("fopen");
return 1;
}
if (fprintf(f, "%lu", val) < 0) {
perror("fprintf");
fclose(f);
return 1;
}
fclose(f);
return 0;
return write_sysfs(file_path, val);
}
static int ksm_read_sysfs(const char *file_path, unsigned long *val)
{
FILE *f = fopen(file_path, "r");
if (!f) {
fprintf(stderr, "f %s\n", file_path);
perror("fopen");
return 1;
}
if (fscanf(f, "%lu", val) != 1) {
perror("fscanf");
fclose(f);
return 1;
}
fclose(f);
return 0;
return read_sysfs(file_path, val);
}
static void ksm_print_sysfs(void)

View file

@ -2,11 +2,14 @@
#define _GNU_SOURCE
#include "../kselftest_harness.h"
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <sys/wait.h>
#include <linux/perf_event.h>
#include "vm_util.h"
FIXTURE(merge)
@ -452,4 +455,44 @@ TEST_F(merge, forked_source_vma)
ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr2 + 5 * page_size);
}
TEST_F(merge, handle_uprobe_upon_merged_vma)
{
const size_t attr_sz = sizeof(struct perf_event_attr);
unsigned int page_size = self->page_size;
const char *probe_file = "./foo";
char *carveout = self->carveout;
struct perf_event_attr attr;
unsigned long type;
void *ptr1, *ptr2;
int fd;
fd = open(probe_file, O_RDWR|O_CREAT, 0600);
ASSERT_GE(fd, 0);
ASSERT_EQ(ftruncate(fd, page_size), 0);
ASSERT_EQ(read_sysfs("/sys/bus/event_source/devices/uprobe/type", &type), 0);
memset(&attr, 0, attr_sz);
attr.size = attr_sz;
attr.type = type;
attr.config1 = (__u64)(long)probe_file;
attr.config2 = 0x0;
ASSERT_GE(syscall(__NR_perf_event_open, &attr, 0, -1, -1, 0), 0);
ptr1 = mmap(&carveout[page_size], 10 * page_size, PROT_EXEC,
MAP_PRIVATE | MAP_FIXED, fd, 0);
ASSERT_NE(ptr1, MAP_FAILED);
ptr2 = mremap(ptr1, page_size, 2 * page_size,
MREMAP_MAYMOVE | MREMAP_FIXED, ptr1 + 5 * page_size);
ASSERT_NE(ptr2, MAP_FAILED);
ASSERT_NE(mremap(ptr2, page_size, page_size,
MREMAP_MAYMOVE | MREMAP_FIXED, ptr1), MAP_FAILED);
close(fd);
remove(probe_file);
}
TEST_HARNESS_MAIN

View file

@ -77,7 +77,7 @@ void show(unsigned long ps)
system(buf);
}
unsigned long read_sysfs(int warn, char *fmt, ...)
unsigned long thuge_read_sysfs(int warn, char *fmt, ...)
{
char *line = NULL;
size_t linelen = 0;
@ -106,7 +106,7 @@ unsigned long read_sysfs(int warn, char *fmt, ...)
unsigned long read_free(unsigned long ps)
{
return read_sysfs(ps != getpagesize(),
return thuge_read_sysfs(ps != getpagesize(),
"/sys/kernel/mm/hugepages/hugepages-%lukB/free_hugepages",
ps >> 10);
}
@ -195,7 +195,7 @@ void find_pagesizes(void)
}
globfree(&g);
if (read_sysfs(0, "/proc/sys/kernel/shmmax") < NUM_PAGES * largest)
if (thuge_read_sysfs(0, "/proc/sys/kernel/shmmax") < NUM_PAGES * largest)
ksft_exit_fail_msg("Please do echo %lu > /proc/sys/kernel/shmmax",
largest * NUM_PAGES);

View file

@ -486,3 +486,41 @@ int close_procmap(struct procmap_fd *procmap)
{
return close(procmap->fd);
}
int write_sysfs(const char *file_path, unsigned long val)
{
FILE *f = fopen(file_path, "w");
if (!f) {
fprintf(stderr, "f %s\n", file_path);
perror("fopen");
return 1;
}
if (fprintf(f, "%lu", val) < 0) {
perror("fprintf");
fclose(f);
return 1;
}
fclose(f);
return 0;
}
int read_sysfs(const char *file_path, unsigned long *val)
{
FILE *f = fopen(file_path, "r");
if (!f) {
fprintf(stderr, "f %s\n", file_path);
perror("fopen");
return 1;
}
if (fscanf(f, "%lu", val) != 1) {
perror("fscanf");
fclose(f);
return 1;
}
fclose(f);
return 0;
}

View file

@ -88,6 +88,8 @@ int open_procmap(pid_t pid, struct procmap_fd *procmap_out);
int query_procmap(struct procmap_fd *procmap);
bool find_vma_procmap(struct procmap_fd *procmap, void *address);
int close_procmap(struct procmap_fd *procmap);
int write_sysfs(const char *file_path, unsigned long val);
int read_sysfs(const char *file_path, unsigned long *val);
static inline int open_self_procmap(struct procmap_fd *procmap_out)
{