zoukankan      html  css  js  c++  java
  • Select 使用不当引发的core,你应该知道的

    排查一个死机问题,搞了好几天时间,最终确定原因;最终确定问题原因,在此分享一下;

    第一步:常规根据core文件查看栈信息,gdb –c core xxxx

    如下rip不正确,指令地址错乱,栈信息已破坏;在此基础上准确定位非常困难,但是仍可发现一些线索;

    screenshot

    根据当前栈信息,大概寻找到怀疑的函数

    screenshot

    查看整个栈上下信息,看有无怀疑的函数:

    screenshot

    所以很有可能就是fetchNSAddrEv函数导致,需要重点关注;
    更深入的细节,限于汇编不深入,比较难分析,不过可以有另外途径;

    第二步:因为core是能复现出来,所以思路是重新编译版本,增加编译选项-fstack-protector

    -fstack-protector:
    启用堆栈保护,不过只为局部变量中含有 char 数组的函数插入保护代码。
    -fstack-protector-all:
    启用堆栈保护,为所有函数插入保护代码。
    详细参见:http://www.cnblogs.com/napoleon_liu/archive/2011/02/14/1953983.html
    复现后,栈结构如下:

    screenshot

    这个栈结构,看着比较爽了; AsyncConnect返回时stackcheck检查栈溢出了;

    第三步、审查代码,所以重点排查该函数:

    先贴下代码

    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
    ",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;
    		}
    }
    

    几个人审查代码,看了好久,没有发现该函数有什么问题;

    第四步:检查日志及系统配置

    进步查看日志发现有大量的select超时的打印,而且core掉时,必然在超时事件之后,此时怀疑select调用是否会出问题;因此,首先修改select为epoll调用进行测试,问题不能复现了;

    五、如此,加深怀疑slecect相关处理

    @福巴找到内核代码查看select相关实现;
    screenshot
    当n值超过1024上限就会导致设置到数组之外,篡改掉内存;

    FD_SET(m_nSockFD, &rset); 在m_nSockFD超过1024时,会导致rset数组越界,篡改后续内存;这也佐证了为什么select很多超时错误,因为m_nSockFD大小越界,没有落在select监听的套接字集合内;
    从OSS环境上看,压测时,有大量连接并发处理,所以在压测时才最终发现该问题;

    六、总结:

    所有使用select系统调用的代码应提高警惕,系统使用的套接字超过默认配置的(1024,看系统配置)会导致这个潜在问题;

  • 相关阅读:
    1.27
    1.25
    Representation Learning with Contrastive Predictive Coding
    Learning a Similarity Metric Discriminatively, with Application to Face Verification
    噪声对比估计(负样本采样)
    Certified Adversarial Robustness via Randomized Smoothing
    Certified Robustness to Adversarial Examples with Differential Privacy
    Dynamic Routing Between Capsules
    Defending Adversarial Attacks by Correcting logits
    Visualizing Data using t-SNE
  • 原文地址:https://www.cnblogs.com/happyliu/p/9463887.html
Copyright © 2011-2022 走看看