zoukankan      html  css  js  c++  java
  • MapViewOfFile引起的问题。。。

    最近在fix bug的时候,遇到一个由于MapViewOfFile引起的问题。在此把分析的思路记下来。

    先介绍一下背景。

    项目里面有一个component叫做Message。功能分为两方面:message writer和message reader。项目有多个UI进程。需要支持每个进程写消息,同时也需要在每个进程里面有个mini message viewer显示消息。另外,在一个叫做console manager的进程里有一个Message Viewer。

    现在这个message writer被实现为一个singleton COM EXE。如果哪个进程需要写消息,可以创建instance,然后通过Interface来写消息。message reader是一个普通的COM object。message viewer会create一个instance,然后定期来读消息,显示在UI上。

    从上面分析我们可以看出,message是跨进程share的。该项目是通过MMF来达到该目的的。message writer会create一块memory mapping file,然后在该块file上面记录信息。message reader会打开该块MMF,然后从其中读取信息。

    OK。回到正题。

    现在是这样的。用户在其他一个进程操作的时候,突然,console manager 崩溃了。于是,捕捉dump,看一下出问题的call stack.

    0:000> kbn
     # ChildEBP RetAddr  Args to Child             
    00 0012e23c 7c90df4a 7c864742 00000002 0012e3a8 ntdll!KiFastSystemCallRet
    01 0012e240 7c864742 00000002 0012e3a8 00000001 ntdll!ZwWaitForMultipleObjects+0xc
    02 0012e578 7c843892 0012e5a0 7c839b21 0012e5a8 kernel32!UnhandledExceptionFilter+0x8b9
    03 0012e580 7c839b21 0012e5a8 00000000 0012e5a8 kernel32!BaseProcessStart+0x39
    04 0012e5a8 7c9032a8 0012e694 0012ffe0 0012e6b0 kernel32!_except_handler3+0x61
    05 0012e5cc 7c90327a 0012e694 0012ffe0 0012e6b0 ntdll!ExecuteHandler2+0x26
    06 0012e67c 7c90e48a 00000000 0012e6b0 0012e694 ntdll!ExecuteHandler+0x24
    07 0012e67c 77135720 00000000 0012e6b0 0012e694 ntdll!KiUserExceptionDispatcher+0xe
    08 0012e9a0 4d954ca4 00a90060 0012ea48 0012e9c8 oleaut32!SystemTimeToVariantTime+0xb
    09 0012e9d0 4d954c64 00a90060 0012ea48 0012ea48 MsgMMFReader!ATL::AtlConvertSystemTimeToVariantTime+0x34
    0a 0012e9e4 4d954c33 00a90060 0012ea48 0012ea04 MsgMMFReader!ATL::COleDateTime::ConvertSystemTimeToVariantTime+0x14
    0b 0012e9f4 4d954c14 00a90060 0012ea48 0012eaac MsgMMFReader!ATL::COleDateTime::operator=+0x13
    0c 0012ea04 4d94a41e 00a90060 49595134 0012ebbc MsgMMFReader!ATL::COleDateTime::COleDateTime
    0d 0012eaac 4d949a34 04dbb6e8 00005112 05472ca3 MsgMMFReader!CMsgMMFReaderBase::PopulateSafeArrayWithLogRecordInfo+0x1be
    0e 0012eacc 4d94afc2 000050dd 00005112 49595160 MsgMMFReader!CMsgMMFReaderBase::ReadMessageFromMMF+0xb4
    0f 0012eaf8 4d94bcd5 000050dd 00005112 49595008 MsgMMFReader!MsgMMFReaderBase::LockAndReadMMFDetails+0x52
    10 0012eb90 77520c7a 04dbb6e8 00000001 0651eae4 MsgMMFReader!CSlbMsgMMFReaderBase::GetMessages+0x475
    11 0012ebc8 77587e9e 4d94b860 04dbb6e8 0012ecfc ole32!CallFrame::Invoke+0x54
    12 0012ec10 775881c2 05efd2d8 0012ed4c 0012ecd8 ole32!CCtxChnl::SyncInvoke2+0x68
    13 0012ecc4 775899d0 001736c0 001a1f48 0012edd0 ole32!CCtxChnl::SendReceive2+0x201

    UnhandledExceptionFilter很眼熟。于是看一下exception context

    0:000> dd 0012e5a0
    0012e5a0  0012e694 0012e6b0 0012e5cc 7c9032a8
    0012e5b0  0012e694 0012ffe0 0012e6b0 0012e668
    0012e5c0  0012eaa0 7c9032bc 0012ffe0 0012e67c
    0012e5d0  7c90327a 0012e694 0012ffe0 0012e6b0
    0012e5e0  0012e668 7c839ac0 00000001 0012e694
    0012e5f0  0012ffe0 7c92aa0f 0012e694 0012ffe0
    0012e600  0012e6b0 0012e668 7c839ac0 0012ebbc
    0012e610  0012e694 05d7dc0c 00000001 7c9100b8
    0:000> .cxr 0012e6b0
    eax=00a90060 ebx=05d7db80 ecx=00000000 edx=0012ea48 esi=05d7dc0c edi=0012ebbc
    eip=77135720 esp=0012e97c ebp=0012e9a0 iopl=0         nv up ei pl nz ac po nc
    cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010212
    oleaut32!SystemTimeToVariantTime+0xb:
    77135720 668b08          mov     cx,word ptr [eax]        ds:0023:00a90060=01b2
    0:000> .exr 0012e694
    ExceptionAddress: 77135720 (oleaut32!SystemTimeToVariantTime+0x0000000b)
       ExceptionCode: c0000005 (Access violation)
      ExceptionFlags: 00000000
    NumberParameters: 2
       Parameter[0]: 00000000
       Parameter[1]: 00a90060
    Attempt to read from address 00a90060
    从上面来看,system在试图访问 00a90060的时候出现了AV。ok,看一下00a90060的属性。

    0:000> !address 00a90060
        00a90000 : 00a90000 - 00001000
                        Type     00040000 MEM_MAPPED
                        Protect  00000002 PAGE_READONLY
                        State    00001000 MEM_COMMIT
                        Usage    RegionUsageIsVAD

    应该没有问题啊。从上面我们可以看出,该处内存属于memory mapping file,并且是read only属性。

    奇怪。

    思考一段时间之后,我们决定看一下该内存块存储的是不是我们想要的东西。

    0:000> dt SYSTEMTIME 00a90060
    MsgMMFReader!SYSTEMTIME
       +0x000 wYear            : 0x1b2
       +0x002 wMonth           : 0
       +0x004 wDayOfWeek       : 0
       +0x006 wDay             : 0
       +0x008 wHour            : 0x2fdc
       +0x00a wMinute          : 0xb44f
       +0x00c wSecond          : 0x350
       +0x00e wMilliseconds    : 0

    这个地方肯定有问题。wYear 0x1b2 = 434, wMonth = 0。这个时间肯定是不对的。

    于是,我们分析是如何获得该内存地址的。经过分析源代码后,我们发现该处地址是通过offset取得的。难道该offset算的不对?有没有可能越界访问?于是,我们取到了基地址,并看一下该基地址的相关信息。

    0:000> !address 0x00a8fff8
        00a80000 : 00a80000 - 00010000
                        Type     00040000 MEM_MAPPED
                        Protect  00000002 PAGE_READONLY
                        State    00001000 MEM_COMMIT
                        Usage    RegionUsageIsVAD

    该基地址是0x00a8fff8。同样也是memory mapping file。

    但是,我们注意到一个严重的问题:该基地址所属的内存块从0x00a80000开始,size是0x10000。所以,它的最后一块地址是0x00a8ffff。而我们现在试图访问0x00a90060。

    典型的访问越界。只不过00a80000后面紧跟着另外一块commit过的内存块,所以才到了后期才出的av。如果是一块reserved的内存快,估计一开始就av了。

    接下来,我们就要分析,访问为什么会越界?看了看控制访问的地方,逻辑都很正确。于是,我们决定看看memory mapping file是如何map的。下面是代码。

    1m_baseLogMMF = MapViewOfFile(m_hLogMMFMapping,
    2 FILE_MAP_READ,
    3 0,
    4 m_offsetToStartMapView,
    5 m_numberOfBytesToMap);
    6 if(m_baseLogMMF == NULL
    7 && ERROR_ACCESS_DENIED == ::GetLastError())
    8 {
    9 //it's because the file size is smaller than what we want. so we only map what the file has.
    10 m_baseLogMMF = ::MapViewOfFile(
    11 m_hLogMMFMapping, // Handle to File Mapping object from CreateFileMapping()
    12 FILE_MAP_READ, // FILE_MAP_ALL_ACCESS
    13 0, // High order offset (32 bytes) in to start View.
    14 m_offsetToStartMapView, // Low order offset (32 bytes) in to start View.
    15 0);// read to file end
    16 }
    17
    大概猜到了root cause。第一次map的时候失败了,然后第二次map的时候直接map到文件末尾。但是,
    m_numberOfBytesToMap并没有更新。所以后面会越界访问。

    问题似乎清楚了。但这并不是真正的root cause。
  • 相关阅读:
    windows cmd中查看某个命令所在的路径
    linux vi编辑器中,如何通过快捷键上下翻页?
    linux系统中,查看当前系统中,都在监听哪些端口
    下载mysql server安装包的时候,不登录oracle账号,实现下载
    plsql developer中,清除登录历史
    linux环境下,清空history中记录的历史命令
    Linux下搭建hadoop开发环境-超详细
    HDFS架构详解-非官档
    SSH免密码登录配置方法详解
    bin/hdfs dfs命令存在WARN util.NativeCodeLoader问题消除方法
  • 原文地址:https://www.cnblogs.com/xiaxi/p/2027067.html
Copyright © 2011-2022 走看看