Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 13566] ファイルの概要

コミット

commit 3f34248a7712e451b4217aa135e9236e93ece964
Author: Russ Cox <rsc@golang.org>
Date:   Fri Aug 3 15:27:35 2012 -0400

    cmd/ld: add PT_PAX_FLAGS ELF header
    
    PAX systems are Linux systems that are more paranoid about memory permissions.
    These flags tell them to relax when running Go binaries.
    
    Fixes #47.
    
    R=iant
    CC=golang-dev
    https://golang.org/cl/6326054

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/3f34248a7712e451b4217aa135e9236e93ece964

元コミット内容

cmd/ld: add PT_PAX_FLAGS ELF header

PAXシステムは、メモリパーミッションに関してより厳格なLinuxシステムです。 これらのフラグは、Goバイナリを実行する際にPAXシステムに緩和を指示します。

Fixes #47.

変更の背景

このコミットは、Go言語のリンカ (cmd/ld) に PT_PAX_FLAGS というELFヘッダを追加するものです。背景には、PaX (Patch for eXecutable) と呼ばれるLinuxカーネルのセキュリティ機能との互換性の問題がありました。

PaXは、メモリの実行権限を厳しく制限することで、バッファオーバーフローなどのメモリ関連の脆弱性を悪用した攻撃を防ぐことを目的としたセキュリティパッチです。具体的には、データ領域からのコード実行を禁止したり、アドレス空間配置のランダム化 (ASLR) を強化したりします。

しかし、Go言語のランタイムは、ガベージコレクションやコルーチン (goroutine) のスタック管理などの内部的な動作のために、実行時にメモリ領域のパーミッションを変更する (mprotect システムコールを使用する) ことがあります。PaXが有効なシステムでは、このような動的なメモリパーミッションの変更がセキュリティポリシーに違反すると見なされ、Goバイナリがクラッシュしたり、正しく動作しなかったりする問題が発生していました。

この問題は、GoプロジェクトのIssue #47として報告されていました。このコミットは、GoバイナリがPaXシステム上で適切に動作するように、リンカが出力するELFファイルにPT_PAX_FLAGSヘッダを追加することで、PaXシステムに対してGoバイナリの特定の動作を「緩和」させることを目的としています。これにより、Goのランタイムが必要とするメモリ操作がPaXによってブロックされるのを防ぎます。

前提知識の解説

ELF (Executable and Linkable Format)

ELFは、Unix系システムで実行可能ファイル、オブジェクトファイル、共有ライブラリなどを表現するための標準的なファイルフォーマットです。ELFファイルは、ヘッダ、プログラムヘッダテーブル、セクションヘッダテーブル、そして実際のデータ(コードやデータ)で構成されます。

  • ELFヘッダ: ファイルの基本的な情報(マジックナンバー、アーキテクチャ、OS ABIなど)を含みます。
  • プログラムヘッダテーブル (Program Header Table): 実行時にメモリにロードされるセグメントのレイアウトを記述します。各エントリは「プログラムヘッダ (Program Header)」と呼ばれ、セグメントのタイプ(例: PT_LOADPT_GNU_STACK)、メモリ上の位置、サイズ、パーミッションなどを定義します。
  • セクションヘッダテーブル (Section Header Table): リンク時に使用されるファイルの論理的な構造(コードセクション、データセクションなど)を記述します。

PaX (Patch for eXecutable)

PaXは、Linuxカーネルのセキュリティ拡張機能であり、メモリ保護を強化することで、エクスプロイト(脆弱性攻撃)を困難にします。主な機能は以下の通りです。

  • W^X (Write XOR Execute): メモリページが書き込み可能であると同時に実行可能である状態を禁止します。これにより、データ領域に注入された悪意のあるコードが実行されるのを防ぎます。
  • ASLR (Address Space Layout Randomization): プロセスのアドレス空間のレイアウトをランダム化し、攻撃者が特定のコードやデータのメモリ上の位置を予測することを困難にします。
  • mprotect制限: mprotect()システムコールによるメモリパーミッションの変更を制限します。これは、攻撃者が非実行可能メモリを実行可能に変更するのを防ぐためです。

PT_PAX_FLAGS

PT_PAX_FLAGSは、PaXシステムがELFバイナリに対して適用するセキュリティポリシーを細かく制御するためのプログラムヘッダのタイプです。このヘッダが存在する場合、PaXはバイナリに埋め込まれたフラグを読み取り、それに応じてセキュリティ機能を調整します。これにより、特定のアプリケーションがPaXの厳格なポリシーと衝突する場合でも、セキュリティを完全に無効にすることなく、必要な緩和措置を講じることが可能になります。PT_PAX_FLAGSのタイプ値は 0x65041580 です。

mprotect, randexec, emutramp

これらはPaXが提供するセキュリティ機能の一部です。

  • mprotect: 前述の通り、mprotect()システムコールによるメモリパーミッション変更の制限を指します。Goバイナリが動的にメモリパーミッションを変更する必要がある場合、この制限が問題となります。
  • randexec (RANDEXEC): 非PIE (Position Independent Executable) バイナリのベースアドレスをランダム化するPaXの機能です。ASLRの一種ですが、特定のバイナリタイプに特化しています。
  • emutramp (EMUTRAMP): 「エミュレート・トランポリン」の略で、JITコンパイラやネストされた関数など、実行時に動的にコードを生成するプログラムがPaXのW^X保護と衝突するのを避けるための機能です。EMUTRAMPを有効にすることで、PaXはこれらの動的に生成されたコードの実行を許可します。

このコミットでは、0x2a00というフラグ値が設定されています。これは、mprotect, randexec, emutramp の各機能を無効化(または緩和)することをPaXに指示するものです。具体的には、PaXのドキュメントによると、0x2a00は以下のフラグの組み合わせに相当します。

  • PAX_MPROTECT: mprotectの制限を無効化
  • PAX_RANDEXEC: randexecを無効化
  • PAX_EMUTRAMP: emutrampを無効化

これらのフラグを無効にすることで、Goランタイムが実行時に必要とするメモリ操作がPaXによって妨げられなくなり、GoバイナリがPaXシステム上で正常に動作するようになります。

技術的詳細

このコミットは、Go言語のリンカ (cmd/ld) がELF実行可能ファイルを生成する際に、PT_PAX_FLAGSプログラムヘッダを追加するように変更します。

具体的には、以下のファイルが変更されています。

  • src/cmd/6l/asm.c: 6lはamd64アーキテクチャ用のリンカです。
  • src/cmd/8l/asm.c: 8lはx86アーキテクチャ用のリンカです。
  • src/cmd/ld/elf.h: ELF関連の定数を定義するヘッダファイルです。

変更の核心は、asmb()関数(アセンブリコードを生成し、ELFヘッダやプログラムヘッダを設定する関数)内で、既存のPT_GNU_STACKヘッダの後に新しいPT_PAX_FLAGSヘッダを追加する点です。

新しいPT_PAX_FLAGSプログラムヘッダは、以下のプロパティで初期化されます。

  • ph->type = PT_PAX_FLAGS;: ヘッダのタイプをPT_PAX_FLAGSに設定します。この定数はsrc/cmd/ld/elf.h0x65041580として新しく定義されています。
  • ph->flags = 0x2a00;: PaXシステムに渡すフラグ値を0x2a00に設定します。この値は、mprotect, randexec, emutramp の各機能を無効化(または緩和)することを意味します。
  • ph->align = 8; (6lの場合) または ph->align = 4; (8lの場合): メモリのアライメントを設定します。これは、プログラムヘッダがメモリにロードされる際の配置要件です。

この変更により、Goリンカによって生成された実行可能ファイルは、PaXが有効なLinuxシステム上で実行される際に、PaXの厳格なメモリ保護ポリシーの一部を緩和するようPaXカーネルに明示的に要求するようになります。これにより、Goランタイムが動的にメモリパーミッションを変更する操作が許可され、Goバイナリの互換性と安定性が向上します。

コアとなるコードの変更箇所

src/cmd/6l/asm.c および src/cmd/8l/asm.c

これらのファイルは、Goリンカのアーキテクチャ固有の部分であり、ELF実行可能ファイルのプログラムヘッダを構築するロジックを含んでいます。

--- a/src/cmd/6l/asm.c
+++ b/src/cmd/6l/asm.c
@@ -1082,6 +1082,11 @@ asmb(void)\n 	\tph->type = PT_GNU_STACK;\n 	\tph->flags = PF_W+PF_R;\n 	\tph->align = 8;\n+\t\t\n+\t\tph = newElfPhdr();\n+\t\tph->type = PT_PAX_FLAGS;\n+\t\tph->flags = 0x2a00; // mprotect, randexec, emutramp disabled\n+\t\tph->align = 8;\
 \n 	\tsh = newElfShstrtab(elfstr[ElfStrShstrtab]);\n 	\tsh->type = SHT_STRTAB;\

--- a/src/cmd/8l/asm.c
+++ b/src/cmd/8l/asm.c
@@ -1127,6 +1127,11 @@ asmb(void)\n 	\tph->flags = PF_W+PF_R;\n 	\tph->align = 4;\n \n+\t\tph = newElfPhdr();\n+\t\tph->type = PT_PAX_FLAGS;\n+\t\tph->flags = 0x2a00; // mprotect, randexec, emutramp disabled\n+\t\tph->align = 4;\
+\n 	\tsh = newElfShstrtab(elfstr[ElfStrShstrtab]);\n 	\tsh->type = SHT_STRTAB;\
 	\tsh->addralign = 1;\

src/cmd/ld/elf.h

このヘッダファイルには、ELFフォーマットに関連する定数が定義されています。

--- a/src/cmd/ld/elf.h
+++ b/src/cmd/ld/elf.h
@@ -251,6 +251,7 @@ typedef struct {\n #define PT_LOPROC	0x70000000	/* First processor-specific type. */\n #define PT_HIPROC	0x7fffffff	/* Last processor-specific type. */\n #define PT_GNU_STACK	0x6474e551\n+#define PT_PAX_FLAGS	0x65041580\
 \n /* Values for p_flags. */\n #define PF_X		0x1		/* Executable. */

コアとなるコードの解説

上記の変更は、GoリンカがELF実行可能ファイルを生成する際のプログラムヘッダテーブルに、新しいエントリを追加するものです。

  1. src/cmd/ld/elf.h の変更:

    • #define PT_PAX_FLAGS 0x65041580 という行が追加されています。これは、PT_PAX_FLAGSという新しいプログラムヘッダタイプに、PaXが認識する特定の数値(0x65041580)を割り当てています。この数値は、ELF仕様で定義されているプロセッサ固有のタイプ範囲(PT_LOPROCからPT_HIPROC)内にあり、PaXがこのヘッダを識別するために使用します。
  2. src/cmd/6l/asm.c および src/cmd/8l/asm.c の変更:

    • newElfPhdr()関数を呼び出して、新しいELFプログラムヘッダ構造体を作成しています。
    • 作成したプログラムヘッダのtypeフィールドを、新しく定義したPT_PAX_FLAGSに設定しています。
    • flagsフィールドには0x2a00という値を設定しています。この値は、PaXシステムに対して、Goバイナリの実行時にmprotectrandexecemutrampといった特定のセキュリティ機能を無効化(または緩和)するよう指示するものです。コメントにも「mprotect, randexec, emutramp disabled」と明記されており、このフラグの意図が明確に示されています。
    • alignフィールドは、メモリのアライメント要件を設定します。6l(amd64)では8バイト、8l(x86)では4バイトに設定されています。

これらの変更により、Goリンカは、GoバイナリがPaXが有効なシステム上で実行される際に、PaXの厳格なメモリ保護ポリシーの一部を緩和するよう明示的に要求するELFヘッダを埋め込むようになります。これにより、Goランタイムが動的にメモリパーミッションを変更する操作が許可され、Goバイナリの互換性と安定性が向上します。

関連リンク

参考にした情報源リンク