Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

skip permission checks for extended attributes #14220

Merged
merged 1 commit into from
Dec 12, 2022

Conversation

ixhamza
Copy link
Member

@ixhamza ixhamza commented Nov 23, 2022

zfs_zaccess_trivial() calls the generic_permission() to read xattr attributes. This causes deadlock if called from zpl_xattr_set_dir() context as xattr and the dent locks are already held in this scenario. This commit skips the permissions checks for extended attributes since the Linux VFS stack already checks it before passing us the control.

How Has This Been Tested?

Without this patch, setfacl would be deadlocked:

USER_NON_ROOT=$USER
truncate -s 1G /tmp/f1
zpool create tank /tmp/f1 -f
zfs create -o xattr=dir -o acltype=posix -o mountpoint=/data tank/data
echo "TEMP" > /data/file1
chown $USER_NON_ROOT:$USER_NON_ROOT /data/file1
setfacl -m u:$USER_NON_ROOT:rwx /data/file1

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Performance enhancement (non-breaking change which improves efficiency)
  • Code cleanup (non-breaking change which makes code smaller or more readable)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Library ABI change (libzfs, libzfs_core, libnvpair, libuutil and libzfsbootenv)
  • Documentation (a change to man pages or other documentation)

Checklist:

@ixhamza
Copy link
Member Author

ixhamza commented Nov 23, 2022

@amotin, @freqlabs, @anodos325.

Copy link
Contributor

@youzhongyang youzhongyang left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is nice! I noticed the same issue one week ago but then was distracted by something else.

The fix looks good to me, I think a test case is worth to be added.

@ixhamza
Copy link
Member Author

ixhamza commented Nov 23, 2022

Thanks, @youzhongyang. @amotin suggested some modifications internally, I am testing those and will also add the test case.

@@ -433,14 +433,26 @@ zpl_xattr_get(struct inode *ip, const char *name, void *value, size_t size)
cred_t *cr = CRED();
fstrans_cookie_t cookie;
int error;
int xattr_lock = 0;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be a boolean_t rather than an int.

@ixhamza ixhamza marked this pull request as draft November 25, 2022 21:23
@ixhamza ixhamza marked this pull request as ready for review November 28, 2022 11:50
@ghost ghost added the Status: Code Review Needed Ready for review and testing label Nov 28, 2022
@@ -2465,6 +2465,8 @@ zfs_zaccess_trivial(znode_t *zp, uint32_t *working_mode, cred_t *cr,
return (unmapped ? SET_ERROR(EPERM) : 0);
}

rw_enter(&zp->z_gperm_lock, RW_READER);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This lock is only ever taken as a reader and doesn't provide any protection. Should this have been a RW_WRITER? Was the intent here to serial calls to generic_permission or was it to safely set/clear zp->z_lockedxattr . If the latter, couldn't we instead us the zp->z_lock.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. @amotin also pointed it out and I am working on an alternative solution. The lock was supposed to protect the generic_permission() API as zp->z_lockedxattr is global to each znode. So, the write locks on every permission check do not seem to be a good idea.

@ixhamza ixhamza marked this pull request as draft November 28, 2022 20:52
@ixhamza ixhamza force-pushed the NAS-118611 branch 3 times, most recently from b27fd4f to 11d64be Compare December 4, 2022 22:02
@ixhamza ixhamza marked this pull request as ready for review December 5, 2022 14:06
@@ -555,6 +555,7 @@ zfs_create(znode_t *dzp, char *name, vattr_t *vap, int excl,
boolean_t fuid_dirtied;
boolean_t have_acl = B_FALSE;
boolean_t waited = B_FALSE;
boolean_t skipaclchk = (flag & ATTR_NOACLCHECK) ?B_TRUE :B_FALSE;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

skip_acl for consistency, spacing around ternary operator

Suggested change
boolean_t skipaclchk = (flag & ATTR_NOACLCHECK) ?B_TRUE :B_FALSE;
boolean_t skip_acl = (flag & ATTR_NOACLCHECK) ? B_TRUE : B_FALSE;

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did it to keep it in one line but will change.

@@ -911,7 +912,7 @@ zfs_get_xattrdir(znode_t *zp, znode_t **xzpp, cred_t *cr, int flags)
va.va_mode = S_IFDIR | S_ISVTX | 0777;
zfs_fuid_map_ids(zp, cr, &va.va_uid, &va.va_gid);

error = zfs_make_xattrdir(zp, &va, xzpp, cr);
error = zfs_make_xattrdir(zp, &va, xzpp, cr, B_FALSE);

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While FreeBSD does not have this deadlock, could we make the code symmetric? If we do not check additional ACLs on Linux, why should we do it on FreeBSD?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I will update this for consistency. The boolean is not being used for FreeBSD but was added to make the build compatible.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm. I see Linux calls zfs_zaccess(), while FreeBSD doesn't at all, so there is nothing really to skip. So you may ignore that comment of mine. But now I wonder why is this there on Linux?

Copy link
Member Author

@ixhamza ixhamza Dec 6, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I checked and it was added during the initial commit while porting zfs on Linux 34dc7c2f2#diff-5c59a2f8d51fe5cd3ff56b5d01f01a9c7cd1a28b32a61a8bbc136bbc91e18da9R821. Since Linux kernel checks the permissions within VFS stack already, I am also not very sure if it's something we really need here or maybe it covers some edge case that I am not aware of.
@behlendorf.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@behlendorf Do you think if zfs_zaccess() should be kept in zfs_make_xattrdir() for Linux. FreeBSD does not have it and I am not sure if it's really required for Linux also.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ixhamza it seems to me this shouldn't be required for Linux either, and the VFS will be performing the needed access checks. If you can do some manual testing to make sure there's nothing obvious we're overlooking I think we can drop this. It's been so long I don't recall why it was originally added with the original port.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@behlendorf Thank you. I did some testing by setting/retrieving extended attributes and manipulating ACLs for different users and VFS already seems to be checking all the required permissions. I have also updated the PR.

@ixhamza ixhamza force-pushed the NAS-118611 branch 3 times, most recently from 85357d9 to 2280f07 Compare December 9, 2022 15:19
@ixhamza ixhamza changed the title zfs_zaccess_trivial() causes deadlock for xattr attributes skip permission checks for extended attributes Dec 9, 2022
zfs_zaccess_trivial() calls the generic_permission() to read
xattr attributes. This causes deadlock if called from
zpl_xattr_set_dir() context as xattr and the dent locks are
already held in this scenario. This commit skips the permissions
checks for extended attributes since the Linux VFS stack already
checks it before passing us the control.

Signed-off-by: Ameer Hamza <ahamza@ixsystems.com>
Copy link

@ghost ghost left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That worked out nicely.

@behlendorf behlendorf added Status: Accepted Ready to integrate (reviewed, tested) and removed Status: Code Review Needed Ready for review and testing labels Dec 12, 2022
@behlendorf behlendorf merged commit e378571 into openzfs:master Dec 12, 2022
andrewc12 pushed a commit to andrewc12/openzfs that referenced this pull request Dec 17, 2022
zfs_zaccess_trivial() calls the generic_permission() to read
xattr attributes. This causes deadlock if called from
zpl_xattr_set_dir() context as xattr and the dent locks are
already held in this scenario. This commit skips the permissions
checks for extended attributes since the Linux VFS stack already
checks it before passing us the control.

Reviewed-by: Ryan Moeller <ryan@iXsystems.com>
Reviewed-by: Alexander Motin <mav@FreeBSD.org>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Youzhong Yang <yyang@mathworks.com>
Signed-off-by: Ameer Hamza <ahamza@ixsystems.com>
Closes openzfs#14220
@ixhamza ixhamza deleted the NAS-118611 branch August 2, 2023 17:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Status: Accepted Ready to integrate (reviewed, tested)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants