Search code examples
cmacosmmapkernel-extensionxnu

Mach pager loading file to memory in mmap file


following my question here, I've extended my research about the actual read/write from file to memory and vice versa when calling file mmap. unlike READ and WRITE syscalls, the MMAP case is different, as it can be shown in the backtrace below:

(lldb) bt
* thread #3, name = '0xffffff801a6c24c0', queue = '0x0', stop reason = step in
* frame #0: 0xffffff80133b0788 kernel`ubc_map [inlined] VNOP_MMAP(fflags=<unavailable>, ctx=0xffffff8021a74af0) at kpi_vfs.c:3649 [opt]
frame #1: 0xffffff80133b0775 kernel`ubc_map(vp=<unavailable>, flags=<unavailable>) at ubc_subr.c:1793 [opt]
frame #2: 0xffffff8012f571dd kernel`vnode_pager_map(mem_obj=<unavailable>, prot=<unavailable>) at bsd_vm.c:737 [opt]
frame #3: 0xffffff8012f7a1cd kernel`vm_map_enter_mem_object_control [inlined] memory_object_map(memory_object=<unavailable>, prot=<unavailable>) at memory_object.c:2332 [opt]
frame #4: 0xffffff8012f7a1c3 kernel`vm_map_enter_mem_object_control(target_map=<unavailable>, address=<unavailable>, initial_size=<unavailable>, mask=<unavailable>, flags=<unavailable>, control=<unavailable>, offset=1, copy=<unavailable>, cur_protection=<unavailable>, max_protection=<unavailable>, inheritance=<unavailable>) at vm_map.c:4493 [opt]
frame #5: 0xffffff80133751a8 kernel`mmap(p=<unavailable>, uap=<unavailable>, retval=<unavailable>) at kern_mman.c:600 [opt]
frame #6: 0xffffff8013425695 kernel`unix_syscall64(state=<unavailable>) at systemcalls.c:376 [opt]
frame #7: 0xffffff8012e9dd46 kernel`hndl_unix_scall64 + 22

it seem like the mach pager is doing all the work here (of reading the file into memory). I know that load_machfile usually do the reading file part using vn_rdwr when loading new image in execve, but I couldn't find any synonym in vnode_pager_map function tree.

so my question is which method actually read the file contents in mmap sys call ?


Solution

  • Ok, So after reading the comments to my question, it turns out that the mapping is done lazy.. meaning, when there an attempt to access the memory. I've put some breakpoint in the relevant functions and verify that when I read the from the memory of the mapped file, it encounter page fault and retrieve the data as a result (see backtrace below) :

    * thread #12, name = '0xffffff801a378078', queue = '0x0', stop reason = breakpoint 6.1
    * frame #0: 0xffffff801310abe4 kernel`cluster_read(vp=0xffffff801b5300f8, uio=0xffffff801d6f5050, filesize=146309, xflags=0) at vfs_cluster.c:3615 [opt]
    frame #1: 0xffffff7f946c860d
    frame #2: 0xffffff801314b5d8 kernel`VNOP_READ(vp=0xffffff80724bb528, uio=0x0000000000000000, ioflag=<unavailable>, ctx=<unavailable>) at kpi_vfs.c:3410 [opt]
    frame #3: 0xffffff7f946c2fbb
    frame #4: 0xffffff801314e5f4 kernel`VNOP_GETXATTR(vp=0x0000000000000000, name=<unavailable>, uio=<unavailable>, size=<unavailable>, options=<unavailable>, ctx=<unavailable>) at kpi_vfs.c:5424 [opt]
    frame #5: 0xffffff7f94f81a6b
    frame #6: 0xffffff7f94f81d93
    frame #7: 0xffffff7f94f816e0
    frame #8: 0xffffff8013160844 kernel`decmpfs_fetch_uncompressed_data(vp=<unavailable>, cp=<unavailable>, hdr=<unavailable>, offset=<unavailable>, size=<unavailable>, nvec=1, vec=<unavailable>, bytes_read=<unavailable>) at decmpfs.c:1064 [opt]
    frame #9: 0xffffff80131601d3 kernel`decmpfs_pagein_compressed(ap=<unavailable>, is_compressed=0xffffff80724bba68, cp=<unavailable>) at decmpfs.c:1199 [opt]
    frame #10: 0xffffff7f946cf340
    frame #11: 0xffffff80133fc817 kernel`vnode_pagein [inlined] VNOP_PAGEIN(pl_offset=<unavailable>, size=0, flags=4096, ctx=<unavailable>) at kpi_vfs.c:5207 [opt]
    frame #12: 0xffffff80133fc7d0 kernel`vnode_pagein(vp=<unavailable>, upl=<unavailable>, upl_offset=188416, f_offset=<unavailable>, size=<unavailable>, flags=4096, errorp=<unavailable>) at vnode_pager.c:597 [opt]
    frame #13: 0xffffff8012f573c8 kernel`vnode_pager_cluster_read(vnode_object=0xffffff801deaf348, base_offset=188416, offset=<unavailable>, io_streaming=<unavailable>, cnt=0x0000000000001000) at bsd_vm.c:864 [opt]
    frame #14: 0xffffff8012f57153 kernel`vnode_pager_data_request(mem_obj=0xffffff801deaf348, offset=188416, length=<unavailable>, desired_access=<unavailable>, fault_info=<unavailable>) at bsd_vm.c:645 [opt]
    frame #15: 0xffffff8012f66043 kernel`vm_fault_page [inlined] memory_object_data_request(memory_object=0xffffff801deaf348, offset=<unavailable>, length=4096, desired_access=1, fault_info=0x0000000000000000) at memory_object.c:2228 [opt]
    frame #16: 0xffffff8012f6602d kernel`vm_fault_page(first_object=<unavailable>, first_offset=<unavailable>, fault_type=<unavailable>, must_be_resident=0, caller_lookup=0, protection=<unavailable>, result_page=<unavailable>, top_page=<unavailable>, type_of_fault=0x0000000100000001, error_code=<unavailable>, no_zero_fill=<unavailable>, data_supply=0, fault_info=0x0000000000000000) at vm_fault.c:1794 [opt]
    frame #17: 0xffffff8012f6afd6 kernel`vm_fault_internal(map=<unavailable>, vaddr=<unavailable>, caller_prot=1, change_wiring=0, interruptible=2, caller_pmap=<unavailable>, caller_pmap_addr=0, physpage_p=<unavailable>) at vm_fault.c:4716 [opt]
    frame #18: 0xffffff80130015ec kernel`user_trap [inlined] vm_fault(map=<unavailable>, vaddr=<unavailable>, fault_type=<unavailable>, change_wiring=0, interruptible=2, caller_pmap=<unavailable>, caller_pmap_addr=0) at vm_fault.c:3397 [opt]
    frame #19: 0xffffff80130015c8 kernel`user_trap(saved_state=0xffffff801a49c300) at trap.c:1123 [opt]
    frame #20: 0xffffff8012e9d455 kernel`hndl_alltraps + 229