From dd55f146240edebbe004b5bf964fed6c1a0bea98 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Tue, 4 Mar 2025 16:13:13 +0000 Subject: [PATCH] ANDROID: binder: change how Rust Binder is loaded The binder.impl parameter currently controls whether C Binder is loaded or not, even if no Rust Binder driver is present. This patch changes it so that C Binder always gets loaded even if binder.impl=rust is set. To actually load a different Binder driver, it must explicitly ask C Binder to unload itself. This ensures that: 1. If binder.impl=rust is set on a device without Rust Binder available, it will boot normally with C Binder instead of failing to boot. 2. By not listing unload_binder in the symbol list, we ensure that Binder can only be loaded as a GKI module, not as a vendor module. Bug: 388786466 Change-Id: I791154e8f10abd5c470bb697b5e1035c33af897f Signed-off-by: Alice Ryhl --- drivers/android/binder.c | 117 ++++++++++++++++++++++-------- drivers/android/binder_internal.h | 8 ++ drivers/android/binderfs.c | 9 +++ 3 files changed, 103 insertions(+), 31 deletions(-) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index b00ebe1660a0..f0c1b4c8fe42 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -7196,34 +7196,6 @@ const struct binder_debugfs_entry binder_debugfs_entries[] = { {} /* terminator */ }; -bool binder_use_rust; -EXPORT_SYMBOL_GPL(binder_use_rust); - -static int binder_impl_param_set(const char *buffer, const struct kernel_param *kp) -{ - if (!strcmp(buffer, "rust")) - binder_use_rust = true; - else if (!strcmp(buffer, "c")) - binder_use_rust = false; - else - return -EINVAL; - - return 0; -} - -static int binder_impl_param_get(char *buffer, const struct kernel_param *kp) -{ - /* The buffer is 4k bytes, so this will not overflow. */ - return sprintf(buffer, "%s\n", binder_use_rust ? "rust" : "c"); -} - -static const struct kernel_param_ops binder_impl_param_ops = { - .set = binder_impl_param_set, - .get = binder_impl_param_get, -}; - -module_param_cb(impl, &binder_impl_param_ops, NULL, 0444); - static int __init init_binder_device(const char *name) { int ret; @@ -7262,9 +7234,6 @@ static int __init binder_init(void) char *device_names = NULL; const struct binder_debugfs_entry *db_entry; - if (binder_use_rust) - return 0; - ret = binder_alloc_shrinker_init(); if (ret) return ret; @@ -7328,6 +7297,92 @@ err_alloc_device_names_failed: device_initcall(binder_init); +#define BINDER_USE_C 0 +#define BINDER_USE_RUST 1 +#define BINDER_USE_RUST_LOADED 2 +int binder_use_rust; +EXPORT_SYMBOL_GPL(binder_use_rust); + +static DEFINE_MUTEX(binder_use_rust_lock); + +/* + * Called by Rust Binder to unload the C Binder driver. + */ +int unload_binder(void) +{ + int ret = 0; + + if (!IS_ENABLED(CONFIG_ANDROID_BINDERFS)) + return -EINVAL; + + mutex_lock(&binder_use_rust_lock); + if (binder_use_rust == BINDER_USE_RUST) + binder_use_rust = BINDER_USE_RUST_LOADED; + else + ret = -EINVAL; + mutex_unlock(&binder_use_rust_lock); + + if (!ret) { + unload_binderfs(); + debugfs_remove_recursive(binder_debugfs_dir_entry_root); + binder_alloc_shrinker_exit(); + } + + return ret; +} +EXPORT_SYMBOL_GPL(unload_binder); + +int on_binderfs_mount(void) +{ + int ret = 0; + + mutex_lock(&binder_use_rust_lock); + if (binder_use_rust == BINDER_USE_RUST) { + /* + * C binder was mounted before loading the Rust Binder module. + * In this case, we fall back to using C Binder even though + * Rust Binder was requested. + */ + pr_warn("Using C Binder even though binder.impl=rust is set.\n"); + binder_use_rust = BINDER_USE_C; + } + + if (binder_use_rust == BINDER_USE_RUST_LOADED) { + /* + * Rust Binder is requested *and* has already started unloading + * C Binder. Fail the attempt to mount C Binder. + */ + ret = -EINVAL; + } + mutex_unlock(&binder_use_rust_lock); + return ret; +} + +static int binder_impl_param_set(const char *buffer, const struct kernel_param *kp) +{ + if (!strcmp(buffer, "rust")) + binder_use_rust = true; + else if (!strcmp(buffer, "c")) + binder_use_rust = false; + else + return -EINVAL; + + return 0; +} + +static int binder_impl_param_get(char *buffer, const struct kernel_param *kp) +{ + /* The buffer is 4k bytes, so this will not overflow. */ + return sprintf(buffer, "%s\n", binder_use_rust ? "rust" : "c"); +} + +static const struct kernel_param_ops binder_impl_param_ops = { + .set = binder_impl_param_set, + .get = binder_impl_param_get, +}; + +module_param_cb(impl, &binder_impl_param_ops, NULL, 0444); + #define CREATE_TRACE_POINTS #include "binder_trace.h" EXPORT_TRACEPOINT_SYMBOL_GPL(binder_transaction_received); diff --git a/drivers/android/binder_internal.h b/drivers/android/binder_internal.h index 65720b3b35a3..66c96ab9e7ae 100644 --- a/drivers/android/binder_internal.h +++ b/drivers/android/binder_internal.h @@ -17,6 +17,14 @@ #include "binder_alloc.h" #include "dbitmap.h" +extern int binder_use_rust; +#ifdef CONFIG_ANDROID_BINDERFS +void unload_binderfs(void); +int on_binderfs_mount(void); +#else +static inline void unload_binderfs(void) {} +#endif + struct binder_context { struct binder_node *binder_context_mgr_node; struct mutex context_mgr_node_lock; diff --git a/drivers/android/binderfs.c b/drivers/android/binderfs.c index ad1fa7abc323..6602fbf0dbd7 100644 --- a/drivers/android/binderfs.c +++ b/drivers/android/binderfs.c @@ -765,6 +765,9 @@ static int binderfs_init_fs_context(struct fs_context *fc) { struct binderfs_mount_opts *ctx; + if (on_binderfs_mount()) + return -EINVAL; + ctx = kzalloc(sizeof(struct binderfs_mount_opts), GFP_KERNEL); if (!ctx) return -ENOMEM; @@ -832,3 +835,9 @@ int __init init_binderfs(void) return ret; } + +void unload_binderfs(void) +{ + unregister_filesystem(&binder_fs_type); + unregister_chrdev_region(binderfs_dev, BINDERFS_MAX_MINOR); +}