【问题标题】:How can I use strncat without buffer overflow concerns?如何在没有缓冲区溢出问题的情况下使用 strncat?
【发布时间】:2011-10-17 18:06:33
【问题描述】:

我有一个缓冲区,我正在做很多 strncat。我想确保我永远不会溢出缓冲区大小。

char buff[64];

strcpy(buff, "String 1");

strncat(buff, "String 2", sizeof(buff));

strncat(buff, "String 3", sizeof(buff));

我想说的是 buff - xxx,而不是 sizeof(buff)。我想确保我永远不会覆盖缓冲区

【问题讨论】:

    标签: c string buffer-overflow


    【解决方案1】:

    考虑现有字符串的大小和空终止符

    #define BUFFER_SIZE 64
    char buff[BUFFER_SIZE];
    
    //Use strncpy
    strncpy(buff, "String 1", BUFFER_SIZE - 1);
    buff[BUFFER_SIZE - 1] = '\0';
    
    strncat(buff, "String 2", BUFFER_SIZE - strlen(buff) - 1);
    
    strncat(buff, "String 3", BUFFER_SIZE - strlen(buff) - 1);
    

    【讨论】:

    • buff[BUFFER_SIZE - 1] = '\0';不需要,因为 strncat 总是零终止
    • @fliX strncpy 没有。如果在复制整个字符串 src 之前达到 count ,则生成的字符数组不是以 null 结尾的。但是对于这个特定的例子,BUFFER_SIZE=64 和 "String 1" 更短,你是对的,它是不需要的,因为 strncpy 也会复制空值:en.cppreference.com/w/cpp/string/byte/strncpy
    【解决方案2】:

    为什么不使用snprintf?与strncat 不同,它需要缓冲区的大小,但更重要的是,没有隐藏的 O(n)。

    Strcat 需要在它连接的每个字符串上找到空终止符,并且每次都遍历整个缓冲区以找到结尾。每次字符串变长时,strcat 都会变慢。另一方面,Sprintf 可以跟踪结束。你会发现

    snprintf(buf, sizeof buf, "%s%s%s", "String1", "String2", "String3");
    

    通常是一种更快、更易读的解决方案。

    【讨论】:

    • 听起来不错。但是,我有几个缓冲区/字符串要写入一个全局缓冲区。这也意味着要等到我拥有所有可用的字符串/缓冲区才能使用 snprintf,否则缓冲区将被覆盖。
    • 如果等不及,snprintf会返回写入的字符数,这样就可以存储缓冲区偏移量,允许offset+=snprintf(buf+offset, (sizeof buf)-offset, "%s", "String2")
    • 这个答案很有说服力。 strcat 隐式搜索 NULL 终止符。
    【解决方案3】:

    您在原始代码中使用strncat 函数的方式实际上适用于另一个函数:strlcat(注意l 而不是n)。 strlcat 函数不是标准的,但它是strncat 的流行实现提供的替代品。 strlcat 期望整个目标缓冲区的总大小作为其最后一个参数。

    同时,strncat 期望目标缓冲区的 剩余 未使用部分的大小作为其第三个参数。因此,您的原始代码不正确。

    我建议不要对strncpy 进行可怕的滥用并使用strlen 调用进行明确的重新扫描(Joe 的回答中存在这两个问题),您可以使用实现提供的strlcat 或自己实现一个(如果您的实现没有提供strlcat)。

    http://en.wikipedia.org/wiki/Strlcpy

    【讨论】:

      【解决方案4】:

      这是最好的方法。 sizeof() 如果您不在本地分配它,则只会为您提供指向数据的指针的大小(在这种情况下您确实在本地分配,但最好这样做,如果代码被重新分解,它将起作用)。

      #define MAXBUFFSIZE 64
      
      char buff[MAXBUFFSIZE];
      
      buff[0] = 0;  // or some string
      
      strncat(buff, "String x",MAXBUFFSIZE - strlen(buff) - 1);
      

      【讨论】:

      • 在这种情况下,sizeof 会给他整个缓冲区的大小,因为它是一个数组,而不是动态分配的内存块。
      • @Hogan:不正确。当sizeof 应用于数组对象时,它计算为数组对象的总大小。 OP 的代码中没有任何类型的“指向数据的指针”。
      • 我喜欢我被否决的投票并且接受的答案从我的被盗,因为它至少在一分钟后发布。
      • @yan -- 本地缓冲区的真实点 -- 我修改以指出我的真实点。如果它被重新分解以分配,将会有丑陋的错误。
      • @andreyt - 见上面的评论 - 我似乎无法通知 2 个人。
      【解决方案5】:

      霍根已经充分回答了这个问题;但是,如果您担心strcat(...) 中的缓冲区溢出,您同样应该担心所有其他字符串函数中的缓冲区溢出。

      使用strnlen(...)strncpy(...) 真正确保您留在缓冲区内。如果您没有strnlen(...) 函数,请编写它。

      【讨论】:

      • strnlenstrncpy 都是处理固定宽度字符串的函数。它们与以空字符结尾的字符串无关。同时,OP特别对以空结尾的字符串感兴趣,如下问题所示。确实,人们经常可以看到strncpy误用 和以null 结尾的字符串,好吧。但我完全不清楚strnlen 在这里做什么。
      • 一般来说,将空终止字符串处理为可能被固定长度的字符串绑定是防止缓冲区溢出的原因。当依赖空终止符(通过使用非 n 函数)时,您依赖于空终止符距离字符串的开头只有这么远,这可能会溢出您正在复制到的缓冲区(如果假设关于空终止字符不成立)。
      【解决方案6】:

      在这种情况下,我会使用memccpy 而不是strncat - 它更安全、更快。 (它也比 Dave 使用 snprintf mentioned 的方法更快):

      /**
       * Returns the number of bytes copied (not including terminating '\0').
       * Always terminates @buf with '\0'.
       */ 
      int add_strings(char *buf, int len)
      {
          char *p = buf;
      
          if (len <= 0)
              return 0;
      
          p[len - 1] = '\0'; /* always terminate */
      
          p = memccpy(buf, "String 1", '\0', len - 1);
          if (p == NULL)
              return len - 1;
      
          p = memccpy(p - 1, "String 2", '\0', len - 1 - (p - buf));
          if (p == NULL)
              return len - 1;
      
          p = memccpy(p - 1, "String 3", '\0', len - 1 - (p - buf));
      
          return (p == NULL ? len : p - buf) - 1;
      }
      

      【讨论】:

        猜你喜欢
        • 2021-06-15
        • 2022-01-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-12-06
        • 1970-01-01
        • 2021-09-19
        • 2014-06-14
        相关资源
        最近更新 更多