为什么 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 上完成工作。
我正在研究活页夹系统的源代码。我想更改发送到活页夹服务器的包裹数据。为此,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 上完成工作。