为什么 Android 在将 PROT_WRITE 标志添加到 /dev/binder 的 mmap() 后无法启动

Why Android fail to boot after add PROT_WRITE flag to mmap() of /dev/binder

我正在研究活页夹系统的源代码。我想更改发送到活页夹服务器的包裹数据。为此,binder 服务器接收的 Parcel 数据的位置应该是可写的。但是在我将 PROT_WRITE 标志添加到 mmap 后,我的 Pixel 3 总是在出现 Google 徽标后无法启动。

Android 通过仅将binder驱动程序中的内存传递给接收器(binder服务器)来优化binder系统的内存性能。在内部实现中,binder server 启动 IPCThreadPool who open(/dev/binder) 可读可写(我假设是 ioctl())。然后 mmap /dev/binder 只读。

在./system/libhwbinder/ProcessState.cpp 和./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 }

我只是将 405 行更改为

mVMStart = mmap(0, mMmapSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);

既然ICPThreadPool已经开启了/dev/binder可读可写,我想我也可以mmap /dev/binder可读可写了。然后我就可以更改 Parcel 数据内容了。

但是刷机后,我的 Pixel 3 只是在 Google 徽标出现后重新启动。 adb工具看不到日志。

我想通了为什么不能把内存区改成可写的原因

Binder 驱动在内核 space 中工作,通过 ioctl 与服务进程通信。服务进程运行IPCThreadPool使用mmap获取包裹数据。它涉及Binder驱动程序的设计。为了获得性能优势,Binder 在整个通信过程中只复制一次用户数据。那是客户端进程将其包裹数据传递给 copy_from_user 中的 Binder 驱动程序的时候。然后Binder驱动程序将其内存映射到服务器进程,并将数据指针传递给服务器进程。它减少了一个副本过程。

对于mmap,驱动应该提供自己的mmap函数来完成自定义map操作。其中,vma->vm_flags 决定映射内存的权限。并且 Binder 驱动程序禁止了所有与可写相关的标志。特别是,删除了 VM_MAYWRITE 标志以确保映射内存永远不会更改为可写。

因此,您需要更改 binder_mmap 的实现以允许在 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;

这将在我的 Pixel 3 运行 aosp 9.0 上完成工作。