]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
binder: introduce transaction reports via netlink
authorLi Li <dualli@google.com>
Sun, 27 Jul 2025 18:29:06 +0000 (18:29 +0000)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 19 Aug 2025 10:53:01 +0000 (12:53 +0200)
Introduce a generic netlink multicast event to report binder transaction
failures to userspace. This allows subscribers to monitor these events
and take appropriate actions, such as stopping a misbehaving application
that is spamming a service with huge amount of transactions.

The multicast event contains full details of the failed transactions,
including the sender/target PIDs, payload size and specific error code.
This interface is defined using a YAML spec, from which the UAPI and
kernel headers and source are auto-generated.

Signed-off-by: Li Li <dualli@google.com>
Signed-off-by: Carlos Llamas <cmllamas@google.com>
Link: https://lore.kernel.org/r/20250727182932.2499194-4-cmllamas@google.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Documentation/netlink/specs/binder.yaml [new file with mode: 0644]
MAINTAINERS
drivers/android/Kconfig
drivers/android/Makefile
drivers/android/binder.c
drivers/android/binder_netlink.c [new file with mode: 0644]
drivers/android/binder_netlink.h [new file with mode: 0644]
include/uapi/linux/android/binder_netlink.h [new file with mode: 0644]

diff --git a/Documentation/netlink/specs/binder.yaml b/Documentation/netlink/specs/binder.yaml
new file mode 100644 (file)
index 0000000..140b77a
--- /dev/null
@@ -0,0 +1,93 @@
+# SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
+#
+# Copyright 2025 Google LLC
+#
+---
+name: binder
+protocol: genetlink
+uapi-header: linux/android/binder_netlink.h
+doc: Binder interface over generic netlink
+
+attribute-sets:
+  -
+    name: report
+    doc: |
+      Attributes included within a transaction failure report. The elements
+      correspond directly with the specific transaction that failed, along
+      with the error returned to the sender e.g. BR_DEAD_REPLY.
+
+    attributes:
+      -
+        name: error
+        type: u32
+        doc: The enum binder_driver_return_protocol returned to the sender.
+      -
+        name: context
+        type: string
+        doc: The binder context where the transaction occurred.
+      -
+        name: from_pid
+        type: u32
+        doc: The PID of the sender process.
+      -
+        name: from_tid
+        type: u32
+        doc: The TID of the sender thread.
+      -
+        name: to_pid
+        type: u32
+        doc: |
+          The PID of the recipient process. This attribute may not be present
+          if the target could not be determined.
+      -
+        name: to_tid
+        type: u32
+        doc: |
+          The TID of the recipient thread. This attribute may not be present
+          if the target could not be determined.
+      -
+        name: is_reply
+        type: flag
+        doc: When present, indicates the failed transaction is a reply.
+      -
+        name: flags
+        type: u32
+        doc: The bitmask of enum transaction_flags from the transaction.
+      -
+        name: code
+        type: u32
+        doc: The application-defined code from the transaction.
+      -
+        name: data_size
+        type: u32
+        doc: The transaction payload size in bytes.
+
+operations:
+  list:
+    -
+      name: report
+      doc: |
+        A multicast event sent to userspace subscribers to notify them about
+        binder transaction failures. The generated report provides the full
+        details of the specific transaction that failed. The intention is for
+        programs to monitor these events and react to the failures as needed.
+
+      attribute-set: report
+      mcgrp: report
+      event:
+        attributes:
+          - error
+          - context
+          - from_pid
+          - from_tid
+          - to_pid
+          - to_tid
+          - is_reply
+          - flags
+          - code
+          - data_size
+
+mcast-groups:
+  list:
+    -
+      name: report
index daf520a13bdf6a991c0160a96620f40308c29ee0..febc92cca659be5951914de926ebfb254a1a4e26 100644 (file)
@@ -1790,6 +1790,7 @@ M:        Suren Baghdasaryan <surenb@google.com>
 L:     linux-kernel@vger.kernel.org
 S:     Supported
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging.git
+F:     Documentation/netlink/specs/binder.yaml
 F:     drivers/android/
 
 ANDROID GOLDFISH PIC DRIVER
index 5b3b8041f8279529e5a40678cf9fff996a721f20..75af3cf472c8f8a3b93698911c6115ff5692943a 100644 (file)
@@ -4,6 +4,7 @@ menu "Android"
 config ANDROID_BINDER_IPC
        bool "Android Binder IPC Driver"
        depends on MMU
+       depends on NET
        default n
        help
          Binder is used in Android for both communication between processes,
index c5d47be0276cdbf99dabc1b1c3c0410ff2426926..f422f91e026b2c421c6036f0d5c4286a9cebe8ee 100644 (file)
@@ -2,5 +2,5 @@
 ccflags-y += -I$(src)                  # needed for trace events
 
 obj-$(CONFIG_ANDROID_BINDERFS)         += binderfs.o
-obj-$(CONFIG_ANDROID_BINDER_IPC)       += binder.o binder_alloc.o
+obj-$(CONFIG_ANDROID_BINDER_IPC)       += binder.o binder_alloc.o binder_netlink.o
 obj-$(CONFIG_ANDROID_BINDER_ALLOC_KUNIT_TEST)  += tests/
index e4f9cbc6c93f4cf1b41754a2f6d8df1c2bf7edfb..edfa15d39d6f12dbf033fc28b64cb082260df20a 100644 (file)
@@ -74,6 +74,7 @@
 
 #include <linux/cacheflush.h>
 
+#include "binder_netlink.h"
 #include "binder_internal.h"
 #include "binder_trace.h"
 
@@ -2993,6 +2994,67 @@ static void binder_set_txn_from_error(struct binder_transaction *t, int id,
        binder_thread_dec_tmpref(from);
 }
 
+/**
+ * binder_netlink_report() - report a transaction failure via netlink
+ * @proc:      the binder proc sending the transaction
+ * @t:         the binder transaction that failed
+ * @data_size: the user provided data size for the transaction
+ * @error:     enum binder_driver_return_protocol returned to sender
+ */
+static void binder_netlink_report(struct binder_proc *proc,
+                                 struct binder_transaction *t,
+                                 u32 data_size,
+                                 u32 error)
+{
+       const char *context = proc->context->name;
+       struct sk_buff *skb;
+       void *hdr;
+
+       if (!genl_has_listeners(&binder_nl_family, &init_net,
+                               BINDER_NLGRP_REPORT))
+               return;
+
+       skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!skb)
+               return;
+
+       hdr = genlmsg_put(skb, 0, 0, &binder_nl_family, 0, BINDER_CMD_REPORT);
+       if (!hdr)
+               goto free_skb;
+
+       if (nla_put_u32(skb, BINDER_A_REPORT_ERROR, error) ||
+           nla_put_string(skb, BINDER_A_REPORT_CONTEXT, context) ||
+           nla_put_u32(skb, BINDER_A_REPORT_FROM_PID, t->from_pid) ||
+           nla_put_u32(skb, BINDER_A_REPORT_FROM_TID, t->from_tid))
+               goto cancel_skb;
+
+       if (t->to_proc &&
+           nla_put_u32(skb, BINDER_A_REPORT_TO_PID, t->to_proc->pid))
+               goto cancel_skb;
+
+       if (t->to_thread &&
+           nla_put_u32(skb, BINDER_A_REPORT_TO_TID, t->to_thread->pid))
+               goto cancel_skb;
+
+       if (t->is_reply && nla_put_flag(skb, BINDER_A_REPORT_IS_REPLY))
+               goto cancel_skb;
+
+       if (nla_put_u32(skb, BINDER_A_REPORT_FLAGS, t->flags) ||
+           nla_put_u32(skb, BINDER_A_REPORT_CODE, t->code) ||
+           nla_put_u32(skb, BINDER_A_REPORT_DATA_SIZE, data_size))
+               goto cancel_skb;
+
+       genlmsg_end(skb, hdr);
+       genlmsg_multicast(&binder_nl_family, skb, 0, BINDER_NLGRP_REPORT,
+                         GFP_KERNEL);
+       return;
+
+cancel_skb:
+       genlmsg_cancel(skb, hdr);
+free_skb:
+       nlmsg_free(skb);
+}
+
 static void binder_transaction(struct binder_proc *proc,
                               struct binder_thread *thread,
                               struct binder_transaction_data *tr, int reply,
@@ -3679,10 +3741,13 @@ static void binder_transaction(struct binder_proc *proc,
                return_error_line = __LINE__;
                goto err_copy_data_failed;
        }
-       if (t->buffer->oneway_spam_suspect)
+       if (t->buffer->oneway_spam_suspect) {
                tcomplete->type = BINDER_WORK_TRANSACTION_ONEWAY_SPAM_SUSPECT;
-       else
+               binder_netlink_report(proc, t, tr->data_size,
+                                     BR_ONEWAY_SPAM_SUSPECT);
+       } else {
                tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
+       }
 
        if (reply) {
                binder_enqueue_thread_work(thread, tcomplete);
@@ -3730,8 +3795,11 @@ static void binder_transaction(struct binder_proc *proc,
                 * process and is put in a pending queue, waiting for the target
                 * process to be unfrozen.
                 */
-               if (return_error == BR_TRANSACTION_PENDING_FROZEN)
+               if (return_error == BR_TRANSACTION_PENDING_FROZEN) {
                        tcomplete->type = BINDER_WORK_TRANSACTION_PENDING;
+                       binder_netlink_report(proc, t, tr->data_size,
+                                             return_error);
+               }
                binder_enqueue_thread_work(thread, tcomplete);
                if (return_error &&
                    return_error != BR_TRANSACTION_PENDING_FROZEN)
@@ -3789,6 +3857,8 @@ err_invalid_target_handle:
                binder_dec_node(target_node, 1, 0);
                binder_dec_node_tmpref(target_node);
        }
+
+       binder_netlink_report(proc, t, tr->data_size, return_error);
        kfree(t);
        binder_stats_deleted(BINDER_STAT_TRANSACTION);
 err_alloc_t_failed:
@@ -7059,12 +7129,19 @@ static int __init binder_init(void)
                }
        }
 
-       ret = init_binderfs();
+       ret = genl_register_family(&binder_nl_family);
        if (ret)
                goto err_init_binder_device_failed;
 
+       ret = init_binderfs();
+       if (ret)
+               goto err_init_binderfs_failed;
+
        return ret;
 
+err_init_binderfs_failed:
+       genl_unregister_family(&binder_nl_family);
+
 err_init_binder_device_failed:
        hlist_for_each_entry_safe(device, tmp, &binder_devices, hlist) {
                misc_deregister(&device->miscdev);
diff --git a/drivers/android/binder_netlink.c b/drivers/android/binder_netlink.c
new file mode 100644 (file)
index 0000000..d05397a
--- /dev/null
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
+/* Do not edit directly, auto-generated from: */
+/*     Documentation/netlink/specs/binder.yaml */
+/* YNL-GEN kernel source */
+
+#include <net/netlink.h>
+#include <net/genetlink.h>
+
+#include "binder_netlink.h"
+
+#include <uapi/linux/android/binder_netlink.h>
+
+/* Ops table for binder */
+static const struct genl_split_ops binder_nl_ops[] = {
+};
+
+static const struct genl_multicast_group binder_nl_mcgrps[] = {
+       [BINDER_NLGRP_REPORT] = { "report", },
+};
+
+struct genl_family binder_nl_family __ro_after_init = {
+       .name           = BINDER_FAMILY_NAME,
+       .version        = BINDER_FAMILY_VERSION,
+       .netnsok        = true,
+       .parallel_ops   = true,
+       .module         = THIS_MODULE,
+       .split_ops      = binder_nl_ops,
+       .n_split_ops    = ARRAY_SIZE(binder_nl_ops),
+       .mcgrps         = binder_nl_mcgrps,
+       .n_mcgrps       = ARRAY_SIZE(binder_nl_mcgrps),
+};
diff --git a/drivers/android/binder_netlink.h b/drivers/android/binder_netlink.h
new file mode 100644 (file)
index 0000000..882c7a6
--- /dev/null
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
+/* Do not edit directly, auto-generated from: */
+/*     Documentation/netlink/specs/binder.yaml */
+/* YNL-GEN kernel header */
+
+#ifndef _LINUX_BINDER_GEN_H
+#define _LINUX_BINDER_GEN_H
+
+#include <net/netlink.h>
+#include <net/genetlink.h>
+
+#include <uapi/linux/android/binder_netlink.h>
+
+enum {
+       BINDER_NLGRP_REPORT,
+};
+
+extern struct genl_family binder_nl_family;
+
+#endif /* _LINUX_BINDER_GEN_H */
diff --git a/include/uapi/linux/android/binder_netlink.h b/include/uapi/linux/android/binder_netlink.h
new file mode 100644 (file)
index 0000000..b218f96
--- /dev/null
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
+/* Do not edit directly, auto-generated from: */
+/*     Documentation/netlink/specs/binder.yaml */
+/* YNL-GEN uapi header */
+
+#ifndef _UAPI_LINUX_ANDROID_BINDER_NETLINK_H
+#define _UAPI_LINUX_ANDROID_BINDER_NETLINK_H
+
+#define BINDER_FAMILY_NAME     "binder"
+#define BINDER_FAMILY_VERSION  1
+
+enum {
+       BINDER_A_REPORT_ERROR = 1,
+       BINDER_A_REPORT_CONTEXT,
+       BINDER_A_REPORT_FROM_PID,
+       BINDER_A_REPORT_FROM_TID,
+       BINDER_A_REPORT_TO_PID,
+       BINDER_A_REPORT_TO_TID,
+       BINDER_A_REPORT_IS_REPLY,
+       BINDER_A_REPORT_FLAGS,
+       BINDER_A_REPORT_CODE,
+       BINDER_A_REPORT_DATA_SIZE,
+
+       __BINDER_A_REPORT_MAX,
+       BINDER_A_REPORT_MAX = (__BINDER_A_REPORT_MAX - 1)
+};
+
+enum {
+       BINDER_CMD_REPORT = 1,
+
+       __BINDER_CMD_MAX,
+       BINDER_CMD_MAX = (__BINDER_CMD_MAX - 1)
+};
+
+#define BINDER_MCGRP_REPORT    "report"
+
+#endif /* _UAPI_LINUX_ANDROID_BINDER_NETLINK_H */