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

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

このコミットは、GoコンパイラのCフロントエンド(src/cmd/cc)における、型(typ)処理関数内の配列の範囲外アクセス(out-of-bounds array access)のバグを修正するものです。具体的には、ewidth配列へのアクセスが、定義されている型定数(NTYPE)の範囲を超えて行われる可能性があった問題を解決しています。

コミット

commit 436f297d1e8cb941d859a00467395a8c541035e6
Author: Anthony Martin <ality@pbrane.org>
Date:   Thu Feb 23 14:28:16 2012 -0500

    cc: fix an out of bounds array access
    
    Alternatively, we could expand the ewidth array
    in [568]c/txt.c to have NALLTYPES elements and
    give all types above NTYPE a width of -1.
    
    I don't think it's worth it since TDOT and TOLD
    are the only two type values above NTYPE that
    are passed to typ:
    
    $ /tmp/cctypes
    cc/dcl.c:683:                   t->down = typ(TOLD, T);\
    cc/dcl.c:919:           return typ(TDOT, T);\
    $
    
    Fixes #3063.
    
    R=golang-dev, rsc
    CC=golang-dev
    https://golang.org/cl/5694047

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

https://github.com/golang/go/commit/436f297d1e8cb941d859a00467395a8c541035e6

元コミット内容

このコミットは、GoコンパイラのCフロントエンド(cc)において、typ関数がewidth配列にアクセスする際に発生する可能性のある範囲外アクセスを修正します。コミットメッセージでは、ewidth配列をNALLTYPES要素に拡張し、NTYPEを超えるすべての型に幅-1を与える代替案も提示されていますが、TDOTTOLDのみがNTYPEを超える値としてtyp関数に渡されるため、その必要はないと判断されています。この修正は、Issue #3063を解決します。

変更の背景

GoコンパイラのCフロントエンド(cc)では、型の情報を管理するためにtyp関数が使用され、その中でewidthという配列が型の幅(サイズ)を格納するために利用されていました。しかし、特定の型定数(TDOTTOLDなど)がewidth配列の有効なインデックス範囲(NTYPEまで)を超えてtyp関数に渡されると、配列の範囲外アクセスが発生し、プログラムのクラッシュや予期せぬ動作を引き起こす可能性がありました。

この問題は、Go Issue #3063として報告されており、コンパイラの安定性と堅牢性を確保するために修正が必要でした。コミットメッセージに記載されているように、TDOTTOLDは、NTYPEを超える値を持つ型定数でありながら、typ関数に渡されることが確認されています。

前提知識の解説

  • Goコンパイラ(cc: Go言語のソースコードを機械語に変換するコンパイラの一部です。特にsrc/cmd/ccは、GoコンパイラのCフロントエンドとして機能し、C言語の構文解析や型チェックなど、低レベルな処理を担当します。Goコンパイラは、Go言語で書かれたコードを直接コンパイルするだけでなく、C言語のコードを扱うためのツールチェーンも含まれています。
  • 型システム: プログラミング言語において、データの種類(整数、浮動小数点数、文字列など)を定義し、それらのデータがどのように操作できるかを規定する仕組みです。コンパイラは型システムを利用して、プログラムの正当性を検証し、メモリの割り当てや操作を最適化します。
  • typ関数: GoコンパイラのCフロントエンドにおいて、新しい型オブジェクトを作成または既存の型情報を取得・操作するための関数であると推測されます。この関数は、引数として型を表す整数値(et)と、関連する型情報(d)を受け取ります。
  • ewidth配列: ewidthは "element width" の略であると考えられ、Goコンパイラ内部で各型のメモリ上での幅(サイズ)をバイト単位で格納するために使用される配列です。例えば、int型が4バイトであれば、ewidth[INT_TYPE_CONSTANT]には4が格納されるといった具合です。
  • NTYPE: Goコンパイラ内部で定義されている、有効な型定数の最大値を示す定数です。ewidth配列のサイズは、通常このNTYPEに基づいて決定されます。
  • TDOTTOLD: これらはGoコンパイラ内部で使われる特殊な型定数です。コミットメッセージから、これらがNTYPEよりも大きな値を持つことが示唆されており、通常のデータ型とは異なる特殊な意味を持つ型(例えば、可変引数リストの終端を示すTDOTや、古い型定義を示すTOLDなど)である可能性があります。
  • 配列の範囲外アクセス (Out-of-bounds array access): プログラムが配列の境界を超えてメモリにアクセスしようとするときに発生するエラーです。これは、未定義の動作を引き起こし、プログラムのクラッシュ、データの破損、またはセキュリティ上の脆弱性につながる可能性があります。C言語のような低レベル言語では、プログラマがメモリ管理を直接行うため、このようなエラーが発生しやすいです。

技術的詳細

このコミットの核心は、src/cmd/cc/sub.cファイル内のtyp関数におけるewidth配列へのアクセス方法の変更です。

元のコードでは、typ関数に渡された型定数etが直接ewidth配列のインデックスとして使用されていました。

t->width = ewidth[et];

しかし、etの値がewidth配列の有効なインデックス範囲(NTYPEまで)を超えた場合、これは範囲外アクセスとなり、未定義の動作を引き起こします。コミットメッセージによると、TDOTTOLDという型定数がNTYPEよりも大きな値を持つにもかかわらず、typ関数に渡されることが確認されていました。

修正後のコードでは、ewidth配列にアクセスする前にetの値がNTYPE未満であるかをチェックする条件分岐が追加されました。

if(et < NTYPE)
    t->width = ewidth[et];
else
    t->width = -1; // for TDOT or TOLD in prototype

この変更により、etNTYPE未満の場合のみewidth[et]にアクセスし、それ以外の場合(つまり、TDOTTOLDのようなNTYPEを超える値の場合)はt->width-1を設定するようになりました。-1という値は、これらの特殊な型が具体的なメモリ幅を持たないことを示す、またはコンパイラがこれらの型を特別に処理する必要があることを示すマーカーとして機能すると考えられます。これにより、ewidth配列の範囲外アクセスが防止され、コンパイラの安定性が向上しました。

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

変更はsrc/cmd/cc/sub.cファイルにあります。

--- a/src/cmd/cc/sub.c
+++ b/src/cmd/cc/sub.c
@@ -156,7 +156,10 @@ typ(int et, Type *d)
  	t->link = d;
  	t->down = T;
  	t->sym = S;
- 	t->width = ewidth[et];
+ 	if(et < NTYPE)
+ 		t->width = ewidth[et];
+ 	else
+ 		t->width = -1; // for TDOT or TOLD in prototype
  	t->offset = 0;
  	t->shift = 0;
  	t->nbits = 0;

コアとなるコードの解説

変更されたコードは、typ関数内でType構造体のwidthフィールドを設定する部分です。

  • 変更前:

    t->width = ewidth[et];
    

    この行は、et(要素型)をインデックスとしてewidth配列から値を取得し、それをt->widthに直接代入していました。etewidth配列の有効な範囲外の値(例えばNTYPE以上)である場合、これは範囲外アクセスを引き起こします。

  • 変更後:

    if(et < NTYPE)
        t->width = ewidth[et];
    else
        t->width = -1; // for TDOT or TOLD in prototype
    

    この変更では、if文が追加され、etNTYPE未満であるかどうかをチェックします。

    • et < NTYPEが真の場合:etewidth配列の有効なインデックス範囲内にあるため、以前と同様にewidth[et]の値がt->widthに代入されます。
    • et < NTYPEが偽の場合:etNTYPE以上であることを意味します。この場合、ewidth配列へのアクセスは行われず、代わりにt->width-1が代入されます。コメントにあるように、これはTDOTTOLDのような特殊な型がプロトタイプで渡された場合に適用されます。-1は、これらの型が具体的なメモリ幅を持たないことを示すか、コンパイラがこれらの型を特別に処理する必要があることを示す値として機能します。

この修正により、ewidth配列の範囲外アクセスが確実に防止され、コンパイラの堅牢性が向上しました。

関連リンク

  • Go Issue #3063: https://github.com/golang/go/issues/3063
  • Gerrit Change-Id: I2222222222222222222222222222222222222222 (コミットメッセージのhttps://golang.org/cl/5694047に対応するGerritの変更ID)

参考にした情報源リンク