Commit e36a82ee authored by Kamalesh Babulal's avatar Kamalesh Babulal Committed by Michael Ellerman
Browse files

powerpc/livepatch: Fix livepatch stack access



While running stress test with livepatch module loaded, kernel bug was
triggered.

  cpu 0x5: Vector: 400 (Instruction Access) at [c0000000eb9d3b60]
  5:mon> t
  [c0000000eb9d3de0] c0000000eb9d3e30 (unreliable)
  [c0000000eb9d3e30] c000000000008ab4 hardware_interrupt_common+0x114/0x120
   --- Exception: 501 (Hardware Interrupt) at c000000000053040 livepatch_handler+0x4c/0x74
  [c0000000eb9d4120] 0000000057ac6e9d (unreliable)
  [d0000000089d9f78] 2e0965747962382e
  SP (965747962342e09) is in userspace

When an interrupt occurs during the livepatch_handler execution, it's
possible for the livepatch_stack and/or thread_info to be corrupted.
eg:

  Task A                        Interrupt Handler
  =========                     =================
  livepatch_handler:
  mr r0, r1
  ld r1, TI_livepatch_sp(r12)
                                hardware_interrupt_common:
                                  do_IRQ+0x8:
                                    mflr    r0          <- saved stack pointer is overwritten
                                    bl      _mcount
                                    ...
                                    std     r27,-40(r1) <- overwrite of thread_info()

  lis r2, STACK_END_MAGIC@h
  ori r2, r2, STACK_END_MAGIC@l
  ld  r12, -8(r1)

Fix the corruption by using r11 register for livepatch stack
manipulation, instead of shuffling task stack and livepatch stack into
r1 register. Using r11 register also avoids disabling/enabling irq's
while setting up the livepatch stack.

Signed-off-by: default avatarKamalesh Babulal <kamalesh@linux.vnet.ibm.com>
Reviewed-by: default avatarNaveen N. Rao <naveen.n.rao@linux.vnet.ibm.com>
Reviewed-by: default avatarBalbir Singh <bsingharora@gmail.com>
Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
parent 53ecde0b
Loading
Loading
Loading
Loading
+15 −30
Original line number Diff line number Diff line
@@ -181,34 +181,25 @@ _GLOBAL(ftrace_stub)
	 *  - we have no stack frame and can not allocate one
	 *  - LR points back to the original caller (in A)
	 *  - CTR holds the new NIP in C
	 *  - r0 & r12 are free
	 *
	 * r0 can't be used as the base register for a DS-form load or store, so
	 * we temporarily shuffle r1 (stack pointer) into r0 and then put it back.
	 *  - r0, r11 & r12 are free
	 */
livepatch_handler:
	CURRENT_THREAD_INFO(r12, r1)

	/* Save stack pointer into r0 */
	mr	r0, r1

	/* Allocate 3 x 8 bytes */
	ld	r1, TI_livepatch_sp(r12)
	addi	r1, r1, 24
	std	r1, TI_livepatch_sp(r12)
	ld	r11, TI_livepatch_sp(r12)
	addi	r11, r11, 24
	std	r11, TI_livepatch_sp(r12)

	/* Save toc & real LR on livepatch stack */
	std	r2,  -24(r1)
	std	r2,  -24(r11)
	mflr	r12
	std	r12, -16(r1)
	std	r12, -16(r11)

	/* Store stack end marker */
	lis     r12, STACK_END_MAGIC@h
	ori     r12, r12, STACK_END_MAGIC@l
	std	r12, -8(r1)

	/* Restore real stack pointer */
	mr	r1, r0
	std	r12, -8(r11)

	/* Put ctr in r12 for global entry and branch there */
	mfctr	r12
@@ -216,36 +207,30 @@ livepatch_handler:

	/*
	 * Now we are returning from the patched function to the original
	 * caller A. We are free to use r0 and r12, and we can use r2 until we
	 * caller A. We are free to use r11, r12 and we can use r2 until we
	 * restore it.
	 */

	CURRENT_THREAD_INFO(r12, r1)

	/* Save stack pointer into r0 */
	mr	r0, r1

	ld	r1, TI_livepatch_sp(r12)
	ld	r11, TI_livepatch_sp(r12)

	/* Check stack marker hasn't been trashed */
	lis     r2,  STACK_END_MAGIC@h
	ori     r2,  r2, STACK_END_MAGIC@l
	ld	r12, -8(r1)
	ld	r12, -8(r11)
1:	tdne	r12, r2
	EMIT_BUG_ENTRY 1b, __FILE__, __LINE__ - 1, 0

	/* Restore LR & toc from livepatch stack */
	ld	r12, -16(r1)
	ld	r12, -16(r11)
	mtlr	r12
	ld	r2,  -24(r1)
	ld	r2,  -24(r11)

	/* Pop livepatch stack frame */
	CURRENT_THREAD_INFO(r12, r0)
	subi	r1, r1, 24
	std	r1, TI_livepatch_sp(r12)

	/* Restore real stack pointer */
	mr	r1, r0
	CURRENT_THREAD_INFO(r12, r1)
	subi	r11, r11, 24
	std	r11, TI_livepatch_sp(r12)

	/* Return to original caller of live patched function */
	blr