Subject: Proposed fixes for security hole in sigreturn
To: None <port-i386@sun-lamp.cs.berkeley.edu>
From: John Brezak <brezak@apollo.hp.com>
List: port-i386
Date: 01/06/1994 14:49:08
Here is a fix to (hopefully) close a security hole and panic point in sigreturn.
Here are the assumptions:
- A user attempts to return from a signal handler with a selector that is a GDT
selector and gives them more priv.
- A user sets a garbage LDT selector
- A user attempts to get more priv by modifying the RPL.
Checks added:
- Only allow LDT selectors
- Be use UPL 3 is used
- Be sure that the LDT is initially bzeroed
- Check range of LDT.
Other fixes included.
- Initial per user LDT is allocated as one page (512 entries), but can grow to
the maximum of 8192 entries. (Can the LDT contain a linear (contig)
Virtual address ?).
Questions:
- Should the uprintf()'s be kept -or
- Should a SIGBUS (or other signal) be forced upon the user for attempting
bad things in sigreturn() ? The current bahavior is to simply force
an exit. This gives no clue to the user program for debugging.
*** machdep.c.orig Sun Jan 2 14:22:38 1994
--- machdep.c Sat Jan 1 23:53:36 1994
***************
*** 530,535 ****
--- 530,570 ----
p->p_sigmask = scp->sc_mask &~
(sigmask(SIGKILL)|sigmask(SIGCONT)|sigmask(SIGSTOP));
+ /*
+ * Sanity check the user's selectors and error if they
+ * are suspect.
+ */
+ #define max_ldt_sel(pcb) \
+ ((((pcb)->pcb_ldt)? \
+ (gdt_segs[GUSERLDT_SEL].ssd_limit + 1) : \
+ sizeof(ldt))/sizeof(union descriptor))
+
+ #define valid_sel(sel) \
+ (((sel) == 0) || \
+ (ISPL((sel)) == SEL_UPL && ISLDT((sel))) || \
+ (ISLDT((sel)) && IDXSEL((sel)) < max_ldt_sel((struct pcb *)(p->p_addr))))
+
+ if (scp->sc_cs&0xffff != _ucodesel || scp->sc_ss&0xffff != _udatasel ||
+ scp->sc_ds&0xffff != _udatasel || scp->sc_es&0xffff != _udatasel) {
+ if (!valid_sel(scp->sc_cs)) {
+ uprintf("pid %d: attempted to use illegal CS selector %x from signal handler\n", p->p_pid, scp->sc_cs&0xffff);
+ return(EINVAL);
+ }
+ if (!valid_sel(scp->sc_ss)) {
+ uprintf("pid %d: attempted to use illegal SS selector %x from signal handler\n", p->p_pid, scp->sc_ss&0xffff);
+ return(EINVAL);
+ }
+ if (!valid_sel(scp->sc_ds)) {
+ uprintf("pid %d: attempted to use illegal DS selector %x from signal handler\n", p->p_pid, scp->sc_ds&0xffff);
+ return(EINVAL);
+ }
+ if (!valid_sel(scp->sc_es)) {
+ uprintf("pid %d: attempted to use illegal ES selector %x from signal handler\n", p->p_pid, scp->sc_es&0xffff);
+ return(EINVAL);
+ }
+ }
+ #undef valid_sel
+
/*
* Restore signal context.
*/
*** sys_machdep.c.orig Sat Jan 1 14:56:37 1994
--- sys_machdep.c Sat Jan 1 17:40:19 1994
***************
*** 136,141 ****
--- 136,142 ----
int num;
};
+
i386_get_ldt(p, args, retval)
struct proc *p;
char *args;
***************
*** 213,232 ****
if (uap->start < 0 || uap->num < 0)
return(EINVAL);
! /* XXX Should be 8192 ! */
! if (uap->start > 512 ||
! (uap->start + uap->num) > 512)
return(EINVAL);
/* allocate user ldt */
! if (!pcb->pcb_ldt) {
! union descriptor *new_ldt =
! (union descriptor *)kmem_alloc(kernel_map, 512*sizeof(union descriptor));
! bcopy(ldt, new_ldt, sizeof(ldt));
pcb->pcb_ldt = (caddr_t)new_ldt;
! pcb->pcb_ldt_len = 512; /* XXX need to grow */
#ifdef DEBUG
! printf("i386_set_ldt(%d): new_ldt=%x\n", p->p_pid, new_ldt);
#endif
}
--- 214,252 ----
if (uap->start < 0 || uap->num < 0)
return(EINVAL);
! #define NUM_LDT 8192
!
! if (uap->start > NUM_LDT ||
! (uap->start + uap->num) > NUM_LDT)
return(EINVAL);
+ n = uap->start + uap->num;
+
/* allocate user ldt */
! if (!pcb->pcb_ldt || pcb->pcb_ldt_len < n) {
! union descriptor *old_ldt = ldt, *new_ldt;
!
! n = round_page(n*sizeof(union descriptor)) / sizeof(union descriptor);
!
! new_ldt = (union descriptor *)kmem_alloc(kernel_map, n*sizeof(union descriptor));
! i = sizeof(ldt);
!
! bzero(new_ldt, n*sizeof(union descriptor));
! if (pcb->pcb_ldt) {
! #ifdef DEBUG
! printf("i386_set_ldt(%d): growing pcb LDT to %d\n", p->p_pid, n);
! #endif
! old_ldt = (union descriptor *)pcb->pcb_ldt;
! i = pcb->pcb_ldt_len * sizeof(union descriptor);
! }
! bcopy(old_ldt, new_ldt, i);
! if (pcb->pcb_ldt)
! kmem_free(kernel_map, (vm_offset_t)pcb->pcb_ldt,
! (pcb->pcb_ldt_len * sizeof(union descriptor)));
pcb->pcb_ldt = (caddr_t)new_ldt;
! pcb->pcb_ldt_len = n;
#ifdef DEBUG
! printf("i386_set_ldt(%d): ldt_len=%d new_ldt=%x old_ldt=%x\n", p->p_pid, n, new_ldt, old_ldt);
#endif
}
*** vm_machdep.c.orig Sat Jan 1 15:02:01 1994
--- vm_machdep.c Sat Jan 1 15:08:38 1994
***************
*** 194,200 ****
--- 194,202 ----
if (pcb->pcb_ldt) {
kmem_free(kernel_map, (vm_offset_t)pcb->pcb_ldt,
(pcb->pcb_ldt_len * sizeof(union descriptor)));
+ /* null old refs; prob not needed, but feels good */
pcb->pcb_ldt = NULL;
+ pcb->pcb_ldt_len = 0;
}
#endif
kmem_free(kernel_map, (vm_offset_t)p->p_addr, ctob(UPAGES));
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
John Brezak UUCP: uunet!apollo.hp!brezak
Hewlett Packard/Apollo Internet: brezak@ch.hp.com
300 Apollo Drive Phone: (508) 436-4915
Chelmsford, Massachusetts Fax: (508) 436-5103
------------------------------------------------------------------------------