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

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

このコミットは、Go言語のツールチェインに含まれるアセンブラ(cmd/5a, cmd/6a, cmd/8a)におけるフラグ解析のバグ修正に関するものです。具体的には、不正なフラグが渡された際にツールがクラッシュする問題を解決しています。

コミット

commit 8124a02cb23c0ac988f6e6be4e3e4104baf987c9
Author: Russ Cox <rsc@golang.org>
Date:   Fri Jul 12 14:23:36 2013 -0400

    cmd/5a, cmd/6a, cmd/8a: fix flag parsing
    
    go tool 6a -$(unicode fffd) was crashing.
    
    Fixes #5878.
    
    R=ken2
    CC=golang-dev
    https://golang.org/cl/11208045

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

https://github.com/golang/go/commit/8124a02cb23c0ac988f6e6be4e3e4104baf987c9

元コミット内容

cmd/5a, cmd/6a, cmd/8a: fix flag parsing

go tool 6a -$(unicode fffd) was crashing.

Fixes #5878.

R=ken2
CC=golang-dev
https://golang.org/cl/11208045

変更の背景

このコミットは、Go Issue #5878「cmd/6a crashes with invalid flag」を修正するために行われました。報告された問題は、go tool 6a -$(unicode fffd) のように不正なUnicode文字を含むフラグをアセンブラに渡すと、ツールがクラッシュするというものでした。

このクラッシュは、フラグ解析ロジックにおける境界チェックの不備が原因でした。具体的には、debug配列のインデックスとして使用される値の検証が不適切であり、配列の範囲外アクセスが発生していました。これにより、プログラムが予期せぬ動作(クラッシュ)を引き起こしていました。

前提知識の解説

  • cmd/5a, cmd/6a, cmd/8a: これらはGo言語のツールチェインに含まれるアセンブラです。
    • 5a: Plan 9 5-series (ARM) アセンブラ
    • 6a: Plan 9 6-series (x86-64) アセンブラ
    • 8a: Plan 9 8-series (x86) アセンブラ これらはGoのコンパイラが生成するアセンブリコードを処理するために使用されます。Goのツールチェインは、歴史的にPlan 9のツールチェインに由来しており、これらのアセンブラもその名残です。
  • ARGBEGIN / ARGC(): これらはPlan 9スタイルのコマンドライン引数解析マクロです。
    • ARGBEGIN: 引数解析ループの開始を示すマクロで、通常はmain関数の先頭近くに配置されます。
    • ARGC(): 現在処理中のコマンドライン引数の文字(または値)を返すマクロです。このマクロは、引数がフラグ(-で始まる)である場合に、そのフラグの文字を返します。
  • debug 配列: Goの内部ツール(アセンブラなど)には、デバッグフラグを管理するためのdebugという名前のグローバル配列がよく見られます。この配列は、特定のデバッグオプションが有効になっているかどうかを示すブール値(または整数値)を格納するために使用されます。配列のインデックスは、通常、フラグ文字のASCII値に対応します。例えば、-dフラグが指定された場合、debug['d'](またはdebug[ASCII値 of 'd'])が設定されるといった使い方をします。sizeof(debug)は、この配列のサイズ(バイト単位)を示し、配列が保持できる要素の最大数(通常はsizeof(debug) / sizeof(debug[0]))を間接的に示します。

技術的詳細

このバグは、ARGBEGINマクロ内でコマンドライン引数を処理する際に発生していました。ARGC()が返す値は、コマンドラインフラグの文字コードです。この文字コードがdebug配列のインデックスとして直接使用されていました。

元のコードでは、if(c >= 0 || c < sizeof(debug)) という条件式が使用されていました。この条件式は論理OR (||) を使用しているため、c >= 0 が真であれば、c < sizeof(debug) の評価は行われず、条件全体が真となります。

問題は、ARGC()が負の値を返す可能性があること、または非常に大きな正の値を返す可能性があることです。特に、不正なUnicode文字(例: \ufffd)がフラグとして渡された場合、ARGC()が返す値がdebug配列の有効なインデックス範囲(0からsizeof(debug)-1)を逸脱する可能性がありました。

  • c >= 0 || c < sizeof(debug) の場合:
    • cが負の値(例: -1)の場合、c >= 0 は偽ですが、c < sizeof(debug) は真になる可能性があります(sizeof(debug)は通常正の整数なので)。この場合、debug[c] は負のインデックスアクセスとなり、未定義動作を引き起こします。
    • cが非常に大きな正の値(例: 256以上)の場合、c >= 0 は真なので、条件全体が真となり、debug[c] は配列の範囲外アクセスとなり、クラッシュの原因となります。

修正は、この条件式を if(c >= 0 && c < sizeof(debug)) に変更することでした。論理AND (&&) を使用することで、c0以上であり、かつdebug配列のサイズ未満である場合にのみ、debug[c]へのアクセスが許可されるようになります。これにより、cが有効な配列インデックスの範囲内にあることが保証され、範囲外アクセスによるクラッシュが防止されます。

この修正は、src/cmd/5a/lex.csrc/cmd/6a/lex.csrc/cmd/8a/lex.c の3つのファイルに適用されており、これらすべてのアセンブラで同様の脆弱性が存在したことを示しています。

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

以下のコードスニペットは、src/cmd/5a/lex.csrc/cmd/6a/lex.csrc/cmd/8a/lex.c の各ファイルにおける変更を示しています。

--- a/src/cmd/5a/lex.c
+++ b/src/cmd/5a/lex.c
@@ -68,7 +68,7 @@ main(int argc, char *argv[])
  	ARGBEGIN {
  	default:
  	\tc = ARGC();
-\t\tif(c >= 0 || c < sizeof(debug))\n+\t\tif(c >= 0 && c < sizeof(debug))
  	\t\tdebug[c] = 1;
  	\tbreak;

コアとなるコードの解説

変更の中心は、if文の条件式です。

  • 変更前: if(c >= 0 || c < sizeof(debug))
    • この条件は、「cが0以上である」またはcdebug配列のサイズ未満である」のいずれかが真であれば、ブロック内のコードを実行します。
    • 前述の通り、csizeof(debug)よりも大きいが0以上の場合(例: c = 200, sizeof(debug) = 128)、c >= 0が真となるため、debug[c]へのアクセスが発生し、配列の範囲外アクセスとなります。
    • また、cが負の値の場合(例: c = -1)、c < sizeof(debug)が真となるため、debug[c]へのアクセスが発生し、負のインデックスアクセスとなります。
  • 変更後: if(c >= 0 && c < sizeof(debug))
    • この条件は、「cが0以上である」かつcdebug配列のサイズ未満である」の両方が真である場合にのみ、ブロック内のコードを実行します。
    • これにより、cdebug配列の有効なインデックス範囲(0からsizeof(debug)-1)内に収まっていることが厳密に保証されます。
    • 結果として、不正なフラグ文字がARGC()によって返されたとしても、debug配列への範囲外アクセスや負のインデックスアクセスが防止され、ツールのクラッシュが回避されます。

この修正は、非常に小さく見えますが、セキュリティと安定性の観点から非常に重要です。不正な入力に対する堅牢性を高めるための典型的な境界チェックの修正例と言えます。

関連リンク

  • Go Issue #5878: https://code.google.com/p/go/issues/detail?id=5878 (古いGo Issueトラッカーのリンクですが、現在はGitHubにリダイレクトされるはずです)
  • Gerrit Change-Id: I2124a02cb23c0ac988f6e6be4e3e4104baf987c9 (GoのGerritコードレビューシステムでの変更ID)
  • Go CL 11208045: https://golang.org/cl/11208045 (Goのコードレビューシステムへのリンク)

参考にした情報源リンク