[PATCH RFC] ocfs2: fix circular locking dependency in ocfs2_mknod()
"syzbot" <[email protected]>
| Newsgroups | dev.linux.lists.syzbot |
|---|---|
| Message-ID | <[email protected]> |
A circular locking dependency was detected involving three locks:
ip_alloc_sem, j_trans_barrier (the journal transaction barrier), and
ip_xattr_sem.
======================================================
WARNING: possible circular locking dependency detected
Chain exists of:
&oi->ip_alloc_sem --> &journal->j_trans_barrier --> &oi->ip_xattr_sem
Possible unsafe locking scenario:
CPU0 CPU1
lock(&oi->ip_xattr_sem);
lock(&journal->j_trans_barrier);
lock(&oi->ip_xattr_sem);
lock(&oi->ip_alloc_sem);
*** DEADLOCK ***
======================================================
The lock inversion occurs in ocfs2_mknod() -> ocfs2_init_acl().
ocfs2_mknod() starts a transaction via ocfs2_start_trans(), acquiring
j_trans_barrier. While holding this transaction handle, it calls
ocfs2_init_acl(), which acquires ip_xattr_sem to read the default ACL of
the parent directory. This violates the strict OCFS2 lock ordering rule
that inode semaphores (ip_alloc_sem, ip_xattr_sem, etc.) must be acquired
before starting a transaction.
To resolve this, fetch the default ACL before starting the transaction in
ocfs2_mknod() and pass it down to ocfs2_init_acl(). We use get_inode_acl()
to retrieve the ACL, which safely handles recursive cluster locking and
correctly takes ip_xattr_sem before any transaction is started. The same
logic is applied to ocfs2_init_security_and_acl(), the only other caller of
ocfs2_init_acl().
Fixes: 16c8d569f570 ("ocfs2/acl: use 'ip_xattr_sem' to protect getting extended attribute")
Assisted-by: Gemini:gemini-3.6-flash Gemini:gemini-3.1-pro-preview syzbot
Reported-by: [email protected]
Closes: https://syzkaller.appspot.com/bug?extid=1fed2de07d8e11a3ec1b
Link: https://syzkaller.appspot.com/ai_job?id=22bc5699-2231-4026-98db-8f9510b94925
To: "Joel Becker" <[email protected]>
To: "Joseph Qi" <[email protected]>
To: "Mark Fasheh" <[email protected]>
To: <[email protected]>
To: "piaojun" <[email protected]>
Cc: <[email protected]>
---
diff --git a/fs/ocfs2/acl.c b/fs/ocfs2/acl.c
index af1e2cedb..a16386c23 100644
--- a/fs/ocfs2/acl.c
+++ b/fs/ocfs2/acl.c
@@ -353,7 +353,7 @@ int ocfs2_init_acl(handle_t *handle,
struct inode *inode,
struct inode *dir,
struct buffer_head *di_bh,
- struct buffer_head *dir_bh,
+ struct posix_acl *default_acl,
struct ocfs2_alloc_context *meta_ac,
struct ocfs2_alloc_context *data_ac)
{
@@ -364,12 +364,8 @@ int ocfs2_init_acl(handle_t *handle,
if (!S_ISLNK(inode->i_mode)) {
if (osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL) {
- down_read(&OCFS2_I(dir)->ip_xattr_sem);
- acl = ocfs2_get_acl_nolock(dir, ACL_TYPE_DEFAULT,
- dir_bh);
- up_read(&OCFS2_I(dir)->ip_xattr_sem);
- if (IS_ERR(acl))
- return PTR_ERR(acl);
+ if (default_acl)
+ acl = posix_acl_dup(default_acl);
}
if (!acl) {
mode = inode->i_mode & ~current_umask();
diff --git a/fs/ocfs2/acl.h b/fs/ocfs2/acl.h
index 667c6f03f..c1ba2eb27 100644
--- a/fs/ocfs2/acl.h
+++ b/fs/ocfs2/acl.h
@@ -21,7 +21,7 @@ int ocfs2_iop_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
struct posix_acl *acl, int type);
extern int ocfs2_acl_chmod(struct inode *, struct buffer_head *);
extern int ocfs2_init_acl(handle_t *, struct inode *, struct inode *,
- struct buffer_head *, struct buffer_head *,
+ struct buffer_head *, struct posix_acl *,
struct ocfs2_alloc_context *,
struct ocfs2_alloc_context *);
diff --git a/fs/ocfs2/namei.c b/fs/ocfs2/namei.c
index 1277666c7..6bfc2b9ab 100644
--- a/fs/ocfs2/namei.c
+++ b/fs/ocfs2/namei.c
@@ -27,6 +27,7 @@
#include <linux/highmem.h>
#include <linux/quotaops.h>
#include <linux/iversion.h>
+#include <linux/posix_acl.h>
#include <cluster/masklog.h>
@@ -256,6 +257,7 @@ static int ocfs2_mknod(struct mnt_idmap *idmap,
sigset_t oldset;
int did_block_signals = 0;
struct ocfs2_dentry_lock *dl = NULL;
+ struct posix_acl *default_acl = NULL;
trace_ocfs2_mknod(dir, dentry, dentry->d_name.len, dentry->d_name.name,
(unsigned long long)OCFS2_I(dir)->ip_blkno,
@@ -362,6 +364,15 @@ static int ocfs2_mknod(struct mnt_idmap *idmap,
goto leave;
}
+ if (osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL) {
+ default_acl = get_inode_acl(dir, ACL_TYPE_DEFAULT);
+ if (IS_ERR(default_acl)) {
+ status = PTR_ERR(default_acl);
+ default_acl = NULL;
+ goto leave;
+ }
+ }
+
handle = ocfs2_start_trans(osb, ocfs2_mknod_credits(osb->sb,
S_ISDIR(mode),
xattr_credits));
@@ -411,7 +422,7 @@ static int ocfs2_mknod(struct mnt_idmap *idmap,
inc_nlink(dir);
}
- status = ocfs2_init_acl(handle, inode, dir, new_fe_bh, parent_fe_bh,
+ status = ocfs2_init_acl(handle, inode, dir, new_fe_bh, default_acl,
meta_ac, data_ac);
if (status < 0) {
@@ -488,6 +499,9 @@ static int ocfs2_mknod(struct mnt_idmap *idmap,
if (meta_ac)
ocfs2_free_alloc_context(meta_ac);
+ if (default_acl)
+ posix_acl_release(default_acl);
+
/*
* We should call iput after the i_rwsem of the bitmap been
* unlocked in ocfs2_free_alloc_context, or the
diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c
index fcddd3c13..1b020f33f 100644
--- a/fs/ocfs2/xattr.c
+++ b/fs/ocfs2/xattr.c
@@ -26,6 +26,7 @@
#include <linux/module.h>
#include <linux/string.h>
#include <linux/security.h>
+#include <linux/posix_acl.h>
#include <cluster/masklog.h>
@@ -7257,6 +7258,7 @@ int ocfs2_init_security_and_acl(struct inode *dir,
{
int ret = 0;
struct buffer_head *dir_bh = NULL;
+ struct posix_acl *default_acl = NULL;
ret = ocfs2_init_security_get(inode, dir, qstr, NULL);
if (ret) {
@@ -7269,10 +7271,23 @@ int ocfs2_init_security_and_acl(struct inode *dir,
mlog_errno(ret);
goto leave;
}
- ret = ocfs2_init_acl(NULL, inode, dir, NULL, dir_bh, NULL, NULL);
+
+ if (OCFS2_SB(dir->i_sb)->s_mount_opt & OCFS2_MOUNT_POSIX_ACL) {
+ default_acl = get_inode_acl(dir, ACL_TYPE_DEFAULT);
+ if (IS_ERR(default_acl)) {
+ ret = PTR_ERR(default_acl);
+ default_acl = NULL;
+ goto unlock;
+ }
+ }
+
+ ret = ocfs2_init_acl(NULL, inode, dir, NULL, default_acl, NULL, NULL);
if (ret)
mlog_errno(ret);
+ if (default_acl)
+ posix_acl_release(default_acl);
+unlock:
ocfs2_inode_unlock(dir, 0);
brelse(dir_bh);
leave:
base-commit: 8d3ae59288f1e7d58d76558a6ee96d533bc5019f
--
This is an AI-generated patch subject to moderation.
Reply with '#syz upstream' to Sign-off the patch as a human author
and send it to the upstream kernel mailing lists.
Reply with '#syz reject' to reject it ('#syz unreject' to undo).
See https://goo.gle/syzbot-ai-patches for information about AI-generated patches.
You can comment on the patch as usual, syzbot will try to address
the comments and send a new version of the patch if necessary.
syzbot engineers can be reached at [email protected].