Not all architectures have the return address (RA) in user space saved
on the stack on function entry, such as x86-64 does due to its CALL
instruction pushing the RA onto the stack.  Architectures/ABIs, such as
s390, also do not necessarily enforce to save the RA in user space on
the stack in the function prologue or even at all, for instance in leaf
functions.

Treat a RA offset from CFA of zero as indication that the RA is not
saved (on the stack).  For the topmost frame treat it as indication that
the RA is in the link/RA register, such as on arm64 and s390, and obtain
it from there.  For non-topmost frames treat it as error, as the RA must
be saved.

Additionally allow the SP to be unchanged in the topmost frame, for
architectures where SP at function entry == SP at call site, such as
arm64 and s390.

Note that treating a RA offset from CFA of zero as indication that
the RA is not saved on the stack additionally allows for architectures,
such as s390, where the frame pointer (FP) may be saved without the RA
being saved as well.  Provided that such architectures represent this
in SFrame by encoding the "missing" RA offset using a padding RA offset
with a value of zero.

Cc: Steven Rostedt <[email protected]>
Cc: Josh Poimboeuf <[email protected]>
Cc: Masami Hiramatsu <[email protected]>
Cc: Mathieu Desnoyers <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Ingo Molnar <[email protected]>
Cc: Jiri Olsa <[email protected]>
Cc: Arnaldo Carvalho de Melo <[email protected]>
Cc: Namhyung Kim <[email protected]>
Cc: Thomas Gleixner <[email protected]>
Cc: Andrii Nakryiko <[email protected]>
Cc: Indu Bhagat <[email protected]>
Cc: "Jose E. Marchesi" <[email protected]>
Cc: Beau Belgrave <[email protected]>
Cc: Jens Remus <[email protected]>
Cc: Linus Torvalds <[email protected]>
Cc: Andrew Morton <[email protected]>
Cc: Florian Weimer <[email protected]>
Cc: Sam James <[email protected]>
Cc: Kees Cook <[email protected]>
Cc: "Carlos O'Donell" <[email protected]>
Signed-off-by: Jens Remus <[email protected]>
---

Notes (jremus):
    Changes in v13:
    - New patch.  Based on my s390 sframe support series patch
      "unwind_user: Enable archs that pass RA in a register":
      
https://lore.kernel.org/all/[email protected]/

 include/linux/unwind_user.h |  9 +++++++++
 kernel/unwind/sframe.c      |  6 ++----
 kernel/unwind/user.c        | 17 +++++++++++++----
 3 files changed, 24 insertions(+), 8 deletions(-)

diff --git a/include/linux/unwind_user.h b/include/linux/unwind_user.h
index 64618618febd..bc2edae39955 100644
--- a/include/linux/unwind_user.h
+++ b/include/linux/unwind_user.h
@@ -23,6 +23,15 @@ static inline bool unwind_user_at_function_start(struct 
pt_regs *regs)
 #define unwind_user_at_function_start unwind_user_at_function_start
 #endif
 
+#ifndef unwind_user_get_ra_reg
+static inline int unwind_user_get_ra_reg(unsigned long *val)
+{
+       WARN_ON_ONCE(1);
+       return -EINVAL;
+}
+#define unwind_user_get_ra_reg unwind_user_get_ra_reg
+#endif
+
 int unwind_user(struct unwind_stacktrace *trace, unsigned int max_entries);
 
 #endif /* _LINUX_UNWIND_USER_H */
diff --git a/kernel/unwind/sframe.c b/kernel/unwind/sframe.c
index cf353bdbc907..fc905504ddde 100644
--- a/kernel/unwind/sframe.c
+++ b/kernel/unwind/sframe.c
@@ -243,10 +243,8 @@ static __always_inline int __read_fre(struct 
sframe_section *sec,
        dataword_count--;
 
        ra_off = sec->ra_off;
-       if (!ra_off) {
-               if (!dataword_count--)
-                       return -EFAULT;
-
+       if (!ra_off && dataword_count) {
+               dataword_count--;
                UNSAFE_GET_USER_INC(ra_off, cur, dataword_size, Efault);
        }
 
diff --git a/kernel/unwind/user.c b/kernel/unwind/user.c
index fdb1001e3750..9ceef9b2b8db 100644
--- a/kernel/unwind/user.c
+++ b/kernel/unwind/user.c
@@ -48,8 +48,12 @@ static int unwind_user_next_common(struct unwind_user_state 
*state,
        }
        cfa += frame->cfa_off;
 
-       /* Make sure that stack is not going in wrong direction */
-       if (cfa <= state->sp)
+       /*
+        * Make sure that stack is not going in wrong direction.  Allow SP
+        * to be unchanged for the topmost frame, by subtracting topmost,
+        * which is either 0 or 1.
+        */
+       if (cfa <= state->sp - state->topmost)
                return -EINVAL;
 
        /* Make sure that the address is word aligned */
@@ -57,8 +61,13 @@ static int unwind_user_next_common(struct unwind_user_state 
*state,
                return -EINVAL;
 
        /* Get the Return Address (RA) */
-       if (get_user_word(&ra, cfa, frame->ra_off, state->ws))
-               return -EINVAL;
+       if (frame->ra_off) {
+               if (get_user_word(&ra, cfa, frame->ra_off, state->ws))
+                       return -EINVAL;
+       } else {
+               if (!state->topmost || unwind_user_get_ra_reg(&ra))
+                       return -EINVAL;
+       }
 
        /* Get the Frame Pointer (FP) */
        if (frame->fp_off && get_user_word(&fp, cfa, frame->fp_off, state->ws))
-- 
2.51.0


Reply via email to