3

この状況は、名前マングリングなしでのみ発生する可能性があるため (私は信じています)、以下のコードは C です。Ac で次のように定義された関数 A があるとします。

void A(int x, int y){
    //Do stuff
}

現在、別のファイル Bc もあります。

extern "C"{
    void A(int x, int y, int z);
}

void B(){
    A(1, 2, 3);
}

A は最初は 2 つの引数しか持たないと宣言されていますが、Bc で宣言すると余分な引数があり、B() で 3 番目の引数で呼び出されます。この状況が発生する可能性があることはわかっています。たとえば、fortran サブルーチンとリンクしている場合や、動的にリンクしている場合です。

関数に余分な引数を渡すのは安全ではないと思いますが、関数が呼び出されて引数が渡されたときにメモリ内で何が起こっているのか説明できますか? したがって、使用も要求もされていないこの「余分な」引数を渡すことがどれほど安全かということです。

余分な引数が、関数内で使用されるメモリ内のスペースを上書きする可能性はありますか? それとも、A への関数呼び出しは、引数用にメモリ内のスペースを割り当ててから、引数メモリ ブロックの先頭がどこにあるかを A に伝え、A は最初の 2 つの引数を読み取り、最後の引数を無視して、完全に安全にしますか?

関数に関する情報は非常に啓発的です、ありがとう。

4

7 に答える 7

4

使用される呼び出し規約によって異なります。ではcdecl、呼び出し元が引数を右から左の順序でスタックにプッシュし、次に呼び出し先がスタック ポインターをオフセットしてそれらにアクセスします。この場合、あまりにも多くの引数を呼び出しても何も壊れません。

ただし、左から右への呼び出し規則がある場合は、問題が発生します。

于 2010-09-09T14:34:10.093 に答える
4

リンケージは実装定義なので、一概には言えません。

そうは言っても、C の他の機能 (特に vardic パラメーター) は、通常はそれを可能にする実装を強制します。

たとえば、次のように書いた場合に失敗する実装は知りません。

 printf("%d", 1, 2);

ただし、単に「1」を出力します。

ここにいる多くの人が を持ち出しcdecl、慣例pascalを呼び出しています。__stdcallただし、これらはいずれも標準の一部ではなく、特定の実装のすべての機能です。最初の文に戻ります。

于 2010-09-09T14:36:46.840 に答える
3

cdecl 呼び出し規約では、呼び出し元がスタックをクリーンアップする責任があるため、これは安全です。対照的に、pascal呼び出し規約では、呼び出し先がクリーンアップを担当するため、これは危険です。

于 2010-09-09T14:34:33.367 に答える
1

C では、これは制約違反であるため、未定義の動作が発生します。

「呼び出される関数を表す式がプロトタイプを含む型を持つ場合、引数の数はパラメーターの数と一致する必要があります。」(C99、§6.5.2.2)

とはいえ、実際には、基本的な呼び出し規約に大きく依存します。

于 2010-09-09T19:44:02.567 に答える
0

少なくとも C と C++ では害はありません。引数は右から左にプッシュされ、呼び出し先がスタックのクリーンアップを担当します。

ただし、可変引数を使用するか、関数型をキャストしない限り、コンパイラはこれを許可しません。例えば:

#include <stdio.h>

static void foo (int a, int b, int c, int d, int e, int f, int g)
{
    printf ("A:%d B:%d C:%d D:%d E:%d F:%d G:%d \n",
            a, b, c, d, e, f, g);
}

int main ()
{
    typedef void (*bad_foo) (int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int);
    foo (1, 2, 3, 4, 5, 6, 7);
    bad_foo f = (bad_foo) (&foo);
    f (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17);
}

アセンブリ コードを見ると、すべてのパラメーターがレジスターにプッシュされますが、余分なパラメーターは無視されます。

于 2010-09-09T14:53:35.650 に答える
0

そのようなコードは、 One Definition Ruleに違反しています (とにかく、C の同等物です...) それが機能するかどうかは、完全にプラットフォーム固有です。

特に x86 では、関数が宣言されている場合__cdecl、呼び出し元が__stdcallスタックをクリーンアップするため、機能しますが、(ほとんどの Win32 関数がそうであるように) 呼び出し先がスタックをクリーンアップし、その場合は間違ってクリーンアップします (パラメーターが多すぎます)。したがって、使用される外部関数の呼び出し規約に依存します。

なぜあなたがこれをやりたいのか理解できません。

于 2010-09-09T14:32:40.353 に答える
-1

正しく理解できれば、プログラムがメモリからランダムなコードを実行する可能性があります。関数が呼び出されると、戻りアドレス (関数が終了したときにプログラムがジャンプして戻る場所) を含むいくつかの値がスタックにプッシュされます。その後、関数の引数 (x、y、z) がスタックにプッシュされ、プログラムは関数のエントリ ポイントにジャンプします。関数は、スタックから引数 (x, y) をポップし、何かを実行してから、スタックから戻りアドレス (この場合は z、これは間違っています) をポップして、そこに戻ります。

スタックの詳細については、次のとおりです。 http://www.tenouk.com/Bufferoverflowc/Bufferoverflow2a.html

于 2010-09-09T14:36:42.583 に答える