[インデックス 14211] ファイルの概要
このコミットは、Goコンパイラ(cmd/gc
)とリンカ(cmd/ld
)におけるシンボル命名規則の改善と、予約済みインポートパスの保護に関するものです。具体的には、弱いシンボル(weak symbols)のプレフィックスをweak
からgo.weak
に変更し、さらにgo
やtype
といった内部的なシンボルプレフィックスがインポートパスとして誤用されることを防ぐための防御機構を導入しています。これにより、シンボル名の衝突や予期せぬ動作を防ぎ、Goツールチェインの堅牢性を向上させています。
コミット
- コミットハッシュ:
80dbe74360e8576337208ec68baca7a63946a72a
- Author: Russ Cox rsc@golang.org
- Date: Tue Oct 23 11:16:08 2012 -0400
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/80dbe74360e857633720ec68baca7a63946a72a
元コミット内容
cmd/gc, cmd/ld: use go.weak instead of weak as the weak symbol prefix
Also defend our symbol prefixes (now just "go" and "type")
from use as import paths.
Fixes #4257.
R=ken2
CC=golang-dev
https://golang.org/cl/6744072
変更の背景
この変更は、Go言語のコンパイラとリンカにおけるシンボル管理の堅牢性を高めるために行われました。主な背景は以下の2点です。
- 弱いシンボルプレフィックスの明確化: 以前は弱いシンボルに対して
weak
というプレフィックスが使用されていましたが、これは一般的な単語であり、将来的に他の目的で使用される可能性や、意図しない衝突を引き起こす可能性がありました。Go言語の内部的なシンボルであることを明確にするため、より具体的なgo.weak
というプレフィックスに変更する必要がありました。 - 予約済みインポートパスの保護: Go言語のコンパイラやリンカは、内部的に
go
やtype
といった特定の文字列をシンボルや型情報のプレフィックスとして使用しています。もしユーザーがこれらの文字列をパッケージのインポートパスとして使用した場合、内部的なシンボルとインポートパスが衝突し、コンパイルエラーやリンカエラー、あるいはより深刻な予期せぬ動作を引き起こす可能性がありました。このコミットは、このような衝突を未然に防ぐための防御機構を導入し、ツールチェインの安定性とセキュリティを向上させることを目的としています。
特に、Fixes #4257
という記述から、この変更がGoのIssue 4257を解決するために行われたことがわかります。このIssueでは、go
という名前のパッケージをインポートしようとするとコンパイルエラーが発生するという問題が報告されていました。これは、コンパイラが内部的にgo
を予約語として扱っているため、インポートパスとして使用できないという問題でした。
前提知識の解説
1. シンボルとリンカ
プログラミングにおいて、シンボルとは、変数、関数、クラスなどのプログラム要素を一意に識別するための名前です。コンパイラはソースコードを機械語に変換する際に、これらのシンボルを生成し、リンカは複数のコンパイル済みオブジェクトファイルやライブラリを結合して実行可能ファイルを生成する際に、これらのシンボルを解決(参照を実際のメモリ位置に紐付け)します。
2. 弱いシンボル (Weak Symbols)
弱いシンボル(weak symbols)は、リンカにおける特殊なシンボルの一種です。通常、同じ名前のシンボルが複数定義されている場合、リンカはエラーを報告します。しかし、弱いシンボルを使用すると、同じ名前の強いシンボル(通常のシンボル)が存在する場合、リンカは強いシンボルを優先し、弱いシンボルを無視します。強いシンボルが存在しない場合は、弱いシンボルが使用されます。
これは、ライブラリがデフォルトの実装を提供しつつ、ユーザーがその実装を独自のバージョンでオーバーライドできるようにする場合などに便利です。Go言語の内部では、特定の最適化やランタイムの挙動を制御するために弱いシンボルが利用されることがあります。
3. Go言語のコンパイラ (cmd/gc
) とリンカ (cmd/ld
)
cmd/gc
(Go Compiler): Go言語のソースコードをコンパイルし、オブジェクトファイルを生成する役割を担います。この過程で、Goの構文解析、型チェック、中間コード生成などが行われます。cmd/ld
(Go Linker):cmd/gc
によって生成されたオブジェクトファイルや、Goの標準ライブラリなどを結合し、最終的な実行可能ファイルを生成します。シンボル解決やアドレスの割り当てなどを行います。
4. Bison (Yacc)
src/cmd/gc/go.y
やsrc/cmd/gc/y.tab.c
、src/cmd/gc/y.tab.h
といったファイルは、Goコンパイラの構文解析器(パーサー)に関連しています。これらは、通常、Bison(またはYacc)というパーサー生成ツールによって生成されます。
.y
ファイル: Bisonの入力ファイルで、文法規則とそれに対応するアクション(Goのコード)が記述されています。y.tab.c
: Bisonが.y
ファイルから生成するC言語のソースファイルで、実際のパーサーの実装が含まれます。y.tab.h
: Bisonが生成するヘッダーファイルで、トークン定義などが含まれます。
このコミットでは、y.tab.c
とy.tab.h
に大きな変更が見られますが、これはgo.y
の変更によってBisonが再生成された結果である可能性が高いです。
技術的詳細
このコミットの技術的な変更は、主に以下の2つの側面に焦点を当てています。
1. 弱いシンボルプレフィックスの変更 (weak
-> go.weak
)
Goコンパイラとリンカは、内部的に特定のシンボルにweak
というプレフィックスを付けていました。このコミットでは、このプレフィックスをgo.weak
に変更しています。
-
src/cmd/gc/lex.c
:weaktypepkg
という内部パッケージのパスが"weak.type"
から"go.weak.type"
に変更されています。これは、Goの型システムに関連する弱いシンボルを管理するための内部パッケージです。- コメントも
// not weak%2etype
から// not go%2eweak%2etype
に変更されており、URLエンコードされた形式でのパスの解釈に関する注意が更新されています。
-
src/cmd/ld/go.c
:- リンカ側でも、弱いシンボルを識別するためのロジックが更新され、
go.weak
プレフィックスを認識するように変更されています。これにより、コンパイラとリンカの間で弱いシンボルの命名規則の一貫性が保たれます。
- リンカ側でも、弱いシンボルを識別するためのロジックが更新され、
この変更の目的は、weak
という一般的な単語が他の目的で使用される可能性を排除し、Goの内部的なシンボルであることをより明確にすることです。これにより、将来的なシンボル名の衝突を防ぎ、ツールチェインの安定性を向上させます。
2. 予約済みインポートパスの保護
Goコンパイラは、go
やtype
といった特定の文字列を内部的なシンボルプレフィックスとして使用しています。このコミットでは、これらの文字列がユーザーのインポートパスとして使用されることを防ぐための検証ロジックが追加されています。
-
src/cmd/gc/subr.c
:isbadimport
という新しい関数が導入されています。この関数は、与えられたインポートパスが予約済みの文字列("go"
や"type"
)と一致するかどうかをチェックします。reservedimports
という静的配列が追加され、予約済みのインポートパスが定義されています。mkpkg
関数(パッケージを作成する関数)からisbadimport
の呼び出しが削除されています。これは、mkpkg
がパッケージパスを正規化する前にisbadimport
を呼び出すと、正規化されていないパスで誤ってエラーを報告する可能性があるためです。代わりに、importimport
やimportfile
といった、より適切なタイミングでインポートパスの検証を行う場所でisbadimport
が呼び出されるようになります。
-
src/cmd/gc/export.c
:importimport
関数(インポートされたパッケージのシンボルを処理する関数)の冒頭でisbadimport(z)
が呼び出され、インポートパスz
が不正でないかチェックされます。不正な場合はerrorexit()
で終了します。
-
src/cmd/gc/go.y
:- Go言語の文法定義ファイルにおいて、
hidden_importsym
ルール内でインポートパスの検証が追加されています。mkpkg
を呼び出す前にisbadimport
がチェックされ、不正なインポートパスが使用された場合にエラーを発生させます。
- Go言語の文法定義ファイルにおいて、
-
src/cmd/gc/lex.c
:importfile
関数(ファイルからインポートを処理する関数)内で、正規化されたインポートパスに対してisbadimport
が呼び出されるようになっています。これにより、ファイルシステム上のパスが予約済みインポートパスと衝突しないかどうかが検証されます。
これらの変更により、Goコンパイラは、go
やtype
といった内部的に重要な文字列がインポートパスとして使用されることを積極的に防ぐことができます。これにより、シンボル名の衝突によるコンパイルエラーやリンカエラー、さらには予期せぬランタイムの挙動を防ぎ、Goツールチェインの安定性とセキュリティが向上します。
コアとなるコードの変更箇所
1. src/cmd/gc/lex.c
における weaktypepkg
のパス変更
--- a/src/cmd/gc/lex.c
+++ b/src/cmd/gc/lex.c
@@ -216,9 +216,9 @@ main(int argc, char *argv[])
typepkg = mkpkg(strlit("type"));
typepkg->name = "type";
- weaktypepkg = mkpkg(strlit("weak.type"));
- weaktypepkg->name = "weak.type";
- weaktypepkg->prefix = "weak.type"; // not weak%2etype
+ weaktypepkg = mkpkg(strlit("go.weak.type"));
+ weaktypepkg->name = "go.weak.type";
+ weaktypepkg->prefix = "go.weak.type"; // not go%2eweak%2etype
unsafepkg = mkpkg(strlit("unsafe"));
unsafepkg->name = "unsafe";
2. src/cmd/gc/subr.c
における isbadimport
関数の追加と reservedimports
の定義
--- a/src/cmd/gc/subr.c
+++ b/src/cmd/gc/subr.c
@@ -3572,9 +3572,6 @@ mkpkg(Strlit *path)
Pkg *p;
int h;
- if(isbadimport(path))
- errorexit();
-
h = stringhash(path->s) & (nelem(phash)-1);
for(p=phash[h]; p; p=p->link)
if(p->path->len == path->len && memcmp(path->s, p->path->s, path->len) == 0)
@@ -3623,9 +3620,15 @@ addinit(Node **np, NodeList *init)
n->ullman = UINF;
}
+static char* reservedimports[] = {
+ "go",
+ "type",
+};
+
int
isbadimport(Strlit *path)
{
+ int i;
char *s;
Rune r;
@@ -3633,6 +3636,13 @@ isbadimport(Strlit *path)
yyerror("import path contains NUL");
return 1;
}
+
+ for(i=0; i<nelem(reservedimports); i++) {
+ if(strcmp(path->s, reservedimports[i]) == 0) {
+ yyerror("import path \"%s\" is reserved and cannot be used", path->s);
+ return 1;
+ }
+ }
s = path->s;
while(*s) {
3. src/cmd/gc/export.c
における importimport
での isbadimport
の呼び出し
--- a/src/cmd/gc/export.c
+++ b/src/cmd/gc/export.c
@@ -393,6 +393,8 @@ importimport(Sym *s, Strlit *z)
// human-readable messages.
Pkg *p;
+ if(isbadimport(z))
+ errorexit();
p = mkpkg(z);
if(p->name == nil) {
p->name = s->name;
4. src/cmd/gc/go.y
における hidden_importsym
での isbadimport
の呼び出し
--- a/src/cmd/gc/go.y
+++ b/src/cmd/gc/go.y
@@ -1030,10 +1030,16 @@ sym:
hidden_importsym:
'@' LLITERAL '.' LNAME
{
+ Pkg *p;
+
if($2.u.sval->len == 0)
- $$ = pkglookup($4->name, importpkg);
- else
- $$ = pkglookup($4->name, mkpkg($2.u.sval));
+ p = importpkg;
+ else {
+ if(isbadimport($2.u.sval))
+ errorexit();
+ p = mkpkg($2.u.sval);
+ }
+ $$ = pkglookup($4->name, p);
}
name:
コアとなるコードの解説
1. src/cmd/gc/lex.c
の変更
weaktypepkg
は、Goコンパイラが内部的に使用する「弱い型」に関連するパッケージを表す構造体です。この変更は、そのパッケージのパスとプレフィックスをweak.type
からgo.weak.type
に更新しています。これは、Goの内部的なシンボルであることをより明確にし、一般的なweak
という単語との衝突を避けるための命名規則の変更です。コメントもそれに合わせて更新され、URLエンコードされた形式での解釈に関する注意が反映されています。
2. src/cmd/gc/subr.c
の変更
reservedimports
配列: この配列は、Goコンパイラがインポートパスとして使用することを禁止する文字列("go"
と"type"
)を定義しています。これらの文字列は、Goの内部的なシンボルや型システムで特別な意味を持つため、ユーザーがパッケージ名として使用すると衝突が発生する可能性があります。isbadimport
関数: この関数は、引数として渡されたインポートパスがreservedimports
配列内のいずれかの文字列と一致するかどうかをチェックします。一致した場合、yyerror
(Bisonによって生成されるエラー報告関数)を呼び出してエラーメッセージを出力し、1
を返して不正なインポートパスであることを示します。また、インポートパスにNULL文字が含まれていないかの基本的なチェックも行います。mkpkg
からのisbadimport
呼び出し削除:mkpkg
はパッケージパスを正規化する前に呼び出される可能性があるため、正規化されていないパスでisbadimport
を呼び出すと誤ったエラーを報告する可能性があります。そのため、より適切なタイミング(インポート処理の初期段階)でisbadimport
が呼び出されるように変更されました。
3. src/cmd/gc/export.c
の変更
importimport
関数は、他のパッケージからエクスポートされたシンボルを現在のパッケージにインポートする際に呼び出されます。この関数内でisbadimport(z)
が呼び出されることで、インポートしようとしているパッケージのパスz
が予約済みの文字列でないかどうかがチェックされます。もし不正なパスであれば、errorexit()
が呼び出され、コンパイルが中断されます。これにより、不正なインポートパスがシステムに持ち込まれることを防ぎます。
4. src/cmd/gc/go.y
の変更
go.y
はGo言語の文法定義ファイルであり、hidden_importsym
は隠れたインポートシンボルを処理する文法ルールです。このルール内で、インポートパスを処理する前にisbadimport($2.u.sval)
が呼び出されるようになりました。$2.u.sval
はインポートパスの文字列リテラルを表します。これにより、構文解析の段階で、予約済みの文字列がインポートパスとして使用されていないかどうかが検証され、不正な場合はerrorexit()
でコンパイルが停止されます。
これらの変更は、Goコンパイラとリンカの内部的な整合性を保ちつつ、ユーザーが意図せず内部予約語をインポートパスとして使用してしまうことによる問題を未然に防ぐための重要なセキュリティおよび安定性向上策です。
関連リンク
- Go Issue 4257: https://github.com/golang/go/issues/4257
- このコミットが解決した具体的な問題について記述されています。
参考にした情報源リンク
- Go Issue 4257: https://github.com/golang/go/issues/4257
- GNU Bison: https://www.gnu.org/software/bison/
- Weak symbols in C/C++: https://en.wikipedia.org/wiki/Weak_symbol (一般的な弱いシンボルの概念理解のため)
- Go source code (for context and file structure): https://github.com/golang/go
- Go CL 6744072: https://golang.org/cl/6744072 (元の変更リスト)