strncpy复制超过指定的大小

时间:2014-12-06 11:00:04

标签: c++ strncpy

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

#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 =“((状态:0.000000 Std30c5 =州:T)或((状态:0.000000 Std30c6 =州:T)或((状态:0.000000 Std30c7 =州:T)或((状态:0.000000 Std30c8 =州:T)或((状态:0.000000 Std30c9 =州:T)或((状态:0.000000 Std30ca =州:T)或((状态:0.000000 Std30cb =州:T)或((状态:0.000000 Std30cc =州:T )OR((状态:0.000000 Std30cd =状态:T)或((状态:0.000000 Std30ce =状态:T)OR((状态:0.000000 Std30cf =状态:T)或(... 0.000000 =状态:T))) )))))))))的ÌÌÌÌJ¢Š£OO )“

为什么strncpy会复制其他字符?

(这导致了一个问题,因为错误的strnlen会导致解包逻辑变得混乱!)

我想这与“strncpy bug 512 bytes”有关...请帮我理解这个bug。

5 个答案:

答案 0 :(得分:1)

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

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

见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正确检查目标缓冲区的边界,   nul-terminate在所有情况下都返回源字符串的长度,   允许检测截断。此API已被大多数人采用   现代操作系统和许多独立软件包,   包括OpenBSD(起源于哪里),Sun Solaris,FreeBSD,NetBSD,   Linux内核,rsync和GNOME项目。值得注意的例外   是GNU标准C库,glibc [12],其维护者   坚决拒绝包含这些改进的API,标记它们   尽管事先有证据证明他们是“可怜的低效BSD废话”[4]   大多数情况比它们取代的API更快[13]。结果是,   超过100个OpenBSD端口树中的软件包   保持自己的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

答案 1 :(得分:0)

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

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

否则代码将在您的情况下不安全。

考虑到将这两个标题放在一起没有任何意义

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

删除第一个标题,并仅使用C ++中的第二个标题。

#include <cstring>

答案 2 :(得分:0)

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

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

答案 3 :(得分:-1)

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

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

请注意,{+ 1}}在C ++中已弃用;您可以改用char *str1 = "...

答案 4 :(得分:-1)

在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;
}

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