---

Notes (jremus):
    Link: 
https://lore.kernel.org/all/[email protected]/
    
    The struct sframe_fre field ip_off must be u32, as the SFrame FRE start
    address (sfre_start_address) is unsigned 8-bit, 16-bit, or 32-bit:
    
https://sourceware.org/binutils/docs/sframe-spec.html#SFrame-Frame-Row-Entries
    
    When searching for a FRE of a FDE for an IP, the IP offset from function
    start address (ip_off = ip - (sec->sframe_start + fde->start_addr)) is
    always positive, as the search for a FDE for the same IP returned a
    FDE with:  sec->sframe_start + fde->start_addr <= ip
    This enables comparison against the unsigned FDE ip_off.
    
    This fixup includes a proposed fix from Josh (with minor modification
    due to duplicate macro names) to correctly perform sign extension when
    reading (un-)signed SFrame FDE/FRE fields:
    https://lore.kernel.org/all/20250207210614.nks6bxad4jhdulwg@jpoimboe/

 kernel/unwind/sframe.c | 38 +++++++++++++++++++++++++++-----------
 1 file changed, 27 insertions(+), 11 deletions(-)

diff --git a/kernel/unwind/sframe.c b/kernel/unwind/sframe.c
index 3f7cc9bc27eb..8804ac59edfa 100644
--- a/kernel/unwind/sframe.c
+++ b/kernel/unwind/sframe.c
@@ -19,7 +19,7 @@
 
 struct sframe_fre {
        unsigned int    size;
-       s32             ip_off;
+       u32             ip_off;
        s32             cfa_off;
        s32             ra_off;
        s32             fp_off;
@@ -129,33 +129,48 @@ static __always_inline int __find_fde(struct 
sframe_section *sec,
        return -EFAULT;
 }
 
-#define __UNSAFE_GET_USER_INC(to, from, type, label)                   \
+#define ____UNSAFE_GET_USER_INC(to, from, type, label)                 \
 ({                                                                     \
        type __to;                                                      \
        unsafe_get_user(__to, (type __user *)from, label);              \
        from += sizeof(__to);                                           \
-       to = (typeof(to))__to;                                                  
\
+       to = __to;                                                      \
 })
 
-#define UNSAFE_GET_USER_INC(to, from, size, label)                     \
+#define __UNSAFE_GET_USER_INC(to, from, size, label, u_or_s)           \
 ({                                                                     \
        switch (size) {                                                 \
        case 1:                                                         \
-               __UNSAFE_GET_USER_INC(to, from, u8, label);             \
+               ____UNSAFE_GET_USER_INC(to, from, u_or_s##8, label);    \
                break;                                                  \
        case 2:                                                         \
-               __UNSAFE_GET_USER_INC(to, from, u16, label);            \
+               ____UNSAFE_GET_USER_INC(to, from, u_or_s##16, label);   \
                break;                                                  \
        case 4:                                                         \
-               __UNSAFE_GET_USER_INC(to, from, u32, label);            \
+               ____UNSAFE_GET_USER_INC(to, from, u_or_s##32, label);   \
                break;                                                  \
        default:                                                        \
-               dbg_sec_uaccess("%d: bad UNSAFE_GET_USER_INC size %u\n",\
+               dbg_sec_uaccess("%d: bad unsafe_get_user() size %u\n",  \
                                __LINE__, size);                        \
                return -EFAULT;                                         \
        }                                                               \
 })
 
+#define UNSAFE_GET_USER_UNSIGNED_INC(to, from, size, label)            \
+       __UNSAFE_GET_USER_INC(to, from, size, label, u)
+
+#define UNSAFE_GET_USER_SIGNED_INC(to, from, size, label)              \
+       __UNSAFE_GET_USER_INC(to, from, size, label, s)
+
+#define UNSAFE_GET_USER_INC(to, from, size, label)                             
\
+       _Generic(to,                                                            
\
+                u8:    UNSAFE_GET_USER_UNSIGNED_INC(to, from, size, label),    
\
+                u16:   UNSAFE_GET_USER_UNSIGNED_INC(to, from, size, label),    
\
+                u32:   UNSAFE_GET_USER_UNSIGNED_INC(to, from, size, label),    
\
+                s8:    UNSAFE_GET_USER_SIGNED_INC(to, from, size, label),      
\
+                s16:   UNSAFE_GET_USER_SIGNED_INC(to, from, size, label),      
\
+                s32:   UNSAFE_GET_USER_SIGNED_INC(to, from, size, label))
+
 static __always_inline int __read_fre(struct sframe_section *sec,
                                      struct sframe_fde *fde,
                                      unsigned long fre_addr,
@@ -164,7 +179,8 @@ static __always_inline int __read_fre(struct sframe_section 
*sec,
        unsigned char fde_type = SFRAME_FUNC_FDE_TYPE(fde->info);
        unsigned char fre_type = SFRAME_FUNC_FRE_TYPE(fde->info);
        unsigned char offset_count, offset_size;
-       s32 ip_off, cfa_off, ra_off, fp_off;
+       u32 ip_off;
+       s32 cfa_off, ra_off, fp_off;
        unsigned long cur = fre_addr;
        unsigned char addr_size;
        u8 info;
@@ -248,9 +264,9 @@ static __always_inline int __find_fre(struct sframe_section 
*sec,
        unsigned long fre_addr;
        bool which = false;
        unsigned int i;
-       s32 ip_off;
+       u32 ip_off;
 
-       ip_off = (s32)(ip - sec->sframe_start) - fde->start_addr;
+       ip_off = ip - (sec->sframe_start + fde->start_addr);
 
        if (fde_type == SFRAME_FDE_TYPE_PCMASK)
                ip_off %= fde->rep_size;
-- 
2.45.2


Reply via email to