[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Fwd: [patch] linux-2.4 Ext2/3 compatibility problem with EAs on symlinks



Herbert, I strongly believe that this patch should be included in all 2.4.x 
kernels at the earliest opportunity.  2.4.x kernels that don't have either 
this patch or the ACL patch will fail to boot on file systems that have been 
used with SE Linux.  This problem has already forced users to re-install 
systems.

The current situation is that anyone who once uses a file system with my new 
SE Linux packages or Fedora with SE Linux will be unable to boot a regular 
Debian 2.4.x kernel.

Also the problem is even more severe than it first appears, as many 
organizations are using Linux kernel source for the creation of boot loaders.  
Such boot loaders will refuse to boot from file systems that have been used 
with SE Linux.  A Sun/Cobalt developer has recently released boot ROM images 
with SE Linux which fix this problem (and also provide native SE Linux 
support in the ROM kernel image), unfortunately a bug in that ROM made it 
unusable for me (a new ROM is due out after Christmas which should fix this).

If you decide to apply this patch to the standard Debian kernel source tree 
(instead of the ACL patch which I have the impression you are currently 
considering) then please notify me and Yann ASAP as it will require that Yann 
change the kernel-patch-acl package and I change the kernel-patch-2.4-lsm 
package.

----------  Forwarded Message  ----------

Subject: [patch] linux-2.4 Ext2/3 compatibility problem with EAs on symlinks
Date: Tue, 23 Dec 2003 01:43
From: "Stephen C. Tweedie" <sct@redhat.com>
To: "ext2-devel@lists.sourceforge.net" <ext2-devel@lists.sourceforge.net>, SE 
Linux <selinux@tycho.nsa.gov>, ACL devel list <acl-devel@bestbits.at>
Cc: linux-kernel <linux-kernel@vger.kernel.org>, Stephen Tweedie 
<sct@redhat.com>, "Theodore Ts'o" <tytso@mit.edu>, Marcelo Tosatti 
<marcelo.tosatti@cyclades.com>

Hi all,

I found why people running SELinux on 2.6 were having trouble booting
again on 2.4 kernels, and it's nothing to do with SELinux per-se.  It's
a compatibility problem with extended attributes.

The trouble is that setting an EA on a fast symlink upsets the kernel's
symlink detection code.  Older kernels will see a symlink with non-zero
i_blocks, and will assume that the symlink is a slow one --- ie. that
the first direct block of the inode points to the symlink contents ---
and will end up doing a block lookup from what is in fact the first four
ascii chars from the symlink path.

2.6 kernels deal with this by detecting a non-zero i_file_acl field and
subtracting the EA's blocks from i_blocks before checking if the block
count is zero.  The patch below adds the same compatibility code to
2.4.  Booting a SELinux partition results in immediate death via access
beyond end-of-device as soon as the kernel tries to dereference /bin/sh
(a symlink to /bin/bash on this box); with the fix, it's fine.

Any kernel built with EA patches will be immune to this, even if the EA
config is not set.  So, 2.6 is simply not vulnerable.

There are a couple of questions left over.  First, should we be adding a
compatibility flag so that mounting filesystems with EAs on symlinks is
rejected on older kernels?  Secondly, there's the problem of getting EA
refcounts out-of-sync if you delete an inode with EAs on an older,
non-EA-aware kernel.  Even with the fix, the old kernel will still fail
to update the refcount on the EA block so we'd need a fsck to fix things
up; setting a readonly compatibility bit when EAs are present would
avoid that, but it's not clear whether the underlying problem is bad
enough to justify the hit in usability.

Cheers,
 Stephen

-------------------------------------------------------



-- 
http://www.coker.com.au/selinux/   My NSA Security Enhanced Linux packages
http://www.coker.com.au/bonnie++/  Bonnie++ hard drive benchmark
http://www.coker.com.au/postal/    Postal SMTP/POP benchmark
http://www.coker.com.au/~russell/  My home page
===== fs/ext3/inode.c 1.16 vs edited =====
--- linux-2.4-tmp/fs/ext2/inode.c.=K0000=.orig
+++ linux-2.4-tmp/fs/ext2/inode.c
@@ -35,6 +35,17 @@ MODULE_AUTHOR("Remy Card and others");
 MODULE_DESCRIPTION("Second Extended Filesystem");
 MODULE_LICENSE("GPL");
 
+/*
+ * Test whether an inode is a fast symlink.
+ */
+static inline int ext2_inode_is_fast_symlink(struct inode *inode)
+{
+	int ea_blocks = inode->u.ext2_i.i_file_acl ?
+		(inode->i_sb->s_blocksize >> 9) : 0;
+
+	return (S_ISLNK(inode->i_mode) &&
+		inode->i_blocks - ea_blocks == 0);
+}
 
 static int ext2_update_inode(struct inode * inode, int do_sync);
 
@@ -801,6 +812,8 @@ void ext2_truncate (struct inode * inode
 	if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
 	    S_ISLNK(inode->i_mode)))
 		return;
+	if (ext2_inode_is_fast_symlink(inode))
+		return;
 	if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
 		return;
 
@@ -1001,7 +1014,7 @@ void ext2_read_inode (struct inode * ino
 		inode->i_fop = &ext2_dir_operations;
 		inode->i_mapping->a_ops = &ext2_aops;
 	} else if (S_ISLNK(inode->i_mode)) {
-		if (!inode->i_blocks)
+		if (ext2_inode_is_fast_symlink(inode))
 			inode->i_op = &ext2_fast_symlink_inode_operations;
 		else {
 			inode->i_op = &page_symlink_inode_operations;
--- linux-2.4-tmp/fs/ext3/inode.c.=K0000=.orig
+++ linux-2.4-tmp/fs/ext3/inode.c
@@ -39,6 +39,18 @@
  */
 #undef SEARCH_FROM_ZERO
 
+/*
+ * Test whether an inode is a fast symlink.
+ */
+static inline int ext3_inode_is_fast_symlink(struct inode *inode)
+{
+	int ea_blocks = EXT3_I(inode)->i_file_acl ?
+		(inode->i_sb->s_blocksize >> 9) : 0;
+
+	return (S_ISLNK(inode->i_mode) &&
+		inode->i_blocks - ea_blocks == 0);
+}
+
 /* The ext3 forget function must perform a revoke if we are freeing data
  * which has been journaled.  Metadata (eg. indirect blocks) must be
  * revoked in all cases. 
@@ -1870,6 +1882,8 @@ void ext3_truncate(struct inode * inode)
 	if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
 	    S_ISLNK(inode->i_mode)))
 		return;
+	if (ext3_inode_is_fast_symlink(inode))
+		return;
 	if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
 		return;
 
@@ -2170,7 +2184,7 @@ void ext3_read_inode(struct inode * inod
 		inode->i_op = &ext3_dir_inode_operations;
 		inode->i_fop = &ext3_dir_operations;
 	} else if (S_ISLNK(inode->i_mode)) {
-		if (!inode->i_blocks)
+		if (ext3_inode_is_fast_symlink(inode))
 			inode->i_op = &ext3_fast_symlink_inode_operations;
 		else {
 			inode->i_op = &page_symlink_inode_operations;

Reply to: