From 8d099bfbf256dcccce59fc7997e39b9edf449070 Mon Sep 17 00:00:00 2001 From: Oleksandr Natalenko Date: Mon, 29 Jul 2024 00:38:24 +0200 Subject: [PATCH 1/2] mm: expose per-process KSM control via syscalls d7597f59d1d3 added a new API to enable per-process KSM control. It however uses prctl, which doesn't allow controlling KSM from outside of the current process. Hence, expose this API via 3 syscalls: process_ksm_enable, process_ksm_disable and process_ksm_status. Given sufficient privileges, auto-KSM can be enable by another process. Since these syscalls are not in the upstream kernel, also expose their numbers under /sys/kernel/process_ksm so that userspace tooling like uksmd knows how to use them. Signed-off-by: Oleksandr Natalenko --- arch/alpha/kernel/syscalls/syscall.tbl | 3 + arch/arm/tools/syscall.tbl | 3 + arch/m68k/kernel/syscalls/syscall.tbl | 3 + arch/microblaze/kernel/syscalls/syscall.tbl | 3 + arch/mips/kernel/syscalls/syscall_n32.tbl | 3 + arch/mips/kernel/syscalls/syscall_n64.tbl | 3 + arch/mips/kernel/syscalls/syscall_o32.tbl | 3 + arch/parisc/kernel/syscalls/syscall.tbl | 3 + arch/powerpc/kernel/syscalls/syscall.tbl | 3 + arch/s390/kernel/syscalls/syscall.tbl | 3 + arch/sh/kernel/syscalls/syscall.tbl | 3 + arch/sparc/kernel/syscalls/syscall.tbl | 3 + arch/x86/entry/syscalls/syscall_32.tbl | 3 + arch/x86/entry/syscalls/syscall_64.tbl | 3 + arch/xtensa/kernel/syscalls/syscall.tbl | 3 + include/linux/syscalls.h | 3 + include/uapi/asm-generic/unistd.h | 9 +- kernel/sys.c | 147 ++++++++++++++++++ kernel/sys_ni.c | 3 + scripts/syscall.tbl | 3 + .../arch/powerpc/entry/syscalls/syscall.tbl | 3 + .../perf/arch/s390/entry/syscalls/syscall.tbl | 3 + 22 files changed, 215 insertions(+), 1 deletion(-) diff --git a/arch/alpha/kernel/syscalls/syscall.tbl b/arch/alpha/kernel/syscalls/syscall.tbl index 74720667fe0917..e6a11f3c0a2e3e 100644 --- a/arch/alpha/kernel/syscalls/syscall.tbl +++ b/arch/alpha/kernel/syscalls/syscall.tbl @@ -502,3 +502,6 @@ 570 common lsm_set_self_attr sys_lsm_set_self_attr 571 common lsm_list_modules sys_lsm_list_modules 572 common mseal sys_mseal +573 common process_ksm_enable sys_process_ksm_enable +574 common process_ksm_disable sys_process_ksm_disable +575 common process_ksm_status sys_process_ksm_status diff --git a/arch/arm/tools/syscall.tbl b/arch/arm/tools/syscall.tbl index 23c98203c40fe6..10a3099decbe38 100644 --- a/arch/arm/tools/syscall.tbl +++ b/arch/arm/tools/syscall.tbl @@ -477,3 +477,6 @@ 460 common lsm_set_self_attr sys_lsm_set_self_attr 461 common lsm_list_modules sys_lsm_list_modules 462 common mseal sys_mseal +463 common process_ksm_enable sys_process_ksm_enable +464 common process_ksm_disable sys_process_ksm_disable +465 common process_ksm_status sys_process_ksm_status diff --git a/arch/m68k/kernel/syscalls/syscall.tbl b/arch/m68k/kernel/syscalls/syscall.tbl index 22a3cbd4c60298..12d2c7594bf0b7 100644 --- a/arch/m68k/kernel/syscalls/syscall.tbl +++ b/arch/m68k/kernel/syscalls/syscall.tbl @@ -462,3 +462,6 @@ 460 common lsm_set_self_attr sys_lsm_set_self_attr 461 common lsm_list_modules sys_lsm_list_modules 462 common mseal sys_mseal +463 common process_ksm_enable sys_process_ksm_enable +464 common process_ksm_disable sys_process_ksm_disable +465 common process_ksm_status sys_process_ksm_status diff --git a/arch/microblaze/kernel/syscalls/syscall.tbl b/arch/microblaze/kernel/syscalls/syscall.tbl index 2b81a6bd78b292..e2a93c856eede9 100644 --- a/arch/microblaze/kernel/syscalls/syscall.tbl +++ b/arch/microblaze/kernel/syscalls/syscall.tbl @@ -468,3 +468,6 @@ 460 common lsm_set_self_attr sys_lsm_set_self_attr 461 common lsm_list_modules sys_lsm_list_modules 462 common mseal sys_mseal +463 common process_ksm_enable sys_process_ksm_enable +464 common process_ksm_disable sys_process_ksm_disable +465 common process_ksm_status sys_process_ksm_status diff --git a/arch/mips/kernel/syscalls/syscall_n32.tbl b/arch/mips/kernel/syscalls/syscall_n32.tbl index 953f5b7dc723f6..b921fbf56fa6d9 100644 --- a/arch/mips/kernel/syscalls/syscall_n32.tbl +++ b/arch/mips/kernel/syscalls/syscall_n32.tbl @@ -401,3 +401,6 @@ 460 n32 lsm_set_self_attr sys_lsm_set_self_attr 461 n32 lsm_list_modules sys_lsm_list_modules 462 n32 mseal sys_mseal +463 n32 process_ksm_enable sys_process_ksm_enable +464 n32 process_ksm_disable sys_process_ksm_disable +465 n32 process_ksm_status sys_process_ksm_status diff --git a/arch/mips/kernel/syscalls/syscall_n64.tbl b/arch/mips/kernel/syscalls/syscall_n64.tbl index 1464c6be6eb3c7..8d7f9ddd66f4be 100644 --- a/arch/mips/kernel/syscalls/syscall_n64.tbl +++ b/arch/mips/kernel/syscalls/syscall_n64.tbl @@ -377,3 +377,6 @@ 460 n64 lsm_set_self_attr sys_lsm_set_self_attr 461 n64 lsm_list_modules sys_lsm_list_modules 462 n64 mseal sys_mseal +463 n64 process_ksm_enable sys_process_ksm_enable +464 n64 process_ksm_disable sys_process_ksm_disable +465 n64 process_ksm_status sys_process_ksm_status diff --git a/arch/mips/kernel/syscalls/syscall_o32.tbl b/arch/mips/kernel/syscalls/syscall_o32.tbl index 2439a2491cffe3..9d6142739954d6 100644 --- a/arch/mips/kernel/syscalls/syscall_o32.tbl +++ b/arch/mips/kernel/syscalls/syscall_o32.tbl @@ -450,3 +450,6 @@ 460 o32 lsm_set_self_attr sys_lsm_set_self_attr 461 o32 lsm_list_modules sys_lsm_list_modules 462 o32 mseal sys_mseal +463 o32 process_ksm_enable sys_process_ksm_enable +464 o32 process_ksm_disable sys_process_ksm_disable +465 o32 process_ksm_status sys_process_ksm_status diff --git a/arch/parisc/kernel/syscalls/syscall.tbl b/arch/parisc/kernel/syscalls/syscall.tbl index 66dc406b12e448..9d46476fd90830 100644 --- a/arch/parisc/kernel/syscalls/syscall.tbl +++ b/arch/parisc/kernel/syscalls/syscall.tbl @@ -461,3 +461,6 @@ 460 common lsm_set_self_attr sys_lsm_set_self_attr 461 common lsm_list_modules sys_lsm_list_modules 462 common mseal sys_mseal +463 common process_ksm_enable sys_process_ksm_enable +464 common process_ksm_disable sys_process_ksm_disable +465 common process_ksm_status sys_process_ksm_status diff --git a/arch/powerpc/kernel/syscalls/syscall.tbl b/arch/powerpc/kernel/syscalls/syscall.tbl index ebae8415dfbbab..16f71bc2f6f012 100644 --- a/arch/powerpc/kernel/syscalls/syscall.tbl +++ b/arch/powerpc/kernel/syscalls/syscall.tbl @@ -553,3 +553,6 @@ 460 common lsm_set_self_attr sys_lsm_set_self_attr 461 common lsm_list_modules sys_lsm_list_modules 462 common mseal sys_mseal +463 common process_ksm_enable sys_process_ksm_enable +464 common process_ksm_disable sys_process_ksm_disable +465 common process_ksm_status sys_process_ksm_status diff --git a/arch/s390/kernel/syscalls/syscall.tbl b/arch/s390/kernel/syscalls/syscall.tbl index 01071182763e96..7394bad8178e62 100644 --- a/arch/s390/kernel/syscalls/syscall.tbl +++ b/arch/s390/kernel/syscalls/syscall.tbl @@ -465,3 +465,6 @@ 460 common lsm_set_self_attr sys_lsm_set_self_attr sys_lsm_set_self_attr 461 common lsm_list_modules sys_lsm_list_modules sys_lsm_list_modules 462 common mseal sys_mseal sys_mseal +463 common process_ksm_enable sys_process_ksm_enable sys_process_ksm_enable +464 common process_ksm_disable sys_process_ksm_disable sys_process_ksm_disable +465 common process_ksm_status sys_process_ksm_status sys_process_ksm_status diff --git a/arch/sh/kernel/syscalls/syscall.tbl b/arch/sh/kernel/syscalls/syscall.tbl index c55fd7696d40fc..b9fc31221b8744 100644 --- a/arch/sh/kernel/syscalls/syscall.tbl +++ b/arch/sh/kernel/syscalls/syscall.tbl @@ -466,3 +466,6 @@ 460 common lsm_set_self_attr sys_lsm_set_self_attr 461 common lsm_list_modules sys_lsm_list_modules 462 common mseal sys_mseal +463 common process_ksm_enable sys_process_ksm_enable +464 common process_ksm_disable sys_process_ksm_disable +465 common process_ksm_status sys_process_ksm_status diff --git a/arch/sparc/kernel/syscalls/syscall.tbl b/arch/sparc/kernel/syscalls/syscall.tbl index cfdfb3707c167c..0d79fd77285407 100644 --- a/arch/sparc/kernel/syscalls/syscall.tbl +++ b/arch/sparc/kernel/syscalls/syscall.tbl @@ -508,3 +508,6 @@ 460 common lsm_set_self_attr sys_lsm_set_self_attr 461 common lsm_list_modules sys_lsm_list_modules 462 common mseal sys_mseal +463 common process_ksm_enable sys_process_ksm_enable +464 common process_ksm_disable sys_process_ksm_disable +465 common process_ksm_status sys_process_ksm_status diff --git a/arch/x86/entry/syscalls/syscall_32.tbl b/arch/x86/entry/syscalls/syscall_32.tbl index 534c74b14fab51..c546a30575f1cb 100644 --- a/arch/x86/entry/syscalls/syscall_32.tbl +++ b/arch/x86/entry/syscalls/syscall_32.tbl @@ -468,3 +468,6 @@ 460 i386 lsm_set_self_attr sys_lsm_set_self_attr 461 i386 lsm_list_modules sys_lsm_list_modules 462 i386 mseal sys_mseal +463 i386 process_ksm_enable sys_process_ksm_enable +464 i386 process_ksm_disable sys_process_ksm_disable +465 i386 process_ksm_status sys_process_ksm_status diff --git a/arch/x86/entry/syscalls/syscall_64.tbl b/arch/x86/entry/syscalls/syscall_64.tbl index 7093ee21c0d1c0..0fcd10ba8dfeba 100644 --- a/arch/x86/entry/syscalls/syscall_64.tbl +++ b/arch/x86/entry/syscalls/syscall_64.tbl @@ -386,6 +386,9 @@ 460 common lsm_set_self_attr sys_lsm_set_self_attr 461 common lsm_list_modules sys_lsm_list_modules 462 common mseal sys_mseal +463 common process_ksm_enable sys_process_ksm_enable +464 common process_ksm_disable sys_process_ksm_disable +465 common process_ksm_status sys_process_ksm_status # # Due to a historical design error, certain syscalls are numbered differently diff --git a/arch/xtensa/kernel/syscalls/syscall.tbl b/arch/xtensa/kernel/syscalls/syscall.tbl index 67083fc1b2f563..c1aecee4ad9b6b 100644 --- a/arch/xtensa/kernel/syscalls/syscall.tbl +++ b/arch/xtensa/kernel/syscalls/syscall.tbl @@ -433,3 +433,6 @@ 460 common lsm_set_self_attr sys_lsm_set_self_attr 461 common lsm_list_modules sys_lsm_list_modules 462 common mseal sys_mseal +463 common process_ksm_enable sys_process_ksm_enable +464 common process_ksm_disable sys_process_ksm_disable +465 common process_ksm_status sys_process_ksm_status diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 5758104921e66e..cc9c4fac2412a3 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -818,6 +818,9 @@ asmlinkage long sys_madvise(unsigned long start, size_t len, int behavior); asmlinkage long sys_process_madvise(int pidfd, const struct iovec __user *vec, size_t vlen, int behavior, unsigned int flags); asmlinkage long sys_process_mrelease(int pidfd, unsigned int flags); +asmlinkage long sys_process_ksm_enable(int pidfd, unsigned int flags); +asmlinkage long sys_process_ksm_disable(int pidfd, unsigned int flags); +asmlinkage long sys_process_ksm_status(int pidfd, unsigned int flags); asmlinkage long sys_remap_file_pages(unsigned long start, unsigned long size, unsigned long prot, unsigned long pgoff, unsigned long flags); diff --git a/include/uapi/asm-generic/unistd.h b/include/uapi/asm-generic/unistd.h index 5bf6148cac2b93..613e559ad6e0a3 100644 --- a/include/uapi/asm-generic/unistd.h +++ b/include/uapi/asm-generic/unistd.h @@ -841,8 +841,15 @@ __SYSCALL(__NR_lsm_list_modules, sys_lsm_list_modules) #define __NR_mseal 462 __SYSCALL(__NR_mseal, sys_mseal) +#define __NR_process_ksm_enable 463 +__SYSCALL(__NR_process_ksm_enable, sys_process_ksm_enable) +#define __NR_process_ksm_disable 464 +__SYSCALL(__NR_process_ksm_disable, sys_process_ksm_disable) +#define __NR_process_ksm_status 465 +__SYSCALL(__NR_process_ksm_status, sys_process_ksm_status) + #undef __NR_syscalls -#define __NR_syscalls 463 +#define __NR_syscalls 466 /* * 32 bit systems traditionally used different diff --git a/kernel/sys.c b/kernel/sys.c index 4da31f28fda81f..7d90efbb5adf74 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -2791,6 +2791,153 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3, return error; } +#ifdef CONFIG_KSM +enum pkc_action { + PKSM_ENABLE = 0, + PKSM_DISABLE, + PKSM_STATUS, +}; + +static long do_process_ksm_control(int pidfd, enum pkc_action action) +{ + long ret; + struct pid *pid; + struct task_struct *task; + struct mm_struct *mm; + unsigned int f_flags; + + pid = pidfd_get_pid(pidfd, &f_flags); + if (IS_ERR(pid)) { + ret = PTR_ERR(pid); + goto out; + } + + task = get_pid_task(pid, PIDTYPE_PID); + if (!task) { + ret = -ESRCH; + goto put_pid; + } + + /* Require PTRACE_MODE_READ to avoid leaking ASLR metadata. */ + mm = mm_access(task, PTRACE_MODE_READ_FSCREDS); + if (IS_ERR_OR_NULL(mm)) { + ret = IS_ERR(mm) ? PTR_ERR(mm) : -ESRCH; + goto release_task; + } + + /* Require CAP_SYS_NICE for influencing process performance. */ + if (!capable(CAP_SYS_NICE)) { + ret = -EPERM; + goto release_mm; + } + + if (mmap_write_lock_killable(mm)) { + ret = -EINTR; + goto release_mm; + } + + switch (action) { + case PKSM_ENABLE: + ret = ksm_enable_merge_any(mm); + break; + case PKSM_DISABLE: + ret = ksm_disable_merge_any(mm); + break; + case PKSM_STATUS: + ret = !!test_bit(MMF_VM_MERGE_ANY, &mm->flags); + break; + } + + mmap_write_unlock(mm); + +release_mm: + mmput(mm); +release_task: + put_task_struct(task); +put_pid: + put_pid(pid); +out: + return ret; +} +#endif /* CONFIG_KSM */ + +SYSCALL_DEFINE2(process_ksm_enable, int, pidfd, unsigned int, flags) +{ +#ifdef CONFIG_KSM + if (flags != 0) + return -EINVAL; + + return do_process_ksm_control(pidfd, PKSM_ENABLE); +#else /* CONFIG_KSM */ + return -ENOSYS; +#endif /* CONFIG_KSM */ +} + +SYSCALL_DEFINE2(process_ksm_disable, int, pidfd, unsigned int, flags) +{ +#ifdef CONFIG_KSM + if (flags != 0) + return -EINVAL; + + return do_process_ksm_control(pidfd, PKSM_DISABLE); +#else /* CONFIG_KSM */ + return -ENOSYS; +#endif /* CONFIG_KSM */ +} + +SYSCALL_DEFINE2(process_ksm_status, int, pidfd, unsigned int, flags) +{ +#ifdef CONFIG_KSM + if (flags != 0) + return -EINVAL; + + return do_process_ksm_control(pidfd, PKSM_STATUS); +#else /* CONFIG_KSM */ + return -ENOSYS; +#endif /* CONFIG_KSM */ +} + +#ifdef CONFIG_KSM +static ssize_t process_ksm_enable_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", __NR_process_ksm_enable); +} +static struct kobj_attribute process_ksm_enable_attr = __ATTR_RO(process_ksm_enable); + +static ssize_t process_ksm_disable_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", __NR_process_ksm_disable); +} +static struct kobj_attribute process_ksm_disable_attr = __ATTR_RO(process_ksm_disable); + +static ssize_t process_ksm_status_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", __NR_process_ksm_status); +} +static struct kobj_attribute process_ksm_status_attr = __ATTR_RO(process_ksm_status); + +static struct attribute *process_ksm_sysfs_attrs[] = { + &process_ksm_enable_attr.attr, + &process_ksm_disable_attr.attr, + &process_ksm_status_attr.attr, + NULL, +}; + +static const struct attribute_group process_ksm_sysfs_attr_group = { + .attrs = process_ksm_sysfs_attrs, + .name = "process_ksm", +}; + +static int __init process_ksm_sysfs_init(void) +{ + return sysfs_create_group(kernel_kobj, &process_ksm_sysfs_attr_group); +} +subsys_initcall(process_ksm_sysfs_init); +#endif /* CONFIG_KSM */ + SYSCALL_DEFINE3(getcpu, unsigned __user *, cpup, unsigned __user *, nodep, struct getcpu_cache __user *, unused) { diff --git a/kernel/sys_ni.c b/kernel/sys_ni.c index c00a86931f8c6c..d82213d68522cc 100644 --- a/kernel/sys_ni.c +++ b/kernel/sys_ni.c @@ -186,6 +186,9 @@ COND_SYSCALL(mincore); COND_SYSCALL(madvise); COND_SYSCALL(process_madvise); COND_SYSCALL(process_mrelease); +COND_SYSCALL(process_ksm_enable); +COND_SYSCALL(process_ksm_disable); +COND_SYSCALL(process_ksm_status); COND_SYSCALL(remap_file_pages); COND_SYSCALL(mbind); COND_SYSCALL(get_mempolicy); diff --git a/scripts/syscall.tbl b/scripts/syscall.tbl index 845e24eb372e7f..227d9cc1236515 100644 --- a/scripts/syscall.tbl +++ b/scripts/syscall.tbl @@ -403,3 +403,6 @@ 460 common lsm_set_self_attr sys_lsm_set_self_attr 461 common lsm_list_modules sys_lsm_list_modules 462 common mseal sys_mseal +463 common process_ksm_enable sys_process_ksm_enable +464 common process_ksm_disable sys_process_ksm_disable +465 common process_ksm_status sys_process_ksm_status diff --git a/tools/perf/arch/powerpc/entry/syscalls/syscall.tbl b/tools/perf/arch/powerpc/entry/syscalls/syscall.tbl index ebae8415dfbbab..16f71bc2f6f012 100644 --- a/tools/perf/arch/powerpc/entry/syscalls/syscall.tbl +++ b/tools/perf/arch/powerpc/entry/syscalls/syscall.tbl @@ -553,3 +553,6 @@ 460 common lsm_set_self_attr sys_lsm_set_self_attr 461 common lsm_list_modules sys_lsm_list_modules 462 common mseal sys_mseal +463 common process_ksm_enable sys_process_ksm_enable +464 common process_ksm_disable sys_process_ksm_disable +465 common process_ksm_status sys_process_ksm_status diff --git a/tools/perf/arch/s390/entry/syscalls/syscall.tbl b/tools/perf/arch/s390/entry/syscalls/syscall.tbl index 01071182763e96..7394bad8178e62 100644 --- a/tools/perf/arch/s390/entry/syscalls/syscall.tbl +++ b/tools/perf/arch/s390/entry/syscalls/syscall.tbl @@ -465,3 +465,6 @@ 460 common lsm_set_self_attr sys_lsm_set_self_attr sys_lsm_set_self_attr 461 common lsm_list_modules sys_lsm_list_modules sys_lsm_list_modules 462 common mseal sys_mseal sys_mseal +463 common process_ksm_enable sys_process_ksm_enable sys_process_ksm_enable +464 common process_ksm_disable sys_process_ksm_disable sys_process_ksm_disable +465 common process_ksm_status sys_process_ksm_status sys_process_ksm_status From 719e13a0dc8d2428a1b53b49f9d98eff8ba66785 Mon Sep 17 00:00:00 2001 From: Oleksandr Natalenko Date: Tue, 24 Sep 2024 11:58:41 +0200 Subject: [PATCH 2/2] mm/process_ksm: use pidfd_get_task() instead of pidfd_get_pid()+get_pid_task() Link: https://git.kernel.org/linus/ee9955d61a0a Signed-off-by: Oleksandr Natalenko --- kernel/sys.c | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/kernel/sys.c b/kernel/sys.c index 7d90efbb5adf74..fcd3aeaddd05f2 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -2801,23 +2801,16 @@ enum pkc_action { static long do_process_ksm_control(int pidfd, enum pkc_action action) { long ret; - struct pid *pid; struct task_struct *task; struct mm_struct *mm; unsigned int f_flags; - pid = pidfd_get_pid(pidfd, &f_flags); - if (IS_ERR(pid)) { - ret = PTR_ERR(pid); + task = pidfd_get_task(pidfd, &f_flags); + if (IS_ERR(task)) { + ret = PTR_ERR(task); goto out; } - task = get_pid_task(pid, PIDTYPE_PID); - if (!task) { - ret = -ESRCH; - goto put_pid; - } - /* Require PTRACE_MODE_READ to avoid leaking ASLR metadata. */ mm = mm_access(task, PTRACE_MODE_READ_FSCREDS); if (IS_ERR_OR_NULL(mm)) { @@ -2854,8 +2847,6 @@ static long do_process_ksm_control(int pidfd, enum pkc_action action) mmput(mm); release_task: put_task_struct(task); -put_pid: - put_pid(pid); out: return ret; }