diff -Nrp linux-2.6.16.53/fs/namei.c linux-2.6.16.53-new/fs/namei.c *** linux-2.6.16.53/fs/namei.c 2007-07-25 23:05:45.000000000 +0200 --- linux-2.6.16.53-new/fs/namei.c 2007-09-15 16:13:50.000000000 +0200 *************** static __always_inline void follow_dotdo *** 728,733 **** --- 728,772 ---- } follow_mount(&nd->mnt, &nd->dentry); } + long directory_is_out(struct vfsmount *wdmnt, struct dentry *wdentry, + struct vfsmount *rootmnt, struct dentry *root) + { + struct nameidata oldentry, newentry; + long ret = 1; + + read_lock(¤t->fs->lock); + oldentry.dentry = dget(wdentry); + oldentry.mnt = mntget(wdmnt); + read_unlock(¤t->fs->lock); + newentry.dentry = oldentry.dentry; + newentry.mnt = oldentry.mnt; + + follow_dotdot(&newentry); + /* check it */ + if(newentry.dentry == root && + newentry.mnt == rootmnt){ + ret = 0; + goto out; + } + + while(oldentry.mnt != newentry.mnt || + oldentry.dentry != newentry.dentry){ + + memcpy(&oldentry, &newentry, sizeof(struct nameidata)); + follow_dotdot(&newentry); + + /* check it */ + if(newentry.dentry == root && + newentry.mnt == rootmnt){ + ret = 0; + goto out; + } + } + out: + dput(newentry.dentry); + mntput(newentry.mnt); + return ret; + } /* * It's more convoluted than I'd like it to be, but... it's still fairly diff -Nrp linux-2.6.16.53/fs/open.c linux-2.6.16.53-new/fs/open.c *** linux-2.6.16.53/fs/open.c 2007-07-25 23:05:45.000000000 +0200 --- linux-2.6.16.53-new/fs/open.c 2007-09-15 16:14:52.000000000 +0200 *************** dput_and_out: *** 560,565 **** --- 560,567 ---- out: return error; } + long directory_is_out(struct vfsmount *, struct dentry*, + struct vfsmount *, struct dentry *); asmlinkage long sys_fchdir(unsigned int fd) { *************** asmlinkage long sys_fchdir(unsigned int *** 581,586 **** --- 583,591 ---- error = -ENOTDIR; if (!S_ISDIR(inode->i_mode)) goto out_putf; + if (directory_is_out(mnt, dentry, current->fs->rootmnt, + current->fs->root)) + goto out_putf; error = file_permission(file, MAY_EXEC); if (!error) *************** out: *** 594,600 **** asmlinkage long sys_chroot(const char __user * filename) { struct nameidata nd; ! int error; error = __user_walk(filename, LOOKUP_FOLLOW | LOOKUP_DIRECTORY | LOOKUP_NOALT, &nd); if (error) --- 599,605 ---- asmlinkage long sys_chroot(const char __user * filename) { struct nameidata nd; ! int error, set_wd = 0; error = __user_walk(filename, LOOKUP_FOLLOW | LOOKUP_DIRECTORY | LOOKUP_NOALT, &nd); if (error) *************** asmlinkage long sys_chroot(const char __ *** 607,615 **** --- 612,631 ---- error = -EPERM; if (!capable(CAP_SYS_CHROOT)) goto dput_and_out; + error = -ENOTDIR; + /* + if (directory_is_out(nd.mnt, nd.dentry, current->fs->rootmnt, + current->fs->root)) + goto dput_and_out; + */ + set_wd = directory_is_out(current->fs->pwdmnt, current->fs->pwd, + nd.mnt, nd.dentry); set_fs_root(current->fs, nd.mnt, nd.dentry); set_fs_altroot(); + /* if wd is out, reset it to . */ + if(set_wd) + set_fs_pwd(current->fs, nd.mnt, nd.dentry); error = 0; dput_and_out: path_release(&nd);