以下rip不正确,指令地址错乱,栈信息已破坏;在此基础上准肯定位很是困难,可是仍可发现一些线索;html
根据当前栈信息,大概寻找到怀疑的函数数组
查看整个栈上下信息,看有无怀疑的函数:并发
因此颇有可能就是fetchNSAddrEv函数致使,须要重点关注;
更深刻的细节,限于汇编不深刻,比较难分析,不过能够有另外途径;socket
-fstack-protector:
启用堆栈保护,不过只为局部变量中含有 char 数组的函数插入保护代码。
-fstack-protector-all:
启用堆栈保护,为全部函数插入保护代码。
详细参见:http://www.cnblogs.com/napoleon_liu/archive/2011/02/14/1953983.html
复现后,栈结构以下:函数
这个栈结构,看着比较爽了; AsyncConnect返回时stackcheck检查栈溢出了;测试
先贴下代码fetch
int CHttpHandler::AsyncConnect(const char * pszIpAddr, unsigned short usPort, float fSelectTimeout, const std::string& host) { if (strlen(pszIpAddr) > 16) { SetErrMsg("invalid ip format, pszIpAddr=%s", pszIpAddr); return -1; } m_nSockFD= socket(AF_INET, SOCK_STREAM, 0); m_fSelectTimeout= fSelectTimeout; if (m_nSockFD < 0) { SetErrMsg("init socket error, m_nSockFD=%d", m_nSockFD); return -2; } // set nonblock int flags = fcntl(m_nSockFD, F_GETFL, 0); if (fcntl(m_nSockFD, F_SETFL, flags | O_NONBLOCK) < 0) { close(m_nSockFD); m_nSockFD = -1; SetErrMsg("set sock nonblock error"); return -3; } // set server ip, port struct sockaddr_in serv_addr; socklen_t addr_len; bzero(&serv_addr, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; inet_aton(pszIpAddr, &serv_addr.sin_addr); serv_addr.sin_port = htons(usPort); addr_len = sizeof(serv_addr); //memset(m_szIPAddr, 0, 17); memset(m_szIPAddr,0,sizeof(m_szIPAddr)); if(!host.empty()) { if(host.length()>sizeof(m_szIPAddr)-1) { SetErrMsg("hostName too long ,hostName=%s,hostLen=%d,max_len=%d\n",host.c_str(),host.length(),sizeof(m_szIPAddr)); return -9; } memcpy(m_szIPAddr, host.c_str(), host.length()); } else { memcpy(m_szIPAddr, pszIpAddr, strlen(pszIpAddr)); } /*linger m_sLinger; m_sLinger.l_onoff = 1; m_sLinger.l_linger = 0; setsockopt(m_nSockFD, SOL_SOCKET,SO_LINGER,(const char*)&m_sLinger,sizeof(linger));*/ // connect int nRetCode; nRetCode = connect(m_nSockFD, (struct sockaddr *)&serv_addr, addr_len); if (nRetCode < 0) { if (errno != EINPROGRESS) { SetErrMsg("connect error, errno=%d", errno); close(m_nSockFD); m_nSockFD = -1; return -4; } } if (nRetCode == 0) { ; } else { //warning oss find select cause crash while select timeout fd_set rset, wset; FD_ZERO(&rset); FD_SET(m_nSockFD, &rset); wset = rset; struct timeval tv; tv.tv_sec = (int)m_fSelectTimeout; long usec = int ((m_fSelectTimeout - (int)m_fSelectTimeout)*1000000 ); tv.tv_usec = usec; nRetCode = select(m_nSockFD + 1, NULL, &wset, NULL, &tv); if (nRetCode == 0) { // timeout SetErrMsg("connect error: select timeout, select retcode=%d ", nRetCode); close(m_nSockFD); m_nSockFD = -1; return -5; } if ( FD_ISSET(m_nSockFD, &wset))//FD_ISSET(m_nSockFD, &rset) || { int error; socklen_t len = sizeof(error); if (getsockopt(m_nSockFD, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { SetErrMsg("connect error: getsockopt"); close(m_nSockFD); m_nSockFD = -1; return -6; } if (error) { SetErrMsg("connect error in select operation, error=%d", error); close(m_nSockFD); m_nSockFD = -1; return -7; } } else { SetErrMsg("connect error"); close(m_nSockFD); m_nSockFD = -1; return -8; } }
几我的审查代码,看了很久,没有发现该函数有什么问题;3d
进步查看日志发现有大量的select超时的打印,并且core掉时,必然在超时事件以后,此时怀疑select调用是否会出问题;所以,首先修改select为epoll调用进行测试,问题不能复现了;日志
@福巴找到内核代码查看select相关实现;
当n值超过1024上限就会致使设置到数组以外,篡改掉内存;code
FD_SET(m_nSockFD, &rset); 在m_nSockFD超过1024时,会致使rset数组越界,篡改后续内存;这也佐证了为何select不少超时错误,由于m_nSockFD大小越界,没有落在select监听的套接字集合内;
从OSS环境上看,压测时,有大量链接并发处理,因此在压测时才最终发现该问题;
全部使用select系统调用的代码应提升警戒,系统使用的套接字超过默认配置的(1024,看系统配置)会致使这个潜在问题;