能力值:
( LV3,RANK:30 )
2 楼
如果这段汇编是由编译器生成的,虽然给的只是个汇编片段,仅从修改 esi 寄存器的语句就不难看出,004028A5 mov esi,ecx
如果 ecx 为 0, 那么以后的 esi 是存在为 0 的可能性的,从 esi 使用的频率来看,它肯定是一个经常被访问的变量,所以请检查你的那句 mov esi,ecx 执行完之后不可能为0是否正确,个人意见,望采纳。
能力值:
( LV2,RANK:10 )
3 楼
0040289F mov eax,dword ptr [ecx+14h]
004028A2 sub eax,0
这是前面一 行代码,代码是编译生成的,并不是自己写的。
ecx不可能为0,这然在这一行就已经crash了。
这里:00402906 call dword ptr ds:[437020h] 是调用API CreateIoCompletionPort
在这里也不可能调用成功。
这段代码是一个switch 其实执行的只是很有限的几行
现在我的猜测是在线程挂起的时候,其它线程溢出导致此线程的context被覆盖。
能力值:
( LV3,RANK:30 )
4 楼
那可以看一下是否有其它跳转语句,在不经过前两句的情况下,直接跳转到 mov esi,ecx了。线程挂起,其它线程溢出基本没这个可能,context 是保存在内核中,并且 nt 内核的 context 也不像 linux 那个,在栈顶保存的当前线程对象,所以不存在这个可能。如果汇编代码不长,可以贴出所有的代码,以便分析有更多的上下文。我曾经怀疑过是否是自己写的汇编过程没有保护好寄存器值,但又说是编译器编译的,所以一般不会出现这种现象。把代码贴全吧。
能力值:
( LV2,RANK:10 )
5 楼
0040286F int 3
00402870 mov eax,dword ptr [esp+4]
00402874 mov ecx,dword ptr [eax+4]
00402877 sub esp,8
0040287A push ebx
0040287B mov ebx,dword ptr [ecx+4]
0040287E push ebp
0040287F mov ebp,dword ptr ds:[437040h]
00402885 push esi
00402886 push edi
00402887 push 0FFFFFFFFh
00402889 lea edx,[esp+18h]
0040288D push edx
0040288E lea eax,[esp+24h]
00402892 push eax
00402893 lea ecx,[esp+1Ch]
00402897 push ecx
00402898 push ebx
00402899 call ebp
0040289B mov ecx,dword ptr [esp+1Ch]
0040289F mov eax,dword ptr [ecx+14h]
004028A2 sub eax,0
004028A5 mov esi,ecx
004028A7 je 004028FB
004028A9 sub eax,1
004028AC je 004028DA
004028AE sub eax,1
004028B1 jne 00402887
004028B3 mov eax,dword ptr [esp+10h]
004028B7 test eax,eax
004028B9 ja 004028D0
004028BB push 0
004028BD push esi
004028BE push 0
004028C0 push ebx
004028C1 mov dword ptr [ecx+14h],1
004028C8 call dword ptr ds:[437030h]
004028CE jmp 00402887
004028D0 mov edx,dword ptr [ecx]
004028D2 push eax
004028D3 mov eax,dword ptr [edx+14h]
004028D6 call eax
004028D8 jmp 00402887
004028DA mov edx,dword ptr [ecx]
004028DC mov eax,dword ptr [edx+10h]
004028DF call eax
004028E1 mov ecx,dword ptr [esi+18h]
004028E4 push ecx
004028E5 call dword ptr ds:[4371C0h]
004028EB call 00401200
004028F0 push esi
004028F1 call 00419DAF
004028F6 add esp,4
004028F9 jmp 00402887
004028FB mov edx,dword ptr [ecx+18h]
004028FE push 0
00402900 push ecx
00402901 lea edi,[ecx+18h]
00402904 push ebx
00402905 push edx
00402906 call dword ptr ds:[437020h]
0040290C test eax,eax
0040290E jne 0040292C
00402910 mov eax,dword ptr [edi]
00402912 push eax
00402913 call dword ptr ds:[4371C0h]
00402919 call 00401200
0040291E push esi
0040291F call 00419DAF
00402924 add esp,4
00402927 jmp 00402887
0040292C mov edx,dword ptr [esi]
0040292E mov eax,dword ptr [edx+0Ch]
00402931 mov ecx,esi
00402933 call eax
00402935 jmp 00402887
0040293A int 3
看看
能力值:
( LV2,RANK:10 )
6 楼
template<typename T>
DWORD WINAPI Processor<T>::run(LPVOID param)
{
Processor<T>& procor = *(Processor<T> *)param;
Scheduler<T>& scheder = *procor.scheder;
HANDLE iocp = scheder.iocp;
DWORD ready;
ULONG_PTR key;
WSAOVERLAPPED * overlap;
while (true)
{
::GetQueuedCompletionStatus(iocp, &ready, &key, (LPOVERLAPPED *)&overlap, INFINITE);
T * clt = (T *)key;
switch(clt->event)
{
case T::EV_RECV:
{
if (0 >= ready)
{
clt->event = T::EV_DISCONNECT;
::PostQueuedCompletionStatus(iocp, 0, (ULONG_PTR)clt, NULL);
}
else
{
clt->OnRecv(ready);
}
}
break;
case T::EV_CONNECT:
{
if (NULL == ::CreateIoCompletionPort((HANDLE)clt->fd, iocp, (ULONG_PTR)clt, 0))
{
::closesocket(clt->fd);
delete clt;
}
else
{
clt->OnConnect();
}
}
break;
case T::EV_DISCONNECT:
{
clt->OnDisconnect();
::closesocket(clt->fd);
delete clt;
}
break;
case T::EV_SEND:
break;
}
}
return 0;
}
这是C++代码,崩溃在这一行 clt->OnConnect();
0x0040292c 处未处理的异常: 0xC0000005: 读取位置 0x00000000 时发生访问冲突
能力值:
( LV3,RANK:30 )
7 楼
从得到的汇编看确实没有显示地修改 esi 的值,但这编译出来的代码总感觉不像是 vc 编译器的风格啊,要么就是过度优化了吧,汇编中 ecx 和 esi 都应该是相同的值 ecx = key, esi = ctl,编译器在使用这两个变量时用了两个寄存器来存取,并且在调用 CreateIoCompletionPort 时用的是 ecx ,却在下面调用 clt->OnConnect() 是神来之笔的选用了 esi 来作为该对象的地址来调用虚函数,感觉很奇怪。回到主题,esi 被改变了,是否会因为其它函数中有间接引用到 esi 的地址,如 rep 之类的指定来进行拷贝,却又没有还原呢(这属于编译器的问题了,但在拷贝时 esi 指向 0 确实不敢想像),要想解决这个问题在,ctl 前面加上 volatile ,阻止这种优化。
能力值:
( LV2,RANK:10 )
8 楼
是VC编译的,release版默认的速度优化,之所以用ecx和esi两个寄存器是因为CreateIoCompletionPort会修改ecx的值。我是调试之后才知道。
能力值:
( LV2,RANK:10 )
9 楼
我还是认为是溢出导致覆盖,因为出过几次crash,每一次位置都不一样,都是在看不出明显问题的位置出现的。
但这个才让我觉得会不会是覆盖了content,因为esi确实没有地方会改变它。
也只有溢出也会出现这种莫名其妙的崩溃吧
能力值:
( LV3,RANK:30 )
10 楼
vc 中使用 call ebp 把 ebp 当地址都来使用的很少见啊,那么多空闲的寄存器可用。由于函数内部会改变 ecx 的值,而让编译器在外部做保护的也是少之又少,因为在编译时,不像 MASM 那样,会标识出可能会引起哪些寄存器的变化。一般溢出才会引起这种问题,这个观点我造成,但由于内核栈溢出造成 context 的改变,这个几乎不可能。那些就还有一个情况了,用户级线程溢出导致的指令跳转异常了。
能力值:
( LV3,RANK:30 )
11 楼
或者是 key 的值本身不对,在访问第 0 个偏移时,造成错误。
能力值:
( LV2,RANK:10 )
12 楼
key值本身是没问题的。因为我通过esp拿到栈上保存的esi的值。
并且把该地址作为指针还原类对象成功。
类成员的值都是正常的。
能力值:
( LV2,RANK:10 )
13 楼
而且key值有错误的话,调用CreateIoCompletionPort是不可能成功的。
能力值:
( LV3,RANK:30 )
14 楼
那这个就奇怪了,如果每次崩溃地址不一样,强烈建议,看一下是否是其它线程跳转失误导致,但你说通过栈可以观察到是本线程的值,这就奇怪了,或者看一下头文件是否不匹配,有些函数的调用约定是否出现了不同造成了栈不平衡。
能力值:
( LV2,RANK:10 )
15 楼
我已经有点无语了,同一个程序,在win2008上正常,win2003上就崩溃。
vs2005编译的,现在用vs2013编译了一个,在win2003上测试,目前还没有崩溃,先看看情况能运行多久。
要说其它的线程,一共程序才三个线程,一个线程纯粹的sleep防止程序退出,还一个在accept,一直处于等待状态,因为没有连接。
能力值:
( LV2,RANK:10 )
16 楼
调用约定也没有什么特别的,应该全部是cdcall,如果不同应该还编译不过。
能力值:
( LV3,RANK:30 )
17 楼
还是加一个 volatile 限定一下吧,每次都从内存中取,免得造成寄存器被修改的问题。但是我感觉是否有其它地方修改掉了 esi 的值,而编译器没有察觉到,导致编译出来的代码不合理。