【问题标题】:What happens if strncpy copies random data into a buffer?如果 strncpy 将随机数据复制到缓冲区中会发生什么?
【发布时间】:2019-01-25 01:40:25
【问题描述】:

假设您有一个char 缓冲区,您想将std::string 复制到其中。即使缓冲区有足够的大小,将额外数据复制到字符串范围之外的缓冲区是否会产生后果?

例子

std::string my_string = "hello";
char my_buffer[128];
memset(my_buffer, 0, 128);

strncpy(my_buffer, my_string.c_str(), 128);

所以“hello”会被复制到my_buffer,但my_string 之后的其他 123 个字节数据也会如此。这有什么后果吗?缓冲区保存其他数据是否有害?

【问题讨论】:

  • strncpy 将字符串作为参数?
  • 那不会编译...
  • 即使缓冲区有足够的大小,将太多数据复制到缓冲区中是否会产生后果? -- 什么?是不是太多了?
  • 你应该阅读the documentation。密切注意小字,然后假装strncpy()不存在。
  • @Shawn,从您的链接中,它指出,“如果从 src 复制终止的空字符后,未达到计数,则将额外的空字符写入 dest,直到计数总数字符已被写入。” --> 这意味着my_buffer 将被写入my_string,而my_buffer 之后的所有内容都将用NULL 填充。我理解对了吗?

标签: c++


【解决方案1】:

my_string 之后的其他 123 个字节的数据也是如此

这个假设是不正确的:strncpy 关注源字符串的空终止,从不读取过去的空终止符。剩余数据将设置为'\0' 个字符:

destination 用零填充,直到总共写入了 num 个字符。 [reference]

这与 memcpy 形成对比,后者要求源和目标都具有足够的大小以避免未定义的行为。

【讨论】:

    【解决方案2】:

    好的,假设你想要的是:

    strncpy(my_buffer, my_string.c_str(), 128);
    

    根据定义,Thsi 始终是一个以 0 结尾的字符串,因此考虑:

    将src指向的字符数组的最多count个字符(包括终止的空字符,但不包括空字符后面的任何字符)复制到dest指向的字符数组。

    "hello" 之后不会从原始字符串中复制任何内容,其余的将是 0s:

    如果从 src 复制终止的空字符后,未达到 count,则将额外的空字符写入 dest,直到写入总的 count 个字符。

    【讨论】:

      【解决方案3】:

      根据此处1 的 strncpy() 描述,对于以空结尾的字符串,复制完成到您提供的长度,这样当字符串的结尾出现在前面时,就像在这种情况下,复制完成到它并且不再复制,所以其余的“123字节”没有被复制,复制循环终止

      【讨论】:

        【解决方案4】:

        这个问题的其他答案已经解决了strncpy() 会发生什么(即它会正确复制您的字符串,因为它在 0 终止符字节处停止),所以也许更能说明问题的意图是,如果你有这个呢?

        memcpy(my_buffer, my_string.c_str(), 128);
        

        在这种情况下,函数 (memcpy()) 不知道以 0 结尾的字符串语义,并且总是盲目地将 128 个字节(从 my_string.c_str() 返回的地址开始)复制到地址 @987654326 @。这些字节的前 6 个(左右)将来自 my_string 的内部缓冲区,其余字节将来自之后内存中的任何内容。

        所以问题是,接下来会发生什么?好吧,这个memcpy() 调用从您不知道其目的的“神秘记忆”中读取,因此您这样做是在调用未定义的行为,因此原则上任何事情都可能发生。在实践中,可能的结果是您的缓冲区将包含读取的任何字节的副本(尽管您可能不会注意到它们,因为您将使用不会超过 0/终止符字节的字符串函数无论如何数组)。

        不过,memcpy() 读取的“额外”内存字节可能是标记为禁区的内存页面的一部分,在这种情况下,尝试从该页面读取可能会导致分段错误。

        最后,还有未定义行为的真正错误,那就是你的 C++ 编译器的优化器被允许对你的代码逻辑进行各种疯狂的修改,以使你的程序更高效——并且(假设优化器不是错误的)所有这些优化仍然会导致程序按预期运行——只要程序遵循规则并且不调用未定义的行为。也就是说,如果您的程序以任何方式调用未定义的行为,则可能会以very difficult to predict or understand 的方式应用优化,从而导致程序中出现奇怪/意外的行为。所以一般规则是,避免像瘟疫这样的未定义行为,因为即使你认为它“应该是无害的”,它也很有可能最终会做你意想不到的事情去做,然后你会进入一个漫长而痛苦的调试会话,因为你试图弄清楚发生了什么。

        【讨论】:

        • 你回答了没有被问到的问题。
        • 问的问题是(根据标题):“如果 strncpy() 将随机数据复制到缓冲区会发生什么?”,实际答案是(“ strncpy() 不会那样做,至少在您发布的示例代码中不会“)是很好的信息,但它没有回答“如果会发生什么”部分。
        【解决方案5】:

        假设您的意思是肯定复制该数据,因为您当前的代码将以空终止符结尾。

        基本上,不。那里的任何数据都将仅用作字符串数据,因此除非您随后尝试对该备用数据做一些奇怪的事情(尝试将函数指针指向它并执行它等),否则它基本上是安全的。

        问题在于最初复制原始字符串末尾的随机数据,这可能会溢出到您无权访问的受保护数据中并引发段错误(无法访问的内存异常)

        【讨论】:

          【解决方案6】:

          函数strncpy 将复制所有内容,直到NUL 字符或指定size。就是这样。

          除了其他答案,请记住strncpy 不会在src 字符串长度(不包括NUL)等于缓冲区的size 给定时终止dst 缓冲区。您应该始终执行my_buffer[127]=0 之类的操作,以防止稍后处理字符串时缓冲区溢出。在libbsd 中有strlcpy 定义总是NUL 终止缓冲区。

          看看这个sn-p:

          #include <string.h>
          
          int main(int argc, char ** argv)
          {
              char buf[8];
              char *str = "deadbeef";
          
              strlcpy(buf, str, sizeof(buf));
              printf("%s\n", buf);
          
              strncpy(buf, str, sizeof(buf));
              printf("%s\n", buf);
              return 0;
          }
          

          要查看问题,请使用标志-fsanitize=address 编译它。第二个缓冲区不是 NUL 终止的!

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-12-24
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多