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 <stdio.h> #include <sys/stat.h> #include <errno.h> #include <unistd.h> 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: 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>