COMMAND

    kernel

SYSTEMS AFFECTED

    NetBSD

PROBLEM

    Bill Sommerfeld found  following.  A  subtle bug in  validation of
    user-supplied  arguments  to  a  syscall  can  allow  allow   user
    applications on the i386 platform to transfer control to arbitrary
    addresses in kernel memory, bypassing normal system protections.

    This problem is only present  on the i386 platform, and  only when
    the USER_LDT  kernel configuration  option is  enabled at  compile
    time.  Unfortunately,  this option is  present in the  GENERIC and
    GENERIC_LAPTOP kernel configurations shipped with NetBSD releases,
    as well as several examples, so many users will be affected.

    The  problem  stems  from  the  complexity  of  the  x86 processor
    architecture memory protection system, and other systems are known
    to be  affected.   This includes  systems with  code derived  from
    NetBSD as well as other independently-written systems,  indicating
    that the same mistake may have been repeated elsewhere as well.

    The  Intel  i386  architecture  supports  a  complex and confusing
    protection  and  segmentation  model  relying  on  rings,  segment
    selectors, segment descriptors,  gates, descriptor tables  and the
    like.

    The Local Descriptor Table (LDT) is usually a per-process segment;
    most  unix   OS's  provide   system  calls   allowing  application
    processes to add new segments to the LDT.  In NetBSD, this  option
    is enabled  with "options  USER_LDT" in  the kernel  configuration
    file, and is generally enabled for the WINE Windows emulator.

    One segment  type is  a "call  gate", which  allows code  in outer
    rings to  make calls  to code  in inner,  more priviledged  rings.
    Call  gates  are  one  of  the  three (or more?) different ways to
    implement system calls on the x86.  The segment descriptor in  the
    LDT for a call gate  contains a segment selector and  offset; when
    a program running  in an outer  ring executes a  "ljmp" or "lcall"
    instruction which specifies the  segment selector of a  call gate,
    control is transferred  to the specified  offset of the  specified
    code segment.  If the  gate target is an inner-ring  code segment,
    a ring  transition occurs;  therefore it  is vital  for code which
    creates call gate descriptors to validate that the target  segment
    and offset of the gate is appropriate.

    While reviewing code in NetBSD, it was discovered that the  NetBSD
    "i386_set_ldt" syscall did not  do appropriate validation of  call
    gate targets;  in short,  it's possible  to create  a call gate in
    the LDT  which transfers  control to  an arbitrary  address in the
    kernel.

    The problem affects other Operating Systems also:
    * OpenBSD has the same bug, in code inherited directly from NetBSD
    * FreeBSD also once had the bug, but it was eliminated a couple of
      years ago as part of a change to disable setting call gates.
    * Sun Solaris/x86 has the same bug, in a different  implementation
      of a similar mechanism.
    * The problem is not necessarily  limited to unix; any OS for  the
      x86  which  allows  an  unprivileged  program  to  create   gate
      descriptors in its LDT could have this bug.

    A common misunderstanding of how gate descriptors work may  result
    in the programmer believing  they've defended against this  attack
    (by checking the gate's DPL)  without having done so (you  need to
    check the DPL of the code segment that the gate targets).

    Note that this  behaviour is not  restricted to Intel  processors;
    the  bug  applies  to  implementations  of the x86 architecture by
    other manufacturers as well.

SOLUTION

    Systems  running  NetBSD/i386  with  the  USER_LDT  kernel  option
    enabled are vulnerable.   This includes users running  the GENERIC
    kernel configurations distributed with releases, or users who have
    built their  own kernel  configurations based  on GENERIC  and not
    removed  the  USER_LDT  option.   The  most effective and simplest
    workaround,  for  users  not  requiring  this  option  for Wine or
    similar  emulators,  is  to   build  a  kernel  without   "options
    USER_LDT".  For users who need this option enabled, kernel sources
    must be updated and a new kernel built and installed.

    Systems running releases older than NetBSD 1.4 should be  upgraded
    to NetBSD 1.4.3 before applying the fixes described here.  Systems
    running  NetBSD-current  dated  from  before  2001-01-17 should be
    upgraded to  NetBSD-current dated  2001-01-17 or  later.   Systems
    running NetBSD-release-1-4 or NetBSD-release-1-5 dated from before
    2001-01-18 should be upgraded to 2001-01-18 later.

    The following patch to /sys/arch/i386/i386/sys_machdep.c should be
    applied  before  rebuilding  a  new  kernel with USER_LDT enabled.
    This  patch   can  be   applied  (with   offset  differences)   to
    NetBSD-1.4.x, NetBSD-1.5 or NetBSD-current kernel sources.

    Index: sys_machdep.c
    ===================================================================
    RCS file: /cvsroot/syssrc/sys/arch/i386/i386/sys_machdep.c,v
    retrieving revision 1.54
    retrieving revision 1.55
    diff -c -r1.54 -r1.55
    *** sys_machdep.c	2001/01/16 01:50:36	1.54
    --- sys_machdep.c	2001/01/16 23:32:21	1.55
    ***************
    *** 222,227 ****
    --- 222,238 ----
  			    break;
  		    case SDT_SYS286CGT:
  		    case SDT_SYS386CGT:
    + 			/*
    + 			 * Only allow call gates targeting a segment
    + 			 * in the LDT or a user segment in the fixed
    + 			 * part of the gdt.  Segments in the LDT are
    + 			 * constrained (below) to be user segments.
    + 			 */
    + 			if (desc.gd.gd_p != 0 && !ISLDT(desc.gd.gd_selector) &&
    + 			    ((IDXSEL(desc.gd.gd_selector) >= NGDT) ||
    + 			     (gdt[IDXSEL(desc.gd.gd_selector)].sd.sd_dpl !=
    + 				 SEL_UPL)))
    + 				return (EACCES);
  			    /* Can't replace in use descriptor with gate. */
  			    if (n == fsslot || n == gsslot)
  				    return (EBUSY);