riscv: signal: Report signal frame size to userspace via auxv
authorVincent Chen <vincent.chen@sifive.com>
Mon, 5 Jun 2023 11:07:12 +0000 (11:07 +0000)
committerPalmer Dabbelt <palmer@rivosinc.com>
Thu, 8 Jun 2023 14:16:48 +0000 (07:16 -0700)
The vector register belongs to the signal context. They need to be stored
and restored as entering and leaving the signal handler. According to the
V-extension specification, the maximum length of the vector registers can
be 2^16. Hence, if userspace refers to the MINSIGSTKSZ to create a
sigframe, it may not be enough. To resolve this problem, this patch refers
to the commit 94b07c1f8c39c
("arm64: signal: Report signal frame size to userspace via auxv") to enable
userspace to know the minimum required sigframe size through the auxiliary
vector and use it to allocate enough memory for signal context.

Note that auxv always reports size of the sigframe as if V exists for
all starting processes, whenever the kernel has CONFIG_RISCV_ISA_V. The
reason is that users usually reference this value to allocate an
alternative signal stack, and the user may use V anytime. So the user
must reserve a space for V-context in sigframe in case that the signal
handler invokes after the kernel allocating V.

Signed-off-by: Greentime Hu <greentime.hu@sifive.com>
Signed-off-by: Vincent Chen <vincent.chen@sifive.com>
Signed-off-by: Andy Chiu <andy.chiu@sifive.com>
Acked-by: Conor Dooley <conor.dooley@microchip.com>
Reviewed-by: Björn Töpel <bjorn@rivosinc.com>
Reviewed-by: Guo Ren <guoren@kernel.org>
Reviewed-by: Heiko Stuebner <heiko.stuebner@vrull.eu>
Tested-by: Heiko Stuebner <heiko.stuebner@vrull.eu>
Link: https://lore.kernel.org/r/20230605110724.21391-16-andy.chiu@sifive.com
Signed-off-by: Palmer Dabbelt <palmer@rivosinc.com>
arch/riscv/include/asm/elf.h
arch/riscv/include/asm/processor.h
arch/riscv/include/uapi/asm/auxvec.h
arch/riscv/kernel/signal.c

index 30e7d24559602ec4eb4bcf9e2885b41bc92b0e46..ca23c4f6c440efef8477aa2b5afcc711118600d4 100644 (file)
@@ -105,6 +105,15 @@ do {                                                               \
                get_cache_size(3, CACHE_TYPE_UNIFIED));         \
        NEW_AUX_ENT(AT_L3_CACHEGEOMETRY,                        \
                get_cache_geometry(3, CACHE_TYPE_UNIFIED));     \
+       /*                                                       \
+        * Should always be nonzero unless there's a kernel bug. \
+        * If we haven't determined a sensible value to give to  \
+        * userspace, omit the entry:                            \
+        */                                                      \
+       if (likely(signal_minsigstksz))                          \
+               NEW_AUX_ENT(AT_MINSIGSTKSZ, signal_minsigstksz); \
+       else                                                     \
+               NEW_AUX_ENT(AT_IGNORE, 0);                       \
 } while (0)
 #define ARCH_HAS_SETUP_ADDITIONAL_PAGES
 struct linux_binprm;
index f0ddf691ac5ea3762415840f601636a0e1d7a25b..38ded8c5f207b725989b0b1faa79304010d33d65 100644 (file)
@@ -7,6 +7,7 @@
 #define _ASM_RISCV_PROCESSOR_H
 
 #include <linux/const.h>
+#include <linux/cache.h>
 
 #include <vdso/processor.h>
 
@@ -81,6 +82,7 @@ int riscv_of_parent_hartid(struct device_node *node, unsigned long *hartid);
 extern void riscv_fill_hwcap(void);
 extern int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src);
 
+extern unsigned long signal_minsigstksz __ro_after_init;
 #endif /* __ASSEMBLY__ */
 
 #endif /* _ASM_RISCV_PROCESSOR_H */
index fb187a33ce5897e7b671d7253324d5eadb00c5f3..10aaa83db89ef74a6441f5782698dc82d7e0ee5c 100644 (file)
@@ -35,5 +35,6 @@
 
 /* entries in ARCH_DLINFO */
 #define AT_VECTOR_SIZE_ARCH    9
+#define AT_MINSIGSTKSZ         51
 
 #endif /* _UAPI_ASM_RISCV_AUXVEC_H */
index c46f3dc039bbad62cf784dfd5f7acb55b7bb3806..f117641c1c4910f957343185db9ad913210dc20a 100644 (file)
@@ -23,6 +23,8 @@
 #include <asm/csr.h>
 #include <asm/cacheflush.h>
 
+unsigned long signal_minsigstksz __ro_after_init;
+
 extern u32 __user_rt_sigreturn[2];
 static size_t riscv_v_sc_size __ro_after_init;
 
@@ -197,7 +199,7 @@ static long restore_sigcontext(struct pt_regs *regs,
        return err;
 }
 
-static size_t get_rt_frame_size(void)
+static size_t get_rt_frame_size(bool cal_all)
 {
        struct rt_sigframe __user *frame;
        size_t frame_size;
@@ -205,8 +207,10 @@ static size_t get_rt_frame_size(void)
 
        frame_size = sizeof(*frame);
 
-       if (has_vector() && riscv_v_vstate_query(task_pt_regs(current)))
-               total_context_size += riscv_v_sc_size;
+       if (has_vector()) {
+               if (cal_all || riscv_v_vstate_query(task_pt_regs(current)))
+                       total_context_size += riscv_v_sc_size;
+       }
        /*
         * Preserved a __riscv_ctx_hdr for END signal context header if an
         * extension uses __riscv_extra_ext_header
@@ -226,7 +230,7 @@ SYSCALL_DEFINE0(rt_sigreturn)
        struct rt_sigframe __user *frame;
        struct task_struct *task;
        sigset_t set;
-       size_t frame_size = get_rt_frame_size();
+       size_t frame_size = get_rt_frame_size(false);
 
        /* Always make any pending restarted system calls return -EINTR */
        current->restart_block.fn = do_no_restart_syscall;
@@ -323,7 +327,7 @@ static int setup_rt_frame(struct ksignal *ksig, sigset_t *set,
        struct rt_sigframe __user *frame;
        long err = 0;
        unsigned long __maybe_unused addr;
-       size_t frame_size = get_rt_frame_size();
+       size_t frame_size = get_rt_frame_size(false);
 
        frame = get_sigframe(ksig, regs, frame_size);
        if (!access_ok(frame, frame_size))
@@ -465,4 +469,10 @@ void __init init_rt_signal_env(void)
 {
        riscv_v_sc_size = sizeof(struct __riscv_ctx_hdr) +
                          sizeof(struct __sc_riscv_v_state) + riscv_v_vsize;
+       /*
+        * Determine the stack space required for guaranteed signal delivery.
+        * The signal_minsigstksz will be populated into the AT_MINSIGSTKSZ entry
+        * in the auxiliary array at process startup.
+        */
+       signal_minsigstksz = get_rt_frame_size(true);
 }