【问题标题】:strncpy copying more than the specified sizestrncpy 复制超过指定大小
【发布时间】:2015-02-04 12:14:24
【问题描述】:

我有以下模仿应用程序代码的示例代码。

#include <iostream>
#include <string.h>
#include <cstring>
#include <atlstr.h>
using namespace std;

    void test(char *s, int size)
    {
        //s = "";
        int lens = strlen(s);
        char *str1 = "(( State:0.000000 Std30c5  =  State:T ) OR (( State:0.000000 Std30c6  =  State:T )  OR (( State:0.000000 Std30c7  =  State:T ) OR (( State:0.000000 Std30c8  =  State:T ) OR (( State:0.000000 Std30c9  =  State:T ) OR (( State:0.000000 Std30ca  =  State:T ) OR (( State:0.000000 Std30cb  =  State:T ) OR (( State:0.000000 Std30cc  =  State:T ) OR (( State:0.000000 Std30cd  =  State:T ) OR (( State:0.000000 Std30ce  =  State:T ) OR (( State:0.000000 Std30cf  =  State:T ) OR ( ...0.000000   =  State:T ))))))))))))";
        int len1 = strlen(str1);
        strncpy(s, str1, 512);
        int len = strlen(s);

    }

    int main()
    {

        char strDisplay[512] = "";
        test(strDisplay, 512);


        cout << strDisplay << endl;
        system("pause");
        return 0;
    }

结果是: lenofstrtest = 523; lenofstr1 = 512;

strtest = "((State:0.000000 Std30c5 = State:T) OR ((State:0.000000 Std30c6 = State:T) OR ((State:0.000000 Std30c7 = State:T) OR ((State:0.000000 Std30c8 = State :T ) OR (( State:0.000000 Std30c9 = State:T ) OR (( State:0.000000 Std30ca = State:T ) OR (( State:0.000000 Std30cb = State:T ) OR (( State:0.000000 Std30cc = State:T ) OR (( State:0.000000 Std30cd = State:T ) OR (( State:0.000000 Std30ce = State:T ) OR (( State:0.000000 Std30cf = State:T ) OR ( ...0.000000 = State:T )) )))))))))ÌÌÌÌJ¢Š£øø)"

为什么 strncpy 会复制额外的字符?

(这会导致问题,因为不正确的 strnlen 会导致解包逻辑失控!)

我猜这与“strncpy bug 512 bytes”有关...请帮助我理解这个错误。

【问题讨论】:

  • int len = strlen(strDisplay); cout

标签: c++ strncpy


【解决方案1】:

strncpy 不会在截断的字符串中添加终止字符“\0”,这会导致您遇到的问题。当字符串未正确终止时,它看起来会更长,但您实际看到的是放置在内存中缓冲区之后的数据。这可能会导致严重的问题。

您应该使用strlcpy 而不是strncpy,它会正确终止字符串并返回源字符串的长度,您可以将其与缓冲区的长度进行比较,以了解字符串是否被截断。 strncpy 返回指向缓冲区指针的指针(这不是很有用,因为您已经知道它 - 您将它作为第一个参数传递)并且不会告诉您是否发生任何截断。

见 man strlcpy:

strlcpy() 和 strlcat() 函数复制和连接字符串 具有与 snprintf(3) 相同的输入参数和输出结果。他们 旨在更安全、更一致且不易出错 替换容易误用的函数 strncpy(3) 和 strncat(3)。 strlcpy() 和 strlcat() 取完整大小 目标缓冲区并在有空间时保证 NUL 终止。 请注意,NUL 的空间应包含在 dstsize 中。

和维基百科上的C string handling - Replacements

最流行的[a] 替换是 strlcat 和 strlcpy 函数, 它于 1998 年 12 月出现在 OpenBSD 2.4 中。 [84]这些功能 总是将一个 NUL 写入目标缓冲区,截断结果 如有必要,并返回所需的缓冲区大小, 它允许检测截断并提供一个大小 创建一个不会截断的新缓冲区。

不幸的是,它不包含在 glibc 中 - 请参阅 Secure Portability 论文 达米安·米勒 (PDF):

strlcpy 和 strlcat API 正确检查目标缓冲区的边界, 在所有情况下都终止并返回源字符串的长度, 允许检测截断。该 API 已被大多数人采用 现代操作系统和许多独立软件包, 包括 OpenBSD(它的起源地)、Sun Solaris、FreeBSD、NetBSD、 Linux 内核、rsync 和 GNOME 项目。值得注意的例外 是 GNU 标准 C 库 glibc [12],它的维护者 坚决拒绝包含这些改进的 API,将它们标记为 “非常低效的 BSD 废话”[4],尽管先前的证据表明它们 大多数情况下比它们替换的 API 更快 [13]。因此, OpenBSD 端口树中存在超过 100 个软件包 维护他们自己的 strlcpy 和/或 strlcat 替代品或等价物 API - 不是理想的情况。

它在 libbsd 库中适用于 Linux:

在 Debian 和 Ubuntu 以及其他发行版中有软件包:

即使您不想依赖 glibc 以外的任何东西,也很容易将其添加到您的项目中,因为整个源代码都是在许可许可下可用的简短版本:

/*
 * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <sys/types.h>
#include <string.h>

/*
 * Copy src to string dst of size siz.  At most siz-1 characters
 * will be copied.  Always NUL terminates (unless siz == 0).
 * Returns strlen(src); if retval >= siz, truncation occurred.
 */
size_t
strlcpy(char *dst, const char *src, size_t siz)
{
    char *d = dst;
    const char *s = src;
    size_t n = siz;

    /* Copy as many bytes as will fit */
    if (n != 0) {
        while (--n != 0) {
            if ((*d++ = *s++) == '\0')
                break;
        }
    }

    /* Not enough room in dst, add NUL and traverse rest of src */
    if (n == 0) {
        if (siz != 0)
            *d = '\0';      /* NUL-terminate dst */
        while (*s++)
            ;
    }

    return(s - src - 1);    /* count does not include NUL */
}

来源:http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/lib/libc/string/strlcpy.c?rev=1.11

【讨论】:

    【解决方案2】:

    当您使用函数strncpy 时,您始终必须将其附加到终止零。例如

    strncpy(s, str1, n );
    s[n-1] = '\0';
    

    否则代码会像你的情况一样不安全。

    考虑到这两个标题一起使用没有任何意义

    #include <string.h>
    #include <cstring>
    

    在 C++ 中删除第一个标头并仅使用第二个标头。

    #include <cstring>
    

    【讨论】:

      【解决方案3】:

      strDisplay 应至少分配 513 个单位,因为在 strncpy 中没有隐式添加空终止字符。

      char strDisplay[513] = "";
      strDisplay[512] = '\0'; //recommended
      

      【讨论】:

      • ...你需要实际放入空终止符。
      • 或者,他可以简单地编写程序来完全消除这些错误。
      【解决方案4】:

      在 C++ 中使用 std::string。它会自动为您解决所有这些问题。

      #include <iostream>
      #include <string>
      std::string test()
      {
          return "(( State:0.000000 Std30c5  =  State:T ) OR (( State:0.000000 Std30c6  =  State:T )  OR (( State:0.000000 Std30c7  =  State:T ) OR (( State:0.000000 Std30c8  =  State:T ) OR (( State:0.000000 Std30c9  =  State:T ) OR (( State:0.000000 Std30ca  =  State:T ) OR (( State:0.000000 Std30cb  =  State:T ) OR (( State:0.000000 Std30cc  =  State:T ) OR (( State:0.000000 Std30cd  =  State:T ) OR (( State:0.000000 Std30ce  =  State:T ) OR (( State:0.000000 Std30cf  =  State:T ) OR ( ...0.000000   =  State:T ))))))))))))";
      }
      
      int main()
      {        
          std::cout << test() << std::endl;
          return 0;
      }
      

      请注意,不需要内存管理、临时魔术大小缓冲区或空终止符。

      【讨论】:

        【解决方案5】:

        strncpy 是一个糟糕的函数,因为它不会生成字符串。如果你想使用 C 风格的字符串处理,那么snprintf 更容易安全使用:

        snprintf(s, size, "%s", str1);
        

        请注意,char *str1 = "... 在 C++ 中已弃用;你可以改用char const *str1 = "...

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2018-02-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-09-28
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多