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です。

これは機能しますが、オプティマイザーにとっては明らかに問題があります。バージョン4.7.2を使用するgccと、最適化されていない状態で次のようになります。

$ 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

ただし、に置き換えるstrcpyと、これは正常に機能し、-loopmemcpyに置き換えるとうまく機能します。ただし、と同じようにクラッシュします。私はメモリの外では絶対に上書きされないので、なぜこれがクラッシュするのかわかりません。strcpyforstrncpystrcpymalloc

データ構造の奇妙なオフセットにコピーすることはそれほど一般的ではないことに気付いたので、問題は、のセマンティックコントラクトに違反しているのか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] に従って、コンパイル時に __builtin___memcpy_chk自体がオーバーフローすることを gcc が認識している場合、コンパイル時に警告が表示されます。

それでもあいまいな 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 に答える