]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
4.9-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 10 Aug 2020 12:11:03 +0000 (14:11 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 10 Aug 2020 12:11:03 +0000 (14:11 +0200)
added patches:
binder-prevent-context-manager-from-incrementing-ref-0.patch
xattr-break-delegations-in-set-remove-xattr.patch

queue-4.9/binder-prevent-context-manager-from-incrementing-ref-0.patch [new file with mode: 0644]
queue-4.9/series
queue-4.9/xattr-break-delegations-in-set-remove-xattr.patch [new file with mode: 0644]

diff --git a/queue-4.9/binder-prevent-context-manager-from-incrementing-ref-0.patch b/queue-4.9/binder-prevent-context-manager-from-incrementing-ref-0.patch
new file mode 100644 (file)
index 0000000..1f398ee
--- /dev/null
@@ -0,0 +1,86 @@
+From 4b836a1426cb0f1ef2a6e211d7e553221594f8fc Mon Sep 17 00:00:00 2001
+From: Jann Horn <jannh@google.com>
+Date: Mon, 27 Jul 2020 14:04:24 +0200
+Subject: binder: Prevent context manager from incrementing ref 0
+
+From: Jann Horn <jannh@google.com>
+
+commit 4b836a1426cb0f1ef2a6e211d7e553221594f8fc upstream.
+
+Binder is designed such that a binder_proc never has references to
+itself. If this rule is violated, memory corruption can occur when a
+process sends a transaction to itself; see e.g.
+<https://syzkaller.appspot.com/bug?extid=09e05aba06723a94d43d>.
+
+There is a remaining edgecase through which such a transaction-to-self
+can still occur from the context of a task with BINDER_SET_CONTEXT_MGR
+access:
+
+ - task A opens /dev/binder twice, creating binder_proc instances P1
+   and P2
+ - P1 becomes context manager
+ - P2 calls ACQUIRE on the magic handle 0, allocating index 0 in its
+   handle table
+ - P1 dies (by closing the /dev/binder fd and waiting a bit)
+ - P2 becomes context manager
+ - P2 calls ACQUIRE on the magic handle 0, allocating index 1 in its
+   handle table
+   [this triggers a warning: "binder: 1974:1974 tried to acquire
+   reference to desc 0, got 1 instead"]
+ - task B opens /dev/binder once, creating binder_proc instance P3
+ - P3 calls P2 (via magic handle 0) with (void*)1 as argument (two-way
+   transaction)
+ - P2 receives the handle and uses it to call P3 (two-way transaction)
+ - P3 calls P2 (via magic handle 0) (two-way transaction)
+ - P2 calls P2 (via handle 1) (two-way transaction)
+
+And then, if P2 does *NOT* accept the incoming transaction work, but
+instead closes the binder fd, we get a crash.
+
+Solve it by preventing the context manager from using ACQUIRE on ref 0.
+There shouldn't be any legitimate reason for the context manager to do
+that.
+
+Additionally, print a warning if someone manages to find another way to
+trigger a transaction-to-self bug in the future.
+
+Cc: stable@vger.kernel.org
+Fixes: 457b9a6f09f0 ("Staging: android: add binder driver")
+Acked-by: Todd Kjos <tkjos@google.com>
+Signed-off-by: Jann Horn <jannh@google.com>
+Reviewed-by: Martijn Coenen <maco@android.com>
+Link: https://lore.kernel.org/r/20200727120424.1627555-1-jannh@google.com
+[manual backport: remove fine-grained locking and error reporting that
+                  don't exist in <=4.9]
+Signed-off-by: Jann Horn <jannh@google.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/android/binder.c |    9 +++++++++
+ 1 file changed, 9 insertions(+)
+
+--- a/drivers/android/binder.c
++++ b/drivers/android/binder.c
+@@ -1427,6 +1427,10 @@ static void binder_transaction(struct bi
+                       return_error = BR_DEAD_REPLY;
+                       goto err_dead_binder;
+               }
++              if (WARN_ON(proc == target_proc)) {
++                      return_error = BR_FAILED_REPLY;
++                      goto err_invalid_target_handle;
++              }
+               if (security_binder_transaction(proc->tsk,
+                                               target_proc->tsk) < 0) {
+                       return_error = BR_FAILED_REPLY;
+@@ -1830,6 +1834,11 @@ static int binder_thread_write(struct bi
+                       ptr += sizeof(uint32_t);
+                       if (target == 0 && binder_context_mgr_node &&
+                           (cmd == BC_INCREFS || cmd == BC_ACQUIRE)) {
++                              if (binder_context_mgr_node->proc == proc) {
++                                      binder_user_error("%d:%d context manager tried to acquire desc 0\n",
++                                                        proc->pid, thread->pid);
++                                      return -EINVAL;
++                              }
+                               ref = binder_get_ref_for_node(proc,
+                                              binder_context_mgr_node);
+                               if (ref->desc != target) {
index c6b08dc93c6c2eb1399b4727d01e8986d730efab..1fc4d67316530177d71a1f85bd6391e934833f0f 100644 (file)
@@ -67,3 +67,5 @@ cfg80211-check-vendor-command-doit-pointer-before-us.patch
 igb-reinit_locked-should-be-called-with-rtnl_lock.patch
 atm-fix-atm_dev-refcnt-leaks-in-atmtcp_remove_persis.patch
 tools-lib-traceevent-fix-memory-leak-in-process_dyna.patch
+xattr-break-delegations-in-set-remove-xattr.patch
+binder-prevent-context-manager-from-incrementing-ref-0.patch
diff --git a/queue-4.9/xattr-break-delegations-in-set-remove-xattr.patch b/queue-4.9/xattr-break-delegations-in-set-remove-xattr.patch
new file mode 100644 (file)
index 0000000..d8f233b
--- /dev/null
@@ -0,0 +1,181 @@
+From 08b5d5014a27e717826999ad20e394a8811aae92 Mon Sep 17 00:00:00 2001
+From: Frank van der Linden <fllinden@amazon.com>
+Date: Tue, 23 Jun 2020 22:39:18 +0000
+Subject: xattr: break delegations in {set,remove}xattr
+
+From: Frank van der Linden <fllinden@amazon.com>
+
+commit 08b5d5014a27e717826999ad20e394a8811aae92 upstream.
+
+set/removexattr on an exported filesystem should break NFS delegations.
+This is true in general, but also for the upcoming support for
+RFC 8726 (NFSv4 extended attribute support). Make sure that they do.
+
+Additionally, they need to grow a _locked variant, since callers might
+call this with i_rwsem held (like the NFS server code).
+
+Cc: stable@vger.kernel.org # v4.9+
+Cc: linux-fsdevel@vger.kernel.org
+Cc: Al Viro <viro@zeniv.linux.org.uk>
+Signed-off-by: Frank van der Linden <fllinden@amazon.com>
+Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ fs/xattr.c            |   84 +++++++++++++++++++++++++++++++++++++++++++++-----
+ include/linux/xattr.h |    2 +
+ 2 files changed, 79 insertions(+), 7 deletions(-)
+
+--- a/fs/xattr.c
++++ b/fs/xattr.c
+@@ -203,10 +203,22 @@ int __vfs_setxattr_noperm(struct dentry
+       return error;
+ }
+-
++/**
++ * __vfs_setxattr_locked: set an extended attribute while holding the inode
++ * lock
++ *
++ *  @dentry - object to perform setxattr on
++ *  @name - xattr name to set
++ *  @value - value to set @name to
++ *  @size - size of @value
++ *  @flags - flags to pass into filesystem operations
++ *  @delegated_inode - on return, will contain an inode pointer that
++ *  a delegation was broken on, NULL if none.
++ */
+ int
+-vfs_setxattr(struct dentry *dentry, const char *name, const void *value,
+-              size_t size, int flags)
++__vfs_setxattr_locked(struct dentry *dentry, const char *name,
++              const void *value, size_t size, int flags,
++              struct inode **delegated_inode)
+ {
+       struct inode *inode = dentry->d_inode;
+       int error;
+@@ -215,15 +227,40 @@ vfs_setxattr(struct dentry *dentry, cons
+       if (error)
+               return error;
+-      inode_lock(inode);
+       error = security_inode_setxattr(dentry, name, value, size, flags);
+       if (error)
+               goto out;
++      error = try_break_deleg(inode, delegated_inode);
++      if (error)
++              goto out;
++
+       error = __vfs_setxattr_noperm(dentry, name, value, size, flags);
+ out:
++      return error;
++}
++EXPORT_SYMBOL_GPL(__vfs_setxattr_locked);
++
++int
++vfs_setxattr(struct dentry *dentry, const char *name, const void *value,
++              size_t size, int flags)
++{
++      struct inode *inode = dentry->d_inode;
++      struct inode *delegated_inode = NULL;
++      int error;
++
++retry_deleg:
++      inode_lock(inode);
++      error = __vfs_setxattr_locked(dentry, name, value, size, flags,
++          &delegated_inode);
+       inode_unlock(inode);
++
++      if (delegated_inode) {
++              error = break_deleg_wait(&delegated_inode);
++              if (!error)
++                      goto retry_deleg;
++      }
+       return error;
+ }
+ EXPORT_SYMBOL_GPL(vfs_setxattr);
+@@ -379,8 +416,18 @@ __vfs_removexattr(struct dentry *dentry,
+ }
+ EXPORT_SYMBOL(__vfs_removexattr);
++/**
++ * __vfs_removexattr_locked: set an extended attribute while holding the inode
++ * lock
++ *
++ *  @dentry - object to perform setxattr on
++ *  @name - name of xattr to remove
++ *  @delegated_inode - on return, will contain an inode pointer that
++ *  a delegation was broken on, NULL if none.
++ */
+ int
+-vfs_removexattr(struct dentry *dentry, const char *name)
++__vfs_removexattr_locked(struct dentry *dentry, const char *name,
++              struct inode **delegated_inode)
+ {
+       struct inode *inode = dentry->d_inode;
+       int error;
+@@ -389,11 +436,14 @@ vfs_removexattr(struct dentry *dentry, c
+       if (error)
+               return error;
+-      inode_lock(inode);
+       error = security_inode_removexattr(dentry, name);
+       if (error)
+               goto out;
++      error = try_break_deleg(inode, delegated_inode);
++      if (error)
++              goto out;
++
+       error = __vfs_removexattr(dentry, name);
+       if (!error) {
+@@ -402,12 +452,32 @@ vfs_removexattr(struct dentry *dentry, c
+       }
+ out:
++      return error;
++}
++EXPORT_SYMBOL_GPL(__vfs_removexattr_locked);
++
++int
++vfs_removexattr(struct dentry *dentry, const char *name)
++{
++      struct inode *inode = dentry->d_inode;
++      struct inode *delegated_inode = NULL;
++      int error;
++
++retry_deleg:
++      inode_lock(inode);
++      error = __vfs_removexattr_locked(dentry, name, &delegated_inode);
+       inode_unlock(inode);
++
++      if (delegated_inode) {
++              error = break_deleg_wait(&delegated_inode);
++              if (!error)
++                      goto retry_deleg;
++      }
++
+       return error;
+ }
+ EXPORT_SYMBOL_GPL(vfs_removexattr);
+-
+ /*
+  * Extended attribute SET operations
+  */
+--- a/include/linux/xattr.h
++++ b/include/linux/xattr.h
+@@ -51,8 +51,10 @@ ssize_t vfs_getxattr(struct dentry *, co
+ ssize_t vfs_listxattr(struct dentry *d, char *list, size_t size);
+ int __vfs_setxattr(struct dentry *, struct inode *, const char *, const void *, size_t, int);
+ int __vfs_setxattr_noperm(struct dentry *, const char *, const void *, size_t, int);
++int __vfs_setxattr_locked(struct dentry *, const char *, const void *, size_t, int, struct inode **);
+ int vfs_setxattr(struct dentry *, const char *, const void *, size_t, int);
+ int __vfs_removexattr(struct dentry *, const char *);
++int __vfs_removexattr_locked(struct dentry *, const char *, struct inode **);
+ int vfs_removexattr(struct dentry *, const char *);
+ ssize_t generic_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size);