一个生产环境C++工程段错误的排查

Posted 李迟

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一个生产环境C++工程段错误的排查相关的知识,希望对你有一定的参考价值。

最近遇到一个C++工程段错误的问题,因日志记录有回溯栈的信息,排查比较容易,解决也不难,但过程还是值得记录的。

起因

最近,运维同事发消息反馈,某一机子的一个程序每天凌晨2点准时段错误重启。随后下载并查询日志,发现是一个不怎么使用的服务出问题,当时移交时,这个服务已明确不使用了,但我不敢删除,刚好这个服务开了一个端口,恰好接收不合法数据时又不判断指针结束,从而出现了段错误。自去年部门调整接手代码至今,已发生多次疑难杂症,比如内存泄漏,比如sqlite3数据库文件被无故写入日志数据,比如这次段错误。本命年估计不太顺利。

排查及解决

代码定位

出现问题的代码示例如下:

int split_cmd(char *src, const char *separeter)

    char *ptrsrc = src + 1;
    int idx = 0;
    while (1)
    
        if (*ptrsrc != ',')
        
            ptrsrc++;
            continue;
        
        else
        
            *ptrsrc = 0;
            //tmpp[idx]=ptrsrc;
            if (2 == ++idx)
                break;
        
        if (*ptrsrc == 0)
        
            printf("error cmd\\n");
            return -1;
        
    

问题如下:

  • 入参src为指针,但没有判断是否合法,而且代码中将指针直接+1,这是非法危险的。
  • separeter本意是指定分割的字符,但并没有使用之。
  • 结束条件if (*ptrsrc != ',')过简单,段错误即发现在此处。
  • 循环使用while(1),而不是通过长度遍历。

重现问题

工程是已成型的服务,在网上找了几个 socket 调试助手,但无法连接成功,后使用 linux 的 nc 命令,发送命令如下:

printf "bad cmd" | nc 127.0.0.1 1314 

重现问题。出错代码如下:

if (*ptrsrc != ',')
        
            ptrsrc++;
            continue;
        

解决方法

根据上述列出的问题就能快速找到解决方法,这段代码是解析通过 socket 接收的数据,数据是有长度的,但代码并没有沿用,因为不敢对代码大改,因此只改了判断条件。如下:

int RemoteMonitor::SplitCMDbuf(char *src, const char *separeter)

    if (src == NULL) return -1;
 ...
    while (1)
    
        if (*ptrsrc != '\\0' && *ptrsrc != ',')
        
            ptrsrc++;
            continue;
        
        ...
    

另外打印了远程机器IP和数据(十六进制格式),这样能知道是谁发了什么数据。示例代码如下:

client_len = sizeof(client_addr);
if ((client_sockfd = accept(serverfd, (struct sockaddr *)&client_addr, &client_len)) < 0)

    printf("accept error");
    close(serverfd);
    continue;

char * client_ip = inet_ntoa(client_addr.sin_addr);
// int client_port = ntohs(client_addr.sin_port);
printf( recv from client %s\\n", client_ip);

因开年第一天上班,因此会议比较多,但还是在晚上升级好版本运行。翌日早上观察日志,结果在2点接收到非法指令,但没有出现段错误,分析得知,是一台机器发现了 http 的 GET 请求,将信息反馈至运维同事,后未有消息。

小结

写代码这么久,对出错处理十分小心。函数体第一件事就是判断参数的合法,包括指针是否为空,数值是否在合理范围,等。

nc 命令在某些场合比较方便,比如本次调试,直接在同一机器上执行即可,不需要额外的工具。

对于 socket 的连接,个人觉得加上远程 IP 地址(或端口)会比较好,起码知道连接到哪台服务器,被哪个客户端连接。本例中就新加了客户端的 IP 的打印。

以上是关于一个生产环境C++工程段错误的排查的主要内容,如果未能解决你的问题,请参考以下文章

一个arm平台C++工程段md5错误的排查

一个arm平台C++工程段md5错误的排查

一个C++工程内存泄漏问题的排查及重现工程

一个生产环境C工程内存泄漏问题的排查及重现工程

一个C++工程CPU占用100%问题的排查

一个C++工程CPU占用100%问题的排查