[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <20250528102655.1455423-1-jremus@linux.ibm.com>
Date: Wed, 28 May 2025 12:26:55 +0200
From: Jens Remus <jremus@...ux.ibm.com>
To: rostedt@...dmis.org
Cc: aahringo@...hat.com, acme@...nel.org, adrian.hunter@...el.com,
akpm@...ux-foundation.org, alexander.shishkin@...ux.intel.com,
andrii.nakryiko@...il.com, beaub@...ux.microsoft.com,
blakejones@...gle.com, broonie@...nel.org, fweimer@...hat.com,
indu.bhagat@...cle.com, irogers@...gle.com, jemarch@....org,
jolsa@...nel.org, jordalgo@...a.com, jpoimboe@...nel.org,
jremus@...ux.ibm.com, linux-kernel@...r.kernel.org,
linux-perf-users@...r.kernel.org, linux-toolchains@...r.kernel.org,
linux-trace-kernel@...r.kernel.org, luto@...nel.org,
mark.rutland@....com, mathieu.desnoyers@...icios.com,
mhiramat@...nel.org, mingo@...nel.org, namhyung@...nel.org,
peterz@...radead.org, sam@...too.org, wnliu@...gle.com, x86@...nel.org,
hca@...ux.ibm.com
Subject: [PATCH] fixup! unwind_user/sframe: Add support for reading .sframe contents
---
Notes (jremus):
Link: https://lore.kernel.org/all/b35ca3a3-8de5-4d32-8d30-d4e562f6b0de@linux.ibm.com/
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
Powered by blists - more mailing lists