在一个程序员的编程生涯中,内存越界恐怕是最令人头疼的问题了。引起内存越界的原因很多,这里我就以我的一个回归不通过的问题单为例,分析一下字符串操作如何会引起内存越界问题,对于这种问题该如何避免。
在问题单中,通过mibBrowser配置时区名字时,可能会造成内存越界,引起死机。错误代码如下:(红部分为错误代码)
case timeZoneName:    /* OCTET_STRING, read-write */
{
    switch ( request )
    {
        ……
        case CCD_VALIDATE:
            if ( ccd->valueLen < 1 || ccd->valueLen > 32 )
字符串长度如何定义                rc = ccdErrorInvalidLength();
            break;
        ……
        case CCD_COMMIT:
            strcpy( gSysClock.szTimeZoneName, octetGet(ccd) );
            break;
        }
}
uchar_t * octetGet( ccd_t * pIdb )
{
    return (uchar_t *)(pIdb->pValueBuf);
}
问题重现:
通过命令行配置,没有问题;通过mibBrowser配置,问题重现。
问题分析:   
通过命令配置时,由于pIdb->pValueBuf的指针是在命令行中的一个字符串数组,时区名存放在此数组中,且字符串的结尾有结束符,故在执行红代码时不会出错;
通过mibBrowser配置时,由于SNMP传到CCD的字符串没有结束符,故在执行红代码时出错,gSysClock.szTimeZoneName[33]只有33个字节的空间,若pIdb->pValueBuf指向的内容中前33个或更长的字节中都没有字符串结束符’\0’时,红代码就会拷贝越界,把其他数据覆盖掉。这样当系统操作到这些被覆盖的数据时,就会引起内存越界。
问题解决:
通过mibBrowser配置数据如字符串时,不能依赖于字符串自带的结束符,因为它不传入结束
符,只传送有效数据。这种情况下,只能根据CCD中传入的值长度(ccd->valueLen)来复制数据。另外,在CCD配置数据时,一定要先判断传入数据的长度是否合理,不合理就不能配置(如上蓝代码),否则也会造成内存越界。
结合此问题单,将红代码修改如下:
        case CCD_COMMIT:
            memcpy( gSysClock.szTimeZoneName, octetGet(ccd), ccd->valueLen );
            gSysClock.szTimeZoneName[ccd->valueLen] = '\0';
            break;
这样修改是很正确的。
对于这个问题,我第一次修改时,将红代码改为以下代码:
        case CCD_COMMIT:
            bzero(gSysClock.szTimeZoneName, timeZoneName_sz );
            memcpy( gSysClock.szTimeZoneName, octetGet(ccd), ccd->valueLen );
            break;
修改后没有出现问题,但降低了效率,同时还存在隐患。
bzero()降低了代码的效率,且清空时并没有将gSysClock.szTimeZoneName的整个内存空间都清空,因为timeZoneName_sz的值为32,gSysClock为全局变量,代码中并没有将其清空的代码,它的清空要依赖于系统初始化时将数据区的内存清空来保证,这样做不合理,若系统初始化不清空全局数据结构就同样会造成内存越界问题。
处理这个问题,让我学习到:
1、通过SNMP配置值为字符串的数据时,要注意CCD传入的数据没有结束符,不能使用strcpy()来进行拷贝。
2、字符串操作一定要注意结束符,在字符串拷贝时,要先判断字符串的长度是否合理,若
合理,拷贝数据,拷贝完成后要注意在字符串的后面要加上结束符。
将此问题进行总结,以备忘。也望没有注意到此问题的同事以后不要犯类似的错误。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。