* [PATCH 0/2] Regset cleanups
@ 2022-10-21 22:18 Rick Edgecombe
2022-10-21 22:18 ` [PATCH 1/2] x86: Separate out x86_regset for 32 and 64 bit Rick Edgecombe
2022-10-21 22:18 ` [PATCH 2/2] x86: Improve formatting of user_regset arrays Rick Edgecombe
0 siblings, 2 replies; 5+ messages in thread
From: Rick Edgecombe @ 2022-10-21 22:18 UTC (permalink / raw)
To: x86; +Cc: linux-kernel, rick.p.edgecombe, keescook, ebiederm
These two cleanups came out of the shadow stack series[0]. Resending them
separately for earlier consideration. They were also posted as separate
cleanups once before[1]. Note, Eric Biederman had suggested to change the
pre-existing format of REGSET_FOO32 to REGSET32_FOO. This was missed in an
earlier revision, and the changes are now added in this version.
There should be no functional change. To verify this, testing included checking
for binary differences in ptrace.o by:
1. Have the config be a 64 bit kernel with CONFIG_IA32_EMULATION=y
2. Adjust stock v6.1-rc1 and these changes to have the same line numbers for
the bulk of the code in ptrace.c by adding blank lines.
3. Remove BUILD_BUG_ONs() from new changes.
4. strip and objdump both versions.
5. Compare and find disassembly was identical.
[0] https://lore.kernel.org/lkml/20220929222936.14584-1-rick.p.edgecombe@intel.com/
[1] https://lore.kernel.org/lkml/20220315201706.7576-1-rick.p.edgecombe@intel.com/
Rick Edgecombe (2):
x86: Separate out x86_regset for 32 and 64 bit
x86: Improve formatting of user_regset arrays
arch/x86/kernel/ptrace.c | 174 ++++++++++++++++++++++++---------------
1 file changed, 108 insertions(+), 66 deletions(-)
base-commit: 9abf2313adc1ca1b6180c508c25f22f9395cc780
--
2.17.1
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 1/2] x86: Separate out x86_regset for 32 and 64 bit
2022-10-21 22:18 [PATCH 0/2] Regset cleanups Rick Edgecombe
@ 2022-10-21 22:18 ` Rick Edgecombe
2022-10-21 23:55 ` Kees Cook
2022-10-21 22:18 ` [PATCH 2/2] x86: Improve formatting of user_regset arrays Rick Edgecombe
1 sibling, 1 reply; 5+ messages in thread
From: Rick Edgecombe @ 2022-10-21 22:18 UTC (permalink / raw)
To: x86; +Cc: linux-kernel, rick.p.edgecombe, keescook, ebiederm
In fill_thread_core_info() the ptrace accessible registers are collected
for a core file to be written out as notes. The note array is allocated
from a size calculated by iterating the user regset view, and counting the
regsets that have a non-zero core_note_type. However, this only allows for
there to be non-zero core_note_type at the end of the regset view. If
there are any in the middle, fill_thread_core_info() will overflow the
note allocation, as it iterates over the size of the view and the
allocation would be smaller than that.
To apparently avoid this problem, x86_32_regsets and x86_64_regsets need
to be constructed in a special way. They both draw their indices from a
shared enum x86_regset, but 32 bit and 64 bit don't all support the same
regsets and can be compiled in at the same time in the case of
IA32_EMULATION. So this enum has to be laid out in a special way such that
there are no gaps for both x86_32_regsets and x86_64_regsets. This
involves ordering them just right by creating aliases for enum’s that
are only in one view or the other, or creating multiple versions like
REGSET32_IOPERM/REGSET64_IOPERM.
So the collection of the registers tries to minimize the size of the
allocation, but it doesn’t quite work. Then the x86 ptrace side works
around it by constructing the enum just right to avoid a problem. In the
end there is no functional problem, but it is somewhat strange and
fragile.
It could also be improved like this [1], by better utilizing the smaller
array, but this still wastes space in the regset array’s if they are not
carefully crafted to avoid gaps. Instead, just fully separate out the
enums and give them separate 32 and 64 enum names. Add some bitsize-free
defines for REGSET_GENERAL and REGSET_FP since they are the only two
referred to in bitsize generic code.
While introducing a bunch of new 32/64 enums, change the pattern of the
name from REGSET_FOO32 to REGSET32_FOO to better indicate that the 32 is
in reference to the CPU mode and not the register size, as suggested by
Eric Biederman.
This should have no functional change and is only changing how constants
are generated and referred to.
[1] https://lore.kernel.org/lkml/20180717162502.32274-1-yu-cheng.yu@intel.com/
Signed-off-by: Rick Edgecombe <rick.p.edgecombe@intel.com>
---
arch/x86/kernel/ptrace.c | 67 ++++++++++++++++++++++++++--------------
1 file changed, 43 insertions(+), 24 deletions(-)
diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c
index 37c12fb92906..356495ab37a6 100644
--- a/arch/x86/kernel/ptrace.c
+++ b/arch/x86/kernel/ptrace.c
@@ -44,16 +44,35 @@
#include "tls.h"
-enum x86_regset {
- REGSET_GENERAL,
- REGSET_FP,
- REGSET_XFP,
- REGSET_IOPERM64 = REGSET_XFP,
- REGSET_XSTATE,
- REGSET_TLS,
- REGSET_IOPERM32,
+enum x86_regset_32 {
+ REGSET32_GENERAL,
+ REGSET32_FP,
+ REGSET32_XFP,
+ REGSET32_XSTATE,
+ REGSET32_TLS,
+ REGSET32_IOPERM,
};
+enum x86_regset_64 {
+ REGSET64_GENERAL,
+ REGSET64_FP,
+ REGSET64_IOPERM,
+ REGSET64_XSTATE,
+};
+
+#define REGSET_GENERAL \
+({ \
+ BUILD_BUG_ON((int)REGSET32_GENERAL != (int)REGSET64_GENERAL); \
+ REGSET32_GENERAL; \
+})
+
+#define REGSET_FP \
+({ \
+ BUILD_BUG_ON((int)REGSET32_FP != (int)REGSET64_FP); \
+ REGSET32_FP; \
+})
+
+
struct pt_regs_offset {
const char *name;
int offset;
@@ -788,13 +807,13 @@ long arch_ptrace(struct task_struct *child, long request,
#ifdef CONFIG_X86_32
case PTRACE_GETFPXREGS: /* Get the child extended FPU state. */
return copy_regset_to_user(child, &user_x86_32_view,
- REGSET_XFP,
+ REGSET32_XFP,
0, sizeof(struct user_fxsr_struct),
datap) ? -EIO : 0;
case PTRACE_SETFPXREGS: /* Set the child extended FPU state. */
return copy_regset_from_user(child, &user_x86_32_view,
- REGSET_XFP,
+ REGSET32_XFP,
0, sizeof(struct user_fxsr_struct),
datap) ? -EIO : 0;
#endif
@@ -1086,13 +1105,13 @@ static long ia32_arch_ptrace(struct task_struct *child, compat_long_t request,
case PTRACE_GETFPXREGS: /* Get the child extended FPU state. */
return copy_regset_to_user(child, &user_x86_32_view,
- REGSET_XFP, 0,
+ REGSET32_XFP, 0,
sizeof(struct user32_fxsr_struct),
datap);
case PTRACE_SETFPXREGS: /* Set the child extended FPU state. */
return copy_regset_from_user(child, &user_x86_32_view,
- REGSET_XFP, 0,
+ REGSET32_XFP, 0,
sizeof(struct user32_fxsr_struct),
datap);
@@ -1215,25 +1234,25 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
#ifdef CONFIG_X86_64
static struct user_regset x86_64_regsets[] __ro_after_init = {
- [REGSET_GENERAL] = {
+ [REGSET64_GENERAL] = {
.core_note_type = NT_PRSTATUS,
.n = sizeof(struct user_regs_struct) / sizeof(long),
.size = sizeof(long), .align = sizeof(long),
.regset_get = genregs_get, .set = genregs_set
},
- [REGSET_FP] = {
+ [REGSET64_FP] = {
.core_note_type = NT_PRFPREG,
.n = sizeof(struct fxregs_state) / sizeof(long),
.size = sizeof(long), .align = sizeof(long),
.active = regset_xregset_fpregs_active, .regset_get = xfpregs_get, .set = xfpregs_set
},
- [REGSET_XSTATE] = {
+ [REGSET64_XSTATE] = {
.core_note_type = NT_X86_XSTATE,
.size = sizeof(u64), .align = sizeof(u64),
.active = xstateregs_active, .regset_get = xstateregs_get,
.set = xstateregs_set
},
- [REGSET_IOPERM64] = {
+ [REGSET64_IOPERM] = {
.core_note_type = NT_386_IOPERM,
.n = IO_BITMAP_LONGS,
.size = sizeof(long), .align = sizeof(long),
@@ -1256,31 +1275,31 @@ static const struct user_regset_view user_x86_64_view = {
#if defined CONFIG_X86_32 || defined CONFIG_IA32_EMULATION
static struct user_regset x86_32_regsets[] __ro_after_init = {
- [REGSET_GENERAL] = {
+ [REGSET32_GENERAL] = {
.core_note_type = NT_PRSTATUS,
.n = sizeof(struct user_regs_struct32) / sizeof(u32),
.size = sizeof(u32), .align = sizeof(u32),
.regset_get = genregs32_get, .set = genregs32_set
},
- [REGSET_FP] = {
+ [REGSET32_FP] = {
.core_note_type = NT_PRFPREG,
.n = sizeof(struct user_i387_ia32_struct) / sizeof(u32),
.size = sizeof(u32), .align = sizeof(u32),
.active = regset_fpregs_active, .regset_get = fpregs_get, .set = fpregs_set
},
- [REGSET_XFP] = {
+ [REGSET32_XFP] = {
.core_note_type = NT_PRXFPREG,
.n = sizeof(struct fxregs_state) / sizeof(u32),
.size = sizeof(u32), .align = sizeof(u32),
.active = regset_xregset_fpregs_active, .regset_get = xfpregs_get, .set = xfpregs_set
},
- [REGSET_XSTATE] = {
+ [REGSET32_XSTATE] = {
.core_note_type = NT_X86_XSTATE,
.size = sizeof(u64), .align = sizeof(u64),
.active = xstateregs_active, .regset_get = xstateregs_get,
.set = xstateregs_set
},
- [REGSET_TLS] = {
+ [REGSET32_TLS] = {
.core_note_type = NT_386_TLS,
.n = GDT_ENTRY_TLS_ENTRIES, .bias = GDT_ENTRY_TLS_MIN,
.size = sizeof(struct user_desc),
@@ -1288,7 +1307,7 @@ static struct user_regset x86_32_regsets[] __ro_after_init = {
.active = regset_tls_active,
.regset_get = regset_tls_get, .set = regset_tls_set
},
- [REGSET_IOPERM32] = {
+ [REGSET32_IOPERM] = {
.core_note_type = NT_386_IOPERM,
.n = IO_BITMAP_BYTES / sizeof(u32),
.size = sizeof(u32), .align = sizeof(u32),
@@ -1311,10 +1330,10 @@ u64 xstate_fx_sw_bytes[USER_XSTATE_FX_SW_WORDS];
void __init update_regset_xstate_info(unsigned int size, u64 xstate_mask)
{
#ifdef CONFIG_X86_64
- x86_64_regsets[REGSET_XSTATE].n = size / sizeof(u64);
+ x86_64_regsets[REGSET64_XSTATE].n = size / sizeof(u64);
#endif
#if defined CONFIG_X86_32 || defined CONFIG_IA32_EMULATION
- x86_32_regsets[REGSET_XSTATE].n = size / sizeof(u64);
+ x86_32_regsets[REGSET32_XSTATE].n = size / sizeof(u64);
#endif
xstate_fx_sw_bytes[USER_XSTATE_XCR0_WORD] = xstate_mask;
}
--
2.17.1
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 2/2] x86: Improve formatting of user_regset arrays
2022-10-21 22:18 [PATCH 0/2] Regset cleanups Rick Edgecombe
2022-10-21 22:18 ` [PATCH 1/2] x86: Separate out x86_regset for 32 and 64 bit Rick Edgecombe
@ 2022-10-21 22:18 ` Rick Edgecombe
2022-10-21 23:56 ` Kees Cook
1 sibling, 1 reply; 5+ messages in thread
From: Rick Edgecombe @ 2022-10-21 22:18 UTC (permalink / raw)
To: x86; +Cc: linux-kernel, rick.p.edgecombe, keescook, ebiederm
Back in 2018, Ingo Molnar suggested[0] to improve the formatting of the
struct user_regset arrays. They have multiple member initializations per
line and some lines exceed 100 chars. Reformat them like he suggested.
[0] https://lore.kernel.org/lkml/20180711102035.GB8574@gmail.com/
Signed-off-by: Rick Edgecombe <rick.p.edgecombe@intel.com>
---
- Drop Kees' Reviewed-by, as I had to redo it with the new REGSET64_FOO
format.
arch/x86/kernel/ptrace.c | 107 ++++++++++++++++++++++++---------------
1 file changed, 65 insertions(+), 42 deletions(-)
diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c
index 356495ab37a6..dfaa270a7cc9 100644
--- a/arch/x86/kernel/ptrace.c
+++ b/arch/x86/kernel/ptrace.c
@@ -1235,28 +1235,37 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
static struct user_regset x86_64_regsets[] __ro_after_init = {
[REGSET64_GENERAL] = {
- .core_note_type = NT_PRSTATUS,
- .n = sizeof(struct user_regs_struct) / sizeof(long),
- .size = sizeof(long), .align = sizeof(long),
- .regset_get = genregs_get, .set = genregs_set
+ .core_note_type = NT_PRSTATUS,
+ .n = sizeof(struct user_regs_struct) / sizeof(long),
+ .size = sizeof(long),
+ .align = sizeof(long),
+ .regset_get = genregs_get,
+ .set = genregs_set
},
[REGSET64_FP] = {
- .core_note_type = NT_PRFPREG,
- .n = sizeof(struct fxregs_state) / sizeof(long),
- .size = sizeof(long), .align = sizeof(long),
- .active = regset_xregset_fpregs_active, .regset_get = xfpregs_get, .set = xfpregs_set
+ .core_note_type = NT_PRFPREG,
+ .n = sizeof(struct fxregs_state) / sizeof(long),
+ .size = sizeof(long),
+ .align = sizeof(long),
+ .active = regset_xregset_fpregs_active,
+ .regset_get = xfpregs_get,
+ .set = xfpregs_set
},
[REGSET64_XSTATE] = {
- .core_note_type = NT_X86_XSTATE,
- .size = sizeof(u64), .align = sizeof(u64),
- .active = xstateregs_active, .regset_get = xstateregs_get,
- .set = xstateregs_set
+ .core_note_type = NT_X86_XSTATE,
+ .size = sizeof(u64),
+ .align = sizeof(u64),
+ .active = xstateregs_active,
+ .regset_get = xstateregs_get,
+ .set = xstateregs_set
},
[REGSET64_IOPERM] = {
- .core_note_type = NT_386_IOPERM,
- .n = IO_BITMAP_LONGS,
- .size = sizeof(long), .align = sizeof(long),
- .active = ioperm_active, .regset_get = ioperm_get
+ .core_note_type = NT_386_IOPERM,
+ .n = IO_BITMAP_LONGS,
+ .size = sizeof(long),
+ .align = sizeof(long),
+ .active = ioperm_active,
+ .regset_get = ioperm_get
},
};
@@ -1276,42 +1285,56 @@ static const struct user_regset_view user_x86_64_view = {
#if defined CONFIG_X86_32 || defined CONFIG_IA32_EMULATION
static struct user_regset x86_32_regsets[] __ro_after_init = {
[REGSET32_GENERAL] = {
- .core_note_type = NT_PRSTATUS,
- .n = sizeof(struct user_regs_struct32) / sizeof(u32),
- .size = sizeof(u32), .align = sizeof(u32),
- .regset_get = genregs32_get, .set = genregs32_set
+ .core_note_type = NT_PRSTATUS,
+ .n = sizeof(struct user_regs_struct32) / sizeof(u32),
+ .size = sizeof(u32),
+ .align = sizeof(u32),
+ .regset_get = genregs32_get,
+ .set = genregs32_set
},
[REGSET32_FP] = {
- .core_note_type = NT_PRFPREG,
- .n = sizeof(struct user_i387_ia32_struct) / sizeof(u32),
- .size = sizeof(u32), .align = sizeof(u32),
- .active = regset_fpregs_active, .regset_get = fpregs_get, .set = fpregs_set
+ .core_note_type = NT_PRFPREG,
+ .n = sizeof(struct user_i387_ia32_struct) / sizeof(u32),
+ .size = sizeof(u32),
+ .align = sizeof(u32),
+ .active = regset_fpregs_active,
+ .regset_get = fpregs_get,
+ .set = fpregs_set
},
[REGSET32_XFP] = {
- .core_note_type = NT_PRXFPREG,
- .n = sizeof(struct fxregs_state) / sizeof(u32),
- .size = sizeof(u32), .align = sizeof(u32),
- .active = regset_xregset_fpregs_active, .regset_get = xfpregs_get, .set = xfpregs_set
+ .core_note_type = NT_PRXFPREG,
+ .n = sizeof(struct fxregs_state) / sizeof(u32),
+ .size = sizeof(u32),
+ .align = sizeof(u32),
+ .active = regset_xregset_fpregs_active,
+ .regset_get = xfpregs_get,
+ .set = xfpregs_set
},
[REGSET32_XSTATE] = {
- .core_note_type = NT_X86_XSTATE,
- .size = sizeof(u64), .align = sizeof(u64),
- .active = xstateregs_active, .regset_get = xstateregs_get,
- .set = xstateregs_set
+ .core_note_type = NT_X86_XSTATE,
+ .size = sizeof(u64),
+ .align = sizeof(u64),
+ .active = xstateregs_active,
+ .regset_get = xstateregs_get,
+ .set = xstateregs_set
},
[REGSET32_TLS] = {
- .core_note_type = NT_386_TLS,
- .n = GDT_ENTRY_TLS_ENTRIES, .bias = GDT_ENTRY_TLS_MIN,
- .size = sizeof(struct user_desc),
- .align = sizeof(struct user_desc),
- .active = regset_tls_active,
- .regset_get = regset_tls_get, .set = regset_tls_set
+ .core_note_type = NT_386_TLS,
+ .n = GDT_ENTRY_TLS_ENTRIES,
+ .bias = GDT_ENTRY_TLS_MIN,
+ .size = sizeof(struct user_desc),
+ .align = sizeof(struct user_desc),
+ .active = regset_tls_active,
+ .regset_get = regset_tls_get,
+ .set = regset_tls_set
},
[REGSET32_IOPERM] = {
- .core_note_type = NT_386_IOPERM,
- .n = IO_BITMAP_BYTES / sizeof(u32),
- .size = sizeof(u32), .align = sizeof(u32),
- .active = ioperm_active, .regset_get = ioperm_get
+ .core_note_type = NT_386_IOPERM,
+ .n = IO_BITMAP_BYTES / sizeof(u32),
+ .size = sizeof(u32),
+ .align = sizeof(u32),
+ .active = ioperm_active,
+ .regset_get = ioperm_get
},
};
--
2.17.1
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH 1/2] x86: Separate out x86_regset for 32 and 64 bit
2022-10-21 22:18 ` [PATCH 1/2] x86: Separate out x86_regset for 32 and 64 bit Rick Edgecombe
@ 2022-10-21 23:55 ` Kees Cook
0 siblings, 0 replies; 5+ messages in thread
From: Kees Cook @ 2022-10-21 23:55 UTC (permalink / raw)
To: Rick Edgecombe; +Cc: x86, linux-kernel, ebiederm
On Fri, Oct 21, 2022 at 03:18:02PM -0700, Rick Edgecombe wrote:
> In fill_thread_core_info() the ptrace accessible registers are collected
> for a core file to be written out as notes. The note array is allocated
> from a size calculated by iterating the user regset view, and counting the
> regsets that have a non-zero core_note_type. However, this only allows for
> there to be non-zero core_note_type at the end of the regset view. If
> there are any in the middle, fill_thread_core_info() will overflow the
> note allocation, as it iterates over the size of the view and the
> allocation would be smaller than that.
>
> To apparently avoid this problem, x86_32_regsets and x86_64_regsets need
> to be constructed in a special way. They both draw their indices from a
> shared enum x86_regset, but 32 bit and 64 bit don't all support the same
> regsets and can be compiled in at the same time in the case of
> IA32_EMULATION. So this enum has to be laid out in a special way such that
> there are no gaps for both x86_32_regsets and x86_64_regsets. This
> involves ordering them just right by creating aliases for enum’s that
> are only in one view or the other, or creating multiple versions like
> REGSET32_IOPERM/REGSET64_IOPERM.
>
> So the collection of the registers tries to minimize the size of the
> allocation, but it doesn’t quite work. Then the x86 ptrace side works
> around it by constructing the enum just right to avoid a problem. In the
> end there is no functional problem, but it is somewhat strange and
> fragile.
>
> It could also be improved like this [1], by better utilizing the smaller
> array, but this still wastes space in the regset array’s if they are not
> carefully crafted to avoid gaps. Instead, just fully separate out the
> enums and give them separate 32 and 64 enum names. Add some bitsize-free
> defines for REGSET_GENERAL and REGSET_FP since they are the only two
> referred to in bitsize generic code.
>
> While introducing a bunch of new 32/64 enums, change the pattern of the
> name from REGSET_FOO32 to REGSET32_FOO to better indicate that the 32 is
> in reference to the CPU mode and not the register size, as suggested by
> Eric Biederman.
>
> This should have no functional change and is only changing how constants
> are generated and referred to.
>
> [1] https://lore.kernel.org/lkml/20180717162502.32274-1-yu-cheng.yu@intel.com/
>
> Signed-off-by: Rick Edgecombe <rick.p.edgecombe@intel.com>
Reviewed-by: Kees Cook <keescook@chromium.org>
--
Kees Cook
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH 2/2] x86: Improve formatting of user_regset arrays
2022-10-21 22:18 ` [PATCH 2/2] x86: Improve formatting of user_regset arrays Rick Edgecombe
@ 2022-10-21 23:56 ` Kees Cook
0 siblings, 0 replies; 5+ messages in thread
From: Kees Cook @ 2022-10-21 23:56 UTC (permalink / raw)
To: Rick Edgecombe; +Cc: x86, linux-kernel, ebiederm
On Fri, Oct 21, 2022 at 03:18:03PM -0700, Rick Edgecombe wrote:
> Back in 2018, Ingo Molnar suggested[0] to improve the formatting of the
> struct user_regset arrays. They have multiple member initializations per
> line and some lines exceed 100 chars. Reformat them like he suggested.
>
> [0] https://lore.kernel.org/lkml/20180711102035.GB8574@gmail.com/
>
> Signed-off-by: Rick Edgecombe <rick.p.edgecombe@intel.com>
Reviewed-by: Kees Cook <keescook@chromium.org>
--
Kees Cook
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2022-10-21 23:56 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2022-10-21 22:18 [PATCH 0/2] Regset cleanups Rick Edgecombe
2022-10-21 22:18 ` [PATCH 1/2] x86: Separate out x86_regset for 32 and 64 bit Rick Edgecombe
2022-10-21 23:55 ` Kees Cook
2022-10-21 22:18 ` [PATCH 2/2] x86: Improve formatting of user_regset arrays Rick Edgecombe
2022-10-21 23:56 ` Kees Cook
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox