amd64: Avoid enabling interrupts when handling kernel mode prot faults

When PTI is enabled, we may have been on the trampoline stack when iret
faults.  So, we have to switch back to the regular stack before
re-entering trap().

trap() has the somewhat strange behaviour of re-enabling interrupts when
handling certain kernel-mode execeptions.  In particular, it was doing
this for exceptions raised during execution of iret.  When switching
away from the trampoline stack, however, the thread must not be migrated
to a different CPU.  Fix the problem by simply leaving interrupts
disabled during the window.

Reported by:	syzbot+6cfa544fd86ad4647ffc@syzkaller.appspotmail.com
Reported by:	syzbot+cfdfc9e5a8f28f11a7f5@syzkaller.appspotmail.com
Reviewed by:	kib
MFC after:	1 week
Sponsored by:	The FreeBSD Foundation
Differential Revision:	https://reviews.freebsd.org/D30578
This commit is contained in:
Mark Johnston 2021-05-31 18:49:33 -04:00
parent 8790fe3058
commit 4a59cbc125

View File

@ -236,25 +236,31 @@ trap(struct trapframe *frame)
* interrupts disabled until they are accidentally
* enabled later.
*/
if (TRAPF_USERMODE(frame))
if (TRAPF_USERMODE(frame)) {
uprintf(
"pid %ld (%s): trap %d with interrupts disabled\n",
(long)curproc->p_pid, curthread->td_name, type);
else if (type != T_NMI && type != T_BPTFLT &&
type != T_TRCTRAP) {
/*
* XXX not quite right, since this may be for a
* multiple fault in user mode.
*/
printf("kernel trap %d with interrupts disabled\n",
type);
} else {
switch (type) {
case T_NMI:
case T_BPTFLT:
case T_TRCTRAP:
case T_PROTFLT:
case T_SEGNPFLT:
case T_STKFLT:
break;
default:
printf(
"kernel trap %d with interrupts disabled\n",
type);
/*
* We shouldn't enable interrupts while holding a
* spin lock.
*/
if (td->td_md.md_spinlock_count == 0)
enable_intr();
/*
* We shouldn't enable interrupts while holding a
* spin lock.
*/
if (td->td_md.md_spinlock_count == 0)
enable_intr();
}
}
}
@ -444,6 +450,8 @@ trap(struct trapframe *frame)
* Magic '5' is the number of qwords occupied by
* the hardware trap frame.
*/
KASSERT((read_rflags() & PSL_I) == 0,
("interrupts enabled"));
if (frame->tf_rip == (long)doreti_iret) {
frame->tf_rip = (long)doreti_iret_fault;
if ((PCPU_GET(curpmap)->pm_ucr3 !=