博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Select 使用不当引发的core,你应该知道的
阅读量:4465 次
发布时间:2019-06-08

本文共 4434 字,大约阅读时间需要 14 分钟。

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

第一步:常规根据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\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;        }}

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

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

进步查看日志发现有大量的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,看系统配置)会导致这个潜在问题;

转载于:https://www.cnblogs.com/happyliu/p/9463887.html

你可能感兴趣的文章
ASP.NET Form身份认证
查看>>
基础设施与应用监控之指标、监控和报警简介
查看>>
DNS解析
查看>>
angular.run和angular.config的区别
查看>>
Java网络03 Servlet沙拉
查看>>
[转载]人生感悟:8个笑话 8味人生
查看>>
Siebel课程笔记
查看>>
Docker centos 安装syslog
查看>>
我的Cocos2d-x学习笔记(八)利用CCSpriteBatchNode进行优化
查看>>
转:Loadrunner——Simulate a new user on each iteration设置
查看>>
Openstack中用秘钥对(keypair)生成和访问虚机的方法
查看>>
MySQL系列(三)--MySQL存储引擎
查看>>
MySQL5使用Innodb引擎时如何设置数据文件按表存储
查看>>
UNIX网络编程 - UNIX errno值
查看>>
有多少种JVM
查看>>
System.gc()
查看>>
【BZOJ3926】【ZJOI2015】—诸神眷顾的幻想乡(广义后缀自动机)
查看>>
测者的测试技术手册:自动的自动化框架EvoSuite集成Cobertura得到可视化的代码覆盖报告...
查看>>
"Hello World!" for the NetBeans IDE
查看>>
AlertDialog弹出退出对话框和图片对话框
查看>>