Commit a0c51565 authored by Zhihao Cheng's avatar Zhihao Cheng Committed by Richard Weinberger
Browse files

ubifs: Fix AA deadlock when setting xattr for encrypted file

Following process:
vfs_setxattr(host)
  ubifs_xattr_set
    down_write(host_ui->xattr_sem)   <- lock first time
      create_xattr
        ubifs_new_inode(host)
          fscrypt_prepare_new_inode(host)
            fscrypt_policy_to_inherit(host)
              if (IS_ENCRYPTED(inode))
                fscrypt_require_key(host)
                  fscrypt_get_encryption_info(host)
                    ubifs_xattr_get(host)
                      down_read(host_ui->xattr_sem) <- AA deadlock

, which may trigger an AA deadlock problem:

[  102.620871] INFO: task setfattr:1599 blocked for more than 10 seconds.
[  102.625298]       Not tainted 5.19.0-rc7-00001-gb666b6823ce0-dirty #711
[  102.628732] task:setfattr        state:D stack:    0 pid: 1599
[  102.628749] Call Trace:
[  102.628753]  <TASK>
[  102.628776]  __schedule+0x482/0x1060
[  102.629964]  schedule+0x92/0x1a0
[  102.629976]  rwsem_down_read_slowpath+0x287/0x8c0
[  102.629996]  down_read+0x84/0x170
[  102.630585]  ubifs_xattr_get+0xd1/0x370 [ubifs]
[  102.630730]  ubifs_crypt_get_context+0x1f/0x30 [ubifs]
[  102.630791]  fscrypt_get_encryption_info+0x7d/0x1c0
[  102.630810]  fscrypt_policy_to_inherit+0x56/0xc0
[  102.630817]  fscrypt_prepare_new_inode+0x35/0x160
[  102.630830]  ubifs_new_inode+0xcc/0x4b0 [ubifs]
[  102.630873]  ubifs_xattr_set+0x591/0x9f0 [ubifs]
[  102.630961]  xattr_set+0x8c/0x3e0 [ubifs]
[  102.631003]  __vfs_setxattr+0x71/0xc0
[  102.631026]  vfs_setxattr+0x105/0x270
[  102.631034]  do_setxattr+0x6d/0x110
[  102.631041]  setxattr+0xa0/0xd0
[  102.631087]  __x64_sys_setxattr+0x2f/0x40

Fetch a reproducer in [Link].

Just like ext4 does, which skips encrypting for inode with
EXT4_EA_INODE_FL flag. Stop encypting xattr inode for ubifs.

Link: https://bugzilla.kernel.org/show_bug.cgi?id=216260


Fixes: f4e3634a ("ubifs: Fix races between xattr_{set|get} ...")
Fixes: d475a507 ("ubifs: Add skeleton for fscrypto")
Signed-off-by: default avatarZhihao Cheng <chengzhihao1@huawei.com>
Signed-off-by: default avatarRichard Weinberger <richard@nod.at>
parent 713346ca
Loading
Loading
Loading
Loading
+14 −11
Original line number Diff line number Diff line
@@ -68,13 +68,14 @@ static int inherit_flags(const struct inode *dir, umode_t mode)
 * @c: UBIFS file-system description object
 * @dir: parent directory inode
 * @mode: inode mode flags
 * @is_xattr: whether the inode is xattr inode
 *
 * This function finds an unused inode number, allocates new inode and
 * initializes it. Returns new inode in case of success and an error code in
 * case of failure.
 */
struct inode *ubifs_new_inode(struct ubifs_info *c, struct inode *dir,
			      umode_t mode)
			      umode_t mode, bool is_xattr)
{
	int err;
	struct inode *inode;
@@ -99,11 +100,13 @@ struct inode *ubifs_new_inode(struct ubifs_info *c, struct inode *dir,
			 current_time(inode);
	inode->i_mapping->nrpages = 0;

	if (!is_xattr) {
		err = fscrypt_prepare_new_inode(dir, inode, &encrypted);
		if (err) {
			ubifs_err(c, "fscrypt_prepare_new_inode failed: %i", err);
			goto out_iput;
		}
	}

	switch (mode & S_IFMT) {
	case S_IFREG:
@@ -309,7 +312,7 @@ static int ubifs_create(struct user_namespace *mnt_userns, struct inode *dir,

	sz_change = CALC_DENT_SIZE(fname_len(&nm));

	inode = ubifs_new_inode(c, dir, mode);
	inode = ubifs_new_inode(c, dir, mode, false);
	if (IS_ERR(inode)) {
		err = PTR_ERR(inode);
		goto out_fname;
@@ -370,7 +373,7 @@ static struct inode *create_whiteout(struct inode *dir, struct dentry *dentry)
	if (err)
		return ERR_PTR(err);

	inode = ubifs_new_inode(c, dir, mode);
	inode = ubifs_new_inode(c, dir, mode, false);
	if (IS_ERR(inode)) {
		err = PTR_ERR(inode);
		goto out_free;
@@ -462,7 +465,7 @@ static int ubifs_tmpfile(struct user_namespace *mnt_userns, struct inode *dir,
		return err;
	}

	inode = ubifs_new_inode(c, dir, mode);
	inode = ubifs_new_inode(c, dir, mode, false);
	if (IS_ERR(inode)) {
		err = PTR_ERR(inode);
		goto out_budg;
@@ -1004,7 +1007,7 @@ static int ubifs_mkdir(struct user_namespace *mnt_userns, struct inode *dir,

	sz_change = CALC_DENT_SIZE(fname_len(&nm));

	inode = ubifs_new_inode(c, dir, S_IFDIR | mode);
	inode = ubifs_new_inode(c, dir, S_IFDIR | mode, false);
	if (IS_ERR(inode)) {
		err = PTR_ERR(inode);
		goto out_fname;
@@ -1091,7 +1094,7 @@ static int ubifs_mknod(struct user_namespace *mnt_userns, struct inode *dir,

	sz_change = CALC_DENT_SIZE(fname_len(&nm));

	inode = ubifs_new_inode(c, dir, mode);
	inode = ubifs_new_inode(c, dir, mode, false);
	if (IS_ERR(inode)) {
		kfree(dev);
		err = PTR_ERR(inode);
@@ -1173,7 +1176,7 @@ static int ubifs_symlink(struct user_namespace *mnt_userns, struct inode *dir,

	sz_change = CALC_DENT_SIZE(fname_len(&nm));

	inode = ubifs_new_inode(c, dir, S_IFLNK | S_IRWXUGO);
	inode = ubifs_new_inode(c, dir, S_IFLNK | S_IRWXUGO, false);
	if (IS_ERR(inode)) {
		err = PTR_ERR(inode);
		goto out_fname;
+1 −1
Original line number Diff line number Diff line
@@ -2026,7 +2026,7 @@ int ubifs_update_time(struct inode *inode, struct timespec64 *time, int flags);

/* dir.c */
struct inode *ubifs_new_inode(struct ubifs_info *c, struct inode *dir,
			      umode_t mode);
			      umode_t mode, bool is_xattr);
int ubifs_getattr(struct user_namespace *mnt_userns, const struct path *path, struct kstat *stat,
		  u32 request_mask, unsigned int flags);
int ubifs_check_dir_empty(struct inode *dir);
+1 −1
Original line number Diff line number Diff line
@@ -110,7 +110,7 @@ static int create_xattr(struct ubifs_info *c, struct inode *host,
	if (err)
		return err;

	inode = ubifs_new_inode(c, host, S_IFREG | S_IRWXUGO);
	inode = ubifs_new_inode(c, host, S_IFREG | S_IRWXUGO, true);
	if (IS_ERR(inode)) {
		err = PTR_ERR(inode);
		goto out_budg;