zoukankan      html  css  js  c++  java
  • 消失的字符(关于New CRT)

    看看下面的代码有什么问题?

    int main( void )
    {
    	size_t MAX = 256;	
    	wchar_t * src = new wchar_t[MAX];
    	src[0] = L'H';
    	src[1] = L'i';
    	char *dest = new char[MAX];
    	size_t len = 0;
    
    	wcstombs_s(&len,dest,MAX,src,MAX);
    }
    

    如果我们使用strlen(dest),得到的结果会是0,但是如果我们访问dst[1],其结果是i,是我们期望的,而且如果src的有效字符更多一些,除了第一个字符不在外,其他的都是有的,为什么第一个字符消失了呢?

    这是我碰到的一个使用New CRT的一个bug,觉得New CRT还挺有意思的,所以找来研究一下:

    New CRT最显著的变化叫做Paramter Validation和Sized  Buffers。New CRT中_s的若干API并不是要替代旧的API,也就是说,strcpy还是strcpy,New CRT会引入一个strcpy_s。但是如果编译时使用了新的library,旧的方法就会被标注为 过时的,也就是会得到编译器的警告信息。下面借用Michael的例子(strncat,strncat_s)来说说NEW CRT的常见错误用法。

    char *strncat(
       char *strDest,
       const char *strSource,
       size_t count 
    );
    
    errno_t strcat_s(
       char *strDestination,
       size_t sizeInBytes,
       const char *strSource 
    );

    strncat最后一个参数是Number of characters to append,也就是strDest中剩余的空间或者是要append的数量,而不是strDest的 buffer size。这很容易造成Off-by-one或者Off-by-many的错误。看下面的例子:

    int main( void )
    {
        size_t MAX = 256;    
        wchar_t * src = new wchar_t[MAX];
        src[0] = L'H';
        src[1] = L'i';
        char *dest = new char[MAX];
        size_t len = 0;
    
        wcstombs_s(&len,dest,MAX,src,MAX);
    
        char szTarget[12];
        char *s = "Hello, World";
        
        strncpy(szTarget,s,sizeof(szTarget));
        strncat(szTarget,s,sizeof(szTarget));
    
    }

    看看在strncat执行之前的内存:

    before
    再看看在strncat执行之后的内存:

    after

    strncat发现从0040FAF0处为0(字符串结束的位置),故从此处开始做append。最后得到的结果如图。但是,对比之前的内存,可以发现原来len的空间被覆盖了,也就是发生了stack corrupt。

    一种解决的办法如下所示:

    char szTarget[12];
    char *s = "Hello, World";
    
    strncpy(szTarget,s,sizeof(szTarget));
    strncat(szTarget,s,strlen(szTarget) – strlen(s));

    这种方法的问题在于, If the length of the destination buffer is exactly the same length as the source buffer, many n-functions do not null-terminate the destination buffer, so the call to strlen(szTarget) could return a length greater than the length of the target because there is no trailing '\0' character. 上面的代码中strlen(szTarget)返回20。
    更好的方式是用如下的代码:

    char szTarget[12];
    char *s = "Hello, World";
    
    size_t cSource = strnlen_s(s,20);
    strncpy_s(szTarget,sizeof(szTarget),s,cSource);
    strncat_s(szTarget,sizeof(szTarget),s,cSource);

    如果发现空间不够,就会Assert,而不会造成memory corruption。
    下面是一段来源于某公司的代码,本意是想写出安全的代码。但是它的问题在于,如果输入的字符串是NULL,strncpy就会fail了。

    void noOverflow(char *str)
    {
      char buffer[10];
      strncpy(buffer,str,(sizeof(buffer)-1));
      buffer[(sizeof(buffer)-1)]=0;
      /* Avoiding buffer overflow with the above two lines */
    }

    strncat_s不会fail的原因是更新的runtime 执行更加严格的parameter check,下面就是strcat_s的参数检查部分的代码:

    /* validation section */
    _VALIDATE_RETURN_ERRCODE(front != NULL, EINVAL);
    _VALIDATE_RETURN_ERRCODE(sizeInTChars > 0, EINVAL);
    _VALIDATE_RETURN_ERRCODE(back != NULL || count == 0, EINVAL);

    需要注意的另外一点是*_s函数和strsafe函数的区别:*_s函数在检测到一个error时,会将结果字符串设置为NULL,而strsafe函数比如StringCchCopyStringCchCat在碰到任何错误的时候,其默认行为是尽量填充结果字符串,并最后使用NULL结束之。可以使用下面函数来模仿*_s函数:

    StringCchCatEx(dst,sizeof(dst)/sizeof(dst[0]),src,NULL,NULL,STRSAFE_NULL_ON_FAILURE)

    Reference: Saying Goodbye to an Old Friend, by Michael Howard, http://blog.yezhucn.com/dncode/secure03102004.htm

  • 相关阅读:
    python写泰勒展开式
    8.QR分解的python实现
    7.Bolzmann机解决旅行商问题
    6.BP神经网络的python实现
    5.梯度寻优
    4.推荐系统
    4.决策树的探赜索隐
    BZOJ 1251 序列终结者
    BZOJ 3223 文艺平衡树 [codevs3303翻转区间]
    BZOJ 3224 普通平衡树
  • 原文地址:https://www.cnblogs.com/whyandinside/p/2889516.html
Copyright © 2011-2022 走看看