From 2cf28ac7ab0e1ddd26caaa4530887e09822324a3 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 10 Aug 2020 14:11:03 +0200 Subject: [PATCH] 4.9-stable patches added patches: binder-prevent-context-manager-from-incrementing-ref-0.patch xattr-break-delegations-in-set-remove-xattr.patch --- ...text-manager-from-incrementing-ref-0.patch | 86 +++++++++ queue-4.9/series | 2 + ...reak-delegations-in-set-remove-xattr.patch | 181 ++++++++++++++++++ 3 files changed, 269 insertions(+) create mode 100644 queue-4.9/binder-prevent-context-manager-from-incrementing-ref-0.patch create mode 100644 queue-4.9/xattr-break-delegations-in-set-remove-xattr.patch 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 index 00000000000..1f398ee4cdc --- /dev/null +++ b/queue-4.9/binder-prevent-context-manager-from-incrementing-ref-0.patch @@ -0,0 +1,86 @@ +From 4b836a1426cb0f1ef2a6e211d7e553221594f8fc Mon Sep 17 00:00:00 2001 +From: Jann Horn +Date: Mon, 27 Jul 2020 14:04:24 +0200 +Subject: binder: Prevent context manager from incrementing ref 0 + +From: Jann Horn + +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. +. + +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 +Signed-off-by: Jann Horn +Reviewed-by: Martijn Coenen +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 +Signed-off-by: Greg Kroah-Hartman + +--- + 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) { diff --git a/queue-4.9/series b/queue-4.9/series index c6b08dc93c6..1fc4d673165 100644 --- a/queue-4.9/series +++ b/queue-4.9/series @@ -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 index 00000000000..d8f233b57ed --- /dev/null +++ b/queue-4.9/xattr-break-delegations-in-set-remove-xattr.patch @@ -0,0 +1,181 @@ +From 08b5d5014a27e717826999ad20e394a8811aae92 Mon Sep 17 00:00:00 2001 +From: Frank van der Linden +Date: Tue, 23 Jun 2020 22:39:18 +0000 +Subject: xattr: break delegations in {set,remove}xattr + +From: Frank van der Linden + +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 +Signed-off-by: Frank van der Linden +Signed-off-by: Chuck Lever +Signed-off-by: Greg Kroah-Hartman + +--- + 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); -- 2.47.3