记一次windows10内存压缩模块崩溃分析(代码片段)

author author     2023-04-26     697

关键词:

一:背景

1. 讲故事

在给各位朋友免费分析 .NET程序 各种故障的同时,往往也会收到各种其他类型的dump,比如:Windows 崩溃,C++ 崩溃,Mono 崩溃,真的是啥都有,由于基础知识的相对缺乏,分析起来并不是那么的顺利,今天就聊一个 Windows 崩溃的内核dump 吧,这个 dump 是前几天有位朋友给到我的,让我帮忙看一下,有了dump之后上 windbg 分析。

二:WinDbg 分析

1. 从哪里入手

只要是 Windows 平台上的崩溃,操作系统都会维护一个 EXCEPTION_POINTERS 结构体,这个结构体的解读对分析问题非常重要,使用 !analyze -v 命令简要输出如下:


2: kd> !analyze -v
*******************************************************************************
*                                                                             *
*                        Bugcheck Analysis                                    *
*                                                                             *
*******************************************************************************

UNEXPECTED_STORE_EXCEPTION (154)
The store component caught an unexpected exception.
Arguments:
Arg1: ffffb402b9851000, Pointer to the store context or data manager
Arg2: ffffe607bc53df30, Exception information
Arg3: 0000000000000002, Reserved
Arg4: 0000000000000000, Reserved
...
EXCEPTION_RECORD:  ffffe607bc53eeb8 -- (.exr 0xffffe607bc53eeb8)
ExceptionAddress: fffff80025b04bd0 (nt!RtlDecompressBufferXpressLz+0x0000000000000050)
   ExceptionCode: c0000006 (In-page I/O error)
  ExceptionFlags: 00000000
NumberParameters: 3
   Parameter[0]: 0000000000000000
   Parameter[1]: 0000023f30ee99f0
   Parameter[2]: 00000000c0000185
Inpage operation failed at 0000023f30ee99f0, due to I/O error 00000000c0000185

EXCEPTION_PARAMETER1:  0000000000000000

EXCEPTION_PARAMETER2:  0000023f30ee99f0

CONTEXT:  ffffe607bc53e6f0 -- (.cxr 0xffffe607bc53e6f0)
rax=fffff80025b04b80 rbx=ffff9d808d7fa000 rcx=ffff9d808d7fa000
rdx=ffff9d808d7fa000 rsi=0000000000000002 rdi=0000023f30ee99f0
rip=fffff80025b04bd0 rsp=ffffe607bc53f0f8 rbp=0000023f30eea2fe
 r8=0000023f30ee99f0  r9=0000000000000964 r10=ffff9d808d7faea0
r11=0000023f30eea354 r12=ffffe607bc53f368 r13=ffffb402d84d8000
r14=ffff9d808d7fb000 r15=0000000000000000
iopl=0         nv up ei pl zr na po nc
cs=0010  ss=0000  ds=002b  es=002b  fs=0053  gs=002b             efl=00050246
nt!RtlDecompressBufferXpressLz+0x50:
fffff800`25b04bd0 418b08          mov     ecx,dword ptr [r8] ds:002b:0000023f`30ee99f0=????????
Resetting default scope
...

从卦中信息看,是由于将地址 0000023f30ee99f0 所映射的物理内存页换入到内存中,抛了一个IO错误,从汇编指令 ecx,dword ptr [r8] ds:002b:0000023f30ee99f0=???????? 上也能看的出来。

如果大家不信,可以用 !vtop!pte 观察下它们对应的物理地址和物理页号,都是找不到的。


2: kd> !vtop 0 000000006d34aca0
Amd64VtoP: Virt 000000006d34aca0, pagedir 00000003d81fb002
Amd64VtoP: PML4E 00000003d81fb002
Amd64VtoP: PML4E read error 0x8000FFFF
Virtual address 6d34aca0 translation fails, error 0x8000FFFF.

2: kd> !pte 000000006d34aca0
                                           VA 000000006d34aca0
PXE at FFFF86432190C000    PPE at FFFF864321800008    PDE at FFFF864300001B48    PTE at FFFF860000369A50
contains 0000000000000000
contains 0000000000000000
not valid

2. 洞察异常前的线程栈

有了这个初步信息之后,接下来就来观察异常时的寄存器上下文和线程栈信息,输出如下:


2: kd> .cxr 0xffffe607bc53e6f0 ; k
rax=fffff80025b04b80 rbx=ffff9d808d7fa000 rcx=ffff9d808d7fa000
rdx=ffff9d808d7fa000 rsi=0000000000000002 rdi=0000023f30ee99f0
rip=fffff80025b04bd0 rsp=ffffe607bc53f0f8 rbp=0000023f30eea2fe
 r8=0000023f30ee99f0  r9=0000000000000964 r10=ffff9d808d7faea0
r11=0000023f30eea354 r12=ffffe607bc53f368 r13=ffffb402d84d8000
r14=ffff9d808d7fb000 r15=0000000000000000
iopl=0         nv up ei pl zr na po nc
cs=0010  ss=0000  ds=002b  es=002b  fs=0053  gs=002b             efl=00050246
nt!RtlDecompressBufferXpressLz+0x50:
fffff800`25b04bd0 418b08          mov     ecx,dword ptr [r8] ds:002b:0000023f`30ee99f0=????????
  *** Stack trace for last set context - .thread/.cxr resets it
 # Child-SP          RetAddr               Call Site
00 ffffe607`bc53f0f8 fffff800`25a5bc10     nt!RtlDecompressBufferXpressLz+0x50
01 ffffe607`bc53f110 fffff800`25a5bb14     nt!RtlDecompressBufferEx+0x60
02 ffffe607`bc53f160 fffff800`25a5b9a1     nt!ST_STORE<SM_TRAITS>::StDmSinglePageCopy+0x150
03 ffffe607`bc53f220 fffff800`25b56ff0     nt!ST_STORE<SM_TRAITS>::StDmSinglePageTransfer+0xa5
04 ffffe607`bc53f270 fffff800`25b57904     nt!ST_STORE<SM_TRAITS>::StDmpSinglePageRetrieve+0x180
05 ffffe607`bc53f310 fffff800`25b57aed     nt!ST_STORE<SM_TRAITS>::StDmPageRetrieve+0xc8
06 ffffe607`bc53f3c0 fffff800`25a5c071     nt!SMKM_STORE<SM_TRAITS>::SmStDirectReadIssue+0x85
07 ffffe607`bc53f440 fffff800`25aad478     nt!SMKM_STORE<SM_TRAITS>::SmStDirectReadCallout+0x21
08 ffffe607`bc53f470 fffff800`25a5cb57     nt!KeExpandKernelStackAndCalloutInternal+0x78
09 ffffe607`bc53f4e0 fffff800`25a5713c     nt!SMKM_STORE<SM_TRAITS>::SmStDirectRead+0xc7
0a ffffe607`bc53f5b0 fffff800`25a56b70     nt!SMKM_STORE<SM_TRAITS>::SmStWorkItemQueue+0x1ac
0b ffffe607`bc53f600 fffff800`25b58727     nt!SMKM_STORE_MGR<SM_TRAITS>::SmIoCtxQueueWork+0xc0
0c ffffe607`bc53f690 fffff800`25b2b94b     nt!SMKM_STORE_MGR<SM_TRAITS>::SmPageRead+0x167
0d ffffe607`bc53f700 fffff800`25ad1020     nt!SmPageRead+0x33
0e ffffe607`bc53f750 fffff800`25ad023d     nt!MiIssueHardFaultIo+0x10c
0f ffffe607`bc53f7a0 fffff800`25a6e818     nt!MiIssueHardFault+0x29d
10 ffffe607`bc53f860 fffff800`25c0b6d8     nt!MmAccessFault+0x468
11 ffffe607`bc53fa00 00007ff8`c3089fa2     nt!KiPageFault+0x358
12 00000067`4ca7f270 00000000`00000000     0x00007ff8`c3089fa2

从卦中的调用栈信息看,代码的源头是 用户态 (0x00007ff8c3089fa2) 过来的,应该是访问用户态地址 0000023f30ee99f0 上的内容,由于对应的物理页不在内存中,触发了 nt!KiPageFault 中断,也就是 idt 表中的 0xe 号标记的 缺页中断, 输出如下:


lkd> !idt

Dumping IDT: fffff8050ce87000

00:	fffff80506206400 nt!KiDivideErrorFault
...
0e:	fffff80506209980 nt!KiPageFault

在缺页中断中触发了 IO 操作 MiIssueHardFaultIo 要从pagefiles 中捞页面,接下来就是页读取逻辑 SmPageRead,最后在 RtlDecompressBufferXpressLz 中引发了蓝屏。

如果心比较细的话,你会发现有一个关键词 Decompress ,对,就是解压缩,为什么换入的page还要解压缩呢? 这就是我们的突破点。

3. 为什么会解压缩

要找到这个问题的答案,需要观察下这个异常线程的详细信息,可以用 .thread 切到异常的线程上下文,再用 !thread 观察。


2: kd> .thread
Implicit thread is now ffffb402`be04a080

2: kd> !thread ffffb402`be04a080
THREAD ffffb402be04a080  Cid 0594.2228  Teb: 000000674c5b8000 Win32Thread: 0000000000000000 RUNNING on processor 2
Not impersonating
GetUlongFromAddress: unable to read from fffff8002641152c
Owning Process            ffffb402b8d58080       Image:         <Invalid process>
Attached Process          ffffb402b984a040       Image:         MemCompression
fffff78000000000: Unable to get shared data
Wait Start TickCount      649763       
Context Switch Count      9              IdealProcessor: 0             
ReadMemory error: Cannot get nt!KeMaximumIncrement value.
UserTime                  00:00:00.000
KernelTime                00:00:00.000
Win32 Start Address 0x00007ff8c808afb0
Stack Init ffffe607bc53fb90 Current ffffe607bc53e800
Base ffffe607bc540000 Limit ffffe607bc539000 Call 0000000000000000
Priority 8 BasePriority 7 PriorityDecrement 0 IoPriority 2 PagePriority 2
Child-SP          RetAddr               : Args to Child                                                           : Call Site
ffffe607`bc53de78 fffff800`25d9856e     : 00000000`00000154 ffffb402`b9851000 ffffe607`bc53df30 00000000`00000002 : nt!KeBugCheckEx
ffffe607`bc53de80 fffff800`25c189db     : ffffb402`b9851000 ffffe607`bc53df30 ffffe607`00000002 ffffe607`bc53dfe0 : nt!SMKM_STORE<SM_TRAITS>::SmStUnhandledExceptionFilter+0x7e
ffffe607`bc53ded0 fffff800`25bcfb1f     : fffff800`00000002 fffff800`258d905c ffffe607`bc539000 ffffe607`bc540000 : nt!`SMKM_STORE<SM_TRAITS>::SmStDirectReadIssue\'::`1\'::filt$0+0x22
ffffe607`bc53df00 fffff800`25c062ff     : fffff800`258d905c ffffe607`bc53e4e0 fffff800`25bcfa80 00000000`00000000 : nt!_C_specific_handler+0x9f
...

从卦中信息看,异常线程还有一个附加的进程 ffffb402b984a040,来自于 MemCompression 模块,从名字上看所谓的 压缩解压缩 逻辑应该和它有关系,接下来到网上去搜一下,有一篇文章说的非常好: https://www.howtogeek.com/319933/what-is-memory-compression-in-windows-10/

大意:这是 Windows10 新增的一个功能,用内存压缩技术让RAM中可以存储更多的内存页,相比传统的交换到 PageFiles.sys 有更高的性能,缺点就是需要耗费一些解压缩需要的 CPU 时间。

在 Windows10 上也能窥探一二:

4. 问题解决

解决办法很简单,学 4S 店的问题解决之道,能换的就坚决不修,让朋友把 内存压缩 给关掉,这样就不走
RtlDecompressBufferXpressLz 逻辑,理论上就不会有什么问题了。

关闭之后,据朋友反馈,这几天没有崩溃了。

三:总结

分析内核态相比用户态难度要大的多,需要对操作系统以及CPU的相关知识有一个比较深度的理解,任重道远。。。

记一次.net某制造业mes系统崩溃分析(代码片段)

一:背景1.讲故事前段时间有位朋友微信找到我,说他的程序偶尔会出现内存溢出崩溃,让我帮忙看下是怎么回事,咨询了下程序是x86部署,听到这个词其实心里已经有了数,不管怎么样还是用windbg分析一... 查看详情

记一次腾讯会议的意外崩溃分析(代码片段)

一:背景1.讲故事前段时间在用腾讯会议直播的时候,居然意外崩溃了,还好不是在训练营上课,不然又得重录了,崩完之后发现腾讯会议的bugreport组件会自动生成一个minidump,截图如下:作为一个.NET高级调试的技术博主,非.NET... 查看详情

记一次.net某医疗器械程序崩溃分析(代码片段)

一:背景1.讲故事前段时间有位朋友在微信上找到我,说他的程序偶发性崩溃,让我帮忙看下怎么回事,上面给的压力比较大,对于这种偶发性崩溃,比较好的办法就是利用AEDebug在程序崩溃的时候自动抽一... 查看详情

记一次内存溢出查找分析文档(代码片段)

内存溢出介绍。内存溢出和内存泄漏的联系:内存泄漏会最终导致内存溢出。相同点:都会导致应用程序运行出现问题,性能下降或挂起。不同点:1内存泄漏是导致内存溢出的原因之一,内存泄漏积累起来导... 查看详情

记一次.net某医疗住院系统崩溃分析(代码片段)

一:背景1.讲故事最近收到了两起程序崩溃的dump,查了下都是经典的doublefree造成的,蛮有意思,这里就抽一篇出来分享一下经验供后面的学习者避坑吧。二:WinDbg分析1.崩溃点在哪里windbg带了一个自动化分析... 查看详情

记一次.net某企业erp网站系统崩溃分析(代码片段)

一:背景1.讲故事前段时间收到了一个朋友的求助,说他的ERP网站系统会出现偶发性崩溃,找了好久也没找到是什么原因,让我帮忙看下,其实崩溃好说,用procdump自动抓一个就好,拿到dump之后,接下来就是一顿分析了。二:WinD... 查看详情

记一次.net某智能服装智造系统内存泄漏分析(代码片段)

一:背景1.讲故事上个月有位朋友找到我,说他的程序出现了内存泄漏,不知道如何进一步分析,截图如下:朋友这段话已经说的非常言简意赅了,那就上windbg说话吧。二:Windbg分析1.到底是哪一方面的泄漏根据朋友描述,程序... 查看详情

记一次java的内存泄露分析

当前环境jdk==1.8httpasyncclient==4.1.3代码地址git地址:https://github.com/jasonGeng88/java-network-programming背景前不久,上线了一个新项目,这个项目是一个压测系统,可以简单的看做通过回放词表(http请求数据),不断地向服务发送请求,... 查看详情

记一次.net某桌面奇侠游戏非托管内存泄漏分析(代码片段)

一:背景1.讲故事说实话,这篇dump我本来是不准备上一篇文章来解读的,但它有两点深深的感动了我。无数次的听说用Unity可做游戏开发,但百闻不如一见。游戏中有很多金庸武侠小说才有的名字,太赏心悦目... 查看详情

记一次内存泄漏dump分析

自从进入一家创业公司以后,逐渐忙成狗,却无所收获,感觉自身的技术能力用武之地很少,工作生活都在业务逻辑中颠倒。前些天线上服务内存吃紧,让运维把DUMP拿下来,分析一下聊以自慰。 先来统计一下大对象信息0:000... 查看详情

记一次.net某家装erp系统内存暴涨分析(代码片段)

一:背景1.讲故事前段时间微信上有一位老朋友找到我,说他的程序跑着跑着内存会突然爆高,有时候会下去,有什么会下不去,怀疑是不是某些情况下存在内存泄露,让我帮忙分析一下,其实内存泄露方面的问题还是比较好解... 查看详情

记一次.net某电商定向爬虫内存碎片化分析(代码片段)

一:背景1.讲故事上个月有位朋友wx找到我,说他的程序存在内存泄漏问题,寻求如何解决?如下图所示:从截图中可以看出,这位朋友对windbg的操作还是有些熟悉的,可能缺乏一定的实操经验,所... 查看详情

记一次.net某智慧水厂api非托管内存泄漏分析(代码片段)

一:背景1.讲故事七月底的时候有位朋友在wx上找到我,说他的程序内存占用8G,托管才占用1.5G,询问剩下的内存哪里去了?截图如下:从求助内容看,这位朋友真的太客气了,动不动就谈钱,... 查看详情

记一次.net某外贸erp内存暴涨分析(代码片段)

一:背景1.讲故事上周有位朋友找到我,说他的API被多次调用后出现了内存暴涨,让我帮忙看下是怎么回事?看样子是有些担心,但也不是特别担心,那既然找到我,就给他分析一下吧。二:WinDbg分析1.到底是哪里的泄露这也是... 查看详情

记一次.net某上市工业智造cpu+内存+挂死三高分析(代码片段)

一:背景1.讲故事上个月有位朋友加wx告知他的程序有挂死现象,询问如何进一步分析,截图如下:看这位朋友还是有一定的分析基础,可能玩的少,缺乏一定的分析经验,当我简单分析之后,我发... 查看详情

记一次.net某手术室行为信息系统内存泄露分析(代码片段)

一:背景1.讲故事昨天有位朋友找到我,说他的程序内存存在泄露导致系统特别卡,大地址也开了,让我帮忙看一下怎么回事?今天上午看了下dump,感觉挺有意思,在我的分析之旅中此类问题也蛮少见,算是完善一下体系吧。二... 查看详情

记一次.net某电厂web系统内存泄漏分析

一:背景1.讲故事前段时间有位朋友找到我,说他的程序内存占用比较大,寻求如何解决,截图就不发了,分析下来我感觉除了程序本身的问题之外,.NET5在内存管理方面做的也不够好,所以有必要给大家分享一下。二:WinDbg分... 查看详情

记一次spark内存泄露问题

问题定位:引擎里有一处代码detectDf.persist(detectDf为DataFrame),后续回收动作用的代码为valrdds=sc.getPersistentRDDsrdds.foreach(x=>x._2.unpersist())分析:1.DataFrame跟RDD相比,就是多了 查看详情