aboutsummaryrefslogtreecommitdiff
path: root/sys/kern/imgact_aout.c
diff options
context:
space:
mode:
authorKonstantin Belousov <kib@FreeBSD.org>2017-09-18 20:22:42 +0000
committerKonstantin Belousov <kib@FreeBSD.org>2017-09-18 20:22:42 +0000
commit5efe338f3da0dc100bf37325794a1f2bc422641b (patch)
tree04cb3f7b016c3a00235092441fc17910e49790bc /sys/kern/imgact_aout.c
parentffa317051b8374cd99b74bdcfa544eb0be764fae (diff)
downloadsrc-5efe338f3da0dc100bf37325794a1f2bc422641b.tar.gz
src-5efe338f3da0dc100bf37325794a1f2bc422641b.zip
Fix handling of the segment registers on i386.
Suppose that userspace is executing with the non-standard segment descriptors. Then, until exception or interrupt handler executed SET_KERNEL_SEGS, kernel is still executing with user %ds, %es and %fs. If an interrupt occurs in this window, the interrupt handler is executed unsafely, relying on usability of the usermode registers. If the interrupt results in the context switch on return, the contamination of the kernel state spreads to the thread we switched to. As result, kernel data accesses might fault or, if only the base is changed, completely messed up. More, if the user segment was allocated in LDT, another thread might mark the descriptor as invalid before doreti code tried to reload them. In this case kernel panics. The issue exists for all exception entry points which use trap gate, and thus do not automatically disable interrupts on entry, and for lcall_handler. Fix is two-fold: first, we need to disable interrupts for all kernel entries, changing the IDT descriptor types from trap gate to interrupt gate. Interrupts are re-enabled not earlier than the kernel segments are loaded into the segment registers. Second, we only load the segment registers from the trap frame when returning to usermode. For the later, all interrupt return paths must happen through the doreti common code. There is no way to disable interrupts on call gate, which is the supposed mode of servicing for lcall $7,$0 syscalls. Change the LDT descriptor 0 into a code segment type and point it to the userspace trampoline which redirects the syscall to int $0x80. All the measures make the segment register handling similar to that of amd64. We do not apply amd64 optimizations of not reloading segment registers on return from the syscall. Reported by: Maxime Villard <max@m00nbsd.net> Tested by: pho (the non-lcall part) Reviewed by: jhb Sponsored by: The FreeBSD Foundation MFC after: 2 weeks Differential revision: https://reviews.freebsd.org/D12402
Notes
Notes: svn path=/head/; revision=323722
Diffstat (limited to 'sys/kern/imgact_aout.c')
-rw-r--r--sys/kern/imgact_aout.c17
1 files changed, 17 insertions, 0 deletions
diff --git a/sys/kern/imgact_aout.c b/sys/kern/imgact_aout.c
index cbde46b5acdc..840eb476549a 100644
--- a/sys/kern/imgact_aout.c
+++ b/sys/kern/imgact_aout.c
@@ -27,6 +27,8 @@
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
+#include "opt_compat.h"
+
#include <sys/param.h>
#include <sys/exec.h>
#include <sys/imgact.h>
@@ -337,3 +339,18 @@ exec_aout_imgact(struct image_params *imgp)
*/
static struct execsw aout_execsw = { exec_aout_imgact, "a.out" };
EXEC_SET(aout, aout_execsw);
+
+#if defined(__i386__) && defined(COMPAT_43)
+static void
+exec_init_lcall(void *arg __unused)
+{
+ struct segment_descriptor *gdp;
+ u_int lcall_addr;
+
+ gdp = &ldt[LSYS5CALLS_SEL].sd;
+ lcall_addr = aout_sysvec.sv_psstrings - szlcallcode;
+ gdp->sd_hibase = lcall_addr >> 24;
+ gdp->sd_lobase = lcall_addr;
+}
+SYSINIT(aout, SI_SUB_EXEC + 1, SI_ORDER_ANY, exec_init_lcall, NULL);
+#endif