From: Greg Kroah-Hartman Date: Mon, 13 Jun 2022 07:56:41 +0000 (+0200) Subject: 5.4-stable patches X-Git-Tag: v4.9.318~42 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=a0a5679950e130dbb8ef396971f690f8184a94d9;p=thirdparty%2Fkernel%2Fstable-queue.git 5.4-stable patches added patches: powerpc-32-fix-overread-overwrite-of-thread_struct-via-ptrace.patch --- diff --git a/queue-5.4/powerpc-32-fix-overread-overwrite-of-thread_struct-via-ptrace.patch b/queue-5.4/powerpc-32-fix-overread-overwrite-of-thread_struct-via-ptrace.patch new file mode 100644 index 00000000000..e02da794e3e --- /dev/null +++ b/queue-5.4/powerpc-32-fix-overread-overwrite-of-thread_struct-via-ptrace.patch @@ -0,0 +1,114 @@ +From 8e1278444446fc97778a5e5c99bca1ce0bbc5ec9 Mon Sep 17 00:00:00 2001 +From: Michael Ellerman +Date: Tue, 7 Jun 2022 00:34:56 +1000 +Subject: powerpc/32: Fix overread/overwrite of thread_struct via ptrace + +From: Michael Ellerman + +commit 8e1278444446fc97778a5e5c99bca1ce0bbc5ec9 upstream. + +The ptrace PEEKUSR/POKEUSR (aka PEEKUSER/POKEUSER) API allows a process +to read/write registers of another process. + +To get/set a register, the API takes an index into an imaginary address +space called the "USER area", where the registers of the process are +laid out in some fashion. + +The kernel then maps that index to a particular register in its own data +structures and gets/sets the value. + +The API only allows a single machine-word to be read/written at a time. +So 4 bytes on 32-bit kernels and 8 bytes on 64-bit kernels. + +The way floating point registers (FPRs) are addressed is somewhat +complicated, because double precision float values are 64-bit even on +32-bit CPUs. That means on 32-bit kernels each FPR occupies two +word-sized locations in the USER area. On 64-bit kernels each FPR +occupies one word-sized location in the USER area. + +Internally the kernel stores the FPRs in an array of u64s, or if VSX is +enabled, an array of pairs of u64s where one half of each pair stores +the FPR. Which half of the pair stores the FPR depends on the kernel's +endianness. + +To handle the different layouts of the FPRs depending on VSX/no-VSX and +big/little endian, the TS_FPR() macro was introduced. + +Unfortunately the TS_FPR() macro does not take into account the fact +that the addressing of each FPR differs between 32-bit and 64-bit +kernels. It just takes the index into the "USER area" passed from +userspace and indexes into the fp_state.fpr array. + +On 32-bit there are 64 indexes that address FPRs, but only 32 entries in +the fp_state.fpr array, meaning the user can read/write 256 bytes past +the end of the array. Because the fp_state sits in the middle of the +thread_struct there are various fields than can be overwritten, +including some pointers. As such it may be exploitable. + +It has also been observed to cause systems to hang or otherwise +misbehave when using gdbserver, and is probably the root cause of this +report which could not be easily reproduced: + https://lore.kernel.org/linuxppc-dev/dc38afe9-6b78-f3f5-666b-986939e40fc6@keymile.com/ + +Rather than trying to make the TS_FPR() macro even more complicated to +fix the bug, or add more macros, instead add a special-case for 32-bit +kernels. This is more obvious and hopefully avoids a similar bug +happening again in future. + +Note that because 32-bit kernels never have VSX enabled the code doesn't +need to consider TS_FPRWIDTH/OFFSET at all. Add a BUILD_BUG_ON() to +ensure that 32-bit && VSX is never enabled. + +Fixes: 87fec0514f61 ("powerpc: PTRACE_PEEKUSR/PTRACE_POKEUSER of FPR registers in little endian builds") +Cc: stable@vger.kernel.org # v3.13+ +Reported-by: Ariel Miculas +Tested-by: Christophe Leroy +Signed-off-by: Michael Ellerman +Link: https://lore.kernel.org/r/20220609133245.573565-1-mpe@ellerman.id.au +Signed-off-by: Greg Kroah-Hartman +--- + arch/powerpc/kernel/ptrace.c | 21 +++++++++++++++++---- + 1 file changed, 17 insertions(+), 4 deletions(-) + +--- a/arch/powerpc/kernel/ptrace.c ++++ b/arch/powerpc/kernel/ptrace.c +@@ -3014,8 +3014,13 @@ long arch_ptrace(struct task_struct *chi + + flush_fp_to_thread(child); + if (fpidx < (PT_FPSCR - PT_FPR0)) +- memcpy(&tmp, &child->thread.TS_FPR(fpidx), +- sizeof(long)); ++ if (IS_ENABLED(CONFIG_PPC32)) { ++ // On 32-bit the index we are passed refers to 32-bit words ++ tmp = ((u32 *)child->thread.fp_state.fpr)[fpidx]; ++ } else { ++ memcpy(&tmp, &child->thread.TS_FPR(fpidx), ++ sizeof(long)); ++ } + else + tmp = child->thread.fp_state.fpscr; + } +@@ -3047,8 +3052,13 @@ long arch_ptrace(struct task_struct *chi + + flush_fp_to_thread(child); + if (fpidx < (PT_FPSCR - PT_FPR0)) +- memcpy(&child->thread.TS_FPR(fpidx), &data, +- sizeof(long)); ++ if (IS_ENABLED(CONFIG_PPC32)) { ++ // On 32-bit the index we are passed refers to 32-bit words ++ ((u32 *)child->thread.fp_state.fpr)[fpidx] = data; ++ } else { ++ memcpy(&child->thread.TS_FPR(fpidx), &data, ++ sizeof(long)); ++ } + else + child->thread.fp_state.fpscr = data; + ret = 0; +@@ -3398,4 +3408,7 @@ void __init pt_regs_check(void) + offsetof(struct user_pt_regs, result)); + + BUILD_BUG_ON(sizeof(struct user_pt_regs) > sizeof(struct pt_regs)); ++ ++ // ptrace_get/put_fpr() rely on PPC32 and VSX being incompatible ++ BUILD_BUG_ON(IS_ENABLED(CONFIG_PPC32) && IS_ENABLED(CONFIG_VSX)); + } diff --git a/queue-5.4/series b/queue-5.4/series index 40acde8ea0e..6b03e719571 100644 --- a/queue-5.4/series +++ b/queue-5.4/series @@ -407,3 +407,4 @@ nfc-st21nfca-fix-memory-leaks-in-evt_transaction-handling.patch ixgbe-fix-bcast-packets-rx-on-vf-after-promisc-removal.patch ixgbe-fix-unexpected-vlan-rx-in-promisc-mode-on-vf.patch input-bcm5974-set-missing-urb_no_transfer_dma_map-urb-flag.patch +powerpc-32-fix-overread-overwrite-of-thread_struct-via-ptrace.patch