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


------------------------------------------------------------------------------