mirror of
https://gitlab.com/redhat/centos-stream/src/kernel/centos-stream-10.git
synced 2026-09-09 00:07:04 +08:00
JIRA: https://redhat.atlassian.net/browse/RHEL-215975 Conflicts: arch/loongarch/kernel/syscall.c arch/x86/entry/syscall_32.c Drop these bits not used in RHEL. commit a96ef5848cb096226bf6aff31a90d8b136d99b71 Author: Ryan Roberts <ryan.roberts@arm.com> Date: Tue, 3 Mar 2026 15:08:39 +0000 Previously different architectures were using random sources of differing strength and cost to decide the random kstack offset. A number of architectures (loongarch, powerpc, s390, x86) were using their timestamp counter, at whatever the frequency happened to be. Other arches (arm64, riscv) were using entropy from the crng via get_random_u16(). There have been concerns that in some cases the timestamp counters may be too weak, because they can be easily guessed or influenced by user space. And get_random_u16() has been shown to be too costly for the level of protection kstack offset randomization provides. So let's use a common, architecture-agnostic source of entropy; a per-cpu prng, seeded at boot-time from the crng. This has a few benefits: - We can remove choose_random_kstack_offset(); That was only there to try to make the timestamp counter value a bit harder to influence from user space [*]. - The architecture code is simplified. All it has to do now is call add_random_kstack_offset() in the syscall path. - The strength of the randomness can be reasoned about independently of the architecture. - Arches previously using get_random_u16() now have much faster syscall paths, see below results. [*] Additionally, this gets rid of some redundant work on s390 and x86. Before this patch, those architectures called choose_random_kstack_offset() under arch_exit_to_user_mode_prepare(), which is also called for exception returns to userspace which were *not* syscalls (e.g. regular interrupts). Getting rid of choose_random_kstack_offset() avoids a small amount of redundant work for the non-syscall cases. In some configurations, add_random_kstack_offset() will now call instrumentable code, so for a couple of arches, I have moved the call a bit later to the first point where instrumentation is allowed. This doesn't impact the efficacy of the mechanism. There have been some claims that a prng may be less strong than the timestamp counter if not regularly reseeded. But the prng has a period of about 2^113. So as long as the prng state remains secret, it should not be possible to guess. If the prng state can be accessed, we have bigger problems. Additionally, we are only consuming 6 bits to randomize the stack, so there are only 64 possible random offsets. I assert that it would be trivial for an attacker to brute force by repeating their attack and waiting for the random stack offset to be the desired one. The prng approach seems entirely proportional to this level of protection. Performance data are provided below. The baseline is v6.18 with rndstack on for each respective arch. (I)/(R) indicate statistically significant improvement/regression. arm64 platform is AWS Graviton3 (m7g.metal). x86_64 platform is AWS Sapphire Rapids (m7i.24xlarge): +-----------------+--------------+---------------+---------------+ | Benchmark | Result Class | per-cpu-prng | per-cpu-prng | | | | arm64 (metal) | x86_64 (VM) | +=================+==============+===============+===============+ | syscall/getpid | mean (ns) | (I) -9.50% | (I) -17.65% | | | p99 (ns) | (I) -59.24% | (I) -24.41% | | | p99.9 (ns) | (I) -59.52% | (I) -28.52% | +-----------------+--------------+---------------+---------------+ | syscall/getppid | mean (ns) | (I) -9.52% | (I) -19.24% | | | p99 (ns) | (I) -59.25% | (I) -25.03% | | | p99.9 (ns) | (I) -59.50% | (I) -28.17% | +-----------------+--------------+---------------+---------------+ | syscall/invalid | mean (ns) | (I) -10.31% | (I) -18.56% | | | p99 (ns) | (I) -60.79% | (I) -20.06% | | | p99.9 (ns) | (I) -61.04% | (I) -25.04% | +-----------------+--------------+---------------+---------------+ I tested an earlier version of this change on x86 bare metal and it showed a smaller but still significant improvement. The bare metal system wasn't available this time around so testing was done in a VM instance. I'm guessing the cost of rdtsc is higher for VMs. Acked-by: Mark Rutland <mark.rutland@arm.com> Signed-off-by: Ryan Roberts <ryan.roberts@arm.com> Link: https://patch.msgid.link/20260303150840.3789438-3-ryan.roberts@arm.com Signed-off-by: Kees Cook <kees@kernel.org> Signed-off-by: Mark Salter <msalter@redhat.com>
131 lines
3.6 KiB
C
131 lines
3.6 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* S390 version
|
|
* Copyright IBM Corp. 1999, 2000
|
|
* Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com),
|
|
* Thomas Spatzier (tspat@de.ibm.com)
|
|
*
|
|
* Derived from "arch/i386/kernel/sys_i386.c"
|
|
*
|
|
* This file contains various random system calls that
|
|
* have a non-standard calling sequence on the Linux/s390
|
|
* platform.
|
|
*/
|
|
|
|
#include <linux/errno.h>
|
|
#include <linux/nospec.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/smp.h>
|
|
#include <linux/sem.h>
|
|
#include <linux/msg.h>
|
|
#include <linux/shm.h>
|
|
#include <linux/stat.h>
|
|
#include <linux/syscalls.h>
|
|
#include <linux/mman.h>
|
|
#include <linux/file.h>
|
|
#include <linux/utsname.h>
|
|
#include <linux/personality.h>
|
|
#include <linux/unistd.h>
|
|
#include <linux/ipc.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/string.h>
|
|
#include <linux/thread_info.h>
|
|
#include <linux/entry-common.h>
|
|
|
|
#include <asm/ptrace.h>
|
|
#include <asm/vtime.h>
|
|
|
|
#include "entry.h"
|
|
|
|
#ifdef CONFIG_SYSVIPC
|
|
/*
|
|
* sys_ipc() is the de-multiplexer for the SysV IPC calls.
|
|
*/
|
|
SYSCALL_DEFINE5(s390_ipc, uint, call, int, first, unsigned long, second,
|
|
unsigned long, third, void __user *, ptr)
|
|
{
|
|
if (call >> 16)
|
|
return -EINVAL;
|
|
/* The s390 sys_ipc variant has only five parameters instead of six
|
|
* like the generic variant. The only difference is the handling of
|
|
* the SEMTIMEDOP subcall where on s390 the third parameter is used
|
|
* as a pointer to a struct timespec where the generic variant uses
|
|
* the fifth parameter.
|
|
* Therefore we can call the generic variant by simply passing the
|
|
* third parameter also as fifth parameter.
|
|
*/
|
|
return ksys_ipc(call, first, second, third, ptr, third);
|
|
}
|
|
#endif /* CONFIG_SYSVIPC */
|
|
|
|
SYSCALL_DEFINE1(s390_personality, unsigned int, personality)
|
|
{
|
|
unsigned int ret = current->personality;
|
|
|
|
if (personality(current->personality) == PER_LINUX32 &&
|
|
personality(personality) == PER_LINUX)
|
|
personality |= PER_LINUX32;
|
|
|
|
if (personality != 0xffffffff)
|
|
set_personality(personality);
|
|
|
|
if (personality(ret) == PER_LINUX32)
|
|
ret &= ~PER_LINUX32;
|
|
|
|
return ret;
|
|
}
|
|
|
|
SYSCALL_DEFINE0(ni_syscall)
|
|
{
|
|
return -ENOSYS;
|
|
}
|
|
|
|
void noinstr __do_syscall(struct pt_regs *regs, int per_trap)
|
|
{
|
|
unsigned long nr;
|
|
|
|
enter_from_user_mode(regs);
|
|
add_random_kstack_offset();
|
|
regs->psw = get_lowcore()->svc_old_psw;
|
|
regs->int_code = get_lowcore()->svc_int_code;
|
|
update_timer_sys();
|
|
if (static_branch_likely(&cpu_has_bear))
|
|
current->thread.last_break = regs->last_break;
|
|
local_irq_enable();
|
|
regs->orig_gpr2 = regs->gprs[2];
|
|
if (unlikely(per_trap))
|
|
set_thread_flag(TIF_PER_TRAP);
|
|
regs->flags = 0;
|
|
set_pt_regs_flag(regs, PIF_SYSCALL);
|
|
nr = regs->int_code & 0xffff;
|
|
if (likely(!nr)) {
|
|
nr = regs->gprs[1] & 0xffff;
|
|
regs->int_code &= ~0xffffUL;
|
|
regs->int_code |= nr;
|
|
}
|
|
regs->gprs[2] = nr;
|
|
if (nr == __NR_restart_syscall && !(current->restart_block.arch_data & 1)) {
|
|
regs->psw.addr = current->restart_block.arch_data;
|
|
current->restart_block.arch_data = 1;
|
|
}
|
|
nr = syscall_enter_from_user_mode_work(regs, nr);
|
|
/*
|
|
* In the s390 ptrace ABI, both the syscall number and the return value
|
|
* use gpr2. However, userspace puts the syscall number either in the
|
|
* svc instruction itself, or uses gpr1. To make at least skipping syscalls
|
|
* work, the ptrace code sets PIF_SYSCALL_RET_SET, which is checked here
|
|
* and if set, the syscall will be skipped.
|
|
*/
|
|
if (unlikely(test_and_clear_pt_regs_flag(regs, PIF_SYSCALL_RET_SET)))
|
|
goto out;
|
|
regs->gprs[2] = -ENOSYS;
|
|
if (likely(nr < NR_syscalls)) {
|
|
nr = array_index_nospec(nr, NR_syscalls);
|
|
regs->gprs[2] = current->thread.sys_call_table[nr](regs);
|
|
}
|
|
out:
|
|
syscall_exit_to_user_mode(regs);
|
|
}
|