Do not do torn writes to active LDTs.

Care must be taken when updating the active LDT, since parallel
threads might try to load a segment descriptor which is currently
updated. Since the results are undefined, this cannot be ignored by
claiming to be an application race.

Reviewed by:	jhb
Sponsored by:	The FreeBSD Foundation
MFC after:	2 weeks
Differential revision:	https://reviews.freebsd.org/D12413
This commit is contained in:
kib 2017-09-19 17:57:04 +00:00
parent 62cba400a3
commit 466bfe2553
2 changed files with 42 additions and 27 deletions

View File

@ -583,22 +583,22 @@ amd64_get_ldt(td, uap)
}
int
amd64_set_ldt(td, uap, descs)
struct thread *td;
struct i386_ldt_args *uap;
struct user_segment_descriptor *descs;
amd64_set_ldt(struct thread *td, struct i386_ldt_args *uap,
struct user_segment_descriptor *descs)
{
int error = 0;
unsigned int largest_ld, i;
struct mdproc *mdp = &td->td_proc->p_md;
struct mdproc *mdp;
struct proc_ldt *pldt;
struct user_segment_descriptor *dp;
struct proc *p;
int error;
unsigned int largest_ld, i;
#ifdef DEBUG
printf("amd64_set_ldt: start=%d num=%d descs=%p\n",
uap->start, uap->num, (void *)uap->descs);
#endif
mdp = &td->td_proc->p_md;
error = 0;
set_pcb_flags(td->td_pcb, PCB_FULL_IRET);
p = td->td_proc;
@ -616,10 +616,9 @@ amd64_set_ldt(td, uap, descs)
largest_ld = max_ldt_segment;
if (largest_ld < uap->start)
return (EINVAL);
i = largest_ld - uap->start;
mtx_lock(&dt_lock);
bzero(&((struct user_segment_descriptor *)(pldt->ldt_base))
[uap->start], sizeof(struct user_segment_descriptor) * i);
for (i = uap->start; i < largest_ld; i++)
((uint64_t *)(pldt->ldt_base))[i] = 0;
mtx_unlock(&dt_lock);
return (0);
}
@ -741,14 +740,18 @@ int
amd64_set_ldt_data(struct thread *td, int start, int num,
struct user_segment_descriptor *descs)
{
struct mdproc *mdp = &td->td_proc->p_md;
struct proc_ldt *pldt = mdp->md_ldt;
struct mdproc *mdp;
struct proc_ldt *pldt;
uint64_t *dst, *src;
int i;
mtx_assert(&dt_lock, MA_OWNED);
/* Fill in range */
bcopy(descs,
&((struct user_segment_descriptor *)(pldt->ldt_base))[start],
num * sizeof(struct user_segment_descriptor));
mdp = &td->td_proc->p_md;
pldt = mdp->md_ldt;
dst = (uint64_t *)(pldt->ldt_base);
src = (uint64_t *)descs;
for (i = 0; i < num; i++)
dst[start + i] = src[i];
return (0);
}

View File

@ -51,6 +51,7 @@ __FBSDID("$FreeBSD$");
#include <vm/vm_map.h>
#include <vm/vm_extern.h>
#include <machine/atomic.h>
#include <machine/cpu.h>
#include <machine/pcb.h>
#include <machine/pcb_ext.h>
@ -546,7 +547,7 @@ i386_set_ldt(td, uap, descs)
struct i386_ldt_args *uap;
union descriptor *descs;
{
int error = 0, i;
int error, i;
int largest_ld;
struct mdproc *mdp = &td->td_proc->p_md;
struct proc_ldt *pldt;
@ -556,6 +557,7 @@ i386_set_ldt(td, uap, descs)
printf("i386_set_ldt: start=%d num=%d descs=%p\n",
uap->start, uap->num, (void *)uap->descs);
#endif
error = 0;
if (descs == NULL) {
/* Free descriptors */
@ -578,9 +580,9 @@ i386_set_ldt(td, uap, descs)
largest_ld = uap->start + uap->num;
if (largest_ld > pldt->ldt_len)
largest_ld = pldt->ldt_len;
i = largest_ld - uap->start;
bzero(&((union descriptor *)(pldt->ldt_base))[uap->start],
sizeof(union descriptor) * i);
for (i = uap->start; i < largest_ld; i++)
atomic_store_rel_64(&((uint64_t *)(pldt->ldt_base))[i],
0);
mtx_unlock_spin(&dt_lock);
return (0);
}
@ -702,17 +704,27 @@ i386_set_ldt(td, uap, descs)
static int
i386_set_ldt_data(struct thread *td, int start, int num,
union descriptor *descs)
union descriptor *descs)
{
struct mdproc *mdp = &td->td_proc->p_md;
struct proc_ldt *pldt = mdp->md_ldt;
struct mdproc *mdp;
struct proc_ldt *pldt;
uint64_t *dst, *src;
int i;
mtx_assert(&dt_lock, MA_OWNED);
/* Fill in range */
bcopy(descs,
&((union descriptor *)(pldt->ldt_base))[start],
num * sizeof(union descriptor));
mdp = &td->td_proc->p_md;
pldt = mdp->md_ldt;
dst = (uint64_t *)(pldt->ldt_base);
src = (uint64_t *)descs;
/*
* Atomic(9) is used only to get 64bit atomic store with
* cmpxchg8b when available. There is no op without release
* semantic.
*/
for (i = 0; i < num; i++)
atomic_store_rel_64(&dst[start + i], src[i]);
return (0);
}