aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDave Martin <Dave.Martin@arm.com>2018-06-01 06:10:14 -0400
committerCatalin Marinas <catalin.marinas@arm.com>2018-06-01 10:53:10 -0400
commit94b07c1f8c39c6d839df35fa28ffd1785d385897 (patch)
treea6be3a7a92faed1df66c32a7845d008081dfcd07
parent87c021a8143b6133e0085a8162f2a127462b54a3 (diff)
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 <Dave.Martin@arm.com> Acked-by: Will Deacon <will.deacon@arm.com> Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org> Cc: Alex Bennée <alex.bennee@linaro.org> Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
-rw-r--r--arch/arm64/include/asm/elf.h13
-rw-r--r--arch/arm64/include/asm/processor.h5
-rw-r--r--arch/arm64/include/uapi/asm/auxvec.h3
-rw-r--r--arch/arm64/kernel/cpufeature.c1
-rw-r--r--arch/arm64/kernel/signal.c52
5 files changed, 66 insertions, 8 deletions
diff --git a/arch/arm64/include/asm/elf.h b/arch/arm64/include/asm/elf.h
index fac1c4de7898..433b9554c6a1 100644
--- a/arch/arm64/include/asm/elf.h
+++ b/arch/arm64/include/asm/elf.h
@@ -121,6 +121,9 @@
121 121
122#ifndef __ASSEMBLY__ 122#ifndef __ASSEMBLY__
123 123
124#include <linux/bug.h>
125#include <asm/processor.h> /* for signal_minsigstksz, used by ARCH_DLINFO */
126
124typedef unsigned long elf_greg_t; 127typedef unsigned long elf_greg_t;
125 128
126#define ELF_NGREG (sizeof(struct user_pt_regs) / sizeof(elf_greg_t)) 129#define ELF_NGREG (sizeof(struct user_pt_regs) / sizeof(elf_greg_t))
@@ -148,6 +151,16 @@ typedef struct user_fpsimd_state elf_fpregset_t;
148do { \ 151do { \
149 NEW_AUX_ENT(AT_SYSINFO_EHDR, \ 152 NEW_AUX_ENT(AT_SYSINFO_EHDR, \
150 (elf_addr_t)current->mm->context.vdso); \ 153 (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); \
151} while (0) 164} while (0)
152 165
153#define ARCH_HAS_SETUP_ADDITIONAL_PAGES 166#define ARCH_HAS_SETUP_ADDITIONAL_PAGES
diff --git a/arch/arm64/include/asm/processor.h b/arch/arm64/include/asm/processor.h
index 767598932549..65ab83e8926e 100644
--- a/arch/arm64/include/asm/processor.h
+++ b/arch/arm64/include/asm/processor.h
@@ -35,6 +35,8 @@
35#ifdef __KERNEL__ 35#ifdef __KERNEL__
36 36
37#include <linux/build_bug.h> 37#include <linux/build_bug.h>
38#include <linux/cache.h>
39#include <linux/init.h>
38#include <linux/stddef.h> 40#include <linux/stddef.h>
39#include <linux/string.h> 41#include <linux/string.h>
40 42
@@ -244,6 +246,9 @@ void cpu_enable_pan(const struct arm64_cpu_capabilities *__unused);
244void cpu_enable_cache_maint_trap(const struct arm64_cpu_capabilities *__unused); 246void cpu_enable_cache_maint_trap(const struct arm64_cpu_capabilities *__unused);
245void cpu_clear_disr(const struct arm64_cpu_capabilities *__unused); 247void cpu_clear_disr(const struct arm64_cpu_capabilities *__unused);
246 248
249extern unsigned long __ro_after_init signal_minsigstksz; /* sigframe size */
250extern void __init minsigstksz_setup(void);
251
247/* Userspace interface for PR_SVE_{SET,GET}_VL prctl()s: */ 252/* Userspace interface for PR_SVE_{SET,GET}_VL prctl()s: */
248#define SVE_SET_VL(arg) sve_set_current_vl(arg) 253#define SVE_SET_VL(arg) sve_set_current_vl(arg)
249#define SVE_GET_VL() sve_get_current_vl() 254#define SVE_GET_VL() sve_get_current_vl()
diff --git a/arch/arm64/include/uapi/asm/auxvec.h b/arch/arm64/include/uapi/asm/auxvec.h
index ec0a86d484e1..743c0b84fd30 100644
--- a/arch/arm64/include/uapi/asm/auxvec.h
+++ b/arch/arm64/include/uapi/asm/auxvec.h
@@ -19,7 +19,8 @@
19 19
20/* vDSO location */ 20/* vDSO location */
21#define AT_SYSINFO_EHDR 33 21#define AT_SYSINFO_EHDR 33
22#define AT_MINSIGSTKSZ 51 /* stack needed for signal delivery */
22 23
23#define AT_VECTOR_SIZE_ARCH 1 /* entries in ARCH_DLINFO */ 24#define AT_VECTOR_SIZE_ARCH 2 /* entries in ARCH_DLINFO */
24 25
25#endif 26#endif
diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
index fbee8c17a4e6..d2856b129097 100644
--- a/arch/arm64/kernel/cpufeature.c
+++ b/arch/arm64/kernel/cpufeature.c
@@ -1618,6 +1618,7 @@ void __init setup_cpu_features(void)
1618 pr_info("emulated: Privileged Access Never (PAN) using TTBR0_EL1 switching\n"); 1618 pr_info("emulated: Privileged Access Never (PAN) using TTBR0_EL1 switching\n");
1619 1619
1620 sve_setup(); 1620 sve_setup();
1621 minsigstksz_setup();
1621 1622
1622 /* Advertise that we have computed the system capabilities */ 1623 /* Advertise that we have computed the system capabilities */
1623 set_sys_caps_initialised(); 1624 set_sys_caps_initialised();
diff --git a/arch/arm64/kernel/signal.c b/arch/arm64/kernel/signal.c
index 154b7d30145d..e7da5dba7ba8 100644
--- a/arch/arm64/kernel/signal.c
+++ b/arch/arm64/kernel/signal.c
@@ -17,6 +17,7 @@
17 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */ 18 */
19 19
20#include <linux/cache.h>
20#include <linux/compat.h> 21#include <linux/compat.h>
21#include <linux/errno.h> 22#include <linux/errno.h>
22#include <linux/kernel.h> 23#include <linux/kernel.h>
@@ -570,8 +571,15 @@ badframe:
570 return 0; 571 return 0;
571} 572}
572 573
573/* Determine the layout of optional records in the signal frame */ 574/*
574static int setup_sigframe_layout(struct rt_sigframe_user_layout *user) 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 */
581static int setup_sigframe_layout(struct rt_sigframe_user_layout *user,
582 bool add_all)
575{ 583{
576 int err; 584 int err;
577 585
@@ -581,7 +589,7 @@ static int setup_sigframe_layout(struct rt_sigframe_user_layout *user)
581 return err; 589 return err;
582 590
583 /* fault information, if valid */ 591 /* fault information, if valid */
584 if (current->thread.fault_code) { 592 if (add_all || current->thread.fault_code) {
585 err = sigframe_alloc(user, &user->esr_offset, 593 err = sigframe_alloc(user, &user->esr_offset,
586 sizeof(struct esr_context)); 594 sizeof(struct esr_context));
587 if (err) 595 if (err)
@@ -591,8 +599,14 @@ static int setup_sigframe_layout(struct rt_sigframe_user_layout *user)
591 if (system_supports_sve()) { 599 if (system_supports_sve()) {
592 unsigned int vq = 0; 600 unsigned int vq = 0;
593 601
594 if (test_thread_flag(TIF_SVE)) 602 if (add_all || test_thread_flag(TIF_SVE)) {
595 vq = sve_vq_from_vl(current->thread.sve_vl); 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 }
596 610
597 err = sigframe_alloc(user, &user->sve_offset, 611 err = sigframe_alloc(user, &user->sve_offset,
598 SVE_SIG_CONTEXT_SIZE(vq)); 612 SVE_SIG_CONTEXT_SIZE(vq));
@@ -603,7 +617,6 @@ static int setup_sigframe_layout(struct rt_sigframe_user_layout *user)
603 return sigframe_alloc_end(user); 617 return sigframe_alloc_end(user);
604} 618}
605 619
606
607static int setup_sigframe(struct rt_sigframe_user_layout *user, 620static int setup_sigframe(struct rt_sigframe_user_layout *user,
608 struct pt_regs *regs, sigset_t *set) 621 struct pt_regs *regs, sigset_t *set)
609{ 622{
@@ -701,7 +714,7 @@ static int get_sigframe(struct rt_sigframe_user_layout *user,
701 int err; 714 int err;
702 715
703 init_user_layout(user); 716 init_user_layout(user);
704 err = setup_sigframe_layout(user); 717 err = setup_sigframe_layout(user, false);
705 if (err) 718 if (err)
706 return err; 719 return err;
707 720
@@ -936,3 +949,28 @@ asmlinkage void do_notify_resume(struct pt_regs *regs,
936 thread_flags = READ_ONCE(current_thread_info()->flags); 949 thread_flags = READ_ONCE(current_thread_info()->flags);
937 } while (thread_flags & _TIF_WORK_MASK); 950 } while (thread_flags & _TIF_WORK_MASK);
938} 951}
952
953unsigned 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 */
960void __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}