一个wifi中文名导致程序异常的问题排查

        最近接到客户反馈,通过web登录设备后,切换到"网络设置"时,网页弹出“设备连接服务异常,请检查设备后重新尝试”,在客户那边是必现。然后在公司技术支持那边也是必现,但我拿技术支持的设备来进行复现时,却没复现过。结合复现时设备里的日志信息,是在设备获取周围wifi信息时,没有返回,导致超时,当时想到的就是先规避掉,即把wifi关掉,但wifi使能就在“网络设置”页面,无法从页面上disable掉,悲催了。最后通过后台把wifi去使能后,在技术支持那边操作一切正常。一开始想出一版程序把wifi默认关掉,先解决客户的问题,不过分析代码后,发现了其中的问题,记录如下。

1,web请求wifi信息时,设备做了什么?

        设备里定时搜索wifi信息并保存下来,当web来请求时,返回结果 。但在返回结果前进行了转码,通过查看web收到的信息,返回的是Unicode编码。转码主要代码如下 :

    while(inSSID[i] != '\0')
    {
        if(i >= inMaxLen || j >= outMaxLen)
        {
            break;
        }

        if(inSSID[i] == '\\' && inSSID[i + 1] == 'x')
        {
            hexData1 = Type_CharToHex(inSSID[i + 2]);
            hexData2 = Type_CharToHex(inSSID[i + 3]);

            if(hexData1 != -1 && hexData2 != -1)
            {
//                DEBUG_INFO("hexData1 = 0x%02x, hexData2 = 0x%02x", hexData1, hexData2);
                outSSID[j++] = hexData1*0x10 + hexData2;
                i += 4;
//                continue;
            }
        }
        else if(inSSID[i] == '\r' || inSSID[i] == '\n')//clear CRLF
        {
            outSSID[j++] = '\0';
            i++;
        }
        else
        {
            outSSID[j++] = inSSID[i++];
        }
    }

代码没有注释(看不懂的可以google一下utf8转unicode编码),我也是看不懂,我是通过结果来看的,最后返回给web的是unicode编码,所以这里应该是uft8转unicode,这里暂且不用管是怎么转码的,重点是这个函数里有问题。

2,utf8编码的了解

        要发现上面这个函数的问题,首先得了解utf8编码,有兴趣的可以自行google。我也只是搜索了一下去了解了解,其编码格式有好几种,如:

而在设备里的,是\xXX的编码格式,看代码去判断了if(inSSID[i] == '\\' && inSSID[i + 1] == 'x'),所以设备系统里返回给上层应用的就是\xXX形式的utf8编码格式。然而一个中文的utf8编码格式应该是这样:\xXX\xXX\xXX,即3个\x,若不符合即不合法的uft8编码格式,通过在线转码可以验证,如:

了解这个编码格式后,添加打印日志,如有日志:

因为程序里保存SSID的buf只给了41个字节,所以"10\xe9\x87\x8f\xe8\xba\xab\xe5\xae\xa2\x"刚好40个字符,通过在线转码一看不是正确的编码格式:

正确的应该是:"10\xe9\x87\x8f\xe8\xba\xab\xe5\xae\xa2" ,如:

到这里只能说是找到转码有问题,但web怎么会超时而退出,然后就再不登录不上了呢?

3,转码函数的问题--------栈越界

        分析代码后发现有一段如下:

        if(inSSID[i] == '\\' && inSSID[i + 1] == 'x')
        {
            hexData1 = Type_CharToHex(inSSID[i + 2]);
            hexData2 = Type_CharToHex(inSSID[i + 3]);

            if(hexData1 != -1 && hexData2 != -1)
            {
//                DEBUG_INFO("hexData1 = 0x%02x, hexData2 = 0x%02x", hexData1, hexData2);
                outSSID[j++] = hexData1*0x10 + hexData2;
                i += 4;
//                continue;
            }
        }

注意这一行:i += 4,i向后跳了4个字节,然而如果像上面的转码前是"10\xe9\x87\x8f\xe8\xba\xab\xe5\xae\xa2\x",最后的两个字符就是\x,满足了第一个if的判断,最后也进入了第二个if,然后i往后跳4个字节,跳到哪里去了呢?谁也不知道,最好的情况是跳过去满足while(inSSID[i] != '\0') 退出while循环,否则这里就是一个死循环了,web不超时才怪。于是添加了一条打印日志,如:

最后果然出现死循环:

问题的根因是找到,最后是怎么修改的问题了。这里贴一下我的修改方法,如有更好的方法欢迎@我啊!修改后测试正常,还有其他地方的修改,如:加大了保存SSID的buffer。下面转码时会进行截断,只保留正确的utf8编码格式的字符。

int Wifi_ConvertSSID(const char* inSSID, char* outSSID, int inMaxLen, int outMaxLen)
{
    int i = 0;
    int j = 0;
    int hexData1 = -1;
    int hexData2 = -1;

    if(inSSID == NULL || outSSID == NULL)
    {
        return -1;
    }

    /**
     * \xe9\xbe\x99\xe5\xa5\xb3  中文的utf-8编码必须符合3个\x代表一个中文字符 
     * 
     */
    char tmpSSID[WIFI_NAME_LEN + 1] = {0};
    int idx = 0;
    int charLen = inMaxLen > WIFI_NAME_LEN ? WIFI_NAME_LEN : inMaxLen;

    for(idx = 0; idx < charLen;)
    {
        if(inSSID[idx] == '\\' && inSSID[idx + 1] == 'x')
        {
            if(inSSID[idx + 8] == '\\' && inSSID[idx + 9] == 'x')//这里是探测是否有3个\x
            {
                if(idx + 12 >= charLen)
                {
                    break;
                }
                strncpy(tmpSSID + idx, inSSID + idx, 12); //一次拷贝12个字符,即3个\xXX
                idx += 12;                
            }
            else
            {
                break;
            }            
        }
        else //不是中文的直接拷贝
        {
            tmpSSID[idx] = inSSID[idx];    
            idx++;
        }
    }
    tmpSSID[idx] = '\0';
    printDebug("real ssID = %s, len = %d\n", tmpSSID, idx);

    while(tmpSSID[i] != '\0')
    {
        if(i >= charLen || j >= outMaxLen)
        {
            break;
        }

        if(tmpSSID[i] == '\\' && tmpSSID[i + 1] == 'x')
        {
            hexData1 = Type_CharToHex(tmpSSID[i + 2]);
            hexData2 = Type_CharToHex(tmpSSID[i + 3]);

            if(hexData1 != -1 && hexData2 != -1)
            {
//                DEBUG_INFO("hexData1 = 0x%02x, hexData2 = 0x%02x", hexData1, hexData2);
                outSSID[j++] = hexData1*0x10 + hexData2;
                i += 4;
                if(i > charLen) //最后这里加了一层保险
                {
                    break;
                }
            }
        }
        else if(tmpSSID[i] == '\r' || tmpSSID[i] == '\n')//clear CRLF
        {
            outSSID[j++] = '\0';
            i++;
        }
        else
        {
            outSSID[j++] = tmpSSID[i++];
        }
    }

    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值