Kernel Bugs
chroot() jail can be escaped
The chroot jail can be escaped, because the CWD isn't always preserved
on exec*(2) or fork(2). Take the following example program
and compile it with gcc -static chroot-test.c -o chroot-test.
#include
#include
#include
#include
int
main (void)
{
char buf[1024];
char *b;
int r;
struct stat st;
st.st_ino = 0;
pid_t pid;
b = getcwd (buf, sizeof (buf));
if (!b)
perror ("getcwd");
if (!b)
b = "NULL";
printf ("getcwd() = %s\n", b);
r = stat (".", &st);
printf ("stat(\".\") = %d, ino=%u\n", r, (unsigned) st.st_ino);
if (r)
perror ("stat");
r = stat (b, &st);
printf ("stat(\"%s\") = %d, ino=%u\n", b, r, (unsigned) st.st_ino);
if (r)
perror ("stat");
pid = fork ();
if (pid < 0)
{
perror ("fork error");
}
else if (pid == 0)
{
// child
r = stat (".", &st);
printf ("child: stat(\".\") = %d, ino=%u\n", r, (unsigned) st.st_ino);
if (r)
perror ("child: stat");
return;
}
// parent
r = stat (".", &st);
printf ("parent: stat(\".\") = %d, ino=%u\n", r, (unsigned) st.st_ino);
if (r)
perror ("parent: stat");
wait (NULL);
return;
}
Then create the following shell script:
#! /anydir/ksh
echo "testing in /"
cd /
echo PWD is `pwd`
/anydir/chroot-test
echo
echo "testing in /anydir"
cd /anydir
echo PWD is `pwd`
/anydir/chroot-test
Now do:
$ mkdir -p /tmp/chroot/anydir/
$ cp chroot-test start_test /bin/ksh /tmp/chroot/anydir/
$ /usr/sbin/chroot /tmp/chroot /anydir/start_test
For Interix 3.5 the output is similar to (line numbers added):
1 can't load iconv tables for codeset ASCII
2 testing in /
3 PWD is /
4 getcwd() = /
5 stat(".") = 0, ino=4174
6 chdir("/") = 0
7 stat(".") = 0, ino=531294
8 stat("/") = 0, ino=531294
9 child: stat(".") = 0, ino=531294
10 parent: stat(".") = 0, ino=531294
11
12 testing in /anydir
13 PWD is /anydir
14 getcwd() = /anydir
15 stat(".") = -1, ino=0
16 stat: No such file or directory
17 chdir("/anydir") = 0
18 stat(".") = 0, ino=446354
19 stat("/anydir") = 0, ino=446354
20 child: stat(".") = 0, ino=446354
21 parent: stat(".") = 0, ino=446354
You can see from line 5 and 15 above that stat(2) sees the
absolute root dir. A subsequent chdir(2) corrects
stat(2)'s idea of cwd (line 7 and 18).
As the bug is not in fork(2), see lines 9,10,20,21,
I suppose it's in exec(2).
For Interix 5.2 I saw this output:
1 $ /usr/sbin/chroot /tmp/chroot /anydir/start_test
2 testing in /
3 PWD is /
4 getcwd() = /
5 stat(".") = 0, ino=6606
6 stat("/") = 0, ino=10736
7 child: stat(".") = 0, ino=6606
8 parent: stat(".") = 0, ino=6606
9
10 testing in /anydir
11 PWD is /anydir
12 getcwd() = /anydir
13 stat(".") = -1, ino=0
14 stat: No such file or directory
15 stat("/anydir") = 0, ino=35992
16 parent: stat(".") = -1, ino=35992
17 parent: stat: No such file or directory
18 child: stat(".") = -1, ino=35992
19 child: stat: No such file or directory
Apparently, the bug is here in fork(2).
The inode of C:\Windows\SUA is 6606,
the inode of C:\Windows\SUA\tmp\chroot is 10736.
In line 7 and 8 you can see that both parent and child have left the chroot,
i.e. their cwd has changed to C:\Windows\SUA.
For line 16 and 18 the cwd is apparently believed to be
C:\Windows\SUA\anydir
(instead of the expected C:\Windows\SUA\tmp\chroot\anydir),
which doesn't exist.
For Interix 6.0:
testing in /
PWD is /
getcwd() = /
stat(".") = 0, ino=615222
stat("/") = 0, ino=615222
parent: stat(".") = 0, ino=615222
child: stat(".") = 0, ino=615222
testing in /anydir
PWD is /anydir
getcwd() = /anydir
stat(".") = 0, ino=615223
stat("/anydir") = 0, ino=615223
child: stat(".") = 0, ino=615223
parent: stat(".") = 0, ino=615223
Here everything looks as expected, so the bug seems to have been fixed with
this release of Interix.
see also:
http://www.interopsystems.com/tools/tm.aspx?m=11325#11374
http://www.interopsystems.com/tools/tm.aspx?m=13238
exec() isn't case sensitive
exec(2) isn't case sensitive. If in a directory there is an executable file
and a directory differing only in case, the file can not be executed.
In an otherwise empty directory execute this script:
#! /bin/sh
rm -f ./echo
rmdir ./ECHO
cp /bin/echo .
mkdir ECHO
./echo hello world 1
rm -f ./echo
rmdir ./ECHO
cp /bin/echo .
mkdir ECHO
./echo hello world 2
On Interix 3.5 the result is as expected:
hello world 1
hello world 2
But on Vista it fails:
casetest.sh: line 9: ./echo: Permission denied
casetest.sh: line 17: ./echo: Permission denied
fstat() and unnamed pipe file descriptors
fstat(2) returns st_mode==0 and st_nlink==0
for an unnamed pipe. So S_ISFIFO() is not true for an unnamed
pipe file descriptor.
lstat(): symlinks are not always recognized correctly
Create or locate any Win32 file with the following properties:
- The file has the SYSTEM attribute set
- The file size is > 10 Bytes
- The file size is an ODD number of bytes (say 11, 13, or 15 bytes)
Then execute stat on the file, e.g. C:\boot.ini:
$ stat /dev/fs/C/boot.ini
stat: lstat failed(/dev/fs/C/boot.ini): Bad address
see also:
http://www.interopsystems.com/tools/tm.aspx?m=11002
sendmsg()/recvmsg() are missing
On SFU 3.5 the syscalls sendmsg(2) and recvmsg(2)
are missing.
see:
http://msdn2.microsoft.com/en-us/library/ms811897.aspx
see also:
http://www.interopsystems.com/tools/tm.aspx?m=4891
unlink() cannot unlink some symlinks
Look at the following shell snippet:
$ cd /tmp
$ ln -s /tmp/nonexistent.dir/../foo bar
$ ls -l bar
lr--r--r-- 1 ... ... 27 Jul 16 11:40 bar -> /tmp/nonexistent.dir/../foo
$ rm bar
rm: bar: No such file or directory
$ mkdir nonexistent.dir
$ rm bar
(now successful)
Also unlink(2) cannot unlink symlinks to non-existent dirs,
when the symlink contains a trailing backslash:
$ cd /tmp
$ ln -s /tmp/nonexistent.dir/ foo
lr--r--r-- 1 ... ... 21 Jul 16 11:41 foo -> /tmp/nonexistent.dir/
$ rm foo
rm: foo: No such file or directory
$ mkdir nonexistent.dir
$ rm foo
(now successful)
see also:
http://www.interopsystems.com/tools/tm.aspx?m=12227
select(0, ...) fails
select(2) has a bug, making it fail with "bad address"
if the number of fd's to select on is zero. Setting the
set-size to 1 solves the problem.
see also:
http://thread.gmane.org/gmane.comp.gnu.coreutils.bugs/16077
Bug database
Debian Interix home
Last update of this document: Sunday, 26-Jul-2009 19:48:08 CEST
Copyright 2007-2009 Martin Köppe <mkoeppe 'at' gmx . de>