428 lines
14 KiB
Diff
428 lines
14 KiB
Diff
From fcd9d70190dd7e6536878a6379122f06e3b90919 Mon Sep 17 00:00:00 2001
|
|
From: Oren Laadan <orenl@cellrox.com>
|
|
Date: Sun, 22 Dec 2013 10:07:40 +0000
|
|
Subject: [PATCH 2/2] binder: implement namepsace support for Android binder
|
|
driver
|
|
|
|
Add namespaces support for the Android binder driver.
|
|
As binder is an IPC mechanism, tie its namespace to IPC_NS.
|
|
|
|
In binder, the first process to call BINDER_SET_CONTEXT_MGR ioctl
|
|
becomes the manager with context 0, and thereafter IPC is realized
|
|
through binder handles obtained from this manager.
|
|
|
|
For namespaces, associate a separate binder state with each namespace
|
|
so each namespace has its own context manager. Binder users within a
|
|
namespace interact only with the context manager there. This suffices
|
|
because binder does not allow IPC not via the context manager.
|
|
|
|
Currently, statistics remain global, except for the list of processes
|
|
that hold an open binder device (reported per namespace).
|
|
|
|
Signed-off-by: Oren Laadan <orenl@cellrox.com>
|
|
Acked-by: Amir Goldstein <cardoe@cardoe.com>
|
|
---
|
|
drivers/android/Kconfig | 1 +
|
|
drivers/android/binder.c | 172 ++++++++++++++++++++++++++++++++++++++---------
|
|
2 files changed, 143 insertions(+), 30 deletions(-)
|
|
|
|
diff --git a/drivers/android/Kconfig b/drivers/android/Kconfig
|
|
index bdfc6c6..739063d 100644
|
|
--- a/drivers/android/Kconfig
|
|
+++ b/drivers/android/Kconfig
|
|
@@ -10,6 +10,7 @@ if ANDROID
|
|
config ANDROID_BINDER_IPC
|
|
bool "Android Binder IPC Driver"
|
|
depends on MMU
|
|
+ select SYSVIPC
|
|
default n
|
|
---help---
|
|
Binder is used in Android for both communication between processes,
|
|
diff --git a/drivers/android/binder.c b/drivers/android/binder.c
|
|
index fee479d..2a63e9b 100644
|
|
--- a/drivers/android/binder.c
|
|
+++ b/drivers/android/binder.c
|
|
@@ -37,6 +37,7 @@
|
|
#include <linux/uaccess.h>
|
|
#include <linux/vmalloc.h>
|
|
#include <linux/slab.h>
|
|
+#include <linux/ipc_namespace.h>
|
|
#include <linux/pid_namespace.h>
|
|
#include <linux/security.h>
|
|
|
|
@@ -47,19 +48,98 @@
|
|
#include <uapi/linux/android/binder.h>
|
|
#include "binder_trace.h"
|
|
|
|
+/*
|
|
+ * Using a private context manager for each binder namespace is sufficient
|
|
+ * to isolate between namespaces, because in binder all IPC must be realized
|
|
+ * via hanldes obtained from the context manager.
|
|
+ *
|
|
+ * TODO: currently, most debugfs data is not tracked per binder namespaces.
|
|
+ * Except for "procs" which are properly virtualized, everything else is
|
|
+ * global, including stats, logs, and dead nodes.
|
|
+ */
|
|
+struct binder_namespace {
|
|
+ struct kref kref;
|
|
+
|
|
+ struct binder_node *context_mgr_node;
|
|
+ kuid_t context_mgr_uid;
|
|
+ int last_id;
|
|
+
|
|
+ struct hlist_head procs;
|
|
+};
|
|
+
|
|
+static struct binder_namespace *create_binder_ns(void)
|
|
+{
|
|
+ struct binder_namespace *binder_ns;
|
|
+
|
|
+ binder_ns = kzalloc(sizeof(struct binder_namespace), GFP_KERNEL);
|
|
+ if (binder_ns) {
|
|
+ kref_init(&binder_ns->kref);
|
|
+ binder_ns->context_mgr_uid = INVALID_UID;
|
|
+ INIT_HLIST_HEAD(&binder_ns->procs);
|
|
+ }
|
|
+ return binder_ns;
|
|
+}
|
|
+
|
|
+static void free_binder_ns(struct kref *kref)
|
|
+{
|
|
+ kfree(container_of(kref, struct binder_namespace, kref));
|
|
+}
|
|
+
|
|
+static void get_binder_ns(struct binder_namespace *binder_ns)
|
|
+{
|
|
+ kref_get(&binder_ns->kref);
|
|
+}
|
|
+
|
|
+static void put_binder_ns(struct binder_namespace *binder_ns)
|
|
+{
|
|
+ kref_put(&binder_ns->kref, free_binder_ns);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Binder is an IPC mechanism, so tie its namespace to IPC_NS
|
|
+ * using the generic data pointer and per-ipc operations.
|
|
+ */
|
|
+static struct binder_namespace *current_binder_ns(void)
|
|
+{
|
|
+ return ipc_access_generic(current->nsproxy->ipc_ns);
|
|
+}
|
|
+
|
|
+int binder_init_ns(struct ipc_namespace *ipcns)
|
|
+{
|
|
+ struct binder_namespace *binder_ns;
|
|
+ int ret = -ENOMEM;
|
|
+
|
|
+ binder_ns = create_binder_ns();
|
|
+ if (binder_ns) {
|
|
+ ipc_assign_generic(ipcns, binder_ns);
|
|
+ ret = 0;
|
|
+ }
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+void binder_exit_ns(struct ipc_namespace *ipcns)
|
|
+{
|
|
+ struct binder_namespace *binder_ns;
|
|
+
|
|
+ binder_ns = ipc_access_generic(ipcns);
|
|
+ if (binder_ns)
|
|
+ put_binder_ns(binder_ns);
|
|
+}
|
|
+
|
|
+struct peripc_operations binder_peripc_ops = {
|
|
+ .init = binder_init_ns,
|
|
+ .exit = binder_exit_ns,
|
|
+};
|
|
+
|
|
static DEFINE_RT_MUTEX(binder_main_lock);
|
|
static DEFINE_MUTEX(binder_deferred_lock);
|
|
static DEFINE_MUTEX(binder_mmap_lock);
|
|
|
|
-static HLIST_HEAD(binder_procs);
|
|
static HLIST_HEAD(binder_deferred_list);
|
|
static HLIST_HEAD(binder_dead_nodes);
|
|
|
|
static struct dentry *binder_debugfs_dir_entry_root;
|
|
static struct dentry *binder_debugfs_dir_entry_proc;
|
|
-static struct binder_node *binder_context_mgr_node;
|
|
-static kuid_t binder_context_mgr_uid = INVALID_UID;
|
|
-static int binder_last_id;
|
|
static struct workqueue_struct *binder_deferred_workqueue;
|
|
|
|
#define BINDER_DEBUG_ENTRY(name) \
|
|
@@ -327,6 +407,8 @@ struct binder_proc {
|
|
int ready_threads;
|
|
long default_priority;
|
|
struct dentry *debugfs_entry;
|
|
+
|
|
+ struct binder_namespace *binder_ns;
|
|
};
|
|
|
|
enum {
|
|
@@ -910,7 +992,7 @@ static struct binder_node *binder_new_node(struct binder_proc *proc,
|
|
binder_stats_created(BINDER_STAT_NODE);
|
|
rb_link_node(&node->rb_node, parent, p);
|
|
rb_insert_color(&node->rb_node, &proc->nodes);
|
|
- node->debug_id = ++binder_last_id;
|
|
+ node->debug_id = ++proc->binder_ns->last_id;
|
|
node->proc = proc;
|
|
node->ptr = ptr;
|
|
node->cookie = cookie;
|
|
@@ -931,7 +1013,7 @@ static int binder_inc_node(struct binder_node *node, int strong, int internal,
|
|
if (internal) {
|
|
if (target_list == NULL &&
|
|
node->internal_strong_refs == 0 &&
|
|
- !(node == binder_context_mgr_node &&
|
|
+ !(node == node->proc->binder_ns->context_mgr_node &&
|
|
node->has_strong_ref)) {
|
|
pr_err("invalid inc strong node for %d\n",
|
|
node->debug_id);
|
|
@@ -1045,13 +1127,13 @@ static struct binder_ref *binder_get_ref_for_node(struct binder_proc *proc,
|
|
if (new_ref == NULL)
|
|
return NULL;
|
|
binder_stats_created(BINDER_STAT_REF);
|
|
- new_ref->debug_id = ++binder_last_id;
|
|
+ new_ref->debug_id = ++proc->binder_ns->last_id;
|
|
new_ref->proc = proc;
|
|
new_ref->node = node;
|
|
rb_link_node(&new_ref->rb_node_node, parent, p);
|
|
rb_insert_color(&new_ref->rb_node_node, &proc->refs_by_node);
|
|
|
|
- new_ref->desc = (node == binder_context_mgr_node) ? 0 : 1;
|
|
+ new_ref->desc = (node == proc->binder_ns->context_mgr_node) ? 0 : 1;
|
|
for (n = rb_first(&proc->refs_by_desc); n != NULL; n = rb_next(n)) {
|
|
ref = rb_entry(n, struct binder_ref, rb_node_desc);
|
|
if (ref->desc > new_ref->desc)
|
|
@@ -1391,7 +1473,7 @@ static void binder_transaction(struct binder_proc *proc,
|
|
}
|
|
target_node = ref->node;
|
|
} else {
|
|
- target_node = binder_context_mgr_node;
|
|
+ target_node = proc->binder_ns->context_mgr_node;
|
|
if (target_node == NULL) {
|
|
return_error = BR_DEAD_REPLY;
|
|
goto err_no_context_mgr_node;
|
|
@@ -1452,7 +1534,7 @@ static void binder_transaction(struct binder_proc *proc,
|
|
}
|
|
binder_stats_created(BINDER_STAT_TRANSACTION_COMPLETE);
|
|
|
|
- t->debug_id = ++binder_last_id;
|
|
+ t->debug_id = ++proc->binder_ns->last_id;
|
|
e->debug_id = t->debug_id;
|
|
|
|
if (reply)
|
|
@@ -1787,10 +1869,10 @@ static int binder_thread_write(struct binder_proc *proc,
|
|
if (get_user(target, (uint32_t __user *)ptr))
|
|
return -EFAULT;
|
|
ptr += sizeof(uint32_t);
|
|
- if (target == 0 && binder_context_mgr_node &&
|
|
+ if (target == 0 && proc->binder_ns->context_mgr_node &&
|
|
(cmd == BC_INCREFS || cmd == BC_ACQUIRE)) {
|
|
ref = binder_get_ref_for_node(proc,
|
|
- binder_context_mgr_node);
|
|
+ proc->binder_ns->context_mgr_node);
|
|
if (ref->desc != target) {
|
|
binder_user_error("%d:%d tried to acquire reference to desc 0, got %d instead\n",
|
|
proc->pid, thread->pid,
|
|
@@ -2696,9 +2778,10 @@ static int binder_ioctl_set_ctx_mgr(struct file *filp)
|
|
{
|
|
int ret = 0;
|
|
struct binder_proc *proc = filp->private_data;
|
|
+ struct binder_namespace *binder_ns = proc->binder_ns;
|
|
kuid_t curr_euid = current_euid();
|
|
|
|
- if (binder_context_mgr_node != NULL) {
|
|
+ if (binder_ns->context_mgr_node != NULL) {
|
|
pr_err("BINDER_SET_CONTEXT_MGR already set\n");
|
|
ret = -EBUSY;
|
|
goto out;
|
|
@@ -2706,27 +2789,27 @@ static int binder_ioctl_set_ctx_mgr(struct file *filp)
|
|
ret = security_binder_set_context_mgr(proc->tsk);
|
|
if (ret < 0)
|
|
goto out;
|
|
- if (uid_valid(binder_context_mgr_uid)) {
|
|
- if (!uid_eq(binder_context_mgr_uid, curr_euid)) {
|
|
+ if (uid_valid(binder_ns->context_mgr_uid)) {
|
|
+ if (!uid_eq(binder_ns->context_mgr_uid, curr_euid)) {
|
|
pr_err("BINDER_SET_CONTEXT_MGR bad uid %d != %d\n",
|
|
from_kuid(&init_user_ns, curr_euid),
|
|
from_kuid(&init_user_ns,
|
|
- binder_context_mgr_uid));
|
|
+ binder_ns->context_mgr_uid));
|
|
ret = -EPERM;
|
|
goto out;
|
|
}
|
|
} else {
|
|
- binder_context_mgr_uid = curr_euid;
|
|
+ binder_ns->context_mgr_uid = curr_euid;
|
|
}
|
|
- binder_context_mgr_node = binder_new_node(proc, 0, 0);
|
|
- if (binder_context_mgr_node == NULL) {
|
|
+ binder_ns->context_mgr_node = binder_new_node(proc, 0, 0);
|
|
+ if (binder_ns->context_mgr_node == NULL) {
|
|
ret = -ENOMEM;
|
|
goto out;
|
|
}
|
|
- binder_context_mgr_node->local_weak_refs++;
|
|
- binder_context_mgr_node->local_strong_refs++;
|
|
- binder_context_mgr_node->has_strong_ref = 1;
|
|
- binder_context_mgr_node->has_weak_ref = 1;
|
|
+ binder_ns->context_mgr_node->local_weak_refs++;
|
|
+ binder_ns->context_mgr_node->local_strong_refs++;
|
|
+ binder_ns->context_mgr_node->has_strong_ref = 1;
|
|
+ binder_ns->context_mgr_node->has_weak_ref = 1;
|
|
out:
|
|
return ret;
|
|
}
|
|
@@ -2947,10 +3030,15 @@ err_bad_arg:
|
|
static int binder_open(struct inode *nodp, struct file *filp)
|
|
{
|
|
struct binder_proc *proc;
|
|
+ struct binder_namespace *binder_ns;
|
|
|
|
binder_debug(BINDER_DEBUG_OPEN_CLOSE, "binder_open: %d:%d\n",
|
|
current->group_leader->pid, current->pid);
|
|
|
|
+ binder_ns = current_binder_ns();
|
|
+ if (binder_ns == NULL)
|
|
+ return -ENOMEM;
|
|
+
|
|
proc = kzalloc(sizeof(*proc), GFP_KERNEL);
|
|
if (proc == NULL)
|
|
return -ENOMEM;
|
|
@@ -2960,10 +3048,13 @@ static int binder_open(struct inode *nodp, struct file *filp)
|
|
init_waitqueue_head(&proc->wait);
|
|
proc->default_priority = task_nice(current);
|
|
|
|
+ proc->binder_ns = binder_ns;
|
|
+ get_binder_ns(binder_ns);
|
|
+
|
|
binder_lock(__func__);
|
|
|
|
binder_stats_created(BINDER_STAT_PROC);
|
|
- hlist_add_head(&proc->proc_node, &binder_procs);
|
|
+ hlist_add_head(&proc->proc_node, &binder_ns->procs);
|
|
proc->pid = current->group_leader->pid;
|
|
INIT_LIST_HEAD(&proc->delivered_death);
|
|
filp->private_data = proc;
|
|
@@ -3067,6 +3158,7 @@ static int binder_node_release(struct binder_node *node, int refs)
|
|
|
|
static void binder_deferred_release(struct binder_proc *proc)
|
|
{
|
|
+ struct binder_namespace *binder_ns = proc->binder_ns;
|
|
struct binder_transaction *t;
|
|
struct rb_node *n;
|
|
int threads, nodes, incoming_refs, outgoing_refs, buffers,
|
|
@@ -3077,11 +3169,12 @@ static void binder_deferred_release(struct binder_proc *proc)
|
|
|
|
hlist_del(&proc->proc_node);
|
|
|
|
- if (binder_context_mgr_node && binder_context_mgr_node->proc == proc) {
|
|
+ if (binder_ns->context_mgr_node &&
|
|
+ binder_ns->context_mgr_node->proc == proc) {
|
|
binder_debug(BINDER_DEBUG_DEAD_BINDER,
|
|
"%s: %d context_mgr_node gone\n",
|
|
__func__, proc->pid);
|
|
- binder_context_mgr_node = NULL;
|
|
+ binder_ns->context_mgr_node = NULL;
|
|
}
|
|
|
|
threads = 0;
|
|
@@ -3160,6 +3253,7 @@ static void binder_deferred_release(struct binder_proc *proc)
|
|
vfree(proc->buffer);
|
|
}
|
|
|
|
+ put_binder_ns(proc->binder_ns);
|
|
put_task_struct(proc->tsk);
|
|
|
|
binder_debug(BINDER_DEBUG_OPEN_CLOSE,
|
|
@@ -3540,10 +3634,14 @@ static void print_binder_proc_stats(struct seq_file *m,
|
|
|
|
static int binder_state_show(struct seq_file *m, void *unused)
|
|
{
|
|
+ struct binder_namespace *binder_ns = current_binder_ns();
|
|
struct binder_proc *proc;
|
|
struct binder_node *node;
|
|
int do_lock = !binder_debug_no_lock;
|
|
|
|
+ if (binder_ns == NULL)
|
|
+ return 0;
|
|
+
|
|
if (do_lock)
|
|
binder_lock(__func__);
|
|
|
|
@@ -3554,7 +3652,7 @@ static int binder_state_show(struct seq_file *m, void *unused)
|
|
hlist_for_each_entry(node, &binder_dead_nodes, dead_node)
|
|
print_binder_node(m, node);
|
|
|
|
- hlist_for_each_entry(proc, &binder_procs, proc_node)
|
|
+ hlist_for_each_entry(proc, &binder_ns->procs, proc_node)
|
|
print_binder_proc(m, proc, 1);
|
|
if (do_lock)
|
|
binder_unlock(__func__);
|
|
@@ -3563,9 +3661,13 @@ static int binder_state_show(struct seq_file *m, void *unused)
|
|
|
|
static int binder_stats_show(struct seq_file *m, void *unused)
|
|
{
|
|
+ struct binder_namespace *binder_ns = current_binder_ns();
|
|
struct binder_proc *proc;
|
|
int do_lock = !binder_debug_no_lock;
|
|
|
|
+ if (binder_ns == NULL)
|
|
+ return 0;
|
|
+
|
|
if (do_lock)
|
|
binder_lock(__func__);
|
|
|
|
@@ -3573,7 +3675,7 @@ static int binder_stats_show(struct seq_file *m, void *unused)
|
|
|
|
print_binder_stats(m, "", &binder_stats);
|
|
|
|
- hlist_for_each_entry(proc, &binder_procs, proc_node)
|
|
+ hlist_for_each_entry(proc, &binder_ns->procs, proc_node)
|
|
print_binder_proc_stats(m, proc);
|
|
if (do_lock)
|
|
binder_unlock(__func__);
|
|
@@ -3582,14 +3684,18 @@ static int binder_stats_show(struct seq_file *m, void *unused)
|
|
|
|
static int binder_transactions_show(struct seq_file *m, void *unused)
|
|
{
|
|
+ struct binder_namespace *binder_ns = current_binder_ns();
|
|
struct binder_proc *proc;
|
|
int do_lock = !binder_debug_no_lock;
|
|
|
|
+ if (binder_ns == NULL)
|
|
+ return 0;
|
|
+
|
|
if (do_lock)
|
|
binder_lock(__func__);
|
|
|
|
seq_puts(m, "binder transactions:\n");
|
|
- hlist_for_each_entry(proc, &binder_procs, proc_node)
|
|
+ hlist_for_each_entry(proc, &binder_ns->procs, proc_node)
|
|
print_binder_proc(m, proc, 0);
|
|
if (do_lock)
|
|
binder_unlock(__func__);
|
|
@@ -3661,9 +3767,15 @@ static int __init binder_init(void)
|
|
{
|
|
int ret;
|
|
|
|
+ ret = register_peripc_ops(&binder_peripc_ops);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+
|
|
binder_deferred_workqueue = create_singlethread_workqueue("binder");
|
|
- if (!binder_deferred_workqueue)
|
|
+ if (!binder_deferred_workqueue) {
|
|
+ unregister_peripc_ops(&binder_peripc_ops);
|
|
return -ENOMEM;
|
|
+ }
|
|
|
|
binder_debugfs_dir_entry_root = debugfs_create_dir("binder", NULL);
|
|
if (binder_debugfs_dir_entry_root)
|
|
--
|
|
2.7.4
|
|
|