I'm digging into the source code of binder system. I want to change the Parcel data I send to the binder server. To do this, the location of Parcel data received by binder server should be writable. But after I add PROT_WRITE flag to mmap, my Pixel 3 always fail to boot after a Google logo.
Android optimize the memory performance of binder system by just passing the memory in binder driver to the receiver(binder server). In the internal implementation, binder server start IPCThreadPool who open(/dev/binder) readable and writable(which I assume for ioctl()). Then mmap /dev/binder readonly.
In ./system/libhwbinder/ProcessState.cpp And ./frameworks/native/libs/binder/ProcessState.cpp
387 ProcessState::ProcessState(size_t mmap_size)
388 : mDriverFD(open_driver())
389 , mVMStart(MAP_FAILED)
390 , mThreadCountLock(PTHREAD_MUTEX_INITIALIZER)
391 , mThreadCountDecrement(PTHREAD_COND_INITIALIZER)
392 , mExecutingThreadsCount(0)
393 , mMaxThreads(DEFAULT_MAX_BINDER_THREADS)
394 , mStarvationStartTimeMs(0)
395 , mManagesContexts(false)
396 , mBinderContextCheckFunc(NULL)
397 , mBinderContextUserData(NULL)
398 , mThreadPoolStarted(false)
399 , mSpawnThreadOnStart(true)
400 , mThreadPoolSeq(1)
401 , mMmapSize(mmap_size)
402 {
403 if (mDriverFD >= 0) {
404 // mmap the binder, providing a chunk of virtual address space to receive transactions.
405 mVMStart = mmap(0, mMmapSize, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
406 if (mVMStart == MAP_FAILED) {
407 // *sigh*
408 ALOGE("Using /dev/hwbinder failed: unable to mmap transaction memory.\n");
409 close(mDriverFD);
410 mDriverFD = -1;
411 }
412 }
413 else {
414 ALOGE("Binder driver could not be opened. Terminating.");
415 }
416 }
I just change 405 line to
mVMStart = mmap(0, mMmapSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
Since ICPThreadPool has opened /dev/binder readably and writably, I guess I also can mmap /dev/binder readably and writably. Then I' ll be able to change Parcel data content.
But after flashing, my Pixel 3 just reboot after Google logo shows up. No log can be seen from adb tools.
I've figured out the reason why I can't change the memory area to writable.
Binder driver works in kernel space and communicate with service process by ioctl
. And service process running IPCThreadPool
get parcel data using mmap
. It concerns the design of binder driver. To achieve performance advantage, Binder only copy user data once during the whole communicating process. That's when client process pass its parcel data to Binder driver in copy_from_user
. Then Binder driver map its memory to server process and pass the pointer of data to server process. It reduce one copy process.
To mmap, driver should provide its own mmap function to complete custom map operation. In it, vma->vm_flags
decide the mmaped memory's permission. And Binder driver banned all writable-related flags. Particularly, VM_MAYWRITE
flag is removed to ensure mmaped memory will never be changed to writable.
Consequently, you need to change the binder_mmap
's implementation to permit permission change after mmap
.
project private/msm-google/
diff --git a/drivers/android/binder.c b/drivers/android/binder.c
index f9062d4..6d89f2b 100644
--- a/drivers/android/binder.c
+++ b/drivers/android/binder.c
@@ -4994,7 +4996,7 @@ static int binder_mmap(struct file *filp, struct vm_area_struct *vma)
failure_string = "bad vm_flags";
goto err_bad_arg;
}
- vma->vm_flags = (vma->vm_flags | VM_DONTCOPY) & ~VM_MAYWRITE;
+ vma->vm_flags = (vma->vm_flags | VM_DONTCOPY) | VM_MAYWRITE;
vma->vm_ops = &binder_vm_ops;
vma->vm_private_data = proc;
This will do the work on my Pixel 3 running aosp 9.0.