From 4eb6615c1498cb8bff76c31e9596b585410f507d Mon Sep 17 00:00:00 2001 From: Oleksandr Natalenko Date: Mon, 30 Sep 2024 08:58:38 +0200 Subject: 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 | 138 ++++++++++++++++++ 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, 206 insertions(+), 1 deletion(-) --- 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 --- 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 --- 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 --- 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 --- 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 --- 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 --- 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 --- 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 --- 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 --- 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 --- 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 --- 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 --- 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 --- 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 --- 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 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -818,6 +818,9 @@ asmlinkage long sys_madvise(unsigned lon 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); --- 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 #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 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -2791,6 +2791,144 @@ SYSCALL_DEFINE5(prctl, int, option, unsi 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 task_struct *task; + struct mm_struct *mm; + unsigned int f_flags; + + task = pidfd_get_task(pidfd, &f_flags); + if (IS_ERR(task)) { + ret = PTR_ERR(task); + goto out; + } + + /* 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); +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) { --- 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); --- 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 --- 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 --- 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