linux-mm.kvack.org archive mirror
 help / color / mirror / Atom feed
* [PATCH linux-next v2 0/2] ksm: fix exec/fork inheritance
@ 2025-10-07 10:25 xu.xin16
  2025-10-07 10:28 ` [PATCH linux-next v2 1/2] mm/ksm: fix exec/fork inheritance support for prctl xu.xin16
  2025-10-07 10:29 ` [PATCH linux-next v2 2/2] selftests: update ksm inheritation tests for prctl fork/exec xu.xin16
  0 siblings, 2 replies; 3+ messages in thread
From: xu.xin16 @ 2025-10-07 10:25 UTC (permalink / raw)
  To: akpm, david, tujinjiang, shr
  Cc: linux-mm, linux-kernel, stable, yang.yang29, wang.yaxin

From: xu xin <xu.xin16@zte.com.cn>

This series aim to fix exec/fork inheritance. See the detailed description
of issue at the following patch.

PATCH 1: the patch to fix the issue.
PATCH 2: a reproduce program or testcase.

xu xin (2):
  mm/ksm: fix exec/fork inheritance support for prctl
  selftests: update ksm inheritation tests for prctl fork/exec

 include/linux/ksm.h                           |  4 +-
 mm/ksm.c                                      | 20 ++++++-
 .../selftests/mm/ksm_functional_tests.c       | 57 +++++++++++++++++++
 3 files changed, 76 insertions(+), 5 deletions(-)

-- 
2.25.1


^ permalink raw reply	[flat|nested] 3+ messages in thread

* [PATCH linux-next v2 1/2] mm/ksm: fix exec/fork inheritance support for prctl
  2025-10-07 10:25 [PATCH linux-next v2 0/2] ksm: fix exec/fork inheritance xu.xin16
@ 2025-10-07 10:28 ` xu.xin16
  2025-10-07 10:29 ` [PATCH linux-next v2 2/2] selftests: update ksm inheritation tests for prctl fork/exec xu.xin16
  1 sibling, 0 replies; 3+ messages in thread
From: xu.xin16 @ 2025-10-07 10:28 UTC (permalink / raw)
  To: xu.xin16, akpm, david, shr
  Cc: akpm, david, tujinjiang, shr, linux-mm, linux-kernel, stable,
	yang.yang29, wang.yaxin

From: xu xin <xu.xin16@zte.com.cn>

Background
==========

The commit d7597f59d1d33 ("mm: add new api to enable ksm per process") introduce
MMF_VM_MERGE_ANY for mm->flags, and allow user to set it by prctl() so that the
process's VMAs are forcely scanned by ksmd. Sequently, the commit 3c6f33b7273a
("mm/ksm: support fork/exec for prctl") support inheritsingMMF_VM_MERGE_ANY flag
when a task calls execve(). Lastly, The commit 3a9e567ca45fb
("mm/ksm: fix ksm exec support for prctl") fixed the issue that ksmd doesn't scan
the mm_struct with MMF_VM_MERGE_ANY by adding the mm_slot to ksm_mm_head
in __bprm_mm_init().

Problem
=======

In some extreme scenarios, however, this inheritance of MMF_VM_MERGE_ANY during
exec/fork can fail. For example, when the scanning frequency of ksmd is tuned
extremely high, a process carrying MMF_VM_MERGE_ANY may still fail to pass it to
the newly exec'd process. This happens because ksm_execve() is executed too early
in the do_execve flow (prematurely adding the new mm_struct to the ksm_mm_slot list).

As a result, before do_execve completes, ksmd may have already performed a scan and
found that this new mm_struct has no VM_MERGEABLE VMAs, thus clearing its
MMF_VM_MERGE_ANY flag. Consequently, when the new program executes, the flag
MMF_VM_MERGE_ANY inheritance missed.

Root reason
===========

The commit d7597f59d1d33 ("mm: add new api to enable ksm per process") clear the
flag MMF_VM_MERGE_ANY when ksmd found no VM_MERGEABLE VMAs.

Solution
========

First, Don't clear MMF_VM_MERGE_ANY when ksmd found no VM_MERGEABLE VMAs, because perhaps
their mm_struct has just been added to ksm_mm_slot list, and its process has not yet
officially started running or has not yet performed mmap/brk to allocate anonymous VMAS.

Second, recheck MMF_VM_MERGEABLE again if a process takes MMF_VM_MERGE_ANY, and create
a mm_slot and join it into ksm_scan_list again.

Fixes: 3c6f33b7273a ("mm/ksm: support fork/exec for prctl")
Fixes: d7597f59d1d3 ("mm: add new api to enable ksm per process")
Signed-off-by: xu xin <xu.xin16@zte.com.cn>
Cc: stable@vger.kernel.org
Cc: Stefan Roesch <shr@devkernel.io>
Cc: David Hildenbrand <david@redhat.com>
Cc: Jinjiang Tu <tujinjiang@huawei.com>
Cc: Wang Yaxin <wang.yaxin@zte.com.cn>
---
 include/linux/ksm.h |  4 ++--
 mm/ksm.c            | 20 +++++++++++++++++---
 2 files changed, 19 insertions(+), 5 deletions(-)

diff --git a/include/linux/ksm.h b/include/linux/ksm.h
index 067538fc4d58..c982694c987b 100644
--- a/include/linux/ksm.h
+++ b/include/linux/ksm.h
@@ -17,7 +17,7 @@
 #ifdef CONFIG_KSM
 int ksm_madvise(struct vm_area_struct *vma, unsigned long start,
 		unsigned long end, int advice, vm_flags_t *vm_flags);
-vm_flags_t ksm_vma_flags(const struct mm_struct *mm, const struct file *file,
+vm_flags_t ksm_vma_flags(struct mm_struct *mm, const struct file *file,
 			 vm_flags_t vm_flags);
 int ksm_enable_merge_any(struct mm_struct *mm);
 int ksm_disable_merge_any(struct mm_struct *mm);
@@ -103,7 +103,7 @@ bool ksm_process_mergeable(struct mm_struct *mm);

 #else  /* !CONFIG_KSM */

-static inline vm_flags_t ksm_vma_flags(const struct mm_struct *mm,
+static inline vm_flags_t ksm_vma_flags(struct mm_struct *mm,
 		const struct file *file, vm_flags_t vm_flags)
 {
 	return vm_flags;
diff --git a/mm/ksm.c b/mm/ksm.c
index 04019a15b25d..19efe3d41c75 100644
--- a/mm/ksm.c
+++ b/mm/ksm.c
@@ -2617,8 +2617,14 @@ static struct ksm_rmap_item *scan_get_next_rmap_item(struct page **page)
 		spin_unlock(&ksm_mmlist_lock);

 		mm_slot_free(mm_slot_cache, mm_slot);
+		/*
+		 * Only clear MMF_VM_MERGEABLE. We must not clear
+		 * MMF_VM_MERGE_ANY, because for those MMF_VM_MERGE_ANY process,
+		 * perhaps their mm_struct has just been added to ksm_mm_slot
+		 * list, and its process has not yet officially started running
+		 * or has not yet performed mmap/brk to allocate anonymous VMAS.
+		 */
 		mm_flags_clear(MMF_VM_MERGEABLE, mm);
-		mm_flags_clear(MMF_VM_MERGE_ANY, mm);
 		mmap_read_unlock(mm);
 		mmdrop(mm);
 	} else {
@@ -2736,12 +2742,20 @@ static int __ksm_del_vma(struct vm_area_struct *vma)
  *
  * Returns: @vm_flags possibly updated to mark mergeable.
  */
-vm_flags_t ksm_vma_flags(const struct mm_struct *mm, const struct file *file,
+vm_flags_t ksm_vma_flags(struct mm_struct *mm, const struct file *file,
 			 vm_flags_t vm_flags)
 {
 	if (mm_flags_test(MMF_VM_MERGE_ANY, mm) &&
-	    __ksm_should_add_vma(file, vm_flags))
+	    __ksm_should_add_vma(file, vm_flags)) {
 		vm_flags |= VM_MERGEABLE;
+		/*
+		 * Generally, the flags here always include MMF_VM_MERGEABLE.
+		 * However, in rare cases, this flag may be cleared by ksmd who
+		 * scans a cycle without finding any mergeable vma.
+		 */
+		if (unlikely(!mm_flags_test(MMF_VM_MERGEABLE, mm)))
+			__ksm_enter(mm);
+	}

 	return vm_flags;
 }
-- 
2.25.1


^ permalink raw reply	[flat|nested] 3+ messages in thread

* [PATCH linux-next v2 2/2] selftests: update ksm inheritation tests for prctl fork/exec
  2025-10-07 10:25 [PATCH linux-next v2 0/2] ksm: fix exec/fork inheritance xu.xin16
  2025-10-07 10:28 ` [PATCH linux-next v2 1/2] mm/ksm: fix exec/fork inheritance support for prctl xu.xin16
@ 2025-10-07 10:29 ` xu.xin16
  1 sibling, 0 replies; 3+ messages in thread
From: xu.xin16 @ 2025-10-07 10:29 UTC (permalink / raw)
  To: xu.xin16, akpm, david, tujinjiang, shr
  Cc: akpm, david, tujinjiang, shr, linux-mm, linux-kernel, stable,
	yang.yang29, wang.yaxin

From: xu xin <xu.xin16@zte.com.cn>

To reproduce the issue mentioned by [1], this add a setting of pages_to_scan
and sleep_millisecs at the start of test_prctl_fork_exec(). The main change
is just raise the scanning frequency of ksmd.

[1] https://lore.kernel.org/all/202510012256278259zrhgATlLA2C510DMD3qI@zte.com.cn/

Signed-off-by: xu xin <xu.xin16@zte.com.cn>
---
 .../selftests/mm/ksm_functional_tests.c       | 57 +++++++++++++++++++
 1 file changed, 57 insertions(+)

diff --git a/tools/testing/selftests/mm/ksm_functional_tests.c b/tools/testing/selftests/mm/ksm_functional_tests.c
index ac136f04b8d6..95afa5cfc062 100644
--- a/tools/testing/selftests/mm/ksm_functional_tests.c
+++ b/tools/testing/selftests/mm/ksm_functional_tests.c
@@ -38,6 +38,8 @@ enum ksm_merge_mode {
 };

 static int mem_fd;
+static int pages_to_scan_fd;
+static int sleep_millisecs_fd;
 static int pagemap_fd;
 static size_t pagesize;

@@ -493,6 +495,46 @@ static void test_prctl_fork(void)
 	ksft_test_result_pass("PR_SET_MEMORY_MERGE value is inherited\n");
 }

+static int start_ksmd_and_set_frequency(char *pages_to_scan, char *sleep_ms)
+{
+	int ksm_fd;
+
+	ksm_fd = open("/sys/kernel/mm/ksm/run", O_RDWR);
+	if (ksm_fd < 0)
+		return -errno;
+
+	if (write(ksm_fd, "1", 1) != 1)
+		return -errno;
+
+	if (write(pages_to_scan_fd, pages_to_scan, strlen(pages_to_scan)) <= 0)
+		return -errno;
+
+	if (write(sleep_millisecs_fd, sleep_ms, strlen(sleep_ms)) <= 0)
+		return -errno;
+
+	return 0;
+}
+
+static int stop_ksmd_and_restore_frequency(void)
+{
+	int ksm_fd;
+
+	ksm_fd = open("/sys/kernel/mm/ksm/run", O_RDWR);
+	if (ksm_fd < 0)
+		return -errno;
+
+	if (write(ksm_fd, "2", 1) != 1)
+		return -errno;
+
+	if (write(pages_to_scan_fd, "100", 3) <= 0)
+		return -errno;
+
+	if (write(sleep_millisecs_fd, "20", 2) <= 0)
+		return -errno;
+
+	return 0;
+}
+
 static void test_prctl_fork_exec(void)
 {
 	int ret, status;
@@ -500,6 +542,9 @@ static void test_prctl_fork_exec(void)

 	ksft_print_msg("[RUN] %s\n", __func__);

+	if (start_ksmd_and_set_frequency("2000", "0"))
+		ksft_test_result_fail("set ksmd's scanning frequency failed\n");
+
 	ret = prctl(PR_SET_MEMORY_MERGE, 1, 0, 0, 0);
 	if (ret < 0 && errno == EINVAL) {
 		ksft_test_result_skip("PR_SET_MEMORY_MERGE not supported\n");
@@ -542,6 +587,11 @@ static void test_prctl_fork_exec(void)
 		return;
 	}

+	if (stop_ksmd_and_restore_frequency()) {
+		ksft_test_result_fail("restore ksmd frequency failed\n");
+		return;
+	}
+
 	ksft_test_result_pass("PR_SET_MEMORY_MERGE value is inherited\n");
 }

@@ -656,6 +706,13 @@ static void init_global_file_handles(void)
 		ksft_exit_skip("open(\"/proc/self/pagemap\") failed\n");
 	if (ksm_get_self_merging_pages() < 0)
 		ksft_exit_skip("accessing \"/proc/self/ksm_merging_pages\") failed\n");
+
+	pages_to_scan_fd = open("/sys/kernel/mm/ksm/pages_to_scan", O_RDWR);
+	if (pages_to_scan_fd < 0)
+		ksft_exit_fail_msg("opening /sys/kernel/mm/ksm/pages_to_scan failed\n");
+	sleep_millisecs_fd = open("/sys/kernel/mm/ksm/sleep_millisecs", O_RDWR);
+	if (sleep_millisecs_fd < 0)
+		ksft_exit_fail_msg("opening /sys/kernel/mm/ksm/sleep_millisecs failed\n");
 }

 int main(int argc, char **argv)
-- 
2.25.1


^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2025-10-07 10:29 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-10-07 10:25 [PATCH linux-next v2 0/2] ksm: fix exec/fork inheritance xu.xin16
2025-10-07 10:28 ` [PATCH linux-next v2 1/2] mm/ksm: fix exec/fork inheritance support for prctl xu.xin16
2025-10-07 10:29 ` [PATCH linux-next v2 2/2] selftests: update ksm inheritation tests for prctl fork/exec xu.xin16

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox