43

アプリのスタック スペースの量と、実行中のスタック使用量の最高水準点を確認する標準的な方法はありますか?

また、実際のオーバーフローという恐ろしいケースでは何が起こるでしょうか?

クラッシュしたり、例外やシグナルをトリガーしたりしますか? 標準はありますか、それともすべてのシステムとコンパイラで異なりますか?

特に Windows、Linux、および Macintosh を探しています。

4

9 に答える 9

16

Windowsでは、スタック オーバーフロー例外が生成されます。

次の Windows コードは、これを示しています。

#include <stdio.h>
#include <windows.h>

void StackOverFlow()
{
  CONTEXT context;

  // we are interested control registers
  context.ContextFlags = CONTEXT_CONTROL;

  // get the details
  GetThreadContext(GetCurrentThread(), &context);

  // print the stack pointer
  printf("Esp: %X\n", context.Esp);

  // this will eventually overflow the stack
  StackOverFlow();
}

DWORD ExceptionFilter(EXCEPTION_POINTERS *pointers, DWORD dwException)
{
  return EXCEPTION_EXECUTE_HANDLER;
}

void main()
{
  CONTEXT context;

  // we are interested control registers
  context.ContextFlags = CONTEXT_CONTROL;

  // get the details
  GetThreadContext(GetCurrentThread(), &context);

  // print the stack pointer
  printf("Esp: %X\n", context.Esp);

  __try
  {
    // cause a stack overflow
    StackOverFlow();
  }
  __except(ExceptionFilter(GetExceptionInformation(), GetExceptionCode()))
  {
    printf("\n****** ExceptionFilter fired ******\n");
  }
}

この exe を実行すると、次の出力が生成されます。

Esp: 12FC4C
Esp: 12F96C
Esp: 12F68C
.....
Esp: 33D8C
Esp: 33AAC
Esp: 337CC

****** ExceptionFilter fired ******
于 2008-10-14T02:21:29.303 に答える
15

Linux では、コードがスタックを超えて書き込もうとすると、セグメンテーション違反が発生します。

スタックのサイズは、プロセス間で継承されるプロパティです。ulimit -s(in sh, ksh, zsh) やlimit stacksize( tcsh, ) などのコマンドを使用して、シェルで読み取りまたは変更できる場合zsh

プログラムから、スタックのサイズは次を使用して読み取ることができます

#include <sys/resource.h>
#include <stdio.h>

int main() {
    struct rlimit l;
    getrlimit(RLIMIT_STACK, &l);
    printf("stack_size = %ld\n", l.rlim_cur);
    return 0;
}

利用可能なスタックのサイズを取得する標準的な方法がわかりません。

スタックは で始まり、argcその後に環境の内容argvとコピー、そして変数が続きます。ただし、カーネルはスタックの開始位置をランダム化できるため、 より上にダミーの値が存在する可能性があるため、より下に利用可能なバイトargcがあると仮定するのは誤りです。l.rlim_cur&argc

スタックの正確な場所を取得する 1 つの方法は、ファイルを調べることです/proc/1234/maps (1234はプログラムのプロセス ID です)。これらの境界がわかれば、最新のローカル変数のアドレスを調べることで、スタックがどれだけ使用されているかを計算できます。

于 2008-10-14T14:22:26.900 に答える
10

gcc は、「安全でない」関数呼び出しの戻りアドレスと通常の変数の間に追加のメモリ ブロックを配置します (この例では、関数は void test() {char a[10]; b[20]} です)。

call stack:
-----------
return address
dummy
char b[10]
char a[20]

関数がポインター 'a' に 36 バイトを書き込むと、オーバーフローにより戻りアドレスが「破損」します (セキュリティ違反の可能性があります)。しかし、ポインタと戻りアドレスの間にある「ダミー」の値も変更されるため、プログラムは警告を出してクラッシュします (これは -fno-stack-protector で無効にできます)。

于 2008-10-14T02:23:38.363 に答える
7

Linuxでは、Gnu libsigsegvライブラリに関数が含まれています。この関数はstackoverflow_install_handler、スタックオーバーフローを検出できます(場合によっては、回復に役立ちます)。

于 2009-10-17T04:02:59.040 に答える
6

Windowsでは、スタック(特定のスレッド用)は、作成前にこのスレッドに指定されたスタックサイズに達するまで、オンデマンドで拡張されます。

オンデマンドの成長は、最初に利用可能なスタックのフラグメントのみがあり、その後にガードページが続くという点で、ガードページを使用して推進されます。ガードページは、ヒットすると例外をトリガーします。この例外は特別であり、システムによって処理されます。 you-処理により、使用可能なスタックスペースが増加し(制限に達しているかどうかもチェックされます!)、読み取り操作が再試行されます。

制限に達すると、それ以上成長することはなく、スタックオーバーフローの例外が発生します。現在のスタックベースと制限は、スレッド環境ブロックの_NT_TIB(スレッド情報ブロック)と呼ばれる構造体に格納されます。デバッガーが手元にある場合は、次のように表示されます。

0:000> dt ntdll!_teb @$teb nttib.
   +0x000 NtTib  : 
      +0x000 ExceptionList : 0x0012e030 _EXCEPTION_REGISTRATION_RECORD
      +0x004 StackBase : 0x00130000 
      +0x008 StackLimit : 0x0011e000 
      +0x00c SubSystemTib : (null) 
      +0x010 FiberData : 0x00001e00 
      +0x010 Version : 0x1e00
      +0x014 ArbitraryUserPointer : (null) 
      +0x018 Self   : 0x7ffdf000 _NT_TIB

StackLimit属性は、オンデマンドで更新されます。このメモリブロックの属性を確認すると、次のようなものが表示されます。

0:000> !address 0x0011e000 
    00030000 : 0011e000 - 00012000
                    Type     00020000 MEM_PRIVATE
                    Protect  00000004 PAGE_READWRITE
                    State    00001000 MEM_COMMIT
                    Usage    RegionUsageStack
                    Pid.Tid  abc.560

そして、その隣のページをチェックすると、ガード属性が明らかになります。

0:000> !address 0x0011e000-1000
    00030000 : 0011d000 - 00001000
                    Type     00020000 MEM_PRIVATE
                    Protect  00000104 PAGE_READWRITE | PAGE_GUARD
                    State    00001000 MEM_COMMIT
                    Usage    RegionUsageStack
                    Pid.Tid  abc.560

それが役に立てば幸い。

于 2008-12-19T08:46:13.073 に答える
4

スタック オーバーフローは、おそらく処理するのが最も厄介な種類の例外です。例外ハンドラーは最小限の量のスタックを処理する必要があるためです (通常、この目的のために予約されているのは 1 ページだけです)。

このタイプの例外を処理する難しさに関する興味深い議論については、次のブログ投稿を参照してください: Chris Brumme の1および2では、.NET の観点からの問題、特に CLR のホスティングに焦点を当てています。

于 2008-10-14T02:36:57.823 に答える
1

Visual Studio で editbin を使用して、スタック サイズを変更することができます。この情報はmsdn.microsoft.com/en-us/library/35yc2tc3.aspxにあります。

于 2011-08-11T10:26:27.827 に答える
1

Linux を使用している場合は、alternate-signal-stack を使用することをお勧めします。

  1. この場合、すべてのシグナルは代替スタックで処理されます。
  2. スタック オーバーフローが発生した場合、システムは SEGV 信号を生成します。これは代替スタックで処理できます。
  3. これを使用しないと、シグナルを処理できない可能性があり、処理やエラー報告なしでプログラムがクラッシュする可能性があります。
于 2008-12-19T07:35:54.057 に答える
1

一部のコンパイラは、スタックの残りの空き容量を返す stackavail() 関数をサポートしています。多くのスタック領域を必要とするプログラムで関数を呼び出す前に、この関数を使用して、それらを呼び出しても安全かどうかを判断できます。

于 2008-10-14T08:21:21.683 に答える