How an actively exploited transaction descriptor refcount overflow in drivers/android/binder.c allowed malicious apps to escalate to full root privileges.
Imagine a coat-check attendant who gives you a claim ticket. If you ask for 65,536 tickets, his tiny counter rolls back to 0. He thinks you have zero coats checked, but you still hold the claim stubs. When another customer drops off a priceless jewel, he gives it the claim number 1—and you hand him your duplicate stub, walking out with the crown jewels.
An untrusted zero-permission Android app opens /dev/binder.
The app repeatedly creates transactional node references until the 32-bit counter wraps around.
The kernel marks the node as unreferenced and frees its slab memory.
The app reallocates the memory with a fake function pointer table, hijacking kernel control flow.
Представлено на понятном высокоуровневом исходном коде (без ассемблера и бинарных дампов).
// VULNERABLE: drivers/android/binder.c before patch
static void binder_inc_node_nil(struct binder_node *node) {
// ROOT CAUSE:
// Raw unsigned increment without checking for integer overflow saturation!
node->internal_strong_refs++;
if (node->internal_strong_refs == 0) {
// Wrapped around! Node will be prematurely destroyed!
binder_free_node(node);
}
}
// SECURE: drivers/android/binder.c patch
static void binder_inc_node_nil(struct binder_node *node) {
// 1. Enforce atomic saturating reference count arithmetic
if (unlikely(node->internal_strong_refs == UINT_MAX)) {
pr_err("binder: strong ref overflow detected, killing task\n");
binder_user_error("binder: node ref overflow\n");
return;
}
// 2. Safely increment verified counter
node->internal_strong_refs++;
}