From 740fd64d65cc70e1bfd61ab9e8061eb2d1f39c5d Mon Sep 17 00:00:00 2001 From: David Xu Date: Sun, 10 Jul 2005 23:31:11 +0000 Subject: [PATCH] Validate if the value written into {FS,GS}.base is a canonical address, writting non-canonical address can cause kernel a panic, by restricting base values to 0..VM_MAXUSER_ADDRESS, ensuring only canonical values get written to the registers. Reviewed by: peter, Josepha Koshy < joseph.koshy at gmail dot com > Approved by: re (scottl) --- sys/alpha/alpha/vm_machdep.c | 3 +- sys/amd64/amd64/sys_machdep.c | 51 ++++++++++++++++++++++++-------- sys/amd64/amd64/vm_machdep.c | 6 +++- sys/arm/arm/vm_machdep.c | 3 +- sys/i386/i386/vm_machdep.c | 3 +- sys/ia64/ia64/vm_machdep.c | 3 +- sys/kern/kern_thr.c | 7 ++++- sys/powerpc/aim/vm_machdep.c | 3 +- sys/powerpc/powerpc/vm_machdep.c | 3 +- sys/sparc64/sparc64/vm_machdep.c | 3 +- sys/sys/proc.h | 2 +- 11 files changed, 65 insertions(+), 22 deletions(-) diff --git a/sys/alpha/alpha/vm_machdep.c b/sys/alpha/alpha/vm_machdep.c index f8e90287fc80..cfbdf2c1d21f 100644 --- a/sys/alpha/alpha/vm_machdep.c +++ b/sys/alpha/alpha/vm_machdep.c @@ -360,7 +360,7 @@ cpu_set_upcall_kse(struct thread *td, void (*entry)(void *), void *arg, tf->tf_regs[FRAME_FLAGS] = 0; /* full restore */ } -void +int cpu_set_user_tls(struct thread *td, void *tls_base) { @@ -368,6 +368,7 @@ cpu_set_user_tls(struct thread *td, void *tls_base) td->td_pcb->pcb_hw.apcb_unique = (unsigned long)tls_base; else alpha_pal_wrunique((uintptr_t)tls_base); + return (0); } /* diff --git a/sys/amd64/amd64/sys_machdep.c b/sys/amd64/amd64/sys_machdep.c index 25fbb6f0d04f..7f022d00bcce 100644 --- a/sys/amd64/amd64/sys_machdep.c +++ b/sys/amd64/amd64/sys_machdep.c @@ -42,6 +42,10 @@ __FBSDID("$FreeBSD$"); #include #include +#include +#include +#include + #ifndef _SYS_SYSPROTO_H_ struct sysarch_args { int op; @@ -57,6 +61,7 @@ sysarch(td, uap) int error = 0; struct pcb *pcb = curthread->td_pcb; uint32_t i386base; + uint64_t a64base; switch(uap->op) { case I386_GET_FSBASE: @@ -65,9 +70,12 @@ sysarch(td, uap) break; case I386_SET_FSBASE: error = copyin(uap->parms, &i386base, sizeof(i386base)); - pcb->pcb_fsbase = i386base; - if (!error) - wrmsr(MSR_FSBASE, pcb->pcb_fsbase); + if (!error) { + critical_enter(); + wrmsr(MSR_FSBASE, i386base); + pcb->pcb_fsbase = i386base; + critical_exit(); + } break; case I386_GET_GSBASE: i386base = pcb->pcb_gsbase; @@ -75,18 +83,29 @@ sysarch(td, uap) break; case I386_SET_GSBASE: error = copyin(uap->parms, &i386base, sizeof(i386base)); - pcb->pcb_gsbase = i386base; - if (!error) - wrmsr(MSR_KGSBASE, pcb->pcb_gsbase); + if (!error) { + critical_enter(); + wrmsr(MSR_KGSBASE, i386base); + pcb->pcb_gsbase = i386base; + critical_exit(); + } break; case AMD64_GET_FSBASE: error = copyout(&pcb->pcb_fsbase, uap->parms, sizeof(pcb->pcb_fsbase)); break; case AMD64_SET_FSBASE: - error = copyin(uap->parms, &pcb->pcb_fsbase, sizeof(pcb->pcb_fsbase)); - if (!error) - wrmsr(MSR_FSBASE, pcb->pcb_fsbase); + error = copyin(uap->parms, &a64base, sizeof(a64base)); + if (!error) { + if (a64base < VM_MAXUSER_ADDRESS) { + critical_enter(); + wrmsr(MSR_FSBASE, a64base); + pcb->pcb_fsbase = a64base; + critical_exit(); + } else { + error = EINVAL; + } + } break; case AMD64_GET_GSBASE: @@ -94,9 +113,17 @@ sysarch(td, uap) break; case AMD64_SET_GSBASE: - error = copyin(uap->parms, &pcb->pcb_gsbase, sizeof(pcb->pcb_gsbase)); - if (!error) - wrmsr(MSR_KGSBASE, pcb->pcb_gsbase); + error = copyin(uap->parms, &a64base, sizeof(a64base)); + if (!error) { + if (a64base < VM_MAXUSER_ADDRESS) { + critical_enter(); + wrmsr(MSR_KGSBASE, a64base); + pcb->pcb_gsbase = a64base; + critical_exit(); + } else { + error = EINVAL; + } + } break; default: diff --git a/sys/amd64/amd64/vm_machdep.c b/sys/amd64/amd64/vm_machdep.c index 5acc1c04a481..424adafc5bb3 100644 --- a/sys/amd64/amd64/vm_machdep.c +++ b/sys/amd64/amd64/vm_machdep.c @@ -341,10 +341,13 @@ cpu_set_upcall_kse(struct thread *td, void (*entry)(void *), void *arg, td->td_frame->tf_rdi = (register_t)arg; } -void +int cpu_set_user_tls(struct thread *td, void *tls_base) { + if ((u_int64_t)tls_base >= VM_MAXUSER_ADDRESS) + return (EINVAL); + if (td == curthread) { critical_enter(); td->td_pcb->pcb_fsbase = (register_t)tls_base; @@ -353,6 +356,7 @@ cpu_set_user_tls(struct thread *td, void *tls_base) } else { td->td_pcb->pcb_fsbase = (register_t)tls_base; } + return (0); } #ifdef SMP diff --git a/sys/arm/arm/vm_machdep.c b/sys/arm/arm/vm_machdep.c index 6c0760e72f3b..154958dab969 100644 --- a/sys/arm/arm/vm_machdep.c +++ b/sys/arm/arm/vm_machdep.c @@ -297,7 +297,7 @@ cpu_set_upcall_kse(struct thread *td, void (*entry)(void *), void *arg, tf->tf_spsr = PSR_USR32_MODE; } -void +int cpu_set_user_tls(struct thread *td, void *tls_base) { @@ -308,6 +308,7 @@ cpu_set_user_tls(struct thread *td, void *tls_base) *(void **)ARM_TP_ADDRESS = tls_base; critical_exit(); } + return (0); } void diff --git a/sys/i386/i386/vm_machdep.c b/sys/i386/i386/vm_machdep.c index 7a9837d53fbd..9ad7891ff293 100644 --- a/sys/i386/i386/vm_machdep.c +++ b/sys/i386/i386/vm_machdep.c @@ -472,7 +472,7 @@ cpu_set_upcall_kse(struct thread *td, void (*entry)(void *), void *arg, (int)arg); } -void +int cpu_set_user_tls(struct thread *td, void *tls_base) { struct segment_descriptor sd; @@ -503,6 +503,7 @@ cpu_set_user_tls(struct thread *td, void *tls_base) load_gs(GSEL(GUGS_SEL, SEL_UPL)); } critical_exit(); + return (0); } /* diff --git a/sys/ia64/ia64/vm_machdep.c b/sys/ia64/ia64/vm_machdep.c index f7d63014452f..685aece38ee7 100644 --- a/sys/ia64/ia64/vm_machdep.c +++ b/sys/ia64/ia64/vm_machdep.c @@ -209,10 +209,11 @@ cpu_set_upcall_kse(struct thread *td, void (*entry)(void *), void *arg, } } -void +int cpu_set_user_tls(struct thread *td, void *tls_base) { td->td_frame->tf_special.tp = (unsigned long)tls_base; + return (0); } /* diff --git a/sys/kern/kern_thr.c b/sys/kern/kern_thr.c index 4a552a22623d..0e8b3e81f69f 100644 --- a/sys/kern/kern_thr.c +++ b/sys/kern/kern_thr.c @@ -176,7 +176,12 @@ create_thread(struct thread *td, mcontext_t *ctx, /* Set upcall address to user thread entry function. */ cpu_set_upcall_kse(newtd, start_func, arg, &stack); /* Setup user TLS address and TLS pointer register. */ - cpu_set_user_tls(newtd, tls_base); + error = cpu_set_user_tls(newtd, tls_base); + if (error != 0) { + thread_free(newtd); + crfree(td->td_ucred); + return (error); + } } if ((td->td_proc->p_flag & P_HADTHREADS) == 0) { diff --git a/sys/powerpc/aim/vm_machdep.c b/sys/powerpc/aim/vm_machdep.c index 60274f32d70e..4b896eaf7e42 100644 --- a/sys/powerpc/aim/vm_machdep.c +++ b/sys/powerpc/aim/vm_machdep.c @@ -355,9 +355,10 @@ cpu_set_upcall_kse(struct thread *td, void (*entry)(void *), void *arg, td->td_retval[1] = 0; } -void +int cpu_set_user_tls(struct thread *td, void *tls_base) { td->td_frame->fixreg[2] = (register_t)tls_base; + return (0); } diff --git a/sys/powerpc/powerpc/vm_machdep.c b/sys/powerpc/powerpc/vm_machdep.c index 60274f32d70e..4b896eaf7e42 100644 --- a/sys/powerpc/powerpc/vm_machdep.c +++ b/sys/powerpc/powerpc/vm_machdep.c @@ -355,9 +355,10 @@ cpu_set_upcall_kse(struct thread *td, void (*entry)(void *), void *arg, td->td_retval[1] = 0; } -void +int cpu_set_user_tls(struct thread *td, void *tls_base) { td->td_frame->fixreg[2] = (register_t)tls_base; + return (0); } diff --git a/sys/sparc64/sparc64/vm_machdep.c b/sys/sparc64/sparc64/vm_machdep.c index 20bf46b5ed9c..9dd80fdb8a55 100644 --- a/sys/sparc64/sparc64/vm_machdep.c +++ b/sys/sparc64/sparc64/vm_machdep.c @@ -196,13 +196,14 @@ cpu_set_upcall_kse(struct thread *td, void (*entry)(void *), void *arg, td->td_retval[1] = tf->tf_out[1]; } -void +int cpu_set_user_tls(struct thread *td, void *tls_base) { if (td == curthread) flushw(); td->td_frame->tf_global[7] = (uint64_t) tls_base; + return (0); } /* diff --git a/sys/sys/proc.h b/sys/sys/proc.h index b165c071aa44..a408ecccb670 100644 --- a/sys/sys/proc.h +++ b/sys/sys/proc.h @@ -897,7 +897,7 @@ void kse_GC(void); void kseinit(void); void cpu_set_upcall(struct thread *td, struct thread *td0); void cpu_set_upcall_kse(struct thread *, void (*)(void *), void *, stack_t *); -void cpu_set_user_tls(struct thread *, void *tls_base); +int cpu_set_user_tls(struct thread *, void *tls_base); void cpu_thread_clean(struct thread *); void cpu_thread_exit(struct thread *); void cpu_thread_setup(struct thread *td);