[PATCH] powerpc: Save AMR/IAMR when switching tasks

Samuel Holland samuel at sholland.org
Fri Sep 16 15:05:14 AEST 2022


With CONFIG_PREEMPT=y (involuntary preemption enabled), it is possible
to switch away from a task inside copy_{from,to}_user. This left the CPU
with userspace access enabled until after the next IRQ or privilege
level switch, when AMR/IAMR got reset to AMR_KU[AE]P_BLOCKED. Then, when
switching back to the original task, the userspace access would fault:

  Kernel attempted to write user page (3fff7ab68190) - exploit attempt? (uid: 65536)
  ------------[ cut here ]------------
  Bug: Write fault blocked by KUAP!
  WARNING: CPU: 56 PID: 4939 at arch/powerpc/mm/fault.c:228 ___do_page_fault+0x7b4/0xaa0
  CPU: 56 PID: 4939 Comm: git Tainted: G        W         5.19.8-00005-gba424747260d #1
  NIP:  c0000000000555e4 LR: c0000000000555e0 CTR: c00000000079d9d0
  REGS: c00000008f507370 TRAP: 0700   Tainted: G        W          (5.19.8-00005-gba424747260d)
  MSR:  9000000000021033 <SF,HV,ME,IR,DR,RI,LE>  CR: 28042222  XER: 20040000
  CFAR: c000000000123780 IRQMASK: 3
  NIP [c0000000000555e4] ___do_page_fault+0x7b4/0xaa0
  LR [c0000000000555e0] ___do_page_fault+0x7b0/0xaa0
  Call Trace:
  [c00000008f507610] [c0000000000555e0] ___do_page_fault+0x7b0/0xaa0 (unreliable)
  [c00000008f5076c0] [c000000000055938] do_page_fault+0x68/0x130
  [c00000008f5076f0] [c000000000008914] data_access_common_virt+0x194/0x1f0
  --- interrupt: 300 at __copy_tofrom_user_base+0x9c/0x5a4
  NIP:  c00000000007b1a8 LR: c00000000073f4d4 CTR: 0000000000000080
  REGS: c00000008f507760 TRAP: 0300   Tainted: G        W          (5.19.8-00005-gba424747260d)
  MSR:  900000000280b033 <SF,HV,VEC,VSX,EE,FP,ME,IR,DR,RI,LE>  CR: 24002220  XER: 20040000
  CFAR: c00000000007b174 DAR: 00003fff7ab68190 DSISR: 0a000000 IRQMASK: 0
  NIP [c00000000007b1a8] __copy_tofrom_user_base+0x9c/0x5a4
  LR [c00000000073f4d4] copyout+0x74/0x150
  --- interrupt: 300
  [c00000008f507a30] [c0000000007430cc] copy_page_to_iter+0x12c/0x4b0
  [c00000008f507ab0] [c0000000002c7c20] filemap_read+0x200/0x460
  [c00000008f507bf0] [c0000000005f96f4] xfs_file_buffered_read+0x104/0x170
  [c00000008f507c30] [c0000000005f9800] xfs_file_read_iter+0xa0/0x150
  [c00000008f507c70] [c0000000003bddc8] new_sync_read+0x108/0x180
  [c00000008f507d10] [c0000000003c06b0] vfs_read+0x1d0/0x240
  [c00000008f507d60] [c0000000003c0ba4] ksys_read+0x84/0x140
  [c00000008f507db0] [c00000000002a3fc] system_call_exception+0x15c/0x300
  [c00000008f507e10] [c00000000000c63c] system_call_common+0xec/0x250
  --- interrupt: c00 at 0x3fff83aa7238
  NIP:  00003fff83aa7238 LR: 00003fff83a923b8 CTR: 0000000000000000
  REGS: c00000008f507e80 TRAP: 0c00   Tainted: G        W          (5.19.8-00005-gba424747260d)
  MSR:  900000000280f033 <SF,HV,VEC,VSX,EE,PR,FP,ME,IR,DR,RI,LE>  CR: 80002482  XER: 00000000
  IRQMASK: 0
  NIP [00003fff83aa7238] 0x3fff83aa7238
  LR [00003fff83a923b8] 0x3fff83a923b8
  --- interrupt: c00
  Instruction dump:
  e87f0100 48101021 60000000 2c230000 4182fee8 408e0128 3c82ff80 3884e978
  3c62ff80 3863ea78 480ce13d 60000000 <0fe00000> fb010070 fb810090 e80100c0
  ---[ end trace 0000000000000000 ]---

Fix this by saving and restoring the kernel-side AMR/IAMR values when
switching tasks.

Fixes: 890274c2dc4c ("powerpc/64s: Implement KUAP for Radix MMU")
Signed-off-by: Samuel Holland <samuel at sholland.org>
---
I have no idea if this is the right change to make, and it could be
optimized, but my system has been stable with this patch for 5 days now.

Without the patch, I hit the bug every few minutes when my load average
is <1, and I hit it immediately if I try to do a parallel kernel build.

Because of the instability (file I/O randomly raises SIGBUS), I don't
think anyone would run a system in this configuration, so I don't think
this bug is exploitable.

 arch/powerpc/kernel/process.c | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c
index 0fbda89cd1bb..69b189d63124 100644
--- a/arch/powerpc/kernel/process.c
+++ b/arch/powerpc/kernel/process.c
@@ -1150,6 +1150,12 @@ static inline void save_sprs(struct thread_struct *t)
 		 */
 		t->tar = mfspr(SPRN_TAR);
 	}
+	if (t->regs) {
+		if (mmu_has_feature(MMU_FTR_BOOK3S_KUAP))
+			t->regs->amr = mfspr(SPRN_AMR);
+		if (mmu_has_feature(MMU_FTR_BOOK3S_KUEP))
+			t->regs->iamr = mfspr(SPRN_IAMR);
+	}
 #endif
 }
 
@@ -1228,6 +1234,13 @@ static inline void restore_sprs(struct thread_struct *old_thread,
 	if (cpu_has_feature(CPU_FTR_P9_TIDR) &&
 	    old_thread->tidr != new_thread->tidr)
 		mtspr(SPRN_TIDR, new_thread->tidr);
+	if (new_thread->regs) {
+		if (mmu_has_feature(MMU_FTR_BOOK3S_KUAP))
+			mtspr(SPRN_AMR, new_thread->regs->amr);
+		if (mmu_has_feature(MMU_FTR_BOOK3S_KUEP))
+			mtspr(SPRN_IAMR, new_thread->regs->iamr);
+		isync();
+	}
 #endif
 
 }
-- 
2.35.1



More information about the Linuxppc-dev mailing list