14

32 ビット Windows アプリケーションの場合、ESP を明示的にデクリメントせずに一時的なスワップ領域に ESP の下のスタック メモリを使用することは有効ですか?

で浮動小数点値を返す関数を考えてみましょうST(0)。値が現在 EAX の場合、たとえば次のようになります。

PUSH   EAX
FLD    [ESP]
ADD    ESP,4  // or POP EAX, etc
// return...

または、ESP レジスタを変更せずに、次のようにすることもできます。

MOV    [ESP-4], EAX
FLD    [ESP-4]
// return...

どちらの場合も同じことが起こりますが、最初のケースでは、メモリを使用する前にスタック ポインターをデクリメントし、その後でインクリメントするように注意します。後者の場合、私たちはしません。

この値をスタックに永続化する必要があるにもかかわらず (再入の問題、PUSHing と値の読み取りの間の関数呼び出しなど)、このような ESP の下のスタックへの書き込みが無効になる根本的な理由はありますか?

4

5 に答える 5

7

一般的な場合 ( x86/x64プラットフォーム) - 割り込みはいつでも実行できます。これにより、メモリ以下のスタック ポインターが上書きされます (現在のスタックで実行された場合)。これは、スタック ポインターの下に何かを一時的に保存しても、カーネル モードでは有効ではないためです。割り込みは現在のカーネル スタックを使用します。しかし、ユーザーモードの状況では、Windowsは割り込みテーブル(IDT)を構築し、割り込みが発生したときに常にカーネルモードとカーネルスタックで実行されます。その結果、ユーザー モード スタック (スタック ポインターの下) は影響を受けません。そして、関数呼び出しを行わなくなるまで、ポインタの下のスタックスペースを一時的に使用する可能性があります。例外が発生する場合(たとえば、無効なアドレスへのアクセスによる)-スペースベロースタックポインターも上書きされます-もちろん、CPU例外はカーネルモードとカーネルスタックで実行されますが、カーネルよりも経由でユーザー空間でコールバックを実行しますntdll.KiDispatchExecptionすでに現在のスタック空間にあります。一般に、これは Windows ユーザー モード (現在の実装) で有効ですが、何をしているのかをよく理解する必要があります。しかし、これはめったに使用されないと思います


もちろん、Windowsユーザーモードで、スタックポインターの下に書き込むことができるというコメントで指摘された正確さは、現在の実装の動作です。これは文書化または保証されていません。

ただし、これは非常に基本的なことです。変更される可能性はほとんどありません。割り込みは、常に特権カーネル モードでのみ実行されます。カーネル モードは、カーネル モード スタックのみを使用します。ユーザーモードコンテキストはまったく信頼されていません。ユーザーモードプログラムが間違ったスタックポインタを設定するとどうなりますか? mov rsp,1またはで言う mov esp,1?この命令の直後に割り込みが発生します。そのような無効な esp/rsp で実行を開始するとどうなりますか? すべてのオペレーティング システムがクラッシュしました。この割り込みはカーネルスタックでのみ実行されるためです。ユーザースタックスペースを上書きしません。

また、スタックはスペースが限られていることに注意する必要があります (ユーザー モードでも)。1 ページ (4Kb) のエラーが発生した場合にアクセスします (ガード ページを下に移動するには、ページごとにスタック プローブを実行する必要があります)。

そして最後に、通常アクセスする必要はありません- どの問題で最初[ESP-4], EAXにデクリメントしますか? ESPループでスタックスペースにアクセスする必要がある場合でも、スタックポインターを1回だけ減らす必要があります-1つの追加命令(ループ内ではない)は、パフォーマンスやコードサイズに変化はありません。

したがって、正式なものにもかかわらず、これはWindowsユーザーモードで正しく機能します。これを使用することをお勧めします(必要ではありません)


もちろん、正式な文書には次のように書かれています:

スタック使用量

RSP の現在のアドレスを超えるすべてのメモリは揮発性と見なされます

ただし、これはカーネルモードも含めて一般的なケースです。私はユーザーモードについて書き、現在の実装に基づいています


将来のウィンドウで可能になり、「直接」apcまたはいくつかの「直接」シグナルを追加します-一部のコードは、スレッドがカーネルに入った直後(通常のハードウェア割り込み中)にコールバックを介して実行されます。この後、esp 以下はすべて未定義になります。しかし、これが存在しないまで。このコードが常に(現在のビルドで)正しく機能するまで。

于 2018-09-10T13:16:25.643 に答える
2

ここでのいくつかの回答は、プロセスが「アラート可能な状態」にある場合にのみ配信でき、Unix シグナルハンドラーのように通常の実行に関して真に非同期ではないことを示して、APC (非同期プロシージャーコール) に言及しています。

Windows 10 バージョン 1809 では、Unix 信号のようにいつでも起動できる特別ユーザー APC が導入されています。低レベルの詳細については、この記事を参照してください。

特別ユーザー APC は、RS5 で追加された (そして NtQueueApcThreadEx を通じて公開された) メカニズムですが、最近 (インサイダー ビルドで) 新しいシステムコール NtQueueApcThreadEx2 を通じて公開されました。このタイプの APC が使用される場合、実行の途中でスレッドにシグナルが送られ、特別な APC が実行されます。

于 2020-09-19T08:51:18.493 に答える