Skip to content

Commit 57483a3

Browse files
ynaffitgnaygregkh
authored andcommitted
binder: Create safe versions of binder log files
Binder defines several seq_files that can be accessed via debugfs or binderfs. Some of these files (e.g., 'state' and 'transactions') contain more granular information about binder's internal state that is helpful for debugging, but they also leak userspace address data through user-defined 'cookie' or 'ptr' values. Consequently, access to these files must be heavily restricted. Add two new files, 'state_hashed' and 'transactions_hashed', that reproduce the information in the original files but use the kernel's raw pointer obfuscation to hash any potential user addresses. This approach allows systems to grant broader access to the new files without having to change the security policy around the existing ones. In practice, userspace populates these fields with user addresses, but within the driver, these values only serve as unique identifiers for their associated binder objects. Consequently, binder logs can obfuscate these values and still retain meaning. While this strategy prevents leaking information about the userspace memory layout in the existing log files, it also decouples log messages about binder objects from their user-defined identifiers. Acked-by: Carlos Llamas <cmllamas@google.com> Tested-by: Carlos Llamas <cmllamas@google.com> Signed-off-by: "Tiffany Y. Yang" <ynaffit@google.com> Link: https://lore.kernel.org/r/20250510013435.1520671-7-ynaffit@google.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 91f1bba commit 57483a3

1 file changed

Lines changed: 79 additions & 27 deletions

File tree

drivers/android/binder.c

Lines changed: 79 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -6377,7 +6377,7 @@ static void print_binder_work_ilocked(struct seq_file *m,
63776377
struct binder_proc *proc,
63786378
const char *prefix,
63796379
const char *transaction_prefix,
6380-
struct binder_work *w)
6380+
struct binder_work *w, bool hash_ptrs)
63816381
{
63826382
struct binder_node *node;
63836383
struct binder_transaction *t;
@@ -6400,9 +6400,15 @@ static void print_binder_work_ilocked(struct seq_file *m,
64006400
break;
64016401
case BINDER_WORK_NODE:
64026402
node = container_of(w, struct binder_node, work);
6403-
seq_printf(m, "%snode work %d: u%016llx c%016llx\n",
6404-
prefix, node->debug_id,
6405-
(u64)node->ptr, (u64)node->cookie);
6403+
if (hash_ptrs)
6404+
seq_printf(m, "%snode work %d: u%p c%p\n",
6405+
prefix, node->debug_id,
6406+
(void *)(long)node->ptr,
6407+
(void *)(long)node->cookie);
6408+
else
6409+
seq_printf(m, "%snode work %d: u%016llx c%016llx\n",
6410+
prefix, node->debug_id,
6411+
(u64)node->ptr, (u64)node->cookie);
64066412
break;
64076413
case BINDER_WORK_DEAD_BINDER:
64086414
seq_printf(m, "%shas dead binder\n", prefix);
@@ -6427,7 +6433,7 @@ static void print_binder_work_ilocked(struct seq_file *m,
64276433

64286434
static void print_binder_thread_ilocked(struct seq_file *m,
64296435
struct binder_thread *thread,
6430-
bool print_always)
6436+
bool print_always, bool hash_ptrs)
64316437
{
64326438
struct binder_transaction *t;
64336439
struct binder_work *w;
@@ -6457,23 +6463,30 @@ static void print_binder_thread_ilocked(struct seq_file *m,
64576463
}
64586464
list_for_each_entry(w, &thread->todo, entry) {
64596465
print_binder_work_ilocked(m, thread->proc, " ",
6460-
" pending transaction", w);
6466+
" pending transaction",
6467+
w, hash_ptrs);
64616468
}
64626469
if (!print_always && m->count == header_pos)
64636470
m->count = start_pos;
64646471
}
64656472

64666473
static void print_binder_node_nilocked(struct seq_file *m,
6467-
struct binder_node *node)
6474+
struct binder_node *node,
6475+
bool hash_ptrs)
64686476
{
64696477
struct binder_ref *ref;
64706478
struct binder_work *w;
64716479
int count;
64726480

64736481
count = hlist_count_nodes(&node->refs);
64746482

6475-
seq_printf(m, " node %d: u%016llx c%016llx hs %d hw %d ls %d lw %d is %d iw %d tr %d",
6476-
node->debug_id, (u64)node->ptr, (u64)node->cookie,
6483+
if (hash_ptrs)
6484+
seq_printf(m, " node %d: u%p c%p", node->debug_id,
6485+
(void *)(long)node->ptr, (void *)(long)node->cookie);
6486+
else
6487+
seq_printf(m, " node %d: u%016llx c%016llx", node->debug_id,
6488+
(u64)node->ptr, (u64)node->cookie);
6489+
seq_printf(m, " hs %d hw %d ls %d lw %d is %d iw %d tr %d",
64776490
node->has_strong_ref, node->has_weak_ref,
64786491
node->local_strong_refs, node->local_weak_refs,
64796492
node->internal_strong_refs, count, node->tmp_refs);
@@ -6486,7 +6499,8 @@ static void print_binder_node_nilocked(struct seq_file *m,
64866499
if (node->proc) {
64876500
list_for_each_entry(w, &node->async_todo, entry)
64886501
print_binder_work_ilocked(m, node->proc, " ",
6489-
" pending async transaction", w);
6502+
" pending async transaction",
6503+
w, hash_ptrs);
64906504
}
64916505
}
64926506

@@ -6508,6 +6522,7 @@ static void print_binder_ref_olocked(struct seq_file *m,
65086522
* @proc: struct binder_proc we hold the inner_proc_lock to (if any)
65096523
* @node: struct binder_node to print fields of
65106524
* @prev_node: struct binder_node we hold a temporary reference to (if any)
6525+
* @hash_ptrs: whether to hash @node's binder_uintptr_t fields
65116526
*
65126527
* Helper function to handle synchronization around printing a struct
65136528
* binder_node while iterating through @proc->nodes or the dead nodes list.
@@ -6520,7 +6535,7 @@ static void print_binder_ref_olocked(struct seq_file *m,
65206535
static struct binder_node *
65216536
print_next_binder_node_ilocked(struct seq_file *m, struct binder_proc *proc,
65226537
struct binder_node *node,
6523-
struct binder_node *prev_node)
6538+
struct binder_node *prev_node, bool hash_ptrs)
65246539
{
65256540
/*
65266541
* Take a temporary reference on the node so that isn't freed while
@@ -6538,7 +6553,7 @@ print_next_binder_node_ilocked(struct seq_file *m, struct binder_proc *proc,
65386553
if (prev_node)
65396554
binder_put_node(prev_node);
65406555
binder_node_inner_lock(node);
6541-
print_binder_node_nilocked(m, node);
6556+
print_binder_node_nilocked(m, node, hash_ptrs);
65426557
binder_node_inner_unlock(node);
65436558
if (proc)
65446559
binder_inner_proc_lock(proc);
@@ -6548,7 +6563,7 @@ print_next_binder_node_ilocked(struct seq_file *m, struct binder_proc *proc,
65486563
}
65496564

65506565
static void print_binder_proc(struct seq_file *m, struct binder_proc *proc,
6551-
bool print_all)
6566+
bool print_all, bool hash_ptrs)
65526567
{
65536568
struct binder_work *w;
65546569
struct rb_node *n;
@@ -6563,7 +6578,7 @@ static void print_binder_proc(struct seq_file *m, struct binder_proc *proc,
65636578
binder_inner_proc_lock(proc);
65646579
for (n = rb_first(&proc->threads); n; n = rb_next(n))
65656580
print_binder_thread_ilocked(m, rb_entry(n, struct binder_thread,
6566-
rb_node), print_all);
6581+
rb_node), print_all, hash_ptrs);
65676582

65686583
for (n = rb_first(&proc->nodes); n; n = rb_next(n)) {
65696584
struct binder_node *node = rb_entry(n, struct binder_node,
@@ -6572,7 +6587,8 @@ static void print_binder_proc(struct seq_file *m, struct binder_proc *proc,
65726587
continue;
65736588

65746589
last_node = print_next_binder_node_ilocked(m, proc, node,
6575-
last_node);
6590+
last_node,
6591+
hash_ptrs);
65766592
}
65776593
binder_inner_proc_unlock(proc);
65786594
if (last_node)
@@ -6590,7 +6606,8 @@ static void print_binder_proc(struct seq_file *m, struct binder_proc *proc,
65906606
binder_inner_proc_lock(proc);
65916607
list_for_each_entry(w, &proc->todo, entry)
65926608
print_binder_work_ilocked(m, proc, " ",
6593-
" pending transaction", w);
6609+
" pending transaction", w,
6610+
hash_ptrs);
65946611
list_for_each_entry(w, &proc->delivered_death, entry) {
65956612
seq_puts(m, " has delivered dead binder\n");
65966613
break;
@@ -6772,7 +6789,7 @@ static void print_binder_proc_stats(struct seq_file *m,
67726789
print_binder_stats(m, " ", &proc->stats);
67736790
}
67746791

6775-
static int state_show(struct seq_file *m, void *unused)
6792+
static void print_binder_state(struct seq_file *m, bool hash_ptrs)
67766793
{
67776794
struct binder_proc *proc;
67786795
struct binder_node *node;
@@ -6785,16 +6802,38 @@ static int state_show(struct seq_file *m, void *unused)
67856802
seq_puts(m, "dead nodes:\n");
67866803
hlist_for_each_entry(node, &binder_dead_nodes, dead_node)
67876804
last_node = print_next_binder_node_ilocked(m, NULL, node,
6788-
last_node);
6805+
last_node,
6806+
hash_ptrs);
67896807
spin_unlock(&binder_dead_nodes_lock);
67906808
if (last_node)
67916809
binder_put_node(last_node);
67926810

67936811
mutex_lock(&binder_procs_lock);
67946812
hlist_for_each_entry(proc, &binder_procs, proc_node)
6795-
print_binder_proc(m, proc, true);
6813+
print_binder_proc(m, proc, true, hash_ptrs);
67966814
mutex_unlock(&binder_procs_lock);
6815+
}
6816+
6817+
static void print_binder_transactions(struct seq_file *m, bool hash_ptrs)
6818+
{
6819+
struct binder_proc *proc;
6820+
6821+
seq_puts(m, "binder transactions:\n");
6822+
mutex_lock(&binder_procs_lock);
6823+
hlist_for_each_entry(proc, &binder_procs, proc_node)
6824+
print_binder_proc(m, proc, false, hash_ptrs);
6825+
mutex_unlock(&binder_procs_lock);
6826+
}
6827+
6828+
static int state_show(struct seq_file *m, void *unused)
6829+
{
6830+
print_binder_state(m, false);
6831+
return 0;
6832+
}
67976833

6834+
static int state_hashed_show(struct seq_file *m, void *unused)
6835+
{
6836+
print_binder_state(m, true);
67986837
return 0;
67996838
}
68006839

@@ -6816,14 +6855,13 @@ static int stats_show(struct seq_file *m, void *unused)
68166855

68176856
static int transactions_show(struct seq_file *m, void *unused)
68186857
{
6819-
struct binder_proc *proc;
6820-
6821-
seq_puts(m, "binder transactions:\n");
6822-
mutex_lock(&binder_procs_lock);
6823-
hlist_for_each_entry(proc, &binder_procs, proc_node)
6824-
print_binder_proc(m, proc, false);
6825-
mutex_unlock(&binder_procs_lock);
6858+
print_binder_transactions(m, false);
6859+
return 0;
6860+
}
68266861

6862+
static int transactions_hashed_show(struct seq_file *m, void *unused)
6863+
{
6864+
print_binder_transactions(m, true);
68276865
return 0;
68286866
}
68296867

@@ -6836,7 +6874,7 @@ static int proc_show(struct seq_file *m, void *unused)
68366874
hlist_for_each_entry(itr, &binder_procs, proc_node) {
68376875
if (itr->pid == pid) {
68386876
seq_puts(m, "binder proc state:\n");
6839-
print_binder_proc(m, itr, true);
6877+
print_binder_proc(m, itr, true, false);
68406878
}
68416879
}
68426880
mutex_unlock(&binder_procs_lock);
@@ -6903,8 +6941,10 @@ const struct file_operations binder_fops = {
69036941
};
69046942

69056943
DEFINE_SHOW_ATTRIBUTE(state);
6944+
DEFINE_SHOW_ATTRIBUTE(state_hashed);
69066945
DEFINE_SHOW_ATTRIBUTE(stats);
69076946
DEFINE_SHOW_ATTRIBUTE(transactions);
6947+
DEFINE_SHOW_ATTRIBUTE(transactions_hashed);
69086948
DEFINE_SHOW_ATTRIBUTE(transaction_log);
69096949

69106950
const struct binder_debugfs_entry binder_debugfs_entries[] = {
@@ -6914,6 +6954,12 @@ const struct binder_debugfs_entry binder_debugfs_entries[] = {
69146954
.fops = &state_fops,
69156955
.data = NULL,
69166956
},
6957+
{
6958+
.name = "state_hashed",
6959+
.mode = 0444,
6960+
.fops = &state_hashed_fops,
6961+
.data = NULL,
6962+
},
69176963
{
69186964
.name = "stats",
69196965
.mode = 0444,
@@ -6926,6 +6972,12 @@ const struct binder_debugfs_entry binder_debugfs_entries[] = {
69266972
.fops = &transactions_fops,
69276973
.data = NULL,
69286974
},
6975+
{
6976+
.name = "transactions_hashed",
6977+
.mode = 0444,
6978+
.fops = &transactions_hashed_fops,
6979+
.data = NULL,
6980+
},
69296981
{
69306982
.name = "transaction_log",
69316983
.mode = 0444,

0 commit comments

Comments
 (0)