SFrame may represent an undefined return address (RA) as SFrame FRE
without any offsets as indication for an outermost frame.

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:
    - Use SFRAME_V3_FRE_RA_UNDEFINED_P() instead of struct
      sframe_fre_internal.ra_undefined field.
    - Reduce indentation of assignments.

 kernel/unwind/sframe.c | 15 ++++++++++++++-
 kernel/unwind/sframe.h |  1 +
 2 files changed, 15 insertions(+), 1 deletion(-)

diff --git a/kernel/unwind/sframe.c b/kernel/unwind/sframe.c
index 9b3779924395..6004b43aac34 100644
--- a/kernel/unwind/sframe.c
+++ b/kernel/unwind/sframe.c
@@ -218,7 +218,7 @@ static __always_inline int __read_fre(struct sframe_section 
*sec,
        UNSAFE_GET_USER_INC(info, cur, 1, Efault);
        dataword_count = SFRAME_V3_FRE_DATAWORD_COUNT(info);
        dataword_size  = 
dataword_size_enum_to_size(SFRAME_V3_FRE_DATAWORD_SIZE(info));
-       if (!dataword_count || !dataword_size)
+       if (!dataword_size)
                return -EFAULT;
 
        if (cur + (dataword_count * dataword_size) > sec->fres_end)
@@ -228,6 +228,17 @@ static __always_inline int __read_fre(struct 
sframe_section *sec,
        if (fde_type != SFRAME_FDE_TYPE_REGULAR)
                return -EFAULT;
 
+       if (!dataword_count) {
+               /*
+                * A FRE without data words indicates RA undefined /
+                * outermost frame.
+                */
+               cfa_off = 0;
+               ra_off  = 0;
+               fp_off  = 0;
+               goto done;
+       }
+
        UNSAFE_GET_USER_INC(cfa_off, cur, dataword_size, Efault);
        dataword_count--;
 
@@ -248,6 +259,7 @@ static __always_inline int __read_fre(struct sframe_section 
*sec,
        if (dataword_count)
                return -EFAULT;
 
+done:
        fre->size       = addr_size + 1 + (dataword_count * dataword_size);
        fre->ip_off     = ip_off;
        fre->cfa_off    = cfa_off;
@@ -314,6 +326,7 @@ static __always_inline int __find_fre(struct sframe_section 
*sec,
        frame->ra_off  = fre->ra_off;
        frame->fp_off  = fre->fp_off;
        frame->use_fp  = SFRAME_V3_FRE_CFA_BASE_REG_ID(fre->info) == 
SFRAME_BASE_REG_FP;
+       frame->outermost = SFRAME_V3_FRE_RA_UNDEFINED_P(fre->info);
 
        return 0;
 }
diff --git a/kernel/unwind/sframe.h b/kernel/unwind/sframe.h
index 07a2e99855f9..3fcc15534e5a 100644
--- a/kernel/unwind/sframe.h
+++ b/kernel/unwind/sframe.h
@@ -77,5 +77,6 @@ struct sframe_fda_v3 {
 #define SFRAME_V3_FRE_DATAWORD_COUNT(info)             (((info) >> 1) & 0xf)
 #define SFRAME_V3_FRE_DATAWORD_SIZE(info)              (((info) >> 5) & 0x3)
 #define SFRAME_V3_AARCH64_FRE_MANGLED_RA_P(info)       (((info) >> 7) & 0x1)
+#define SFRAME_V3_FRE_RA_UNDEFINED_P(info)             
(SFRAME_V3_FRE_DATAWORD_COUNT(info) == 0)
 
 #endif /* _SFRAME_H */
-- 
2.51.0


Reply via email to