Commit 0fbcb525 authored by Eric Biggers's avatar Eric Biggers Committed by Theodore Ts'o
Browse files

ext4: disable fast-commit of encrypted dir operations



fast-commit of create, link, and unlink operations in encrypted
directories is completely broken because the unencrypted filenames are
being written to the fast-commit journal instead of the encrypted
filenames.  These operations can't be replayed, as encryption keys
aren't present at journal replay time.  It is also an information leak.

Until if/when we can get this working properly, make encrypted directory
operations ineligible for fast-commit.

Note that fast-commit operations on encrypted regular files continue to
be allowed, as they seem to work.

Fixes: aa75f4d3 ("ext4: main fast-commit commit path")
Cc: <stable@vger.kernel.org> # v5.10+
Signed-off-by: default avatarEric Biggers <ebiggers@google.com>
Link: https://lore.kernel.org/r/20221106224841.279231-2-ebiggers@kernel.org


Signed-off-by: default avatarTheodore Ts'o <tytso@mit.edu>
parent a71248b1
Loading
Loading
Loading
Loading
+25 −16
Original line number Diff line number Diff line
@@ -420,25 +420,34 @@ static int __track_dentry_update(struct inode *inode, void *arg, bool update)
	struct __track_dentry_update_args *dentry_update =
		(struct __track_dentry_update_args *)arg;
	struct dentry *dentry = dentry_update->dentry;
	struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
	struct inode *dir = dentry->d_parent->d_inode;
	struct super_block *sb = inode->i_sb;
	struct ext4_sb_info *sbi = EXT4_SB(sb);

	mutex_unlock(&ei->i_fc_lock);

	if (IS_ENCRYPTED(dir)) {
		ext4_fc_mark_ineligible(sb, EXT4_FC_REASON_ENCRYPTED_FILENAME,
					NULL);
		mutex_lock(&ei->i_fc_lock);
		return -EOPNOTSUPP;
	}

	node = kmem_cache_alloc(ext4_fc_dentry_cachep, GFP_NOFS);
	if (!node) {
		ext4_fc_mark_ineligible(inode->i_sb, EXT4_FC_REASON_NOMEM, NULL);
		ext4_fc_mark_ineligible(sb, EXT4_FC_REASON_NOMEM, NULL);
		mutex_lock(&ei->i_fc_lock);
		return -ENOMEM;
	}

	node->fcd_op = dentry_update->op;
	node->fcd_parent = dentry->d_parent->d_inode->i_ino;
	node->fcd_parent = dir->i_ino;
	node->fcd_ino = inode->i_ino;
	if (dentry->d_name.len > DNAME_INLINE_LEN) {
		node->fcd_name.name = kmalloc(dentry->d_name.len, GFP_NOFS);
		if (!node->fcd_name.name) {
			kmem_cache_free(ext4_fc_dentry_cachep, node);
			ext4_fc_mark_ineligible(inode->i_sb,
				EXT4_FC_REASON_NOMEM, NULL);
			ext4_fc_mark_ineligible(sb, EXT4_FC_REASON_NOMEM, NULL);
			mutex_lock(&ei->i_fc_lock);
			return -ENOMEM;
		}
@@ -2249,17 +2258,17 @@ void ext4_fc_init(struct super_block *sb, journal_t *journal)
	journal->j_fc_cleanup_callback = ext4_fc_cleanup;
}

static const char *fc_ineligible_reasons[] = {
	"Extended attributes changed",
	"Cross rename",
	"Journal flag changed",
	"Insufficient memory",
	"Swap boot",
	"Resize",
	"Dir renamed",
	"Falloc range op",
	"Data journalling",
	"FC Commit Failed"
static const char * const fc_ineligible_reasons[] = {
	[EXT4_FC_REASON_XATTR] = "Extended attributes changed",
	[EXT4_FC_REASON_CROSS_RENAME] = "Cross rename",
	[EXT4_FC_REASON_JOURNAL_FLAG_CHANGE] = "Journal flag changed",
	[EXT4_FC_REASON_NOMEM] = "Insufficient memory",
	[EXT4_FC_REASON_SWAP_BOOT] = "Swap boot",
	[EXT4_FC_REASON_RESIZE] = "Resize",
	[EXT4_FC_REASON_RENAME_DIR] = "Dir renamed",
	[EXT4_FC_REASON_FALLOC_RANGE] = "Falloc range op",
	[EXT4_FC_REASON_INODE_JOURNAL_DATA] = "Data journalling",
	[EXT4_FC_REASON_ENCRYPTED_FILENAME] = "Encrypted filename",
};

int ext4_fc_info_show(struct seq_file *seq, void *v)
+1 −0
Original line number Diff line number Diff line
@@ -96,6 +96,7 @@ enum {
	EXT4_FC_REASON_RENAME_DIR,
	EXT4_FC_REASON_FALLOC_RANGE,
	EXT4_FC_REASON_INODE_JOURNAL_DATA,
	EXT4_FC_REASON_ENCRYPTED_FILENAME,
	EXT4_FC_REASON_MAX
};

+5 −2
Original line number Diff line number Diff line
@@ -104,6 +104,7 @@ TRACE_DEFINE_ENUM(EXT4_FC_REASON_RESIZE);
TRACE_DEFINE_ENUM(EXT4_FC_REASON_RENAME_DIR);
TRACE_DEFINE_ENUM(EXT4_FC_REASON_FALLOC_RANGE);
TRACE_DEFINE_ENUM(EXT4_FC_REASON_INODE_JOURNAL_DATA);
TRACE_DEFINE_ENUM(EXT4_FC_REASON_ENCRYPTED_FILENAME);
TRACE_DEFINE_ENUM(EXT4_FC_REASON_MAX);

#define show_fc_reason(reason)						\
@@ -116,7 +117,8 @@ TRACE_DEFINE_ENUM(EXT4_FC_REASON_MAX);
		{ EXT4_FC_REASON_RESIZE,	"RESIZE"},		\
		{ EXT4_FC_REASON_RENAME_DIR,	"RENAME_DIR"},		\
		{ EXT4_FC_REASON_FALLOC_RANGE,	"FALLOC_RANGE"},	\
		{ EXT4_FC_REASON_INODE_JOURNAL_DATA,	"INODE_JOURNAL_DATA"})
		{ EXT4_FC_REASON_INODE_JOURNAL_DATA,	"INODE_JOURNAL_DATA"}, \
		{ EXT4_FC_REASON_ENCRYPTED_FILENAME,	"ENCRYPTED_FILENAME"})

TRACE_EVENT(ext4_other_inode_update_time,
	TP_PROTO(struct inode *inode, ino_t orig_ino),
@@ -2799,7 +2801,7 @@ TRACE_EVENT(ext4_fc_stats,
	),

	TP_printk("dev %d,%d fc ineligible reasons:\n"
		  "%s:%u, %s:%u, %s:%u, %s:%u, %s:%u, %s:%u, %s:%u, %s:%u, %s:%u "
		  "%s:%u, %s:%u, %s:%u, %s:%u, %s:%u, %s:%u, %s:%u, %s:%u, %s:%u, %s:%u"
		  "num_commits:%lu, ineligible: %lu, numblks: %lu",
		  MAJOR(__entry->dev), MINOR(__entry->dev),
		  FC_REASON_NAME_STAT(EXT4_FC_REASON_XATTR),
@@ -2811,6 +2813,7 @@ TRACE_EVENT(ext4_fc_stats,
		  FC_REASON_NAME_STAT(EXT4_FC_REASON_RENAME_DIR),
		  FC_REASON_NAME_STAT(EXT4_FC_REASON_FALLOC_RANGE),
		  FC_REASON_NAME_STAT(EXT4_FC_REASON_INODE_JOURNAL_DATA),
		  FC_REASON_NAME_STAT(EXT4_FC_REASON_ENCRYPTED_FILENAME),
		  __entry->fc_commits, __entry->fc_ineligible_commits,
		  __entry->fc_numblks)
);