ovl: setxattr: avoid deadlock when writing IMA xattrs
Subsystems (4)
Hi Krisztian,
Quoted 5 lines
If we're writing an extended attribute used by IMA, don't try to lock
sb_writers(mnt_want_write) ori_mutex. We're being called fromima_file_freeand the necessary locks are already being held. Trying to lock them again will deadlock.
But it probably isn't the only function calling ovl_setxattr(). So in
addition to testing S_IMA, only if the security.ima xattr is being set, would this be safe.
Mimi
Quoted 60 lines
In practice we test if the real inode has the
S_IMAflag set and if it does we call__vfs_setxattr_noperminstead of the usualvfs_setxattrwe call for all other cases.Signed-off-by Krisztian Litkey <[email protected]>--- fs/overlayfs/inode.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-)fs/overlayfs/inode.c +18
-6@@ -222,12 +222,18 @@ static bool ovl_is_private_xattr(const char *name)int ovl_setxattr (struct dentry *dentry,const char *name,const void *value,size_t size,int flags) { -int err; +int err, ima;struct dentry *upperdentry; +struct inode *inode; - err =ovl_want_write (dentry); -if (err) - goto out; + inode =ovl_dentry_real (dentry)->d_inode; + ima =IS_IMA (inode); + +if (!ima) { + err =ovl_want_write (dentry); +if (err) + goto out; + } err = -EPERM ;if (ovl_is_private_xattr (name))@@ -238,10 +244,16 @@ int ovl_setxattr(struct dentry *dentry, constchar *name, goto out_drop_write; upperdentry =ovl_dentry_upper (dentry); - err =vfs_setxattr (upperdentry, name, value, size, flags); + +if (!ima) + err =vfs_setxattr (upperdentry, name, value, size, flags); +else + err =__vfs_setxattr_noperm (upperdentry, name, value, size, + flags); out_drop_write: -ovl_drop_write (dentry); +if (!ima) +ovl_drop_write (dentry); out:return err;}
Signature · 1 line
2.5.5
PATCH v2 1/1 ovl: setxattr: avoid deadlock when setting IMA xattr.
If we're writing the extended attribute used by IMA, don't try to lock sb_writers (mnt_want_write) or i_mutex. We're being called from ima_file_free and the necessary locks are already being held. Trying to lock them again will deadlock.
In practice we test if the real inode has the S_IMA flag set and if the xattr being set is the IMA attribute. If both are true, we call __vfs_setxattr_noperm instead of the usual vfs_setxattr we call for all other cases.
--- fs/overlayfs/inode.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-)
fs/overlayfs/inode.c +18 -6
@@ -222,12 +222,18 @@ static bool ovl_is_private_xattr(const char *name)int ovl_setxattr (struct dentry *dentry,const char *name,const void *value,size_t size,int flags) { -int err; +int err, ima;struct dentry *upperdentry; +struct inode *inode; - err =ovl_want_write (dentry); -if (err) - goto out; + inode =ovl_dentry_real (dentry)->d_inode; + ima =IS_IMA (inode) && !strcmp (name,XATTR_NAME_IMA ); + +if (!ima) { + err =ovl_want_write (dentry); +if (err) + goto out; + } err = -EPERM ;if (ovl_is_private_xattr (name))
@@ -238,10 +244,16 @@ int ovl_setxattr(struct dentry *dentry, const char *name, goto out_drop_write; upperdentry =ovl_dentry_upper (dentry); - err =vfs_setxattr (upperdentry, name, value, size, flags); + +if (!ima) + err =vfs_setxattr (upperdentry, name, value, size, flags); +else + err =__vfs_setxattr_noperm (upperdentry, name, value, size, + flags); out_drop_write: -ovl_drop_write (dentry); +if (!ima) +ovl_drop_write (dentry); out:return err; }
Signature · 1 line
2.5.5
A message that is not in the archive (sent off-list or not yet received): [email protected]
Re: [PATCH v2 1/1] ovl: setxattr: avoid deadlock when setting IMA xattr.
Quoted 17 lines · Mimi Zohar
Krisztian Litkey wrote on 05/15/2016 04:07:35 PM:
Quoted 5 lines
If we're writing the extended attribute used by IMA, don't try to lock
sb_writers(mnt_want_write) ori_mutex. We're being called fromima_file_freeand the necessary locks are already being held. Trying to lock them again will deadlock.Thinking about this some more, security.ima can be written here in
ima_file_free, but it also can be written by userspace. The former updates the file hash, while the latter is used to write the file signature.Quoted 5 lines
In practice we test if the real inode has the
S_IMAflag set and if the xattr being set is the IMA attribute. If both are true, we call__vfs_setxattr_noperminstead of the usualvfs_setxattrwe call for all other cases.
Quoted 4 lines
Quoted 1 line
Signed-off-by Krisztian Litkey <[email protected]>Writing the file signature from userspace should call the normal
vfs_setxattr().
Yes. If there was a reliable way to detect that we're being called as an end result of ima_fix_attr we should check for that and branch accordingly. Based on your original suggestion I was under the impression that testing S_IMA would have been the right check. Now based on your recent comment, and checking the code, I see that it will be set for all inodes that are associated with an iint cache entry...
Unless we add a dedicated *vfs_setxattr* flag just for this and pass it from ima_fix_xattr (so that it'd propagate back to ovl_setxattr where we test and branch accordingly), I see no easy way to check for the necessary precondition. And adding a dedicated flag would probably be considered a horrible kludge... so I think I need to dig deeper and see if I can come up with another way of fixing this.
I have taken a small peek at other architecturally not quite unlike FS implementations. Encryptfs seems to be one of the closest ones. I will test if their approach works together with IMA and if it does I'll try to roll a similar implementation for overlayfs. Does that sound reasonable to you ?
However, if it turns out that a similar deadlock is triggerable using IMA with encryptfs then I think I'll be out of ideas about how to attack this problem.
Quoted 3 lines
Mimi
Cheers,
Krisztian
Quoted 57 lines
Quoted 56 lines
--- fs/overlayfs/inode.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-)fs/overlayfs/inode.c +18
-6@@ -222,12 +222,18 @@ static bool ovl_is_private_xattr(const char *name)int ovl_setxattr (struct dentry *dentry,const char *name,const void *value,size_t size,int flags) { -int err; +int err, ima;struct dentry *upperdentry; +struct inode *inode; - err =ovl_want_write (dentry); -if (err) - goto out; + inode =ovl_dentry_real (dentry)->d_inode; + ima =IS_IMA (inode) && !strcmp (name,XATTR_NAME_IMA ); + +if (!ima) { + err =ovl_want_write (dentry); +if (err) + goto out; + } err = -EPERM ;if (ovl_is_private_xattr (name))@@ -238,10 +244,16 @@ int ovl_setxattr(struct dentry *dentry, constchar *name, goto out_drop_write; upperdentry =ovl_dentry_upper (dentry); - err =vfs_setxattr (upperdentry, name, value, size, flags); + +if (!ima) + err =vfs_setxattr (upperdentry, name, value, size, flags); +else + err =__vfs_setxattr_noperm (upperdentry, name, value, size, + flags); out_drop_write: -ovl_drop_write (dentry); +if (!ima) +ovl_drop_write (dentry); out:return err;}
--
2.5.5
Re: [PATCH v2 1/1] ovl: setxattr: avoid deadlock when setting IMA xattr.
Quoted 38 lines · Krisztian Litkey
Quoted 17 lines · Mimi Zohar
Krisztian Litkey wrote on 05/15/2016 04:07:35 PM:
Quoted 5 lines
If we're writing the extended attribute used by IMA, don't try to lock
sb_writers(mnt_want_write) ori_mutex. We're being called fromima_file_freeand the necessary locks are already being held. Trying to lock them again will deadlock.Thinking about this some more, security.ima can be written here in
ima_file_free, but it also can be written by userspace. The former updates the file hash, while the latter is used to write the file signature.Quoted 5 lines
In practice we test if the real inode has the
S_IMAflag set and if the xattr being set is the IMA attribute. If both are true, we call__vfs_setxattr_noperminstead of the usualvfs_setxattrwe call for all other cases.Quoted 4 lines
Quoted 1 line
Signed-off-by Krisztian Litkey <[email protected]>Writing the file signature from userspace should call the normal
vfs_setxattr().Yes. If there was a reliable way to detect that we're being called as an end result of
ima_fix_attrwe should check for that and branch accordingly. Based on your original suggestion I was under the impression that testingS_IMAwould have been the right check. Now based on your recent comment, and checking the code, I see that it will be set for all inodes that are associated with an iint cache entry...Unless we add a dedicated *vfs_setxattr* flag just for this and pass it from
ima_fix_xattr(so that it'd propagate back toovl_setxattrwhere we test and branch accordingly), I see no easy way to check for the necessary precondition. And adding a dedicated flag would probably be considered a horrible kludge... so I think I need to dig deeper and see if I can come up with another way of fixing this.
Quoted 5 lines
I have taken a small peek at other architecturally not quite unlike FS implementations. Encryptfs seems to be one of the closest ones. I will test if their approach works together with IMA and if it does I'll try to roll a similar implementation for overlayfs. Does that sound reasonable to you ?
Hmm... after having taken a closer look at ecryptfs, I think its setxattr is not comparable to overlayfs. The former is much simpler because it simply passes the call on to the underlying filesystem implementation calling vfs_setxattr, much like overlayfs was trying to do. But ecryptfs does not need to do anything else besides setting the attribute so it can call vfs_setxattr without taking any locks or mutexes itself.
However, there is a crucial difference in overlayfs: it might need to copy the dentry (and all directories leading up to it) on demand in setxattr. This happens if setxattr is being called for a file system entry which has never been modified in/through the overlay. I think this copy operation is what really should be protected by mnt_{want,drop}_write.
So the right thing to do would be to protect the copy and do the rest by unconditionally calling __vfs_setxattr_noperm. But wouldn't that just bring us back to the original problem: if i_mutex is already held, can't really mnt_want_write without triggering the lockdep problem, right ?
Quoted 4 lines
Quoted 4 lines
Mimi
Cheers,
Krisztian
Quoted 61 lines
Cheers,
KrisztianQuoted 57 lines
Quoted 56 lines
--- fs/overlayfs/inode.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-)fs/overlayfs/inode.c +18
-6@@ -222,12 +222,18 @@ static bool ovl_is_private_xattr(const char *name)int ovl_setxattr (struct dentry *dentry,const char *name,const void *value,size_t size,int flags) { -int err; +int err, ima;struct dentry *upperdentry; +struct inode *inode; - err =ovl_want_write (dentry); -if (err) - goto out; + inode =ovl_dentry_real (dentry)->d_inode; + ima =IS_IMA (inode) && !strcmp (name,XATTR_NAME_IMA ); + +if (!ima) { + err =ovl_want_write (dentry); +if (err) + goto out; + } err = -EPERM ;if (ovl_is_private_xattr (name))@@ -238,10 +244,16 @@ int ovl_setxattr(struct dentry *dentry, constchar *name, goto out_drop_write; upperdentry =ovl_dentry_upper (dentry); - err =vfs_setxattr (upperdentry, name, value, size, flags); + +if (!ima) + err =vfs_setxattr (upperdentry, name, value, size, flags); +else + err =__vfs_setxattr_noperm (upperdentry, name, value, size, + flags); out_drop_write: -ovl_drop_write (dentry); +if (!ima) +ovl_drop_write (dentry); out:return err;}
--
2.5.5
Re: [PATCH v2 1/1] ovl: setxattr: avoid deadlock when setting IMA xattr.
Quoted 32 lines · Krisztian Litkey
Quoted 31 lines · Krisztian Litkey
Quoted 17 lines · Mimi Zohar
Krisztian Litkey wrote on 05/15/2016 04:07:35 PM:
Quoted 5 lines
If we're writing the extended attribute used by IMA, don't try to lock
sb_writers(mnt_want_write) ori_mutex. We're being called fromima_file_freeand the necessary locks are already being held. Trying to lock them again will deadlock.Thinking about this some more, security.ima can be written here in
ima_file_free, but it also can be written by userspace. The former updates the file hash, while the latter is used to write the file signature.Quoted 5 lines
In practice we test if the real inode has the
S_IMAflag set and if the xattr being set is the IMA attribute. If both are true, we call__vfs_setxattr_noperminstead of the usualvfs_setxattrwe call for all other cases.Quoted 4 lines
Quoted 1 line
Signed-off-by Krisztian Litkey <[email protected]>Writing the file signature from userspace should call the normal
vfs_setxattr().Yes. If there was a reliable way to detect that we're being called as an end result of
ima_fix_attrwe should check for that and branch accordingly. Based on your original suggestion I was under the impression that testingS_IMAwould have been the right check. Now based on your recent comment, and checking the code, I see that it will be set for all inodes that are associated with an iint cache entry...
Quoted 6 lines
Quoted 6 lines
Unless we add a dedicated *vfs_setxattr* flag just for this and pass it from
ima_fix_xattr(so that it'd propagate back toovl_setxattrwhere we test and branch accordingly), I see no easy way to check for the necessary precondition. And adding a dedicated flag would probably be considered a horrible kludge... so I think I need to dig deeper and see if I can come up with another way of fixing this.
Another option, as the i_mutex lock is only taken for security.ima when called from ima_file_free (__fput), would be to check if i_mutex is already locked (mutex_is_locked). If all three - IS_IMA, security.ima and i_mutex taken - call __vfs_setxattr_noperm(). Not a great solution, but it is a solution.
Quoted 19 lines
Quoted 5 lines
I have taken a small peek at other architecturally not quite unlike FS implementations. Encryptfs seems to be one of the closest ones. I will test if their approach works together with IMA and if it does I'll try to roll a similar implementation for overlayfs. Does that sound reasonable to you ?
Hmm... after having taken a closer look at ecryptfs, I think its setxattr is not comparable to overlayfs. The former is much simpler because it simply passes the call on to the underlying filesystem implementation calling
vfs_setxattr, much like overlayfs was trying to do. But ecryptfs does not need to do anything else besides setting the attribute so it can callvfs_setxattrwithout taking any locks or mutexes itself.However, there is a crucial difference in overlayfs: it might need to copy the dentry (and all directories leading up to it) on demand in setxattr. This happens if setxattr is being called for a file system entry which has never been modified in/through the overlay. I think this copy operation is what really should be protected by mnt_{want,drop}_write.
Quoted 4 lines
So the right thing to do would be to protect the copy and do the rest by unconditionally calling
__vfs_setxattr_noperm. But wouldn't that just bring us back to the original problem: ifi_mutexis already held, can't reallymnt_want_writewithout triggering the lockdep problem, right ?
Yes, I think so.
Mimi
PATCH v3 1/1 ovl: setxattr: don't deadlock when called from ima_fix_xattr.
IMA tracks the integrity of files by hashing the file content and storing the hash in an IMA-specific extended attribute (security.ima). When a file opened for writing is closed by the last writer, IMA recalculates the hash and updates the extended attribute. Updating the attribute happens by locking the inode mutex and calling __vfs_setxattr_noperm.
For a file on an overlayfs mount, this causes ovl_setxattr being eventually called (from __vfs_setxattr_noperm via inode->i_op->setxattr) with the inode already locked. In this case we cannot do the xattr setting by calling vfs_setxattr since that will try to lock the inode mutex again. To avoid a deadlock, we check if the mutex is already locked and call __vfs_setxattr_noperm or vfs_setxattr accordingly.
--- fs/overlayfs/inode.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-)
fs/overlayfs/inode.c +14 -11
@@ -223,25 +223,28 @@ int ovl_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags) { int err; -struct dentry *upperdentry; +struct dentry *upper; + +if (ovl_is_private_xattr (name)) +return -EPERM ; err =ovl_want_write (dentry);if (err) goto out; - err = -EPERM ; -if (ovl_is_private_xattr (name)) - goto out_drop_write; - err =ovl_copy_up (dentry); -if (err) - goto out_drop_write; +if (!err) + upper =ovl_dentry_upper (dentry); - upperdentry =ovl_dentry_upper (dentry); - err =vfs_setxattr (upperdentry, name, value, size, flags); - -out_drop_write:ovl_drop_write (dentry); + +if (err) + goto out; + +if (mutex_is_locked (&upper->d_inode->i_mutex)) + err =__vfs_setxattr_noperm (upper, name, value, size, flags); +else + err =vfs_setxattr (upper, name, value, size, flags); out:return err; }
Signature · 1 line
2.5.5
Re: [PATCH v3 1/1] ovl: setxattr: don't deadlock when called from ima_fix_xattr.
Hi Krisztian,
cc'ing LSM and AL for comments
Quoted 49 lines · Krisztian Litkey
IMA tracks the integrity of files by hashing the file content and storing the hash in an IMA-specific extended attribute (security.ima). When a file opened for writing is closed by the last writer, IMA recalculates the hash and updates the extended attribute. Updating the attribute happens by locking the inode mutex and calling
__vfs_setxattr_noperm.For a file on an overlayfs mount, this causes
ovl_setxattrbeing eventually called (from__vfs_setxattr_nopermviainode->i_op->setxattr) with the inode already locked. In this case we cannot do the xattr setting by callingvfs_setxattrsince that will try to lock the inode mutex again. To avoid a deadlock, we check if the mutex is already locked and call__vfs_setxattr_nopermorvfs_setxattraccordingly.Signed-off-by Krisztian Litkey <[email protected]>--- fs/overlayfs/inode.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-)fs/overlayfs/inode.c +6
-8@@ -223,25 +223,28 @@ int ovl_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags) { int err; -struct dentry *upperdentry; +struct dentry *upper; + +if (ovl_is_private_xattr (name)) +return -EPERM ; err =ovl_want_write (dentry);if (err) goto out; - err = -EPERM ; -if (ovl_is_private_xattr (name)) - goto out_drop_write; - err =ovl_copy_up (dentry); -if (err) - goto out_drop_write; +if (!err) + upper =ovl_dentry_upper (dentry); - upperdentry =ovl_dentry_upper (dentry);
fs/overlayfs/inode.c int ovl_setxattr(struct dentry *dentry, const char *name, · line 245
Quoted 10 lines
fs/overlayfs/inode.c +6
-3- err =vfs_setxattr (upperdentry, name, value, size, flags); - -out_drop_write:ovl_drop_write (dentry); + +if (err) + goto out; + +if (mutex_is_locked (&upper->d_inode->i_mutex)) + err =__vfs_setxattr_noperm (upper, name, value, size, flags);
As far as I'm aware, the only time that i_mutex is taken, is during__fput() when IMA writes security.ima. Previous versions of this patch
checked whether the xattr being written was security.ima. It would
probably be a good idea not to make that assumption here. The question
is what should happen if the i_mutex is locked, but the xattr isn't security.ima. At minimum it should be audited. Al, any comments?
Mimi
Quoted 5 lines
fs/overlayfs/inode.c +2
-0+else + err =vfs_setxattr (upper, name, value, size, flags); out:return err; }
Re: [PATCH v3 1/1] ovl: setxattr: don't deadlock when called from ima_fix_xattr.
Quoted 9 lines · Mimi Zohar
fs/overlayfs/inode.c int ovl_setxattr(struct dentry *dentry, const char *name, · line 245
Quoted 2 lines
fs/overlayfs/inode.c +2
-0+if (mutex_is_locked (&upper->d_inode->i_mutex)) + err =__vfs_setxattr_noperm (upper, name, value, size, flags);As far as I'm aware, the only time that
i_mutexis taken, is during__fput()when IMA writes security.ima. Previous versions of this patch
checked whether the xattr being written was security.ima. It would
probably be a good idea not to make that assumption here. The question
is what should happen if thei_mutexis locked, but the xattr isn't security.ima. At minimum it should be audited. Al, any comments?
ITYM "printable", and that's somewhat harder. OK, let me try:
Anybody using ..._is_lock() kind of primitives that way ought to be (re)educated before they are allowed near any kind of multithreaded code _anywhere_. mutex could've been held by a different thread of execution and dropped just as mutex_is_locked() returns. Or at any subsequent point. This is 100% bogus; one should *never* write that kind of code. As in "here's your well-earned F-, better luck next semester".
I haven't seen the full patch (you've quoted only a part of that gem), but about the only way for it to be correct is to have it continue with
+ else
+ err = <identical call>
Practically all calls of mutex_is_locked() are of form
WARN_ON(!mutex_is_locked(...)) or equivalent thereof. And the rest contains similar... wonders - for example, take a look at drivers/media/rc/imon.c; imon_ir_change_protocol() has this
if (!mutex_is_locked(&ictx->lock)) {
unlock = true;
mutex_lock(&ictx->lock);
}
retval = send_packet(ictx);
if (retval)
goto out;
ictx->rc_type = *rc_type;
ictx->pad_mouse = false;out:
if (unlock)
mutex_unlock(&ictx->lock);Finding why it's exploitably racy is left as a trivial exercise for readers...
Folks, if you see something of that sort in the code, it's a huge red flag. There are legitimate uses of mutex_is_locked other than asserts, but those are extremely rare.
I would need to see more context to be able to comment on the problem in question, but this patch is almost certainly broken.
Re: [PATCH v3 1/1] ovl: setxattr: don't deadlock when called from ima_fix_xattr.
Quoted 41 lines · Al Viro
Quoted 9 lines · Mimi Zohar
fs/overlayfs/inode.c int ovl_setxattr(struct dentry *dentry, const char *name, · line 245
Quoted 2 lines
fs/overlayfs/inode.c +2
-0+if (mutex_is_locked (&upper->d_inode->i_mutex)) + err =__vfs_setxattr_noperm (upper, name, value, size, flags);As far as I'm aware, the only time that
i_mutexis taken, is during__fput()when IMA writes security.ima. Previous versions of this patch
checked whether the xattr being written was security.ima. It would
probably be a good idea not to make that assumption here. The question
is what should happen if thei_mutexis locked, but the xattr isn't security.ima. At minimum it should be audited. Al, any comments?ITYM "printable", and that's somewhat harder. OK, let me try:
Anybody using ..._is_lock() kind of primitives that way ought to be (re)educated before they are allowed near any kind of multithreaded code _anywhere_. mutex could've been held by a different thread of execution and dropped just as
mutex_is_locked()returns. Or at any subsequent point. This is 100% bogus; one should *never* write that kind of code. As in "here's your well-earned F-, better luck next semester".I haven't seen the full patch (you've quoted only a part of that gem), but about the only way for it to be correct is to have it continue with
+ else
+ err = <identical call>Practically all calls of
mutex_is_locked()are of form
WARN_ON(!mutex_is_locked(...)) or equivalent thereof. And the rest contains similar... wonders - for example, take a look atdrivers/media/rc/imon.c;imon_ir_change_protocol()has thisif (!mutex_is_locked(&ictx->lock)) { unlock = true; mutex_lock(&ictx->lock); } retval = send_packet(ictx); if (retval) goto out; ictx->rc_type = *rc_type; ictx->pad_mouse = false;
Quoted 8 lines
out:
if (unlock) mutex_unlock(&ictx->lock);Finding why it's exploitably racy is left as a trivial exercise for readers...
Folks, if you see something of that sort in the code, it's a huge red flag. There are legitimate uses of
mutex_is_lockedother than asserts, but those are extremely rare.
My fault for even suggesting it.
Quoted 2 lines
I would need to see more context to be able to comment on the problem in question, but this patch is almost certainly broken.
We deferred __fput() back in 2012 in order for IMA to safely take thei_mutex and write security.ima. Writing the security.ima xattr now
triggers overlayfs to write the xattr, but overlayfs doesn't differentiate between callers - as a result of userspace or as described
here in __fput(). All calls to ovl_setxattr() should call vfs_sexattr,
except the one triggered by __fput(). Refer to the original lockdep
report -
http://thread.gmane.org/gmane.linux.file-systems.union/640
Al, any help in resolving this lockdep would be much appreciated.
Mimi
Re: [PATCH v3 1/1] ovl: setxattr: don't deadlock when called from ima_fix_xattr.
Quoted 47 lines · Mimi Zohar
Quoted 46 lines · Al Viro
Quoted 9 lines · Mimi Zohar
Quoted 2 lines
+ if (
mutex_is_locked(&upper->d_inode->i_mutex))
+ err =__vfs_setxattr_noperm(upper, name, value, size, flags);As far as I'm aware, the only time that
i_mutexis taken, is during__fput()when IMA writes security.ima. Previous versions of this patch
checked whether the xattr being written was security.ima. It would
probably be a good idea not to make that assumption here. The question
is what should happen if thei_mutexis locked, but the xattr isn't security.ima. At minimum it should be audited. Al, any comments?ITYM "printable", and that's somewhat harder. OK, let me try:
Anybody using ..._is_lock() kind of primitives that way ought to be (re)educated before they are allowed near any kind of multithreaded code _anywhere_. mutex could've been held by a different thread of execution and dropped just as
mutex_is_locked()returns. Or at any subsequent point. This is 100% bogus; one should *never* write that kind of code. As in "here's your well-earned F-, better luck next semester".I haven't seen the full patch (you've quoted only a part of that gem), but about the only way for it to be correct is to have it continue with
+ else
+ err = <identical call>Practically all calls of
mutex_is_locked()are of form
WARN_ON(!mutex_is_locked(...)) or equivalent thereof. And the rest contains similar... wonders - for example, take a look atdrivers/media/rc/imon.c;imon_ir_change_protocol()has thisif (!mutex_is_locked(&ictx->lock)) { unlock = true; mutex_lock(&ictx->lock); } retval = send_packet(ictx); if (retval) goto out; ictx->rc_type = *rc_type; ictx->pad_mouse = false;out:
if (unlock) mutex_unlock(&ictx->lock);Finding why it's exploitably racy is left as a trivial exercise for readers...
Quoted 5 lines
Quoted 3 lines
Folks, if you see something of that sort in the code, it's a huge red flag. There are legitimate uses of
mutex_is_lockedother than asserts, but those are extremely rare.My fault for even suggesting it.
Mimi, it is not your fault. I should have never missed something so ridiculously trivial as this. I was simply being an idiot and rolled a patch with my brain completely shut off and without stopping for a single second to think about what I was doing. I agre with Al that in that state of mind one should not be anywhere near a keyboard, let alone trying to write kernel code. Got what I deserved.
Quoted 11 lines
Quoted 2 lines
I would need to see more context to be able to comment on the problem in question, but this patch is almost certainly broken.
We deferred
__fput()back in 2012 in order for IMA to safely take thei_mutexand write security.ima. Writing the security.ima xattr now
triggers overlayfs to write the xattr, but overlayfs doesn't differentiate between callers - as a result of userspace or as described
here in__fput(). All calls toovl_setxattr()should callvfs_sexattr,
except the one triggered by__fput(). Refer to the original lockdep
report -
http://thread.gmane.org/gmane.linux.file-systems.union/640
How about the other (maybe kludgy) option of adding a dedicated inode flag for this, setting it in the call to __vfs_setxattr_noperm in ima_appraise and forcibly clearing it in vfs_setxattr. ovl_setxattr could then test it and branch accordingly...
Quoted 5 lines
Al, any help in resolving this lockdep would be much appreciated.
Mimi
Cheers,
Krisztian the Idiot, hiding in a quiet corner, bowing his head in shame
Quoted 4 lines
--
To unsubscribe from this list: send the line "unsubscribe linux-unionfs" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Miklos Szeredi
maintainerRe: [PATCH v3 1/1] ovl: setxattr: don't deadlock when called from ima_fix_xattr.
Quoted 10 lines · Krisztian Litkey
Quoted 8 lines · Mimi Zohar
We deferred
__fput()back in 2012 in order for IMA to safely take thei_mutexand write security.ima. Writing the security.ima xattr now
triggers overlayfs to write the xattr, but overlayfs doesn't differentiate between callers - as a result of userspace or as described
here in__fput(). All calls toovl_setxattr()should callvfs_sexattr,
except the one triggered by__fput(). Refer to the original lockdep
report -
http://thread.gmane.org/gmane.linux.file-systems.union/640
Looks like more fallout from 4bacc9c9234c ("overlayfs: Make f_path always point to the overlay and f_inode to the underlay").
Not sure what we want here. Doing everything on the underlying dentry/inode would be trivial (see attached patch).
Question is, can we get setxattr() on file opened for O_RDONLY? If so, then that could fail on overlayfs (lower layer is read-only).
Thanks,
Miklos
---
From: Miklos Szeredi <[email protected]>
Subject: ima: use file_path()
Ima tries to call ->setxattr() on overlayfs dentry after having locked underlying inode, which results in a deadlock.
--- security/integrity/ima/ima_appraise.c | 4 ++-- security/integrity/ima/ima_main.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-)
security/integrity/ima/ima_main.c +1 -1
@@ -222,7 +222,7 @@ static int process_measurement(struct fiif ((action &IMA_APPRAISE_SUBMASK ) ||strcmp (template_desc->name,IMA_TEMPLATE_IMA_NAME ) !=0 )/* read 'security.ima' */ - xattr_len =ima_read_xattr (file->f_path.dentry, &xattr_value); + xattr_len =ima_read_xattr (file_dentry (file), &xattr_value); hash_algo =ima_get_hash_algo (xattr_value, xattr_len);
security/integrity/ima/ima_appraise.c +2 -2
@@ -190,7 +190,7 @@ int ima_appraise_measurement(enum ima_ho {static const char op[] ="appraise_data" ;char *cause ="unknown" ; -struct dentry *dentry = file->f_path.dentry; +struct dentry *dentry =file_dentry (file);struct inode *inode =d_backing_inode (dentry);enum integrity_status status =INTEGRITY_UNKNOWN ;int rc = xattr_len, hash_start =0 ;
@@ -295,7 +295,7 @@ int ima_appraise_measurement(enum ima_ho*/ void ima_update_xattr (struct integrity_iint_cache *iint,struct file *file) { -struct dentry *dentry = file->f_path.dentry; +struct dentry *dentry =file_dentry (file);int rc =0 ;/* do not collect and update hash for digital signatures */
Re: [PATCH v3 1/1] ovl: setxattr: don't deadlock when called from ima_fix_xattr.
Only tangentially related, but... that bug had been discussed,
without any results: the fallback in ima_d_path() to ->d_name.name is completely broken. There is no warranty whatsoever that dentry won't be renamed, with its earlier (too long to be embedded into dentry itself) ->d_name.name getting freed. Right under your code.
Could we please get rid of that thing? "A message in a near-oom
situation might be less informative than we'd like" is better than "this code might end up dereferencing freed memory".
Another similar bug is in ima_collect_measurement() -
const char *filename = file->f_path.dentry->d_name.name;
...
integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode,
filename, "collect_data", audit_cause,
with no warranty whatsoever that you are not passing a pointer to freed
memory. The same goes for ima_eventname_init_common() -
if (event_data->file) {
cur_filename = event_data->file->f_path.dentry->d_name.name;
cur_filename_len = strlen(cur_filename);
} else ...
return ima_write_template_field_data(cur_filename, cur_filename_len,
DATA_FMT_STRING, field_data);
Re: [PATCH v3 1/1] ovl: setxattr: don't deadlock when called from ima_fix_xattr.
Quoted 5 lines · Al Viro
Only tangentially related, but... that bug had been discussed,
without any results: the fallback inima_d_path()to ->d_name.name is completely broken. There is no warranty whatsoever that dentry won't be renamed, with its earlier (too long to be embedded into dentry itself) ->d_name.name getting freed. Right under your code.
Isn't i_mutex required?
Quoted 24 lines
Could we please get rid of that thing? "A message in a near-oom
situation might be less informative than we'd like" is better than "this code might end up dereferencing freed memory".Another similar bug is in ima_collect_measurement() - const char *filename = file->f_path.dentry->d_name.name; ... integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, filename, "collect_data", audit_cause, with no warranty whatsoever that you are not passing a pointer to freed memory. The same goes for ima_eventname_init_common() - if (event_data->file) { cur_filename = event_data->file->f_path.dentry->d_name.name; cur_filename_len = strlen(cur_filename); } else...
return ima_write_template_field_data(cur_filename,cur_filename_len,
DATA_FMT_STRING,field_data);
--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH v3 1/1] ovl: setxattr: don't deadlock when called from ima_fix_xattr.
In reply to Al ViroQuoted 5 lines · Al Viro
Only tangentially related, but... that bug had been discussed,
without any results: the fallback inima_d_path()to ->d_name.name is completely broken. There is no warranty whatsoever that dentry won't be renamed, with its earlier (too long to be embedded into dentry itself) ->d_name.name getting freed. Right under your code.
Isn't i_mutex required?
Quoted 24 lines
Could we please get rid of that thing? "A message in a near-oom
situation might be less informative than we'd like" is better than "this code might end up dereferencing freed memory".Another similar bug is in ima_collect_measurement() - const char *filename = file->f_path.dentry->d_name.name; ... integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, filename, "collect_data", audit_cause, with no warranty whatsoever that you are not passing a pointer to freed memory. The same goes for ima_eventname_init_common() - if (event_data->file) { cur_filename = event_data->file->f_path.dentry->d_name.name; cur_filename_len = strlen(cur_filename); } else...
return ima_write_template_field_data(cur_filename,cur_filename_len,
DATA_FMT_STRING,field_data);
--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH v3 1/1] ovl: setxattr: don't deadlock when called from ima_fix_xattr.
In reply to Al ViroQuoted 5 lines · Al Viro
Only tangentially related, but... that bug had been discussed,
without any results: the fallback inima_d_path()to ->d_name.name is completely broken. There is no warranty whatsoever that dentry won't be renamed, with its earlier (too long to be embedded into dentry itself) ->d_name.name getting freed. Right under your code.
Isn't i_mutex required?
Quoted 24 lines
Could we please get rid of that thing? "A message in a near-oom
situation might be less informative than we'd like" is better than "this code might end up dereferencing freed memory".Another similar bug is in ima_collect_measurement() - const char *filename = file->f_path.dentry->d_name.name; ... integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, filename, "collect_data", audit_cause, with no warranty whatsoever that you are not passing a pointer to freed memory. The same goes for ima_eventname_init_common() - if (event_data->file) { cur_filename = event_data->file->f_path.dentry->d_name.name; cur_filename_len = strlen(cur_filename); } else...
return ima_write_template_field_data(cur_filename,cur_filename_len,
DATA_FMT_STRING,field_data);
--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH v3 1/1] ovl: setxattr: don't deadlock when called from ima_fix_xattr.
In reply to Al ViroQuoted 5 lines · Al Viro
Only tangentially related, but... that bug had been discussed,
without any results: the fallback inima_d_path()to ->d_name.name is completely broken. There is no warranty whatsoever that dentry won't be renamed, with its earlier (too long to be embedded into dentry itself) ->d_name.name getting freed. Right under your code.
Isn't i_mutex required?
Quoted 24 lines
Could we please get rid of that thing? "A message in a near-oom
situation might be less informative than we'd like" is better than "this code might end up dereferencing freed memory".Another similar bug is in ima_collect_measurement() - const char *filename = file->f_path.dentry->d_name.name; ... integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, filename, "collect_data", audit_cause, with no warranty whatsoever that you are not passing a pointer to freed memory. The same goes for ima_eventname_init_common() - if (event_data->file) { cur_filename = event_data->file->f_path.dentry->d_name.name; cur_filename_len = strlen(cur_filename); } else...
return ima_write_template_field_data(cur_filename,cur_filename_len,
DATA_FMT_STRING,field_data);
--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH v3 1/1] ovl: setxattr: don't deadlock when called from ima_fix_xattr.
In reply to Miklos SzerediQuoted 12 lines · Miklos Szeredi
Quoted 10 lines · Krisztian Litkey
Quoted 8 lines · Mimi Zohar
We deferred
__fput()back in 2012 in order for IMA to safely take thei_mutexand write security.ima. Writing the security.ima xattr now
triggers overlayfs to write the xattr, but overlayfs doesn't differentiate between callers - as a result of userspace or as described
here in__fput(). All calls toovl_setxattr()should callvfs_sexattr,
except the one triggered by__fput(). Refer to the original lockdep
report -
http://thread.gmane.org/gmane.linux.file-systems.union/640
Quoted 8 lines
Looks like more fallout from
4bacc9c9234c("overlayfs: Makef_pathalways point to the overlay andf_inodeto the underlay").Not sure what we want here. Doing everything on the underlying dentry/inode would be trivial (see attached patch).
Question is, can we get
setxattr()on file opened forO_RDONLY? If so, then that could fail on overlayfs (lower layer is read-only).
Normally only after a file has been modified is the xattr written. However in "fix" mode, the xattr would be written for files opened read-only (eg. bprm, mmap execute).
Mimi
Re: [PATCH v3 1/1] ovl: setxattr: don't deadlock when called from ima_fix_xattr.
In reply to Miklos SzerediQuoted 12 lines · Miklos Szeredi
Quoted 10 lines · Krisztian Litkey
Quoted 8 lines · Mimi Zohar
We deferred
__fput()back in 2012 in order for IMA to safely take thei_mutexand write security.ima. Writing the security.ima xattr now
triggers overlayfs to write the xattr, but overlayfs doesn't differentiate between callers - as a result of userspace or as described
here in__fput(). All calls toovl_setxattr()should callvfs_sexattr,
except the one triggered by__fput(). Refer to the original lockdep
report -
http://thread.gmane.org/gmane.linux.file-systems.union/640
Quoted 8 lines
Looks like more fallout from
4bacc9c9234c("overlayfs: Makef_pathalways point to the overlay andf_inodeto the underlay").Not sure what we want here. Doing everything on the underlying dentry/inode would be trivial (see attached patch).
Question is, can we get
setxattr()on file opened forO_RDONLY? If so, then that could fail on overlayfs (lower layer is read-only).
Normally only after a file has been modified is the xattr written. However in "fix" mode, the xattr would be written for files opened read-only (eg. bprm, mmap execute).
Mimi
Re: [PATCH v3 1/1] ovl: setxattr: don't deadlock when called from ima_fix_xattr.
In reply to Miklos SzerediQuoted 12 lines · Miklos Szeredi
Quoted 10 lines · Krisztian Litkey
Quoted 8 lines · Mimi Zohar
We deferred
__fput()back in 2012 in order for IMA to safely take thei_mutexand write security.ima. Writing the security.ima xattr now
triggers overlayfs to write the xattr, but overlayfs doesn't differentiate between callers - as a result of userspace or as described
here in__fput(). All calls toovl_setxattr()should callvfs_sexattr,
except the one triggered by__fput(). Refer to the original lockdep
report -
http://thread.gmane.org/gmane.linux.file-systems.union/640
Quoted 8 lines
Looks like more fallout from
4bacc9c9234c("overlayfs: Makef_pathalways point to the overlay andf_inodeto the underlay").Not sure what we want here. Doing everything on the underlying dentry/inode would be trivial (see attached patch).
Question is, can we get
setxattr()on file opened forO_RDONLY? If so, then that could fail on overlayfs (lower layer is read-only).
Normally only after a file has been modified is the xattr written. However in "fix" mode, the xattr would be written for files opened read-only (eg. bprm, mmap execute).
Mimi
Re: [PATCH v3 1/1] ovl: setxattr: don't deadlock when called from ima_fix_xattr.
In reply to Miklos SzerediQuoted 12 lines · Miklos Szeredi
Quoted 10 lines · Krisztian Litkey
Quoted 8 lines · Mimi Zohar
We deferred
__fput()back in 2012 in order for IMA to safely take thei_mutexand write security.ima. Writing the security.ima xattr now
triggers overlayfs to write the xattr, but overlayfs doesn't differentiate between callers - as a result of userspace or as described
here in__fput(). All calls toovl_setxattr()should callvfs_sexattr,
except the one triggered by__fput(). Refer to the original lockdep
report -
http://thread.gmane.org/gmane.linux.file-systems.union/640
Quoted 8 lines
Looks like more fallout from
4bacc9c9234c("overlayfs: Makef_pathalways point to the overlay andf_inodeto the underlay").Not sure what we want here. Doing everything on the underlying dentry/inode would be trivial (see attached patch).
Question is, can we get
setxattr()on file opened forO_RDONLY? If so, then that could fail on overlayfs (lower layer is read-only).
Normally only after a file has been modified is the xattr written. However in "fix" mode, the xattr would be written for files opened read-only (eg. bprm, mmap execute).
Mimi
Re: [PATCH v3 1/1] ovl: setxattr: don't deadlock when called from ima_fix_xattr.
In reply to Krisztian LitkeyQuoted 53 lines · Krisztian Litkey
IMA tracks the integrity of files by hashing the file content and storing the hash in an IMA-specific extended attribute (security.ima). When a file opened for writing is closed by the last writer, IMA recalculates the hash and updates the extended attribute. Updating the attribute happens by locking the inode mutex and calling
__vfs_setxattr_noperm.For a file on an overlayfs mount, this causes
ovl_setxattrbeing eventually called (from__vfs_setxattr_nopermviainode->i_op->setxattr) with the inode already locked. In this case we cannot do the xattr setting by callingvfs_setxattrsince that will try to lock the inode mutex again. To avoid a deadlock, we check if the mutex is already locked and call__vfs_setxattr_nopermorvfs_setxattraccordingly.Signed-off-by Krisztian Litkey <[email protected]>--- fs/overlayfs/inode.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-)fs/overlayfs/inode.c +6
-11@@ -223,25 +223,28 @@ int ovl_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags) { int err; -struct dentry *upperdentry; +struct dentry *upper; + +if (ovl_is_private_xattr (name)) +return -EPERM ; err =ovl_want_write (dentry);if (err) goto out; - err = -EPERM ; -if (ovl_is_private_xattr (name)) - goto out_drop_write; - err =ovl_copy_up (dentry); -if (err) - goto out_drop_write; +if (!err) + upper =ovl_dentry_upper (dentry); - upperdentry =ovl_dentry_upper (dentry); - err =vfs_setxattr (upperdentry, name, value, size, flags); - -out_drop_write:ovl_drop_write (dentry);
fs/overlayfs/inode.c int ovl_setxattr(struct dentry *dentry, const char *name, · line 249
Quoted 10 lines
fs/overlayfs/inode.c +8
-0+ +if (err) + goto out; + +if (mutex_is_locked (&upper->d_inode->i_mutex)) + err =__vfs_setxattr_noperm (upper, name, value, size, flags); +else + err =vfs_setxattr (upper, name, value, size, flags); out:return err;
This is making the assumption that it is us who have locked it already. What if it is not, what if it is another thread? It being locked does not tell us it is us locking it and that it will remain locked does it?
-apw