3

我在 C 程序中使用了一个特定的数据结构,我用它来将类型信息附加到值。一个简单的示例如下所示:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct teststruct
{
    char type;
    union {
        char s;
        int i;
        float f;
    } value;
};

int main()
{
    struct teststruct *t = malloc(sizeof(struct teststruct)+10);
    t->type = 'i';
    t->value.i = 42;
    free(t);

    t = malloc(sizeof(struct teststruct)+10);
    t->type = 's';
    strcpy(&t->value.s, "test");

    free(t);

    printf("Finished without errors.\n");
}

如您所见,我的意图是使用type字段来识别值的类型,并使用value字段来包含可能值的联合。当数据是字符串时,想法是分配更多的内存sizeof(struct teststruct),然后使用&t->value.s.

虽然这可行,但对于优化器来说显然是有问题的。使用gcc4.7.2 版,我在非优化条件下得到以下结果:

$ gcc -O0 -o test test.c
$ ./test 
Finished without errors.

没问题。但是,在优化器下,它给了我一个警告:

$ gcc -O2 -o test test.c
In file included from /usr/include/string.h:642:0,
                 from test.c:4:
In function ‘strcpy’,
    inlined from ‘main’ at test.c:25:15:
/usr/include/i386-linux-gnu/bits/string3.h:105:3: warning: call to __builtin___memcpy_chk will always overflow destination buffer [enabled by default]

确实,

$ ./test 
*** buffer overflow detected ***: ./test terminated

但是,如果我替换strcpymemcpy,这可以正常工作,如果我替换strcpyfor-loop 也可以很好地工作。但是,strncpy崩溃就像strcpy. 我绝对不会在malloc'ed 内存之外被覆盖,所以我不知道为什么会崩溃。

我意识到复制到数据结构的奇怪偏移量并不常见,所以问题是,我是否违反了 的某些语义契约strcpy,或者这是编译器中的错误?

谢谢。

4

2 回答 2

2

您的代码在此处无效:

strcpy(&t->value.s, "test");
// value.s is a single char, not a string that you can store
// an arbitrary number of characters into.
//

你要么需要给s一些空间

struct teststruct
{
    char type;
    union {
        char s[10]; // length depends on your specific needs
        int i;
        float f;
    } value;
};

或制作 sa 指针并根据需要动态分配

struct teststruct
{
    char type;
    union {
        char *s;
        int i;
        float f;
    } value;
};
// instead of strcpy(&t->value.s, "test"); use t->value.s = strdup("test")
// don't forget to free the space when you are done.
于 2012-11-21T00:55:53.567 回答
0

我自己不是 gcc 专家,但在阅读 [1] 之后,我相信 gcc -O2 正在使用 memcpy 实现 strcpy() 调用,并使用 __builtin___memcpy_chk 检查溢出。

根据 [1] 中的示例, __builtin___memcpy_chk 的原因显然是为了检查目标对象的长度,并允许在运行时捕获缓冲区溢出。

而且根据 [1],如果 gcc 在编译时知道 __builtin___memcpy_chk本身会溢出,您将收到编译时警告。

如果您仍然希望保留模糊的 char 数组分配,我会说您至少应该使用安全的 strcpy:

strncpy(&t->value.s, "test", 9);

请注意,我无法在我的机器上触发此行为,因此我无法反汇编代码来查看实际情况。

[1] https://gcc.gnu.org/onlinedocs/gcc/Object-Size-Checking.html

于 2016-03-09T19:47:17.933 回答