[PATCH 3/3] reiserfs: fix crashes in extended attributes implementation

From: Eugene Kapun
Date: Thu Jul 09 2009 - 14:05:53 EST


From: Eugene Kapun <abacabadabacaba@xxxxxxxxx>

Fixes crashes when:
* .reiserfs_priv exists and is not a directory
* .reiserfs_priv/xattrs exists and is not a directory
* .reiserfs_prix/xattrs/<something> exists and is not a directory
* .reiserfs_priv/xattrs/<something>/<something> exists and is not a
regular file
Signed-off-by: Eugene Kapun <abacabadabacaba@xxxxxxxxx>
---
This patch applies to linux-2.6.31-rc2-git4.
This patch depends on patch 2/3 (reiserfs: add mount option that
disables extended attributes support).
This patch prevents reiserfs from crashing if certain directories are
actually files or something else, and vice versa. Instead, it will
refuse to mount such filesystem or abort operations on files with -EIO.

fs/reiserfs/super.c | 53 ++++++++++++++++++++++--------------------
fs/reiserfs/xattr.c | 44 +++++++++++++++++++++++++++-------
2 files changed, 63 insertions(+), 34 deletions(-)

diff -uprN linux-2.6.31-rc2-git3.2/fs/reiserfs/super.c linux-2.6.31-rc2-git3.3/fs/reiserfs/super.c
--- linux-2.6.31-rc2-git3.2/fs/reiserfs/super.c 2009-07-09 15:03:10.000000000 +0400
+++ linux-2.6.31-rc2-git3.3/fs/reiserfs/super.c 2009-07-09 13:57:15.000000000 +0400
@@ -1762,11 +1762,8 @@ static int reiserfs_fill_super(struct su
}
// define and initialize hash function
sbi->s_hash_function = hash_function(s);
- if (sbi->s_hash_function == NULL) {
- dput(s->s_root);
- s->s_root = NULL;
- goto error;
- }
+ if (sbi->s_hash_function == NULL)
+ goto error_root;

if (is_reiserfs_3_5(rs)
|| (is_reiserfs_jr(rs) && SB_VERSION(s) == REISERFS_VERSION_1))
@@ -1779,11 +1776,8 @@ static int reiserfs_fill_super(struct su
if (!(s->s_flags & MS_RDONLY)) {

errval = journal_begin(&th, s, 1);
- if (errval) {
- dput(s->s_root);
- s->s_root = NULL;
- goto error;
- }
+ if (errval)
+ goto error_root;
reiserfs_prepare_for_journal(s, SB_BUFFER_WITH_SB(s), 1);

set_sb_umount_state(rs, REISERFS_ERROR_FS);
@@ -1834,18 +1828,12 @@ static int reiserfs_fill_super(struct su

journal_mark_dirty(&th, s, SB_BUFFER_WITH_SB(s));
errval = journal_end(&th, s, 1);
- if (errval) {
- dput(s->s_root);
- s->s_root = NULL;
- goto error;
- }
+ if (errval)
+ goto error_root;

if ((errval = reiserfs_lookup_privroot(s)) ||
- (errval = reiserfs_xattr_init(s, s->s_flags))) {
- dput(s->s_root);
- s->s_root = NULL;
- goto error;
- }
+ (errval = reiserfs_xattr_init(s, s->s_flags)))
+ goto error_privroot;

/* look for files which were to be removed in previous session */
finish_unfinished(s);
@@ -1855,11 +1843,8 @@ static int reiserfs_fill_super(struct su
}

if ((errval = reiserfs_lookup_privroot(s)) ||
- (errval = reiserfs_xattr_init(s, s->s_flags))) {
- dput(s->s_root);
- s->s_root = NULL;
- goto error;
- }
+ (errval = reiserfs_xattr_init(s, s->s_flags)))
+ goto error_privroot;
}
// mark hash in super block: it could be unset. overwrite should be ok
set_sb_hash_function_code(rs, function2code(sbi->s_hash_function));
@@ -1873,6 +1858,24 @@ static int reiserfs_fill_super(struct su

return (0);

+error_privroot:
+#ifdef CONFIG_REISERFS_FS_EXTENDED
+ if (REISERFS_SB(s)->priv_root) {
+ if (REISERFS_SB(s)->xattr_root) {
+ d_invalidate(REISERFS_SB(s)->xattr_root);
+ dput(REISERFS_SB(s)->xattr_root);
+ REISERFS_SB(s)->xattr_root = NULL;
+ }
+ d_invalidate(REISERFS_SB(s)->priv_root);
+ dput(REISERFS_SB(s)->priv_root);
+ REISERFS_SB(s)->priv_root = NULL;
+ }
+#endif
+
+error_root:
+ dput(s->s_root);
+ s->s_root = NULL;
+
error:
if (jinit_done) { /* kill the commit thread, free journal ram */
journal_release_error(NULL, s);
diff -uprN linux-2.6.31-rc2-git3.2/fs/reiserfs/xattr.c linux-2.6.31-rc2-git3.3/fs/reiserfs/xattr.c
--- linux-2.6.31-rc2-git3.2/fs/reiserfs/xattr.c 2009-07-09 15:06:12.000000000 +0400
+++ linux-2.6.31-rc2-git3.3/fs/reiserfs/xattr.c 2009-07-09 13:57:15.000000000 +0400
@@ -155,10 +155,17 @@ static struct dentry *open_xa_dir(const
mutex_lock_nested(&xaroot->d_inode->i_mutex, I_MUTEX_XATTR);

xadir = lookup_one_len(namebuf, xaroot, strlen(namebuf));
- if (!IS_ERR(xadir) && !xadir->d_inode) {
- int err = -ENODATA;
- if (xattr_may_create(flags))
- err = xattr_mkdir(xaroot->d_inode, xadir, 0700);
+ if (!IS_ERR(xadir)) {
+ int err;
+ if (xadir->d_inode) {
+ err = 0;
+ if (!S_ISDIR(xadir->d_inode->i_mode))
+ err = -EIO;
+ } else {
+ err = -ENODATA;
+ if (xattr_may_create(flags))
+ err = xattr_mkdir(xaroot->d_inode, xadir, 0700);
+ }
if (err) {
dput(xadir);
xadir = ERR_PTR(err);
@@ -372,7 +379,8 @@ static struct dentry *xattr_lookup(struc
if (xattr_may_create(flags))
err = xattr_create(xadir->d_inode, xafile,
0700|S_IFREG);
- }
+ } else if (!S_ISREG(xafile->d_inode->i_mode))
+ err = -EIO;

if (err)
dput(xafile);
@@ -992,8 +1000,17 @@ int reiserfs_lookup_privroot(struct supe
REISERFS_SB(s)->priv_root = dentry;
if (!reiserfs_expose_privroot(s))
s->s_root->d_op = &xattr_lookup_poison_ops;
- if (dentry->d_inode)
- dentry->d_inode->i_flags |= S_PRIVATE;
+ if (dentry->d_inode) {
+ if (S_ISDIR(dentry->d_inode->i_mode)) {
+ dentry->d_inode->i_flags |= S_PRIVATE;
+ } else {
+ reiserfs_warning(s, NULL,
+ PRIVROOT_NAME " exists and "
+ "is not a directory. Remount "
+ "with -o noext to fix this");
+ err = -EINVAL;
+ }
+ }
} else
err = PTR_ERR(dentry);
mutex_unlock(&s->s_root->d_inode->i_mutex);
@@ -1028,9 +1045,18 @@ int reiserfs_xattr_init(struct super_blo
struct dentry *dentry;
dentry = lookup_one_len(XAROOT_NAME, privroot,
strlen(XAROOT_NAME));
- if (!IS_ERR(dentry))
+ if (!IS_ERR(dentry)) {
REISERFS_SB(s)->xattr_root = dentry;
- else
+ if (dentry->d_inode &&
+ !S_ISDIR(dentry->d_inode->i_mode)) {
+ reiserfs_warning(s, NULL,
+ PRIVROOT_NAME "/" XAROOT_NAME
+ " exists and is not "
+ "a directory. Remount with "
+ "-o noext to fix this");
+ err = -EINVAL;
+ }
+ } else
err = PTR_ERR(dentry);
}
mutex_unlock(&privroot->d_inode->i_mutex);


--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/