Skip to content

MemoryHeapBase

追踪 MemoryHeapBase 的 Ashmem/memfd/设备 fd 创建、mmap、seal、IMemoryHeap 远端映射和释放生命周期。

基于android-17.0.0_r1
AndroidBinder共享内存memfd

MemoryHeapBase ​

MemoryHeapBase 的关键边界是“fd owner”和“本进程映射”分离:构造或 mapfd 建立映射,Binder 导出的是可重建的 heap 描述,远端再按自己的地址空间映射。

本文承接 MemoryDealer内存分配器 和 Ashmem共享内存。MemoryDealer 负责切子块,MemoryHeapBase 才拥有共享 fd、本地映射、size、offset 和访问 flags。本文从三类构造函数进入 mapfd,再追踪 IMemoryHeap 如何把 fd 交给远端 BpMemoryHeap 完成第二次 mmap。

1. 对象状态 ​

源码文件:frameworks/native/libs/binder/include/binder/MemoryHeapBase.h

MemoryHeapBase 继承 BnMemoryHeap,拥有 mFD、mSize、mBase、mFlags、mDevice、mNeedUnmap 和 mOffset。它既是本地映射 owner,也是可通过 Binder 暴露 heap 元数据的服务对象。

2. 创建堆 ​

源码文件:frameworks/native/libs/binder/MemoryHeapBase.cpp

size 构造函数先按页向上对齐。默认创建 Ashmem;设置 FORCE_MEMFD 时直接 memfd_create、ftruncate,随后进入 mapfd。创建者本地映射始终允许写入,即使最终 flags 是 READ_ONLY,只读约束主要面向后续接收者。

3. Memfd封印 ​

源码文件:frameworks/native/libs/binder/MemoryHeapBase.cpp

cpp
const int seals = ((mFlags & READ_ONLY) ? F_SEAL_FUTURE_WRITE : 0) |
        F_SEAL_GROW | F_SEAL_SHRINK |
        ((mFlags & MEMFD_ALLOW_SEALING_FLAG) ? 0 : F_SEAL_SEAL);
fcntl(fd, F_ADD_SEALS, seals);

默认禁止伸缩并封死后续添加 seal;READ_ONLY 增加 F_SEAL_FUTURE_WRITE。MEMFD_ALLOW_SEALING 则不添加 F_SEAL_SEAL,允许客户端继续加 seal,但头文件明确警告这会带来 TOCTOU/DoS 风险。

4. 外部fd ​

fd 构造函数使用 F_DUPFD_CLOEXEC 复制调用者 fd,再映射指定 offset;对象不夺取原 fd 所有权。device 构造函数则自行 open 设备,NO_CACHING 会增加 O_SYNC。这两类构造禁止 FORCE_MEMFD/MEMFD_ALLOW_SEALING,因为 backing 已由外部决定。

5. mapfd流程 ​

源码文件:frameworks/native/libs/binder/MemoryHeapBase.cpp

size 为 0 时通过 fstat 推断文件长度,并检查 off_t 到 size_t 是否溢出。未设置 DONT_MAP_LOCALLY 时按 flags 选择 PROT_READ/PROT_WRITE,执行 MAP_SHARED mmap;成功后记录 fd、base、size、offset,并设置 mNeedUnmap。

DONT_MAP_LOCALLY 只保存 fd 和元数据,把 mBase 设为 null;它适合仅把 heap 提供给远端的进程。调用者不能在该进程中解引用 getBase()。

6. Binder导出 ​

源码文件:frameworks/native/libs/binder/IMemory.cpp

cpp
case HEAP_ID:
    reply->writeFileDescriptor(getHeapID());
    reply->writeUint64(getSize());
    reply->writeInt64(getOffset());
    reply->writeUint32(getFlags());
    return NO_ERROR;

BnMemoryHeap 通过一次 Binder 事务返回 fd、size、offset 和 flags。Binder 驱动为接收进程安装新的 fd,远端 fd 数字不必与本地 mFD 相同。

7. 远端映射 ​

源码文件:frameworks/native/libs/binder/IMemory.cpp

BpMemoryHeap::assertReallyMapped 调用 HEAP_ID,检查 64 位 size/offset 能否在本地类型表示,dup 返回的 Parcel fd,然后根据 READ_ONLY 选择 mmap protection。只有 mmap 成功后才以 release memory ordering 发布 mHeapId,让其他线程安全读取 base/size。

HeapCache 复用同一 Binder heap 的映射,避免每个 MemoryBase 子对象重复 mmap;代理析构时按 real/cache heap 路径解除映射或减少缓存引用。

8. 释放路径 ​

源码文件:frameworks/native/libs/binder/MemoryHeapBase.cpp

dispose 用 android_atomic_or(-1, &mFD) 取得并失效 fd,保证重复 dispose/析构只执行一次。若 mNeedUnmap 为 true,先 munmap,再清空 base/size 并 close fd。已经通过 Binder 传出的目标 fd 是独立引用,不会因服务端 close 立即失效。

9. 错误边界 ​

  • memfd create/ftruncate 失败:对象保持无效 fd/base。
  • mmap 失败:关闭当前 fd并返回负 errno。
  • seal 失败:构造函数解除映射、清零 size 并关闭 fd。
  • ILP32 无法表示远端 size/offset:代理拒绝映射。
  • DONT_MAP_LOCALLY 下误用 base:得到 null,而不是有效地址。
  • READ_ONLY 不等于创建者当前映射只读;要区分 creator 和 remote protection。

10. 单元测试 ​

源码文件:frameworks/native/libs/binder/tests/binderMemoryHeapBaseUnitTest.cpp

测试验证 FORCE_MEMFD 确实产生 /memfd: fd;默认 seal 包含 grow、shrink、seal;MEMFD_ALLOW_SEALING 去除 seal;READ_ONLY 增加 future-write,并断言 ftruncate 缩小失败。测试直接检查内核 seals,而非只检查 flags 缓存。

11. 层次对照 ​

MemoryHeapBase 管整块共享映射;MemoryBase 描述 heap 中的 offset/size;MemoryDealer 管多个 MemoryBase 的空闲区间;Binder 只传递 heap fd 和元数据。定位问题时先判断失败属于 heap 创建、远端 mmap、子块 allocator 还是 transaction fd translation。

12. 阅读检查 ​

从 size 构造函数追到远端 BpMemoryHeap::assertReallyMapped,标出两次 mmap、fd duplication、seal 和 close 的 owner。再分别解释 READ_ONLY、DONT_MAP_LOCALLY、外部 fd offset 三个 flags/参数怎样改变本地和远端行为。