Improve amd64_get_ldt().

Provide consistent snapshot of the requested descriptors by preventing
other threads from modifying LDT while we fetch the data, lock dt_lock
around the read.  Copy the data into intermediate buffer, which is
copied out after the lock is dropped.

Use guaranteed atomic (aligned volatile) reads of the descriptors to
use same-size atomic as CPU update to set A bit in the descriptor type
field.

Improve overflow checking for the descriptors range calculations and
remove unneeded casts.

Reviewed by:	bde
Sponsored by:	The FreeBSD Foundation
MFC after:	1 week
This commit is contained in:
Konstantin Belousov 2017-10-05 12:29:34 +00:00
parent a4bc304da5
commit 287c718f32

View File

@ -550,34 +550,36 @@ user_ldt_deref(struct proc_ldt *pldt)
int
amd64_get_ldt(struct thread *td, struct i386_ldt_args *uap)
{
int error = 0;
struct proc_ldt *pldt;
int num;
struct user_segment_descriptor *lp;
uint64_t *data;
u_int i, num;
int error;
#ifdef DEBUG
printf("amd64_get_ldt: start=%u num=%u descs=%p\n",
uap->start, uap->num, (void *)uap->descs);
#endif
if ((pldt = td->td_proc->p_md.md_ldt) != NULL) {
lp = &((struct user_segment_descriptor *)(pldt->ldt_base))
[uap->start];
num = min(uap->num, max_ldt_segment);
} else
if (uap->start >= max_ldt_segment)
return (EINVAL);
if ((uap->start > (unsigned int)max_ldt_segment) ||
((unsigned int)num > (unsigned int)max_ldt_segment) ||
((unsigned int)(uap->start + num) > (unsigned int)max_ldt_segment))
return(EINVAL);
error = copyout(lp, uap->descs, num *
num = min(uap->num, max_ldt_segment - uap->start);
pldt = td->td_proc->p_md.md_ldt;
if (pldt == NULL)
return (EINVAL);
lp = &((struct user_segment_descriptor *)(pldt->ldt_base))[uap->start];
data = malloc(num * sizeof(struct user_segment_descriptor), M_TEMP,
M_WAITOK);
mtx_lock(&dt_lock);
for (i = 0; i < num; i++)
data[i] = ((volatile uint64_t *)lp)[i];
mtx_unlock(&dt_lock);
error = copyout(data, uap->descs, num *
sizeof(struct user_segment_descriptor));
if (!error)
free(data, M_TEMP);
if (error == 0)
td->td_retval[0] = num;
return(error);
return (error);
}
int