Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d375fed0 authored by Theodore Ts'o's avatar Theodore Ts'o Committed by Jaegeuk Kim
Browse files

ext4: fix kernel oops caused by spurious casefold flag



If an directory has the a casefold flag set without the casefold
feature set, s_encoding will not be initialized, and this will cause
the kernel to dereference a NULL pointer.  In addition to adding
checks to avoid these kernel oops, attempts to load inodes with the
casefold flag when the casefold feature is not enable will cause the
file system to be declared corrupted.

[Jaegeuk Kim: use EXT4_ERROR_INODE]
Signed-off-by: default avatarTheodore Ts'o <tytso@mit.edu>
parent ad37c94d
Loading
Loading
Loading
Loading
+4 −3
Original line number Diff line number Diff line
@@ -667,14 +667,15 @@ static int ext4_d_compare(const struct dentry *dentry, unsigned int len,
			  const char *str, const struct qstr *name)
{
	struct qstr qstr = {.name = str, .len = len };
	struct inode *inode = dentry->d_parent->d_inode;

	if (!IS_CASEFOLDED(dentry->d_parent->d_inode)) {
	if (!IS_CASEFOLDED(inode) || !EXT4_SB(inode->i_sb)->s_encoding) {
		if (len != name->len)
			return -1;
		return memcmp(str, name->name, len);
	}

	return ext4_ci_compare(dentry->d_parent->d_inode, name, &qstr, false);
	return ext4_ci_compare(inode, name, &qstr, false);
}

static int ext4_d_hash(const struct dentry *dentry, struct qstr *str)
@@ -684,7 +685,7 @@ static int ext4_d_hash(const struct dentry *dentry, struct qstr *str)
	unsigned char *norm;
	int len, ret = 0;

	if (!IS_CASEFOLDED(dentry->d_inode))
	if (!IS_CASEFOLDED(dentry->d_inode) || !um)
		return 0;

	norm = kmalloc(PATH_MAX, GFP_ATOMIC);
+1 −1
Original line number Diff line number Diff line
@@ -278,7 +278,7 @@ int ext4fs_dirhash(const struct inode *dir, const char *name, int len,
	unsigned char *buff;
	struct qstr qstr = {.name = name, .len = len };

	if (len && IS_CASEFOLDED(dir)) {
	if (len && IS_CASEFOLDED(dir) && um) {
		buff = kzalloc(sizeof(char) * PATH_MAX, GFP_KERNEL);
		if (!buff)
			return -ENOMEM;
+3 −0
Original line number Diff line number Diff line
@@ -5042,6 +5042,9 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino)
		EXT4_ERROR_INODE(inode, "bogus i_mode (%o)", inode->i_mode);
		goto bad_inode;
	}
	if (IS_CASEFOLDED(inode) && !ext4_has_feature_casefold(inode->i_sb))
		EXT4_ERROR_INODE(inode,
				 "casefold flag without casefold feature");
	brelse(iloc.bh);

	unlock_new_inode(inode);
+2 −2
Original line number Diff line number Diff line
@@ -1294,7 +1294,7 @@ void ext4_fname_setup_ci_filename(struct inode *dir, const struct qstr *iname,
{
	int len;

	if (!IS_CASEFOLDED(dir)) {
	if (!IS_CASEFOLDED(dir) || !EXT4_SB(dir->i_sb)->s_encoding) {
		cf_name->name = NULL;
		return;
	}
@@ -2175,7 +2175,7 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry,

#ifdef CONFIG_UNICODE
	if (ext4_has_strict_mode(sbi) && IS_CASEFOLDED(dir) &&
	    utf8_validate(sbi->s_encoding, &dentry->d_name))
	    sbi->s_encoding && utf8_validate(sbi->s_encoding, &dentry->d_name))
		return -EINVAL;
#endif