diff --git a/arch/x86/include/asm/unwind_user.h b/arch/x86/include/asm/unwind_user.h
index ae46906c3b39..0650bcfae461 100644
--- a/arch/x86/include/asm/unwind_user.h
+++ b/arch/x86/include/asm/unwind_user.h
@@ -66,6 +66,7 @@ static inline int unwind_user_get_reg(unsigned long *val, unsigned int regnum)
.rule = UNWIND_USER_RULE_CFA_OFFSET_DEREF,\
.offset = -2*(ws), \
}, \
+ .sp_off = 0, \
.outermost = false,
#define ARCH_INIT_USER_FP_ENTRY_FRAME(ws) \
@@ -80,6 +81,7 @@ static inline int unwind_user_get_reg(unsigned long *val, unsigned int regnum)
.fp = { \
.rule = UNWIND_USER_RULE_RETAIN,\
}, \
+ .sp_off = 0, \
.outermost = false,
static inline bool unwind_user_at_function_start(struct pt_regs *regs)diff --git a/arch/x86/include/asm/unwind_user_sframe.h b/arch/x86/include/asm/unwind_user_sframe.h
index d828ae1a4aac..40b03b482d1a 100644
--- a/arch/x86/include/asm/unwind_user_sframe.h
+++ b/arch/x86/include/asm/unwind_user_sframe.h
@@ -9,4 +9,6 @@
#endif
+#include <asm-generic/unwind_user_sframe.h>
+
#endif /* _ASM_X86_UNWIND_USER_SFRAME_H */
diff --git a/include/asm-generic/Kbuild b/include/asm-generic/Kbuild
index 9aff61e7b8f2..91f86dd84677 100644
--- a/include/asm-generic/Kbuild
+++ b/include/asm-generic/Kbuild
@@ -61,6 +61,7 @@ mandatory-y += topology.h
mandatory-y += trace_clock.h
mandatory-y += uaccess.h
mandatory-y += unwind_user.h
+mandatory-y += unwind_user_sframe.h
mandatory-y += vermagic.h
mandatory-y += vga.h
mandatory-y += video.h
diff --git a/include/asm-generic/unwind_user_sframe.h b/include/asm-generic/unwind_user_sframe.h
new file mode 100644
index 000000000000..8c9ac47bc8bd
--- /dev/null
+++ b/include/asm-generic/unwind_user_sframe.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _ASM_GENERIC_UNWIND_USER_SFRAME_H
+#define _ASM_GENERIC_UNWIND_USER_SFRAME_H
+
+#include <linux/types.h>
+
+#ifndef SFRAME_SP_OFFSET
+/* Most archs/ABIs define CFA as SP at call site, so that SP = CFA + 0. */
+#define SFRAME_SP_OFFSET 0
+#endif
+
+#endif /* _ASM_GENERIC_UNWIND_USER_SFRAME_H */
diff --git a/include/linux/unwind_user_types.h b/include/linux/unwind_user_types.h
index 059e5c76f2f3..89c71a4553b2 100644
--- a/include/linux/unwind_user_types.h
+++ b/include/linux/unwind_user_types.h
@@ -65,6 +65,7 @@ struct unwind_user_frame {
struct unwind_user_cfa_rule_data cfa;
struct unwind_user_rule_data ra;
struct unwind_user_rule_data fp;
+ s32 sp_off;
bool outermost;
};
diff --git a/kernel/unwind/sframe.c b/kernel/unwind/sframe.c
index f24997e84e05..6a6221ce6d12 100644
--- a/kernel/unwind/sframe.c
+++ b/kernel/unwind/sframe.c
@@ -496,6 +496,7 @@ static __always_inline int __find_fre(struct sframe_section *sec,
return -EINVAL;
sframe_init_rule_data(&frame->ra, fre->ra_ctl, fre->ra_off);
sframe_init_rule_data(&frame->fp, fre->fp_ctl, fre->fp_off);
+ frame->sp_off = SFRAME_SP_OFFSET;
frame->outermost = SFRAME_V3_FRE_RA_UNDEFINED_P(fre->info);
return 0;
diff --git a/kernel/unwind/user.c b/kernel/unwind/user.c
index eb7d9489f671..e21d088f7543 100644
--- a/kernel/unwind/user.c
+++ b/kernel/unwind/user.c
@@ -30,7 +30,7 @@ get_user_word(unsigned long *word, unsigned long base, int off, unsigned int ws)
static int unwind_user_next_common(struct unwind_user_state *state,
const struct unwind_user_frame *frame)
{
- unsigned long cfa, fp, ra;
+ unsigned long cfa, sp, fp, ra;
/* Stop unwinding when reaching an outermost frame. */
if (frame->outermost) {@@ -62,16 +62,17 @@ static int unwind_user_next_common(struct unwind_user_state *state,
get_user_word(&cfa, cfa, 0, state->ws))
return -EINVAL;
+ /* Get the Stack Pointer (SP) */
+ sp = cfa + frame->sp_off;
/*
* 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)
+ if (sp <= state->sp - state->topmost)
return -EINVAL;
-
/* Make sure that the address is word aligned */
- if (cfa & (state->ws - 1))
+ if (sp & (state->ws - 1))
return -EINVAL;
/* Get the Return Address (RA) */
@@ -122,7 +123,7 @@ static int unwind_user_next_common(struct unwind_user_state *state,
return -EINVAL;
state->ip = ra;
- state->sp = cfa;
+ state->sp = sp;
state->fp = fp;
state->topmost = false;
return 0;
--
2.51.0