git: 8892176c86db - main - amd64: check that %cs and %ss values from ucontext fit into registers

From: Konstantin Belousov <kib_at_FreeBSD.org>
Date: Fri, 20 Mar 2026 22:49:51 UTC
The branch main has been updated by kib:

URL: https://cgit.FreeBSD.org/src/commit/?id=8892176c86db18bd175cc00a2d52dff080babec1

commit 8892176c86db18bd175cc00a2d52dff080babec1
Author:     Konstantin Belousov <kib@FreeBSD.org>
AuthorDate: 2026-03-15 07:17:24 +0000
Commit:     Konstantin Belousov <kib@FreeBSD.org>
CommitDate: 2026-03-20 21:05:15 +0000

    amd64: check that %cs and %ss values from ucontext fit into registers
    
    This change only checks that the values from the user-supplied context
    are not truncated by C implicit type convertions.  The validity of the
    segment selectors is still checked by hardware.
    
    Reviewed by:    jhb, markj
    Sponsored by:   The FreeBSD Foundation
    MFC after:      1 week
    Differential revision:  https://reviews.freebsd.org/D55861
---
 sys/amd64/amd64/exec_machdep.c | 19 +++++++++++++++++++
 sys/amd64/ia32/ia32_signal.c   | 28 ++++++++++++++++++++++++++++
 2 files changed, 47 insertions(+)

diff --git a/sys/amd64/amd64/exec_machdep.c b/sys/amd64/amd64/exec_machdep.c
index b5eda6f83d46..7d567c561c52 100644
--- a/sys/amd64/amd64/exec_machdep.c
+++ b/sys/amd64/amd64/exec_machdep.c
@@ -94,6 +94,15 @@ _Static_assert(sizeof(mcontext_t) == 800, "mcontext_t size incorrect");
 _Static_assert(sizeof(ucontext_t) == 880, "ucontext_t size incorrect");
 _Static_assert(sizeof(siginfo_t) == 80, "siginfo_t size incorrect");
 
+/*
+ * Check that the value r is 16bit, i.e. fits into a segment register.
+ */
+static bool
+is_seg_val(register_t r)
+{
+	return ((uint64_t)r <= 0xffff);
+}
+
 /*
  * Send an interrupt to process.
  *
@@ -262,6 +271,14 @@ sys_sigreturn(struct thread *td, struct sigreturn_args *uap)
 		return (EINVAL);
 	}
 
+	if (!is_seg_val(ucp->uc_mcontext.mc_ss) ||
+	    !is_seg_val(ucp->uc_mcontext.mc_cs)) {
+		uprintf("pid %d (%s): sigreturn cs = %#lx ss = %#lx\n",
+		    p->p_pid, td->td_name, ucp->uc_mcontext.mc_cs,
+		    ucp->uc_mcontext.mc_ss);
+		return (EINVAL);
+	}
+
 	/*
 	 * Don't allow users to load a valid privileged %cs.  Let the
 	 * hardware check for invalid selectors, excess privilege in
@@ -659,6 +676,8 @@ set_mcontext(struct thread *td, mcontext_t *mcp)
 	if (mcp->mc_len != sizeof(*mcp) ||
 	    (mcp->mc_flags & ~_MC_FLAG_MASK) != 0)
 		return (EINVAL);
+	if (!is_seg_val(mcp->mc_ss) || !is_seg_val(mcp->mc_cs))
+		return (EINVAL);
 	rflags = (mcp->mc_rflags & PSL_USERCHANGE) |
 	    (tp->tf_rflags & ~PSL_USERCHANGE);
 	if (mcp->mc_flags & _MC_HASFPXSTATE) {
diff --git a/sys/amd64/ia32/ia32_signal.c b/sys/amd64/ia32/ia32_signal.c
index 54e170450dba..3b26244932b4 100644
--- a/sys/amd64/ia32/ia32_signal.c
+++ b/sys/amd64/ia32/ia32_signal.c
@@ -88,6 +88,15 @@ extern char _binary_elf_vdso32_so_1_size;
 static void freebsd4_ia32_sendsig(sig_t, ksiginfo_t *, sigset_t *);
 #endif
 
+/*
+ * Check that the value r is 16bit, i.e. fits into a segment register.
+ */
+static bool
+is_seg_val(uint32_t r)
+{
+	return (r <= 0xffff);
+}
+
 static void
 ia32_get_fpcontext(struct thread *td, struct ia32_mcontext *mcp,
     char **xfpusave, size_t *xfpusave_len)
@@ -205,6 +214,8 @@ ia32_set_mcontext(struct thread *td, struct ia32_mcontext *mcp)
 	tp = td->td_frame;
 	if (mcp->mc_len != sizeof(*mcp))
 		return (EINVAL);
+	if (!is_seg_val(mcp->mc_ss) || !is_seg_val(mcp->mc_cs))
+		return (EINVAL);
 	rflags = (mcp->mc_eflags & PSL_USERCHANGE) |
 	    (tp->tf_rflags & ~PSL_USERCHANGE);
 	if (mcp->mc_flags & _MC_IA32_HASFPXSTATE) {
@@ -707,6 +718,8 @@ ofreebsd32_sigreturn(struct thread *td, struct ofreebsd32_sigreturn_args *uap)
 	if (!EFL_SECURE(eflags, regs->tf_rflags)) {
 		return (EINVAL);
 	}
+	if (!is_seg_val(scp->sc_ss) || !is_seg_val(scp->sc_cs))
+		return (EINVAL);
 	if (!CS_SECURE(scp->sc_cs)) {
 		ksiginfo_init_trap(&ksi);
 		ksi.ksi_signo = SIGBUS;
@@ -772,6 +785,13 @@ freebsd4_freebsd32_sigreturn(struct thread *td,
 		return (EINVAL);
 	}
 
+	if (!is_seg_val(ucp->uc_mcontext.mc_ss) ||
+	    !is_seg_val(ucp->uc_mcontext.mc_cs)) {
+		uprintf("pid %d (%s): sigreturn cs = %#x ss = %#x\n",
+		    td->td_proc->p_pid, td->td_name, ucp->uc_mcontext.mc_cs,
+		    ucp->uc_mcontext.mc_ss);
+		return (EINVAL);
+	}
 	/*
 	 * Don't allow users to load a valid privileged %cs.  Let the
 	 * hardware check for invalid selectors, excess privilege in
@@ -841,6 +861,14 @@ freebsd32_sigreturn(struct thread *td, struct freebsd32_sigreturn_args *uap)
 		return (EINVAL);
 	}
 
+	if (!is_seg_val(ucp->uc_mcontext.mc_ss) ||
+	    !is_seg_val(ucp->uc_mcontext.mc_cs)) {
+		uprintf("pid %d (%s): sigreturn cs = %#x ss = %#x\n",
+		    td->td_proc->p_pid, td->td_name, ucp->uc_mcontext.mc_cs,
+		    ucp->uc_mcontext.mc_ss);
+		return (EINVAL);
+	}
+
 	/*
 	 * Don't allow users to load a valid privileged %cs.  Let the
 	 * hardware check for invalid selectors, excess privilege in