[インデックス 16327] ファイルの概要
このコミットは、GoランタイムがWindows上でOSメモリを割り当てる際に、実行可能としてマークしないように変更するものです。具体的には、VirtualAlloc
システムコールを使用する際のメモリ保護フラグをPAGE_EXECUTE_READWRITE
からPAGE_READWRITE
に変更しています。これにより、セキュリティの向上と、場合によってはパフォーマンスの最適化が期待されます。
コミット
commit 28f74608b5ab87cce5e19d91de915649bf4d0865
Author: Alex Brainman <alex.brainman@gmail.com>
Date: Fri May 17 13:37:30 2013 +1000
runtime: do not mark os memory as executable on windows
R=golang-dev, bradfitz, khr
CC=golang-dev
https://golang.org/cl/9235046
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/28f74608b5ab87cce5e19d91de915649bf4d0865
元コミット内容
runtime: do not mark os memory as executable on windows
R=golang-dev, bradfitz, khr
CC=golang-dev
https://golang.org/cl/9235046
変更の背景
この変更の背景には、主にセキュリティの強化と、不要な実行権限の付与による潜在的なリスクの排除があります。Goランタイムは、プログラムの実行に必要なメモリをOSから動的に確保します。以前はWindows環境において、この確保されたメモリ領域にデフォルトで「読み取り」「書き込み」「実行」の権限(PAGE_EXECUTE_READWRITE
)を付与していました。
しかし、データ領域として使用されるメモリに実行権限が付与されていると、悪意のあるコードがそのデータ領域に注入され、実行されるという攻撃(バッファオーバーフローなどによるコードインジェクション攻撃)のリスクが高まります。これはData Execution Prevention (DEP)などのセキュリティ機能が防ごうとする種類の攻撃です。
このコミットは、Goランタイムが確保するメモリのうち、コードの実行に直接関わらないデータ領域については、明示的に実行権限を剥奪し、「読み取り」と「書き込み」の権限(PAGE_READWRITE
)のみを付与するように変更することで、このような攻撃に対する耐性を向上させることを目的としています。これにより、Goプログラムのセキュリティが強化されます。
前提知識の解説
1. 仮想メモリとメモリ保護
現代のオペレーティングシステム(OS)は、仮想メモリという概念を使用しています。これは、各プロセスが物理メモリを直接操作するのではなく、仮想アドレス空間を介してメモリにアクセスする仕組みです。OSは、この仮想アドレスを物理メモリのアドレスにマッピングし、メモリの分離と保護を実現します。
メモリ保護は、OSが各メモリページに対して設定するアクセス権限のことです。これにより、あるプロセスが他のプロセスのメモリ領域にアクセスしたり、許可されていない操作(例えば、データ領域の実行)を行ったりすることを防ぎます。
2. Windowsのメモリ管理API: VirtualAlloc
Windows APIには、仮想メモリを管理するための関数群が提供されています。その中でもVirtualAlloc
関数は、プロセスのアドレス空間内でメモリ領域を予約、コミット、または解放するために使用されます。
VirtualAlloc
の重要な引数の一つに、flProtect
(メモリ保護フラグ)があります。これは、割り当てられるメモリ領域にどのようなアクセス権限を付与するかを指定します。
3. メモリ保護フラグ
-
PAGE_READWRITE
(0x0004): このフラグは、メモリ領域が読み取りと書き込みの両方でアクセス可能であることを示します。データ領域やヒープメモリなど、コードの実行を意図しないメモリ領域に通常使用されます。 -
PAGE_EXECUTE_READWRITE
(0x40): このフラグは、メモリ領域が読み取り、書き込み、および実行のすべてでアクセス可能であることを示します。通常、動的に生成されるコード(JITコンパイラなど)や、実行可能ファイルがロードされるメモリ領域に使用されます。しかし、データ領域にこのフラグが付与されていると、セキュリティ上のリスクが生じます。
4. Data Execution Prevention (DEP)
DEPは、Windowsのセキュリティ機能の一つで、データ領域としてマークされたメモリ領域からのコード実行を防ぐことを目的としています。これにより、バッファオーバーフローなどの脆弱性を悪用した攻撃(例えば、スタックに注入された悪意のあるコードの実行)を軽減します。DEPが有効なシステムでは、PAGE_EXECUTE_READWRITE
のような実行権限を持つメモリ領域は厳しく監視されます。
5. Goランタイムのメモリ管理
Goランタイムは、独自のメモリ管理(ガベージコレクションを含む)を行います。OSから大きなメモリブロックをSysAlloc
(システムアロケーション)を通じて取得し、それをGoのヒープとして管理します。このコミットは、GoランタイムがOSからメモリを取得する際の初期のメモリ保護設定に関するものです。
技術的詳細
このコミットは、src/pkg/runtime/mem_windows.c
ファイル内のGoランタイムのWindows固有のメモリ管理コードを変更しています。具体的には、以下の3つの関数におけるVirtualAlloc
システムコールの呼び出しで渡されるメモリ保護フラグが変更されています。
-
runtime·SysAlloc
: Goランタイムが新しいメモリ領域をコミット(物理メモリを割り当て、仮想アドレス空間にマッピング)する際に使用されます。以前はPAGE_EXECUTE_READWRITE
を使用していましたが、PAGE_READWRITE
に変更されました。 -
runtime·SysReserve
: Goランタイムがアドレス空間内のメモリ領域を予約する際に使用されます。この予約された領域は、後でコミットされる可能性があります。ここでもPAGE_EXECUTE_READWRITE
からPAGE_READWRITE
への変更が行われました。 -
runtime·SysMap
: 予約されたメモリ領域をコミットする際に使用されます。ここでも同様にPAGE_EXECUTE_READWRITE
からPAGE_READWRITE
への変更が行われました。
これらの変更により、GoランタイムがWindows上でOSから取得するメモリ領域は、デフォルトで実行権限を持たなくなります。これにより、データ領域が誤って実行可能として扱われるリスクが低減され、DEPなどのセキュリティ機能との連携がより適切になります。
Goランタイムは、必要に応じて特定のメモリ領域(例えば、JITで生成されるコードなど)に対して後から実行権限を付与するメカニズムを持っているため、この変更がGoプログラムの正常な動作を妨げることはありません。これは、必要な最小限の権限のみを付与するという「最小権限の原則」に則った変更と言えます。
コアとなるコードの変更箇所
diff --git a/src/pkg/runtime/mem_windows.c b/src/pkg/runtime/mem_windows.c
index 7840daa22c..7ac0c6aaf1 100644
--- a/src/pkg/runtime/mem_windows.c
+++ b/src/pkg/runtime/mem_windows.c
@@ -13,6 +13,7 @@ enum {
MEM_RESERVE = 0x8000,
+ PAGE_READWRITE = 0x0004,
PAGE_EXECUTE_READWRITE = 0x40,
};
@@ -25,7 +26,7 @@ void*
runtime·SysAlloc(uintptr n)
{
mstats.sys += n;
- return runtime·stdcall(runtime·VirtualAlloc, 4, nil, n, (uintptr)(MEM_COMMIT|MEM_RESERVE), (uintptr)PAGE_EXECUTE_READWRITE);
+ return runtime·stdcall(runtime·VirtualAlloc, 4, nil, n, (uintptr)(MEM_COMMIT|MEM_RESERVE), (uintptr)PAGE_READWRITE);
}
void
@@ -51,12 +52,12 @@ runtime·SysReserve(void *v, uintptr n)
{
// v is just a hint.
// First try at v.
- v = runtime·stdcall(runtime·VirtualAlloc, 4, v, n, (uintptr)MEM_RESERVE, (uintptr)PAGE_EXECUTE_READWRITE);
+ v = runtime·stdcall(runtime·VirtualAlloc, 4, v, n, (uintptr)MEM_RESERVE, (uintptr)PAGE_READWRITE);
if(v != nil)
return v;
// Next let the kernel choose the address.
- return runtime·stdcall(runtime·VirtualAlloc, 4, nil, n, (uintptr)MEM_RESERVE, (uintptr)PAGE_EXECUTE_READWRITE);
+ return runtime·stdcall(runtime·VirtualAlloc, 4, nil, n, (uintptr)MEM_RESERVE, (uintptr)PAGE_READWRITE);
}
void
@@ -65,7 +66,7 @@ runtime·SysMap(void *v, uintptr n)
void *p;
mstats.sys += n;
- p = runtime·stdcall(runtime·VirtualAlloc, 4, v, n, (uintptr)MEM_COMMIT, (uintptr)PAGE_EXECUTE_READWRITE);
+ p = runtime·stdcall(runtime·VirtualAlloc, 4, v, n, (uintptr)MEM_COMMIT, (uintptr)PAGE_READWRITE);
if(p != v)
runtime·throw("runtime: cannot map pages in arena address space");
}
コアとなるコードの解説
このコミットの核心は、src/pkg/runtime/mem_windows.c
ファイル内のVirtualAlloc
システムコールへの引数変更です。
-
PAGE_READWRITE
の追加: まず、enum
ブロックにPAGE_READWRITE = 0x0004
が新しく定義されています。これは、読み取りと書き込みの権限のみを持つメモリページを示す定数です。 -
runtime·SysAlloc
の変更:runtime·SysAlloc
関数は、GoランタイムがOSから新しいメモリを割り当てる際に呼び出されます。変更前はPAGE_EXECUTE_READWRITE
フラグを使用していましたが、変更後はPAGE_READWRITE
を使用するように修正されています。これにより、Goランタイムが確保するメモリは、デフォルトで実行権限を持たなくなります。 -
runtime·SysReserve
の変更:runtime·SysReserve
関数は、メモリ領域を予約する際に呼び出されます。ここでも同様に、VirtualAlloc
のflProtect
引数がPAGE_EXECUTE_READWRITE
からPAGE_READWRITE
に変更されています。予約段階で実行権限を付与しないことで、より厳格なメモリ保護が実現されます。 -
runtime·SysMap
の変更:runtime·SysMap
関数は、予約されたメモリ領域をコミット(実際に物理メモリを割り当てて使用可能にする)する際に呼び出されます。ここでもPAGE_EXECUTE_READWRITE
からPAGE_READWRITE
への変更が行われています。
これらの変更は、GoランタイムがWindows上でメモリを確保する際のデフォルトの挙動を変更し、セキュリティを向上させることを目的としています。データとして使用されるメモリ領域に不要な実行権限を付与しないことで、悪意のあるコードの実行を防ぐためのOSのセキュリティ機能(DEPなど)がより効果的に機能するようになります。
関連リンク
- Go CL 9235046: https://golang.org/cl/9235046
- GitHubコミットページ: https://github.com/golang/go/commit/28f74608b5ab87cce5e19d91de915649bf4d0865
参考にした情報源リンク
- Microsoft Docs - VirtualAlloc function: https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualalloc
- Microsoft Docs - Memory Protection Constants: https://learn.microsoft.com/en-us/windows/win32/memory/memory-protection-constants
- Microsoft Docs - Data Execution Prevention: https://learn.microsoft.com/en-us/windows/win32/memory/data-execution-prevention
- Goのメモリ管理に関する一般的な情報 (Goの公式ドキュメントやブログ記事など)
- オペレーティングシステムの仮想メモリ管理に関する一般的な情報