SOFTWARE_NX_FAULT的疑问?

shunning88 2009-08-17 03:06:26
碰到一个很神奇的问题,可能和Windows的NX机制有关。
描述如下,大虾们伸出援手~~~Help me

系统环境:
Windows2003 (2003默认开启了NX)
SQLSERVER2000

软件崩溃后,产生dmp。
使用windbg载入dmp进行分析。
提示为
c0000005(Access violation)
Attempt to execute non-executable address 00886169
DEFAULT_BUCKET_ID: SOFTWARE_NX_FAULT
EIP指向 00886169 8b4c2404 mov ecx,dword ptr [esp+4]

我的分析过程如下:
1.SOFTWARE_NX_FAULT 查了下关于该标识的含义,似乎只有当违反了NX机制(在非代码段中执行代码)时,才可能产生该错误。可是这时EIP指向明明是代码段,如下:
上下文环境:
eax=00000001 ebx=00000000 ecx=02befd28 edx=02befca4 esi=01692c48 edi=0012f758
eip=00886169 esp=02befbe4 ebp=02befcb8 iopl=0 nv up ei pl zr na pe nc
模块:
start end module name
00880000 00984000 mfc71u (private pdb symbols)
所以00886169肯定是代码段了。

2.Attempt to execute non-executable address 00886169
按windbg里所提示的,执行了不可以被执行的地址0x00886169,那么我查看了关于该地址的页属性
0:007> !address 0x00886169
00880000 : 00881000 - 000dc000
Type 01000000 MEM_IMAGE
Protect 00000020 PAGE_EXECUTE_READ
State 00001000 MEM_COMMIT
Usage RegionUsageImage
FullPath C:\WINDOWS\system32\mfc71u.dll
该页的属性为PAGE_EXECUTE_READ,是可以执行的。
很矛盾...

3.00886169 8b4c2404 mov ecx,dword ptr [esp+4]
如果esp+4指向莫名的地址,那也可能会出现这种错误。
0:007> !address esp+4
02af0000 : 02bed000 - 00003000
Type 00020000 MEM_PRIVATE
Protect 00000004 PAGE_READWRITE
State 00001000 MEM_COMMIT
Usage RegionUsageStack
Pid.Tid 528.614
也同样是正常的...

原本怀疑是开启了NX的问题,但现象与NX所表现出来的现象又不相同。
在网上看到的示例,EIP指向的地址由于NX的作用,会变得不可执行。
这个问题困扰我很久了,用了很多很多的时间与精力都解决不了。
希望得到各位大大的帮助。。

以下是windbg的分析结果:
0:007> !analyze -v
*******************************************************************************
* *
* Exception Analysis *
* *
*******************************************************************************

*************************************************************************
*** ***
*** ***
*** Your debugger is not using the correct symbols ***
*** ***
*** In order for this command to work properly, your symbol path ***
*** must point to .pdb files that have full type information. ***
*** ***
*** Certain .pdb files (such as the public OS symbols) do not ***
*** contain the required information. Contact the group that ***
*** provided you with these symbols if you need this command to ***
*** work. ***
*** ***
*** Type referenced: kernel32!pNlsUserInfo ***
*** ***
*************************************************************************
*************************************************************************
*** ***
*** ***
*** Your debugger is not using the correct symbols ***
*** ***
*** In order for this command to work properly, your symbol path ***
*** must point to .pdb files that have full type information. ***
*** ***
*** Certain .pdb files (such as the public OS symbols) do not ***
*** contain the required information. Contact the group that ***
*** provided you with these symbols if you need this command to ***
*** work. ***
*** ***
*** Type referenced: kernel32!pNlsUserInfo ***
*** ***
*************************************************************************

FAULTING_IP:
mfc71u!ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t> > >::Format+0 [f:\vs70builds\6030\vc\mfcatl\ship\atlmfc\include\cstringt.h @ 1795]
00886169 8b4c2404 mov ecx,dword ptr [esp+4]

EXCEPTION_RECORD: ffffffff -- (.exr 0xffffffffffffffff)
ExceptionAddress: 00886169 (mfc71u!ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t> > >::Format)
ExceptionCode: c0000005 (Access violation)
ExceptionFlags: 00000000
NumberParameters: 2
Parameter[0]: 00000008
Parameter[1]: 00886169
Attempt to execute non-executable address 00886169

DEFAULT_BUCKET_ID: SOFTWARE_NX_FAULT

PROCESS_NAME: DocSystem.exe

ERROR_CODE: (NTSTATUS) 0xc0000005 - "0x%08lx"

WRITE_ADDRESS: 00886169

NTGLOBALFLAG: 0

APPLICATION_VERIFIER_FLAGS: 0

LAST_CONTROL_TRANSFER: from 013ca90c to 00886169

STACK_TEXT:
02befbe0 013ca90c 02befca4 013dfa68 00000008 mfc71u!ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t> > >::Format [f:\vs70builds\6030\vc\mfcatl\ship\atlmfc\include\cstringt.h @ 1795]
02befcb8 013b8df0 01692c68 016965f0 02befd28 DataBaseSystem!CPto_Bussiness::CPto_TimeOutSearch+0x8c [e:\workspace\Êý¾Ý¿âÄ£¿é\databasesystem\tables\pto_bussiness.cpp @ 39]
02befcec 013ba825 00004844 00000000 02befd28 DataBaseSystem!CDBSQLSERVER::DBSqlServer_IBussinessWork+0xbc0 [e:\workspace\Êý¾Ý¿âÄ£¿é\databasesystem\innerclass\dbsqlserver.cpp @ 517]
02befd00 004312a2 00004844 00000000 02befd28 DataBaseSystem!DBSystem_IBussinessWork+0x35 [e:\workspace\Êý¾Ý¿âÄ£¿é\databasesystem\interface\dbinterface.cpp @ 138]
WARNING: Stack unwind information not available. Following frames may be wrong.
02befd10 0012f758 004308d0 02beffec 01696a38 DocSystem+0x312a2
02befd28 009633b0 00000000 009633b0 009633b0 0x12f758
02befd2c 00000000 009633b0 009633b0 009633b0 mfc71u!afxStringManager+0x14


STACK_COMMAND: ~7s; .ecxr ; kb

FAULTING_THREAD: 00000614

PRIMARY_PROBLEM_CLASS: SOFTWARE_NX_FAULT

BUGCHECK_STR: APPLICATION_FAULT_SOFTWARE_NX_FAULT_SOFTWARE_NX_FAULT_FALSE_POSITIVE

FOLLOWUP_IP:
mfc71u!ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t> > >::Format+0 [f:\vs70builds\6030\vc\mfcatl\ship\atlmfc\include\cstringt.h @ 1795]
00886169 8b4c2404 mov ecx,dword ptr [esp+4]

FAULTING_SOURCE_CODE:
No source found for 'f:\vs70builds\6030\vc\mfcatl\ship\atlmfc\include\cstringt.h'


SYMBOL_STACK_INDEX: 0

SYMBOL_NAME: mfc71u!ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t> > >::Format+0

FOLLOWUP_NAME: MachineOwner

MODULE_NAME: mfc71u

IMAGE_NAME: mfc71u.dll

DEBUG_FLR_IMAGE_TIMESTAMP: 44b45834

FAILURE_BUCKET_ID: mfc71u.dll!ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t> > >::Format_c000

BUCKET_ID: APPLICATION_FAULT_SOFTWARE_NX_FAULT_SOFTWARE_NX_FAULT_FALSE_POSITIVE_mfc71u!ATL::CStringT_wchar_t,StrTraitMFC_DLL_wchar_t,ATL::ChTraitsCRT_wchar_t_____::Format+0

Followup: MachineOwner
---------

0:007> .ecxr
eax=00000001 ebx=00000000 ecx=02befd28 edx=02befca4 esi=01692c48 edi=0012f758
eip=00886169 esp=02befbe4 ebp=02befcb8 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
mfc71u!ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t> > >::Format:
00886169 8b4c2404 mov ecx,dword ptr [esp+4] ss:0023:02befbe8=02befca4

另,模块中同时加载了MFC71U与MFC71两个DLL,不知是否有关系。
00880000 00984000 mfc71u (private pdb symbols)
7c140000 7c243000 mfc71 (deferred)

...全文
308 点赞 收藏 16
写回复
16 条回复
切换为时间正序
当前发帖距今超过3年,不再开放新的回复
发表回复
shunning88 2009-08-31
结贴了。。。打算升级下界面库~很多历史原因
回复
shunning88 2009-08-31
问题果然出现在界面库内~~~
去掉界面库后,同样环境下,再也没出过错。。
回复
shunning88 2009-08-28
WinDBG用了符号文件和源码之后,应该不算黑箱了。
使用哪种调试器并不重要,只要能看内存,能看堆栈,载个符号就可以了。
重要的是如何发现问题的本质。
不过嘛,我还真想用VC,WinDBG界面实在不友好,一堆堆的符号字符,眼看花了。

之前的问题有了新的进展,原来所有的DUMP文件,都不是第一现场。
为了让环境但简单些,我排除了一切可以排除的模块。
最后发现问题出在界面库中。目前看起来界面库中有BUG,会无缘无故的释放掉一个指针。
dump实在看不出什么了,准备用写日志的方式来跟踪出错时的界面库情况。
回复
MoXiaoRab 2009-08-25
有VC环境调试器不用,你为什么非要弄WinDBG进行黑箱,无语
回复
shunning88 2009-08-25
8月25日最新的情况:
采用QTP自动化测试,减轻不少负担,增加了该BUG的重现速度。
仔细分析了十来个的DUMP文件,虽然报错堆栈甚至连模块都不相同,但却这些DUMP有着一个共同点。
就是在BUGCHECK_STR中都有着相同的提示:SOFTWARE_NX_FAULT_FALSE_POSITIVE
FALSE_POSITIVE在英文中的解释为误报
那么按字面上的翻译,应该是软件NX错误误报。
操作系统还会告诉我,这个程序发生的异常是误报?既然知道程序是误报了,为什么不自己处理了?
在我的程序中,DUMP机制是使用SetUnhandledExceptionFilter来注册一个顶层过滤函数。
在顶层过滤函数内,调MiniDumpWriteDump生成DUMP,在函数完成时返回exception_Execute_handle。
这样系统原来那个UnhandledExceptionFilter就不会走了。
找到2K的UnhandledExceptionFilter代码,发现在写资源区的情况下UnhandledExceptionFilter返回exception_continue_execute,让程序继续正常运行。
如果我确定发生这种异常的情况下,程序还能正常运行,那么只要处理了这种情况,程序就不会乱崩了。

带着这种设想,我尝试了以下的操作:
为了验证这种SOFTWARE_NX_FAULT_FALSE_POSITIVE异常发生后是否能返回异常点继续正常的执行代码,
我去掉了自己程序内的DUMP机制,将Windbg设为了实时调试.
在QTP反复的操作下,BUG重现了。
BUGCHECK_STR同样提示SOFTWARE_NX_FAULT_FALSE_POSITIVE.
对于一般的异常,如果这种异常没有被解决,即使在Windbg让程序继续执行,这种异常还是会被触发,经过两轮分发,最后又回到Windbg上。而对于我碰到的这个异常,g之后程序还真的正常运行下去了。-_-##
所以,我估计还真的是误报了。
现在我更想知道,为什么会报出误报这种异常,总有个原因吧。。。



回复
shunning88 2009-08-20
很感谢whoo同学和compilerit同学。
该BUG实际上只是一系列类似BUG中的一个。

这一系列的BUG情况都有一个共同点,就是在CString的方法上出现异常。
例如,重载的等号,上面所看到的Format,以及CString构造方法内都会出现这种异常。
而堆栈回溯却不能发现有任何错误。

现在我先按whoo同学提议的方法进行测试。
回复
compilerit 2009-08-20
这个估计不是你程序的问题,是受到其它程序影响,当然也可能是操作系统的bug,毕竟32位操作系统上实现的DEP都是没有硬件支持的,软件模拟的比较忽悠人,可能有bug
回复
compilerit 2009-08-19
我错了,我用的是旧版的MSDN,在线MSDN有说明,DEP是8没错
Exception code Meaning
EXCEPTION_ACCESS_VIOLATION
The first element of the array contains a read-write flag that indicates the type of operation that caused the access violation. If this value is zero, the thread attempted to read the inaccessible data. If this value is 1, the thread attempted to write to an inaccessible address. If this value is 8, the thread causes a user-mode data execution prevention (DEP) violation.
The second array element specifies the virtual address of the inaccessible data.
EXCEPTION_IN_PAGE_ERROR
The first element of the array contains a read-write flag that indicates the type of operation that caused the access violation. If this value is zero, the thread attempted to read the inaccessible data. If this value is 1, the thread attempted to write to an inaccessible address. If this value is 8, the thread causes a user-mode data execution prevention (DEP) violation.
The second array element specifies the virtual address of the inaccessible data.
The third array element specifies the underlying NTSTATUS code that resulted in the exception.
回复
compilerit 2009-08-19
EXCEPTION_RECORD: ffffffff -- (.exr 0xffffffffffffffff)
ExceptionAddress: 00886169 (mfc71u!ATL::CStringT <wchar_t,StrTraitMFC_DLL <wchar_t,ATL::ChTraitsCRT <wchar_t> > >::Format)
ExceptionCode: c0000005 (Access violation)
ExceptionFlags: 00000000
NumberParameters: 2
Parameter[0]: 00000008
Parameter[1]: 00886169
Attempt to execute non-executable address 00886169

这组是EXCEPTION_RECORD参数,不是楼上理解的Format函数的参数

这里比较奇怪,Parameter[0]按照MSDN的说法只会是0或者1

Exception code Meaning
EXCEPTION_ACCESS_VIOLATION The first element of the array contains a read-write flag that indicates the type of operation that caused the access violation. If this value is zero, the thread attempted to read the inaccessible data. If this value is 1, the thread attempted to write to an inaccessible address.
The second array element specifies the virtual address of the inaccessible data.

回复
whoo 2009-08-19
从 "系统运行3~4天才会出现一次" 来看,很像是内存被覆写的类似野指针或者数组越界的错误。 但是一般的栈错误,或者简单的内存错误,即便没有影响到运行结果,调试版应该仍会报告一些警告信息。

所以建议还是用vc加载调试器跑一段时间,然后退出。 仔细检查输出是否有内存相关的警告信息。
回复
whoo 2009-08-19
注意这个地址: 00886169.
00886169 8b4c2404 mov ecx,dword ptr [esp+4]

和format的第一个参数:Parameter[1]: 00886169

EXCEPTION_RECORD: ffffffff -- (.exr 0xffffffffffffffff)
ExceptionAddress: 00886169 (mfc71u!ATL::CStringT <wchar_t,StrTraitMFC_DLL <wchar_t,ATL::ChTraitsCRT <wchar_t> > >::Format)
ExceptionCode: c0000005 (Access violation)
ExceptionFlags: 00000000
NumberParameters: 2
Parameter[0]: 00000008
Parameter[1]: 00886169
Attempt to execute non-executable address 00886169


参数已经不对了。并且是很奇怪的字符串地址变成了当前指令地址。 说明在这附近已经有栈溢出或者栈调用错误发生。

注意到这一行:
LAST_CONTROL_TRANSFER: from 013ca90c to 00886169

上一步的执行地址013ca90c似乎还没问题。


建议
1. 检查format语句上下的相关调用。
2. 在这一句 call dword ptr [013d80f0] 前后,检查 this 的所有成员变量是否正常。
回复
shunning88 2009-08-19
感谢whoo同学的关注与提醒。
我回溯了这段堆栈,在这段堆栈内,我详细的检查了Format的参数。
很遗憾,并没有发现有任何错误,堆栈内的数据和我手工分析汇编算出来的数据是相同的。
Format是类里的成员函数,有两个参数。
一般情况对于类里的成员,编译器采用thiscall的约定。
但format函数被声明为了采用c调用方式。
所以,在反汇编看来,这里一共会被压栈3次。
C++代码:
strBaseSQL.Format(_T("select * from taa_timeout where tt_nodestatus&%d<1"),8);

汇编代码:
013caa06 6a08 push 8 ->压入8
013caa08 68c0f83d01 push offset DataBaseSystem!`string' (013df8c0) ->压入字符串"select * from taa_timeout where tt_nodestatus&%d<1"
013caa0d 8d55ec lea edx,[ebp-14h] ->取出ebp-14h的有效地址,this指针
013caa10 52 push edx
....略
013caa23 ff15f0803d01 call dword ptr [013d80f0]

对应栈:
02befbd4 00000000
02befbd8 00000000
02befbdc 00000000
02befbe0 00000000
02befbe4 013ca90c 调用call时自动压入的返回地址
02befbe8 02befca4 包含this指针
02befbec 013dfa68 "select * from taa_timeout where tt_nodestatus&%d<1"
02befbf0 00000008 8
02befbf4 0012f758
02befbf8 01692c48
02befbfc 00000000

所以,可以确定调用Format时,参数没有问题的。
再来看
013caa23 ff15f0803d01 call dword ptr [013d80f0]
在0x013d80f0地址上保存的是format的地址。
当cpu执行到format的第一条指令mov ecx,dword ptr [esp+4]时,
就出现了C0000005错误。

这个BUG,出现的机率比较低,系统运行3~4天才会出现一次。
而用VC一步一步的去跟是没有任何问题的。

很是郁闷啊~~
回复
shunning88 2009-08-18
T_T 难怪发错版块了~
回复
shunning88 2009-08-18
T_T 难怪发错版块了~
回复
zw0558 2009-08-18
学习
回复
whoo 2009-08-18
不用这么大张旗鼓吧,MFC的程序,直接用VC开调试运行,多测试下不就行了。

而且信息也很明显,指的是CString的format的问题。 format早期是个很容易被利用的溢出攻击点,7.1应该有所改善,但是传入错误的参数还是很容易崩溃的。

根据堆栈检查上层函数的调用参数就可以了。
回复
发动态
发帖子
硬件/系统
创建于2007-09-28

2590

社区成员

VC/MFC 硬件/系统
申请成为版主
社区公告
暂无公告