Skip to content

Commit 94b07c1

Browse files
Dave Martinctmarinas
authored andcommitted
arm64: signal: Report signal frame size to userspace via auxv
Stateful CPU architecture extensions may require the signal frame to grow to a size that exceeds the arch's MINSIGSTKSZ #define. However, changing this #define is an ABI break. To allow userspace the option of determining the signal frame size in a more forwards-compatible way, this patch adds a new auxv entry tagged with AT_MINSIGSTKSZ, which provides the maximum signal frame size that the process can observe during its lifetime. If AT_MINSIGSTKSZ is absent from the aux vector, the caller can assume that the MINSIGSTKSZ #define is sufficient. This allows for a consistent interface with older kernels that do not provide AT_MINSIGSTKSZ. The idea is that libc could expose this via sysconf() or some similar mechanism. There is deliberately no AT_SIGSTKSZ. The kernel knows nothing about userspace's own stack overheads and should not pretend to know. For arm64: The primary motivation for this interface is the Scalable Vector Extension, which can require at least 4KB or so of extra space in the signal frame for the largest hardware implementations. To determine the correct value, a "Christmas tree" mode (via the add_all argument) is added to setup_sigframe_layout(), to simulate addition of all possible records to the signal frame at maximum possible size. If this procedure goes wrong somehow, resulting in a stupidly large frame layout and hence failure of sigframe_alloc() to allocate a record to the frame, then this is indicative of a kernel bug. In this case, we WARN() and no attempt is made to populate AT_MINSIGSTKSZ for userspace. For arm64 SVE: The SVE context block in the signal frame needs to be considered too when computing the maximum possible signal frame size. Because the size of this block depends on the vector length, this patch computes the size based not on the thread's current vector length but instead on the maximum possible vector length: this determines the maximum size of SVE context block that can be observed in any signal frame for the lifetime of the process. Signed-off-by: Dave Martin <[email protected]> Acked-by: Will Deacon <[email protected]> Cc: Ard Biesheuvel <[email protected]> Cc: Alex Bennée <[email protected]> Signed-off-by: Catalin Marinas <[email protected]>
1 parent 87c021a commit 94b07c1

File tree

5 files changed

+66
-8
lines changed

5 files changed

+66
-8
lines changed

arch/arm64/include/asm/elf.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,9 @@
121121

122122
#ifndef __ASSEMBLY__
123123

124+
#include <linux/bug.h>
125+
#include <asm/processor.h> /* for signal_minsigstksz, used by ARCH_DLINFO */
126+
124127
typedef unsigned long elf_greg_t;
125128

126129
#define ELF_NGREG (sizeof(struct user_pt_regs) / sizeof(elf_greg_t))
@@ -148,6 +151,16 @@ typedef struct user_fpsimd_state elf_fpregset_t;
148151
do { \
149152
NEW_AUX_ENT(AT_SYSINFO_EHDR, \
150153
(elf_addr_t)current->mm->context.vdso); \
154+
\
155+
/* \
156+
* Should always be nonzero unless there's a kernel bug. \
157+
* If we haven't determined a sensible value to give to \
158+
* userspace, omit the entry: \
159+
*/ \
160+
if (likely(signal_minsigstksz)) \
161+
NEW_AUX_ENT(AT_MINSIGSTKSZ, signal_minsigstksz); \
162+
else \
163+
NEW_AUX_ENT(AT_IGNORE, 0); \
151164
} while (0)
152165

153166
#define ARCH_HAS_SETUP_ADDITIONAL_PAGES

arch/arm64/include/asm/processor.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@
3535
#ifdef __KERNEL__
3636

3737
#include <linux/build_bug.h>
38+
#include <linux/cache.h>
39+
#include <linux/init.h>
3840
#include <linux/stddef.h>
3941
#include <linux/string.h>
4042

@@ -244,6 +246,9 @@ void cpu_enable_pan(const struct arm64_cpu_capabilities *__unused);
244246
void cpu_enable_cache_maint_trap(const struct arm64_cpu_capabilities *__unused);
245247
void cpu_clear_disr(const struct arm64_cpu_capabilities *__unused);
246248

249+
extern unsigned long __ro_after_init signal_minsigstksz; /* sigframe size */
250+
extern void __init minsigstksz_setup(void);
251+
247252
/* Userspace interface for PR_SVE_{SET,GET}_VL prctl()s: */
248253
#define SVE_SET_VL(arg) sve_set_current_vl(arg)
249254
#define SVE_GET_VL() sve_get_current_vl()

arch/arm64/include/uapi/asm/auxvec.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919

2020
/* vDSO location */
2121
#define AT_SYSINFO_EHDR 33
22+
#define AT_MINSIGSTKSZ 51 /* stack needed for signal delivery */
2223

23-
#define AT_VECTOR_SIZE_ARCH 1 /* entries in ARCH_DLINFO */
24+
#define AT_VECTOR_SIZE_ARCH 2 /* entries in ARCH_DLINFO */
2425

2526
#endif

arch/arm64/kernel/cpufeature.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1618,6 +1618,7 @@ void __init setup_cpu_features(void)
16181618
pr_info("emulated: Privileged Access Never (PAN) using TTBR0_EL1 switching\n");
16191619

16201620
sve_setup();
1621+
minsigstksz_setup();
16211622

16221623
/* Advertise that we have computed the system capabilities */
16231624
set_sys_caps_initialised();

arch/arm64/kernel/signal.c

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
* along with this program. If not, see <http://www.gnu.org/licenses/>.
1818
*/
1919

20+
#include <linux/cache.h>
2021
#include <linux/compat.h>
2122
#include <linux/errno.h>
2223
#include <linux/kernel.h>
@@ -570,8 +571,15 @@ asmlinkage long sys_rt_sigreturn(struct pt_regs *regs)
570571
return 0;
571572
}
572573

573-
/* Determine the layout of optional records in the signal frame */
574-
static int setup_sigframe_layout(struct rt_sigframe_user_layout *user)
574+
/*
575+
* Determine the layout of optional records in the signal frame
576+
*
577+
* add_all: if true, lays out the biggest possible signal frame for
578+
* this task; otherwise, generates a layout for the current state
579+
* of the task.
580+
*/
581+
static int setup_sigframe_layout(struct rt_sigframe_user_layout *user,
582+
bool add_all)
575583
{
576584
int err;
577585

@@ -581,7 +589,7 @@ static int setup_sigframe_layout(struct rt_sigframe_user_layout *user)
581589
return err;
582590

583591
/* fault information, if valid */
584-
if (current->thread.fault_code) {
592+
if (add_all || current->thread.fault_code) {
585593
err = sigframe_alloc(user, &user->esr_offset,
586594
sizeof(struct esr_context));
587595
if (err)
@@ -591,8 +599,14 @@ static int setup_sigframe_layout(struct rt_sigframe_user_layout *user)
591599
if (system_supports_sve()) {
592600
unsigned int vq = 0;
593601

594-
if (test_thread_flag(TIF_SVE))
595-
vq = sve_vq_from_vl(current->thread.sve_vl);
602+
if (add_all || test_thread_flag(TIF_SVE)) {
603+
int vl = sve_max_vl;
604+
605+
if (!add_all)
606+
vl = current->thread.sve_vl;
607+
608+
vq = sve_vq_from_vl(vl);
609+
}
596610

597611
err = sigframe_alloc(user, &user->sve_offset,
598612
SVE_SIG_CONTEXT_SIZE(vq));
@@ -603,7 +617,6 @@ static int setup_sigframe_layout(struct rt_sigframe_user_layout *user)
603617
return sigframe_alloc_end(user);
604618
}
605619

606-
607620
static int setup_sigframe(struct rt_sigframe_user_layout *user,
608621
struct pt_regs *regs, sigset_t *set)
609622
{
@@ -701,7 +714,7 @@ static int get_sigframe(struct rt_sigframe_user_layout *user,
701714
int err;
702715

703716
init_user_layout(user);
704-
err = setup_sigframe_layout(user);
717+
err = setup_sigframe_layout(user, false);
705718
if (err)
706719
return err;
707720

@@ -936,3 +949,28 @@ asmlinkage void do_notify_resume(struct pt_regs *regs,
936949
thread_flags = READ_ONCE(current_thread_info()->flags);
937950
} while (thread_flags & _TIF_WORK_MASK);
938951
}
952+
953+
unsigned long __ro_after_init signal_minsigstksz;
954+
955+
/*
956+
* Determine the stack space required for guaranteed signal devliery.
957+
* This function is used to populate AT_MINSIGSTKSZ at process startup.
958+
* cpufeatures setup is assumed to be complete.
959+
*/
960+
void __init minsigstksz_setup(void)
961+
{
962+
struct rt_sigframe_user_layout user;
963+
964+
init_user_layout(&user);
965+
966+
/*
967+
* If this fails, SIGFRAME_MAXSZ needs to be enlarged. It won't
968+
* be big enough, but it's our best guess:
969+
*/
970+
if (WARN_ON(setup_sigframe_layout(&user, true)))
971+
return;
972+
973+
signal_minsigstksz = sigframe_size(&user) +
974+
round_up(sizeof(struct frame_record), 16) +
975+
16; /* max alignment padding */
976+
}

0 commit comments

Comments
 (0)