From 2bfddf30aae4cac4a5eb7fc119b4709c5cbe58f6 Mon Sep 17 00:00:00 2001 From: Hang Lu Date: Fri, 9 Apr 2021 17:40:46 +0800 Subject: [PATCH 01/11] BACKPORT: binder: tell userspace to dump current backtrace when detected oneway spamming When async binder buffer got exhausted, some normal oneway transactions will also be discarded and may cause system or application failures. By that time, the binder debug information we dump may not be relevant to the root cause. And this issue is difficult to debug if without the backtrace of the thread sending spam. This change will send BR_ONEWAY_SPAM_SUSPECT to userspace when oneway spamming is detected, request to dump current backtrace. Oneway spamming will be reported only once when exceeding the threshold (target process dips below 80% of its oneway space, and current process is responsible for either more than 50 transactions, or more than 50% of the oneway space). And the detection will restart when the async buffer has returned to a healthy state. Acked-by: Todd Kjos Signed-off-by: Hang Lu Link: https://lore.kernel.org/r/1617961246-4502-3-git-send-email-hangl@codeaurora.org Signed-off-by: Greg Kroah-Hartman Bug: 181190340 Change-Id: Id3d2526099bc89f04d8ad3ad6e48141b2a8f2515 (cherry picked from commit a7dc1e6f99df59799ab0128d9c4e47bbeceb934d) Signed-off-by: Hang Lu [cmllamas: fix trivial merge issue] Signed-off-by: Carlos Llamas --- drivers/android/binder.c | 27 ++++++++++++++++++++++++--- drivers/android/binder_alloc.c | 15 ++++++++++++--- drivers/android/binder_alloc.h | 8 +++++++- drivers/android/binder_internal.h | 6 +++++- include/uapi/linux/android/binder.h | 8 ++++++++ 5 files changed, 56 insertions(+), 8 deletions(-) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 28117ddb9d6d..ee240af42660 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -3570,7 +3570,10 @@ static void binder_transaction(struct binder_proc *proc, return_error_line = __LINE__; goto err_copy_data_failed; } - tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE; + if (t->buffer->oneway_spam_suspect) + tcomplete->type = BINDER_WORK_TRANSACTION_ONEWAY_SPAM_SUSPECT; + else + tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE; t->work.type = BINDER_WORK_TRANSACTION; if (reply) { @@ -4447,9 +4450,14 @@ retry: binder_stat_br(proc, thread, cmd); } break; - case BINDER_WORK_TRANSACTION_COMPLETE: { + case BINDER_WORK_TRANSACTION_COMPLETE: + case BINDER_WORK_TRANSACTION_ONEWAY_SPAM_SUSPECT: { + if (proc->oneway_spam_detection_enabled && + w->type == BINDER_WORK_TRANSACTION_ONEWAY_SPAM_SUSPECT) + cmd = BR_ONEWAY_SPAM_SUSPECT; + else + cmd = BR_TRANSACTION_COMPLETE; binder_inner_proc_unlock(proc); - cmd = BR_TRANSACTION_COMPLETE; kfree(w); binder_stats_deleted(BINDER_STAT_TRANSACTION_COMPLETE); if (put_user(cmd, (uint32_t __user *)ptr)) @@ -5474,6 +5482,18 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) } break; } + case BINDER_ENABLE_ONEWAY_SPAM_DETECTION: { + uint32_t enable; + + if (copy_from_user(&enable, ubuf, sizeof(enable))) { + ret = -EINVAL; + goto err; + } + binder_inner_proc_lock(proc); + proc->oneway_spam_detection_enabled = (bool)enable; + binder_inner_proc_unlock(proc); + break; + } default: ret = -EINVAL; goto err; @@ -6160,6 +6180,7 @@ static const char * const binder_return_strings[] = { "BR_CLEAR_DEATH_NOTIFICATION_DONE", "BR_FAILED_REPLY", "BR_FROZEN_REPLY", + "BR_ONEWAY_SPAM_SUSPECT", }; static const char * const binder_command_strings[] = { diff --git a/drivers/android/binder_alloc.c b/drivers/android/binder_alloc.c index ceb70543ca90..82123c484d0d 100644 --- a/drivers/android/binder_alloc.c +++ b/drivers/android/binder_alloc.c @@ -339,7 +339,7 @@ static inline struct vm_area_struct *binder_alloc_get_vma( return vma; } -static void debug_low_async_space_locked(struct binder_alloc *alloc, int pid) +static bool debug_low_async_space_locked(struct binder_alloc *alloc, int pid) { /* * Find the amount and size of buffers allocated by the current caller; @@ -367,13 +367,19 @@ static void debug_low_async_space_locked(struct binder_alloc *alloc, int pid) /* * Warn if this pid has more than 50 transactions, or more than 50% of - * async space (which is 25% of total buffer size). + * async space (which is 25% of total buffer size). Oneway spam is only + * detected when the threshold is exceeded. */ if (num_buffers > 50 || total_alloc_size > alloc->buffer_size / 4) { binder_alloc_debug(BINDER_DEBUG_USER_ERROR, "%d: pid %d spamming oneway? %zd buffers allocated for a total size of %zd\n", alloc->pid, pid, num_buffers, total_alloc_size); + if (!alloc->oneway_spam_detected) { + alloc->oneway_spam_detected = true; + return true; + } } + return false; } static struct binder_buffer *binder_alloc_new_buf_locked( @@ -526,6 +532,7 @@ static struct binder_buffer *binder_alloc_new_buf_locked( buffer->async_transaction = is_async; buffer->extra_buffers_size = extra_buffers_size; buffer->pid = pid; + buffer->oneway_spam_suspect = false; if (is_async) { alloc->free_async_space -= size; binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC_ASYNC, @@ -537,7 +544,9 @@ static struct binder_buffer *binder_alloc_new_buf_locked( * of async space left (which is less than 10% of total * buffer size). */ - debug_low_async_space_locked(alloc, pid); + buffer->oneway_spam_suspect = debug_low_async_space_locked(alloc, pid); + } else { + alloc->oneway_spam_detected = false; } } return buffer; diff --git a/drivers/android/binder_alloc.h b/drivers/android/binder_alloc.h index f6052c97bce5..399f2b269f2c 100644 --- a/drivers/android/binder_alloc.h +++ b/drivers/android/binder_alloc.h @@ -26,6 +26,8 @@ struct binder_transaction; * @clear_on_free: %true if buffer must be zeroed after use * @allow_user_free: %true if user is allowed to free buffer * @async_transaction: %true if buffer is in use for an async txn + * @oneway_spam_suspect: %true if total async allocate size just exceed + * spamming detect threshold * @debug_id: unique ID for debugging * @transaction: pointer to associated struct binder_transaction * @target_node: struct binder_node associated with this buffer @@ -45,7 +47,8 @@ struct binder_buffer { unsigned clear_on_free:1; unsigned allow_user_free:1; unsigned async_transaction:1; - unsigned debug_id:28; + unsigned oneway_spam_suspect:1; + unsigned debug_id:27; struct binder_transaction *transaction; @@ -87,6 +90,8 @@ struct binder_lru_page { * @buffer_size: size of address space specified via mmap * @pid: pid for associated binder_proc (invariant after init) * @pages_high: high watermark of offset in @pages + * @oneway_spam_detected: %true if oneway spam detection fired, clear that + * flag once the async buffer has returned to a healthy state * * Bookkeeping structure for per-proc address space management for binder * buffers. It is normally initialized during binder_init() and binder_mmap() @@ -107,6 +112,7 @@ struct binder_alloc { uint32_t buffer_free; int pid; size_t pages_high; + bool oneway_spam_detected; }; #ifdef CONFIG_ANDROID_BINDER_IPC_SELFTEST diff --git a/drivers/android/binder_internal.h b/drivers/android/binder_internal.h index 78870970ec71..9e2b3aa92757 100644 --- a/drivers/android/binder_internal.h +++ b/drivers/android/binder_internal.h @@ -155,7 +155,7 @@ enum binder_stat_types { }; struct binder_stats { - atomic_t br[_IOC_NR(BR_FROZEN_REPLY) + 1]; + atomic_t br[_IOC_NR(BR_ONEWAY_SPAM_SUSPECT) + 1]; atomic_t bc[_IOC_NR(BC_REPLY_SG) + 1]; atomic_t obj_created[BINDER_STAT_COUNT]; atomic_t obj_deleted[BINDER_STAT_COUNT]; @@ -174,6 +174,7 @@ struct binder_work { enum binder_work_type { BINDER_WORK_TRANSACTION = 1, BINDER_WORK_TRANSACTION_COMPLETE, + BINDER_WORK_TRANSACTION_ONEWAY_SPAM_SUSPECT, BINDER_WORK_RETURN_ERROR, BINDER_WORK_NODE, BINDER_WORK_DEAD_BINDER, @@ -438,6 +439,8 @@ enum binder_prio_state { * @outer_lock: no nesting under innor or node lock * Lock order: 1) outer, 2) node, 3) inner * @binderfs_entry: process-specific binderfs log file + * @oneway_spam_detection_enabled: process enabled oneway spam detection + * or not * * Bookkeeping structure for binder processes */ @@ -473,6 +476,7 @@ struct binder_proc { spinlock_t inner_lock; spinlock_t outer_lock; struct dentry *binderfs_entry; + bool oneway_spam_detection_enabled; }; /** diff --git a/include/uapi/linux/android/binder.h b/include/uapi/linux/android/binder.h index 14340b0267b7..5d894de61747 100644 --- a/include/uapi/linux/android/binder.h +++ b/include/uapi/linux/android/binder.h @@ -296,6 +296,7 @@ struct binder_frozen_status_info { #define BINDER_SET_CONTEXT_MGR_EXT _IOW('b', 13, struct flat_binder_object) #define BINDER_FREEZE _IOW('b', 14, struct binder_freeze_info) #define BINDER_GET_FROZEN_INFO _IOWR('b', 15, struct binder_frozen_status_info) +#define BINDER_ENABLE_ONEWAY_SPAM_DETECTION _IOW('b', 16, __u32) /* * NOTE: Two special error codes you should check for when calling @@ -483,6 +484,13 @@ enum binder_driver_return_protocol { * The target of the last transaction (either a bcTRANSACTION or * a bcATTEMPT_ACQUIRE) is frozen. No parameters. */ + + BR_ONEWAY_SPAM_SUSPECT = _IO('r', 19), + /* + * Current process sent too many oneway calls to target, and the last + * asynchronous transaction makes the allocated async buffer size exceed + * detection threshold. No parameters. + */ }; enum binder_driver_command_protocol { From 334fe73bdd073688ef70036687a5e4a93d2c6493 Mon Sep 17 00:00:00 2001 From: Li Li Date: Thu, 26 May 2022 15:00:18 -0700 Subject: [PATCH 02/11] FROMGIT: Binder: add TF_UPDATE_TXN to replace outdated txn When the target process is busy, incoming oneway transactions are queued in the async_todo list. If the clients continue sending extra oneway transactions while the target process is frozen, this queue can become too large to accommodate new transactions. That's why binder driver introduced ONEWAY_SPAM_DETECTION to detect this situation. It's helpful to debug the async binder buffer exhausting issue, but the issue itself isn't solved directly. In real cases applications are designed to send oneway transactions repeatedly, delivering updated inforamtion to the target process. Typical examples are Wi-Fi signal strength and some real time sensor data. Even if the apps might only care about the lastet information, all outdated oneway transactions are still accumulated there until the frozen process is thawed later. For this kind of situations, there's no existing method to skip those outdated transactions and deliver the latest one only. This patch introduces a new transaction flag TF_UPDATE_TXN. To use it, use apps can set this new flag along with TF_ONE_WAY. When such an oneway transaction is to be queued into the async_todo list of a frozen process, binder driver will check if any previous pending transactions can be superseded by comparing their code, flags and target node. If such an outdated pending transaction is found, the latest transaction will supersede that outdated one. This effectively prevents the async binder buffer running out and saves unnecessary binder read workloads. Acked-by: Todd Kjos Signed-off-by: Li Li Link: https://lore.kernel.org/r/20220526220018.3334775-2-dualli@chromium.org Signed-off-by: Greg Kroah-Hartman Bug: 231624308 Test: manually check async binder buffer size of frozen apps Test: stress test with kernel 4.14/4.19/5.10/5.15 (cherry picked from commit 9864bb4801331daa48514face9d0f4861e4d485b git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc.git char-misc-next) Change-Id: I1c4bff1eda1ca15aaaad5bf696c8fc00be743176 --- drivers/android/binder.c | 78 +++++++++++++++++++++++++++++ drivers/android/binder_trace.h | 4 ++ include/uapi/linux/android/binder.h | 1 + 3 files changed, 83 insertions(+) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index ee240af42660..f30c555f4584 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -2798,6 +2798,56 @@ static int binder_fixup_parent(struct list_head *pf_head, return binder_add_fixup(pf_head, buffer_offset, bp->buffer, 0); } +/** + * binder_can_update_transaction() - Can a txn be superseded by an updated one? + * @t1: the pending async txn in the frozen process + * @t2: the new async txn to supersede the outdated pending one + * + * Return: true if t2 can supersede t1 + * false if t2 can not supersede t1 + */ +static bool binder_can_update_transaction(struct binder_transaction *t1, + struct binder_transaction *t2) +{ + if ((t1->flags & t2->flags & (TF_ONE_WAY | TF_UPDATE_TXN)) != + (TF_ONE_WAY | TF_UPDATE_TXN) || !t1->to_proc || !t2->to_proc) + return false; + if (t1->to_proc->tsk == t2->to_proc->tsk && t1->code == t2->code && + t1->flags == t2->flags && t1->buffer->pid == t2->buffer->pid && + t1->buffer->target_node->ptr == t2->buffer->target_node->ptr && + t1->buffer->target_node->cookie == t2->buffer->target_node->cookie) + return true; + return false; +} + +/** + * binder_find_outdated_transaction_ilocked() - Find the outdated transaction + * @t: new async transaction + * @target_list: list to find outdated transaction + * + * Return: the outdated transaction if found + * NULL if no outdated transacton can be found + * + * Requires the proc->inner_lock to be held. + */ +static struct binder_transaction * +binder_find_outdated_transaction_ilocked(struct binder_transaction *t, + struct list_head *target_list) +{ + struct binder_work *w; + + list_for_each_entry(w, target_list, entry) { + struct binder_transaction *t_queued; + + if (w->type != BINDER_WORK_TRANSACTION) + continue; + t_queued = container_of(w, struct binder_transaction, work); + if (binder_can_update_transaction(t_queued, t)) + return t_queued; + } + return NULL; +} + /** * binder_proc_transaction() - sends a transaction to a process and wakes it up * @t: transaction to send @@ -2823,6 +2873,7 @@ static int binder_proc_transaction(struct binder_transaction *t, struct binder_node *node = t->buffer->target_node; bool oneway = !!(t->flags & TF_ONE_WAY); bool pending_async = false; + struct binder_transaction *t_outdated = NULL; BUG_ON(!node); binder_node_lock(node); @@ -2858,6 +2909,17 @@ static int binder_proc_transaction(struct binder_transaction *t, } else if (!pending_async) { binder_enqueue_work_ilocked(&t->work, &proc->todo); } else { + if ((t->flags & TF_UPDATE_TXN) && proc->is_frozen) { + t_outdated = binder_find_outdated_transaction_ilocked(t, + &node->async_todo); + if (t_outdated) { + binder_debug(BINDER_DEBUG_TRANSACTION, + "txn %d supersedes %d\n", + t->debug_id, t_outdated->debug_id); + list_del_init(&t_outdated->work.entry); + proc->outstanding_txns--; + } + } binder_enqueue_work_ilocked(&t->work, &node->async_todo); } @@ -2868,6 +2930,22 @@ static int binder_proc_transaction(struct binder_transaction *t, binder_inner_proc_unlock(proc); binder_node_unlock(node); + /* + * To reduce potential contention, free the outdated transaction and + * buffer after releasing the locks. + */ + if (t_outdated) { + struct binder_buffer *buffer = t_outdated->buffer; + + t_outdated->buffer = NULL; + buffer->transaction = NULL; + trace_binder_transaction_update_buffer_release(buffer); + binder_transaction_buffer_release(proc, NULL, buffer, 0, 0); + binder_alloc_free_buf(&proc->alloc, buffer); + kfree(t_outdated); + binder_stats_deleted(BINDER_STAT_TRANSACTION); + } + return 0; } diff --git a/drivers/android/binder_trace.h b/drivers/android/binder_trace.h index a70e23716ad0..8c4a6c3774a1 100644 --- a/drivers/android/binder_trace.h +++ b/drivers/android/binder_trace.h @@ -306,6 +306,10 @@ DEFINE_EVENT(binder_buffer_class, binder_transaction_failed_buffer_release, TP_PROTO(struct binder_buffer *buffer), TP_ARGS(buffer)); +DEFINE_EVENT(binder_buffer_class, binder_transaction_update_buffer_release, + TP_PROTO(struct binder_buffer *buffer), + TP_ARGS(buffer)); + TRACE_EVENT(binder_update_page_range, TP_PROTO(struct binder_alloc *alloc, bool allocate, void __user *start, void __user *end), diff --git a/include/uapi/linux/android/binder.h b/include/uapi/linux/android/binder.h index 5d894de61747..0523f7c020b5 100644 --- a/include/uapi/linux/android/binder.h +++ b/include/uapi/linux/android/binder.h @@ -319,6 +319,7 @@ enum transaction_flags { TF_STATUS_CODE = 0x08, /* contents are a 32-bit status code */ TF_ACCEPT_FDS = 0x10, /* allow replies with file descriptors */ TF_CLEAR_BUF = 0x20, /* clear buffer on txn complete */ + TF_UPDATE_TXN = 0x40, /* update the outdated pending async txn */ }; struct binder_transaction_data { From 31f1f4b2aaf2abdcee69310800f9f4fb24b38696 Mon Sep 17 00:00:00 2001 From: Carlos Llamas Date: Fri, 8 Dec 2023 03:48:42 +0000 Subject: [PATCH 03/11] FROMLIST: binder: fix memory leaks of spam and pending work commit 1aa3aaf8953c84bad398adf6c3cabc9d6685bf7d upstream A transaction complete work is allocated and queued for each transaction. Under certain conditions the work->type might be marked as BINDER_WORK_TRANSACTION_ONEWAY_SPAM_SUSPECT to notify userspace about potential spamming threads or as BINDER_WORK_TRANSACTION_PENDING when the target is currently frozen. However, these work types are not being handled in binder_release_work() so they will leak during a cleanup. This was reported by syzkaller with the following kmemleak dump: BUG: memory leak unreferenced object 0xffff88810e2d6de0 (size 32): comm "syz-executor338", pid 5046, jiffies 4294968230 (age 13.590s) hex dump (first 32 bytes): e0 6d 2d 0e 81 88 ff ff e0 6d 2d 0e 81 88 ff ff .m-......m-..... 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ backtrace: [] kmalloc_trace+0x25/0x90 mm/slab_common.c:1114 [] kmalloc include/linux/slab.h:599 [inline] [] kzalloc include/linux/slab.h:720 [inline] [] binder_transaction+0x573/0x4050 drivers/android/binder.c:3152 [] binder_thread_write+0x6b5/0x1860 drivers/android/binder.c:4010 [] binder_ioctl_write_read drivers/android/binder.c:5066 [inline] [] binder_ioctl+0x1b2c/0x3cf0 drivers/android/binder.c:5352 [] vfs_ioctl fs/ioctl.c:51 [inline] [] __do_sys_ioctl fs/ioctl.c:871 [inline] [] __se_sys_ioctl fs/ioctl.c:857 [inline] [] __x64_sys_ioctl+0xf2/0x140 fs/ioctl.c:857 [] do_syscall_x64 arch/x86/entry/common.c:50 [inline] [] do_syscall_64+0x38/0xb0 arch/x86/entry/common.c:80 [] entry_SYSCALL_64_after_hwframe+0x63/0xcd Fix the leaks by kfreeing these work types in binder_release_work() and handle them as a BINDER_WORK_TRANSACTION_COMPLETE cleanup. Cc: stable@vger.kernel.org Fixes: a7dc1e6f99df ("binder: tell userspace to dump current backtrace when detected oneway spamming") Reported-by: syzbot+7f10c1653e35933c0f1e@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=7f10c1653e35933c0f1e Suggested-by: Alice Ryhl Signed-off-by: Carlos Llamas Reviewed-by: Alice Ryhl Acked-by: Todd Kjos Link: https://lore.kernel.org/r/20230922175138.230331-1-cmllamas@google.com Signed-off-by: Greg Kroah-Hartman [cmllamas: backport to v5.15 by dropping BINDER_WORK_TRANSACTION_PENDING as commit 0567461a7a6e is not present. Remove fixes tag accordingly.] Signed-off-by: Carlos Llamas Signed-off-by: Sasha Levin Link: https://lore.kernel.org/all/20231208034842.997899-1-cmllamas@google.com/ Change-Id: I8e1ee7af87ef5706544e4f320e9498b8f4855a6b [cmllamas: also backport to v5.4 to fix OOT 8a09136176f6] Signed-off-by: Carlos Llamas --- drivers/android/binder.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index f30c555f4584..f2685dcf8880 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -4857,6 +4857,7 @@ static void binder_release_work(struct binder_proc *proc, "undelivered TRANSACTION_ERROR: %u\n", e->cmd); } break; + case BINDER_WORK_TRANSACTION_ONEWAY_SPAM_SUSPECT: case BINDER_WORK_TRANSACTION_COMPLETE: { binder_debug(BINDER_DEBUG_DEAD_TRANSACTION, "undelivered TRANSACTION_COMPLETE\n"); From 460de65538db546d12f63472c248e4ecb37374ee Mon Sep 17 00:00:00 2001 From: Carlos Llamas Date: Thu, 15 Jul 2021 03:18:03 +0000 Subject: [PATCH 04/11] BACKPORT: binderfs: add support for feature files Provide userspace with a mechanism to discover features supported by the binder driver to refrain from using any unsupported ones in the first place. Starting with "oneway_spam_detection" only new features are to be listed under binderfs and all previous ones are assumed to be supported. Assuming an instance of binderfs has been mounted at /dev/binderfs, binder feature files can be found under /dev/binderfs/features/. Usage example: $ mkdir /dev/binderfs $ mount -t binder binder /dev/binderfs $ cat /dev/binderfs/features/oneway_spam_detection 1 Acked-by: Christian Brauner Signed-off-by: Carlos Llamas Link: https://lore.kernel.org/r/20210715031805.1725878-1-cmllamas@google.com Signed-off-by: Greg Kroah-Hartman (cherry picked from commit fc470abf54b2bd6e539065e07905e767b443d719) Bug: 191910201 Signed-off-by: Carlos Llamas [cmllamas: fix merge conflicts due to missing 095cf502b31e] Change-Id: Ia5c03aa1881981bee26459e741134b83d5b59693 --- drivers/android/binderfs.c | 39 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/drivers/android/binderfs.c b/drivers/android/binderfs.c index f303106b3362..98678d965010 100644 --- a/drivers/android/binderfs.c +++ b/drivers/android/binderfs.c @@ -59,12 +59,20 @@ enum binderfs_stats_mode { STATS_GLOBAL, }; +struct binder_features { + bool oneway_spam_detection; +}; + static const match_table_t tokens = { { Opt_max, "max=%d" }, { Opt_stats_mode, "stats=%s" }, { Opt_err, NULL } }; +static struct binder_features binder_features = { + .oneway_spam_detection = true, +}; + static inline struct binderfs_info *BINDERFS_I(const struct inode *inode) { return inode->i_sb->s_fs_info; @@ -589,6 +597,33 @@ out: return dentry; } +static int binder_features_show(struct seq_file *m, void *unused) +{ + bool *feature = m->private; + + seq_printf(m, "%d\n", *feature); + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(binder_features); + +static int init_binder_features(struct super_block *sb) +{ + struct dentry *dentry, *dir; + + dir = binderfs_create_dir(sb->s_root, "features"); + if (IS_ERR(dir)) + return PTR_ERR(dir); + + dentry = binderfs_create_file(dir, "oneway_spam_detection", + &binder_features_fops, + &binder_features.oneway_spam_detection); + if (IS_ERR(dentry)) + return PTR_ERR(dentry); + + return 0; +} + static int init_binder_logs(struct super_block *sb) { struct dentry *binder_logs_root_dir, *dentry, *proc_log_dir; @@ -730,6 +765,10 @@ static int binderfs_fill_super(struct super_block *sb, void *data, int silent) name++; } + ret = init_binder_features(sb); + if (ret) + return ret; + if (info->mount_opts.stats_mode == STATS_GLOBAL) return init_binder_logs(sb); From 4d4f8b7a7f85b105628f27bf8e0eb3aee0b773d8 Mon Sep 17 00:00:00 2001 From: Carlos Llamas Date: Thu, 15 Jul 2021 03:18:04 +0000 Subject: [PATCH 05/11] UPSTREAM: docs: binderfs: add section about feature files Document how binder feature files can be used to determine whether a feature is supported by the binder driver. "oneway_spam_detection" is used as an example as it is the first available feature file. Acked-by: Christian Brauner Signed-off-by: Carlos Llamas Link: https://lore.kernel.org/r/20210715031805.1725878-2-cmllamas@google.com Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 06e1721d2a265d1247093f5ad5ae2958ef10a604) Bug: 191910201 Signed-off-by: Carlos Llamas Change-Id: I9c4542e0ee65dd94a492fe0440ba8f1a48d8b797 --- Documentation/admin-guide/binderfs.rst | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Documentation/admin-guide/binderfs.rst b/Documentation/admin-guide/binderfs.rst index c009671f8434..0cfd5f063480 100644 --- a/Documentation/admin-guide/binderfs.rst +++ b/Documentation/admin-guide/binderfs.rst @@ -66,3 +66,16 @@ that the `rm() `_ tool can be used to delete them. Note that the ``binder-control`` device cannot be deleted since this would make the binderfs instance unuseable. The ``binder-control`` device will be deleted when the binderfs instance is unmounted and all references to it have been dropped. + +Binder features +--------------- + +Assuming an instance of binderfs has been mounted at ``/dev/binderfs``, the +features supported by the binder driver can be located under +``/dev/binderfs/features/``. The presence of individual files can be tested +to determine whether a particular feature is supported by the driver. + +Example:: + + cat /dev/binderfs/features/oneway_spam_detection + 1 From 8c4165a04351941a87befd08483ab8832a3e5fb5 Mon Sep 17 00:00:00 2001 From: Carlos Llamas Date: Thu, 15 Jul 2021 03:18:05 +0000 Subject: [PATCH 06/11] BACKPORT: selftests/binderfs: add test for feature files Verify that feature files are created successfully after mounting a binderfs instance. Note that only "oneway_spam_detection" feature is tested with this patch as it is currently the only feature listed. Acked-by: Christian Brauner Signed-off-by: Carlos Llamas Link: https://lore.kernel.org/r/20210715031805.1725878-3-cmllamas@google.com Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 07e913418ce4ba5eb620dd4668bf91ec94e11136) Bug: 191910201 Signed-off-by: Carlos Llamas [cmllamas: fix merge issues due to missing eaa163caa4cc] Change-Id: I86d7ef34b3099c8714c319e48029aaf3dbf87081 --- .../filesystems/binderfs/binderfs_test.c | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tools/testing/selftests/filesystems/binderfs/binderfs_test.c b/tools/testing/selftests/filesystems/binderfs/binderfs_test.c index 8c2ed962e1c7..e6e679a03bc9 100644 --- a/tools/testing/selftests/filesystems/binderfs/binderfs_test.c +++ b/tools/testing/selftests/filesystems/binderfs/binderfs_test.c @@ -17,6 +17,10 @@ #include #include "../../kselftest.h" +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) +#endif + static ssize_t write_nointr(int fd, const void *buf, size_t count) { ssize_t ret; @@ -140,6 +144,10 @@ static void __do_binderfs_test(void) bool keep = false; struct binderfs_device device = { 0 }; struct binder_version version = { 0 }; + char device_path[sizeof("/dev/binderfs/") + BINDERFS_MAX_NAME]; + static const char * const binder_features[] = { + "oneway_spam_detection", + }; change_to_mountns(); @@ -241,6 +249,20 @@ static void __do_binderfs_test(void) /* binder-control device removal failed as expected */ ksft_inc_xfail_cnt(); + for (int i = 0; i < ARRAY_SIZE(binder_features); i++) { + snprintf(device_path, sizeof(device_path), + "/dev/binderfs/features/%s", binder_features[i]); + fd = open(device_path, O_CLOEXEC | O_RDONLY); + if (fd < 0) { + ksft_exit_fail_msg("%s - Failed to open binder feature: %s", + strerror(errno), binder_features[i]); + } + close(fd); + } + + /* success: binder feature files found */ + ksft_inc_pass_cnt(); + on_error: ret = umount2("/dev/binderfs", MNT_DETACH); keep ?: rmdir_protect_errno("/dev/binderfs"); From d1e87637cdbad4fd36dbf3799204f5a48150bc3f Mon Sep 17 00:00:00 2001 From: Yu-Ting Tseng Date: Tue, 9 Jul 2024 00:00:47 -0700 Subject: [PATCH 07/11] BACKPORT: FROMGIT: binder: frozen notification Frozen processes present a significant challenge in binder transactions. When a process is frozen, it cannot, by design, accept and/or respond to binder transactions. As a result, the sender needs to adjust its behavior, such as postponing transactions until the peer process unfreezes. However, there is currently no way to subscribe to these state change events, making it impossible to implement frozen-aware behaviors efficiently. Introduce a binder API for subscribing to frozen state change events. This allows programs to react to changes in peer process state, mitigating issues related to binder transactions sent to frozen processes. Implementation details: For a given binder_ref, the state of frozen notification can be one of the followings: 1. Userspace doesn't want a notification. binder_ref->freeze is null. 2. Userspace wants a notification but none is in flight. list_empty(&binder_ref->freeze->work.entry) = true 3. A notification is in flight and waiting to be read by userspace. binder_ref_freeze.sent is false. 4. A notification was read by userspace and kernel is waiting for an ack. binder_ref_freeze.sent is true. When a notification is in flight, new state change events are coalesced into the existing binder_ref_freeze struct. If userspace hasn't picked up the notification yet, the driver simply rewrites the state. Otherwise, the notification is flagged as requiring a resend, which will be performed once userspace acks the original notification that's inflight. See https://r.android.com/3070045 for how userspace is going to use this feature. Signed-off-by: Yu-Ting Tseng Acked-by: Carlos Llamas Link: https://lore.kernel.org/r/20240709070047.4055369-4-yutingtseng@google.com Signed-off-by: Greg Kroah-Hartman Bug: 363013421 (cherry picked from commit d579b04a52a183db47dfcb7a44304d7747d551e1 git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc.git char-misc-next) Change-Id: I5dd32abba932ca7d03ae58660143e075ed778b81 [cmllamas: fix merge conflicts due to missing 0567461a7a6e] Signed-off-by: Carlos Llamas --- arch/arm64/tools/gen-hyprel | Bin 0 -> 16512 bytes drivers/android/binder.c | 283 +++++++++++++++++++++++++++- drivers/android/binder_internal.h | 21 ++- include/uapi/linux/android/binder.h | 36 ++++ 4 files changed, 337 insertions(+), 3 deletions(-) create mode 100755 arch/arm64/tools/gen-hyprel diff --git a/arch/arm64/tools/gen-hyprel b/arch/arm64/tools/gen-hyprel new file mode 100755 index 0000000000000000000000000000000000000000..83299628cbfe2637803b5e2f54190ac94f884d43 GIT binary patch literal 16512 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 literal 0 HcmV?d00001 diff --git a/drivers/android/binder.c b/drivers/android/binder.c index f2685dcf8880..84b87d8d25d6 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -1423,6 +1423,7 @@ static void binder_free_ref(struct binder_ref *ref) if (ref->node) binder_free_node(ref->node); kfree(ref->death); + kfree(ref->freeze); kfree(ref); } @@ -3800,6 +3801,155 @@ err_invalid_target_handle: } } +static int +binder_request_freeze_notification(struct binder_proc *proc, + struct binder_thread *thread, + struct binder_handle_cookie *handle_cookie) +{ + struct binder_ref_freeze *freeze; + struct binder_ref *ref; + bool is_frozen; + + freeze = kzalloc(sizeof(*freeze), GFP_KERNEL); + if (!freeze) + return -ENOMEM; + binder_proc_lock(proc); + ref = binder_get_ref_olocked(proc, handle_cookie->handle, false); + if (!ref) { + binder_user_error("%d:%d BC_REQUEST_FREEZE_NOTIFICATION invalid ref %d\n", + proc->pid, thread->pid, handle_cookie->handle); + binder_proc_unlock(proc); + kfree(freeze); + return -EINVAL; + } + + binder_node_lock(ref->node); + + if (ref->freeze || !ref->node->proc) { + binder_user_error("%d:%d invalid BC_REQUEST_FREEZE_NOTIFICATION %s\n", + proc->pid, thread->pid, + ref->freeze ? "already set" : "dead node"); + binder_node_unlock(ref->node); + binder_proc_unlock(proc); + kfree(freeze); + return -EINVAL; + } + binder_inner_proc_lock(ref->node->proc); + is_frozen = ref->node->proc->is_frozen; + binder_inner_proc_unlock(ref->node->proc); + + binder_stats_created(BINDER_STAT_FREEZE); + INIT_LIST_HEAD(&freeze->work.entry); + freeze->cookie = handle_cookie->cookie; + freeze->work.type = BINDER_WORK_FROZEN_BINDER; + freeze->is_frozen = is_frozen; + + ref->freeze = freeze; + + binder_inner_proc_lock(proc); + binder_enqueue_work_ilocked(&ref->freeze->work, &proc->todo); + binder_wakeup_proc_ilocked(proc); + binder_inner_proc_unlock(proc); + + binder_node_unlock(ref->node); + binder_proc_unlock(proc); + return 0; +} + +static int +binder_clear_freeze_notification(struct binder_proc *proc, + struct binder_thread *thread, + struct binder_handle_cookie *handle_cookie) +{ + struct binder_ref_freeze *freeze; + struct binder_ref *ref; + + binder_proc_lock(proc); + ref = binder_get_ref_olocked(proc, handle_cookie->handle, false); + if (!ref) { + binder_user_error("%d:%d BC_CLEAR_FREEZE_NOTIFICATION invalid ref %d\n", + proc->pid, thread->pid, handle_cookie->handle); + binder_proc_unlock(proc); + return -EINVAL; + } + + binder_node_lock(ref->node); + + if (!ref->freeze) { + binder_user_error("%d:%d BC_CLEAR_FREEZE_NOTIFICATION freeze notification not active\n", + proc->pid, thread->pid); + binder_node_unlock(ref->node); + binder_proc_unlock(proc); + return -EINVAL; + } + freeze = ref->freeze; + binder_inner_proc_lock(proc); + if (freeze->cookie != handle_cookie->cookie) { + binder_user_error("%d:%d BC_CLEAR_FREEZE_NOTIFICATION freeze notification cookie mismatch %016llx != %016llx\n", + proc->pid, thread->pid, (u64)freeze->cookie, + (u64)handle_cookie->cookie); + binder_inner_proc_unlock(proc); + binder_node_unlock(ref->node); + binder_proc_unlock(proc); + return -EINVAL; + } + ref->freeze = NULL; + /* + * Take the existing freeze object and overwrite its work type. There are three cases here: + * 1. No pending notification. In this case just add the work to the queue. + * 2. A notification was sent and is pending an ack from userspace. Once an ack arrives, we + * should resend with the new work type. + * 3. A notification is pending to be sent. Since the work is already in the queue, nothing + * needs to be done here. + */ + freeze->work.type = BINDER_WORK_CLEAR_FREEZE_NOTIFICATION; + if (list_empty(&freeze->work.entry)) { + binder_enqueue_work_ilocked(&freeze->work, &proc->todo); + binder_wakeup_proc_ilocked(proc); + } else if (freeze->sent) { + freeze->resend = true; + } + binder_inner_proc_unlock(proc); + binder_node_unlock(ref->node); + binder_proc_unlock(proc); + return 0; +} + +static int +binder_freeze_notification_done(struct binder_proc *proc, + struct binder_thread *thread, + binder_uintptr_t cookie) +{ + struct binder_ref_freeze *freeze = NULL; + struct binder_work *w; + + binder_inner_proc_lock(proc); + list_for_each_entry(w, &proc->delivered_freeze, entry) { + struct binder_ref_freeze *tmp_freeze = + container_of(w, struct binder_ref_freeze, work); + + if (tmp_freeze->cookie == cookie) { + freeze = tmp_freeze; + break; + } + } + if (!freeze) { + binder_user_error("%d:%d BC_FREEZE_NOTIFICATION_DONE %016llx not found\n", + proc->pid, thread->pid, (u64)cookie); + binder_inner_proc_unlock(proc); + return -EINVAL; + } + binder_dequeue_work_ilocked(&freeze->work); + freeze->sent = false; + if (freeze->resend) { + freeze->resend = false; + binder_enqueue_work_ilocked(&freeze->work, &proc->todo); + binder_wakeup_proc_ilocked(proc); + } + binder_inner_proc_unlock(proc); + return 0; +} + /** * binder_free_buf() - free the specified buffer * @proc: binder proc that owns buffer @@ -4275,6 +4425,44 @@ static int binder_thread_write(struct binder_proc *proc, binder_inner_proc_unlock(proc); } break; + case BC_REQUEST_FREEZE_NOTIFICATION: { + struct binder_handle_cookie handle_cookie; + int error; + + if (copy_from_user(&handle_cookie, ptr, sizeof(handle_cookie))) + return -EFAULT; + ptr += sizeof(handle_cookie); + error = binder_request_freeze_notification(proc, thread, + &handle_cookie); + if (error) + return error; + } break; + + case BC_CLEAR_FREEZE_NOTIFICATION: { + struct binder_handle_cookie handle_cookie; + int error; + + if (copy_from_user(&handle_cookie, ptr, sizeof(handle_cookie))) + return -EFAULT; + ptr += sizeof(handle_cookie); + error = binder_clear_freeze_notification(proc, thread, &handle_cookie); + if (error) + return error; + } break; + + case BC_FREEZE_NOTIFICATION_DONE: { + binder_uintptr_t cookie; + int error; + + if (get_user(cookie, (binder_uintptr_t __user *)ptr)) + return -EFAULT; + + ptr += sizeof(cookie); + error = binder_freeze_notification_done(proc, thread, cookie); + if (error) + return error; + } break; + default: pr_err("%d:%d unknown command %d\n", proc->pid, thread->pid, cmd); @@ -4675,6 +4863,46 @@ retry: if (cmd == BR_DEAD_BINDER) goto done; /* DEAD_BINDER notifications can cause transactions */ } break; + + case BINDER_WORK_FROZEN_BINDER: { + struct binder_ref_freeze *freeze; + struct binder_frozen_state_info info; + + memset(&info, 0, sizeof(info)); + freeze = container_of(w, struct binder_ref_freeze, work); + info.is_frozen = freeze->is_frozen; + info.cookie = freeze->cookie; + freeze->sent = true; + binder_enqueue_work_ilocked(w, &proc->delivered_freeze); + binder_inner_proc_unlock(proc); + + if (put_user(BR_FROZEN_BINDER, (uint32_t __user *)ptr)) + return -EFAULT; + ptr += sizeof(uint32_t); + if (copy_to_user(ptr, &info, sizeof(info))) + return -EFAULT; + ptr += sizeof(info); + binder_stat_br(proc, thread, BR_FROZEN_BINDER); + goto done; /* BR_FROZEN_BINDER notifications can cause transactions */ + } break; + + case BINDER_WORK_CLEAR_FREEZE_NOTIFICATION: { + struct binder_ref_freeze *freeze = + container_of(w, struct binder_ref_freeze, work); + binder_uintptr_t cookie = freeze->cookie; + + binder_inner_proc_unlock(proc); + kfree(freeze); + binder_stats_deleted(BINDER_STAT_FREEZE); + if (put_user(BR_CLEAR_FREEZE_NOTIFICATION_DONE, (uint32_t __user *)ptr)) + return -EFAULT; + ptr += sizeof(uint32_t); + if (put_user(cookie, (binder_uintptr_t __user *)ptr)) + return -EFAULT; + ptr += sizeof(binder_uintptr_t); + binder_stat_br(proc, thread, BR_CLEAR_FREEZE_NOTIFICATION_DONE); + } break; + default: binder_inner_proc_unlock(proc); pr_err("%d:%d: bad work type %d\n", @@ -5287,6 +5515,48 @@ static bool binder_txns_pending_ilocked(struct binder_proc *proc) return false; } +static void binder_add_freeze_work(struct binder_proc *proc, bool is_frozen) +{ + struct rb_node *n; + struct binder_ref *ref; + + binder_inner_proc_lock(proc); + for (n = rb_first(&proc->nodes); n; n = rb_next(n)) { + struct binder_node *node; + + node = rb_entry(n, struct binder_node, rb_node); + binder_inner_proc_unlock(proc); + binder_node_lock(node); + hlist_for_each_entry(ref, &node->refs, node_entry) { + /* + * Need the node lock to synchronize + * with new notification requests and the + * inner lock to synchronize with queued + * freeze notifications. + */ + binder_inner_proc_lock(ref->proc); + if (!ref->freeze) { + binder_inner_proc_unlock(ref->proc); + continue; + } + ref->freeze->work.type = BINDER_WORK_FROZEN_BINDER; + if (list_empty(&ref->freeze->work.entry)) { + ref->freeze->is_frozen = is_frozen; + binder_enqueue_work_ilocked(&ref->freeze->work, &ref->proc->todo); + binder_wakeup_proc_ilocked(ref->proc); + } else { + if (ref->freeze->sent && ref->freeze->is_frozen != is_frozen) + ref->freeze->resend = true; + ref->freeze->is_frozen = is_frozen; + } + binder_inner_proc_unlock(ref->proc); + } + binder_node_unlock(node); + binder_inner_proc_lock(proc); + } + binder_inner_proc_unlock(proc); +} + static int binder_ioctl_freeze(struct binder_freeze_info *info, struct binder_proc *target_proc) { @@ -5298,6 +5568,7 @@ static int binder_ioctl_freeze(struct binder_freeze_info *info, target_proc->async_recv = false; target_proc->is_frozen = false; binder_inner_proc_unlock(target_proc); + binder_add_freeze_work(target_proc, false); return 0; } @@ -5330,6 +5601,8 @@ static int binder_ioctl_freeze(struct binder_freeze_info *info, binder_inner_proc_lock(target_proc); target_proc->is_frozen = false; binder_inner_proc_unlock(target_proc); + } else { + binder_add_freeze_work(target_proc, true); } return ret; @@ -5707,6 +5980,7 @@ static int binder_open(struct inode *nodp, struct file *filp) binder_stats_created(BINDER_STAT_PROC); proc->pid = current->group_leader->pid; INIT_LIST_HEAD(&proc->delivered_death); + INIT_LIST_HEAD(&proc->delivered_freeze); INIT_LIST_HEAD(&proc->waiting_threads); filp->private_data = proc; @@ -6260,6 +6534,9 @@ static const char * const binder_return_strings[] = { "BR_FAILED_REPLY", "BR_FROZEN_REPLY", "BR_ONEWAY_SPAM_SUSPECT", + "UNSUPPORTED", + "BR_FROZEN_BINDER", + "BR_CLEAR_FREEZE_NOTIFICATION_DONE", }; static const char * const binder_command_strings[] = { @@ -6282,6 +6559,9 @@ static const char * const binder_command_strings[] = { "BC_DEAD_BINDER_DONE", "BC_TRANSACTION_SG", "BC_REPLY_SG", + "BC_REQUEST_FREEZE_NOTIFICATION", + "BC_CLEAR_FREEZE_NOTIFICATION", + "BC_FREEZE_NOTIFICATION_DONE", }; static const char * const binder_objstat_strings[] = { @@ -6291,7 +6571,8 @@ static const char * const binder_objstat_strings[] = { "ref", "death", "transaction", - "transaction_complete" + "transaction_complete", + "freeze", }; static void print_binder_stats(struct seq_file *m, const char *prefix, diff --git a/drivers/android/binder_internal.h b/drivers/android/binder_internal.h index 9e2b3aa92757..6f7c2c0ba5a5 100644 --- a/drivers/android/binder_internal.h +++ b/drivers/android/binder_internal.h @@ -151,12 +151,13 @@ enum binder_stat_types { BINDER_STAT_DEATH, BINDER_STAT_TRANSACTION, BINDER_STAT_TRANSACTION_COMPLETE, + BINDER_STAT_FREEZE, BINDER_STAT_COUNT }; struct binder_stats { - atomic_t br[_IOC_NR(BR_ONEWAY_SPAM_SUSPECT) + 1]; - atomic_t bc[_IOC_NR(BC_REPLY_SG) + 1]; + atomic_t br[_IOC_NR(BR_CLEAR_FREEZE_NOTIFICATION_DONE) + 1]; + atomic_t bc[_IOC_NR(BC_FREEZE_NOTIFICATION_DONE) + 1]; atomic_t obj_created[BINDER_STAT_COUNT]; atomic_t obj_deleted[BINDER_STAT_COUNT]; }; @@ -180,6 +181,8 @@ struct binder_work { BINDER_WORK_DEAD_BINDER, BINDER_WORK_DEAD_BINDER_AND_CLEAR, BINDER_WORK_CLEAR_DEATH_NOTIFICATION, + BINDER_WORK_FROZEN_BINDER, + BINDER_WORK_CLEAR_FREEZE_NOTIFICATION, } type; }; @@ -301,6 +304,14 @@ struct binder_ref_death { binder_uintptr_t cookie; }; +struct binder_ref_freeze { + struct binder_work work; + binder_uintptr_t cookie; + bool is_frozen:1; + bool sent:1; + bool resend:1; +}; + /** * struct binder_ref_data - binder_ref counts and id * @debug_id: unique ID for the ref @@ -333,6 +344,8 @@ struct binder_ref_data { * @node indicates the node must be freed * @death: pointer to death notification (ref_death) if requested * (protected by @node->lock) + * @freeze: pointer to freeze notification (ref_freeze) if requested + * (protected by @node->lock) * * Structure to track references from procA to target node (on procB). This * structure is unsafe to access without holding @proc->outer_lock. @@ -349,6 +362,7 @@ struct binder_ref { struct binder_proc *proc; struct binder_node *node; struct binder_ref_death *death; + struct binder_ref_freeze *freeze; }; /** @@ -419,6 +433,8 @@ enum binder_prio_state { * (atomics, no lock needed) * @delivered_death: list of delivered death notification * (protected by @inner_lock) + * @delivered_freeze: list of delivered freeze notification + * (protected by @inner_lock) * @max_threads: cap on number of binder threads * (protected by @inner_lock) * @requested_threads: number of binder threads requested but not @@ -465,6 +481,7 @@ struct binder_proc { struct list_head todo; struct binder_stats stats; struct list_head delivered_death; + struct list_head delivered_freeze; int max_threads; int requested_threads; int requested_threads_started; diff --git a/include/uapi/linux/android/binder.h b/include/uapi/linux/android/binder.h index 0523f7c020b5..d59a424b79fe 100644 --- a/include/uapi/linux/android/binder.h +++ b/include/uapi/linux/android/binder.h @@ -284,6 +284,12 @@ struct binder_frozen_status_info { __u32 async_recv; }; +struct binder_frozen_state_info { + binder_uintptr_t cookie; + __u32 is_frozen; + __u32 reserved; +}; + #define BINDER_WRITE_READ _IOWR('b', 1, struct binder_write_read) #define BINDER_SET_IDLE_TIMEOUT _IOW('b', 3, __s64) #define BINDER_SET_MAX_THREADS _IOW('b', 5, __u32) @@ -492,6 +498,17 @@ enum binder_driver_return_protocol { * asynchronous transaction makes the allocated async buffer size exceed * detection threshold. No parameters. */ + + BR_FROZEN_BINDER = _IOR('r', 21, struct binder_frozen_state_info), + /* + * The cookie and a boolean (is_frozen) that indicates whether the process + * transitioned into a frozen or an unfrozen state. + */ + + BR_CLEAR_FREEZE_NOTIFICATION_DONE = _IOR('r', 22, binder_uintptr_t), + /* + * void *: cookie + */ }; enum binder_driver_command_protocol { @@ -575,6 +592,25 @@ enum binder_driver_command_protocol { /* * binder_transaction_data_sg: the sent command. */ + + BC_REQUEST_FREEZE_NOTIFICATION = + _IOW('c', 19, struct binder_handle_cookie), + /* + * int: handle + * void *: cookie + */ + + BC_CLEAR_FREEZE_NOTIFICATION = _IOW('c', 20, + struct binder_handle_cookie), + /* + * int: handle + * void *: cookie + */ + + BC_FREEZE_NOTIFICATION_DONE = _IOW('c', 21, binder_uintptr_t), + /* + * void *: cookie + */ }; #endif /* _UAPI_LINUX_BINDER_H */ From 1063c2fa628aca25c8f2f8e2ffdd6ef721155680 Mon Sep 17 00:00:00 2001 From: Yu-Ting Tseng Date: Tue, 9 Jul 2024 00:00:49 -0700 Subject: [PATCH 08/11] BACKPORT: FROMGIT: binder: frozen notification binder_features flag Add a flag to binder_features to indicate that the freeze notification feature is available. Signed-off-by: Yu-Ting Tseng Acked-by: Carlos Llamas Link: https://lore.kernel.org/r/20240709070047.4055369-6-yutingtseng@google.com Signed-off-by: Greg Kroah-Hartman Bug: 363013421 (cherry picked from commit 30b968b002a92870325a5c9d1ce78eba0ce386e7 git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc.git char-misc-next) Change-Id: Ic26c8ae42d27c6fd8f5daed5eecabd1652e29502 [cmllamas: fix trivial conflicts due to missing extended_error] Signed-off-by: Carlos Llamas --- drivers/android/binderfs.c | 8 ++++++++ .../selftests/filesystems/binderfs/binderfs_test.c | 1 + 2 files changed, 9 insertions(+) diff --git a/drivers/android/binderfs.c b/drivers/android/binderfs.c index 98678d965010..d715305d8fcb 100644 --- a/drivers/android/binderfs.c +++ b/drivers/android/binderfs.c @@ -61,6 +61,7 @@ enum binderfs_stats_mode { struct binder_features { bool oneway_spam_detection; + bool freeze_notification; }; static const match_table_t tokens = { @@ -71,6 +72,7 @@ static const match_table_t tokens = { static struct binder_features binder_features = { .oneway_spam_detection = true, + .freeze_notification = true, }; static inline struct binderfs_info *BINDERFS_I(const struct inode *inode) @@ -621,6 +623,12 @@ static int init_binder_features(struct super_block *sb) if (IS_ERR(dentry)) return PTR_ERR(dentry); + dentry = binderfs_create_file(dir, "freeze_notification", + &binder_features_fops, + &binder_features.freeze_notification); + if (IS_ERR(dentry)) + return PTR_ERR(dentry); + return 0; } diff --git a/tools/testing/selftests/filesystems/binderfs/binderfs_test.c b/tools/testing/selftests/filesystems/binderfs/binderfs_test.c index e6e679a03bc9..8fca95b5beb9 100644 --- a/tools/testing/selftests/filesystems/binderfs/binderfs_test.c +++ b/tools/testing/selftests/filesystems/binderfs/binderfs_test.c @@ -147,6 +147,7 @@ static void __do_binderfs_test(void) char device_path[sizeof("/dev/binderfs/") + BINDERFS_MAX_NAME]; static const char * const binder_features[] = { "oneway_spam_detection", + "freeze_notification", }; change_to_mountns(); From be02156857e4768e44930e355e5828f0739202d8 Mon Sep 17 00:00:00 2001 From: Carlos Llamas Date: Thu, 29 Aug 2024 18:18:15 +0000 Subject: [PATCH 09/11] ANDROID: binder: fix KMI issues due to frozen notification The patches to support binder's frozen notification feature break the KMI. This change fixes such issues by (1) moving proc->delivered_freeze into the existing proc_wrapper struction, (2) dropping the frozen stats support and (3) amending the STG due to a harmless enum binder_work_type addition. These are the reported KMI issues fixed by this patch: function symbol 'int __traceiter_binder_transaction_received(void*, struct binder_transaction*)' changed CRC changed from 0x74e9c98b to 0xfe0f8640 type 'struct binder_proc' changed byte size changed from 584 to 632 member 'struct list_head delivered_death' changed offset changed by 256 member 'struct list_head delivered_freeze' was added 13 members ('u32 max_threads' .. 'u64 android_oem_data1') changed offset changed by 384 type 'struct binder_thread' changed byte size changed from 464 to 496 2 members ('atomic_t tmp_ref' .. 'bool is_dead') changed offset changed by 224 4 members ('struct task_struct* task' .. 'enum binder_prio_state prio_state') changed offset changed by 256 type 'struct binder_stats' changed byte size changed from 216 to 244 member changed from 'atomic_t br[21]' to 'atomic_t br[23]' type changed from 'atomic_t[21]' to 'atomic_t[23]' number of elements changed from 21 to 23 member changed from 'atomic_t bc[19]' to 'atomic_t bc[22]' offset changed from 672 to 736 type changed from 'atomic_t[19]' to 'atomic_t[22]' number of elements changed from 19 to 22 member changed from 'atomic_t obj_created[7]' to 'atomic_t obj_created[8]' offset changed from 1280 to 1440 type changed from 'atomic_t[7]' to 'atomic_t[8]' number of elements changed from 7 to 8 member changed from 'atomic_t obj_deleted[7]' to 'atomic_t obj_deleted[8]' offset changed from 1504 to 1696 type changed from 'atomic_t[7]' to 'atomic_t[8]' number of elements changed from 7 to 8 type 'enum binder_work_type' changed enumerator 'BINDER_WORK_FROZEN_BINDER' (10) was added enumerator 'BINDER_WORK_CLEAR_FREEZE_NOTIFICATION' (11) was added Bug: 363013421 Change-Id: If9f1f14a2eda215a4c9cb0823c50c8e0e8079ef1 Signed-off-by: Carlos Llamas --- drivers/android/binder.c | 15 +++------------ drivers/android/binder_internal.h | 19 +++++++++++++------ 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 84b87d8d25d6..a54787f57eb3 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -3838,7 +3838,6 @@ binder_request_freeze_notification(struct binder_proc *proc, is_frozen = ref->node->proc->is_frozen; binder_inner_proc_unlock(ref->node->proc); - binder_stats_created(BINDER_STAT_FREEZE); INIT_LIST_HEAD(&freeze->work.entry); freeze->cookie = handle_cookie->cookie; freeze->work.type = BINDER_WORK_FROZEN_BINDER; @@ -3924,7 +3923,7 @@ binder_freeze_notification_done(struct binder_proc *proc, struct binder_work *w; binder_inner_proc_lock(proc); - list_for_each_entry(w, &proc->delivered_freeze, entry) { + list_for_each_entry(w, &proc_wrapper(proc)->delivered_freeze, entry) { struct binder_ref_freeze *tmp_freeze = container_of(w, struct binder_ref_freeze, work); @@ -4873,7 +4872,7 @@ retry: info.is_frozen = freeze->is_frozen; info.cookie = freeze->cookie; freeze->sent = true; - binder_enqueue_work_ilocked(w, &proc->delivered_freeze); + binder_enqueue_work_ilocked(w, &proc_wrapper(proc)->delivered_freeze); binder_inner_proc_unlock(proc); if (put_user(BR_FROZEN_BINDER, (uint32_t __user *)ptr)) @@ -4893,7 +4892,6 @@ retry: binder_inner_proc_unlock(proc); kfree(freeze); - binder_stats_deleted(BINDER_STAT_FREEZE); if (put_user(BR_CLEAR_FREEZE_NOTIFICATION_DONE, (uint32_t __user *)ptr)) return -EFAULT; ptr += sizeof(uint32_t); @@ -5980,7 +5978,7 @@ static int binder_open(struct inode *nodp, struct file *filp) binder_stats_created(BINDER_STAT_PROC); proc->pid = current->group_leader->pid; INIT_LIST_HEAD(&proc->delivered_death); - INIT_LIST_HEAD(&proc->delivered_freeze); + INIT_LIST_HEAD(&proc_wrapper(proc)->delivered_freeze); INIT_LIST_HEAD(&proc->waiting_threads); filp->private_data = proc; @@ -6534,9 +6532,6 @@ static const char * const binder_return_strings[] = { "BR_FAILED_REPLY", "BR_FROZEN_REPLY", "BR_ONEWAY_SPAM_SUSPECT", - "UNSUPPORTED", - "BR_FROZEN_BINDER", - "BR_CLEAR_FREEZE_NOTIFICATION_DONE", }; static const char * const binder_command_strings[] = { @@ -6559,9 +6554,6 @@ static const char * const binder_command_strings[] = { "BC_DEAD_BINDER_DONE", "BC_TRANSACTION_SG", "BC_REPLY_SG", - "BC_REQUEST_FREEZE_NOTIFICATION", - "BC_CLEAR_FREEZE_NOTIFICATION", - "BC_FREEZE_NOTIFICATION_DONE", }; static const char * const binder_objstat_strings[] = { @@ -6572,7 +6564,6 @@ static const char * const binder_objstat_strings[] = { "death", "transaction", "transaction_complete", - "freeze", }; static void print_binder_stats(struct seq_file *m, const char *prefix, diff --git a/drivers/android/binder_internal.h b/drivers/android/binder_internal.h index 6f7c2c0ba5a5..7a6c74313727 100644 --- a/drivers/android/binder_internal.h +++ b/drivers/android/binder_internal.h @@ -151,13 +151,12 @@ enum binder_stat_types { BINDER_STAT_DEATH, BINDER_STAT_TRANSACTION, BINDER_STAT_TRANSACTION_COMPLETE, - BINDER_STAT_FREEZE, BINDER_STAT_COUNT }; struct binder_stats { - atomic_t br[_IOC_NR(BR_CLEAR_FREEZE_NOTIFICATION_DONE) + 1]; - atomic_t bc[_IOC_NR(BC_FREEZE_NOTIFICATION_DONE) + 1]; + atomic_t br[_IOC_NR(BR_ONEWAY_SPAM_SUSPECT) + 1]; + atomic_t bc[_IOC_NR(BC_REPLY_SG) + 1]; atomic_t obj_created[BINDER_STAT_COUNT]; atomic_t obj_deleted[BINDER_STAT_COUNT]; }; @@ -181,8 +180,10 @@ struct binder_work { BINDER_WORK_DEAD_BINDER, BINDER_WORK_DEAD_BINDER_AND_CLEAR, BINDER_WORK_CLEAR_DEATH_NOTIFICATION, +#ifndef __GENKSYMS__ BINDER_WORK_FROZEN_BINDER, BINDER_WORK_CLEAR_FREEZE_NOTIFICATION, +#endif } type; }; @@ -433,8 +434,6 @@ enum binder_prio_state { * (atomics, no lock needed) * @delivered_death: list of delivered death notification * (protected by @inner_lock) - * @delivered_freeze: list of delivered freeze notification - * (protected by @inner_lock) * @max_threads: cap on number of binder threads * (protected by @inner_lock) * @requested_threads: number of binder threads requested but not @@ -481,7 +480,6 @@ struct binder_proc { struct list_head todo; struct binder_stats stats; struct list_head delivered_death; - struct list_head delivered_freeze; int max_threads; int requested_threads; int requested_threads_started; @@ -502,6 +500,8 @@ struct binder_proc { * @cred struct cred associated with the `struct file` * in binder_open() * (invariant after initialized) + * @delivered_freeze: list of delivered freeze notification + * (protected by @inner_lock) * * Extended binder_proc -- needed to add the "cred" field without * changing the KMI for binder_proc. @@ -509,6 +509,7 @@ struct binder_proc { struct binder_proc_ext { struct binder_proc proc; const struct cred *cred; + struct list_head delivered_freeze; }; static inline const struct cred *binder_get_cred(struct binder_proc *proc) @@ -519,6 +520,12 @@ static inline const struct cred *binder_get_cred(struct binder_proc *proc) return eproc->cred; } +static inline +struct binder_proc_ext *proc_wrapper(struct binder_proc *proc) +{ + return container_of(proc, struct binder_proc_ext, proc); +} + /** * struct binder_thread - binder thread bookkeeping * @proc: binder process for this thread From a03c6437cfe8318cdf980ac454cb73298d9d4d8e Mon Sep 17 00:00:00 2001 From: Carlos Llamas Date: Sat, 7 Sep 2024 01:47:39 +0000 Subject: [PATCH 10/11] ANDROID: fix ENOMEM check of binder_proc_ext The check should be done against 'eproc' before it gets dereferenced. Fixes: d49297739550 ("BACKPORT: binder: use euid from cred instead of using task") Change-Id: Ief0c08212c4da8bdfdf628474de9dd30ee5a8db0 Signed-off-by: Carlos Llamas --- drivers/android/binder.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index a54787f57eb3..43fb1c0c9034 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -5944,9 +5944,9 @@ static int binder_open(struct inode *nodp, struct file *filp) current->group_leader->pid, current->pid); eproc = kzalloc(sizeof(*eproc), GFP_KERNEL); - proc = &eproc->proc; - if (proc == NULL) + if (eproc == NULL) return -ENOMEM; + proc = &eproc->proc; spin_lock_init(&proc->inner_lock); spin_lock_init(&proc->outer_lock); get_task_struct(current->group_leader); From d62984adb112e9b02a3e754e0b5b3295d7501600 Mon Sep 17 00:00:00 2001 From: Carlos Llamas Date: Mon, 9 Sep 2024 22:11:51 +0000 Subject: [PATCH 11/11] ANDROID: delete tool added by mistake Remove the gen-hyprel binary added accidentally while backporting a patch into an older branch. The tool gets generated in newer builds and wasn't part of the gitignore file here. Fixes: d1e87637cdba ("BACKPORT: FROMGIT: binder: frozen notification") Change-Id: I103358cb2ca9c5fb934f047033e44c04fe85298d Signed-off-by: Carlos Llamas --- arch/arm64/tools/gen-hyprel | Bin 16512 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100755 arch/arm64/tools/gen-hyprel diff --git a/arch/arm64/tools/gen-hyprel b/arch/arm64/tools/gen-hyprel deleted file mode 100755 index 83299628cbfe2637803b5e2f54190ac94f884d43..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16512 zcmeHO4R9OBbzbmCqG*Yr6v?He*1RxcOxYwvilR&@q6|=?bo5XXqaRxxM?OIWKw<&{ zEO3aZG_k2rbc@1toK9?KGMQHOq-iFJrDCnxx9Ap2aBLJehy~!Qq)h4Eia@K%rHXu7qve&LWY<8IBJ}r_STN-XiIQEZGNKz` zD(V%I?5e4%`9j*YP~n)eKHHw5^rC=Xq6DBY&9x)dY@1Yz*`9p*fX<81{bxH8lii%Q zo6~k#N&%5xQ_d&lg#Np=zd~w2N5zmwAqwpdYP&+ZUE`Tj8&vkaYWw~(?G0+X7n+ry zLfWdtf+@H6aoFipYsC8#KFxb|d!^1pyFL$0by~%`Q1Hmc4eLU_woot}A8#A)-q^Nr zgDVzst(Of`T+{}QshztIh>8g@Lo&9f`f~hJT1mg>!ZQz@dg&$mwKu-H>?ePpJoU^M z&9&@t3`1%r-H8k$E0>Q916#TRwEC$w)6|=oj+Zy!-!y+&i4TkN27>nC6 zF%*i#{30^u4~v-X^GBl~QIJR!mZ0H?6^aab?O-G<{Nq7ejE;K8L;&t=L5^vFZWKEQ z`g^xo>s_6l+3I>%SGL}{L0J9!cUkc79}dQBe{}z@ZJ|imzu$W#L?I53M#4HoOF@eQ zX>?X%WL9SXRGp4hQbyypQhX9A;p_1`Uw8<^xJtyeEWRqZh(`T{=D#cT72-bdRQSHh z`80k{$uH^qBFpFW@6|!K(zBfTnOl{dt|2;yT%OL6>sz2y&F07vV76S!kuT0laX-$H z)3wQk#{=CbI0sVaRtWmpsZdpUtZ^%bA`SThxS^a&?%gw17yCO4dPCixhWH!o7*E3LMnxDnd+2}@%_z}{l=Q1eG zPY_R2s`RYnzehYxY0@*2|2FY7wMw6o{5OcFsZIK%!dNX)2SRko=z$ zPt%F?nB@PAc$&JT4@>@Y#M2ZlJt+BF@PP-w%i_7ibGzq&Xa9kH=JB6@38KkNg}u&9 zUT-FQ4g=XSV@`Lyi7lC(Y#DH61e@zcCX%A=squ z05tLB)Qs~_FQH>*; zkfK^Y-JDgKb6LV&;J}^n#4;8m8aD4uZ za89Mcnn|<8oVw^d(WP`-O~*MivE)G)k~;Og^TaKLIrqu}bGpZi=O(jq`_cwP_*v*v zm*1mqEb*dlCRZE*ah~yP=})|5o|{`>I%dsHPdF#HArt+NcZX=EJ=w0F+;GTsa z4DBfpIkcZ$3edv<{R4F@^{8t2YfyX6jzeT7&LLHA;?V?Fd!$urBn!V=!H*I==>+gW z0#+`$9|GVT6}~%*->vXF34iuAG}tGD_*d6M3I zmjkVWC+F#BJZEp9Ad@TJ0>JYa1|M?R_3{$p&9c((lP@In@ef>Syy3 zcvu1Z6>yaXz5rmVnqV^uwn@R76zpaVdsN;7RxF0pb9Oo6@;v5N0d)Y8z6)yE;RY4* zPj4c`Jqi&a#A!yXREQ@eqE8|Inh;-P#43e2DG>t-@gqV+8L?U+?v{uh3bAxC5Q9Jj zT+YT5kkNg%;}`vj=k78Sub7iRy*#)d=bKJz&lI|vSz)ae;;pM_`u%_|4XHU@{R)vy zZ`&*I^Rt!e`1x6snbY@!Cp88Bti*SHo5i z+dTX9Q7FlZek=2S9U&~2at6v7C}*IYfpP}Q87ODq|5yebc>8c5<{kELHkxC`7Jn#U z1;cn1wY3)Sf$%(K#r#9`w#gE%;ZWp=Hw0L7tX8;=Mxs8WUAV>~V;UlRF5gv=ug%7` zNIc{-!V%k`_gMx$G%y0ekZh<{6vYkuZG&EA#bZV!P+*|*TGWnSf)&7XaNbzVAEiD> z->#T##qcK1*xF+djRab5La$x^5nptz5%3~HpV1N;iS;yx;;lxMD|ij;?`ig}tyO^( z_0N9Vw)(B0&mXq$Y(Hf5^cd~qw*>NBw~sfEE8nVi)%geX+adkl*mvUxbeo&+tNFM# z-u24w!0*~`r}DewckOqB^t(PU3(b7mK(8kD+VS&#dFYaUJN}D)Eh~;!yB!Of{XqoXxxQ82YmJ+6|A2>U;?C(O;2wj9lhGWn6^_-FAET#S ztOAOG*E1O#^tYg20bTq?Ci7j;(B(`9kIC{I6%gV0^j_f@uXn6oys+j0$HIF0c7;AF zF@V!4CRho)5Nn_#{A@atIYq#_`WhsnS~DSTUG=e>I@d@ygr?yal;d_`|v!~K=Lb;fj6Z(YlM)tx+fRztebTNYwNlp@zk{d z0VFJX>uNk$$0(O_2Fe*IXP}&cat8jNWq`krn@TG5Q%>cuuJAW%Piwt?oDx4)uO8oVraNPVvnk^Ij-9CHyrNlR6uS=FMLnEqm0$ z$NOP_44!-uOD&dKK06YPS>E`#7>bOJh5T5QfQO<8+9MFOywRxln1!Xo=rIw9dPn`1 zFFrbY3??}omS}@^9@MgS?Dg#Gv-);#xA4jS_CMV1+10-dR@hf!^_g00Zr>}codbJ% zJpo5G53)MiRPcT~31~}0KG;voIYM?-Ze7UJ z%FPdX5+n+EwH1LkN)!O{_bOPvSi~CfhJCb;p??o7eZjC5kNJH9NyWiB5{qena`y!- zP3L)}T@`r(%fjvmPAqMkD7qJdw`t^=%6%DG33g-TZwAR~WFG09>P;Vc3gL|zS~w8+!j-$tWc!biNZ5#jP3 z3!@1|?Wodx${&rw5fR zm0e^xWvT|Uvfw&e@QwzD(0s%u&j^kNjtgf`xNs?r;tDBp_5a63G{40yk8W?WH0XH+ zQ=Z>ywZw9r=tuBRb4s@F*7i&d=yD6sGg!6}m0bIS+MX$`tK}BH-?OYAm0WwC7cn&$ zsA-}79gzP9b5xF>=j}{ew4I*LX6O0r){6?QxUoIY>zRIul8ZtyQkDD9^J|(9QJ2}C zpA(q!a{(Z-Xm`wtKSGV>;B3$HZl+Bd$?-Fe>D|!NoSu1}*E4l%`@;COSWKfrIc0l( zu3*Z~72JMd{~y-&z1kr^r!c*&+3E89~^d_vy{7BeW1Lie!&$&-6WLa_xCtAf@d`vX-jCc1#z*IM<%%?St(K&vCLF zwqt%Vj7i4vA9gFvVQoLgKuwJTeyMI>!?RkpZ_G+mg<_?WOS5BFspN4&JDku1?hhcP qcP|`2-+$@);Mk=hAG%cKkpk!3F2nF7Qq=yDTNHvmHO(yrtoU~%FhP(2